<?php

namespace Ds\Component\Locale\Service;

use Doctrine\Common\Annotations\Reader;
use Ds\Component\Locale\Model\Annotation\Locale;
use Ds\Component\Locale\Model\Type\Localizable;
use ReflectionClass;

/**
 * Class LocaleService
 *
 * @package Ds\Component\Locale
 */
final class LocaleService
{
    /**
     * @var \Doctrine\Common\Annotations\Reader
     */
    private $annotationReader;

    /**
     * Constructor
     *
     * @param \Doctrine\Common\Annotations\Reader $annotationReader
     */
    public function __construct(Reader $annotationReader)
    {
        $this->annotationReader = $annotationReader;
    }

    /**
     * Localize model
     *
     * @param \Ds\Component\Locale\Model\Type\Localizable $model
     * @param string $locale
     */
    public function localize(Localizable $model, string $locale)
    {
        $properties = $this->getProperties($model);

        foreach ($properties as $property) {
            $name = ucfirst($property->getName());
            $values = $model->{'get'.$name}();

            if (array_key_exists($locale, $values)) {
                $model->{'set'.$name}([$locale => $values[$locale]]);
            } else {
                $model->{'set'.$name}([$locale => null]);
            }
        }
    }

    /**
     * Get properties with Locale annotation
     *
     * @param \Ds\Component\Locale\Model\Type\Localizable $model
     * @return array
     */
    public function getProperties(Localizable $model): array
    {
        $class = get_class($model);

        if (substr($class, 0, 15) === 'Proxies\\__CG__\\') {
            $class = substr($class, 15);
        }

        $properties = [];
        $reflection = new ReflectionClass($class);

        foreach ($reflection->getProperties() as $property) {
            $annotation = $this->annotationReader->getPropertyAnnotation($property, Locale::class);

            if (!$annotation) {
                continue;
            }

            $properties[] = $property;
        }

        return $properties;
    }
}


<?php

namespace Ds\Component\Camunda\Service;

use Ds\Component\Api\Service\Service;
use Ds\Component\Camunda\Model\ProcessDefinition;
use Ds\Component\Camunda\Model\Variable;
use Ds\Component\Camunda\Model\Xml;
use Ds\Component\Camunda\Query\ProcessDefinitionParameters as Parameters;
use LogicException;
use SimpleXMLElement;

/**
 * Class ProcessDefinitionService
 *
 * @package Ds\Component\Camunda
 */
final class ProcessDefinitionService implements Service
{
    use Base;

    /**
     * @const string
     */
    const MODEL = ProcessDefinition::class;

    /**
     * @const string
     */
    const RESOURCE_LIST = '/rest/process-definition';
    const RESOURCE_COUNT = '/rest/process-definition/count';
    const RESOURCE_OBJECT = '/rest/process-definition/{id}';
    const RESOURCE_OBJECT_BY_KEY = '/rest/process-definition/key/{key}';
    const RESOURCE_OBJECT_BY_KEY_AND_TENANT_ID = '/rest/process-definition/key/{key}/tenant-id/{tenant-id}';
    const RESOURCE_OBJECT_XML = '/rest/process-definition/{id}/xml';
    const RESOURCE_OBJECT_XML_BY_KEY = '/rest/process-definition/key/{key}/xml';
    const RESOURCE_OBJECT_XML_BY_KEY_AND_TENANT_ID = '/rest/process-definition/key/{key}/xml';
    const RESOURCE_OBJECT_START = '/rest/process-definition/{id}/start';
    const RESOURCE_OBJECT_START_BY_KEY = '/rest/process-definition/key/{key}/start';
    const RESOURCE_OBJECT_START_BY_KEY_AND_TENANT_ID = '/rest/process-definition/key/{key}/tenant-id/{tenant-id}/start';
    const RESOURCE_OBJECT_START_FORM = '/rest/process-definition/{id}/startForm';
    const RESOURCE_OBJECT_START_FORM_BY_KEY = '/rest/process-definition/key/{key}/startForm';
    const RESOURCE_OBJECT_START_FORM_BY_KEY_AND_TENANT_ID = '/rest/process-definition/key/{key}/tenant-id/{tenant-id}/startForm';

    /**
     * @var array
     */
    private static $map = [
        'id',
        'key',
        'name',
        'description',
        'category',
        'resource',
        'deploymentId',
        'diagram',
        'tenantId',
        'version',
        'versionTag'
    ];

    /**
     * Get list of process definitions
     *
     * @param \Ds\Component\Camunda\Query\ProcessDefinitionParameters $parameters
     * @return array
     */
    public function getList(Parameters $parameters = null)
    {
        $options = [
            'headers' => [
                'Accept' => 'application/json'
            ]
        ];
        $objects = $this->execute('GET', static::RESOURCE_LIST, $options);
        $list = [];

        foreach ($objects as $object) {
            $model = static::toModel($object);
            $list[] = $model;
        }

        return $list;
    }

    /**
     * Get count of process definitions
     *
     * @param \Ds\Component\Camunda\Query\ProcessDefinitionParameters $parameters
     * @return integer
     */
    public function getCount(Parameters $parameters = null)
    {
        $options = [
            'headers' => [
                'Accept' => 'application/json'
            ]
        ];
        $result = $this->execute('GET', static::RESOURCE_COUNT, $options);

        return $result->count;
    }

    /**
     * Get process definition
     *
     * @param string $id
     * @param \Ds\Component\Camunda\Query\ProcessDefinitionParameters $parameters
     * @return \Ds\Component\Camunda\Model\ProcessDefinition
     * @throws \LogicException
     */
    public function get($id, Parameters $parameters = null)
    {
        if (null !== $id) {
            $resource = str_replace('{id}', $id, static::RESOURCE_OBJECT);
        } else {
            $key = $parameters->getKey();
            $tenantId = $parameters->getTenantId();

            switch (true) {
                case null !== $key && null !== $tenantId:
                    $resource = str_replace(['{key}', '{tenant-id}'], [$key, $tenantId], static::RESOURCE_OBJECT_BY_KEY_AND_TENANT_ID);
                    break;

                case null !== $key:
                    $resource = str_replace('{key}', $key, static::RESOURCE_OBJECT_BY_KEY);
                    break;

                default:
                    throw new LogicException('"Key" and/or "TenantId" parameters are not defined.');
            }
        }

        $options = [
            'headers' => [
                'Accept' => 'application/json'
            ]
        ];
        $object = $this->execute('GET', $resource, $options);
        $model = static::toModel($object);

        return $model;
    }

    /**
     * Get process definition xml
     *
     * @param string $id
     * @param \Ds\Component\Camunda\Query\ProcessDefinitionParameters $parameters
     * @return string
     * @throws \LogicException
     */
    public function getXml($id, Parameters $parameters = null)
    {
        if (null !== $id) {
            $resource = str_replace('{id}', $id, static::RESOURCE_OBJECT_XML);
        } else {
            $key = $parameters->getKey();
            $tenantId = $parameters->getTenantId();

            switch (true) {
                case null !== $key && null !== $tenantId:
                    $resource = str_replace(['{key}', '{tenant-id}'], [$key, $tenantId], static::RESOURCE_OBJECT_XML_BY_KEY_AND_TENANT_ID);
                    break;

                case null !== $key:
                    $resource = str_replace('{key}', $key, static::RESOURCE_OBJECT_XML_BY_KEY);
                    break;

                default:
                    throw new LogicException('"Key" and/or "TenantId" parameters are not defined.');
            }
        }

        $options = [
            'headers' => [
                'Accept' => 'application/json'
            ]
        ];
        $object = $this->execute('GET', $resource, $options);
        $model = new Xml;
        $model
            ->setId($object->id)
            ->setXml(new SimpleXMLElement($object->bpmn20Xml));

        return $model;
    }

    /**
     * Start a process definition instance
     *
     * @param string $id
     * @param \Ds\Component\Camunda\Query\ProcessDefinitionParameters $parameters
     * @return \Ds\Component\Camunda\Model\ProcessInstance
     */
    public function start($id, Parameters $parameters = null)
    {
        if (null !== $id) {
            $resource = str_replace('{id}', $id, static::RESOURCE_OBJECT_START);
        } else {
            $key = $parameters->getKey();
            $tenantId = $parameters->getTenantId();

            switch (true) {
                case null !== $key && null !== $tenantId:
                    $resource = str_replace(['{key}', '{tenant-id}'], [$key, $tenantId], static::RESOURCE_OBJECT_START_BY_KEY_AND_TENANT_ID);
                    break;

                case null !== $key:
                    $resource = str_replace('{key}', $key, static::RESOURCE_OBJECT_START_BY_KEY);
                    break;

                default:
                    throw new LogicException('"Key" and/or "TenantId" parameters are not defined.');
            }
        }

        $options = [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ]
        ];

        if ($parameters) {
            $parameters = (array) $parameters->toObject(true);

            foreach ($parameters as $name => $value) {
                switch ($name) {
                    case 'variables':
                        foreach ($value as $variable) {
                            $options['json'][$name][$variable->name] = [
                                'value' => Variable::TYPE_JSON === $variable->type ? json_encode($variable->value) : $variable->value,
                                'type' => $variable->type
                            ];
                        }

                        break;

                    case 'key':
                        break;

                    default:
                        $options['json'][$name] = $value;
                }
            }
        }

        $object = $this->execute('POST', $resource, $options);
        $model = ProcessInstanceService::toModel($object);

        return $model;
    }

    /**
     * Get process definition start form
     *
     * @param string $id
     * @param \Ds\Component\Camunda\Query\ProcessDefinitionParameters $parameters
     * @return string
     */
    public function getStartForm($id, Parameters $parameters = null)
    {
        if (null !== $id) {
            $resource = str_replace('{id}', $id, static::RESOURCE_OBJECT_START_FORM);
        } else {
            $key = $parameters->getKey();
            $tenantId = $parameters->getTenantId();

            switch (true) {
                case null !== $key && null !== $tenantId:
                    $resource = str_replace(['{key}', '{tenant-id}'], [$key, $tenantId], static::RESOURCE_OBJECT_START_FORM_BY_KEY_AND_TENANT_ID);
                    break;

                case null !== $key:
                    $resource = str_replace('{key}', $key, static::RESOURCE_OBJECT_START_FORM_BY_KEY);
                    break;

                default:
                    throw new LogicException('"Key" and/or "TenantId" parameters are not defined.');
            }
        }

        $options = [
            'headers' => [
                'Accept' => 'application/json'
            ]
        ];
        $result = $this->execute('GET', $resource, $options);

        return $result->key;
    }
}


<?php

namespace Ds\Component\Camunda\Fixture;

use Doctrine\Common\Persistence\ObjectManager;
use Ds\Component\Api\Api\Api;
use Ds\Component\Camunda\Model\Tenant as TenantModel;
use Ds\Component\Camunda\Query\TenantParameters;
use Ds\Component\Database\Fixture\Yaml;

/**
 * Trait Tenant
 *
 * @package Ds\Component\Camunda
 */
trait Tenant
{
    use Yaml;

    /**
     * @var string
     */
    private $path;

    /**
     * {@inheritdoc}
     */
    public function load(ObjectManager $manager)
    {
        $api = $this->container->get(Api::class)->get('workflow.tenant');
        $parameters = new TenantParameters;
        $tenants = $api->getList($parameters);

        foreach ($tenants as $tenant) {
            $parameters = new TenantParameters;
            $api->delete($tenant->getId(), $parameters);
        }

        $objects = $this->parse($this->path);

        foreach ($objects as $object) {
            $tenant = new TenantModel;
            $tenant->setName($object->name);
            $api->create($tenant);
        }
    }
}


<?php

namespace Ds\Component\Encryption\Service;

use Doctrine\Common\Annotations\Reader;
use Ds\Component\Encryption\Model\Annotation\Encrypt;
use Ds\Component\Encryption\Model\Type\Encryptable;
use ReflectionObject;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

/**
 * Class EncryptionService
 *
 * @package Ds\Component\Encryption
 */
final class EncryptionService
{
    /**
     * @var \Ds\Component\Encryption\Service\CipherService
     */
    private $cipherService;

    /**
     * @var \Doctrine\Common\Annotations\Reader
     */
    private $annotationReader;

    /**
     * @var \Symfony\Component\ExpressionLanguage\ExpressionLanguage
     */
    private $expressionLanguage;

    /**
     * Constructor
     *
     * @param \Ds\Component\Encryption\Service\CipherService $cipherService
     * @param \Doctrine\Common\Annotations\Reader $annotationReader
     */
    public function __construct(CipherService $cipherService, Reader $annotationReader)
    {
        $this->cipherService = $cipherService;
        $this->annotationReader = $annotationReader;
        $this->expressionLanguage = new ExpressionLanguage;
    }

    /**
     * Encrypt model using secret key
     *
     * @param \Ds\Component\Encryption\Model\Type\Encryptable $model
     * @return \Ds\Component\Encryption\Service\EncryptionService
     */
    public function encrypt(Encryptable $model): EncryptionService
    {
        if ($model->getEncrypted()) {
            return $this;
        }

        $properties = $this->getProperties($model);

        foreach ($properties as $property) {
            $property->setAccessible(true);
            $property->setValue($model, $this->cipherService->encrypt($property->getValue($model)));
        }

        $model->setEncrypted(true);

        return $this;
    }

    /**
     * Decrypt model using secret key
     *
     * @param \Ds\Component\Encryption\Model\Type\Encryptable $model
     * @return \Ds\Component\Encryption\Service\EncryptionService
     */
    public function decrypt(Encryptable $model): EncryptionService
    {
        if (!$model->getEncrypted()) {
            return $this;
        }

        $properties = $this->getProperties($model);

        foreach ($properties as $property) {
            $property->setAccessible(true);
            $property->setValue($model, $this->cipherService->decrypt($property->getValue($model)));
        }

        $model->setEncrypted(false);

        return $this;
    }

    /**
     * Get properties with Encrypt annotation
     *
     * @param \Ds\Component\Encryption\Model\Type\Encryptable $model
     * @return \ReflectionProperty[]
     */
    private function getProperties(Encryptable $model): array
    {
        $properties = [];
        $reflection = new ReflectionObject($model);

        foreach ($reflection->getProperties() as $property) {
            $annotation = $this->annotationReader->getPropertyAnnotation($property, Encrypt::class);

            if (!$annotation) {
                continue;
            }

            if (null !== $annotation->applicable && !$this->expressionLanguage->evaluate($annotation->applicable, ['object' => $model])) {
                continue;
            }

            $properties[] = $property;
        }

        return $properties;
    }
}


<?php

namespace Ds\Component\Tenant\EventListener\Token;

use Ds\Component\Tenant\Service\TenantService;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;

/**
 * Class TenantListener
 *
 * @package Ds\Component\Tenant
 */
final class TenantListener
{
    /**
     * @var \Ds\Component\Tenant\Service\TenantService
     */
    private $tenantService;

    /**
     * @var string
     */
    private $attribute;

    /**
     * Constructor
     *
     * @param \Ds\Component\Tenant\Service\TenantService $tenantService
     * @param string $attribute
     */
    public function __construct(TenantService $tenantService, string $attribute = 'tenant')
    {
        $this->tenantService = $tenantService;
        $this->attribute = $attribute;
    }

    /**
     * Add the user tenant to the token
     *
     * @param \Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent $event
     */
    public function created(JWTCreatedEvent $event)
    {
        $payload = $event->getData();
        $user = $event->getUser();
        $payload[$this->attribute] = $user->getTenant();
        $event->setData($payload);
    }

    /**
     * Mark the token as invalid if the user tenant is missing
     *
     * @param \Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent $event
     */
    public function decoded(JWTDecodedEvent $event)
    {
        $payload = $event->getPayload();

        // Make property accessor paths compatible by converting payload to recursive associative array
        $payload = json_decode(json_encode($payload), true);

        if (!array_key_exists($this->attribute, $payload)) {
            $event->markAsInvalid();
        }

        $uuid = $payload[$this->attribute];
        $tenant = $this->tenantService->getRepository()->findBy(['uuid' => $uuid]);

        if (!$tenant) {
            $event->markAsInvalid();
        }
    }
}


<?php

namespace Ds\Component\Discovery\Repository;

use Doctrine\Common\Collections\ArrayCollection;
use Ds\Component\Discovery\Model\Service;
use GuzzleHttp\Exception\ClientException;
use stdClass;

/**
 * Class ServiceRepository
 *
 * @package Ds\Component\Discovery
 */
final class ServiceRepository extends Repository
{
    /**
     * @const string
     */
    const RESOURCE_LIST = '/v1/catalog/services';
    const RESOURCE_OBJECT = '/v1/catalog/service/{id}';

    /**
     * {@inheritdoc}
     */
    public function find($id)
    {
        $resource = 'http://'.$this->host.str_replace('{id}', $id, static::RESOURCE_OBJECT);

        try {
            $response = $this->client->request('GET', $resource, [
                'headers' => [
                    'X-Consul-Token' => $this->token
                ]
            ]);
        } catch (ClientException $exception) {
            return null;
        }

        $body = (string) $response->getBody();
        $objects = \GuzzleHttp\json_decode($body);

        if (!$objects) {
            return null;
        }

        $model = $this->toModel($objects[0]);

        return $model;
    }

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

    }

    /**
     * {@inheritdoc}
     */
    public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
    {
        $resource = 'http://'.$this->host.static::RESOURCE_LIST;

        try {
            $response = $this->client->request('GET', $resource, [
                'headers' => [
                    'X-Consul-Token' => $this->token
                ]
            ]);
        } catch (ClientException $exception) {
            return null;
        }

        $body = (string) $response->getBody();
        $objects = \GuzzleHttp\json_decode($body);
        $models = new ArrayCollection;

        if ($objects) {
            foreach ($objects as $id => $tags) {
                if (array_key_exists('id', $criteria) && !preg_match($criteria['id'], $id)) {
                    continue;
                }

                if (array_key_exists('tag', $criteria) && !in_array($criteria['tag'], $tags)) {
                    continue;
                }

                $response = $this->client->request('GET', 'http://'.$this->host.'/v1/catalog/service/'.$id, [
                    'headers' => [
                        'X-Consul-Token' => $this->token
                    ]
                ]);
                $body = (string) $response->getBody();
                $objects = \GuzzleHttp\json_decode($body);

                if (!$objects) {
                    continue;
                }

                $models->add($this->toModel($objects[0]));
            }
        }

        return $models;
    }

    /**
     * {@inheritdoc}
     */
    public function findOneBy(array $criteria)
    {

    }

    /**
     * {@inheritdoc}
     */
    public function getClassName()
    {
        return Service::class;
    }

    /**
     * Type cast object to model
     *
     * @param \stdClass $object
     * @return \Ds\Component\Discovery\Model\Service
     */
    protected function toModel(stdClass $object)
    {
        $class = $this->getClassName();
        $model = new $class;
        $model
            ->setId($object->ServiceID)
            ->setIp($object->ServiceAddress)
            ->setPort($object->ServicePort)
            ->setMeta((array) $object->ServiceMeta)
            ->setTags($object->ServiceTags);

        return $model;
    }
}


<?php

namespace Ds\Component\Security\EventListener\Token;

use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\PropertyAccess\PropertyAccess;

/**
 * Class ClientListener
 *
 * @package Ds\Component\Security
 */
final class ClientListener
{
    /**
     * @var \Symfony\Component\PropertyAccess\PropertyAccessor
     */
    private $accessor;

    /**
     * @var \Symfony\Component\HttpFoundation\RequestStack
     */
    private $requestStack;

    /**
     * @var boolean
     */
    private $validate;

    /**
     * @var string
     */
    private $property;

    /**
     * @var integer
     */
    private $length;

    /**
     * Constructor
     *
     * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
     * @param boolean $validate
     * @param string $property
     * @param integer $length
     */
    public function __construct(RequestStack $requestStack, bool $validate = true, string $property = '[client]', int $length = 8)
    {
        $this->accessor = PropertyAccess::createPropertyAccessor();
        $this->requestStack = $requestStack;
        $this->validate = $validate;
        $this->property = $property;
        $this->length = $length;
    }

    /**
     * Add the client signature to the token
     *
     * @param \Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent $event
     */
    public function created(JWTCreatedEvent $event)
    {
        $data = $event->getData();
        $this->accessor->setValue($data, $this->property, $this->getSignature());
        $event->setData($data);
    }

    /**
     * Mark the token as invalid if the client signature is missing or is not matching
     *
     * @param \Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent $event
     */
    public function decoded(JWTDecodedEvent $event)
    {
        $payload = $event->getPayload();

        // Make property accessor paths compatible by converting payload to recursive associative array
        $payload = json_decode(json_encode($payload), true);

        if (!$this->accessor->isReadable($payload, $this->property)) {
            $event->markAsInvalid();
        } elseif ($this->validate && $this->accessor->getValue($payload, $this->property) !== $this->getSignature()) {
            $event->markAsInvalid();
        }
    }

    /**
     * Get the client signature
     *
     * @return string
     */
    protected function getSignature(): string
    {
        $request = $this->requestStack->getCurrentRequest();
        $signature = substr(md5($request->server->get('HTTP_USER_AGENT')), 0, $this->length);

        return $signature;
    }
}


<?php

namespace CommerceGuys\Zone\Model;

use CommerceGuys\Addressing\AddressInterface;

/**
 * Matches the EU zone.
 *
 * For performance reasons the list of EU countries is hardcoded, avoiding
 * the need to create and call 28 ZoneMemberCountry instances.
 */
class ZoneMemberEu extends ZoneMember
{
    /**
     * Creates a ZoneMemberEu instance.
     */
    public function __construct()
    {
        $this->name = 'EU';
    }

    /**
     * {@inheritdoc}
     */
    public function match(AddressInterface $address)
    {
        $euCountries = [
            'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI',
            'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV',
            'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK',
        ];
        $countryCode = $address->getCountryCode();

        return in_array($countryCode, $euCountries);
    }
}


<?php

namespace Ds\Component\Api\Query\Attribute;

/**
 * Trait StaffUuid
 *
 * @package Ds\Component\Api
 */
trait StaffUuid
{
    /**
     * @var string
     */
    private $staffUuid; # region accessors

    /**
     * Set staff uuid
     *
     * @param string $staffUuid
     * @return object
     */
    public function setStaffUuid(?string $staffUuid)
    {
        $this->staffUuid = $staffUuid;
        $this->_staffUuid = true;

        return $this;
    }

    /**
     * Get staff uuid
     *
     * @return string
     */
    public function getStaffUuid(): ?string
    {
        return $this->staffUuid;
    }

    # endregion

    /**
     * @var boolean
     */
    private $_staffUuid;
}


<?php

namespace Ds\Component\Camunda\Query\Attribute;

/**
 * Trait MaxResults
 *
 * @package Ds\Component\Camunda
 */
trait MaxResults
{
    /**
     * @var integer
     */
    private $maxResults; # region accessors

    /**
     * Set max results
     *
     * @param integer $maxResults
     * @return object
     */
    public function setMaxResults(?int $maxResults)
    {
        $this->maxResults = $maxResults;
        $this->_maxResults = null !== $maxResults;

        return $this;
    }

    /**
     * Get max results
     *
     * @return integer
     */
    public function getMaxResults(): ?int
    {
        return $this->maxResults;
    }

    # endregion

    /**
     * @var boolean
     */
    private $_maxResults;
}


<?php

namespace Ds\Component\Formio\Service;

use Ds\Component\Api\Service\Service;
use Ds\Component\Formio\Model\Form;
use Ds\Component\Formio\Query\FormParameters as Parameters;
use stdClass;
use DateTime;

/**
 * Class FormService
 *
 * @package Ds\Component\Formio
 */
final class FormService implements Service
{
    use Base;

    /**
     * @const string
     */
    const MODEL = Form::class;

    /**
     * @const string
     */
    const RESOURCE_LIST = '/form';
    const RESOURCE_OBJECT = '/form/{id}';
    const RESOURCE_OBJECT_BY_PATH = '/{path}';

    /**
     * @var array
     */
    private static $map = [
        'id' => '_id',
        'updated' => 'modified',
        'created',
        'title',
        'display',
        'type',
        'name',
        'path',
        'components',
        'tags',
        'access',
        'owner',
        'submissionAccess'
    ];

    /**
     * Get form list
     *
     * @param \Ds\Component\Formio\Query\FormParameters $parameters
     * @return array
     */
    public function getList(Parameters $parameters = null): array
    {
        $objects = $this->execute('GET', static::RESOURCE_LIST);
        $list = [];

        foreach ($objects as $object) {
            $model = static::toModel($object);
            $list[] = $model;
        }

        return $list;
    }

    /**
     * Get form
     *
     * @param string $id
     * @param \Ds\Component\Formio\Query\FormParameters $parameters
     * @return \Ds\Component\Formio\Model\Form
     */
    public function get(?string $id, Parameters $parameters = null): Form
    {
        if (null !== $id) {
            $resource = str_replace('{id}', $id, static::RESOURCE_OBJECT);
        } else {
            $resource = str_replace('{path}', $parameters->getPath(), static::RESOURCE_OBJECT_BY_PATH);
        }

        $options = [
            'headers' => [
                'Accept' => 'application/json'
            ]
        ];

        $object = $this->execute('GET', $resource, $options);
        /** @var \Ds\Component\Formio\Model\Form $model */
        $model = static::toModel($object);

        return $model;
    }

    /**
     * Create form
     *
     * @param \Ds\Component\Formio\Model\Form $form
     * @param \Ds\Component\Formio\Query\FormParameters $parameters
     * @return \Ds\Component\Formio\Model\Form
     */
    public function create(Form $form, Parameters $parameters = null): Form
    {
        $options = [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ],
            'json' => [
                'title' => $form->getTitle(),
                'display' => $form->getDisplay(),
                'type' => $form->getType(),
                'name' => $form->getName(),
                'path' => $form->getPath(),
                'tags' => $form->getTags(),
                'components' => $form->getComponents(),
                'submissionAccess' => $form->getSubmissionAccess()
            ]
        ];

        $object = $this->execute('POST', static::RESOURCE_LIST, $options);
        /** @var \Ds\Component\Formio\Model\Form $model */
        $model = static::toModel($object);

        return $model;
    }

    /**
     * Update form
     *
     * @param \Ds\Component\Formio\Model\Form $form
     * @param \Ds\Component\Formio\Query\FormParameters $parameters
     * @return \Ds\Component\Formio\Model\Form
     */
    public function update(Form $form, Parameters $parameters = null): Form
    {
        $options = [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ],
            'json' => [
                'title' => $form->getTitle(),
                'display' => $form->getDisplay(),
                'type' => $form->getType(),
                'name' => $form->getName(),
                'path' => $form->getPath(),
                'tags' => $form->getTags(),
                'components' => $form->getComponents(),
                'submissionAccess' => $form->getSubmissionAccess()
            ]
        ];

        $resource = str_replace('{path}', $form->getPath(), static::RESOURCE_OBJECT_BY_PATH);
        $object = $this->execute('PUT', $resource, $options);
        /** @var \Ds\Component\Formio\Model\Form $model */
        $model = static::toModel($object);

        return $model;
    }

    /**
     * Delete form
     *
     * @param string $path
     * @param \Ds\Component\Formio\Query\FormParameters $parameters
     */
    public function delete($path, Parameters $parameters = null)
    {
        $resource = str_replace('{path}', $path, static::RESOURCE_OBJECT_BY_PATH);
        $this->execute('DELETE', $resource);
    }
}


<?php

namespace Ds\Component\Identity\Voter;

use Ds\Component\Security\Model\Identity;
use Ds\Component\Model\Type\Deletable;
use Ds\Component\Security\Model\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;

/**
 * Class DeletedVoter
 *
 * @package Ds\Component\Identity
 */
final class DeletedVoter extends Voter
{
    /**
     * {@inheritdoc}
     */
    protected function supports($attribute, $subject)
    {
        if (!$subject instanceof Deletable) {
            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        $user = $token->getUser();

        if ($user instanceof \Symfony\Component\Security\Core\User\User && in_array('ROLE_SYSTEM', $user->getRoles())) {
            return true;
        }

        if (!$user instanceof User) {
            return false;
        }

        if (in_array($user->getIdentity()->getType(), [Identity::SYSTEM, Identity::STAFF], true)) {
            return true;
        }

        if (!$subject->getDeleted()) {
            return true;
        }

        return false;
    }
}


<?php

namespace Ds\Component\Encryption\EventListener\Entity\Encryptable;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Ds\Component\Encryption\Model\Type\Encryptable;

/**
 * Class PostUpdateListener
 *
 * @package Ds\Component\Encryption
 */
final class PostUpdateListener extends AbstractListener
{
    /**
     * Decrypt encryptable entity properties after saving in storage.
     *
     * @param \Doctrine\ORM\Event\LifecycleEventArgs $args
     */
    public function postUpdate(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        if (!$entity instanceof Encryptable) {
            return;
        }

        $this->encryptionService->decrypt($entity);
    }
}


<?php

namespace Ds\Component\Camunda\Query\Attribute;

/**
 * Trait WithVariablesInReturn
 *
 * @package Ds\Component\Camunda
 */
trait WithVariablesInReturn
{
    /**
     * @var string
     */
    private $withVariablesInReturn; # region accessors

    /**
     * Set with variables in return
     *
     * @param boolean $withVariablesInReturn
     * @return object
     */
    public function setWithVariablesInReturn(?bool $withVariablesInReturn)
    {
        $this->withVariablesInReturn = $withVariablesInReturn;
        $this->_withVariablesInReturn = null !== $withVariablesInReturn;

        return $this;
    }

    /**
     * Get with variables in return
     *
     * @return boolean
     */
    public function getWithVariablesInReturn(): ?bool
    {
        return $this->withVariablesInReturn;
    }

    # endregion

    /**
     * @var boolean
     */
    private $_withVariablesInReturn;
}


<?php

namespace Ds\Component\Api\Resolver;

use Ds\Component\Api\Api\Api;
use Ds\Component\Resolver\Exception\UnresolvedException;
use Ds\Component\Resolver\Resolver\Resolver;
use Exception;
use OutOfRangeException;
use Symfony\Component\PropertyAccess\PropertyAccess;

/**
 * Class ApiResolver
 *
 * @package Ds\Component\Api
 */
final class ApiResolver implements Resolver
{
    /**
     * @const string
     */
    const PATTERN = '/^ds\.([_a-zA-Z0-9]+)\.([_a-zA-Z0-9]+)\[([-a-zA-Z0-9]+)\]\.(.+)/';

    /**
     * @var \Ds\Component\Api\Api\Api
     */
    private $api;

    /**
     * Constructor
     *
     * @param \Ds\Component\Api\Api\Api $api
     */
    public function __construct(Api $api)
    {
        $this->api = $api;
    }

    /**
     * {@inheritdoc}
     */
    public function isMatch(string $variable, array &$matches = []): bool
    {
        if (!preg_match(static::PATTERN, $variable, $matches)) {
            return false;
        }

        $service = $matches[1];
        $resource = $matches[2];

        try {
            $this->api->get($service.'.'.$resource);
        } catch (OutOfRangeException $exception) {
            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function resolve(string $variable)
    {
        $matches = [];

        if (!$this->isMatch($variable, $matches)) {
            throw new UnresolvedException('Variable pattern "'.$variable.'" is not valid.');
        }

        $service = $matches[1];
        $resource = $matches[2];
        $id = $matches[3];
        $property = $matches[4];
        $model = $this->api->get($service.'.'.$resource)->get($id);
        $accessor = PropertyAccess::createPropertyAccessor();

        try {
            $value = $accessor->getValue($model, $property);
        } catch (Exception $exception) {
            throw new UnresolvedException('Variable pattern "'.$variable.'" is not valid.', 0, $exception);
        }

        return $value;
    }
}


<?php

namespace Ds\Component\Formio\Service;

use Ds\Component\Api\Service\Service;
use Ds\Component\Formio\Model\Submission;
use Ds\Component\Formio\Query\SubmissionParameters as Parameters;
use Ds\Component\Formio\Exception\ValidationException;
use GuzzleHttp\Exception\ClientException;

/**
 * Class SubmissionService
 *
 * @package Ds\Component\Formio
 */
final class SubmissionService implements Service
{
    use Base;

    /**
     * @const string
     */
    const MODEL = Submission::class;

    /**
     * @const string
     */
    const RESOURCE_LIST = '/{form}/submission';
    const RESOURCE_OBJECT = '/{form}/submission/{id}';
    const RESOURCE_OBJECT_EXISTS = '/{form}/exists';

    /**
     * @var array
     */
    private static $map = [
        'id' => '_id',
        'updated' => 'modified',
        'created',
        'form',
        'data',
        'externalIds',
        'access',
        'roles',
        'owner'
    ];

    /**
     * Get submissions list
     *
     * @param string $form
     * @param \Ds\Component\Formio\Query\SubmissionParameters $parameters
     * @return array
     */
    public function getList($form, Parameters $parameters = null): array
    {
        $objects = $this->execute('GET', 'http://www.mocky.io/v2/592c3c86110000f8016df7de');
        $list = [];

        foreach ($objects as $object) {
            $model = static::toModel($object);
            $list[] = $model;
        }

        return $list;
    }

    /**
     * Get submission
     *
     * @param string $form
     * @param string $id
     * @return \Ds\Component\Formio\Model\Submission
     */
    public function get($form, $id): Submission
    {
        $object = $this->execute('GET', 'http://www.mocky.io/v2/592c4353110000e3026df7f8');
        /** @var \Ds\Component\Formio\Model\Submission $model */
        $model = static::toModel($object);

        return $model;
    }

    /**
     * Check if a submission exists
     *
     * @param string $form
     * @param \Ds\Component\Formio\Query\SubmissionParameters $parameters
     * @return boolean
     */
    public function exists($form, Parameters $parameters = null): bool
    {
        $object = $this->execute('GET', 'http://www.mocky.io/v2/592c6f7311000029066df850');

        if ($object && property_exists($object, '_id') && $object->_id) {
            return true;
        }

        return false;
    }

    /**
     * Create submission
     *
     * @param \Ds\Component\Formio\Model\Submission $submission
     * @param \Ds\Component\Formio\Query\SubmissionParameters $parameters
     * @return \Ds\Component\Formio\Model\Submission
     * @throws \Ds\Component\Formio\Exception\ValidationException
     */
    public function create(Submission $submission, Parameters $parameters = null): Submission
    {
        $resource = str_replace('{form}', $submission->getForm(), static::RESOURCE_LIST);
        $options = [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ],
            'json' => [
                'data' => (array) $submission->getData()
            ]
        ];

        if ($parameters) {
            $options['query'] = (array) $parameters->toObject(true);
        }

        $object = $this->execute('POST', $resource, $options);

        if (!is_bool($object)) {
            $submission = static::toModel($object);
        }

        return $submission;
    }

    /**
     * Update submission
     *
     * @param \Ds\Component\Formio\Model\Submission $submission
     */
    public function update(Submission $submission)
    {

    }

    /**
     * Delete submission
     *
     * @param string $id
     */
    public function delete(string $id)
    {

    }
}


<?php

namespace Ds\Component\Translation\Validator;

use Ds\Component\Translation\Model\Type\Translatable;
use Ds\Component\Translation\Service\TranslationService;
use LogicException;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
 * Class ValidValidator
 *
 * @package Ds\Component\Translation
 */
final class ValidValidator extends ConstraintValidator
{
    /**
     * @var \Ds\Component\Translation\Service\TranslationService
     */
    private $translationService;

    /**
     * Constructor
     *
     * @param \Ds\Component\Translation\Service\TranslationService $translationService
     */
    public function __construct(TranslationService $translationService)
    {
        $this->translationService = $translationService;
    }

    /**
     * Validate value
     *
     * @param mixed $value
     * @param \Symfony\Component\Validator\Constraint $constraint
     */
    public function validate($value, Constraint $constraint)
    {
        if (!$value instanceof Translatable) {
            throw new LogicException('Value is not translatable while trying to validate translations.');
        }

        $locales = [];

        foreach ($value->getTranslations() as $translation) {
            $locales[] = $translation->getLocale();
        }

//        $this->context
//            ->buildViolation($constraint->getMessage())
//            ->atPath('')
//            ->addViolation();
    }
}


<?php

namespace Ds\Component\Api\Service;

use DateTime;
use Ds\Component\Api\Model\Model;
use GuzzleHttp\Client;
use InvalidArgumentException;
use LogicException;
use stdClass;
use UnexpectedValueException;

/**
 * Trait Base
 *
 * @package Ds\Component\Api
 */
trait Base
{
    /**
     * Cast object to model
     *
     * @param \stdClass $object
     * @return \Ds\Component\Api\Model\Model
     * @throws \LogicException
     */
    public static function toModel(stdClass $object)
    {
        if (null === static::MODEL) {
            throw new LogicException('Model class is not defined.');
        }

        $class = static::MODEL;
        $model = new $class;

        foreach (static::$map as $local => $remote) {
            if (is_int($local)) {
                $local = $remote;
            }

            if (property_exists($object, $remote)) {
                switch ($local) {
                    case 'createdAt':
                    case 'updatedAt':
                    case 'lastLogin':
                        $model->{'set'.ucfirst($local)}(new DateTime($object->$remote));
                        break;

                    case 'data':
                    case 'title':
                        $model->{'set'.ucfirst($local)}((array) $object->$remote);
                        break;

                    case 'individual':
                        // @todo reverse load for relationships
                        break;

                    case 'organization':
                        // @todo reverse load for relationships
                        break;

                    case 'permissions':
                        // @todo reverse load for relationships
                        break;

                    default:
                        $model->{'set'.ucfirst($local)}($object->$remote);
                }
            }
        }

        return $model;
    }

    /**
     * Cast model to object
     *
     * @param \Ds\Component\Api\Model\Model $model
     * @return stdClass
     * @throws \LogicException
     */
    public static function toObject(Model $model)
    {
        if (null === static::MODEL) {
            throw new LogicException('Model class is not defined.');
        }

        $object = new stdClass;

        foreach (static::$map as $local => $remote) {
            if (is_int($local)) {
                $local = $remote;
            }

            $value = $model->{'get'.ucfirst($local)}();

            switch ($local) {
                case 'createdAt':
                case 'updatedAt':
                    if ($value) {
                        $object->$remote = $value->format('Y-m-d\TH:i:sP');
                    }

                    break;

                case 'lastLogin':
                    $object->$remote = null;

                    if ($value) {
                        $object->$remote = $value->format('Y-m-d\TH:i:sP');
                    }

                    break;

                case 'individual':
                    $object->$remote = '/individuals/'.$value->getUuid();
                    break;

                case 'organization':
                    $object->$remote = '/organizations/'.$value->getUuid();
                    break;

                case 'permissions':
                    $object->$remote = [];

                    foreach ($value as $permission) {
                        $object->$remote[] = PermissionService::toObject($permission);
                    }

                    break;

                default:
                    $object->$remote = $value;
            }
        }

        return $object;
    }

    /**
     * @var \GuzzleHttp\ClientInterface
     */
    private $client;

    /**
     * @var string
     */
    private $host; # region accessors

    /**
     * {@inheritdoc}
     */
    public function setHost(string $host)
    {
        $this->host = $host;

        return $this;
    }

    # endregion

    /**
     * @var array
     */
    private $headers; # region accessors

    /**
     * {@inheritdoc}
     */
    public function setHeaders(array $headers)
    {
        $this->headers = $headers;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setHeader(string $name, string $value)
    {
        $this->headers[$name] = $value;

        return $this;
    }

    # endregion

    /**
     * Constructor
     *
     * @param string $host
     * @param array $headers
     */
    public function __construct(string $host = null, array $headers = [])
    {
        $this->client = new Client;
        $this->host = $host;

        if (!isset($headers['Content-Type'])) {
            $headers['Content-Type'] = 'application/json';
        }

        if (!isset($headers['Accept'])) {
            $headers['Accept'] = 'application/json';
        }

        $this->headers = $headers;
    }

    /**
     * Execute api request
     *
     * @param string $method
     * @param string $resource
     * @param array $options
     * @return mixed
     */
    protected function execute($method, $resource, array $options = [])
    {
        $uri = $this->host.$resource;

        foreach ($this->headers as $key => $value) {
            if (!isset($options['headers'][$key])) {
                $options['headers'][$key] = $value;
            }
        }

        $response = $this->client->request($method, $uri, $options);
        $data = (string) $response->getBody();

        if ('' !== $data) {
            try {
                $data = \GuzzleHttp\json_decode($data);
            } catch (InvalidArgumentException $exception) {
                throw new UnexpectedValueException('Service response is not valid.', 0, $exception);
            }
        }

        return $data;
    }
}


<?php

namespace Ds\Component\Model\Attribute\Accessor;

use InvalidArgumentException;

/**
 * Trait OwnerUuid
 *
 * @package Ds\Component\Model
 */
trait OwnerUuid
{
    /**
     * Set owner uuid
     *
     * @param string $ownerUuid
     * @return object
     * @throws \InvalidArgumentException
     */
    public function setOwnerUuid(?string $ownerUuid)
    {
        if (null !== $ownerUuid) {
            if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $ownerUuid)) {
                throw new InvalidArgumentException('Owner uuid is not valid.');
            }
        }

        $this->ownerUuid = $ownerUuid;

        return $this;
    }

    /**
     * Get owner uuid
     *
     * @return string
     */
    public function getOwnerUuid(): ?string
    {
        return $this->ownerUuid;
    }
}


<?php

namespace Ds\Component\Camunda\Service;

use Ds\Component\Api\Service\Service;
use Ds\Component\Camunda\Model\Task;
use Ds\Component\Camunda\Model\Variable;
use Ds\Component\Camunda\Query\TaskParameters as Parameters;
use InvalidArgumentException;
use stdClass;

/**
 * Class TaskService
 *
 * @package Ds\Component\Camunda
 */
final class TaskService implements Service
{
    use Base {
        toModel as protected baseToModel;
    }

    /**
     * @const string
     */
    const MODEL = Task::class;

    /**
     * @const string
     */
    const RESOURCE_LIST = '/rest/task';
    const RESOURCE_COUNT = '/rest/task/count';
    const RESOURCE_OBJECT = '/rest/task/{id}';
    const RESOURCE_SUBMIT = '/rest/task/{id}/submit-form';
    const RESOURCE_CLAIM = '/rest/task/{id}/claim';
    const RESOURCE_UNCLAIM = '/rest/task/{id}/unclaim';

    /**
     * @var array
     */
    private static $map = [
        'id',
        'name',
        'assignee',
        'created',
        'due',
        'followUp',
        'delegationState',
        'description',
        'executionId',
        'owner',
        'parentTaskId',
        'priority',
        'processDefinitionId',
        'processInstanceId',
        'caseExecutionId',
        'caseDefinitionId',
        'caseInstanceId',
        'taskDefinitionKey',
        'formKey',
        'tenantId'
    ];

    /**
     * @var \Ds\Component\Camunda\Service\Task\VariableService
     */
    public $variable;

    /**
     * Get task list
     *
     * @param \Ds\Component\Camunda\Query\TaskParameters $parameters
     * @return array
     */
    public function getList(Parameters $parameters = null)
    {
        $options = [
            'headers' => [
                'Accept' => 'application/json'
            ],
            'query' => (array)  $parameters->toObject(true)
        ];
        $objects = $this->execute('GET', static::RESOURCE_LIST, $options);
        $list = [];

        foreach ($objects as $object) {
            $model = static::toModel($object);
            $list[] = $model;
        }

        return $list;
    }

    /**
     * Get count
     *
     * @param \Ds\Component\Camunda\Query\TaskParameters $parameters
     * @return integer
     */
    public function getCount(Parameters $parameters = null)
    {
        $options = [
            'headers' => [
                'Accept' => 'application/json'
            ]
        ];
        $result = $this->execute('GET', static::RESOURCE_COUNT, $options);

        return $result->count;
    }

    /**
     * Get task
     *
     * @param string $id
     * @return \Ds\Component\Camunda\Model\Task
     */
    public function get($id)
    {
        $resource = str_replace('{id}', $id, static::RESOURCE_OBJECT);
        $options = [
            'headers' => [
                'Accept' => 'application/hal+json'
            ]
        ];
        $object = $this->execute('GET', $resource, $options);
        $model = static::toModel($object);

        return $model;
    }

    /**
     * Submit task data
     *
     * @param string $id
     * @param array $variables
     * @throws \InvalidArgumentException
     */
    public function submit($id, array $variables)
    {
        foreach ($variables as $variable) {
            if (!$variable instanceof Variable) {
                throw new InvalidArgumentException('Array of variables is not valid.');
            }
        }

        $resource = str_replace('{id}', $id, static::RESOURCE_SUBMIT);
        $options = [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ]
        ];

        foreach ($variables as $variable) {
            // @todo Standardize variable toObject logic (see ProcessInstanceService::start)
            $options['json']['variables'][$variable->getName()] = [
                'value' => Variable::TYPE_JSON === $variable->getType() ? json_encode($variable->getValue()) : $variable->getValue(),
                'type' => $variable->getType()
            ];
        }

        $this->execute('POST', $resource, $options);
    }

    /**
     * Claim task
     *
     * @param string $id
     * @param string $userId
     */
    public function claim($id, $userId)
    {
        $resource = str_replace('{id}', $id, static::RESOURCE_CLAIM);
        $options = [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ],
            'json' => [
                'userId' => $userId
            ]
        ];
        $this->execute('POST', $resource, $options);
    }

    /**
     * Unclaim task
     *
     * @param string $id
     */
    public function unclaim($id)
    {
        $options = [
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ]
        ];
        $resource = str_replace('{id}', $id, static::RESOURCE_UNCLAIM);
        $this->execute('POST', $resource, $options);
    }

    /**
     * Cast object to model
     *
     * @param \stdClass $object
     * @return \Ds\Component\Camunda\Model\Model
     * @throws \LogicException
     */
    public static function toModel(stdClass $object)
    {
        $model = static::baseToModel($object);

        // @todo Parse everything that is embedded
        if (isset($object->_embedded->identityLink)) {
            foreach ($object->_embedded->identityLink as $identity) {
                if ('candidate' === $identity->type) {
                    $model->setCandidateGroup($identity->groupId);
                }
            }
        }

        return $model;
    }
}


<?php

namespace Ds\Component\Database\Util;

use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Yaml\Yaml;

/**
 * Class Parameters
 *
 * @package Ds\Component\Database
 */
final class Parameters
{
    /**
     * Parse parameters string to array
     *
     * @param string $string
     * @return array
     */
    public static function parse(string $string): array
    {
        $array = (array) Yaml::parse($string, Yaml::PARSE_OBJECT_FOR_MAP);

        return $array;
    }

    /**
     * Parse parameters file to array
     *
     * @param string $file
     * @return array
     */
    public static function parseFile(string $file): array
    {
        $string = file_get_contents($file);

        return static::parse($string);
    }

    /**
     * Replace parameter placeholders with data
     *
     * @param string $string
     * @param array $data
     * @return string
     */
    public static function replace(string $string, array $data = []): string
    {
        $expressionLanguage = new ExpressionLanguage;
        preg_match_all('/\%([a-z0-9_\[\]\"\.]+)\%/i', $string, $matches);
        $placeholders = array_unique($matches[1]);
        $translations = [];

        foreach ($placeholders as $placeholder) {
            $translations['%'.$placeholder.'%'] = $expressionLanguage->evaluate($placeholder, $data);
        }

        $string = strtr($string, $translations);

        return $string;
    }
}


<?php

namespace Ds\Component\Parameter\EventListener\Entity\Parameter;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreFlushEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Ds\Component\Parameter\Collection\ParameterCollection;
use Ds\Component\Parameter\Entity\Parameter;

/**
 * Class EncryptListener
 *
 * @package Ds\Component\Parameter
 */
final class EncryptListener
{
    /**
     * @var \Ds\Component\Parameter\Collection\ParameterCollection
     */
    private $parameterCollection;

    /**
     * Constructor
     *
     * @param \Ds\Component\Parameter\Collection\ParameterCollection $parameterCollection
     */
    public function __construct(ParameterCollection $parameterCollection)
    {
        $this->parameterCollection = $parameterCollection;
    }

    /**
     * Set parameter entity encrypt property after retrieving them from storage.
     *
     * @param \Doctrine\ORM\Event\LifecycleEventArgs $args
     */
    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        if (!$entity instanceof Parameter) {
            return;
        }

        $key = $entity->getKey();
        $encrypt = $this->parameterCollection->get($key)['encrypt'];
        $entity->setEncrypt($encrypt);
    }

    /**
     * Set parameter entity encrypt property before saving in storage.
     *
     * @param \Doctrine\ORM\Event\PreUpdateEventArgs $args
     */
    public function preUpdate(PreUpdateEventArgs $args)
    {
        $entity = $args->getEntity();

        if (!$entity instanceof Parameter) {
            return;
        }

        $key = $entity->getKey();
        $encrypt = $this->parameterCollection->get($key)['encrypt'];
        $entity->setEncrypt($encrypt);
    }

    /**
     * Set parameter entity encrypt property before saving in storage.
     *
     * @param \Doctrine\ORM\Event\PreFlushEventArgs $args
     */
    public function preFlush(PreFlushEventArgs $args)
    {
        $entities = $args->getEntityManager()->getUnitOfWork()->getScheduledEntityInsertions();

        foreach ($entities as $entity) {
            if (!$entity instanceof Parameter) {
                continue;
            }

            $key = $entity->getKey();
            $encrypt = $this->parameterCollection->get($key)['encrypt'];
            $entity->setEncrypt($encrypt);
        }
    }
}


<?php

namespace Ds\Component\Acl\Service;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Ds\Component\Acl\Entity\Access;
use Ds\Component\Entity\Service\EntityService;
use Ds\Component\Security\Model\User;

/**
 * Class AccessService
 *
 * @package Ds\Component\Acl
 */
final class AccessService extends EntityService
{
    /**
     * Constructor
     *
     * @param \Doctrine\ORM\EntityManagerInterface $manager
     * @param string $entity
     */
    public function __construct(EntityManagerInterface $manager, string $entity = Access::class)
    {
        parent::__construct($manager, $entity);
    }

    /**
     * Get user compiled permissions
     *
     * @param \Ds\Component\Security\Model\User $user
     * @return ArrayCollection
     */
    public function getPermissions(User $user)
    {
        $permissions = new ArrayCollection;

        // Generic identity permissions
        $accesses = $this->repository->findBy([
            'assignee' => $user->getIdentity()->getType(),
            'assigneeUuid' => null
        ]);

        foreach ($accesses as $access) {
            foreach ($access->getPermissions() as $permission) {
                $permissions->add($permission);
            }
        }

        // Specific identity permissions
        $accesses = $this->repository->findBy([
            'assignee' => $user->getIdentity()->getType(),
            'assigneeUuid' => $user->getIdentity()->getUuid()
        ]);

        foreach ($accesses as $access) {
            foreach ($access->getPermissions() as $permission) {
                $permissions->add($permission);
            }
        }

        // Roles permissions
        $accesses = $this->repository->findBy([
            'assignee' => 'Role',
            'assigneeUuid' => $user->getIdentity()->getRoles()
        ]);

        foreach ($accesses as $access) {
            foreach ($access->getPermissions() as $permission) {
                $permissions->add($permission);
            }
        }

        return $permissions;
    }
}


<?php

namespace Ds\Component\Api\Query;

use stdClass;

/**
 * Trait Base
 *
 * @package Ds\Component\Api
 */
trait Base
{
    /**
     * {@inheritdoc}
     */
    public function toObject(bool $minimal = false)
    {
        $object = new stdClass;

        foreach ($this as $key => $value) {
            if ('_' === substr($key, 0, 1)) {
                continue;
            }

            if ($minimal && !$this->{'_'.$key}) {
                continue;
            }

            $object->$key = $value;
        }

        return $object;
    }
}


<?php

namespace Ds\Component\Parameter\Command\Parameter;

use Ds\Component\Parameter\Service\ParameterService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class GetCommand
 *
 * @package Ds\Component\Parameter
 */
final class GetCommand extends Command
{
    /**
     * @var \Ds\Component\Parameter\Service\ParameterService
     */
    private $parameterService;

    /**
     * Constructor
     *
     * @param \Ds\Component\Parameter\Service\ParameterService $parameterService
     * @param string $name
     */
    public function __construct(ParameterService $parameterService, $name = null)
    {
        parent::__construct($name);
        $this->parameterService = $parameterService;
    }

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this
            ->setName('ds:parameter:get')
            ->addArgument('key', InputArgument::REQUIRED, 'The parameter key.')
            ->setDescription('Gets a parameter value.')
            ->setHelp('This command allows you to get a parameter value.');
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $key = $input->getArgument('key');
        $value = $this->parameterService->get($key);
        $output->write($value);
    }
}


<?php

namespace Ds\Component\Config\Fixture;

use Doctrine\Common\Persistence\ObjectManager;
use Ds\Component\Config\Entity\Config as ConfigEntity;
use Ds\Component\Database\Fixture\Yaml;

/**
 * Trait Config
 *
 * @package Ds\Component\Config
 */
trait Config
{
    use Yaml;

    /**
     * @var string
     */
    private $path;

    /**
     * {@inheritdoc}
     */
    public function load(ObjectManager $manager)
    {
        $objects = $this->parse($this->path);

        foreach ($objects as $object) {
            $config = new ConfigEntity;
            $config
                ->setUuid($object->uuid)
                ->setOwner($object->owner)
                ->setOwnerUuid($object->owner_uuid)
                ->setKey($object->key)
                ->setValue($object->value)
                ->setTenant($object->tenant);
            $manager->persist($config);
        }

        $manager->flush();
    }
}


<?php

namespace Ds\Component\Tenant\Migration;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use Ramsey\Uuid\Uuid;

/**
 * Class Version0_15_0
 *
 * @package Ds\Component\Tenant
 */
final class Version0_15_0 extends AbstractMigration
{
    /**
     * Up migration
     *
     * @param \Doctrine\DBAL\Schema\Schema $schema
     * @param array $tenants
     */
    public function up(Schema $schema, array $tenants = [])
    {
        $sequences['ds_tenant_id_seq'] = 1 + count($tenants);

        switch ($this->platform->getName()) {
            case 'postgresql':
                $this->addSql('CREATE SEQUENCE ds_tenant_id_seq INCREMENT BY 1 MINVALUE 1 START '.$sequences['ds_tenant_id_seq']);
                $this->addSql('CREATE TABLE ds_tenant (id INT NOT NULL, uuid UUID NOT NULL, version INT DEFAULT 1 NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updated_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, PRIMARY KEY(id))');
                $this->addSql('CREATE UNIQUE INDEX UNIQ_EF5FAEEAD17F50A6 ON ds_tenant (uuid)');

                $i = 0;

                foreach ($tenants as $tenant) {
                    if (null === $tenant->uuid) {
                        $tenant->uuid = Uuid::uuid4()->toString();
                    }

                    $this->addSql(sprintf(
                        'INSERT INTO ds_tenant (id, uuid, created_at, updated_at) VALUES (%d, %s, %s, %s);',
                        ++$i,
                        $this->connection->quote($tenant->uuid),
                        'now()',
                        'now()'
                    ));
                }

                break;

            default:
                $this->abortIf(true,'Migration cannot be executed on "'.$this->platform->getName().'".');
                break;
        }
    }

    /**
     * Down migration
     *
     * @param \Doctrine\DBAL\Schema\Schema $schema
     */
    public function down(Schema $schema)
    {
        switch ($this->platform->getName()) {
            case 'postgresql':
                $this->addSql('DROP SEQUENCE ds_tenant_id_seq CASCADE');
                $this->addSql('DROP TABLE ds_tenant');
                break;

            default:
                $this->abortIf(true,'Migration cannot be executed on "'.$this->platform->getName().'".');
                break;
        }
    }
}


<?php

namespace Ds\Component\Parameter\Migration;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use Ds\Component\Container\Attribute;
use Ds\Component\Encryption\Service\CipherService;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;

/**
 * Class Version0_15_0
 *
 * @package Ds\Component\Parameter
 */
final class Version0_15_0 extends AbstractMigration implements ContainerAwareInterface
{
    use Attribute\Container;

    /**
     * Up migration
     *
     * @param \Doctrine\DBAL\Schema\Schema $schema
     * @param array $parameters
     */
    public function up(Schema $schema, array $parameters = [])
    {
        $cipherService = $this->container->get(CipherService::class);
        $encrypted = ['ds_system.user.password'];
        $sequences['ds_parameter_id_seq'] = 1 + count($parameters);

        switch ($this->platform->getName()) {
            case 'postgresql':
                $this->addSql('CREATE SEQUENCE ds_parameter_id_seq INCREMENT BY 1 MINVALUE 1 START '.$sequences['ds_parameter_id_seq']);
                $this->addSql('CREATE TABLE ds_parameter (id INT NOT NULL, "key" VARCHAR(255) NOT NULL, value TEXT DEFAULT NULL, PRIMARY KEY(id))');
                $this->addSql('CREATE UNIQUE INDEX UNIQ_B3C0FD91F48571EB ON ds_parameter ("key")');

                $i = 0;

                foreach ($parameters as $parameter) {
                    $parameter->value = serialize($parameter->value);

                    if (in_array($parameter->key, $encrypted)) {
                        $parameter->value = $cipherService->encrypt($parameter->value);
                    }

                    $this->addSql(sprintf(
                        'INSERT INTO ds_parameter (id, key, value) VALUES (%d, %s, %s);',
                        ++$i,
                        $this->connection->quote($parameter->key),
                        $this->connection->quote($parameter->value)
                    ));
                }

                break;

            default:
                $this->abortIf(true,'Migration cannot be executed on "'.$this->platform->getName().'".');
                break;
        }
    }

    /**
     * Down migration
     *
     * @param \Doctrine\DBAL\Schema\Schema $schema
     */
    public function down(Schema $schema)
    {
        switch ($this->platform->getName()) {
            case 'postgresql':
                $this->addSql('DROP SEQUENCE ds_parameter_id_seq CASCADE');
                $this->addSql('DROP TABLE ds_parameter');
                break;

            default:
                $this->abortIf(true,'Migration cannot be executed on "'.$this->platform->getName().'".');
                break;
        }
    }
}


<?php

namespace Ds\Component\Parameter\Fixture;

use Doctrine\Common\Persistence\ObjectManager;
use Ds\Component\Database\Fixture\Yaml;
use Ds\Component\Parameter\Entity\Parameter as ParameterEntity;

/**
 * Trait Parameter
 *
 * @package Ds\Component\Parameter
 */
trait Parameter
{
    use Yaml;

    /**
     * @var string
     */
    private $path;

    /**
     * {@inheritdoc}
     */
    public function load(ObjectManager $manager)
    {
        $objects = $this->parse($this->path);

        foreach ($objects as $object) {
            $parameter = new ParameterEntity;
            $parameter
                ->setKey($object->key)
                ->setValue($object->value);
            $manager->persist($parameter);
        }

        $manager->flush();
    }
}


<?php

namespace Ds\Component\Acl\EventListener;

use ApiPlatform\Core\DataProvider\PaginatorInterface as Paginator;
use Ds\Component\Acl\Collection\EntityCollection;
use Ds\Component\Acl\Model\Permission;
use LogicException;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

/**
 * Class EntityListener
 *
 * @package Ds\Component\Acl
 */
final class EntityListener
{
    /**
     * @var \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface
     */
    private $tokenStorage;

    /**
     * @var \Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
     */
    private $voter;

    /**
     * @var \Ds\Component\Acl\Collection\EntityCollection
     */
    private $entityCollection;

    /**
     * Constructor
     *
     * @param \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface $tokenStorage
     * @param \Symfony\Component\Security\Core\Authorization\Voter\VoterInterface $voter
     * @param \Ds\Component\Acl\Collection\EntityCollection $entityCollection
     */
    public function __construct(TokenStorageInterface $tokenStorage, VoterInterface $voter, EntityCollection $entityCollection)
    {
        $this->tokenStorage = $tokenStorage;
        $this->voter = $voter;
        $this->entityCollection = $entityCollection;
    }

    /**
     * Deny access if the user does not have proper permissions
     *
     * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
     */
    public function kernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        $entity = $request->attributes->get('_api_resource_class');

        if (null === $entity) {
            return;
        }

        if (!$this->entityCollection->contains($entity)) {
            return;
        }

        $token = $this->tokenStorage->getToken();

        if (!$token) {
            return;
        }

        switch (true) {
            case 'get' === $request->attributes->get('_api_collection_operation_name'):
                $attribute = Permission::BROWSE;
                break;

            case 'get' === $request->attributes->get('_api_item_operation_name'):
                $attribute = Permission::READ;
                break;

            case 'put' === $request->attributes->get('_api_item_operation_name'):
                $attribute = Permission::EDIT;
                break;

            case 'post' === $request->attributes->get('_api_collection_operation_name'):
                $attribute = Permission::ADD;
                break;

            case 'delete' === $request->attributes->get('_api_item_operation_name'):
                $attribute = Permission::DELETE;
                break;

            default:
                return;
        }

        $data = $request->attributes->get('data');

        if ($data instanceof Paginator || is_array($data)) {
            foreach ($data as $item) {
                $vote = $this->voter->vote($token, $item, [$attribute]);

                if (VoterInterface::ACCESS_ABSTAIN === $vote) {
                    throw new LogicException('Voter cannot abstain from voting.');
                }

                if (VoterInterface::ACCESS_GRANTED !== $vote) {
                    throw new AccessDeniedException('Access denied.');
                }
            }
        } else {
            $vote = $this->voter->vote($token, $data, [$attribute]);

            if (VoterInterface::ACCESS_ABSTAIN === $vote) {
                throw new LogicException('Voter cannot abstain from voting.');
            }

            if (VoterInterface::ACCESS_GRANTED !== $vote) {
                throw new AccessDeniedException('Access denied.');
            }
        }
    }
}


<?php

namespace Ds\Component\Formio\Service;

use Ds\Component\Api\Service\Service;
use Ds\Component\Formio\Model\Role;

/**
 * Class RoleService
 *
 * @package Ds\Component\Formio
 */
final class RoleService implements Service
{
    use Base;

    /**
     * @const string
     */
    const MODEL = Role::class;

    /**
     * @const string
     */
    const RESOURCE_LIST = '/role';

    /**
     * @var array
     */
    private static $map = [
        'id' => '_id',
        'created',
        'updated' => 'modified',
        'title',
        'machineName',
        'description',
        'admin',
        'default'
    ];

    /**
     * Get role list
     *
     * @return array
     */
    public function getList(): array
    {
        $objects = $this->execute('GET', static::RESOURCE_LIST);
        $list = [];

        foreach ($objects as $name => $object) {
            $model = static::toModel($object);
            $list[] = $model;
        }

        return $list;
    }
}


<?php

namespace Ds\Component\Model\Attribute\Accessor;

use OutOfRangeException;

/**
 * Trait Meta
 *
 * @package Ds\Component\Model
 */
trait Meta
{
    /**
     * Set meta
     *
     * @param array $meta
     * @return object
     */
    public function setMeta(?array $meta)
    {
        $this->meta = $meta;

        return $this;
    }

    /**
     * Get meta
     *
     * @param string $property
     * @return array
     * @throws \OutOfRangeException
     */
    public function getMeta(?string $property)
    {
        if (null === $property) {
            return $this->meta;
        }

        if (!array_key_exists($property, $this->meta)) {
            throw new OutOfRangeException('Array property does not exist.');
        }

        return $this->meta[$property];
    }
}


<?php

namespace Ds\Component\Config\Tenant\Loader;

use Ds\Component\Database\Util\Objects;
use Ds\Component\Tenant\Entity\Tenant;

/**
 * Trait Config
 *
 * @package Ds\Component\Config
 */
trait Config
{
    /**
     * @var \Ds\Component\Config\Service\ConfigService
     */
    private $configService;

    /**
     * @var string
     */
    private $path;

    /**
     * {@inheritdoc}
     */
    public function load(Tenant $tenant)
    {
        $data = (array) json_decode(json_encode($tenant->getData()));
        $data['config'] = (array) $data['config'];
        $objects = Objects::parseFile($this->path, $data);
        $manager = $this->configService->getManager();

        foreach ($objects as $object) {
            $config = $this->configService->createInstance();
            $config
                ->setOwner($object->owner)
                ->setOwnerUuid($object->owner_uuid)
                ->setKey($object->key)
                ->setValue($object->value)
                ->setTenant($object->tenant);
            $manager->persist($config);
            $manager->flush();
            $manager->detach($config);
        }
    }
}


<?php

namespace Ds\Component\Camunda\Query\Attribute;

/**
 * Trait DeserializeValues
 *
 * @package Ds\Component\Camunda
 */
trait DeserializeValues
{
    /**
     * @var boolean
     */
    private $deserializeValues; # region accessors

    /**
     * Set deserialize values
     *
     * @param boolean $deserializeValues
     * @return object
     */
    public function setDeserializeValues(?bool $deserializeValues)
    {
        $this->deserializeValues = $deserializeValues;
        $this->_deserializeValues = null !== $deserializeValues;

        return $this;
    }

    /**
     * Get deserializeValues
     *
     * @return boolean
     */
    public function getDeserializeValues(): ?bool
    {
        return $this->deserializeValues;
    }

    # endregion

    /**
     * @var boolean
     */
    private $_deserializeValues;
}


<?php

namespace Ds\Component\Camunda\Query\Attribute;

/**
 * Trait Key
 *
 * @package Ds\Component\Camunda
 */
trait Key
{
    /**
     * @var string
     */
    private $key; # region accessors

    /**
     * Set key
     *
     * @param string $key
     * @return object
     */
    public function setKey(?string $key)
    {
        $this->key = $key;
        $this->_key = null !== $key;

        return $this;
    }

    /**
     * Get key
     *
     * @return string
     */
    public function getKey(): ?string
    {
        return $this->key;
    }

    # endregion

    /**
     * @var boolean
     */
    private $_key;
}


<?php

namespace Ds\Component\Security\DependencyInjection;

use Ds\Component\Security\EventListener\Token;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

/**
 * Class DsSecurityExtension
 *
 * @package Ds\Component\Security
 */
final class DsSecurityExtension extends Extension implements PrependExtensionInterface
{
    /**
     * {@inheritdoc}
     */
    public function prepend(ContainerBuilder $container)
    {
        $container->prependExtensionConfig('ds_security', [
            'token' => [
                'uuid' => false,
                'ip' => false,
                'client' => false,
                'modifier' => false,
                'identity' => [
                    'roles' => false,
                    'type' => false,
                    'uuid' => false
                ]
            ]
        ]);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('packages/security.yaml');
    }

    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yaml');

        $configuration = new Configuration;
        $config = $this->processConfiguration($configuration, $configs);

        $expressionLanguage = new ExpressionLanguage;
        $token = [
            '["uuid"]' => Token\UuidListener::class,
            '["ip"]' => Token\IpListener::class,
            '["client"]' => Token\ClientListener::class,
            '["modifier"]' => Token\ModifierListener::class,
            '["identity"]["roles"]' => Token\Identity\RolesListener::class,
            '["identity"]["type"]' => Token\Identity\TypeListener::class,
            '["identity"]["uuid"]' => Token\Identity\UuidListener::class
        ];

        foreach ($token as $property => $class) {
            if (!$expressionLanguage->evaluate('token'.$property, ['token' => $config['token']])) {
                $container->removeDefinition($class);
            }
        }
    }
}


<?php

namespace Ds\Component\Translation\Doctrine\ORM\Filter;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter as BaseSearchFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;

/**
 * Class SearchFilter
 *
 * @package Ds\Component\Translation
 */
final class SearchFilter extends BaseSearchFilter
{
    use Translation;

    /**
     * Filter property
     *
     * @param string $property
     * @param mixed $value
     * @param \Doctrine\ORM\QueryBuilder $queryBuilder
     * @param \ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface $queryNameGenerator
     * @param string $resourceClass
     * @param string|null $operationName
     * @throws \DomainException
     */
    protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
    {
        if (!$this->isPropertyEnabled($property)) {
            return;
        }

        $strategy = $this->properties[$property] ?? self::STRATEGY_EXACT;
        $alias = $this->addJoinTranslation($queryBuilder, $resourceClass);
        $caseSensitive = true;

        if (strpos($strategy, 'i') === 0) {
            $strategy = substr($strategy, 1);
            $caseSensitive = false;
        }

        $this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $alias, $property, $value, $caseSensitive);
    }
}


<?php

namespace Ds\Component\Statistic\DependencyInjection\Compiler;

use Ds\Component\Statistic\Collection\StatCollection;
use LogicException;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Class StatPass
 *
 * @package Ds\Component\Statistic
 */
final class StatPass implements CompilerPassInterface
{
    /**
     * Process
     *
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @throws \LogicException
     */
    public function process(ContainerBuilder $container)
    {
        if (!$container->has(StatCollection::class)) {
            return;
        }

        $collection = $container->findDefinition(StatCollection::class);
        $stats = $container->findTaggedServiceIds('ds_statistic.stat');

        foreach ($stats as $id => $tags) {
            foreach ($tags as $tag) {
                if (!array_key_exists('alias', $tag)) {
                    throw new LogicException('Tag attribute "alias" is missing.');
                }

                $stat = $container->findDefinition($id);
                $stat->addMethodCall('setAlias', [$tag['alias']]);
                $collection->addMethodCall('set', [$tag['alias'], new Reference($id)]);
            }
        }
    }
}


<?php

namespace Ds\Component\Api\Service;

use Ds\Component\Api\Model\Access;
use Ds\Component\Api\Query\AccessParameters as Parameters;

/**
 * Class AccessService
 *
 * @package Ds\Component\Api
 */
final class AccessService implements Service
{
    use Base;

    /**
     * @const string
     */
    const MODEL = Access::class;

    /**
     * @const string
     */
    const RESOURCE_LIST = '/accesses';
    const RESOURCE_OBJECT = '/accesses/{id}';

    /**
     * @var array
     */
    private static $map = [
        'id',
        'uuid',
        'createdAt',
        'updatedAt',
        'owner',
        'ownerUuid',
        'assignee',
        'assigneeUuid',
        'permissions',
        'version'
    ];

    /**
     * Get access list
     *
     * @param \Ds\Component\Api\Query\AccessParameters $parameters
     * @return array
     */
    public function getList(Parameters $parameters = null): array
    {
        $options = [];

        if ($parameters) {
            $options['query'] = (array) $parameters->toObject(true);
        }

        $objects = $this->execute('GET', static::RESOURCE_LIST, $options);
        $list = [];

        foreach ($objects as $object) {
            $model = static::toModel($object);
            $list[] = $model;
        }

        return $list;
    }

    /**
     * Create access
     *
     * @param \Ds\Component\Api\Model\Access $access
     * @param \Ds\Component\Api\Query\AccessParameters $parameters
     * @return \Ds\Component\Api\Model\Access
     */
    public function create(Access $access, Parameters $parameters = null): Access
    {
        $options = [];
        $options['json'] = (array) static::toObject($access);

        if ($parameters) {
            $options['query'] = (array) $parameters->toObject(true);
        }

        $object = $this->execute('POST', static::RESOURCE_LIST, $options);
        /** @var \Ds\Component\Api\Model\Access $access */
        $access = static::toModel($object);

        return $access;
    }

    /**
     * Delete access
     *
     * @param \Ds\Component\Api\Model\Access $access
     */
    public function delete(Access $access)
    {
        $resource = str_replace('{id}', $access->getUuid(), static::RESOURCE_OBJECT);
        $this->execute('DELETE', $resource);
    }
}


<?php

namespace Ds\Component\Acl\Serializer\Normalizer\Property;

use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
use DomainException;
use Ds\Component\Acl\Collection\EntityCollection;
use Ds\Component\Acl\Model\Permission;
use LogicException;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;

/**
 * Class AbstractNormalizer
 *
 * @package Ds\Component\Acl
 */
abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface
{
    /**
     * @var \ApiPlatform\Core\Serializer\AbstractItemNormalizer
     */
    private $decorated;

    /**
     * @var \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface
     */
    private $tokenStorage;

    /**
     * @var \Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
     */
    private $voter;

    /**
     * @var \Symfony\Component\Serializer\SerializerInterface
     */
    private $serializer;

    /**
     * @var \Ds\Component\Acl\Collection\EntityCollection
     */
    private $entityCollection;

    /**
     * Constructor
     *
     * @param \ApiPlatform\Core\Serializer\AbstractItemNormalizer $decorated
     * @param \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface $tokenStorage
     * @param \Symfony\Component\Security\Core\Authorization\Voter\VoterInterface $voter
     * @param \Ds\Component\Acl\Collection\EntityCollection $entityCollection
     */
    public function __construct(AbstractItemNormalizer $decorated, TokenStorageInterface $tokenStorage, VoterInterface $voter, EntityCollection $entityCollection)
    {
        $this->decorated = $decorated;
        $this->tokenStorage = $tokenStorage;
        $this->voter = $voter;
        $this->entityCollection = $entityCollection;
    }

    /**
     * {@inheritdoc}
     */
    public function supportsDenormalization($data, $type, $format = null)
    {
        return $this->decorated->supportsDenormalization($data, $type, $format);
    }

    /**
     * {@inheritdoc}
     */
    public function supportsNormalization($data, $format = null)
    {
        return $this->decorated->supportsNormalization($data, $format);
    }

    /**
     * {@inheritdoc}
     */
    public function denormalize($data, $class, $format = null, array $context = [])
    {
        if ($this->entityCollection->contains($class)) {
            $token = $this->tokenStorage->getToken();

            if (!$token) {
                throw new LogicException('Token is not defined.');
            }

            $attribute = Permission::EDIT;
            $subject = [
                0 => null,
                1 => null
            ];

            if (array_key_exists('object_to_populate', $context)) {
                $subject[0] = $context['object_to_populate'];
            } else {
                $object = new $context['resource_class'];

                foreach ($data as $property => $value) {
                    if (in_array($property, ['uuid', 'owner', 'ownerUuid', 'identity', 'identityUuid'], true)) {
                        // Set the required values for permission scope voters to vote on.
                        $object->{'set'.$property}($value);
                    }
                }

                $subject[0] = $object;
            }

            foreach (array_keys($data) as $property) {
                $subject[1] = $property;
                $vote = $this->voter->vote($token, $subject, [$attribute]);

                if (VoterInterface::ACCESS_ABSTAIN === $vote) {
                    throw new LogicException('Voter cannot abstain from voting.');
                }

                if (VoterInterface::ACCESS_DENIED === $vote) {
                    unset($data[$property]);
                }
            }
        }

        return $this->decorated->denormalize($data, $class, $format, $context);
    }

    /**
     * {@inheritdoc}
     */
    public function normalize($object, $format = null, array $context = [])
    {
        $data = $this->decorated->normalize($object, $format, $context);
        $class = get_class($object);

        if ($this->entityCollection->contains($class)) {
            $token = $this->tokenStorage->getToken();

            if (!$token) {
                throw new LogicException('Token is not defined.');
            }

            switch (true) {
                case array_key_exists('collection_operation_name', $context):
                    $attribute = Permission::BROWSE;
                    break;

                case array_key_exists('item_operation_name', $context):
                    $attribute = Permission::READ;
                    break;

                default:
                    throw new DomainException('Operation does not exist.');
            }

            $subject = [
                0 => $object,
                1 => null
            ];

            foreach (array_keys($data) as $property) {
                $subject[1] = $property;
                $vote = $this->voter->vote($token, $subject, [$attribute]);

                if (VoterInterface::ACCESS_ABSTAIN === $vote) {
                    throw new LogicException('Voter cannot abstain from voting.');
                }

                if (VoterInterface::ACCESS_DENIED === $vote) {
                    unset($data[$property]);
                }
            }
        }

        return $data;
    }

    /**
     * {@inheritdoc}
     */
    public function setSerializer(SerializerInterface $serializer)
    {
        $this->serializer = $serializer;

        if ($this->decorated instanceof SerializerAwareInterface) {
            $this->decorated->setSerializer($serializer);
        }
    }
}


<?php

namespace Ds\Component\Identity\Service;

use Ds\Component\Model\Type\Identitiable;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

/**
 * Class IdentityService
 *
 * @package Ds\Component\Identity
 */
final class IdentityService
{
    /**
     * @var \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface
     */
    private $tokenStorage;

    /**
     * Constructor
     *
     * @param \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface $tokenStorage
     */
    public function __construct(TokenStorageInterface $tokenStorage)
    {
        $this->tokenStorage = $tokenStorage;
    }

    /**
     * Generate entity identity, if none present
     *
     * @param \Ds\Component\Model\Type\Identitiable $model
     * @param boolean $overwrite
     * @return \Ds\Component\Identity\Service\IdentityService
     */
    public function generateIdentity(Identitiable $model, bool $overwrite = false)
    {
        if (null === $model->getIdentity() || $overwrite) {
            $user = $this->tokenStorage->getToken()->getUser();
            $model
                ->setIdentity($user->getIdentity()->getType())
                ->setIdentityUuid($user->getIdentity()->getUuid());
        }

        return $this;
    }
}


<?php

namespace Ds\Component\Entity\DependencyInjection;

use Ds\Component\Entity\EventListener;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader;

/**
 * Class DsEntityExtension
 *
 * @package Ds\Component\Entity
 */
final class DsEntityExtension extends Extension implements PrependExtensionInterface
{
    /**
     * {@inheritdoc}
     */
    public function prepend(ContainerBuilder $container)
    {
        $container->prependExtensionConfig('ds_entity', [
            'uuidentifiable' => false,
            'custom_identifiable' => false
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yaml');

        $configuration = new Configuration;
        $config = $this->processConfiguration($configuration, $configs);

        $definitions = [
            'uuidentifiable' => EventListener\UuidentifiableListener::class,
            'custom_identifiable' => EventListener\CustomIdentifiableListener::class
        ];

        foreach ($config as $behavior => $enabled) {
            if (!$enabled) {
                $container->removeDefinition($definitions[$behavior]);
            }
        }
    }
}


<?php

namespace Ds\Component\Config\Tenant\Unloader;

use Doctrine\ORM\EntityManagerInterface;
use Ds\Component\Config\Entity\Config;
use Ds\Component\Tenant\Entity\Tenant;
use Ds\Component\Tenant\Loader\Unloader;

/**
 * Class ConfigUnloader
 *
 * @package Ds\Component\Config
 */
final class ConfigUnloader implements Unloader
{
    /**
     * @var \Doctrine\ORM\EntityManagerInterface
     */
    private $entityManager;

    /**
     * Constructor
     *
     * @param \Doctrine\ORM\EntityManagerInterface $entityManager
     */
    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     * {@inheritdoc}
     */
    public function unload(Tenant $tenant)
    {
        $builder = $this->entityManager->getRepository(Config::class)->createQueryBuilder('e');
        $builder
            ->delete()
            ->where('e.tenant = :tenant')
            ->setParameter('tenant', $tenant->getUuid());
        $query = $builder->getQuery();
        $query->execute();
    }
}


<?php

namespace Ds\Component\Translation\EventListener;

use ApiPlatform\Core\DataProvider\PaginatorInterface as Paginator;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Ds\Component\Translation\Model\Type\Translatable;
use Ds\Component\Translation\Model\Type\Translation;
use Ds\Component\Translation\Service\TranslationService;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

/**
 * Class TranslateListener
 *
 * @package Ds\Component\Translation
 */
final class TranslateListener
{
    /**
     * @var \Ds\Component\Translation\Service\TranslationService
     */
    private $translationService;

    /**
     * Constructor
     *
     * @param \Ds\Component\Translation\Service\TranslationService $translationService
     */
    public function __construct(TranslationService $translationService)
    {
        $this->translationService = $translationService;
    }

    /**
     * Transfer translations from properties to source before saving them to storage
     *
     * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
     */
    public function kernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        $entity = $request->attributes->get('_api_resource_class');

        if (null === $entity) {
            return;
        }

        if (!in_array(Translatable::class, class_implements($entity), true)) {
            return;
        }

        $model = $request->attributes->get('data');

        if ('post' === $request->attributes->get('_api_collection_operation_name')) {
            $this->translationService->transfer($model);
        } else if ('put' === $request->attributes->get('_api_item_operation_name')) {
            $this->translationService->transfer($model);
            $this->translationService->translate($model);
        }
    }

    /**
     * Generate a custom id before persisting the entity, if none provided
     *
     * @param \Doctrine\ORM\Event\LifecycleEventArgs $args
     */
    public function prePersist(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        if (!$entity instanceof Translatable) {
            return;
        }

        $this->translationService->transfer($entity);
    }

    /**
     * Encrypt encryptable entity properties before saving in storage.
     *
     * @param \Doctrine\ORM\Event\PreUpdateEventArgs $args
     */
    public function preUpdate(PreUpdateEventArgs $args)
    {
        $entity = $args->getEntity();

        if (!$entity instanceof Translatable) {
            return;
        }

        $this->translationService->transfer($entity);
    }

    /**
     * Transfer translations from source to properties after retrieving them from storage
     *
     * @param \Doctrine\ORM\Event\LifecycleEventArgs $args
     */
    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        if (!$entity instanceof Translatable) {
            return;
        }

        $this->translationService->translate($entity);
    }
}


<?php

namespace Ds\Component\Encryption\EventListener\Entity\Encryptable;

use Doctrine\ORM\Event\PostFlushEventArgs;
use Ds\Component\Encryption\Model\Type\Encryptable;

/**
 * Class PostFlushListener
 *
 * @package Ds\Component\Encryption
 */
final class PostFlushListener extends AbstractListener
{
    /**
     * Decrypt encryptable entity properties after saving in storage.
     *
     * @param \Doctrine\ORM\Event\PostFlushEventArgs $args
     */
    public function postFlush(PostFlushEventArgs $args)
    {
        $maps = $args->getEntityManager()->getUnitOfWork()->getIdentityMap();

        foreach ($maps as $entities) {
            foreach ($entities as $entity) {
                if (!$entity instanceof Encryptable) {
                    continue;
                }

                $this->encryptionService->decrypt($entity);
            }
        }
    }
}


<?php

namespace Ds\Component\Camunda\Model\Attribute;

use Ds\Component\Camunda\Model\Variable;
use OutOfRangeException;

/**
 * Trait Variables
 *
 * @package Ds\Component\Camunda
 */
trait Variables
{
    /**
     * @var array
     */
    private $variables; # region accessors

    /**
     * Set variables
     *
     * @param array $variables
     * @return object
     */
    public function setVariables(array $variables)
    {
        $this->variables = [];

        foreach ($variables as $variable) {
            $this->addVariable($variable);
        }

        return $this;
    }

    /**
     * Add variable
     *
     * @param \Ds\Component\Camunda\Model\Variable $variable
     * @return object
     */
    public function addVariable(Variable $variable)
    {
        $this->variables[$variable->getName()] = $variable;

        return $this;
    }

    /**
     * Get variables
     *
     * @param string $name
     * @return array
     * @throws \OutOfRangeException
     */
    public function getVariables($name = null)
    {
        if (null === $name) {
            return $this->variables;
        }

        if (!array_key_exists($name, $this->variables)) {
            throw new OutOfRangeException('Variable does not exist.');
        }

        return $this->variables[$name];
    }

    # endregion
}