<?php
/**
 * MIT License <https://opensource.org/licenses/mit>
 *
 * Copyright (c) 2015 Kerem Güneş
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
declare(strict_types=1);

namespace froq\util;

/**
 * Util.
 * @package froq\util
 * @object  froq\util\Util
 * @author  Kerem Güneş <k-gun@mail.com>
 * @since   1.0
 */
final /* static */ class Util
{
    /**
     * Set env.
     * @param string $key
     * @param any    $value
     */
    public static function setEnv(string $key, $value): void
    {
        $_ENV[$key] = $value;
    }

    /**
     * Get env.
     * @param  string   $key
     * @param  any|null $valueDefault
     * @return any
     */
    public static function getEnv(string $key, $valueDefault = null)
    {
        // uppers for nginx
        $value = $_ENV[$key] ?? $_ENV[strtoupper($key)] ??
                 $_SERVER[$key] ?? $_SERVER[strtoupper($key)] ?? $valueDefault;

        if ($value === null) {
            if (false === ($value = getenv($key))) {
                $value = $valueDefault;
            } elseif (function_exists('apache_getenv') && false === ($value = apache_getenv($key))) {
                $value = $valueDefault;
            }
        }

        return $value;
    }

    /**
     * Get client ip.
     * @return ?string
     */
    public static function getClientIp(): ?string
    {
        $ip = null;
        if (null != ($ip = self::getEnv('HTTP_X_FORWARDED_FOR'))) {
            if (false !== strpos($ip, ',')) {
                $ip = trim((string) end(explode(',', $ip)));
            }
        }
        // all ok
        elseif (null != ($ip = self::getEnv('HTTP_CLIENT_IP'))) {}
        elseif (null != ($ip = self::getEnv('HTTP_X_REAL_IP'))) {}
        elseif (null != ($ip = self::getEnv('REMOTE_ADDR_REAL'))) {}
        elseif (null != ($ip = self::getEnv('REMOTE_ADDR'))) {}

        return $ip;
    }

    /**
     * Get current url.
     * @param  bool $withQuery
     * @return string
     */
    public static function getCurrentUrl(bool $withQuery = true): string
    {
        static $filter, $scheme, $host, $port;
        if ($filter == null) {
            $filter = function($input) {
                // decode first @important
                $input = rawurldecode($input);

                // encode quotes and html tags
                return html_encode(
                    // remove NUL-byte, ctrl-z, vertical tab
                    preg_replace('~[\x00\x1a\x0b]|%(?:00|1a|0b)~i', '', trim(
                        // slice at \n or \r
                        substr($input, 0, strcspn($input, "\n\r"))
                    ))
                );
            };

            [$scheme, $host, $port] = [
                $_SERVER['REQUEST_SCHEME'], $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT']
            ];
        }

        $path = $_SERVER['REQUEST_URI'];
        $query = '';
        if (strpos($path, '?')) {
            [$path, $query] = explode('?', $path, 2);
        }

        $port = ($scheme != 'https' && $port != '80') ? ':'. $port : '';
        $path = $filter($path);
        if (strpos($path, '//') !== false) {
            $path = preg_replace('~/+~', '/', $path);
        }

        $url = sprintf('%s://%s%s%s', $scheme, $host, $port, $path);
        if ($withQuery && $query != '') {
            $url .= '?'. $filter($query);
        }

        return $url;
    }

    /**
     * Parse query string (without changing dotted param keys).
     * @param  string      $input
     * @param  bool        $encode
     * @param  string|null $ignoredKeys
     * @return array
     * @link   https://github.com/php/php-src/blob/master/main/php_variables.c#L99
     */
    public static function parseQueryString(string $input, bool $encode = false,
        string $ignoredKeys = null): array
    {
        $ret = [];

        if ($input == '') {
            return $ret;
        }

        // hex keys
        $hexed = false;
        if (strpos($input, '.')) {
            $hexed = true;

            // normalize arrays
            if (strpos($input, '%5D')) {
                $input = str_replace(['%5B', '%5D'], ['[', ']'], $input);
            }

            $input = preg_replace_callback('~(^|(?<=&))[^=&\[]+~', function($match) {
                return bin2hex($match[0]);
            }, $input);
        }

        // preserve pluses, so parse_str() will replace all with spaces
        if ($encode) {
            $input = str_replace('+', '%2B', $input);
        }

        parse_str($input, $input);

        // unhex keys
        if ($hexed) {
            foreach ($input as $key => $value) {
                $ret[hex2bin((string) $key)] = $value;
            }
        } else {
            $ret = $input;
        }

        if ($ignoredKeys != '') {
            $ignoredKeys = explode(',', $ignoredKeys);
            foreach ($ret as $key => $_) {
                if (in_array($key, $ignoredKeys)) {
                    unset($ret[$key]);
                }
            }
        }

        return $ret;
    }

    /**
     * Unparse query string.
     * @param  array       $input
     * @param  bool        $decode
     * @param  string|null $ignoredKeys
     * @param  bool        $stripTags
     * @param  bool        $normalizeArrays
     * @return string
     */
    public static function unparseQueryString(array $input, bool $decode = false,
        string $ignoredKeys = null, bool $stripTags = true, bool $normalizeArrays = true): string
    {
        if ($ignoredKeys != '') {
            $ignoredKeys = explode(',', $ignoredKeys);
            foreach ($input as $key => $_) {
                if (in_array($key, $ignoredKeys)) {
                    unset($input[$key]);
                }
            }
        }

        // fix skipped NULL values by http_build_query()
        static $filter; if ($filter == null) {
            $filter = function ($var) use (&$filter) {
                foreach ($var as $key => $value) {
                    $var[$key] = is_array($value) ? $filter($value) : strval($value);
                }
                return $var;
            };
        }

        $query = http_build_query($filter($input));
        if ($decode) {
            $query = urldecode($query);
        }
        if ($stripTags && strpos($query, '%3C')) {
            $query = preg_replace('~%3C[\w]+(%2F)?%3E~ismU', '', $query);
        }
        if ($normalizeArrays && strpos($query, '%5D')) {
            $query = preg_replace('~([\w\.\-]+)%5B([\w\.\-]+)%5D(=)?~iU', '\1[\2]\3', $query);
        }

        return trim($query);
    }
}


<?php
/**
 * MIT License <https://opensource.org/licenses/mit>
 *
 * Copyright (c) 2015 Kerem Güneş
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
declare(strict_types=1);

namespace froq\util\traits;

/**
 * Property isset trait.
 * @package froq\util\traits
 * @object  froq\util\traits\PropertyIssetTrait
 * @author  Kerem Güneş <k-gun@mail.com>
 * @since   1.0
 */
trait PropertyIssetTrait
{
    /*** Notice: Do not define '__isset' in use'r object. ***/

    /**
     * Isset magic.
     * @param  string $name
     * @return bool
     * @throws froq\util\traits\PropertyTraitException
     */
    public final function __isset(string $name)
    {
        if (!property_exists($this, $name)) {
            throw new PropertyTraitException(sprintf('Property %s::$%s does not exist',
                static::class, $name));
        }

        return ($this->{$name} !== null);
    }
}


<?php

/**
 * This class abstracts access to the GeoCode service
 *
 * @package     Nails
 * @subpackage  module-geo-code
 * @category    Library
 * @author      Nails Dev Team
 * @link
 * @todo        Update this library to be a little more comprehensive, like the CDN library
 */

namespace Nails\GeoCode\Service;

use Nails\Components;
use Nails\Factory;
use Nails\GeoCode\Exception\GeoCodeException;
use Nails\GeoCode\Exception\GeoCodeDriverException;

class GeoCode
{
    use \Nails\Common\Traits\Caching;

    // --------------------------------------------------------------------------

    protected $oDriver;

    // --------------------------------------------------------------------------

    /**
     * The name of the table to store cached results
     */
    const DB_CACHE_TABLE = NAILS_DB_PREFIX . 'geocode_cache';

    // --------------------------------------------------------------------------

    /**
     * The default driver to use if none is specified
     */
    const DEFAULT_DRIVER = 'nails/driver-geo-code-google';

    // --------------------------------------------------------------------------

    /**
     * Construct the Library, test that the driver is valid
     * @throws GeoCodeException
     */
    public function __construct()
    {
        //  Load the driver
        // @todo: build a settings interface for setting and configuring the driver.
        $sSlug    = defined('APP_GEO_CODE_DRIVER') ? strtolower(APP_GEO_CODE_DRIVER) : self::DEFAULT_DRIVER;
        $aDrivers = Components::drivers('nails/module-geo-code');
        $oDriver  = null;

        for ($i=0; $i < count($aDrivers); $i++) {
            if ($aDrivers[$i]->slug == $sSlug) {
                $oDriver = $aDrivers[$i];
                break;
            }
        }

        if (empty($oDriver)) {
            throw new GeoCodeDriverException('"' . $sSlug . '" is not a valid Geo-Code driver', 1);
        }

        $sDriverClass = $oDriver->data->namespace . $oDriver->data->class;

        //  Ensure driver implements the correct interface
        $sInterfaceName = 'Nails\GeoCode\Interfaces\Driver';
        if (!in_array($sInterfaceName, class_implements($sDriverClass))) {

            throw new GeoCodeDriverException(
                '"' . $sDriverClass . '" must implement ' . $sInterfaceName,
                2
            );
        }

        $this->oDriver = Components::getDriverInstance($oDriver);
    }

    // --------------------------------------------------------------------------

    /**
     * Return all information about a given address
     * @param string $sAddress The address to get details for
     * @return null|\Nails\GeoCode\Result\LatLng
     */
    public function lookup($sAddress = '')
    {
        $sAddress = trim($sAddress);

        if (empty($sAddress)){
            return null;
        }

        //  Check caches; local cache first followed by DB cache
        $oCache = $this->getCache($sAddress);

        if (!empty($oCache)) {
            return $oCache;
        }

        $oDb = Factory::service('Database');
        $oDb->select('X(latlng) lat, Y(latlng) lng');
        $oDb->where('address', $sAddress);
        $oDb->limit(1);
        $oResult = $oDb->get(self::DB_CACHE_TABLE)->row();

        if (!empty($oResult)) {

            $oLatLng = Factory::factory('LatLng', 'nails/module-geo-code');
            $oLatLng->setAddress($sAddress);
            $oLatLng->setLat($oResult->lat);
            $oLatLng->setLng($oResult->lng);

        } else {

            $oLatLng = $this->oDriver->lookup($sAddress);

            if (!($oLatLng instanceof \Nails\GeoCode\Result\LatLng)) {

                throw new GeoCodeException(
                    'Geo Code Driver did not return a \Nails\GeoCode\Result\LatLng result',
                    3
                );
            }

            $sLat = $oLatLng->getLat();
            $sLng = $oLatLng->getLng();

            //  Save to the DB Cache
            if (!empty($sLat) && !empty($sLng)) {

                $oDb->set('address', $sAddress);
                $oDb->set('latlng', 'POINT(' . $sLat . ', ' . $sLng . ')', false);
                $oDb->set('created', 'NOW()', false);
                $oDb->insert(self::DB_CACHE_TABLE);
            }
        }

        $this->setCache($sAddress, $oLatLng);

        return $oLatLng;
    }

    // --------------------------------------------------------------------------

    /**
     * Return the address property of a lookup
     * @param string $sAddress The address to look up
     * @return string|null
     */
    public function address($sAddress)
    {
        return $this->lookup($sAddress)->getAddress();
    }

    // --------------------------------------------------------------------------

    /**
     * Return the latLng property of a lookup
     * @param string $sAddress The address to look up
     * @return string|null
     */
    public function latLng($sAddress = '')
    {
        return $this->lookup($sAddress)->getLatLng();
    }

    // --------------------------------------------------------------------------

    /**
     * Return the lat property of a lookup
     * @param string $sAddress The address to look up
     * @return string|null
     */
    public function lat($sAddress = '')
    {
        return $this->lookup($sAddress)->getLat();
    }

    // --------------------------------------------------------------------------

    /**
     * Return the lng property of a lookup
     * @param string $sAddress The address to look up
     * @return string|null
     */
    public function lng($sAddress = '')
    {
        return $this->lookup($sAddress)->getLng();
    }
}


<?php

namespace Gobline\Filter\Validator;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Email extends AbstractValidator
{
    private $value;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if (!is_string($value)) {
            $this->addMessage();

            return false;
        }

        if (filter_var($value, FILTER_VALIDATE_EMAIL) === false) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        return ['The input is not a valid email address'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return ['value' => $this->value];
    }
}


<?php
/**
 * This file is part of Berlioz framework.
 *
 * @license   https://opensource.org/licenses/MIT MIT License
 * @copyright 2017 Ronan GIRON
 * @author    Ronan GIRON <https://github.com/ElGigi>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code, to the root.
 */

declare(strict_types=1);

namespace Berlioz\Config;

use Berlioz\Config\Exception\ConfigException;

/**
 * Class ExtendedJsonConfig.
 *
 * Offer basic configuration class to manage JSON configuration files.
 * Access to the values with get() method, uses 'key.subkey.something' for example.
 *
 * @package Berlioz\Core
 */
class ExtendedJsonConfig extends JsonConfig
{
    /** @var string[] JSON files currently loading */
    private $jsonLoading = [];
    /** @var array User defined actions */
    private static $userDefinedActions = [];

    /**
     * @inheritdoc
     */
    public function __construct(string $json, bool $jsonIsUrl = false)
    {
        parent::__construct($json, $jsonIsUrl);

        // Do actions of variables names
        if (!empty(self::$userDefinedActions)) {
            array_walk_recursive($this->configuration, [$this, 'doActions']);
        }
    }

    /**
     * Load configuration.
     *
     * @param string      $json          JSON data
     * @param bool        $jsonIsUrl     If JSON data is URL? (default: false)
     * @param string|null $baseDirectory Base directory to get JSON file
     *
     * @return array
     * @throws \Berlioz\Config\Exception\ConfigException
     * @throws \Berlioz\Config\Exception\NotFoundException
     */
    protected function load(string $json, bool $jsonIsUrl = false, string $baseDirectory = null): array
    {
        // JSON string?
        if (!$jsonIsUrl) {
            return $this->loadJson($json);
        }

        $configuration = [];
        $localJsonLoading = [];

        do {
            if (!empty($baseDirectory)) {
                $json = realpath($path = sprintf('%s/%s', rtrim($baseDirectory, '\\/'), ltrim($json, '\\/')));
            }
            $baseDirectory = dirname($json);

            // Check recursive file call
            if (in_array($json, $this->jsonLoading)) {
                throw new ConfigException(sprintf('Recursive configuration inclusion/extend for file "%s"', $path ?? $json));
            }

            $configuration = b_array_merge_recursive($this->loadUrl($json), $configuration);

            // Get @extends value
            $extends = $json = $configuration['@extends'] ?? false;
            unset($configuration['@extends']);

            // Add JSON file to currently loading
            $this->jsonLoading[] = $localJsonLoading[] = $json;

            // Do inclusions
            array_walk_recursive($configuration, [$this, 'doInclusions'], $baseDirectory);
        } while ($extends !== false);

        // Remove JSON file from currently loading
        foreach ($localJsonLoading as $json) {
            if ($jsonIsUrl && ($key = array_search($json, $this->jsonLoading)) !== false) {
                unset($this->jsonLoading[$key]);
            }
        }

        return $configuration;
    }

    ////////////////////////////
    /// INCLUSIONS & ACTIONS ///
    ////////////////////////////

    /**
     * Do inclusions.
     *
     * @param mixed  $value
     * @param string $baseDirectory Base directory
     *
     * @throws \Berlioz\Config\Exception\ConfigException
     */
    protected function doInclusions(&$value, $key, string $baseDirectory)
    {
        if (!is_string($value)) {
            return;
        }

        $matches = [];
        if (preg_match(sprintf('/^\s*%1$s(?<action>include|extends)\:(?<var>[\w\-\.\,\s]+)%1$s\s*$/i', preg_quote(self::TAG)), $value, $matches) != 1) {
            return;
        }

        try {
            switch ($matches['action']) {
                case 'include':
                    $value = $this->load($matches['var'], true, $baseDirectory);
                    break;
                case 'extends':
                    $files = explode(',', $matches['var']);
                    $files = array_map('trim', $files);
                    $files = array_map(
                        function ($file) use ($baseDirectory) {
                            return $this->load($file, true, $baseDirectory);
                        },
                        $files);

                    $value = call_user_func_array('b_array_merge_recursive', $files);
                    break;
            }
        } catch (\Exception $e) {
            throw new ConfigException(sprintf('Unable to do action of config line "%s"', $value), 0, $e);
        }
    }

    /**
     * Do actions.
     *
     * @param mixed $value
     *
     * @throws \Berlioz\Config\Exception\ConfigException
     */
    public function doActions(&$value)
    {
        if (!is_string($value)) {
            return;
        }

        $matches = [];
        if (preg_match(sprintf('/^\s*%1$s(?<action>[\w\-\.]+)\:(?<var>[\w\-\.\,\s]+)%1$s\s*$/i', preg_quote(self::TAG)), $value, $matches) != 1) {
            return;
        }

        try {
            if (!isset(self::$userDefinedActions[$matches['action']])) {
                throw new ConfigException(sprintf('Unknown action "%s" in config file', $matches['action']));
            }

            $value = call_user_func_array(self::$userDefinedActions[$matches['action']],
                                          [$matches['var'],
                                           $this]);
        } catch (ConfigException $e) {
            throw $e;
        } catch (\Exception $e) {
            throw new ConfigException(sprintf('Unable to do action of config line "%s"', $value), 0, $e);
        }
    }

    /**
     * Add action.
     *
     * @param string   $name     Action name
     * @param callable $callback Callback
     */
    public static function addAction(string $name, callable $callback)
    {
        self::$userDefinedActions[$name] = $callback;
    }
}


<?php

namespace Gobline\Filter\Sanitizer;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class RTrim implements SanitizerInterface
{
    private $chars;

    /**
     * @param string $chars
     */
    public function __construct($chars = ' ')
    {
        $this->chars = (string) $chars;
    }

    /**
     * {@inheritdoc}
     */
    public function sanitize($value)
    {
        if (!is_scalar($value)) {
            throw new \InvalidArgumentException('Unexpected type: '.gettype($value));
        }

        return rtrim($value, $this->chars);
    }
}


<?php

namespace Gobline\Filter\Validator;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Float extends AbstractValidator
{
    private $value;
    private $allowNegative;

    /**
     * @param bool $allowNegative
     */
    public function __construct($allowNegative = false)
    {
        parent::__construct();

        $this->allowNegative = (bool) $allowNegative;
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if (!is_numeric($value)) {
            $this->addMessage();

            return false;
        }

        $value = (string) $value;

        if (substr_count($value, '.') > 1) {
            $this->addMessage();

            return false;
        }

        $value = str_replace('.', '', $value);

        if ($this->allowNegative) {
            $value = ltrim($value, '-');
        }

        if (!ctype_digit($value)) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        return ['The input is not a valid number'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return ['value' => $this->value];
    }
}


<?php

declare(strict_types=1);

namespace RevisionTen\Forms\Form\Items;

use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\NotBlank;

class ChoiceItem extends Item
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);

        $builder->add('choices', TextareaType::class, [
            'label' => 'Choices',
            'required' => true,
            'attr' => [
                'rows' => 10,
                'placeholder' => 'A list of choices separated by line breaks. You can specify a key by separating it with a pipe char. key|label',
            ],
        ]);

        $builder->add('placeholder', TextType::class, [
            'label' => 'Placeholder',
            'required' => false,
        ]);

        $builder->add('expanded', CheckboxType::class, [
            'label' => 'Expanded',
            'required' => false,
        ]);

        $builder->add('multiple', CheckboxType::class, [
            'label' => 'Multiple',
            'required' => false,
        ]);

        $builder->add('isReceiver', CheckboxType::class, [
            'label' => 'Is Receiver',
            'required' => false,
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public static function getItem(FormBuilderInterface $builder, array $item)
    {
        $attributes = [];

        if (isset($item['read_only']) && $item['read_only']) {
            $attributes['readonly'] = true;
        }

        if (isset($item['twig_variable']) && $item['twig_variable']) {
            $attributes['twig_variable'] = $item['twig_variable'];
        }

        if (isset($item['popover']) && trim($item['popover'])) {
            $attributes['data-toggle'] = 'popover';
            $attributes['data-placement'] = 'top';
            $attributes['data-trigger'] = 'focus';
            $attributes['data-html'] = 'true';
            $attributes['data-content'] = $item['popover'];
        }

        $choices = explode("\n", $item['choices']);

        // TODO: array map.
        $parsedChoices = [];
        foreach ($choices as $key => $line) {
            if (false !== strpos($line, '|')) {
                [$key, $label] = explode('|', $line);
            } else {
                $label = $line;
                $key = $line;
            }

            $parsedChoices[trim((string) $label)] = trim((string) $key);
        }

        $options = [
            'label' => !empty($item['hideLabel']) && $item['hideLabel'] ? false : $item['label'],
            'required' => $item['required'] ?? false,
            'attr' => $attributes,
            'choices' => $parsedChoices,
            'expanded' => $item['expanded'] ?? false,
            'multiple' => $item['multiple'] ?? false,
            'placeholder' => $item['placeholder'] ?? $item['label'],
        ];

        if ($item['expanded']) {
            if ($item['multiple']) {
                $options['label_attr'] = [
                    'class' => 'checkbox-custom',
                ];
            } else {
                $options['label_attr'] = [
                    'class' => 'radio-custom',
                ];
            }
        } elseif (!$item['multiple'])  {
            $options['attr']['class'] = 'custom-select '.($options['attr']['class'] ?? '');
        }

        if (isset($item['required']) && $item['required']) {
            $options['constraints'] = new NotBlank();
        }

        $builder->add($item['name'], ChoiceType::class, $options);
    }

    /**
     * {@inheritdoc}
     */
    public static function getVariables(array $item): array
    {
        $var = $item['multiple'] ? $item['name']."|join(', ')" : $item['name'];

        return [$var];
    }
}


<?php

declare(strict_types=1);

namespace RevisionTen\Forms\Handler;

use RevisionTen\Forms\Command\FormShiftItemCommand;
use RevisionTen\Forms\Event\FormShiftItemEvent;
use RevisionTen\Forms\Model\Form;
use RevisionTen\CQRS\Interfaces\AggregateInterface;
use RevisionTen\CQRS\Interfaces\CommandInterface;
use RevisionTen\CQRS\Interfaces\EventInterface;
use RevisionTen\CQRS\Interfaces\HandlerInterface;
use RevisionTen\CQRS\Message\Message;

final class FormShiftItemHandler extends FormBaseHandler implements HandlerInterface
{
    /**
     * Shifts an item in an array one down.
     *
     * @param array $array
     * @param int   $item
     *
     * @return array
     */
    private static function down(array $array, int $item): array
    {
        if (\count($array) - 1 > $item) {
            $b = \array_slice($array, 0, $item, true);
            $b[] = $array[$item + 1];
            $b[] = $array[$item];
            $b += \array_slice($array, $item + 2, \count($array), true);

            return $b;
        }

        return $array;
    }

    /**
     * Shifts an item in an array one up.
     *
     * @param array $array
     * @param int   $item
     *
     * @return array
     */
    private static function up(array $array, int $item): array
    {
        if ($item > 0 && $item < \count($array)) {
            $b = \array_slice($array, 0, $item - 1, true);
            $b[] = $array[$item];
            $b[] = $array[$item - 1];
            $b += \array_slice($array, $item + 1, \count($array), true);

            return $b;
        }

        return $array;
    }

    /**
     * {@inheritdoc}
     *
     * @var Form $aggregate
     */
    public function execute(CommandInterface $command, AggregateInterface $aggregate): AggregateInterface
    {
        $payload = $command->getPayload();

        $uuid = $payload['uuid'];
        $direction = $payload['direction'];

        // A function that shifts all matching items in a provided direction.
        $shiftFunction = function (&$item, &$collection) use ($direction, $uuid) {
            if (null !== $collection) {
                // Get the key of the item that will shift.
                $itemKey = null;
                foreach ($collection as $key => $subItem) {
                    if ($subItem['uuid'] === $uuid) {
                        $itemKey = $key;
                        continue;
                    }
                }

                if (null !== $itemKey && 'up' === $direction) {
                    $collection = self::up($collection, $itemKey);
                } elseif (null !== $itemKey && 'down' === $direction) {
                    $collection = self::down($collection, $itemKey);
                }
            }
        };

        self::onItem($aggregate, $uuid, $shiftFunction);

        return $aggregate;
    }

    /**
     * {@inheritdoc}
     */
    public static function getCommandClass(): string
    {
        return FormShiftItemCommand::class;
    }

    /**
     * {@inheritdoc}
     */
    public function createEvent(CommandInterface $command): EventInterface
    {
        return new FormShiftItemEvent($command);
    }

    /**
     * {@inheritdoc}
     *
     * @var Form $aggregate
     */
    public function validateCommand(CommandInterface $command, AggregateInterface $aggregate): bool
    {
        $payload = $command->getPayload();
        // The uuid to remove.
        $uuid = $payload['uuid'] ?? null;
        $item = \is_string($uuid) ? self::getItem($aggregate, $uuid) : false;

        if (null === $uuid) {
            $this->messageBus->dispatch(new Message(
                'No uuid to shift is set',
                CODE_BAD_REQUEST,
                $command->getUuid(),
                $command->getAggregateUuid()
            ));

            return false;
        }

        if (!$item) {
            $this->messageBus->dispatch(new Message(
                'Item with this uuid was not found '.$uuid,
                CODE_CONFLICT,
                $command->getUuid(),
                $command->getAggregateUuid()
            ));

            return false;
        }

        if (!isset($payload['direction']) || ('up' !== $payload['direction'] && 'down' !== $payload['direction'])) {
            $this->messageBus->dispatch(new Message(
                'Shift direction is not set',
                CODE_BAD_REQUEST,
                $command->getUuid(),
                $command->getAggregateUuid()
            ));

            return false;
        }

        return true;
    }
}


<?php

declare(strict_types=1);

namespace RevisionTen\Forms\Form\Items;

use RevisionTen\Forms\Interfaces\ItemInterface;
use RevisionTen\Forms\Model\FormRead;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;

class Item extends AbstractType implements ItemInterface
{
    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'items' => false,
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('label', TextType::class, [
            'label' => 'Label',
            'required' => true,
        ]);

        $builder->add('name', TextType::class, [
            'label' => 'Name',
            'required' => true,
            'attr' => [
                'placeholder' => 'only use lowercase letters',
            ],
        ]);

        $builder->add('required', CheckboxType::class, [
            'label' => 'Required',
            'required' => false,
        ]);

        $builder->add('read_only', CheckboxType::class, [
            'label' => 'Read Only',
            'required' => false,
        ]);

        $builder->add('hideLabel', CheckboxType::class, [
            'label' => 'Hide Label',
            'required' => false,
        ]);

        if (class_exists('\Ivory\CKEditorBundle\Form\Type\CKEditorType')) {
            $textAreaType = \Ivory\CKEditorBundle\Form\Type\CKEditorType::class;
        } else {
            $textAreaType = TextareaType::class;
        }

        $builder->add('popover', $textAreaType, [
            'label' => 'Popover',
            'required' => false,
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public static function getItem(FormBuilderInterface $builder, array $item)
    {
        $attributes = [];

        if (isset($item['read_only']) && $item['read_only']) {
            $attributes['readonly'] = true;
        }

        if (isset($item['placeholder']) && $item['placeholder']) {
            $attributes['placeholder'] = $item['placeholder'];
        }

        if (isset($item['twig_variable']) && $item['twig_variable']) {
            $attributes['twig_variable'] = $item['twig_variable'];
        }

        if (isset($item['popover']) && trim($item['popover'])) {
            $attributes['data-toggle'] = 'popover';
            $attributes['data-placement'] = 'top';
            $attributes['data-trigger'] = 'focus';
            $attributes['data-html'] = 'true';
            $attributes['data-content'] = $item['popover'];
        }

        $options = [
            'label' => !empty($item['hideLabel']) && $item['hideLabel'] ? false : $item['label'],
            'required' => $item['required'] ?? false,
            'attr' => $attributes,
        ];

        if (isset($item['required']) && $item['required']) {
            $options['constraints'] = new NotBlank();
        }

        $builder->add($item['name'], TextType::class, $options);
    }

    /**
     * {@inheritdoc}
     */
    public static function getVariables(array $item): array
    {
        return [$item['name']];
    }

    /**
     * {@inheritdoc}
     */
    public function onSubmit(array $data, array $item, FormRead $formRead = null, FormInterface $form): bool
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function onValidate(array $data, array $item, FormRead $formRead = null, FormInterface $form): bool
    {
        return true;
    }
}


<?php

namespace Gobline\Filter\Validator;

use Ramsey\Uuid\Uuid as UuidImpl;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Uuid extends AbstractValidator
{
    private $value;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if (!is_string($value)) {
            $this->addMessage();

            return false;
        }

        if (!UuidImpl::isValid($value)) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        return ['The input is not valid'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return ['value' => $this->value];
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: Olivier
 * Date: 17/04/17
 * Time: 11:34
 */

namespace Dywee\OrderBundle\Controller;

use Dywee\OrderBundle\DyweeOrderEvent;
use Dywee\OrderBundle\Entity\BaseOrderInterface;
use Dywee\OrderBundle\Entity\Payment;
use Dywee\OrderBundle\Event\PaymentValidatedEvent;
use FOS\RestBundle\Controller\Annotations\Route;
use Payum\Core\Model\PaymentInterface;
use Payum\Core\Request\GetHumanStatus;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class PaymentController extends Controller
{
    /**
     * @Route(path="order/payment/prepare", name="order_payment_prepare")
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function prepareAction()
    {
        //$gatewayName = 'stripe_checkout';
        $gatewayName = 'offline';

        $storage = $this->get('payum')->getStorage(Payment::class);

        $order = $this->get('dywee_order_cms.basket_manager')->getBasket();

        $payment = $storage->create();
        $payment->setNumber(uniqid('payment', true));
        $payment->setCurrencyCode('EUR');
        $payment->setTotalAmount($order->getTotalPrice() * 100); // 1.23 EUR
        $payment->setDescription('A description');
        $payment->setClientId($this->getUser()->getId());
        $payment->setClientEmail($this->getUser()->getEmail());

        $storage->update($payment);
        $order->addPayment($payment);
        $order->setPaymentStatus(BaseOrderInterface::PAYMENT_WAITING_VALIDATION);

        $captureToken = $this->get('payum')->getTokenFactory()->createCaptureToken(
            $gatewayName,
            $payment,
            'order_payment_done' // the route to redirect after capture
        );

        return $this->redirect($captureToken->getTargetUrl());
    }

    /**
     * @Route(path="order/payment/done", name="order_payment_done")
     * @param Request $request
     *
     * @return Response
     */
    public function doneAction(Request $request)
    {
        $token = $this->get('payum')->getHttpRequestVerifier()->verify($request);

        $gateway = $this->get('payum')->getGateway($token->getGatewayName());

        // you can invalidate the token. The url could not be requested any more.
        // $this->get('payum')->getHttpRequestVerifier()->invalidate($token);

        // Once you have token you can get the model from the storage directly.
        //$identity = $token->getDetails();
        //$payment = $this->get('payum')->getStorage($identity->getClass())->find($identity);

        // or Payum can fetch the model for you while executing a request (Preferred).
        $gateway->execute($status = new GetHumanStatus($token));
        /** @var PaymentInterface $payment */
        $payment = $status->getFirstModel();
        /** @var BaseOrderInterface $order */
        $order = $payment->getOrder();

        $em = $this->getDoctrine()->getManager();

        if ((float)$payment->getTotalAmount() !== ((float)$order->getTotalPrice()) * 100) {
            throw new \LogicException('amount of the payment is not equal to the amount of the order (' . $payment->getTotalAmount() . '!=' . $order->getTotalPrice());
        }

        $this->get('dywee_order.status_manager')->changePaymentStatus($order, $status);

        $this->get('event_dispatcher')->dispatch(DyweeOrderEvent::PAYMENT_VALIDATED, new PaymentValidatedEvent($order));

        $em->persist($order);
        $em->flush();

        return $this->redirectToRoute('checkout_confirmation');
    }
}


<?php

declare(strict_types=1);

namespace RevisionTen\Forms\Services;

use Doctrine\Common\Collections\Criteria;
use RevisionTen\Forms\Form\Items\TextItem;
use RevisionTen\Forms\Interfaces\ItemInterface;
use RevisionTen\Forms\Model\Form;
use RevisionTen\Forms\Model\FormRead;
use RevisionTen\CQRS\Services\AggregateFactory;
use Doctrine\ORM\EntityManagerInterface;
use RevisionTen\Forms\Model\FormSubmission;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * Class FormService.
 */
class FormService
{
    /**
     * @var ContainerInterface
     */
    private $container;

    /**
     * @var TranslatorInterface
     */
    private $translator;

    /**
     * @var \Swift_Mailer
     */
    private $mailer;

    /**
     * @var \Twig_Environment
     */
    private $twig;

    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    /**
     * @var AggregateFactory
     */
    private $aggregateFactory;

    /**
     * @var FormFactoryInterface
     */
    private $formFactory;

    /**
     * @var array
     */
    private $config;

    /**
     * PagePublishListener constructor.
     *
     * @param ContainerInterface     $container
     * @param TranslatorInterface    $translator
     * @param \Swift_Mailer          $mailer
     * @param \Twig_Environment      $twig
     * @param EntityManagerInterface $entityManager
     * @param AggregateFactory       $aggregateFactory
     * @param FormFactoryInterface   $formFactory
     * @param array                  $config
     */
    public function __construct(ContainerInterface $container, TranslatorInterface $translator, \Swift_Mailer $mailer, \Twig_Environment $twig, EntityManagerInterface $entityManager, AggregateFactory $aggregateFactory, FormFactoryInterface $formFactory, array $config)
    {
        $this->container = $container;
        $this->translator = $translator;
        $this->mailer = $mailer;
        $this->twig = $twig;
        $this->entityManager = $entityManager;
        $this->aggregateFactory = $aggregateFactory;
        $this->formFactory = $formFactory;
        $this->config = $config;
    }

    /**
     * Update the FormRead entity for the admin backend.
     *
     * @param string $formUuid
     */
    public function updateFormRead(string $formUuid): void
    {
        /**
         * @var Form $aggregate
         */
        $aggregate = $this->aggregateFactory->build($formUuid, Form::class);

        // Build FormRead entity from Aggregate.
        $formRead = $this->entityManager->getRepository(FormRead::class)->findOneByUuid($formUuid) ?? new FormRead();
        $formRead->setVersion($aggregate->getStreamVersion());
        $formRead->setUuid($formUuid);

        $formData = json_decode(json_encode($aggregate), true);
        $formRead->setPayload($formData);

        $formRead->setTitle($aggregate->title);
        $formRead->setEmail($aggregate->email);
        $formRead->setEmailCC($aggregate->emailCC);
        $formRead->setEmailBCC($aggregate->emailBCC);
        $formRead->setSender($aggregate->sender);
        $formRead->setTemplate($aggregate->template);
        $formRead->setEmailTemplate($aggregate->emailTemplate);
        $formRead->setEmailTemplateCopy($aggregate->emailTemplateCopy);
        $formRead->setDeleted($aggregate->deleted);
        $formRead->setHtml($aggregate->html);
        $formRead->setSuccessText($aggregate->successText);
        $formRead->setSaveSubmissions($aggregate->saveSubmissions);
        $formRead->setCreated($aggregate->getCreated());
        $formRead->setModified($aggregate->getModified());

        // Persist FormRead entity.
        $this->entityManager->persist($formRead);
        $this->entityManager->flush();
    }

    /**
     * @param string $itemName
     *
     * @return string
     */
    public function getItemClass(string $itemName): string
    {
        $itemTypes = $this->config['item_types'] ?? [];

        return $itemTypes[$itemName]['class'] ?? TextItem::class;
    }

    /**
     * @param string $formUuid
     * @param null   $data
     * @param bool   $ignore_validation
     *
     * @return FormInterface
     */
    public function getForm(string $formUuid, $data = null, bool $ignore_validation = false): FormInterface
    {
        /** @var FormRead $formRead */
        $formRead = $this->entityManager->getRepository(FormRead::class)->findOneBy(['uuid' => $formUuid]);
        $payload = $formRead->getPayload();

        $formName = strtolower(preg_replace('/[^A-Za-z0-9]/', '', $formRead->getTitle()));
        if (empty($formName)) {
            $formName = $formRead->getUuid();
        }

        $formOptions = [
            'action' => '?formular=abgeschickt',
            'validation_groups' => !$ignore_validation,
        ];

        $formBuilder = $this->formFactory->createNamedBuilder($formName, FormType::class, $data, $formOptions);

        foreach ($payload['items'] as $item) {
            $itemClass = $this->getItemClass($item['itemName']);

            if (method_exists($itemClass, 'getItem')) {
                $itemClass::getItem($formBuilder, $item['data']);
            }
        }

        return $formBuilder->getForm();
    }

    /**
     * @param string $ip
     * @param int    $formId
     *
     * @return bool
     */
    public function isBlocked(string $ip, int $formId): bool
    {
        /**
         * Check for recent submissions by this ip on this form.
         *
         * @var FormSubmission[] $formSubmissions
         */
        $formSubmissions = $this->entityManager->getRepository(FormSubmission::class)->findBy([
            'ip' => $ip,
            'form' => $formId,
        ], [
            'created' => Criteria::DESC,
        ], 1);
        /** @var FormSubmission $formSubmission */
        $formSubmission = !empty($formSubmissions) ? array_values($formSubmissions)[0] : null;

        return null !== $formSubmission && time() < $formSubmission->getExpires()->getTimestamp();
    }

    public function handleRequest(Request $request, string $formUuid, array $defaultData): array
    {
        $messages = [];

        /**
         * Get the FormRead entity.
         *
         * @var FormRead $formRead
         */
        $formRead = $this->entityManager->getRepository(FormRead::class)->findOneBy(['uuid' => $formUuid]);
        $aggregateData = $formRead->getPayload();

        $ignore_validation = null !== $request && $request->get('ignore_validation');
        $form = $this->getForm($formUuid, $defaultData, $ignore_validation);
        $form->handleRequest($request);

        // Check If user is blocked.
        $timelimit = $aggregateData['timelimit'] ?? 0;
        $ip = $request->getClientIp();
        $isBlocked = $ip ? $timelimit && $this->isBlocked($ip, $formRead->getId()) : false;

        // Display error If user is blocked.
        if (!$ignore_validation && $isBlocked) {
            $aggregateData = json_decode(json_encode($formRead->getPayload()), true);
            $timeLimitMessage = $aggregateData['timeLimitMessage'] ?? $this->translator->trans('You have already submitted the form, please try again later');
            if ($form->isSubmitted()) {
                $form->addError(new FormError($timeLimitMessage));
            } else {
                $messages[] = [
                    'type' => 'warning',
                    'message' => $timeLimitMessage,
                ];
            }
        }

        if (!$ignore_validation && !$isBlocked && $form->isSubmitted()) {
            // Check if the form is valid.
            #$submittedData = $form->getData();
            $submittedData = array_map(function ($field) {
                /** @var FormInterface $field */
                return $field->getData();
            }, $form->all());
            $data = array_merge($defaultData, $submittedData);
            $isValid = $form->isValid() && $this->onValidate($form,$formRead, $data);

            if ($isValid && $this->onSubmit($form,$formRead, $data)) {

                // Build and send the email.
                $message = new \Swift_Message();

                // Get To Email.
                $to = $this->getField($aggregateData, $data, 'isReceiver') ?? $formRead->getEmail();
                $message->setTo(self::getMailsFromString($to));

                // Get CC Email.
                $cc = $formRead->getEmailCC();
                if ($cc) {
                    $message->setCc(self::getMailsFromString($cc));
                }

                // Get BCC Email.
                $bcc = $formRead->getEmailBCC();
                if ($bcc) {
                    $message->setBcc(self::getMailsFromString($bcc));
                }

                // Get ReplyTo Email.
                $replyTo = $this->getField($aggregateData, $data, 'replyTo');

                // Get ReplyTo Name.
                $replyToName = null;
                $name = $this->getField($aggregateData, $data, 'isName');
                $firstname = $this->getField($aggregateData, $data, 'isFirstname');
                if ($replyTo && $name && \is_string($name)) {
                    $replyToName = $name;
                    if ($firstname && \is_string($firstname)) {
                        $replyToName = $firstname.' '.$replyToName;
                    }
                }

                // Set Reply To.
                $message->setReplyTo($replyTo, $replyToName);

                // Set Sender and From.
                $message->setSender($formRead->getSender(), $replyToName ?? 'Website');
                $message->setFrom($formRead->getSender(), $replyToName ?? 'Website');

                // Get Subject.
                $message->setSubject($this->getField($aggregateData, $data, 'isSubject') ?? $formRead->getTitle());

                // Get the message body.
                [$body, $contentType] = $this->renderEmailTemplate($formRead->getEmailTemplate(), $formRead->getHtml(), $data);
                $message->setBody($body, $contentType);

                // Send different emails to main recipient and copy recipients.
                if (!empty($formRead->getEmailTemplateCopy())) {
                    // Send copies with different body.
                    $messageCc = clone $message;
                    [$body, $contentType] = $this->renderEmailTemplate($formRead->getEmailTemplateCopy(), $formRead->getHtml(), $data);
                    $messageCc->setBody($body, $contentType);
                    $messageCc->setTo(null);
                    $this->mailer->send($messageCc);

                    // Send to main recipient.
                    $message->setCc(null);
                    $message->setBcc(null);
                    $this->mailer->send($message);
                } else {
                    // Send to all recipients with same message body.
                    $this->mailer->send($message);
                }

                // Display Success Message.
                $messages[] = [
                    'type' => 'success',
                    'message' => $formRead->getSuccessText(),
                ];

                // Save submission in submission table.
                if ($timelimit || $formRead->getSaveSubmissions()) {
                    $this->saveFormSubmission((int) $timelimit, $ip, $formRead, $data);
                }
            }
        }

        return [
            'template' => $formRead->getTemplate(),
            'ignore_validation' => $ignore_validation,
            'formView' => $form->createView(),
            'messages' => $messages,
        ];
    }

    private function onValidate($form, FormRead $formRead, array $data): bool
    {
        $aggregateData = $formRead->getPayload();

        // Execute onValidate listeners.
        foreach ($aggregateData['items'] as $item) {
            $itemClass = $this->getItemClass($item['itemName']);
            // Get the form as a service or instantiate it.
            try {
                $itemForm = $this->container->get($itemClass);
            } catch (ServiceNotFoundException $exception) {
                $itemForm = new $itemClass();
            }

            if ($itemForm instanceof ItemInterface && !$itemForm->onValidate($data, $item['data'], $formRead, $form)) {
                break;
            }
        }

        return true;
    }

    private function onSubmit($form, FormRead $formRead, array $data): bool
    {
        $aggregateData = $formRead->getPayload();

        // Execute onSubmit listeners.
        foreach ($aggregateData['items'] as $item) {
            $itemClass = $this->getItemClass($item['itemName']);
            // Get the form as a service or instantiate it.
            try {
                $itemForm = $this->container->get($itemClass);
            } catch (ServiceNotFoundException $exception) {
                $itemForm = new $itemClass();
            }

            if ($itemForm instanceof ItemInterface && !$itemForm->onSubmit($data, $item['data'], $formRead, $form)) {
                return false;
            }
        }

        return true;
    }

    private function saveFormSubmission(int $timelimit, string $ip, FormRead $formRead, array $submittedData): void
    {
        $expiresTimestamp = time() + $timelimit;
        $expires = new \DateTime();
        $expires->setTimestamp($expiresTimestamp);
        $formSubmission = new FormSubmission($formRead, $ip, $expires, $submittedData);
        $this->entityManager->persist($formSubmission);
        $this->entityManager->flush();
    }

    private function renderEmailTemplate(string $emailTemplate, bool $isHtml, $data): ?array
    {
        // Try to render the twig template.
        $body = null;
        $error = null;
        try {
            $view = $this->twig->createTemplate($emailTemplate);
            try {
                $body = $view->render($data);
            } catch (\Twig_Error_Runtime $error) {
                $body = null;
            } catch (\Throwable $error) {
                $body = null;
            }
        } catch (\Twig_Error_Syntax $error) {
            $body = null;
        } catch (\Twig_Error_Loader $error) {
            $body = null;
        }

        // If rendering the twig template fails json_encode the raw form data and send as plain text with error attached.
        if (null === $body && \is_object($error) && method_exists($error, 'getRawMessage')) {
            $body = 'An Error occurred: '.$error->getRawMessage()."\nPlease check your Email-Template at line ".$error->getTemplateLine().". \nHere is the raw form submission:";
            $body .= "\n\n".json_encode($data);
            $isHtml = false;
        }

        $contentType = $isHtml ? 'text/html' : 'text/plain';

        return [
            $body,
            $contentType,
        ];
    }

    /**
     * @param string $mails
     *
     * @return array
     */
    private static function getMailsFromString(string $mails): array
    {
        return array_map('trim', explode(',', $mails));
    }

    /**
     * Check the aggregate for a field with the propertyName boolean
     * property and return the fields value.
     *
     * Example: You have a field with a "replyTo" boolean and want to return
     * its value when it is set to true.
     *
     * Todo: Support child items.
     *
     * @param array  $payload
     * @param array  $data
     * @param string $propertyName
     *
     * @return mixed|null
     */
    private function getField(array $payload, array $data, string $propertyName)
    {
        $isField = false;
        $payload = json_decode(json_encode($payload), true);
        if ($payload['items'] && \is_array($payload['items']) && \count($payload['items']) > 0) {
            foreach ($payload['items'] as $item) {
                if (isset($item['data'][$propertyName]) && $item['data'][$propertyName]) {
                    $isField = $item['data']['name'];
                }
            }
        }

        return $isField ? $data[$isField] : null;
    }

    public function getFormSubmissions(int $id)
    {
        /** @var FormSubmission[] $formSubmissions */
        $formSubmissions = $this->entityManager->getRepository(FormSubmission::class)->findBy([
            'form' => $id,
        ]);

        $payloads = array_map(function ($formSubmission) {
            /** @var FormSubmission $formSubmission */
            return $formSubmission->getPayload();
        }, $formSubmissions);

        return $payloads;
    }
}


<?php

namespace Dywee\OrderBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Dywee\CoreBundle\Model\AddressInterface;
use Dywee\CoreBundle\Model\ProductInterface;
use Dywee\CoreBundle\Traits\TimeDelimitableEntity;
use Dywee\ProductBundle\Entity\BaseProduct;
use Dywee\ProductBundle\Entity\RentableProduct;
use Dywee\ProductBundle\Entity\RentableProductItem;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Dywee\CoreBundle\Model\CustomerInterface;
use Payum\Core\Model\PaymentInterface;

/**
 * BaseOrder
 *
 * @ORM\Table(name="orders")
 * @ORM\Entity(repositoryClass="Dywee\OrderBundle\Repository\BaseOrderRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class BaseOrder implements BaseOrderInterface
{
    use TimestampableEntity;
    use TimeDelimitableEntity;

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var boolean
     *
     * @ORM\Column(name="isGift", type="boolean")
     */
    private $isGift = false;

    /**
     * @var float
     *
     * @ORM\Column(name="priceVatExcl", type="decimal", precision=5, scale=2)
     */
    private $priceVatExcl = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="deliveryCost", type="decimal", precision=5, scale=2)
     */
    private $deliveryCost = 0;

    /**
     * @var string
     *
     * @ORM\Column(name="vatRate", type="decimal", precision=5, scale=2)
     */
    private $vatRate = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="vatPrice", type="decimal", precision=5, scale=2)
     */
    private $vatPrice = 0;

    /**
     * @var string
     *
     * @ORM\Column(name="discountCode", type="string", length=255, nullable=true)
     */
    private $discountCode;

    /**
     * @var float
     *
     * @ORM\Column(name="discountRate", type="decimal", precision=5, scale=2, nullable=true)
     */
    private $discountRate;

    /**
     * @var float
     *
     * @ORM\Column(name="discountValue", type="decimal", precision=5, scale=2, nullable=true)
     */
    private $discountValue;

    /**
     * @var float
     *
     * @ORM\Column(name="priceVatIncl", type="decimal", precision=5, scale=2)
     */
    private $priceVatIncl = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="totalPrice", type="decimal", precision=5, scale=2)
     */
    private $totalPrice = 0;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="text", nullable=true)
     */
    private $description;

    /**
     * @var \DateTime
     *
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $validatedAt;

    /**
     * @var \DateTime
     *
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $endedAt;

    /**
     * @var string
     *
     * @ORM\Column(name="reference", type="string", length=255)
     */
    private $reference;

    /**
     * @var string
     *
     * @ORM\Column(name="invoiceReference", type="string", length=255, nullable=true)
     */
    private $invoiceReference;

    /**
     * @var string
     *
     * @ORM\Column(name="shippingMessage", type="text", nullable=true)
     */
    private $shippingMessage;

    /**
     * @var string
     *
     * @ORM\Column(name="locale", type="string", length=255, nullable=true)
     */
    private $locale;

    /**
     * @var boolean
     *
     * @ORM\Column(name="active", type="boolean")
     */
    private $active = false;

    /**
     * @var string
     *
     * @ORM\Column(name="deposit", type="float")
     */
    private $deposit = 0;

    /**
     * @var string
     *
     * @ORM\Column(name="state", type="text")
     */
    private $state = BaseOrderInterface::STATE_IN_SESSION;

    /**
     * @var CustomerInterface
     * @ORM\ManyToOne(targetEntity="Dywee\CoreBundle\Model\CustomerInterface", cascade={"persist"})
     */
    private $customer;

    /**
     * @var AddressInterface
     * @ORM\ManyToOne(targetEntity="Dywee\AddressBundle\Entity\Address", cascade={"persist"})
     */
    private $billingAddress;

    /**
     * @var AddressInterface
     * @ORM\ManyToOne(targetEntity="Dywee\AddressBundle\Entity\Address", cascade={"persist"})
     */
    private $shippingAddress;

    /**
     * @ORM\OneToMany(targetEntity="OrderElement", mappedBy="order", cascade={"persist", "remove"})
     */
    private $orderElements;

    /**
     * @ORM\OneToMany(targetEntity="OrderDiscountElement", mappedBy="order")
     */
    private $discountElements;

    /**
     * @ORM\OneToMany(targetEntity="Dywee\OrderBundle\Entity\Shipment", mappedBy="order", cascade={"persist", "remove"})
     */
    private $shipments;

    /**
     * @ORM\ManyToOne(targetEntity="Dywee\OrderBundle\Entity\ShippingMethod", cascade={"persist"})
     */
    private $shippingMethod;

    /**
     * @ORM\Column(name="weight", type="float")
     */
    private $weight = 0;

    /**
     * @ORM\Column(name="mailStep", type="smallint")
     */
    private $mailStep = false;

    /**
     * @ORM\Column(name="isPriceTTC", type="boolean")
     */
    private $isPriceTTC = true;

    /**
     * @var string
     * @ORM\Column(type="string", length=15)
     */
    private $type = self::TYPE_ONLY_BUY;

    /**
     * @var boolean
     * @ORM\Column(type="boolean")
     */
    private $mustRecalculShipments = false;

    private $previousState;

    /**
     * @var ArrayCollection|Payment[]
     * @ORM\OneToMany(targetEntity="Dywee\OrderBundle\Entity\Payment", mappedBy="order")
     */
    private $payments;

    /**
     * @var string
     * @ORM\Column(type="string", length=30)
     */
    private $paymentStatus = BaseOrderInterface::PAYMENT_STATUS_NONE;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->orderElements = new ArrayCollection();
        $this->shipments = new ArrayCollection();
        $this->reference = time() . '-' . strtoupper(substr(md5(mt_rand() . mt_rand()), 0, 4));
        $this->discountElements = new ArrayCollection();
        $this->beginAt = new \DateTime();
        $this->payments = new ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set isGift
     *
     * @param boolean $isGift
     *
     * @return BaseOrderInterface
     */
    public function setIsGift($isGift)
    {
        $this->isGift = $isGift;

        return $this;
    }

    /**
     * Get isGift
     *
     * @return boolean
     */
    public function isGift()
    {
        return $this->isGift;
    }

    /**
     * Set priceVatExcl
     *
     * @param float $priceVatExcl
     *
     * @return BaseOrderInterface
     */
    public function setPriceVatExcl($priceVatExcl)
    {
        $this->priceVatExcl = $priceVatExcl;

        return $this;
    }

    /**
     * Get priceVatExcl
     *
     * @return float
     */
    public function getPriceVatExcl()
    {
        return $this->priceVatExcl;
    }

    /**
     * Set deliveryCost
     *
     * @deprecated
     *
     * @param float $deliveryCost
     *
     * @return BaseOrderInterface
     */
    public function setDeliveryCost($deliveryCost)
    {
        $this->deliveryCost = $deliveryCost;

        return $this;
    }

    public function setShippingCost($deliveryCost)
    {
        $this->deliveryCost = $deliveryCost;

        return $this;
    }

    /**
     * Get deliveryCost
     *
     * @deprecated use shippingCost instead
     *
     * @return float
     */
    public function getDeliveryCost()
    {
        return $this->deliveryCost;
    }

    public function getShippingCost()
    {
        return $this->deliveryCost;
    }

    /**
     * Set vatRate
     *
     * @param string $vatRate
     *
     * @return BaseOrderInterface
     */
    public function setVatRate($vatRate)
    {
        $this->vatRate = $vatRate;

        return $this;
    }

    /**
     * Get vatRate
     *
     * @return string
     */
    public function getVatRate()
    {
        return $this->vatRate;
    }

    /**
     * Set vatPrice
     *
     * @param float $vatPrice
     *
     * @return BaseOrderInterface
     */
    public function setVatPrice($vatPrice)
    {
        $this->vatPrice = $vatPrice;

        return $this;
    }

    /**
     * Get vatPrice
     *
     * @return float
     */
    public function getVatPrice()
    {
        return $this->vatPrice;
    }

    /**
     * Set discountCode
     *
     * @param string $discountCode
     *
     * @return BaseOrderInterface
     */
    public function setDiscountCode($discountCode)
    {
        $this->discountCode = $discountCode;

        return $this;
    }

    /**
     * Get discountCode
     *
     * @return string
     */
    public function getDiscountCode()
    {
        return $this->discountCode;
    }

    /**
     * Set discountRate
     *
     * @param float $discountRate
     *
     * @return BaseOrderInterface
     */
    public function setDiscountRate($discountRate)
    {
        $this->discountRate = $discountRate;

        return $this;
    }

    /**
     * Get discountRate
     *
     * @return float
     */
    public function getDiscountRate()
    {
        return $this->discountRate;
    }

    /**
     * Set priceVatIncl
     *
     * @param float $priceVatIncl
     *
     * @return BaseOrderInterface
     */
    public function setPriceVatIncl($priceVatIncl)
    {
        $this->priceVatIncl = $priceVatIncl;

        return $this;
    }

    /**
     * Get priceVatIncl
     *
     * @return float
     */
    public function getPriceVatIncl()
    {
        return $this->priceVatIncl;
    }

    /**
     * Set totalPrice
     *
     * @param float $totalPrice
     *
     * @return BaseOrderInterface
     */
    public function setTotalPrice($totalPrice)
    {
        $this->totalPrice = $totalPrice;
        if ($this->totalPrice === 0) {
            $this->setShippingCost(0);
        }

        return $this;
    }

    /**
     * Get totalPrice
     *
     * @return float
     */
    public function getTotalPrice()
    {
        return $this->totalPrice;
    }

    /**
     * Set description
     *
     * @param string $description
     *
     * @return BaseOrderInterface
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set validatedAt
     *
     * @param \DateTime $validationDate
     *
     * @return BaseOrderInterface
     */
    public function setValidatedAt(\DateTime $validationDate)
    {
        $this->validatedAt = $validationDate;

        return $this;
    }

    /**
     * Get validatedAt
     *
     * @return \DateTime
     */
    public function getValidatedAt()
    {
        return $this->validatedAt;
    }

    /**
     * Set reference
     *
     * @param string $reference
     *
     * @return BaseOrderInterface
     */
    public function setReference($reference)
    {
        $this->reference = $reference;

        return $this;
    }

    /**
     * Get reference
     *
     * @return string
     */
    public function getReference()
    {
        return $this->reference;
    }

    /**
     * Set invoiceReference
     *
     * @param string $invoiceReference
     *
     * @return BaseOrderInterface
     */
    public function setInvoiceReference($invoiceReference)
    {
        $this->invoiceReference = $invoiceReference;

        return $this;
    }

    /**
     * Get invoiceReference
     *
     * @return string
     */
    public function getInvoiceReference()
    {
        return $this->invoiceReference;
    }

    /**
     * Set shippingMessage
     *
     * @param string $shippingMessage
     *
     * @return BaseOrderInterface
     */
    public function setShippingMessage($shippingMessage)
    {
        $this->shippingMessage = $shippingMessage;

        return $this;
    }

    /**
     * Get shippingMessage
     *
     * @return string
     */
    public function getShippingMessage()
    {
        return $this->shippingMessage;
    }

    /**
     * Set locale
     *
     * @param string $locale
     *
     * @return BaseOrderInterface
     */
    public function setLocale($locale)
    {
        $this->locale = $locale;

        return $this;
    }

    /**
     * Get locale
     *
     * @return string
     */
    public function getLocale()
    {
        return $this->locale;
    }

    /**
     * Set active
     *
     * @param boolean $active
     *
     * @return BaseOrderInterface
     */
    public function setActive($active)
    {
        $this->active = $active;

        return $this;
    }

    /**
     * Get active
     *
     * @return boolean
     */
    public function getActive()
    {
        return $this->active;
    }

    /**
     * Set state
     *
     * @param string $state
     *
     * @return BaseOrderInterface
     *
     * @deprecated
     */
    public function setState($state)
    {
        return $this->setStatus($state);
    }

    /**
     * @param string $status
     *
     * @return $this
     */
    public function setStatus($status)
    {
        // We keep the previous state for handling the change of state
        if ($this->state !== $status) {
            $this->setPreviousState($this->getState());
        }

        $this->state = $status;

        return $this;
    }

    /**
     * Get status
     *
     * @return string
     */
    public function getState()
    {
        return $this->getStatus();
    }

    /**
     * Get state
     *
     * @return string
     */
    public function getStatus()
    {
        return $this->state;
    }

    /**
     * Set customer
     *
     * @param CustomerInterface $customer
     *
     * @return BaseOrderInterface
     */
    public function setCustomer(CustomerInterface $customer = null)
    {
        $this->customer = $customer;

        return $this;
    }

    /**
     * Get customer
     *
     * @return CustomerInterface
     */
    public function getCustomer()
    {
        return $this->customer;
    }

    /**
     * @inheritdoc
     */
    public function setBillingAddress(AddressInterface $billingAddress = null)
    {
        $this->billingAddress = $billingAddress;

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getBillingAddress()
    {
        return $this->billingAddress;
    }

    /**
     * @inheritdoc
     */
    public function setShippingAddress(AddressInterface $shippingAddress = null)
    {
        $this->shippingAddress = $shippingAddress;

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getShippingAddress()
    {
        return $this->shippingAddress;
    }

    /**
     * @inheritdoc
     */
    public function setDiscountValue($discountValue)
    {
        $this->discountValue = $discountValue;

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getDiscountValue()
    {
        return $this->discountValue;
    }

    /**
     * @inheritdoc
     */
    public function addOrderElement(OrderElement $orderElements)
    {
        $this->orderElements[] = $orderElements;
        $orderElements->setOrder($this);
        $this->mustRecalculShipments = true;

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function removeOrderElement(OrderElement $orderElements)
    {
        $this->orderElements->removeElement($orderElements);
        $orderElements->setOrder(null);
        $this->mustRecalculShipments = true;

        return $this;
    }

    /**
     * Get orderElements
     *
     * @return \Doctrine\Common\Collections\Collection|OrderElement[]
     */
    public function getOrderElements()
    {
        return $this->orderElements;
    }

    /**
     * @param null $type
     *
     * @return int
     */
    public function countProducts($type = null)
    {
        $quantity = 0;

        foreach ($this->getOrderElements() as $orderElement) {
            if (!$type || $orderElement->getProduct() instanceof $type) {
                $quantity += $orderElement->getQuantity();
            }
        }

        return $quantity;
    }

    /**
     * @inheritdoc
     */
    public function forcePriceCalculation()
    {
        $isTTC = true;

        // Price calculation
        $price = 0;
        if ($this->getShippingAddress() && $this->getShippingAddress()->getCity() && $this->getShippingAddress()->getCity()->getCountry()) {
            $this->setVatRate($this->getShippingAddress()->getCity()->getCountry()->getVatRate());
        }
        $this->setPriceVatIncl(0);
        foreach ($this->getOrderElements() as $orderElement) {
            $price += $orderElement->getTotalPrice();
        }

        if ($isTTC) {
            $this->setPriceVatIncl($price);
            $this->setPriceVatExcl($price / (1 + $this->getVatRate() / 100));
            $this->setVatPrice($this->getPriceVatIncl() - $this->getPriceVatExcl());
        }

        $this->calculateShippingCost();

        $this->setTotalPrice($this->getPriceVatIncl() + $this->getShippingCost() - $this->getDiscountValue());

        return $this;
    }

    /**
     * @return BaseOrder
     * @deprecated
     */
    public function calculShippingCost()
    {
        return $this->calculateShippingCost();
    }

    /**
     * @return $this
     */
    public function calculateShippingCost()
    {
        //TODO le shipping cost doit être calculé via le prix d'envoi de chaque shippingMethod de chaque orderElement
        if ($this->getShippingMethod()) {
            $this->setShippingCost($this->getShippingMethod()->getPrice() * count($this->getShipments()));
        } else {
            $this->setShippingCost(null);
        }

        return $this;
    }

    /**
     * @ORM\PrePersist
     * @ORM\PreUpdate
     */
    public function checkBeforeDB()
    {
        $this->checkType();
        //$this->weightCalculation();
        $this->forcePriceCalculation();
    }

    public function checkType()
    {
        $buy = false;
        $rent = false;

        foreach ($this->getOrderElements() as $element) {
            if ($element instanceof RentableProduct || $element instanceof RentableProductItem) {
                $rent = true;
            } else {
                $buy = true;
            }

            if ($rent && $buy) {
                break;
            }
        }

        if ($buy) {
            if ($rent) {
                $this->setType(BaseOrder::TYPE_BUY_AND_RENT);
            } else {
                $this->setType(BaseOrder::TYPE_ONLY_BUY);
            }
        } elseif ($rent) {
            $this->setType(BaseOrder::TYPE_BUY_AND_RENT);
        }

        return $this;

    }

    /**
     * @param ProductInterface $product
     * @param             $quantity
     * @param int         $locationCoeff
     *
     * @return $this
     */
    public function addProduct(ProductInterface $product, $quantity, $locationCoeff = 1)
    {

    }

    /**
     * @inheritdoc
     */
    public function getQuantityForProduct(ProductInterface $product)
    {
        foreach ($this->getOrderElements() as $key => $orderElement) {
            if ($orderElement->getProduct()->getId() === $product->getId()) {
                return $orderElement->getQuantity();
            }
        }

        return 0;
    }

    /**
     * @inheritdoc
     */
    public function getWeight($type = null)
    {
        $weight = 0;
        foreach ($this->getOrderElements() as $orderElement) {
            if (!$type || $orderElement->getProduct() instanceof $type) {
                $weight += $orderElement->getProduct()->getWeight() * $orderElement->getQuantity();
            }
        }

        return $weight;
    }

    /**
     * @return $this
     */
    public function weightCalculation()
    {
        $weight = 0;
        foreach ($this->getOrderElements() as $key => $orderElement) {
            $weight += $orderElement->getProduct()->getWeight() * $orderElement->getQuantity();
        }
        $this->weight = $weight;

        return $this;
    }

    public function removeProduct(ProductInterface $product)
    {
        $id = is_numeric($product) ? $product : $product->getId();

        foreach ($this->getOrderElements() as $orderElement) {
            if ($orderElement->getProduct()->getId() === $id) {
                $this->removeOrderElement($orderElement);
                $this->mustRecalculShipments = true;
                $this->forcePriceCalculation();
            }
        }

        return $this;
    }

    public function containsElementReduction()
    {
        foreach ($this->orderElements as $orderElement) {
            if ($orderElement->getDiscountValue() > 0) {
                return true;
            }
        }

        return false;
    }

    /**
     * @inheritdoc
     */
    public function setDeposit($deposit)
    {
        $this->deposit = $deposit;

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getDeposit()
    {
        return $this->deposit;
    }

    /**
     * @inheritdoc
     */
    public function setEndedAt(\DateTime $endingDate)
    {
        $this->endedAt = $endingDate;

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getEndedAt()
    {
        return $this->endedAt;
    }

    /**
     * @param null $type
     *
     * @return bool
     */
    public function containsOnlyOneType($type = null)
    {
        $wType = false;
        $sameType = true;
        foreach ($this->getOrderElements() as $orderElement) {
            $xType = $orderElement->getProduct()->getValidatedAt();
            if (!$wType) {
                $wType = $xType;
            } elseif ($xType !== $wType) {
                $sameType = false;
            }
        }
        if ($sameType) {
            if (is_numeric($type)) {
                return $type === $wType;
            }

            return true;
        }

        return false;
    }

    /**
     * @inheritdoc
     */
    public function setIsPriceTTC($isPriceTTC)
    {
        $this->isPriceTTC = $isPriceTTC;

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getIsPriceTTC()
    {
        return $this->isPriceTTC;
    }

    /**
     * @inheritdoc
     */
    public function setMailStep($mailStep)
    {
        $this->mailStep = $mailStep;

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getMailStep()
    {
        return $this->mailStep;
    }

    /**
     * @inheritdoc
     */
    public function decreaseStock()
    {
        foreach ($this->getOrderElements() as $orderElement) {
            $orderElement->getProduct()->decreaseStock($orderElement->getQuantity());
        }

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function refundStock()
    {
        foreach ($this->getOrderElements() as $orderElement) {
            $orderElement->getProduct()->refundStock($orderElement->getQuantity());
        }

        return $this;
    }

    public function setPreviousState($state)
    {
        $this->previousState = $state;

        return $this;
    }

    public function getPreviousState()
    {
        return $this->previousState;
    }

    /**
     * alias
     */
    public function getPrice()
    {
        return $this->getTotalPrice();
    }

    /**
     * alias
     *
     * @param $price
     *
     * @return BaseOrderInterface
     */
    public function setPrice($price)
    {
        return $this->setTotalPrice($price);
    }

    public function addDiscountElement(OrderDiscountElement $element)
    {
        $this->discountElements[] = $element;
        $element->setIterator(count($this->discountElements) + 1);
        $element->setOrder($this);

        return $this;
    }

    /**
     * @return ArrayCollection|OrderDiscountElement[]
     */
    public function getDiscountElements()
    {
        return $this->discountElements;
    }

    /**
     * @param OrderDiscountElement $element
     *
     * @return bool
     */
    public function removeDiscountElement(OrderDiscountElement $element)
    {
        return $this->discountElements->removeElement($element);
    }

    /**
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @param string $type
     *
     * @return BaseOrder
     */
    public function setType($type)
    {
        $this->type = $type;

        return $this;
    }

    /**
     * @return array
     */
    public function getOrderRentElements()
    {
        $elements = [];
        foreach ($this->getOrderElements() as $element) {
            if ($element->getProduct() instanceof RentableProduct) {
                $elements[] = $element;
            }
        }

        return $elements;
    }

    public function addOrderRentElement(OrderElement $orderElement)
    {
        return $this->addOrderElement($orderElement);
    }

    public function removeOrderRentElement(OrderElement $orderElement)
    {
        return $this->removeOrderElement($orderElement);
    }

    /**
     * @return ArrayCollection|Shipment[]
     */
    public function getShipments()
    {
        return $this->shipments;
    }

    /**
     * @param Shipment $shipment
     *
     * @return BaseOrder
     */
    public function addShipment(Shipment $shipment)
    {
        $this->shipments[] = $shipment;
        $shipment->setOrder($this);
        if ($this->getShippingMethod()) {
            $shipment->setShippingMethod($this->getShippingMethod());
        }
        $this->mustRecalculShipments = true;

        return $this;
    }

    public function removeShipment(Shipment $shipment)
    {
        $this->shipments->removeElement($shipment);
        $this->mustRecalculShipments = true;
    }

    public function setShipments($shipments)
    {
        $this->shipments = $shipments;
        $this->mustRecalculShipments = true;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getShippingMethod()
    {
        return $this->shippingMethod;
    }

    /**
     * @param mixed $shippingMethod
     *
     * @return BaseOrder
     */
    public function setShippingMethod(ShippingMethod $shippingMethod)
    {
        $this->shippingMethod = $shippingMethod;
        foreach ($this->getShipments() as $shipment) {
            $shipment->setShippingMethod($this->getShippingMethod());
        }
        $this->calculateShippingCost();
        $this->forcePriceCalculation();

        return $this;
    }

    public $justGotInvoice = false;

    //TODO savoir quand on a besoin de valider, est-ce que c'est dès qu'on a un paiement, ou que la commande passe en active?
    public function isElligibleForInvoice()
    {
        if ($this->getInvoiceReference() || !$this->getBillingAddress() || !$this->getShippingAddress() || !$this->getBillingAddress()->getCountry() || !$this->getShippingAddress()->getCountry()) {
            return false;
        }

        $to = [self::STATE_IN_PROGRESS, self::STATE_READY_FOR_SHIPPING, self::STATE_IN_SHIPPING, self::STATE_FINALIZED];

        if (!in_array($this->getState(), $to, true)) {
            return false;
        }

        //TODO gérer paiement

        return true;
    }

    public function mustRecalculShipments()
    {
        $return = $this->mustRecalculShipments;
        if (!$return && count($this->getShipments()) === 0 && count($this->getOrderElements()) > 0) {
            return true;
        }

        return $this->mustRecalculShipments;
    }

    public function recalculShipmentsFinished()
    {
        $this->mustRecalculShipments = false;

        return $this;
    }

    /**
     * @return ArrayCollection|Payment[]
     */
    public function getPayments()
    {
        return $this->payments;
    }

    /**
     * @param Payment $payment
     *
     * @return $this
     */
    public function addPayment(Payment $payment)
    {
        $this->payments->add($payment);
        $payment->setOrder($this);

        return $this;
    }

    /**
     * @param PaymentInterface $payment
     *
     * @return $this
     */
    public function removePayment(PaymentInterface $payment)
    {
        $this->payments->removeElement($payment);

        return $this;
    }

    /**
     * @return string
     */
    public function getPaymentStatus() : string
    {
        return $this->paymentStatus;
    }

    /**
     * @param string $paymentStatus
     *
     * @return BaseOrder
     */
    public function setPaymentStatus(string $paymentStatus) : BaseOrder
    {
        $this->paymentStatus = $paymentStatus;

        return $this;
    }

    /**
     * @return bool
     */
    public function isMustRecalculShipments() : bool
    {
        return $this->mustRecalculShipments;
    }

    /**
     * @param bool $mustRecalculShipments
     *
     * @return BaseOrder
     */
    public function setMustRecalculShipments(bool $mustRecalculShipments) : BaseOrder
    {
        $this->mustRecalculShipments = $mustRecalculShipments;

        return $this;
    }

}


<?php

namespace Gobline\Filter\Validator;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Required extends AbstractValidator
{
    private $value;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if (
            $value === null ||
            (is_string($value) && trim($value) === '')
        ) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        return ['The input is required'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return ['value' => $this->value];
    }
}


<?php

namespace Gobline\Filter\Validator;

use Gobline\Translator\TranslatorInterface;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
abstract class AbstractValidator implements ValidatorInterface
{
    private $messages = [];
    private $messageTemplates = [];
    private $translator;
    protected static $defaultTranslator;

    public function __construct()
    {
        $templates = $this->getMessageTemplates();

        if (!is_array($templates)) {
            $templates = [(string) $templates];
        }

        $this->messageTemplates = $templates;
    }

    /**
     * @param mixed $templateKey
     */
    public function addMessage($templateKey = null)
    {
        $message = $templateKey ?
            $this->messageTemplates[$templateKey] :
            current($this->messageTemplates);

        if (!in_array($message, $this->messages)) {
            $this->messages[] = $message;
        }
    }

    /**
     * @return bool
     */
    public function hasMessages()
    {
        return (bool) $this->messages;
    }

    public function resetMessages()
    {
        $this->messages = [];
    }

    /**
     * {@inheritdoc}
     */
    public function getMessages($language = null)
    {
        if (!$this->messages) {
            return [];
        }

        $messages = [];
        try {
            do {
                $messages[] = $this->getMessage($language);
                next($this->messages);
            } while (current($this->messages));
        } finally {
            reset($this->messages);
        }

        return $messages;
    }

    /**
     * {@inheritdoc}
     */
    public function getMessage($language = null)
    {
        if (!$this->messages) {
            return null;
        }

        $message = $this->translateMessage(current($this->messages), $language);

        $messageVariables = $this->getMessageVariables();
        foreach ($messageVariables as $name => $value) {
            $message = str_replace('%'.$name.'%', $value, $message);
        }

        return $message;
    }

    /**
     * @return string[]
     */
    abstract protected function getMessageTemplates();

    /**
     * @return array
     */
    abstract protected function getMessageVariables();

    /**
     * {@inheritdoc}
     */
    public function setMessageTemplate($message, $key = null)
    {
        $message = (string) $message;

        if ($key === null) {
            $keys = array_keys($this->messageTemplates);
            foreach ($keys as $key) {
                $this->setMessageTemplate($message, $key);
            }

            return $this;
        } else {
            $this->messageTemplates[$key] = $message;
        }
    }

    /**
     * @param string $message
     * @param string $language
     *
     * @throws \InvalidArgumentException
     *
     * @return string
     */
    private function translateMessage($message, $language = null)
    {
        if ($language !== null) {
            $language = (string) $language;
            if ($language === '') {
                throw new \InvalidArgumentException('$language cannot be empty');
            }
        }

        $translator = $this->getTranslator();

        if (!$translator) {
            return $message;
        }

        return $translator->translate($message, null, $language);
    }

    /**
     * @return TranslatorInterface
     */
    public function getTranslator()
    {
        if ($this->translator) {
            return $this->translator;
        }

        if (static::$defaultTranslator) {
            return static::$defaultTranslator;
        }

        return null;
    }

    /**
     * @param TranslatorInterface $translator
     */
    public function setTranslator(TranslatorInterface $translator)
    {
        $this->translator = $translator;
    }

    /**
     * @param TranslatorInterface $translator
     */
    public static function setDefaultTranslator(TranslatorInterface $translator)
    {
        static::$defaultTranslator = $translator;
    }
}