<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Stefan Heimes <stefan_heimes@hotmail.com>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\DcGeneral\Data;

use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface;
use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException;
use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidPropertyValueException;
use MetaModels\Exceptions\DifferentValuesException;
use MetaModels\IItem;

/**
 * Data model class for DC_General <-> MetaModel adaption.
 */
class Model implements ModelInterface
{

    /**
     * The MetaModel item accessible via this instance.
     *
     * @var IItem
     */
    protected $objItem = null;

    /**
     * The meta information the DC and views need to buffer in this model.
     *
     * @var array
     */
    protected $arrMetaInformation = array();

    /**
     * Return the names of all properties stored within this model.
     *
     * @return string[]
     */
    protected function getPropertyNames()
    {
        $propertyNames = array('id', 'pid', 'tstamp', 'sorting');

        if ($this->getItem()->getMetaModel()->hasVariants()) {
            $propertyNames[] = 'varbase';
            $propertyNames[] = 'vargroup';
        }

        return array_merge($propertyNames, array_keys($this->getItem()->getMetaModel()->getAttributes()));
    }

    /**
     * Returns the native IMetaModelItem instance encapsulated within this abstraction.
     *
     * @return IItem
     */
    public function getItem()
    {
        return $this->objItem;
    }

    /**
     * Create a new instance of this class.
     *
     * @param IItem $objItem The item that shall be encapsulated.
     */
    public function __construct($objItem)
    {
        $this->objItem = $objItem;
    }

    /**
     * {@inheritDoc}
     */
    public function __clone()
    {
        $this->objItem = $this->getItem()->copy();
    }

    /**
     * {@inheritDoc}
     */
    public function getId()
    {
        return $this->getItem()->get('id');
    }

    /**
     * {@inheritDoc}
     */
    public function getProperty($strPropertyName)
    {
        if ($this->getItem()) {
            $varValue = $this->getItem()->get($strPropertyName);
            // Test if it is an attribute, if so, let it transform the data for the widget.
            $objAttribute = $this->getItem()->getAttribute($strPropertyName);
            if ($objAttribute) {
                $varValue = $objAttribute->valueToWidget($varValue);
            }

            return $varValue;
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public function getPropertiesAsArray()
    {
        $arrResult = array();

        foreach ($this->getPropertyNames() as $strKey) {
            $arrResult[$strKey] = $this->getProperty($strKey);
        }
        return $arrResult;
    }

    /**
     * {@inheritDoc}
     */
    public function getMeta($strMetaName)
    {
        if (array_key_exists($strMetaName, $this->arrMetaInformation)) {
            return $this->arrMetaInformation[$strMetaName];
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public function setId($mixID)
    {
        if ($this->getId() == null) {
            $this->getItem()->set('id', $mixID);
            $this->setMeta(static::IS_CHANGED, true);
        }
    }

    /**
     * {@inheritDoc}
     *
     * @throws DcGeneralInvalidPropertyValueException When the property is unable to accept the value.
     */
    public function setProperty($strPropertyName, $varValue)
    {
        if ($this->getItem()) {
            $varInternalValue = $varValue;
            // Test if it is an attribute, if so, let it transform the data for the widget.
            $objAttribute = $this->getItem()->getAttribute($strPropertyName);
            if ($objAttribute) {
                $varInternalValue = $objAttribute->widgetToValue($varValue, $this->getItem()->get('id'));
            }
            if ($varValue !== $this->getProperty($strPropertyName)) {
                $this->setMeta(static::IS_CHANGED, true);
                $this->getItem()->set($strPropertyName, $varInternalValue);
                try {
                    DifferentValuesException::compare($varValue, $this->getProperty($strPropertyName), false);
                } catch (DifferentValuesException $exception) {
                    throw new DcGeneralInvalidPropertyValueException(
                        sprintf(
                            'Property %s (%s) did not accept the value (%s).',
                            $strPropertyName,
                            $objAttribute->get('type'),
                            $exception->getLongMessage()
                        ),
                        1,
                        $exception
                    );
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public function setPropertiesAsArray($arrProperties)
    {
        foreach ($arrProperties as $strKey => $varValue) {
            $this->setProperty($strKey, $varValue);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function setMeta($strMetaName, $varValue)
    {
        $this->arrMetaInformation[$strMetaName] = $varValue;
    }

    /**
     * {@inheritDoc}
     */
    public function hasProperties()
    {
        return ($this->getItem()) ? true : false;
    }

    /**
     * {@inheritDoc}
     */
    public function getIterator()
    {
        return new ModelIterator($this);
    }

    /**
     * {@inheritDoc}
     */
    public function getProviderName()
    {
        return $this->getItem()->getMetaModel()->getTableName();
    }

    /**
     * {@inheritDoc}
     *
     * @throws DcGeneralInvalidArgumentException When a property in the value bag has been marked as invalid.
     */
    public function readFromPropertyValueBag(PropertyValueBagInterface $valueBag)
    {
        foreach ($this->getPropertyNames() as $property) {
            if (!$valueBag->hasPropertyValue($property)) {
                continue;
            }

            if ($valueBag->isPropertyValueInvalid($property)) {
                throw new DcGeneralInvalidArgumentException('The value for property ' . $property . ' is invalid.');
            }

            $this->setProperty($property, $valueBag->getPropertyValue($property));
        }
    }

    /**
     * {@inheritDoc}
     */
    public function writeToPropertyValueBag(PropertyValueBagInterface $valueBag)
    {
        foreach ($this->getPropertyNames() as $property) {
            if (!$valueBag->hasPropertyValue($property)) {
                continue;
            }

            $valueBag->setPropertyValue($property, $this->getProperty($property));
        }
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\Filter\Setting;

use MetaModels\Filter\FilterUrlBuilder;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Attribute type factory for simple lookup filter settings.
 */
class SimpleLookupFilterSettingTypeFactory extends AbstractFilterSettingTypeFactory
{
    /**
     * The event dispatcher.
     *
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * The filter URL builder.
     *
     * @var FilterUrlBuilder
     */
    private $filterUrlBuilder;

    /**
     * {@inheritDoc}
     */
    public function __construct(EventDispatcherInterface $dispatcher, FilterUrlBuilder $filterUrlBuilder)
    {
        parent::__construct();

        $this
            ->setTypeName('simplelookup')
            ->setTypeIcon('bundles/metamodelscore/images/icons/filter_default.png')
            ->setTypeClass(SimpleLookup::class)
            ->allowAttributeTypes();

        $this->dispatcher       = $dispatcher;
        $this->filterUrlBuilder = $filterUrlBuilder;
    }

    /**
     * {@inheritdoc}
     */
    public function createInstance($information, $filterSettings)
    {
        return new SimpleLookup($filterSettings, $information, $this->dispatcher, $this->filterUrlBuilder);
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaCombine;

use Doctrine\DBAL\Connection;
use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent;

/**
 * This class handles obtaining the group options.
 */
class GroupOptionListener
{
    /**
     * The database connection.
     *
     * @var Connection
     */
    private $connection;

    /**
     * Create a new instance.
     *
     * @param Connection $connection The connection.
     */
    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Get all options for the user groups.
     *
     * @param GetOptionsEvent $event The event.
     *
     * @return void
     */
    public function handle(GetOptionsEvent $event)
    {
        if (('tl_metamodel_dca_combine' !== $event->getEnvironment()->getDataDefinition()->getName())
            || ('rows' !== $event->getPropertyName())
            || !in_array($event->getSubPropertyName(), ['be_group', 'fe_group'])) {
            return;
        }

        $isBackend = 'be_group' === $event->getSubPropertyName();

        $groups = $this
            ->connection
            ->createQueryBuilder()
            ->select('id')
            ->addSelect('name')
            ->from($isBackend ? 'tl_user_group' : 'tl_member_group')
            ->execute()
            ->fetchAll(\PDO::FETCH_ASSOC);

        $result     = [];
        $result[-1] = $event->getEnvironment()->getTranslator()->translate(
            $isBackend ? 'sysadmin' : 'anonymous',
            'tl_metamodel_dca_combine'
        );

        foreach ($groups as $group) {
            $result[$group['id']] = $group['name'];
        }

        $event->setOptions($result);
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\MetaModel;

use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent;
use ContaoCommunityAlliance\DcGeneral\Data\ModelId;
use ContaoCommunityAlliance\UrlBuilder\UrlBuilder;

/**
 * This handles the rendering of models to labels.
 */
class DcaCombineButtonListener extends AbstractAbstainingListener
{
    /**
     * Clear the button if the User is not admin.
     *
     * @param GetOperationButtonEvent $event The event.
     *
     * @return void
     */
    public function handle(GetOperationButtonEvent $event)
    {
        if (!$this->wantToHandle($event)) {
            return;
        }

        $command = $event->getCommand();
        if ($command->getName() == 'dca_combine') {
            $event->setHref(
                UrlBuilder::fromUrl($event->getHref())
                    ->setQueryParameter(
                        'id',
                        ModelId::fromValues('tl_metamodel_dca_combine', $event->getModel()->getId())->getSerialized()
                    )
                    ->getUrl()
            );
        }
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\DefinitionBuilder;

use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent;
use MetaModels\DcGeneral\DataDefinition\IMetaModelDataDefinition;

/**
 * This trait delegates the building of the definition to the build method if it is a MetaModel container.
 */
trait MetaModelDefinitionBuilderTrait
{
    /**
     * Handle the event and delegate to the build method.
     *
     * @param BuildDataDefinitionEvent $event The event.
     *
     * @return void
     */
    public function handle(BuildDataDefinitionEvent $event)
    {
        $container = $event->getContainer();
        if ($container instanceof IMetaModelDataDefinition) {
            $this->build($container);
        }
    }

    /**
     * Build the definition.
     *
     * @param IMetaModelDataDefinition $container The container being built.
     *
     * @return void
     */
    abstract protected function build(IMetaModelDataDefinition $container);
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\DcGeneral\DataDefinition\Palette\Condition\Property;

use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface;
use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\LegendInterface;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface;
use Doctrine\DBAL\Connection;

/**
 * Matches the attribute with the id in the named column against the passed type.
 */
class AttributeByIdIsOfType implements PropertyConditionInterface
{
    /**
     * The expected property value.
     *
     * @var mixed
     */
    private $attributeType;

    /**
     * Database connection.
     *
     * @var Connection
     */
    private $connection;

    /**
     * Buffer the attribute types to ease lookup.
     *
     * @var array
     */
    private static $attributeTypes = [];

    /**
     * The field in the models where the id is stored.
     *
     * @var string
     */
    private $idProperty;

    /**
     * Create a new instance.
     *
     * @param string     $attributeType The attribute type name.
     * @param Connection $connection    Database connection.
     * @param string     $idProperty    The field in the models where the id is stored.
     */
    public function __construct($attributeType, Connection $connection, $idProperty)
    {
        $this->attributeType = $attributeType;
        $this->connection    = $connection;
        $this->idProperty    = $idProperty;
    }

    /**
     * Set the attribute type name.
     *
     * @param string $attributeType The attribute type name.
     *
     * @return self
     */
    public function setAttributeType($attributeType)
    {
        $this->attributeType = $attributeType;

        return $this;
    }

    /**
     * Retrieve the attribute type name.
     *
     * @return string
     */
    public function getAttributeType()
    {
        return $this->attributeType;
    }

    /**
     * Retrieve the type name from an attribute.
     *
     * @param int $value The id of an attribute.
     *
     * @return string
     */
    public function getTypeOfAttribute($value)
    {
        if (!isset(self::$attributeTypes[$value])) {
            $statement = $this
                ->connection
                ->createQueryBuilder()
                ->select('type')
                ->from('tl_metamodel_attribute')
                ->where('id=:id')
                ->setParameter('id', $value)
                ->setMaxResults(1)
                ->execute();

            self::$attributeTypes[$value] = $statement->fetch(\PDO::FETCH_COLUMN);
        }

        return self::$attributeTypes[$value];
    }

    /**
     * {@inheritdoc}
     */
    public function match(
        ModelInterface $model = null,
        PropertyValueBag $input = null,
        PropertyInterface $property = null,
        LegendInterface $legend = null
    ) {
        if ($input && $input->hasPropertyValue($this->idProperty)) {
            $value = $input->getPropertyValue($this->idProperty);
        } elseif ($model) {
            $value = $model->getProperty($this->idProperty);
        } else {
            return false;
        }

        return $this->getTypeOfAttribute($value) === $this->getAttributeType();
    }

    /**
     * {@inheritdoc}
     */
    public function __clone()
    {
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Andreas Isaak <info@andreas-isaak.de>
 * @author     Andreas Nölke <zero@brothers-project.de>
 * @author     David Maack <david.maack@arcor.de>
 * @author     Stefan Heimes <stefan_heimes@hotmail.com>
 * @author     Christopher Boelter <christopher@boelter.eu>
 * @author     Ingolf Steinhardt <info@e-spin.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\Helper;

use Contao\Controller;
use Contao\CoreBundle\Image\ImageFactoryInterface;
use Contao\Dbafs;
use Contao\Environment;
use Contao\File;
use Contao\FilesModel;
use Contao\Input;
use Contao\PageError403;
use Contao\Picture;
use Contao\StringUtil;
use Contao\System;
use Contao\Validator;
use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents;
use ContaoCommunityAlliance\Contao\Bindings\Events\Image\ResizeImageEvent;
use ContaoCommunityAlliance\UrlBuilder\UrlBuilder;
use InvalidArgumentException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * This class provides various methods for handling file collection within Contao.
 *
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
 */
class ToolboxFile
{
    /**
     * The event dispatcher.
     *
     * @var EventDispatcherInterface
     *
     * @deprecated The event dispatcher will get removed in 3.0 as we now use the image factory.
     */
    private $dispatcher;

    /**
     * The project root dir.
     *
     * @var string
     */
    private $rootDir;

    /**
     * The image factory for resizing.
     *
     * @var ImageFactoryInterface
     */
    private $imageFactory;

    /**
     * Allowed file extensions.
     *
     * @var array
     */
    protected $acceptedExtensions;

    /**
     * Base language, used for retrieving meta.txt information.
     *
     * @var string
     */
    protected $baseLanguage;

    /**
     * The fallback language, used for retrieving meta.txt information.
     *
     * @var string
     */
    protected $fallbackLanguage;

    /**
     * Determines if we want to generate images or not.
     *
     * @var boolean
     */
    protected $blnShowImages;

    /**
     * Image resize information.
     *
     * @var array
     */
    protected $resizeImages;

    /**
     * The id to use in lightboxes.
     *
     * @var string
     */
    protected $strLightboxId;

    /**
     * The files to process in this instance.
     *
     * @var array
     */
    protected $foundFiles = array();

    /**
     * The pending paths to collect from DB.
     *
     * @var string[]
     */
    protected $pendingPaths = array();

    /**
     * The pending uuids to collect from DB.
     *
     * @var array
     */
    protected $pendingIds = array();

    /**
     * Meta information for files.
     *
     * @var array
     */
    protected $metaInformation;

    /**
     * File id mapping for files.
     *
     * @var string[]
     */
    protected $uuidMap = array();

    /**
     * Buffered file information.
     *
     * @var array
     */
    protected $outputBuffer;

    /**
     * Buffered modification timestamps.
     *
     * @var array
     */
    protected $modifiedTime;

    /**
     * Create a new instance.
     *
     * @param ImageFactoryInterface|EventDispatcherInterface|null $imageFactory The image factory to use).
     * @param string|null                                         $rootDir      The root path of the installation.
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function __construct($imageFactory = null, string $rootDir = null)
    {
        switch (true) {
            case ($imageFactory instanceof ImageFactoryInterface) && !empty($rootDir):
                $this->imageFactory = $imageFactory;
                $this->rootDir      = $rootDir;
                break;
            // This is the deprecated fallback (remove in MetaModels 3.0).
            case $imageFactory instanceof EventDispatcherInterface:
                // @codingStandardsIgnoreStart
                @trigger_error(
                    'Passing an "EventDispatcherInterface" is deprecated, use a "ImageFactoryInterface" instead.',
                    E_USER_DEPRECATED
                );
                // @codingStandardsIgnoreEnd
                $this->dispatcher = $imageFactory;
                break;
            // This is another deprecated fallback (remove in MetaModels 3.0).
            default:
                // @codingStandardsIgnoreStart
                @trigger_error(
                    'Not passing an "ImageFactoryInterface" and root path is deprecated.',
                    E_USER_DEPRECATED
                );
                // @codingStandardsIgnoreEnd
                $this->dispatcher = System::getContainer()->get('event_dispatcher');
        }
        // Initialize some values to sane base.
        $this->setAcceptedExtensions(StringUtil::trimsplit(',', $GLOBALS['TL_CONFIG']['allowedDownload']));
        if (!is_array($_SESSION['metaModels_downloads'])) {
            $_SESSION['metaModels_downloads'] = [];
        }
    }

    /**
     * Set the allowed file extensions.
     *
     * @param string|array $acceptedExtensions The list of accepted file extensions.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function setAcceptedExtensions($acceptedExtensions)
    {
        // We must not allow file extensions that are globally disabled.
        $allowedDownload = StringUtil::trimsplit(',', $GLOBALS['TL_CONFIG']['allowedDownload']);

        if (!is_array($acceptedExtensions)) {
            $acceptedExtensions = StringUtil::trimsplit(',', $acceptedExtensions);
        }

        $this->acceptedExtensions = array_map('strtolower', array_intersect($allowedDownload, $acceptedExtensions));
    }

    /**
     * Retrieve the allowed file extensions.
     *
     * @return array
     */
    public function getAcceptedExtensions()
    {
        return $this->acceptedExtensions;
    }

    /**
     * Set the base language.
     *
     * @param string $baseLanguage The base language to use.
     *
     * @return ToolboxFile
     */
    public function setBaseLanguage($baseLanguage)
    {
        $this->baseLanguage = $baseLanguage;

        return $this;
    }

    /**
     * Retrieve the base language.
     *
     * @return string
     */
    public function getBaseLanguage()
    {
        return $this->baseLanguage;
    }

    /**
     * Set the fallback language.
     *
     * @param string $fallbackLanguage The fallback language to use.
     *
     * @return ToolboxFile
     */
    public function setFallbackLanguage($fallbackLanguage)
    {
        $this->fallbackLanguage = $fallbackLanguage;

        return $this;
    }

    /**
     * Retrieve the fallback language.
     *
     * @return string
     */
    public function getFallbackLanguage()
    {
        return $this->fallbackLanguage;
    }

    /**
     * Set to show/prepare images or not.
     *
     * @param boolean $blnShowImages True to show images, false otherwise.
     *
     * @return ToolboxFile
     */
    public function setShowImages($blnShowImages)
    {
        $this->blnShowImages = $blnShowImages;

        return $this;
    }

    /**
     * Retrieve the flag if images shall be rendered as images.
     *
     * @return boolean
     *
     * @SuppressWarnings(PHPMD.BooleanGetMethodName)
     */
    public function getShowImages()
    {
        return $this->blnShowImages;
    }

    /**
     * Set the resize information.
     *
     * @param array $resizeImages The resize information. Array of 3 elements: 0: Width, 1: Height, 2: Mode.
     *
     * @return ToolboxFile
     */
    public function setResizeImages($resizeImages)
    {
        $this->resizeImages = $resizeImages;

        return $this;
    }

    /**
     * Retrieve the resize information.
     *
     * @return array
     */
    public function getResizeImages()
    {
        return $this->resizeImages;
    }

    /**
     * Sets the Id to use for the lightbox.
     *
     * @param string $strLightboxId The lightbox id to use.
     *
     * @return ToolboxFile
     */
    public function setLightboxId($strLightboxId)
    {
        $this->strLightboxId = $strLightboxId;

        return $this;
    }

    /**
     * Retrieve the lightbox id to use.
     *
     * @return string
     */
    public function getLightboxId()
    {
        return $this->strLightboxId;
    }

    /**
     * Add path to file or folder list.
     *
     * @param string $strPath The path to be added.
     *
     * @return ToolboxFile
     */
    public function addPath($strPath)
    {
        $this->pendingPaths[] = $strPath;

        return $this;
    }

    /**
     * Contao 3 DBAFS Support.
     *
     * @param string $strId String uuid of the file.
     *
     * @return ToolboxFile
     */
    public function addPathById($strId)
    {
        // Check if empty.
        if (empty($strId)) {
            return $this;
        }

        if (!Validator::isBinaryUuid($strId)) {
            $this->pendingIds[] = StringUtil::uuidToBin($strId);
            return $this;
        }

        $this->pendingIds[] = $strId;
        return $this;
    }

    /**
     * Walks the list of pending folders via ToolboxFile::addPath().
     *
     * @return void
     */
    protected function collectFiles()
    {
        $table = FilesModel::getTable();

        $conditions = array();
        $parameters = array();
        if (count($this->pendingIds)) {
            $conditions[] = $table . '.uuid IN(' .
                implode(',', array_fill(0, count($this->pendingIds), 'UNHEX(?)')) . ')';
            $parameters   = array_map('bin2hex', $this->pendingIds);

            $this->pendingIds = array();
        }
        if (count($this->pendingPaths)) {
            $slug = $table . '.path LIKE ?';
            foreach ($this->pendingPaths as $pendingPath) {
                $conditions[] = $slug;
                $parameters[] = $pendingPath . '%';
            }
            $this->pendingPaths = array();
        }

        if (!count($conditions)) {
            return;
        }

        if ($files = FilesModel::findBy(array(implode(' OR ', $conditions)), $parameters)) {
            $this->addFileModels($files);
        }

        if (count($this->pendingPaths)) {
            // Run again.
            $this->collectFiles();
        }
    }

    /**
     * Generate an URL for downloading the given file.
     *
     * @param string $strFile The file that shall be downloaded.
     *
     * @return string
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    protected function getDownloadLink($strFile)
    {
        if (!isset($_SESSION['metaModels_downloads'][$strFile])) {
            $_SESSION['metaModels_downloads'][$strFile] = md5(uniqid());
        }

        return UrlBuilder::fromUrl(Environment::get('request'))
            ->setQueryParameter('file', urlencode($strFile))
            ->setQueryParameter('fileKey', $_SESSION['metaModels_downloads'][$strFile])
            ->getUrl();
    }

    /**
     * Walk all files and fetch desired additional information like image sizes etc.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    protected function fetchAdditionalData()
    {
        $this->modifiedTime = array();
        $this->outputBuffer = array();

        if (!$this->foundFiles) {
            return;
        }

        foreach ($this->foundFiles as $strFile) {
            $this->processFile($strFile);
        }
    }

    /**
     * Maps the sorting from the files to the source.
     *
     * All files from $arrFiles are being walked and the corresponding entry from source gets pulled in.
     *
     * Additionally, the css classes are applied to the returned 'source' array.
     *
     * This returns an array like: array('files' => array(), 'source' => array())
     *
     * @param array $arrFiles  The files to sort.
     *
     * @param array $arrSource The source list.
     *
     * @return array The mapped result.
     */
    protected function remapSorting($arrFiles, $arrSource)
    {
        $files  = array();
        $source = array();

        foreach (array_keys($arrFiles) as $k) {
            $files[]  = $arrFiles[$k];
            $source[] = $arrSource[$k];
        }

        $this->addClasses($source);

        return array
        (
            'files' => $files,
            'source' => $source
        );
    }

    /**
     * Sorts the internal file list by a given condition.
     *
     * Allowed sort types are:
     * name_asc  - Sort by filename ascending.
     * name_desc - Sort by filename descending
     * date_asc  - Sort by modification time ascending.
     * date_desc - Sort by modification time descending.
     * manual    - Sort by passed id array, the array must contain the binary ids of the files.
     * random    - Shuffle all the files around.
     *
     * @param string $sortType The sort condition to be applied.
     *
     * @param array  $sortIds  The list of binary ids to sort by (sort type "manual" only).
     *
     * @return array The sorted file list.
     */
    public function sortFiles($sortType, $sortIds = array())
    {
        switch ($sortType) {
            case 'name_desc':
                return $this->sortByName(false);

            case 'date_asc':
                return $this->sortByDate(true);

            case 'date_desc':
                return $this->sortByDate(false);

            case 'manual':
                return $this->sortByIdList($sortIds);

            case 'random':
                return $this->sortByRandom();

            default:
            case 'name_asc':
        }
        return $this->sortByName(true);
    }

    /**
     * Attach first, last and even/odd classes to the given array.
     *
     * @param array $arrSource The array reference of the array to which the classes shall be added to.
     *
     * @return void
     */
    protected function addClasses(&$arrSource)
    {
        $countFiles = count($arrSource);
        foreach (array_keys($arrSource) as $k) {
            $arrSource[$k]['class'] = (($k == 0) ? ' first' : '') .
                (($k == ($countFiles - 1)) ? ' last' : '') .
                ((($k % 2) == 0) ? ' even' : ' odd');
        }
    }

    /**
     * Sort by filename.
     *
     * @param boolean $blnAscending Flag to determine if sorting shall be applied ascending (default) or descending.
     *
     * @return array
     */
    protected function sortByName($blnAscending = true)
    {
        $arrFiles = $this->foundFiles;

        if (!$arrFiles) {
            return array('files' => array(), 'source' => array());
        }

        \uasort($arrFiles, ($blnAscending) ? '\basename_natcasecmp' : '\basename_natcasercmp');

        return $this->remapSorting($arrFiles, $this->outputBuffer);
    }

    /**
     * Sort by modification time.
     *
     * @param boolean $blnAscending Flag to determine if sorting shall be applied ascending (default) or descending.
     *
     * @return array
     */
    protected function sortByDate($blnAscending = true)
    {
        $arrFiles = $this->foundFiles;
        $arrDates = $this->modifiedTime;

        if (!$arrFiles) {
            return array('files' => array(), 'source' => array());
        }

        if ($blnAscending) {
            array_multisort($arrFiles, SORT_NUMERIC, $arrDates, SORT_ASC);
        } else {
            array_multisort($arrFiles, SORT_NUMERIC, $arrDates, SORT_DESC);
        }

        return $this->remapSorting($arrFiles, $this->outputBuffer);
    }

    /**
     * Sort by passed id list.
     *
     * @param array $sortIds The list of binary ids to sort by.
     *
     * @return array
     */
    protected function sortByIdList($sortIds)
    {
        $fileMap = $this->foundFiles;
        if (!$fileMap) {
            return array('files' => array(), 'source' => array());
        }
        $fileKeys = array_flip(array_keys($this->uuidMap));
        $sorted   = array();
        foreach ($sortIds as $sortStringId) {
            $key          = $fileKeys[$sortStringId];
            $sorted[$key] = $fileMap[$key];
            unset($fileMap[$key]);
        }
        // Add anything not sorted yet to the end.
        $sorted += $fileMap;

        return $this->remapSorting($sorted, $this->outputBuffer);
    }

    /**
     * Shuffle the file list.
     *
     * @return array
     */
    protected function sortByRandom()
    {
        $arrFiles  = $this->foundFiles;
        $arrSource = $this->outputBuffer;

        if (!$arrFiles) {
            return array('files' => array(), 'source' => array());
        }

        $keys  = array_keys($arrFiles);
        $files = array();
        shuffle($keys);
        foreach ($keys as $key) {
            $files[$key] = $arrFiles[$key];
        }

        return $this->remapSorting($files, $arrSource);
    }

    /**
     * Returns the file list.
     *
     * NOTE: you must call resolveFiles() beforehand as otherwise folders are not being evaluated.
     *
     * @return array
     */
    public function getFiles()
    {
        return $this->foundFiles;
    }

    /**
     * Process all folders and resolve to a valid file list.
     *
     * @return ToolboxFile
     */
    public function resolveFiles()
    {
        // Step 1.: fetch all files.
        $this->collectFiles();

        // Step 1.1.: Check if any file is to be served.
        $this->checkDownloads();

        // Step 2.: fetch additional information like modification time etc. and prepare the output buffer.
        $this->fetchAdditionalData();

        return $this;
    }

    /**
     * Check if a file download is desired.
     *
     * See https://github.com/MetaModels/attribute_file/issues/6
     * See https://github.com/MetaModels/core/issues/1014
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    private function checkDownloads()
    {
        // If images are to be shown, get out.
        if ($this->getShowImages()) {
            return;
        }
        if (($file = Input::get('file')) && ($key = Input::get('fileKey'))) {
            // Check key and return 403 if mismatch.
            if (!(array_key_exists($file, $_SESSION['metaModels_downloads'])
                && $_SESSION['metaModels_downloads'][$file] === $key)) {
                $objHandler = new $GLOBALS['TL_PTY']['error_403']();
                /** @var PageError403 $objHandler */
                $objHandler->generate($file);
            }
            // Send the file to the browser if check succeeded.
            Controller::sendFileToBrowser($file);
        }
    }

    /**
     * Translate the file ID to file path.
     *
     * @param string $varValue The file id.
     *
     * @return string
     */
    public static function convertValueToPath($varValue)
    {
        if (empty($varValue)) {
            return '';
        }

        $objFiles = FilesModel::findByPk($varValue);

        if ($objFiles !== null) {
            return $objFiles->path;
        }
        return '';
    }

    /**
     * Convert an array of values handled by MetaModels to a value to be stored in the database (array of bin uuid).
     *
     * The input array must have the following layout:
     * array(
     *   'bin'   => array() // list of the binary ids.
     *   'value' => array() // list of the uuids.
     *   'path'  => array() // list of the paths.
     *   'meta'  => array() // list of the meta data.
     * )
     *
     * @param array $values The values to convert.
     *
     * @return array
     *
     * @throws InvalidArgumentException When the input array is invalid.
     */
    public static function convertValuesToDatabase($values)
    {
        if (!(isset($values['bin']) && isset($values['value']) && isset($values['path']))) {
            throw new InvalidArgumentException('Invalid file array');
        }

        $bin = array();
        foreach ($values['bin'] as $value) {
            $bin[] = $value;
        }

        return $bin;
    }

    /**
     * Convert an array of values stored in the database (array of bin uuid) to a value to be handled by MetaModels.
     *
     * The output array will have the following layout:
     * array(
     *   'bin'   => array() // list of the binary ids.
     *   'value' => array() // list of the uuids.
     *   'path'  => array() // list of the paths.
     *   'meta'  => array() // list of the meta data.
     * )
     *
     * @param array $values The binary uuid values to convert.
     *
     * @return array
     *
     * @throws InvalidArgumentException When the input array is invalid.
     */
    public static function convertValuesToMetaModels($values)
    {
        if (!is_array($values)) {
            throw new InvalidArgumentException('Invalid uuid list.');
        }

        // Convert UUIDs to binary and clean empty values out.
        $values = array_filter(array_map(function ($fileId) {
            return Validator::isStringUuid($fileId) ? StringUtil::uuidToBin($fileId) : $fileId;
        }, $values));

        $result = array(
            'bin'   => array(),
            'value' => array(),
            'path'  => array(),
            'meta'  => array()
        );
        if (empty($values)) {
            return $result;
        }

        $models = FilesModel::findMultipleByUuids($values);

        if ($models === null) {
            return $result;
        }

        foreach ($models as $value) {
            $result['bin'][]   = $value->uuid;
            $result['value'][] = StringUtil::binToUuid($value->uuid);
            $result['path'][]  = $value->path;
            $result['meta'][]  = StringUtil::deserialize($value->meta, true);
        }

        return $result;
    }

    /**
     * Convert an uuid or path to a value to be handled by MetaModels.
     *
     * The output array will have the following layout:
     * array(
     *   'bin'   => array() // list of the binary ids.
     *   'value' => array() // list of the uuids.
     *   'path'  => array() // list of the paths.
     *   'meta'  => array() // list of the meta data.
     * )
     *
     * @param array $values The binary uuids or paths to convert.
     *
     * @return array
     *
     * @throws InvalidArgumentException When any of the input is not a valid uuid or an non existent file.
     */
    public static function convertUuidsOrPathsToMetaModels($values)
    {
        $values = array_filter((array) $values);
        if (empty($values)) {
            return array(
                'bin'   => array(),
                'value' => array(),
                'path'  => array(),
                'meta'  => array()
            );
        }

        foreach ($values as $key => $value) {
            if (!(Validator::isUuid($value))) {
                $file = FilesModel::findByPath($value) ?: Dbafs::addResource($value);
                if (!$file) {
                    throw new InvalidArgumentException('Invalid value.');
                }

                $values[$key] = $file->uuid;
            }
        }

        return self::convertValuesToMetaModels($values);
    }

    /**
     * Add the passed file model collection to the current buffer if the extension is allowed.
     *
     * Must either be called from within collectFiles or collectFiles must be called later on as this method
     * will add models of type folder to the list of pending paths to allow for recursive inclusion.
     *
     * @param FilesModel[] $files     The files to add.
     *
     * @param array        $skipPaths List of directories not to be added to the list of pending directories.
     *
     * @return void
     */
    private function addFileModels($files, $skipPaths = array())
    {
        $baseLanguage     = $this->getBaseLanguage();
        $fallbackLanguage = $this->getFallbackLanguage();
        foreach ($files as $file) {
            if ('folder' === $file->type && !in_array($file->path, $skipPaths)) {
                $this->pendingPaths[] = $file->path . '/';
                continue;
            }
            if (is_file(TL_ROOT . DIRECTORY_SEPARATOR . $file->path) &&
                in_array(strtolower(pathinfo($file->path, PATHINFO_EXTENSION)), $this->acceptedExtensions)
            ) {
                $path                       = $file->path;
                $this->foundFiles[]         = $path;
                $this->uuidMap[$file->uuid] = $path;
                $meta                       = StringUtil::deserialize($file->meta, true);

                if (isset($meta[$baseLanguage])) {
                    $this->metaInformation[dirname($path)][basename($path)] = $meta[$baseLanguage];
                } elseif (isset($meta[$fallbackLanguage])) {
                    $this->metaInformation[dirname($path)][basename($path)] = $meta[$fallbackLanguage];
                }
            }
        }
    }

    /**
     * Process a single file.
     *
     * @param string $fileName The file to fetch data for.
     *
     * @return void
     */
    private function processFile($fileName)
    {
        $file  = new File($fileName);
        $meta  = $this->metaInformation[dirname($fileName)][$file->basename];
        $title = strlen($meta['title']) ? $meta['title'] : StringUtil::specialchars($file->basename);
        if (strlen($meta['caption'])) {
            $altText = $meta['caption'];
        } else {
            $altText = ucfirst(str_replace('_', ' ', preg_replace('/^[0-9]+_/', '', $file->filename)));
        }

        $information = [
            'file'      => $fileName,
            'mtime'     => $file->mtime,
            'alt'       => $altText,
            'caption'   => (!empty($meta['caption']) ? $meta['caption'] : ''),
            'title'     => $title,
            'metafile'  => $meta,
            'icon'      => 'assets/contao/images/' . $file->icon,
            'extension' => $file->extension,
            'size'      => $file->filesize,
            'sizetext'  => sprintf('(%s)', Controller::getReadableSize($file->filesize, 2)),
            'url'       => StringUtil::specialchars($this->getDownloadLink($fileName))
        ];

        // Prepare GD images.
        if ($information['isGdImage'] = $file->isGdImage) {
            $information['src'] = urldecode($this->resizeImage($fileName));
            if (file_exists(TL_ROOT . '/' . $information['src'])) {
                $size              = getimagesize(TL_ROOT . '/' . $information['src']);
                $information['lb'] = 'lb' . $this->getLightboxId();
                $information['w']  = $size[0];
                $information['h']  = $size[1];
                $information['wh'] = $size[3];
            }
        }

        // Prepare SVG images.
        if ($information['isSvgImage'] = $file->isSvgImage) {
            $information['src'] = $fileName;
        }

        // Prepare the picture for provide the image size.
        if ($information['isPicture'] = (int) $this->resizeImages[2]) {
            $picture = Picture::create($file, $this->getResizeImages())->getTemplateData();

            $picture['alt']   = $altText;
            $picture['title'] = $title;

            $information['picture'] = $picture;
        }

        $this->modifiedTime[] = $file->mtime;
        $this->outputBuffer[] = $information;
    }

    /**
     * Resize the image if needed.
     *
     * @param string $fileName The file to resize.
     *
     * @return null|string
     */
    private function resizeImage($fileName)
    {
        list($width, $height, $mode) = $this->getResizeImages();
        if ($this->getShowImages() && ($width || $height || $mode)) {
            if ($this->imageFactory) {
                $image = $this->imageFactory->create(
                    $this->rootDir . '/' . $fileName,
                    [$width, $height, $mode]
                );

                return $image->getUrl($this->rootDir);
            }

            $event = new ResizeImageEvent($fileName, $width, $height, $mode);
            $this->dispatcher->dispatch(ContaoEvents::IMAGE_RESIZE, $event);
            return $event->getResultImage();
        }

        return $fileName;
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\Controller\Backend;

use MetaModels\Attribute\IAttribute;
use MetaModels\Attribute\IInternal;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * This controller provides the add-all action in render settings.
 */
class RenderSettingAddAllController extends AbstractAddAllController
{
    /**
     * Invoke this.
     *
     * @param string  $metaModel     The MetaModel name.
     * @param string  $renderSetting The render setting id.
     * @param Request $request       The request.
     *
     * @return Response
     */
    public function __invoke($metaModel, $renderSetting, Request $request)
    {
        return $this->process('tl_metamodel_rendersetting', $metaModel, $renderSetting, $request);
    }

    /**
     * {@inheritDoc}
     *
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    protected function createEmptyDataFor(IAttribute $attribute, $parentId, $activate, $sort)
    {
        $result   = [
            'attr_id' => $attribute->get('id'),
            'pid'     => $parentId,
            'sorting' => $sort,
            'tstamp'  => time(),
            'enabled' => $activate ? '1' : ''
        ];
        $defaults = $attribute->getDefaultRenderSettings();
        foreach ($defaults->getKeys() as $key) {
            $result[$key] = $defaults->get($key);
        }

        return $result;
    }

    /**
     * Test if the passed attribute is acceptable.
     *
     * @param IAttribute $attribute The attribute to check.
     *
     * @return bool
     */
    protected function accepts(IAttribute $attribute)
    {
        return !($attribute instanceof IInternal) && !empty($attribute->get('id'));
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @author     Ingolf Steinhardt <info@e-spin.de>
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\FrontendIntegration;

use Contao\BackendTemplate;
use Contao\ContentModel;
use Contao\FormModel;
use Contao\Hybrid;
use Contao\ModuleModel;
use Contao\StringUtil;
use Contao\System;
use Doctrine\DBAL\Connection;
use MetaModels\IFactory;
use MetaModels\IMetaModelsServiceContainer;
use MetaModels\MetaModelsServiceContainer;

/**
 * Base implementation of a MetaModel Hybrid element.
 *
 * @property string metamodel                The id of the MetaModel to use.
 * @property string metamodel_filtering      The id of the MetaModel filter setting to use.
 * @property string metamodel_rendersettings The id of the MetaModel render setting to use.
 */
abstract class MetaModelHybrid extends Hybrid
{
    /**
     * The name to display in the wildcard.
     *
     * @var string
     */
    protected $wildCardName;

    /**
     * The link to use in the wildcard.
     *
     * @var string
     */
    protected $wildCardLink;

    /**
     * The link to use in the wildcard.
     *
     * @var string
     */
    protected $typePrefix;

    /**
     * The database connection.
     *
     * @var Connection
     */
    private $connection;

    /**
     * Retrieve the service container.
     *
     * @return IMetaModelsServiceContainer
     *
     * @deprecated The service container will get removed, inject needed services instead.
     */
    public function getServiceContainer()
    {
        // @codingStandardsIgnoreStart
        @trigger_error(
            '"' .__METHOD__ . '" is deprecated as the service container will get removed.',
            E_USER_DEPRECATED
        );
        // @codingStandardsIgnoreEnd

        return System::getContainer()->get(MetaModelsServiceContainer::class);
    }

    /**
     * Retrieve the factory.
     *
     * @return IFactory
     */
    protected function getFactory()
    {
        return System::getContainer()->get('metamodels.factory');
    }

    /**
     * Retrieve the connection.
     *
     * @return Connection
     */
    protected function getConnection()
    {
        if (null === $this->connection) {
            // @codingStandardsIgnoreStart
            @trigger_error(
                'Connection is missing in class ' . static::class .
                '. The automatic fallback will be dropped in MetaModels 3.0. Please use dependency injection',
                E_USER_DEPRECATED
            );
            // @codingStandardsIgnoreEnd
            return $this->connection = System::getContainer()->get('database_connection');
        }

        return $this->connection;
    }

    /**
     * Create a new instance.
     *
     * @param ContentModel|ModuleModel|FormModel $objElement The object from the database.
     *
     * @param string                             $strColumn  The column the element is displayed within.
     */
    public function __construct($objElement, $strColumn = 'main')
    {
        parent::__construct($objElement, $strColumn);

        $this->arrData = method_exists($objElement, 'row') ? $objElement->row() : (array) $objElement;

        // Get CSS ID and headline from the parent element (!).
        $this->cssID      = StringUtil::deserialize($objElement->cssID, true);
        $this->typePrefix = $objElement->typePrefix;
        $this->strKey     = $objElement->type;
        $arrHeadline      = StringUtil::deserialize($objElement->headline);
        $this->headline   = is_array($arrHeadline) ? $arrHeadline['value'] : $arrHeadline;
        $this->hl         = is_array($arrHeadline) ? $arrHeadline['unit'] : 'h1';
    }

    /**
     * Generate the list.
     *
     * @return string
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function generate()
    {
        if (TL_MODE == 'BE') {
            $strInfo = '';
            if ($this->metamodel) {
                // Add CSS file.
                $GLOBALS['TL_CSS'][] = 'system/modules/metamodels/assets/css/style.css';

                // Retrieve name of MetaModels.
                $infoTemplate =
                    '
%s: %s
';

                $factory       = $this->getFactory();
                $metaModelName = $factory->translateIdToMetaModelName($this->metamodel);
                $metaModel     = $factory->getMetaModel($metaModelName);
                $strInfo       = sprintf(
                    $infoTemplate,
                    $GLOBALS['TL_LANG']['MSC']['mm_be_info_name'][1],
                    $GLOBALS['TL_LANG']['MSC']['mm_be_info_name'][0],
                    $metaModel->getName()
                );

                $database = $this->getConnection();

                // Retrieve name of filter.
                if ($this->metamodel_filtering) {
                    $infoFi = $database
                        ->createQueryBuilder()
                        ->select('name')
                        ->from('tl_metamodel_filter')
                        ->where('id=:id')
                        ->setParameter('id', $this->metamodel_filtering)
                        ->execute()
                        ->fetch(\PDO::FETCH_COLUMN);

                    if ($infoFi) {
                        $strInfo .= sprintf(
                            $infoTemplate,
                            $GLOBALS['TL_LANG']['MSC']['mm_be_info_filter'][1],
                            $GLOBALS['TL_LANG']['MSC']['mm_be_info_filter'][0],
                            $infoFi
                        );
                    }
                }

                // Retrieve name of rendersetting.
                if ($this->metamodel_rendersettings) {
                    $infoRs = $database
                        ->createQueryBuilder()
                        ->select('name')
                        ->from('tl_metamodel_rendersettings')
                        ->where('id=:id')
                        ->setParameter('id', $this->metamodel_rendersettings)
                        ->execute()
                        ->fetch(\PDO::FETCH_COLUMN);

                    if ($infoRs) {
                        $strInfo .= sprintf(
                            $infoTemplate,
                            $GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting'][1],
                            $GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting'][0],
                            $infoRs
                        );
                    }
                }
            }

            $objTemplate           = new BackendTemplate('be_wildcard');
            $objTemplate->wildcard = $this->wildCardName . $strInfo;
            $objTemplate->title    = $this->headline;
            $objTemplate->id       = $this->id;
            $objTemplate->link     = ($this->typePrefix == 'mod_' ? 'FE-Modul: ' : '') . $this->name;
            $objTemplate->href     = sprintf($this->wildCardLink, $this->id);

            return $objTemplate->parse();
        }

        return parent::generate();
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSettingCondition;

use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent;
use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent;
use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent;
use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ManipulateWidgetEvent;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface;
use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent;
use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory;
use MetaModels\Attribute\IAttribute;
use MetaModels\IMetaModel;

/**
 * This handles the rendering of models to labels.
 */
class ValueListener extends AbstractListener
{
    /**
     * Provide options for the values contained within a certain attribute.
     *
     * The values get prefixed with 'value_' to ensure numeric values are kept intact.
     *
     * @param GetPropertyOptionsEvent $event The event.
     *
     * @return void
     */
    public function getValueOptions(GetPropertyOptionsEvent $event)
    {
        if (!$this->wantToHandle($event)) {
            return;
        }

        $model     = $event->getModel();
        $metaModel = $this->getMetaModel($event->getEnvironment());
        $attribute = $metaModel->getAttributeById($model->getProperty('attr_id'));

        if ($attribute) {
            $options = $this->getOptionsViaDcGeneral($metaModel, $event->getEnvironment(), $attribute);
            $mangled = [];
            foreach ((array) $options as $key => $option) {
                $mangled['value_' . $key] = $option;
            }

            $event->setOptions($mangled);
        }
    }

    /**
     * Translates an value to a generated alias to allow numeric values.
     *
     * @param DecodePropertyValueForWidgetEvent $event The event.
     *
     * @return void
     */
    public function decodeValue(DecodePropertyValueForWidgetEvent $event)
    {
        if (!$this->wantToHandle($event)) {
            return;
        }

        if (is_array($event->getValue())) {
            $values = [];

            foreach ($event->getValue() as $value) {
                $values[] = 'value_' . $value;
            }

            // Cut off the 'value_' prefix.
            $event->setValue($values);
        } else {
            $event->setValue('value_' . $event->getValue());
        }
    }

    /**
     * Translates an generated alias to the corresponding value.
     *
     * @param EncodePropertyValueFromWidgetEvent $event The event.
     *
     * @return void
     */
    public function encodeValue(EncodePropertyValueFromWidgetEvent $event)
    {
        if (!$this->wantToHandle($event)) {
            return;
        }

        if (is_array($event->getValue())) {
            $values = [];

            foreach ($event->getValue() as $value) {
                $values[] = substr($value, 6);
            }

            // Cut off the 'value_' prefix.
            $event->setValue($values);
        } else {
            // Cut off the 'value_' prefix.
            $event->setValue(substr($event->getValue(), 6));
        }
    }

    /**
     * Set the the value select to multiple.
     *
     * @param ManipulateWidgetEvent $event The event.
     *
     * @return void
     */
    public function setValueOptionsMultiple(ManipulateWidgetEvent $event)
    {
        if (!$this->wantToHandle($event)) {
            return;
        }

        if ($event->getModel()->getProperty('type') !== 'conditionpropertycontainanyof') {
            return;
        }

        $metaModel = $this->getMetaModel($event->getEnvironment());
        $attribute = $metaModel->getAttributeById($event->getModel()->getProperty('attr_id'));

        if (!($attribute && ($attribute->get('type') == 'tags'))) {
            return;
        }

        $event->getWidget()->multiple = true;
    }

    /**
     * {@inheritDoc}
     */
    protected function wantToHandle(AbstractEnvironmentAwareEvent $event)
    {
        if (!parent::wantToHandle($event)) {
            return false;
        }
        if (method_exists($event, 'getPropertyName') && ('value' !== $event->getPropertyName())) {
            return false;
        }
        if (method_exists($event, 'getProperty')) {
            $property = $event->getProperty();
            if ($property instanceof PropertyInterface) {
                $property = $property->getName();
            }
            if ('value' !== $property) {
                return false;
            }
        }

        return true;
    }

    /**
     * Obtain the values of a property within a dc-general instance.
     *
     * @param IMetaModel           $metaModel   The metamodel instance to obtain the values from.
     *
     * @param EnvironmentInterface $environment The environment used in the input screen table dc-general.
     *
     * @param IAttribute           $attribute   The attribute to obtain the values for.
     *
     * @return array
     */
    private function getOptionsViaDcGeneral($metaModel, $environment, $attribute)
    {
        $factory   = DcGeneralFactory::deriveEmptyFromEnvironment($environment)
            ->setContainerName($metaModel->getTableName());
        $dcGeneral = $factory->createDcGeneral();

        $subEnv = $dcGeneral->getEnvironment();
        $optEv  = new GetPropertyOptionsEvent($subEnv, $subEnv->getDataProvider()->getEmptyModel());
        $optEv->setPropertyName($attribute->getColName());
        $subEnv->getEventDispatcher()->dispatch(GetPropertyOptionsEvent::NAME, $optEv);

        $options = $optEv->getOptions();

        return $options;
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\DefinitionBuilder;

use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultPropertiesDefinition;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\DefaultProperty;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\EmptyValueAwarePropertyInterface;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface;
use MetaModels\Attribute\IAttribute;
use MetaModels\Attribute\ITranslated;
use MetaModels\DcGeneral\DataDefinition\IMetaModelDataDefinition;
use MetaModels\DcGeneral\Events\MetaModel\BuildAttributeEvent;
use MetaModels\IFactory;
use MetaModels\ViewCombination\ViewCombination;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * This class builds the property information.
 */
class PropertyDefinitionBuilder
{
    use MetaModelDefinitionBuilderTrait;

    /**
     * The event dispatcher.
     *
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * The view combinations.
     *
     * @var ViewCombination
     */
    private $viewCombination;

    /**
     * The MetaModels factory.
     *
     * @var IFactory
     */
    private $factory;

    /**
     * Create a new instance.
     *
     * @param EventDispatcherInterface $dispatcher      The event dispatcher.
     * @param ViewCombination          $viewCombination The view combination.
     * @param IFactory                 $factory         The factory.
     */
    public function __construct(
        EventDispatcherInterface $dispatcher,
        ViewCombination $viewCombination,
        IFactory $factory
    ) {
        $this->dispatcher      = $dispatcher;
        $this->viewCombination = $viewCombination;
        $this->factory         = $factory;
    }

    /**
     * Build the property definition.
     *
     * @param IMetaModelDataDefinition $container The data container.
     *
     * @return void
     */
    protected function build(IMetaModelDataDefinition $container)
    {
        $inputScreen = $this->viewCombination->getScreen($container->getName());
        if (!$inputScreen) {
            return;
        }

        if ($container->hasPropertiesDefinition()) {
            $definition = $container->getPropertiesDefinition();
        } else {
            $definition = new DefaultPropertiesDefinition();
            $container->setPropertiesDefinition($definition);
        }

        $metaModel = $this->factory->getMetaModel($container->getName());

        // If the current metamodel has variants add the varbase and vargroup to the definition.
        if ($metaModel->hasVariants()) {
            // These are not properties yet, therefore we have to work around.
            $this->getOrCreateProperty($definition, 'varbase');
            $this->getOrCreateProperty($definition, 'vargroup');
        }

        $properties = [];
        foreach ($inputScreen['properties'] as $property) {
            if ('attribute' !== $property['dcatype']) {
                continue;
            }
            $properties[$property['attr_id']] = $property;
        }

        foreach ($metaModel->getAttributes() as $attribute) {
            if (!isset($properties[$attribute->get('id')])) {
                continue;
            }
            $this->buildProperty(
                $definition,
                $attribute,
                $attribute->getFieldDefinition($properties[$attribute->get('id')])
            );

            $event = new BuildAttributeEvent($metaModel, $attribute, $container);
            $this->dispatcher->dispatch($event::NAME, $event);
        }
    }

    /**
     * Build the property information for a certain property from the data container array.
     *
     * @param PropertiesDefinitionInterface $definition The property collection definition.
     * @param IAttribute                    $attribute  The attribute.
     * @param array                         $propInfo   The property info array.
     *
     * @return void
     */
    private function buildProperty(PropertiesDefinitionInterface $definition, IAttribute $attribute, array $propInfo)
    {
        if (!$attribute) {
            return;
        }

        $property = $this->getOrCreateProperty($definition, $attribute->getColName());
        $this->setLabel($property, $propInfo, $attribute);
        $this->setDescription($property, $propInfo);
        $this->setDefaultValue($property, $propInfo);
        $this->setExcluded($property, $propInfo);
        $this->setSearchable($property, $propInfo);
        $this->setFilterable($property, $propInfo);
        $this->setWidgetType($property, $propInfo);
        $this->setOptions($property, $propInfo);
        $this->setExplanation($property, $propInfo);
        $this->setEval($property, $propInfo, ($attribute instanceof ITranslated));
        $this->setEmptyValue($property, $propInfo);
    }

    /**
     * Retrieves or creates a property information in the definition container.
     *
     * @param PropertiesDefinitionInterface $definition The definition container.
     * @param string                        $propName   The property name.
     *
     * @return PropertyInterface
     */
    private function getOrCreateProperty(PropertiesDefinitionInterface $definition, $propName)
    {
        if ($definition->hasProperty($propName)) {
            return $definition->getProperty($propName);
        }
        $property = new DefaultProperty($propName);
        $definition->addProperty($property);

        return $property;
    }

    /**
     * Set the label in the property.
     *
     * @param PropertyInterface $property  The property definition.
     * @param array             $propInfo  The property info array.
     * @param IAttribute        $attribute The attribute.
     *
     * @return void
     */
    private function setLabel(PropertyInterface $property, $propInfo, IAttribute $attribute)
    {
        if ($property->getLabel()) {
            return;
        }
        if (!isset($propInfo['label'])) {
            $property->setLabel($attribute->getName());
            return;
        }
        $lang = $propInfo['label'];
        if (is_array($lang)) {
            $property->setLabel(reset($lang));
            $property->setDescription(next($lang));
            return;
        }
        $property->setLabel($lang);
    }

    /**
     * Set the description in the property.
     *
     * @param PropertyInterface $property The property definition.
     * @param array             $propInfo The property info array.
     *
     * @return void
     */
    private function setDescription(PropertyInterface $property, $propInfo)
    {
        if ($property->getDescription() || !isset($propInfo['description'])) {
            return;
        }

        $property->setDescription($propInfo['description']);
    }

    /**
     * Set the default value in the property.
     *
     * @param PropertyInterface $property The property definition.
     * @param array             $propInfo The property info array.
     *
     * @return void
     */
    private function setDefaultValue(PropertyInterface $property, $propInfo)
    {
        if (!isset($propInfo['default'])) {
            return;
        }
        $property->setDefaultValue($propInfo['default']);
    }

    /**
     * Set the excluded flag in the property.
     *
     * @param PropertyInterface $property The property definition.
     * @param array             $propInfo The property info array.
     *
     * @return void
     */
    private function setExcluded(PropertyInterface $property, $propInfo)
    {
        if (!isset($propInfo['exclude'])) {
            return;
        }
        $property->setExcluded((bool) $propInfo['exclude']);
    }

    /**
     * Set the searchable flag in the property.
     *
     * @param PropertyInterface $property The property definition.
     * @param array             $propInfo The property info array.
     *
     * @return void
     */
    private function setSearchable(PropertyInterface $property, $propInfo)
    {
        if (!isset($propInfo['search'])) {
            return;
        }
        $property->setSearchable((bool) $propInfo['search']);
    }

    /**
     * Set the filterable flag in the property.
     *
     * @param PropertyInterface $property The property definition.
     * @param array             $propInfo The property info array.
     *
     * @return void
     */
    private function setFilterable(PropertyInterface $property, $propInfo)
    {
        if (!isset($propInfo['filter'])) {
            return;
        }
        $property->setFilterable($propInfo['filter']);
    }

    /**
     * Set the widget type in the property.
     *
     * @param PropertyInterface $property The property definition.
     * @param array             $propInfo The property info array.
     *
     * @return void
     */
    private function setWidgetType(PropertyInterface $property, $propInfo)
    {
        if (null !== $property->getWidgetType() || !isset($propInfo['inputType'])) {
            return;
        }

        $property->setWidgetType($propInfo['inputType']);
    }

    /**
     * Set the options in the property.
     *
     * @param PropertyInterface $property The property definition.
     * @param array             $propInfo The property info array.
     *
     * @return void
     */
    private function setOptions(PropertyInterface $property, $propInfo)
    {
        if (null !== $property->getOptions() || !isset($propInfo['options'])) {
            return;
        }

        $property->setOptions($propInfo['options']);
    }

    /**
     * Set the explanation in the property.
     *
     * @param PropertyInterface $property The property definition.
     * @param array             $propInfo The property info array.
     *
     * @return void
     */
    private function setExplanation(PropertyInterface $property, $propInfo)
    {
        if ($property->getExplanation() || !isset($propInfo['explanation'])) {
            return;
        }

        $property->setExplanation($propInfo['explanation']);
    }

    /**
     * Set the evaluation array.
     *
     * @param PropertyInterface $property     The property definition.
     * @param array             $propInfo     The property info array.
     * @param bool              $isTranslated Flag if the MetaModel is translated.
     *
     * @return void
     */
    private function setEval($property, $propInfo, $isTranslated)
    {
        $extra = isset($propInfo['eval']) ? $propInfo['eval'] : [];
        if ($isTranslated) {
            $extra['tl_class'] = 'translat-attr' . (!empty($extra['tl_class']) ? ' ' . $extra['tl_class'] : '');
        }

        $property->setExtra(array_merge((array) $property->getExtra(), $extra));
    }

    /**
     * Set the empty value if defined.
     *
     * @param PropertyInterface $property The property to set the empty value.
     * @param array             $propInfo The property info.
     *
     * @return void
     */
    private function setEmptyValue(PropertyInterface $property, array $propInfo)
    {
        if (!array_key_exists('empty_value', $propInfo) || !($property instanceof EmptyValueAwarePropertyInterface)) {
            return;
        }
        $property->setEmptyValue($propInfo['empty_value']);
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     David Maack <david.maack@arcor.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels;

use MetaModels\Events\CollectMetaModelTableNamesEvent;
use MetaModels\Events\CreateMetaModelEvent;
use MetaModels\Events\GetMetaModelNameFromIdEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * This is the MetaModel factory interface.
 *
 * To create a MetaModel instance, call @link{MetaModelFactory::getMetaModel()}
 */
class Factory implements IFactory
{
    /**
     * The event dispatcher.
     *
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * The service container.
     *
     * @var IMetaModelsServiceContainer
     */
    private $serviceContainer;

    /**
     * The already translated MetaModel names.
     *
     * @var string[]
     */
    private $lookupMap = array();

    /**
     * Create a new instance.
     *
     * @param EventDispatcherInterface $dispatcher The event dispatcher.
     */
    public function __construct(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * Set the service container.
     *
     * @param IMetaModelsServiceContainer $serviceContainer The service container to use.
     *
     * @return Factory
     *
     * @deprecated The service container will get removed, use the symfony service container instead.
     */
    public function setServiceContainer(IMetaModelsServiceContainer $serviceContainer, $deprecationNotice = true)
    {
        if ($deprecationNotice) {
            // @codingStandardsIgnoreStart
            @trigger_error(
                '"' .__METHOD__ . '" is deprecated and will get removed.',
                E_USER_DEPRECATED
            );
            // @codingStandardsIgnoreEnd
        }

        $this->serviceContainer = $serviceContainer;

        return $this;
    }

    /**
     * Retrieve the service container.
     *
     * @return IMetaModelsServiceContainer
     *
     * @deprecated The service container will get removed, use the symfony service container instead.
     */
    public function getServiceContainer()
    {
        // @codingStandardsIgnoreStart
        @trigger_error(
            '"' .__METHOD__ . '" is deprecated - use the services from the service container.',
            E_USER_DEPRECATED
        );
        // @codingStandardsIgnoreEnd
        return $this->serviceContainer;
    }

    /**
     * {@inheritdoc}
     */
    public function translateIdToMetaModelName($metaModelId)
    {
        if (!isset($this->lookupMap[$metaModelId])) {
            $event = new GetMetaModelNameFromIdEvent($metaModelId);

            $this->dispatcher->dispatch($event::NAME, $event);

            $this->lookupMap[$metaModelId] = $event->getMetaModelName();
        }

        return $this->lookupMap[$metaModelId];
    }

    /**
     * {@inheritdoc}
     */
    public function getMetaModel($metaModelName)
    {
        $event = new CreateMetaModelEvent($this, $metaModelName);

        $this->dispatcher->dispatch($event::NAME, $event);

        $metaModel = $event->getMetaModel();

        return $metaModel;
    }

    /**
     * {@inheritdoc}
     */
    public function collectNames()
    {
        $event = new CollectMetaModelTableNamesEvent($this);

        $this->dispatcher->dispatch($event::NAME, $event);

        return $event->getMetaModelNames();
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\DcGeneral;

use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface;
use MetaModels\Events\CreatePropertyConditionEvent;
use MetaModels\IMetaModel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * This is the fallback to globals.
 *
 * @deprecated Only here as bc-layer to MetaModels 2.0 - to be removed in 3.0
 */
class FallbackPropertyConditionFactory
{
    /**
     * The event dispatcher.
     *
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * Create a new instance.
     *
     * @param EventDispatcherInterface $dispatcher The event dispatcher.
     */
    public function __construct(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * Obtain the id list for globally configured types.
     *
     * @return string[]
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function getIds()
    {
        if (!isset($GLOBALS['METAMODELS']['inputscreen_conditions'])) {
            return [];
        }

        // @codingStandardsIgnoreStart Silencing errors is discouraged
        @trigger_error('Configuring input screen conditions via global array is deprecated. ' .
            'Please implement/configure a valid condition factory.', E_USER_DEPRECATED);
        // @codingStandardsIgnoreEnd

        return array_keys($GLOBALS['METAMODELS']['inputscreen_conditions']);
    }

    /**
     * Test if the passed type supports nesting.
     *
     * @param string $conditionType The type name.
     *
     * @return bool|null
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function supportsNesting($conditionType)
    {
        if (!isset($GLOBALS['METAMODELS']['inputscreen_conditions'][$conditionType]['nestingAllowed'])) {
            return null;
        }

        // @codingStandardsIgnoreStart Silencing errors is discouraged
        @trigger_error('Configuring input screen conditions via global array is deprecated. ' .
            'Please implement/configure a valid condition factory.', E_USER_DEPRECATED);
        // @codingStandardsIgnoreEnd

        return $GLOBALS['METAMODELS']['inputscreen_conditions'][$conditionType]['nestingAllowed'];
    }

    /**
     * Get the amount of children this type supports - for undefined returns null.
     *
     * @param string $conditionType The type name.
     *
     * @return int|null
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function maxChildren($conditionType)
    {
        if (!isset($GLOBALS['METAMODELS']['inputscreen_conditions'][$conditionType]['maxChildren'])) {
            return null;
        }

        // @codingStandardsIgnoreStart Silencing errors is discouraged
        @trigger_error('Configuring input screen conditions via global array is deprecated. ' .
            'Please implement/configure a valid condition factory.', E_USER_DEPRECATED);
        // @codingStandardsIgnoreEnd

        return $GLOBALS['METAMODELS']['inputscreen_conditions'][$conditionType]['maxChildren'];
    }

    /**
     * Test if an attribute type is supported for the passed condition type.
     *
     * @param string $conditionType The condition type.
     * @param string $attribute     The attribute type.
     *
     * @return bool|null
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function supportsAttribute($conditionType, $attribute)
    {
        if (!isset($GLOBALS['METAMODELS']['inputscreen_conditions'][$conditionType]['attributes'])) {
            return null;
        }

        // @codingStandardsIgnoreStart Silencing errors is discouraged
        @trigger_error('Configuring input screen conditions via global array is deprecated. ' .
            'Please implement/configure a valid condition factory.', E_USER_DEPRECATED);
        // @codingStandardsIgnoreEnd

        $allowedAttributes = $GLOBALS['METAMODELS']['inputscreen_conditions'][$conditionType]['attributes'];

        return (\is_array($allowedAttributes) && !\in_array($attribute, $allowedAttributes, true));
    }

    /**
     * Create a condition from the passed configuration.
     *
     * @param array      $configuration The configuration.
     * @param IMetaModel $metaModel     The MetaModel instance.
     *
     * @return PropertyConditionInterface
     *
     * @throws \RuntimeException When the condition could not be transformed.
     */
    public function createCondition(array $configuration, IMetaModel $metaModel)
    {
        $event = new CreatePropertyConditionEvent($configuration, $metaModel);
        $this->dispatcher->dispatch(CreatePropertyConditionEvent::NAME, $event);

        if (null === $instance = $event->getInstance()) {
            throw new \RuntimeException(sprintf(
                'Condition of type %s could not be transformed to an instance.',
                $configuration['type']
            ));
        }

        // @codingStandardsIgnoreStart Silencing errors is discouraged
        @trigger_error('Creating input screen conditions via event is deprecated. ' .
            'Please implement a valid condition factory.', E_USER_DEPRECATED);
        // @codingStandardsIgnoreEnd

        return $instance;
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca;

use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent;
use Doctrine\DBAL\Connection;
use MetaModels\IFactory;

/**
 * This provides the parent table options.
 */
class ParentTableOptionListener
{
    /**
     * The database connection.
     *
     * @var Connection
     */
    private $connection;

    /**
     * The MetaModel factory.
     *
     * @var IFactory
     */
    private $factory;

    /**
     * Create a new instance.
     *
     * @param Connection $connection The database connection.
     * @param IFactory   $factory    The MetaModel factory.
     */
    public function __construct(Connection $connection, IFactory $factory)
    {
        $this->connection = $connection;
        $this->factory    = $factory;
    }

    /**
     * Retrieve a list of all backend sections, like "content", "system" etc.
     *
     * @param GetPropertyOptionsEvent $event The event.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
     */
    public function handle(GetPropertyOptionsEvent $event)
    {
        if (('tl_metamodel_dca' !== $event->getEnvironment()->getDataDefinition()->getName())
            || ('ptable' !== $event->getPropertyName())) {
            return;
        }

        $tables = [];
        foreach ($this->connection->getSchemaManager()->listTableNames() as $table) {
            $tables[$table] = $table;
        }

        if ('ctable' === $event->getModel()->getProperty('rendertype')) {
            $currentTable = $this->factory->translateIdToMetaModelName($event->getModel()->getProperty('pid'));
            $tables       = array_filter(
                $tables,
                function ($table) use ($currentTable) {
                    return ($currentTable !== $table);
                }
            );
        }

        $event->setOptions($tables);
    }
}


<?php

/**
 * This file is part of MetaModels/core.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/core
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Andreas Isaak <info@andreas-isaak.de>
 * @author     David Maack <david.maack@arcor.de>
 * @author     Oliver Hoff <oliver@hofff.com>
 * @author     Stefan Heimes <stefan_heimes@hotmail.com>
 * @author     Cliff Parnitzky <github@cliff-parnitzky.de>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\Dca;

use Contao\StringUtil;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface;
use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface;
use ContaoCommunityAlliance\Translator\TranslatorInterface;
use MetaModels\IMetaModel;

/**
 * This class is used as base class from dca handler classes for various callbacks.
 */
class Helper
{
    /**
     * Decode a language array.
     *
     * @param array|string $varValue     The value to decode.
     *
     * @param IMetaModel   $objMetaModel The MetaModel holding the languages.
     *
     * @return string
     */
    public static function decodeLangArray($varValue, IMetaModel $objMetaModel)
    {
        $arrLangValues = StringUtil::deserialize($varValue);
        if (!$objMetaModel->isTranslated()) {
            // If we have an array, return the first value and exit, if not an array, return the value itself.
            return is_array($arrLangValues)
                ? serialize($arrLangValues[key($arrLangValues)])
                : serialize($arrLangValues);
        }

        // Sort like in MetaModel definition.
        $arrLanguages = $objMetaModel->getAvailableLanguages();
        $arrOutput    = array();

        if ($arrLanguages) {
            foreach ($arrLanguages as $strLangCode) {
                if (is_array($arrLangValues)) {
                    $varSubValue = $arrLangValues[$strLangCode];
                } else {
                    $varSubValue = $arrLangValues;
                }

                if (is_array($varSubValue)) {
                    $arrOutput[] = array_merge($varSubValue, array('langcode' => $strLangCode));
                } else {
                    $arrOutput[] = array('langcode' => $strLangCode, 'value' => $varSubValue);
                }
            }
        }
        return serialize($arrOutput);
    }

    /**
     * Decode a language array.
     *
     * @param array|string $varValue     The value to decode.
     *
     * @param IMetaModel   $objMetaModel The MetaModel holding the languages.
     *
     * @return string
     */
    public static function encodeLangArray($varValue, IMetaModel $objMetaModel)
    {
        // Not translated, make it a plain string.
        if (!$objMetaModel->isTranslated()) {
            return $varValue;
        }
        $arrLangValues = StringUtil::deserialize($varValue);
        $arrOutput     = array();
        foreach ($arrLangValues as $varSubValue) {
            $strLangCode = $varSubValue['langcode'];
            unset($varSubValue['langcode']);
            if (count($varSubValue) > 1) {
                $arrOutput[$strLangCode] = $varSubValue;
            } else {
                $arrKeys                 = array_keys($varSubValue);
                $arrOutput[$strLangCode] = $varSubValue[$arrKeys[0]];
            }
        }
        return serialize($arrOutput);
    }

    /**
     * Extract all languages from the MetaModel and return them as array.
     *
     * @param IMetaModel          $metaModel  The MetaModel to extract the languages from.
     *
     * @param TranslatorInterface $translator The translator to use.
     *
     * @return \string[]
     */
    private static function buildLanguageArray(IMetaModel $metaModel, TranslatorInterface $translator)
    {
        $languages = array();
        foreach ((array) $metaModel->getAvailableLanguages() as $langCode) {
            $languages[$langCode] = $translator->translate('LNG.' . $langCode, 'languages');
        }
        asort($languages);

        return $languages;
    }

    /**
     * Create a widget for naming contexts. Use the language and translation information from the MetaModel.
     *
     * @param EnvironmentInterface $environment   The environment.
     *
     * @param PropertyInterface    $property      The property.
     *
     * @param IMetaModel           $metaModel     The MetaModel.
     *
     * @param string               $languageLabel The label to use for the language indicator.
     *
     * @param string               $valueLabel    The label to use for the input field.
     *
     * @param bool                 $isTextArea    If true, the widget will become a textarea, false otherwise.
     *
     * @param array                $arrValues     The values for the widget, needed to highlight the fallback language.
     *
     * @return void
     */
    public static function prepareLanguageAwareWidget(
        EnvironmentInterface $environment,
        PropertyInterface $property,
        IMetaModel $metaModel,
        $languageLabel,
        $valueLabel,
        $isTextArea,
        $arrValues
    ) {
        if (!$metaModel->isTranslated()) {
            $extra = $property->getExtra();

            $extra['tl_class'] .= empty($extra['tl_class']) ? 'w50' : ' w50';

            $property
                ->setWidgetType('text')
                ->setExtra($extra);

            return;
        }

        $fallback   = $metaModel->getFallbackLanguage();
        $languages  = self::buildLanguageArray($metaModel, $environment->getTranslator());
        $neededKeys = array_keys($languages);

        // Ensure we have values for all languages present.
        if (array_diff_key(array_keys($arrValues), $neededKeys)) {
            foreach ($neededKeys as $langCode) {
                $arrValues[$langCode] = '';
            }
        }

        $rowClasses = array();
        foreach (array_keys($arrValues) as $langCode) {
            $rowClasses[] = ($langCode == $fallback) ? 'fallback_language' : 'normal_language';
        }

        $extra = $property->getExtra();

        $extra['minCount']       =
        $extra['maxCount']       = count($languages);
        $extra['disableSorting'] = true;
        $extra['tl_class']       = 'clr';
        $extra['columnFields']   = array
        (
            'langcode' => array
            (
                'label'                 => $languageLabel,
                'exclude'               => true,
                'inputType'             => 'justtextoption',
                'options'               => $languages,
                'eval'                  => array
                (
                    'rowClasses'        => $rowClasses,
                    'valign'            => 'center',
                    'style'             => 'min-width:75px;display:block;'
                )
            ),
            'value' => array
            (
                'label'                 => $valueLabel,
                'exclude'               => true,
                'inputType'             => $isTextArea ? 'textarea' : 'text',
                'eval'                  => array
                (
                    'rowClasses'        => $rowClasses,
                    'style'             => 'width:400px;',
                    'rows'              => 3
                )
            ),
        );

        $property
            ->setWidgetType('multiColumnWizard')
            ->setExtra($extra);
    }

    /**
     * Search all files with the given file extension below the given path.
     *
     * @param string $folder    The folder to scan.
     *
     * @param string $extension The file extension.
     *
     * @return array
     */
    public static function searchFiles($folder, $extension)
    {
        $scanResult = array();
        $result     = array();
        // Check if we have a file or folder.
        if (is_dir(TL_ROOT . '/' . $folder)) {
            $scanResult = scan(TL_ROOT . '/' . $folder);
        }

        // Run each value.
        foreach ($scanResult as $value) {
            if (!is_file(TL_ROOT . '/' . $folder . '/' . $value)) {
                $result += self::searchFiles($folder . '/' . $value, $extension);
            } else {
                if (preg_match('/'.$extension.'$/i', $value)) {
                    $result[$folder][$folder . '/' . $value] = $value;
                }
            }
        }

        return $result;
    }
}