<?php

namespace Bolt\Collection;

use ArrayAccess;
use Bolt\Common\Assert;
use Bolt\Common\Deprecated;
use Bolt\Common\Thrower;
use InvalidArgumentException;
use RuntimeException;
use Traversable;

/**
 * Array functions. All of these methods accept `Traversable` or `ArrayAccess` objects in addition to arrays.
 *
 * Most of these methods are also provided in {@see Bag}, but their implementation is significant enough
 * that the backing logic lives here so they can be used standalone from Bags.
 *
 * @author Carson Full <carsonfull@gmail.com>
 */
class Arr
{
    /** @var \stdClass|null Used with {@see remove} to unset value */
    private static $unsetMarker;

    /**
     * Converts an `iterable`, `null`, or `stdClass` to an array.
     *
     * @param iterable|null|\stdClass $iterable
     *
     * @return array
     */
    public static function from($iterable)
    {
        if (is_array($iterable)) {
            return $iterable;
        }
        // Don't mean to play favorites, but want to optimize where we can.
        if ($iterable instanceof ImmutableBag) {
            return $iterable->toArray();
        }
        if ($iterable instanceof Traversable) {
            return iterator_to_array($iterable);
        }
        if ($iterable === null) {
            return [];
        }
        if ($iterable instanceof \stdClass) {
            return (array) $iterable;
        }

        Assert::nullOrIsIterable($iterable);
    }

    /**
     * Recursively converts an `iterable` to nested arrays.
     *
     * @param iterable|null|\stdClass $iterable
     *
     * @return array
     */
    public static function fromRecursive($iterable)
    {
        $arr = static::from($iterable);

        foreach ($arr as $key => $value) {
            if ($value instanceof \stdClass || \is_iterable($value)) {
                $value = static::fromRecursive($value);
            }
            $arr[$key] = $value;
        }

        return $arr;
    }

    /**
     * Return the values from a single column in the `$input` array, identified by the `$columnKey`.
     *
     * Optionally, an `$indexKey` may be provided to index the values in the returned array by the
     * values from the `$indexKey` column in the input array.
     *
     * Example:
     *
     *     $data = [
     *         ['id' => 10, 'name' => 'Alice'],
     *         ['id' => 20, 'name' => 'Bob'],
     *         ['id' => 30, 'name' => 'Carson'],
     *     ];
     *
     *     Arr::column($data, 'name');
     *     // => ['Alice', 'Bob', 'Carson']
     *
     *     Arr::column($data, 'name', 'id');
     *     // => [10 => 'Alice', 20 => 'Bob', 30 => 'Carson']
     *
     *

     * Note: This matches the {@see array_column} function, however; it accepts an iterable not just an array,
     * it allows for mapping a list of objects implementing `ArrayAccess`, and allows for mapping a list of
     * object properties (which was added to the builtin function in PHP 7.0).
     *
     * @param iterable        $input     A list of arrays or objects from which to pull a column of values
     * @param string|int|null $columnKey The key of the values to return or `null` for no change
     * @param string|int|null $indexKey  The key of the keys to return or `null` for no change
     *
     * @return array
     */
    public static function column($input, $columnKey, $indexKey = null)
    {
        Assert::isIterable($input);

        $output = [];

        foreach ($input as $row) {
            $key = $value = null;
            $keySet = false;

            if ($columnKey === null) {
                $value = $row;
            } elseif (is_array($row) && array_key_exists($columnKey, $row)) {
                $value = $row[$columnKey];
            } elseif ($row instanceof ArrayAccess && isset($row[$columnKey])) {
                $value = $row[$columnKey];
            } elseif (is_object($row) && isset($row->{$columnKey})) {
                $value = $row->{$columnKey};
            } else {
                continue;
            }

            if ($indexKey !== null) {
                /*
                 * For arrays, we use array_key_exists because isset returns false for keys that exist with null values.
                 * For ArrayAccess we assume devs are smarter and don't have this edge case. Regardless, we don't have
                 * another way to check so it's up to them.
                 */
                if (is_array($row) && array_key_exists($indexKey, $row)) {
                    $keySet = true;
                    $key = (string) $row[$indexKey];
                } elseif ($row instanceof ArrayAccess && isset($row[$indexKey])) {
                    $keySet = true;
                    $key = (string) $row[$indexKey];
                } elseif (is_object($row) && isset($row->{$indexKey})) {
                    $keySet = true;
                    $key = (string) $row->{$indexKey};
                }
            }

            if ($keySet) {
                $output[$key] = $value;
            } else {
                $output[] = $value;
            }
        }

        return $output;
    }

    /**
     * Returns whether a key exists from an array or `ArrayAccess` object using path syntax to check nested data.
     *
     * This method does not allow for keys that contain `/`.
     *
     * Example:
     *
     *     // Check if the the bar key of a set of nested arrays exists.
     *     // This is equivalent to isset($data['foo']['baz']['bar']) but won't
     *     // throw warnings for missing keys.
     *     Arr::has($data, 'foo/baz/bar');
     *
     *

     * Note: Using `isset()` with nested data, like `isset($data['a']['b'])`, won't call `offsetExists` for 'a'.
     * It calls `offsetGet('a')` and if 'a' doesn't exist and an `isset` check isn't done in `offsetGet`, a warning is
     * triggered. It could be argued that that `ArrayAccess` object should fix this in their implementation of
     * `offsetGet`, and I would agree. Regardless I think this is nicer syntax.
     *
     * @param array|ArrayAccess $data Data to check values from
     * @param string            $path Path to traverse and check keys from
     *
     * @return bool
     */
    public static function has($data, $path)
    {
        Assert::isArrayAccessible($data);
        Assert::stringNotEmpty($path);

        $path = explode('/', $path);

        while (($part = array_shift($path)) !== null) {
            if (!($data instanceof ArrayAccess) && !is_array($data)) {
                return false;
            }
            if (!(isset($data[$part]) || array_key_exists($part, $data))) {
                return false;
            }
            $data = $data[$part];
        }

        return true;
    }

    /**
     * Gets a value from an array or `ArrayAccess` object using path syntax to retrieve nested data.
     *
     * Example:
     *
     *     // Get the bar key of a set of nested arrays.
     *     // This is equivalent to $data['foo']['baz']['bar'] but won't
     *     // throw warnings for missing keys.
     *     Arr::get($data, 'foo/baz/bar');
     *
     * This method does not allow for keys that contain `/`.
     *
     * This code is adapted from Michael Dowling in his Guzzle library.
     *
     * @param array|ArrayAccess $data    Data to retrieve values from
     * @param string            $path    Path to traverse and retrieve a value from
     * @param mixed|null        $default Default value to return if key does not exist
     *
     * @return mixed|null
     */
    public static function get($data, $path, $default = null)
    {
        Assert::isArrayAccessible($data);
        Assert::stringNotEmpty($path);

        $path = explode('/', $path);

        while (($part = array_shift($path)) !== null) {
            if ((!is_array($data) && !($data instanceof ArrayAccess)) || !isset($data[$part])) {
                return $default;
            }
            $data = $data[$part];
        }

        return $data;
    }

    /**
     * Sets a value in a nested array or `ArrayAccess` object using path syntax to set nested data.
     * Inner arrays will be created as needed to set the value.
     *
     * Example:
     *
     *     // Set an item at a nested structure
     *     Arr::set($data, 'nested/path/hello', 'world');
     *
     *     // Append to a list in a nested structure
     *     Arr::get($data, 'foo/baz');
     *     // => null
     *     Arr::set($data, 'foo/baz/[]', 'a');
     *     Arr::set($data, 'foo/baz/[]', 'b');
     *     Arr::get($data, 'foo/baz');
     *     // => ['a', 'b']
     *
     * This function does not support keys that contain `/` or `[]` characters
     * because these are special tokens used when traversing the data structure.
     * A value may be appended to an existing array by using `[]` as the final
     * key of a path.
     *
     *

     * Note: To set values in arrays that are in `ArrayAccess` objects their
     * `offsetGet()` method needs to be able to return arrays by reference.
     * See {@see MutableBag} for an example of this.
     *
     * This code is adapted from Michael Dowling in his Guzzle library.
     *
     * @param array|ArrayAccess $data  Data to modify by reference
     * @param string            $path  Path to set
     * @param mixed             $value Value to set at the key
     *
     * @throws \RuntimeException when trying to set a path that travels through a scalar value
     * @throws \RuntimeException when trying to set a value in an array that is in an `ArrayAccess` object
     *                           which cannot retrieve arrays by reference
     */
    public static function set(&$data, $path, $value)
    {
        Assert::isArrayAccessible($data);
        Assert::stringNotEmpty($path);

        $queue = explode('/', $path);
        // Optimization for simple sets.
        if (count($queue) === 1) {
            if ($path === '[]') {
                $data[] = $value;
            } elseif (static::$unsetMarker && $value === static::$unsetMarker) {
                unset($data[$path]);
            } else {
                $data[$path] = $value;
            }

            return;
        }

        $invalidKey = null;
        $current = &$data;
        while (($key = array_shift($queue)) !== null) {
            if (!is_array($current) && !($current instanceof ArrayAccess)) {
                throw new RuntimeException(
                    sprintf(
                        "Cannot set '%s', because '%s' is already set and not an array or an object implementing ArrayAccess.",
                        $path,
                        $invalidKey
                    )
                );
            }
            if (!$queue) {
                if ($key === '[]') {
                    $current[] = $value;
                } elseif (static::$unsetMarker && $value === static::$unsetMarker) {
                    unset($current[$key]);
                } else {
                    $current[$key] = $value;
                }

                return;
            }

            if ($current instanceof Bag && !($current instanceof MutableBag)) {
                Deprecated::warn('Mutating items in a ' . Bag::class, 1.1, 'Use a ' . MutableBag::class . ' instead.');
            }

            if (!isset($current[$key])) {
                $current[$key] = [];
            }

            $next = null;
            if ($current instanceof ArrayAccess && !static::canReturnArraysByReference($current, $key, $next, $e)) {
                throw new RuntimeException(
                    sprintf(
                        "Cannot set '%s', because '%s' is an %s which does not return arrays by reference from its offsetGet() method. See %s for an example of how to do this.",
                        $path,
                        $invalidKey,
                        get_class($current),
                        MutableBag::class
                    ),
                    0,
                    $e
                );
            }

            // If checking if object can return arrays by ref needed to fetch the value in the object then
            // use that so we don't have to fetch the value again.
            if ($next !== null) {
                $current = &$next;
                unset($next); // so assigning null above doesn't wipe out actual data
            } else {
                $current = &$current[$key];
            }

            $invalidKey = $key;
        }
    }

    /**
     * Removes and returns a value from an array or `ArrayAccess` object using path syntax to remove nested data.
     *
     * Example:
     *
     *     Arr::remove($data, 'foo/bar');
     *     // => 'baz'
     *     Arr::remove($data, 'foo/bar');
     *     // => null
     *
     * This function does not support keys that contain `/`.
     *
     *

     * Note: To remove values in arrays that are in `ArrayAccess` objects their
     * `offsetGet()` method needs to be able to return arrays by reference.
     * See {@see MutableBag} for an example of this.
     *
     * @param array|ArrayAccess $data    Data to retrieve remove value from
     * @param string            $path    Path to traverse
     * @param mixed|null        $default Default value to return if key does not exist
     *
     * @throws \RuntimeException when trying to remove a path that travels through a scalar value
     * @throws \RuntimeException when trying to remove a value in an array that is in an `ArrayAccess` object
     *                           which cannot retrieve arrays by reference
     *
     * @return mixed
     */
    public static function remove(&$data, $path, $default = null)
    {
        if (!static::$unsetMarker) {
            static::$unsetMarker = new \stdClass();
        }

        // Call get() with special default value so we can know if the key exists without calling has()
        $value = static::get($data, $path, static::$unsetMarker);

        /*
         * If the path doesn't exist don't call set().
         * This also prevents set() from creating middle arrays to get to the leaf node,
         * which doesn't make sense in this case since we are just trying to remove the leaf node.
         */
        if ($value === static::$unsetMarker) {
            return $default;
        }

        // Set with special marker to unset value at path
        static::set($data, $path, static::$unsetMarker);

        return $value;
    }

    /**
     * Returns whether the value is an array or an object implementing `ArrayAccess`.
     *
     * @param mixed $value
     *
     * @return bool
     */
    public static function isAccessible($value)
    {
        return $value instanceof ArrayAccess || is_array($value);
    }

    /**
     * Asserts that the given value is an array or an object implementing `ArrayAccess`.
     *
     * @param mixed $value
     *
     * @throws InvalidArgumentException when it is not
     *
     * @deprecated since 1.0 and will be removed in 2.0. Use {@see \Bolt\Common\Assert::isArrayAccessible} instead.
     */
    public static function assertAccessible($value)
    {
        Deprecated::method(1.0, 'Bolt\Common\Assert::isArrayAccessible');

        Assert::isArrayAccessible($value);
    }

    /**
     * Returns whether the `$iterable` is an associative mapping.
     *
     * Note: Empty arrays are not.
     *
     * @param iterable $iterable
     *
     * @return bool
     */
    public static function isAssociative($iterable)
    {
        if ($iterable instanceof Traversable) {
            $iterable = iterator_to_array($iterable);
        }
        if (!is_array($iterable) || $iterable === []) {
            return false;
        }

        return array_keys($iterable) !== range(0, count($iterable) - 1);
    }

    /**
     * Returns whether the `$iterable` is an indexed list - zero indexed and sequential.
     *
     * Note: Empty iterables are.
     *
     * @param iterable $iterable
     *
     * @return bool
     */
    public static function isIndexed($iterable)
    {
        if (!\is_iterable($iterable)) {
            return false;
        }

        return !static::isAssociative($iterable);
    }

    /**
     * Returns an array with the `$callable` applied to each leaf value in the `$iterable`.
     *
     * This converts all `Traversable` objects to arrays.
     *
     * @param iterable $iterable
     * @param callable $callable Function is passed `($value, $key)`
     *
     * @return array
     */
    public static function mapRecursive($iterable, callable $callable)
    {
        Assert::isIterable($iterable);

        // If internal method with one arg, like strtolower, limit to first arg so warning isn't triggered.
        $ref = new \ReflectionFunction($callable);
        if ($ref->isInternal() && $ref->getNumberOfParameters() === 1) {
            $callable = function ($arg) use ($callable) {
                return $callable($arg);
            };
        }

        return static::doMapRecursive($iterable, $callable);
    }

    /**
     * Internal method do actual recursion after args have been validated by main method.
     *
     * @param iterable $iterable
     * @param callable $callable
     *
     * @return array
     */
    private static function doMapRecursive($iterable, callable $callable)
    {
        $mapped = [];
        foreach ($iterable as $key => $value) {
            $mapped[$key] = \is_iterable($value) ?
                static::doMapRecursive($value, $callable) :
                $callable($value, $key);
        }

        return $mapped;
    }

    /**
     * Replaces values from second iterable into first iterable recursively.
     *
     * This differs from {@see array_replace_recursive} in a couple ways:
     *
     *  - Lists (indexed arrays) from second array completely replace list in first array.
     *
     *  - Null values from second array do not replace lists or associative arrays in first
     *    (they do still replace scalar values).
     *
     * This converts all `Traversable` objects to arrays.
     *
     * @param iterable $iterable1
     * @param iterable $iterable2
     *
     * @return array The combined array
     */
    public static function replaceRecursive($iterable1, $iterable2)
    {
        Assert::allIsIterable([$iterable1, $iterable2]);

        if ($iterable1 instanceof Traversable) {
            $iterable1 = iterator_to_array($iterable1);
        }
        if ($iterable2 instanceof Traversable) {
            $iterable2 = iterator_to_array($iterable2);
        }

        $merged = $iterable1;

        foreach ($iterable2 as $key => $value) {
            if ($value instanceof Traversable) {
                $value = iterator_to_array($value);
            }
            if (is_array($value) && static::isAssociative($value)
                && isset($merged[$key]) && \is_iterable($merged[$key])
            ) {
                $merged[$key] = static::replaceRecursive($merged[$key], $value);
            } elseif ($value === null && isset($merged[$key]) && \is_iterable($merged[$key])) {
                // Convert iterable to array to be consistent.
                if ($merged[$key] instanceof Traversable) {
                    $merged[$key] = iterator_to_array($merged[$key]);
                }
                continue;
            } else {
                $merged[$key] = $value;
            }
        }

        return $merged;
    }

    /**
     * Determine whether the ArrayAccess object can return by reference.
     *
     * @param ArrayAccess      $obj
     * @param string           $key   The key to try with
     * @param ArrayAccess|null $value The value if it needed to be fetched
     * @param \ErrorException  $ex
     *
     * @throws \ErrorException
     *
     * @return bool
     */
    private static function canReturnArraysByReference(ArrayAccess $obj, $key, &$value, &$ex)
    {
        static $supportedClasses = [
            // Add our classes by default to help with performance since we can
            Bag::class                     => true, // but deprecated
            MutableBag::class              => true,

            // These fail reflection check below even though they work fine :rolleyes:
            \ArrayObject::class            => true,
            \ArrayIterator::class          => true,
            \RecursiveArrayIterator::class => true,
        ];
        static $noErrors = [];

        $class = get_class($obj);

        /*
         * Check to see if offsetGet() is defined to return reference (with "&" before method name).
         * This prevents us from triggering indirect modification notices.
         * We know for sure that the method cannot return by reference if not defined correctly, so we cache false.
         * We do not know for sure that the method can return by reference if it is defined correctly, so we cache
         * null instead of true. This allows the reflection check to only happen once, but still drop through to
         * validation below.
         */
        if (!isset($supportedClasses[$class])) {
            $supportedClasses[$class] = (new \ReflectionMethod($obj, 'offsetGet'))->returnsReference() ? null : false;
        }

        // If definite value return that, else run validation below.
        if ($supportedClasses[$class] !== null) {
            return $supportedClasses[$class];
        }

        if (isset($noErrors[$class])) {
            $value1 = &$obj[$key];
        } else {
            Thrower::set();
            try {
                $value1 = &$obj[$key];
            } catch (\ErrorException $e) {
                $msg = $e->getMessage();
                if ($msg === 'Only variable references should be returned by reference' ||
                    strpos($msg, 'Indirect modification of overloaded element') === 0
                ) {
                    $ex = $e;

                    return $supportedClasses[$class] = false;
                }
                throw $e;
            } finally {
                restore_error_handler();
            }

            // We assume the object is not going to trigger warnings at this point
            $noErrors[$class] = true;
        }

        // We cannot validate this result because objects are always returned by reference (and scalars do not matter).
        if (!is_array($value1)) {
            // return value (via parameter) so set() doesn't have to fetch the item again.
            // We cannot do this if is an array because it will be the value instead of the reference.
            $value = $value1;

            return true;
        }

        // Verify the object can return arrays by reference.
        $value2 = &$obj[$key];
        $testKey = uniqid('__reference_test_');
        $value1[$testKey] = 'test';
        $supportedClasses[$class] = isset($value2[$testKey]);
        unset($value1[$testKey]);

        return $supportedClasses[$class];
    }

    /**
     * Flattens an iterable.
     *
     * Example:
     *
     *     // Flatten one level
     *     Arr::flatten([[1, 2], [[3]], 4])
     *     // => [1, 2, [3], 4]
     *
     *     // Flatten all levels
     *     Arr::flatten([[1, 2], [[3]], 4], INF)
     *     // => [1, 2, 3, 4]
     *
     * @param iterable $iterable The iterable to flatten
     * @param int      $depth    How deep to flatten
     *
     * @return array
     */
    public static function flatten($iterable, $depth = 1)
    {
        Assert::isIterable($iterable);

        return static::doFlatten(
            $iterable,
            $depth,
            'is_iterable' // This may be more configurable in the future.
        );
    }

    /**
     * Internal method to do actual flatten recursion after args have been validated by main method.
     *
     * @param iterable $iterable  The iterable to flatten
     * @param int      $depth     How deep to flatten
     * @param callable $predicate Whether to recurse the item
     * @param array    $result    The result array
     *
     * @return array
     */
    private static function doFlatten($iterable, $depth, callable $predicate, array $result = [])
    {
        foreach ($iterable as $item) {
            if ($depth >= 1 && $predicate($item)) {
                $result = static::doFlatten($item, $depth - 1, $predicate, $result);
            } else {
                $result[] = $item;
            }
        }

        return $result;
    }

    /**
     * Private Constructor.
     *
     * @codeCoverageIgnore
     */
    private function __construct()
    {
    }
}


<?php

namespace Sidus\FileUploadBundle\EventListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Sidus\FileUploadBundle\Manager\ResourceManagerInterface;
use Sidus\FileUploadBundle\Model\ResourceInterface;

/**
 * Delete resource files on entity removal
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class ResourceSubscriber implements EventSubscriber
{
    /** @var ResourceManagerInterface */
    protected $resourceManager;

    /**
     * @param ResourceManagerInterface $resourceManager
     */
    public function __construct(ResourceManagerInterface $resourceManager)
    {
        $this->resourceManager = $resourceManager;
    }

    /**
     * @return array
     */
    public function getSubscribedEvents()
    {
        return [
            'preRemove',
        ];
    }

    /**
     * @param LifecycleEventArgs $args
     *
     * @throws \UnexpectedValueException
     */
    public function preRemove(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();
        if ($entity instanceof ResourceInterface) {
            $this->resourceManager->removeResourceFile($entity);

            return;
        }
    }
}


<?php

namespace Sidus\FileUploadBundle\Form\Type;

use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityManagerInterface;
use Sidus\FileUploadBundle\Configuration\ResourceTypeConfiguration;
use Sidus\FileUploadBundle\Manager\ResourceManagerInterface;
use Sidus\FileUploadBundle\Model\ResourceInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * Easy file-upload integration in forms
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class ResourceType extends AbstractType
{
    /** @var ResourceManagerInterface */
    protected $resourceManager;

    /** @var ManagerRegistry */
    protected $doctrine;

    /**
     * @param ResourceManagerInterface $resourceManager
     * @param ManagerRegistry          $doctrine
     */
    public function __construct(ResourceManagerInterface $resourceManager, ManagerRegistry $doctrine)
    {
        $this->resourceManager = $resourceManager;
        $this->doctrine = $doctrine;
    }

    /**
     * @param FormView      $view
     * @param FormInterface $form
     * @param array         $options
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        /** @var ResourceTypeConfiguration $resourceType */
        $resourceType = $options['resource_type'];
        $view->vars['endpoint'] = $resourceType->getEndpoint();
        $view->vars['data'] = $form->getData(); // "data" is the entity and "value" the ID
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array                $options
     *
     * @throws \Exception
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // The view must always be an ID
        $builder->addViewTransformer(
            new CallbackTransformer(
                function ($originalData) {
                    if (!$originalData instanceof ResourceInterface) {
                        return $originalData; // Why is this condition necessary when submitting ? Makes no sense...
                    }

                    return $this->getIdentifierValue($originalData);
                },
                function ($submittedData) {
                    return $submittedData;
                }
            )
        );

        // Model data must always be an entity
        $builder->addModelTransformer(
            new CallbackTransformer(
                function ($originalData) {
                    return $originalData;
                },
                function ($submittedData) use ($options) {
                    /** @var ObjectRepository $repository */
                    $repository = $options['repository'];
                    if (null === $submittedData || '' === $submittedData) {
                        return null;
                    }

                    return $repository->find($submittedData);
                }
            )
        );
    }

    /**
     * @return string
     */
    public function getBlockPrefix()
    {
        return 'sidus_resource';
    }

    /**
     * @param OptionsResolver $resolver
     *
     * @throws \Symfony\Component\OptionsResolver\Exception\AccessException
     * @throws \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
     * @throws \UnexpectedValueException
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setRequired(
            [
                'resource_type',
            ]
        );
        $resolver->setDefaults(
            [
                'class' => null,
                'repository' => null,
                'compound' => false,
                'error_bubbling' => false,
            ]
        );
        $resolver->setNormalizer(
            'resource_type',
            function (Options $options, $value) {
                return $this->resourceManager->getResourceTypeConfiguration($value);
            }
        );
        $resolver->setNormalizer(
            'class',
            function (Options $options, $value) {
                $resourceType = $options['resource_type'];
                if (!$value && $resourceType instanceof ResourceTypeConfiguration) {
                    return $resourceType->getEntity();
                }

                return $value;
            }
        );
        $resolver->setNormalizer(
            'repository',
            function (Options $options, $value) {
                if ($value) {
                    if ($value instanceof ObjectRepository) {
                        return $value;
                    }
                    throw new \UnexpectedValueException("The 'repository' option must be an EntityRepository");
                }
                $class = $options['class'];
                if (!$class) {
                    throw new \UnexpectedValueException("Missing option 'class' or 'repository'");
                }

                $entityManager = $this->doctrine->getManagerForClass($class);
                if (!$entityManager instanceof EntityManagerInterface) {
                    throw new \UnexpectedValueException("No manager found for class {$class}");
                }

                return $entityManager->getRepository($class);
            }
        );
    }

    /**
     * @param ResourceInterface $originalData
     *
     * @throws \InvalidArgumentException
     * @throws \LogicException
     *
     * @return array
     */
    protected function getIdentifierValue(ResourceInterface $originalData)
    {
        $class = ClassUtils::getClass($originalData);
        $entityManager = $this->doctrine->getManagerForClass($class);
        if (!$entityManager instanceof EntityManagerInterface) {
            throw new \InvalidArgumentException("No manager found for class {$class}");
        }
        $metadata = $entityManager->getClassMetadata($class);
        $identifier = $metadata->getIdentifierValues($originalData);
        if (1 !== \count($identifier)) {
            throw new \LogicException('ResourceInterface must have a single identifier (primary key)');
        }

        return array_pop($identifier);
    }
}


<?php

namespace Sidus\FileUploadBundle\Stream;

use Sidus\FileUploadBundle\Manager\ResourceManagerInterface;
use Sidus\FileUploadBundle\Model\ResourceInterface;
use Sidus\FileUploadBundle\Utilities\FilenameTransliterator;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Provides a simple way to stream file to client
 */
class FileStreamer implements FileStreamerInterface
{
    /** @var ResourceManagerInterface */
    protected $resourceManager;

    /**
     * @param ResourceManagerInterface $resourceManager
     */
    public function __construct(ResourceManagerInterface $resourceManager)
    {
        $this->resourceManager = $resourceManager;
    }

    /**
     * @param ResourceInterface $resource
     * @param int               $bufferLength
     *
     * @throws \League\Flysystem\FileNotFoundException
     *
     * @return StreamedResponse
     */
    public function getStreamedResponse(ResourceInterface $resource, $bufferLength = 512)
    {
        $fs = $this->resourceManager->getFilesystem($resource);
        if (!$fs->has($resource->getPath())) {
            throw new NotFoundHttpException("File not found {$resource->getPath()} ({$resource::getType()})");
        }

        $originalFilename = $resource->getPath();
        if ($resource) {
            $originalFilename = $resource->getOriginalFileName();
        }

        $stream = $fs->readStream($resource->getPath());
        if (!$stream) {
            throw new \RuntimeException("Unable to open stream to resource {$resource->getPath()}");
        }

        $response = new StreamedResponse(
            function () use ($stream, $bufferLength) {
                while (!feof($stream)) {
                    echo fread($stream, $bufferLength);
                }
                fclose($stream);
            },
            200
        );

        $response->headers->set(
            'Content-Disposition',
            $response->headers->makeDisposition(
                'attachment',
                $originalFilename,
                FilenameTransliterator::transliterateFilename($originalFilename)
            )
        );
        $response->headers->set('Content-Type', $fs->getMimetype($resource->getPath()));

        return $response;
    }
}


<?php

namespace Sidus\FileUploadBundle\Utilities;

/**
 * Transliterate filenames to escape forbidden characters
 */
class FilenameTransliterator
{
    /**
     * @param string $originalFilename
     *
     * @return string
     */
    public static function transliterateFilename($originalFilename)
    {
        $transliterator = \Transliterator::create('NFD; [:Nonspacing Mark:] Remove; NFC');
        $string = $transliterator->transliterate($originalFilename);

        return trim(
            preg_replace(
                '/[^\x20-\x7E]/',
                '_',
                $string
            ),
            '_'
        );
    }
}


<?php namespace Nord\Lumen\Rbac;

use Exception;
use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\ServiceProvider;
use Nord\Lumen\Rbac\Facades\RbacService as RbacServiceFacade;

class RbacServiceProvider extends ServiceProvider
{
    const CONFIG_KEY = 'rbac';

    /**
     * @inheritdoc
     */
    public function register()
    {
        // If RBAC haven't been configured yet, configure.
        if (!isset($this->app['config'][self::CONFIG_KEY])) {
            $this->app->configure(self::CONFIG_KEY);
        }

        $this->registerContainerBindings($this->app, $this->app['config']);
        $this->registerFacades();
        $this->registerConsoleCommands();
    }

    /**
     * @param Container        $container
     * @param ConfigRepository $config
     *
     * @throws Exception
     */
    protected function registerContainerBindings(Container $container, ConfigRepository $config)
    {
        if (!isset($config['rbac'])) {
            throw new Exception('RBAC configuration not registered.');
        }

        $rbacConfig = $config['rbac'];

        $container->alias('Nord\Lumen\Rbac\RbacService', 'Nord\Lumen\Rbac\Contracts\RbacService');
        $container->alias($rbacConfig['subjectProvider'], 'Nord\Lumen\Rbac\Contracts\SubjectProvider');

        $container->singleton('Nord\Lumen\Rbac\RbacService', function () use ($container) {
            return $this->createService($container);
        });
    }

    /**
     * Register the Facade.
     */
    protected function registerFacades()
    {
        if (!class_exists('Rbac')) {
            class_alias(RbacServiceFacade::class, 'Rbac');
        }
    }

    /**
     * Register console commands.
     */
    protected function registerConsoleCommands()
    {
        $this->commands([
            'Nord\Lumen\Rbac\Console\ConfigureCommand',
        ]);
    }

    /**
     * @param Container $container
     *
     * @return RbacService
     */
    protected function createService(Container $container)
    {
        $overseer        = $container->make('Crisu83\Overseer\Overseer');
        $subjectProvider = $container->make('Nord\Lumen\Rbac\Contracts\SubjectProvider');

        return new RbacService($overseer, $subjectProvider);
    }
}


<?php

    /**
     * Circle Class
     *
     * @package    SVGCreator
     * @subpackage Elements
     * @author     Sérgio Diniz
     * @version    1.0
     */

    namespace SVGCreator\Elements;

    class Circle extends \SVGCreator\Element {

    	const TYPE = \SVGCreator\Element::CIRCLE;

        /**
         * Holds the mandatory fields for this element
         *
         * @var array
         */
    	static protected $mandatoryFields = array(
        									'cx',
        									'cy',
        									'r'
        								);

        /**
         * Specific implementation for validation of element values
         *
         * @throws \SVGCreator\SVGException
         * @return void
         */
    	protected function validateElementValues() {
    		// Iterate over all fields
        	foreach ( self::$mandatoryFields as $field ) {
        		// If the field does not exist then exit with exception
        		if ( array_key_exists($field, $this->attributes) ) {
        			$value = (int) $this->attributes[$field];
        			if ( $value < 0 ) {
        				throw new \SVGCreator\SVGException("The " . $field . " value is lesser than 0, in element " . self::TYPE, 1);
        			}
        		}
        	}
    	}
    }


<?php

namespace Sidus\FileUploadBundle\Composer;

use Composer\Script\Event;
use Mopa\Bridge\Composer\Util\ComposerPathFinder;

/**
 * Composer script to symlink jQuery FileUpload plugin to the public directory of this bundle
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class ScriptHandler
{
    /**
     * Symlink JQuery File Upload plugin in the public directory
     *
     * @param Event $event
     *
     * @throws \Exception
     */
    public static function symlinkJQueryFileUpload(Event $event)
    {
        $IO = $event->getIO();
        $composer = $event->getComposer();
        $cmanager = new ComposerPathFinder($composer);
        $ds = DIRECTORY_SEPARATOR;
        $options = [
            'targetSuffix' => self::getTargetSuffix('jquery-file-upload'),
            'sourcePrefix' => "..{$ds}..{$ds}..{$ds}",
        ];
        list($symlinkTarget, $symlinkName) = $cmanager->getSymlinkFromComposer(
            'sidus/file-upload-bundle',
            'blueimp/jquery-file-upload',
            $options
        );

        $IO->write('Checking Symlink', false);
        if (false === self::checkSymlink($symlinkTarget, $symlinkName, true)) {
            $IO->write('Creating Symlink: '.$symlinkName, false);
            self::createSymlink($symlinkTarget, $symlinkName);
        }
        $IO->write('<info>OK</info>');
    }

    /**
     * Checks symlink's existence.
     *
     * @param string $symlinkTarget The Target
     * @param string $symlinkName   The Name
     * @param boolean $forceSymlink Force to be a link or throw exception
     *
     * @return boolean
     * @throws \Exception
     */
    public static function checkSymlink($symlinkTarget, $symlinkName, $forceSymlink = false)
    {
        if ($forceSymlink && file_exists($symlinkName) && !is_link($symlinkName)) {
            if ('link' !== filetype($symlinkName)) {
                throw new \UnexpectedValueException("{$symlinkName} exists and is not a link");
            }
        } elseif (is_link($symlinkName)) {
            $linkTarget = readlink($symlinkName);
            if ($linkTarget !== $symlinkTarget) {
                if (!$forceSymlink) {
                    throw new \UnexpectedValueException(
                        "Symlink '{$symlinkName}' points to '{$linkTarget}' instead of '{$symlinkTarget}'"
                    );
                }
                unlink($symlinkName);

                return false;
            }

            return true;
        }

        return false;
    }

    /**
     * Create the symlink.
     *
     * @param string $symlinkTarget The Target
     * @param string $symlinkName   The Name
     *
     * @throws \Exception
     */
    public static function createSymlink($symlinkTarget, $symlinkName)
    {
        if (false === @symlink($symlinkTarget, $symlinkName)) {
            throw new \UnexpectedValueException("An error occurred while creating symlink '{$symlinkName}'");
        }
        if (false === $target = readlink($symlinkName)) {
            throw new \UnexpectedValueException("Symlink {$symlinkName} points to target {$target}");
        }
    }

    protected static function getTargetSuffix($end = '')
    {
        $ds = DIRECTORY_SEPARATOR;

        return "{$ds}Resources{$ds}public{$ds}vendor".($end ? $ds.$end : '');
    }
}


<?php

namespace Sidus\FileUploadBundle\Utilities;

/**
 * Small utility class to handle binary size conversions
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class BinarySizeUtility
{
    /**
     * Takes a human-formatted binary size and return a number of octets
     *
     * @param string $size
     * @param string $fallbackUnits
     *
     * @throws \UnexpectedValueException
     *
     * @return int
     */
    public static function parse($size, $fallbackUnits = null)
    {
        preg_match('/^(\d+)[.,]?(\d*)\s*(\w*)$/', $size, $matches);
        if (empty($matches[1]) || (empty($matches[3]) && null === $fallbackUnits)) {
            throw new \UnexpectedValueException("Unable to parse : '{$size}'");
        }
        $oSize = $matches[1];
        if (!empty($matches[2])) {
            $oSize .= '.'.$matches[2];
        }
        $oSize = (float) $oSize;
        $unit = strtolower(empty($matches[3]) ? $fallbackUnits : $matches[3]);
        $byteMultiplier = 1;
        if ('b' === substr($unit, -1)) {
            $byteMultiplier = 8;
            $unit = substr($unit, 0, -1).'o';
        }
        if (!array_key_exists($unit, self::getBinarySizes())) {
            throw new \UnexpectedValueException("Unexpected unit {$unit}");
        }

        return (int) ($oSize * self::getBinarySizes()[$unit] * $byteMultiplier);
    }

    /**
     * Return a binary size in a human readable form.
     *
     * @param int    $size         number of octets
     * @param int    $decimals
     * @param string $decPoint
     * @param string $thousandsSep
     * @param string $unitSep
     *
     * @throws \UnexpectedValueException
     *
     * @return string
     */
    public static function format($size, $decimals = 2, $decPoint = '.', $thousandsSep = '', $unitSep = '')
    {
        $output = $unit = null;
        foreach (self::getBinarySizes() as $unit => $divider) {
            $output = $size / $divider;
            if ($output < 1000) {
                break;
            }
        }
        if (null === $output) {
            throw new \UnexpectedValueException("Unable to parse value: '{$size}'");
        }
        $unit = $unit === 'o' ? 'o' : ucfirst($unit);
        $trimmed = rtrim(rtrim(number_format($output, $decimals, $decPoint, $thousandsSep), '0'), $decPoint);
        $formatted = $trimmed.$unitSep.$unit;

        return str_replace(' ', utf8_encode(chr(160)), $formatted);
    }

    /**
     * @param int    $size
     * @param string $fallbackUnits
     *
     * @throws \UnexpectedValueException
     *
     * @return string
     */
    public static function reformat($size, $fallbackUnits = null)
    {
        return self::format(self::parse($size, $fallbackUnits));
    }

    /**
     * Unit table
     *
     * @return array
     */
    protected static function getBinarySizes()
    {
        return [
            // SI units
            'o' => 1,     // octet
            'ko' => 1e3,   // kilooctet
            'mo' => 1e6,   // mégaoctet
            'go' => 1e9,   // gigaoctet
            'to' => 1e12,  // téraoctet
            'po' => 1e15,  // pétaoctet
            'eo' => 1e18,  // exaoctet
            'zo' => 1e21,  // zettaoctet
            'yo' => 1e24,  // yottaoctet

            // Additionnal binary units
            'kio' => 2 ^ 10,  // kibioctet
            'mio' => 2 ^ 20,  // mébioctet
            'gio' => 2 ^ 30,  // gibioctet
            'tio' => 2 ^ 40,  // tébioctet
            'pio' => 2 ^ 50,  // pébioctet
            'eio' => 2 ^ 60,  // exbioctet
            'zio' => 2 ^ 70,  // zébioctet
            'yio' => 2 ^ 80,  // yobioctet
        ];
    }
}


<?php

namespace Sidus\FileUploadBundle;

use Sidus\FileUploadBundle\DependencyInjection\Compiler\FilesystemCompilerPass;
use Sidus\FileUploadBundle\DependencyInjection\Compiler\FormPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Sidus\FileUploadBundle\Registry\FilesystemRegistry;

/**
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class SidusFileUploadBundle extends Bundle
{
    /**
     * {@inheritdoc}
     */
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new FormPass());
        $container->addCompilerPass(
            new FilesystemCompilerPass(
                FilesystemRegistry::class,
                'oneup_flysystem.filesystem'
            )
        );
    }
}


<?php namespace Nord\Lumen\Rbac\Console;

use Exception;
use Illuminate\Console\Command;
use Nord\Lumen\Rbac\RbacService;

class ConfigureCommand extends Command
{

    /**
     * @inheritdoc
     */
    protected $signature = 'rbac:configure {--config= : Configuration file path.}';

    /**
     * @inheritdoc
     */
    protected $description = 'Configures the RBAC service from a file.';

    /**
     * Run the command.
     */
    public function handle()
    {
        $config = $this->option('config');

        if (!$config) {
            throw new Exception('Config option is required.');
        }

        $configPath = realpath($config);

        if (!file_exists($configPath)) {
            throw new Exception('Configuration file not found.');
        }

        $config = require($configPath);

        $this->info(sprintf('Configuring with file "%s".', $configPath));
        $this->getRbacService()->configure($config);
        $this->info('Configuration done.');
    }

    /**
     * @return RbacService
     */
    private function getRbacService()
    {
        return app(RbacService::class);
    }
}


<?php
	/**
	 * Main Element Class
	 *
	 * @todo  	   Don't accept/return mixed variables, so we can be fully compatible with php 7.0
	 * @package    SVGCreator
	 * @author     Sérgio Diniz
	 * @version    1.0
	 */

	namespace SVGCreator;

	/**
	 * Main element class
	 *
	 * @abstract
	 */
	abstract class Element {

		const CIRCLE = 'circle';
		const DEFS = 'defs';
		const GROUP = 'g';
		const LINE = 'line';
		const MARKER = 'marker';
		const PATH = 'path';
		const RECT = 'rect';
		const SVG = 'svg';
		const TEXT = 'text';
		const POLYGON = 'polygon';

		/**
		 * The array that holds all attributes
		 *
		 * @var array
		 */
		protected $attributes;

		/**
		 * The string that has all the element as a string
		 * @var string
		 */
		protected $elementString;

		/**
		 * Array with the child elements
		 *
		 * @var \SVGCreator\Element[]
		 */
		protected $childElements;

		/**
		 * Definitons array
		 *
		 * @var array
		 */
		private $defs;

		/**
		 * The text to put inside the element
		 *
		 * @var string
		 */
		private $text = null;

		public function __construct($attributes = array()) {

			// Setup the private variables
			$this->attributes = $attributes;
			$this->elementString = '';
			$this->childElements = array();
			$this->defs = array();
		}

		/**
		 * Returns the defs array for this element
		 *
		 * @return array
		 */
		protected function getDefs() {
			return $this->defs;
		}

		/**
		 * Set's the definition array
		 *
		 * @param array
		 *
		 * @return \SVGCreator\Element
		 */
		protected function setDefs($defs = array()) {
			$this->defs = $defs;
			return $this;
		}

		/**
		 * Set's the attributes for this element
		 *
		 * @param array  	$attributes  	An array with the attributes
		 */
		public function setAttributes($attributes) {
			$this->attributes = $attributes;
		}

		/**
		 * Adds an def element to the element
		 *
		 * @param \SVGCreator\Element $def
		 *
		 * @return \SVGCreator\Element
		 */
		public function addDef(\SVGCreator\Element $def) {
			$this->defs[] = $def;
			return $def;
		}

		/**
		 * Validates the element values
		 *
		 * @return void
		 */
		abstract protected function validateElementValues();

		/**
		 * Validates an element
		 *
		 * @return void
		 */
		private function validateElement() {
			$this->validateMandatoryAttribs();
			$this->validateElementValues();
		}

		/**
		 * Validates the mandatory attributes
		 *
		 * @return void
		 */
		protected function validateMandatoryAttribs() {
			// Iterate over all fields
	    	foreach ( static::$mandatoryFields as $field ) {
	    		// If the field does not exist then exit with exception
	    		if ( !array_key_exists($field, $this->attributes) ) {
	    			throw new \SVGCreator\SVGException("The field " . $field . " does not exist for " . static::TYPE . ".", 1);
	    		}
	    	}
		}

		/**
		 * Set's an attribute for the current element
		 *
		 * @param  mixed 	 $attrKey		The name of the attribute
		 * @param  string    $attrValue		The value of the attribute
		 *
		 * @return mixed 					Could be an \SVGCreator\Element or could be the value of the attribute,
		 *                          		this means that the second parameter wasn't passed
		 */
		public function attr($attrKey, $attrValue = null) {
			if ( !is_null($attrValue) ) {
				if ( !is_array($this->attributes) ) {
					$this->attributes = array();
				}
				$this->attributes[$attrKey] = $attrValue;
				return $this;
			} else {
				if ( array_key_exists($attrKey, $this->attributes) ) {
					return $this->attributes[$attrKey];
				} else {
					return null;
				}
			}

		}

		/**
		 * Factory for building the several elements available
		 *
		 * @param  string  			 $type 			The type of element to build
		 *
		 * @return \SVGCreator\Element
		 */
		private function factoryElement($type) {
			switch ( $type ) {
				case \SVGCreator\Element::CIRCLE:
					return new \SVGCreator\Elements\Circle();
					break;
				case \SVGCreator\Element::DEFS:
					return new \SVGCreator\Elements\Defs();
					break;
				case \SVGCreator\Element::GROUP:
					return new \SVGCreator\Elements\Group();
					break;
				case \SVGCreator\Element::LINE:
					return new \SVGCreator\Elements\Line();
					break;
				case \SVGCreator\Element::MARKER:
					return new \SVGCreator\Elements\Marker();
					break;
				case \SVGCreator\Element::PATH:
					return new \SVGCreator\Elements\Path();
					break;
				case \SVGCreator\Element::RECT:
					return new \SVGCreator\Elements\Rect();
					break;
				case \SVGCreator\Element::SVG:
					return new \SVGCreator\Elements\Svg();
					break;
				case \SVGCreator\Element::TEXT:
					return new \SVGCreator\Elements\Text();
					break;
				case \SVGCreator\Element::POLYGON:
					return new \SVGCreator\Elements\Polygon();
					break;
				default:
					throw new \SVGCreator\SVGException("The tag " . $type . " is not implemented yet", 1);
					break;
			}
		}

		/**
		 * Appends an element to the current element
		 *
		 * @param  mixed            	$element 	The element to append to this element could be an object \SVGCreator\Element or a string of the type to create
		 *
		 * @return \SVGCreator\Element 				The element append
		 */
		public function append($element) {
			if ( true === $element instanceof \SVGCreator\Element ) {
				$this->childElements[] = $element;
				return $element;
			} else {
				$elementCreated = $this->factoryElement($element);
				$this->childElements[] = $elementCreated;
				return $elementCreated;
			}
		}

		/**
		 * Returns the element string
		 *
		 * @return string  				Returns the element string
		 */
		public function getString() {
			// Validate the element first of all!
			$this->validateElement();

			// Start writing the tag
			$elementStringData = '';
			$elementStringData = '<' . static::TYPE;
			foreach ( $this->attributes as $key => $data ) {
				$elementStringData .= ' ' . $key . '="' . $data . '"';
			}
			// Close the initiating tag
			$elementStringData .= '>';

			if ( $this->text !== null ) {
				$elementStringData .= $this->text;
			}

			// If it has child elements we have to write them!
			if ( count($this->childElements) > 0 ) {

				// See if there are definitions to put if the type is svg this is run here
				// because the definition area should be the first to appear!
				if  ( static::TYPE == 'svg' ) {
					foreach ( $this->childElements as $childElement ) {
						// Let's get the definitions array from the child element and propagate them to the top!
						$this->defs = array_merge($this->defs, $childElement->getDefs());
					}

					// If there are definitions to add then add them
					if ( count($this->defs) > 0 ) {
						// Create the defs area
						$defArea = new \SVGCreator\Elements\Defs();
						foreach ( $this->defs as $def ) {
							// Append all elements to def area
							$defArea->append($def);
						}
						// Get the defarea xml
						$elementStringData .= $defArea->getString();
					}
				}

				// Iterate trough each element and write it's child element
				foreach ( $this->childElements as $childElement ) {
					$elementStringData .= $childElement->getString();
				}
			}
			// Always close the tag with no self closing, simplifys things a bit
			// and it's not a couple of bytes that has us worried
			$elementStringData .= '</' . static::TYPE . '>';

			$this->elementString = $elementStringData;

			return $this->elementString;
		}

		/**
		 * Saves the element as a file
		 *
		 * @param  string 	$fileName 	The filename with path to save
		 *
		 * @return string 				Returns the filename or false on failure
		 */
		public function saveElementAsFile($fileName) {
			// Get the element string
			$this->getString();
			if ( false === file_put_contents($fileName, $this->elementString) ) {
				return false;
			} else {
				return $fileName;
			}
		}

		/**
		 * Set's or returns the text of the element
		 *
		 * @param  string 	 $text 	 The text to add to the element
		 *
		 * @return \SVGCreator\Element
		 */
		public function text($text) {
			$this->text = $text;

			return $this;
		}
	}


<?php
namespace Czim\CmsTheme\Providers;

use Czim\CmsCore\Contracts\Core\CoreInterface;
use Czim\CmsCore\Support\Enums\Component;
use Czim\CmsTheme\Contracts\Menu\MenuInterface;
use Czim\CmsTheme\Http\ViewComposers\MenuComposer;
use Czim\CmsTheme\Http\ViewComposers\TopComposer;
use Czim\CmsTheme\Menu\Menu;
use Illuminate\Support\ServiceProvider;

class CmsThemeServiceProvider extends ServiceProvider
{

    public function boot()
    {
        $this->bootConfig()
             ->loadTranslations();
    }

    public function register()
    {
        $this->registerConfig()
             ->registerMenu()
             ->loadViews()
             ->registerViewComposers()
             ->publishPublicAssets()
             ->publishResources();
    }

    /**
     * @return $this
     */
    protected function registerConfig()
    {
        $this->mergeConfigFrom(
            realpath(dirname(__DIR__) . '/../config/cms-theme.php'),
            'cms-theme'
        );

        return $this;
    }

    /**
     * Registers the Menu with the service container.
     *
     * @return $this
     */
    protected function registerMenu()
    {
        $this->app->singleton(MenuInterface::class, Menu::class);

        return $this;
    }

    /**
     * Loads basic CMS views.
     *
     * @return $this
     */
    protected function loadViews()
    {
        $this->loadViewsFrom(
            realpath(dirname(__DIR__) . '/../resources/views'),
            'cms'
        );

        return $this;
    }

    /**
     * @return $this
     */
    protected function loadTranslations()
    {
        $this->loadTranslationsFrom(
            realpath(dirname(__DIR__) . '/../resources/lang'),
            'cms'
        );

        return $this;
    }

    /**
     * @return $this
     */
    protected function publishPublicAssets()
    {
        $this->publishes([
            realpath(dirname(__DIR__)) . '/../resources/assets/build' => public_path('_cms'),
            realpath(dirname(__DIR__)) . '/../resources/assets/img'   => public_path('_cms/img'),
        ], 'public');

        return $this;
    }

    /**
     * @return $this
     */
    protected function publishResources()
    {
        $this->publishes([
            realpath(dirname(__DIR__)) . '/../resources/lang' => base_path('resources/lang/vendor/cms'),
        ], 'assets');

        return $this;
    }

    /**
     * @return $this
     */
    protected function bootConfig()
    {
        $this->publishes([
            realpath(dirname(__DIR__) . '/../config/cms-theme.php') => config_path('cms-theme.php'),
        ]);

        return $this;
    }

    /**
     * Registers routes for the entire CMS.
     *
     * @return $this
     */
    protected function registerViewComposers()
    {
        $core = $this->getCore();

        view()->composer($core->config('views.menu'), MenuComposer::class);
        view()->composer($core->config('views.top'), TopComposer::class);

        return $this;
    }

    /**
     * @return CoreInterface
     */
    protected function getCore()
    {
        return app(Component::CORE);
    }

}


<?php

namespace Bolt\Collection;

/**
 * An OO implementation of PHP's array functionality and more.
 *
 * Generally only methods dealing with a single item mutate the current bag,
 * all others return a new bag.
 *
 *

 * Be careful when exposing a `MutableBag` publicly, like with a getter method.
 *
 * It is an object so it is returned by reference,
 * as opposed to arrays that are returned by value.
 * This means someone could take the bag and modify it, and since it is the same
 * instance the modifications are applied to the bag in your class as well.
 * There could be some use-cases for this as long as you are aware of it.
 *
 * This can be mitigated by type-hinting or documenting that the return value is
 * a `Bag`, since `MutableBag` _extends_ `Bag`, so only read-only access is allowed.
 *
 * This still returns the `MutableBag` though so the documented API could
 * be broken and the bag modified anyways.
 * If you want to _ensure_ that the bag cannot be not modified, you can return a
 * copy of the bag with the getter method. You can copy the bag by calling
 * the {@see Bag::immutable} or {@see mutable} methods or by _cloning_ it.
 * This does have a performance penalty though, as every call to the getter creates
 * a new bag. Also if the documented return value is still `MutableBag` it could
 * be confusing to users trying to modify the bag and not seeing results take affect.
 *
 * @author Carson Full <carsonfull@gmail.com>
 */
class MutableBag extends Bag
{
    // region Mutating Methods

    /**
     * Adds an item to the end of this bag.
     *
     * @param mixed $item The item to append
     */
    public function add($item)
    {
        $this->items[] = $item;
    }

    /**
     * Adds an item to the beginning of this bag.
     *
     * @param mixed $item The item to prepend
     */
    public function prepend($item)
    {
        array_unshift($this->items, $item);
    }

    /**
     * Sets a value by key.
     *
     * @param string $key   The key
     * @param mixed  $value The value
     */
    public function set($key, $value)
    {
        $this->items[$key] = $value;
    }

    /**
     * Sets a value using path syntax to set nested data.
     * Inner arrays will be created as needed to set the value.
     *
     * Example:
     *
     *     // Set an item at a nested structure
     *     $bag->setPath('foo/bar', 'color');
     *
     *     // Append to a list in a nested structure
     *     $bag->get($data, 'foo/baz');
     *     // => null
     *     $bag->setPath('foo/baz/[]', 'a');
     *     $bag->setPath('foo/baz/[]', 'b');
     *     $bag->getPath('foo/baz');
     *     // => ['a', 'b']
     *
     * This function does not support keys that contain `/` or `[]` characters
     * because these are special tokens used when traversing the data structure.
     * A value may be appended to an existing array by using `[]` as the final
     * key of a path.
     *
     *

     * Note: To set values in arrays that are in `ArrayAccess` objects their
     * `offsetGet()` method needs to be able to return arrays by reference.
     * See {@see MutableBag} for an example of this.
     *
     * @param string $path  The path to traverse and set the value at
     * @param mixed  $value The value to set
     *
     * @throws \RuntimeException when trying to set a path that travels through a scalar value
     * @throws \RuntimeException when trying to set a value in an array that is in an `ArrayAccess` object
     *                           which cannot retrieve arrays by reference
     */
    public function setPath($path, $value)
    {
        Arr::set($this->items, $path, $value);
    }

    /**
     * Remove all items from bag.
     */
    public function clear()
    {
        $this->items = [];
    }

    /**
     * Removes and returns the item at the specified `$key` from the bag.
     *
     * @param string|int $key     The key of the item to remove
     * @param mixed|null $default The default value to return if the key is not found
     *
     * @return mixed The removed item or `$default`, if the bag did not contain the item
     */
    public function remove($key, $default = null)
    {
        if (!$this->has($key)) {
            return $default;
        }

        $removed = $this->items[$key];
        unset($this->items[$key]);

        return $removed;
    }

    /**
     * Removes and returns a value using path syntax to retrieve nested data.
     *
     * Example:
     *
     *     $bag->removePath('foo/bar');
     *     // => 'baz'
     *     $bag->removePath('foo/bar');
     *     // => null
     *
     * This function does not support keys that contain `/`.
     *
     *

     * Note: To remove values in arrays that are in `ArrayAccess` objects their
     * `offsetGet()` method needs to be able to return arrays by reference.
     * See {@see MutableBag} for an example of this.
     *
     * @param string     $path    Path to traverse and remove the value at
     * @param mixed|null $default Default value to return if key does not exist
     *
     * @throws \RuntimeException when trying to set a path that travels through a scalar value
     * @throws \RuntimeException when trying to set a value in an array that is in an `ArrayAccess` object
     *                           which cannot retrieve arrays by reference
     *
     * @return mixed
     */
    public function removePath($path, $default = null)
    {
        return Arr::remove($this->items, $path, $default);
    }

    /**
     * Removes the given item from the bag if it is found.
     *
     * @param mixed $item
     */
    public function removeItem($item)
    {
        $key = array_search($item, $this->items, true);

        if ($key !== false) {
            unset($this->items[$key]);
        }
    }

    /**
     * Removes and returns the first item in the list.
     *
     * @return mixed|null
     */
    public function removeFirst()
    {
        return array_shift($this->items);
    }

    /**
     * Removes and returns the last item in the list.
     *
     * @return mixed|null
     */
    public function removeLast()
    {
        return array_pop($this->items);
    }

    // endregion

    // region Internal Methods

    /**
     * Don't call directly. Used for ArrayAccess.
     *
     * @internal
     *
     * @inheritdoc
     */
    public function &offsetGet($offset)
    {
        $result = null;
        if (isset($this->items[$offset])) {
            $result = &$this->items[$offset];
        }

        return $result;
    }

    /**
     * Don't call directly. Used for ArrayAccess.
     *
     * @internal
     *
     * @inheritdoc
     */
    public function offsetSet($offset, $value)
    {
        if ($offset === null) {
            $this->add($value);
        } else {
            $this->set($offset, $value);
        }
    }

    /**
     * Don't call directly. Used for ArrayAccess.
     *
     * @internal
     *
     * @inheritdoc
     */
    public function offsetUnset($offset)
    {
        $this->remove($offset);
    }

    // endregion
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Factory;

use Polidog\Chatwork\Entity\Collection\MemberCollection;
use Polidog\Chatwork\Entity\Member;

class MemberFactory extends AbstractFactory
{
    protected $collectionName = MemberCollection::class;

    /**
     * @param array $data
     *
     * @return Member
     */
    public function entity(array $data = [])
    {
        $member = new Member();
        $member->role = $data['role'];
        unset($data['role']);

        // @todo ここでFactoryオブジェクトを生成するのをなんとかしたい...
        $userFactory = new UserFactory();
        $member->account = $userFactory->entity($data);

        return $member;
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Factory;

use Cake\Utility\Inflector;
use Polidog\Chatwork\Entity\Room;

class RoomFactory extends AbstractFactory
{
    /**
     * @param array $data
     *
     * @return Room
     */
    public function entity(array $data = [])
    {
        $room = new Room();
        foreach ($data as $key => $value) {
            $property = Inflector::variable($key);
            $room->$property = $value;
        }

        return $room;
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Factory;

use Cake\Utility\Inflector;
use Polidog\Chatwork\Entity\User;

class UserFactory extends AbstractFactory
{
    /**
     * @param array $data
     *
     * @return User
     */
    public function entity(array $data = [])
    {
        $user = new User();
        foreach ($data as $key => $value) {
            $property = Inflector::variable($key);
            $user->$property = $value;
        }

        return $user;
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Api\Rooms;

use Polidog\Chatwork\Client\ClientInterface;
use Polidog\Chatwork\Entity\Collection\CollectionInterface;
use Polidog\Chatwork\Entity\EntityInterface;
use Polidog\Chatwork\Entity\Factory\MessageFactory;
use Polidog\Chatwork\Entity\Message;

/**
 * Class Messages.
 */
class Messages
{
    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @var MessageFactory
     */
    private $factory;

    /**
     * @var int
     */
    private $roomId;

    /**
     * Messages constructor.
     *
     * @param ClientInterface $client
     * @param MessageFactory  $factory
     * @param int             $roomId
     */
    public function __construct(ClientInterface $client, MessageFactory $factory, int $roomId)
    {
        $this->client = $client;
        $this->factory = $factory;
        $this->roomId = $roomId;
    }

    /**
     * @param bool $force
     *
     * @return CollectionInterface
     */
    public function show($force = false)
    {
        return $this->factory->collection(
            $this->client->get(
                "rooms/{$this->roomId}/messages",
                [
                    'force' => (int) $force,
                ]
            )
        );
    }

    /**
     * @param $id
     * @param bool $force
     *
     * @return Message|EntityInterface
     */
    public function detail($id, $force = false)
    {
        return $this->factory->entity(
            $this->client->get(
                "rooms/{$this->roomId}/messages/{$id}",
                [
                    'force' => (int) $force,
                ]
            )
        );
    }

    /**
     * @param Message $message
     */
    public function create(Message $message)
    {
        $result = $this->client->post(
            "rooms/{$this->roomId}/messages",
            [
                'body' => $message->body,
            ]
        );

        $message->messageId = $result['message_id'];
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Api\Rooms;

use Polidog\Chatwork\Client\ClientInterface;
use Polidog\Chatwork\Entity\Collection\CollectionInterface;
use Polidog\Chatwork\Entity\Factory\TaskFactory;
use Polidog\Chatwork\Entity\Task;

class Tasks
{
    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @var TaskFactory
     */
    private $factory;

    /**
     * @var int
     */
    private $roomId;

    /**
     * Tasks constructor.
     *
     * @param ClientInterface $client
     * @param TaskFactory     $factory
     * @param int             $roomId
     */
    public function __construct(ClientInterface $client, TaskFactory $factory, int $roomId)
    {
        $this->client = $client;
        $this->factory = $factory;
        $this->roomId = $roomId;
    }

    /**
     * @param array $options
     *
     * @return CollectionInterface
     */
    public function show(array $options = [])
    {
        return $this->factory->collection(
            $this->client->get(
                "rooms/{$this->roomId}/tasks",
                $options
            )
        );
    }

    /**
     * @param int $id
     *
     * @return Task
     */
    public function detail($id)
    {
        return $this->factory->entity(
            $this->client->get(
                "rooms/{$this->roomId}/tasks/{$id}"
            )
        );
    }

    /**
     * @param string         $body
     * @param array          $toIds
     * @param \DateTime|null $limit
     *
     * @return int[] task ids
     */
    public function create(string $body, array $toIds, \DateTime $limit = null): array
    {
        return $this->client->post(
            "rooms/{$this->roomId}/tasks",
            [
                'body' => $body,
                'to_ids' => implode(',', $toIds),
                'limit' => $limit instanceof \DateTime ? $limit->getTimestamp() : null,
            ]
        );
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Api\Rooms;

use Polidog\Chatwork\Client\ClientInterface;
use Polidog\Chatwork\Entity\Collection\CollectionInterface;
use Polidog\Chatwork\Entity\Factory\FileFactory;
use Polidog\Chatwork\Entity\File;

/**
 * Class Files.
 */
class Files
{
    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @var FileFactory
     */
    private $factory;

    /**
     * @var int
     */
    private $roomId;

    /**
     * Files constructor.
     *
     * @param ClientInterface $client
     * @param FileFactory     $factory
     * @param int             $roomId
     */
    public function __construct(ClientInterface $client, FileFactory $factory, int $roomId)
    {
        $this->client = $client;
        $this->factory = $factory;
        $this->roomId = $roomId;
    }

    /**
     * @param null $accountId
     *
     * @return CollectionInterface
     */
    public function show($accountId = null)
    {
        $options = [
        ];

        if (!is_null($accountId)) {
            $options['account_id'] = $accountId;
        }

        return $this->factory->collection(
            $this->client->get(
                "rooms/{$this->roomId}/files",
                $options
            )
        );
    }

    /**
     * @param $id
     *
     * @return File
     */
    public function detail($id)
    {
        return $this->factory->entity(
          $this->client->get(
              "rooms/{$this->roomId}/files/{$id}"
          )
        );
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Factory;

use Polidog\Chatwork\Entity\Collection\EntityCollection;

abstract class AbstractFactory implements FactoryInterface
{
    protected $collectionName = EntityCollection::class;

    /**
     * @param array $listUp
     *
     * @return EntityCollection
     */
    public function collection($listUp = null)
    {
        $collection = new $this->collectionName();
        if (!is_array($listUp)) {
            return $collection;
        }

        foreach ($listUp as $value) {
            $collection->add($this->entity($value));
        }

        return $collection;
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Factory;

use Cake\Utility\Inflector;
use Polidog\Chatwork\Entity\Status;

class StatusFactory extends AbstractFactory
{
    /**
     * @param array $data
     *
     * @return Status
     */
    public function entity(array $data = [])
    {
        $status = new Status();
        foreach ($data as $key => $value) {
            $property = Inflector::variable($key);
            $status->$property = $value;
        }

        return $status;
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Factory;

use Cake\Utility\Inflector;
use Polidog\Chatwork\Entity\IncomingRequest;

class IncomingRequestsFactory extends AbstractFactory
{
    /**
     * @param array $data
     *
     * @return IncomingRequest
     */
    public function entity(array $data = [])
    {
        $incomingRequest = new IncomingRequest();
        foreach ($data as $key => $value) {
            $property = Inflector::variable($key);
            $incomingRequest->$property = $value;
        }

        return $incomingRequest;
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Api;

use Polidog\Chatwork\Client\ClientInterface;
use Polidog\Chatwork\Entity\Collection\CollectionInterface;
use Polidog\Chatwork\Entity\Collection\MemberCollection;
use Polidog\Chatwork\Entity\Factory\FileFactory;
use Polidog\Chatwork\Entity\Factory\MemberFactory;
use Polidog\Chatwork\Entity\Factory\MessageFactory;
use Polidog\Chatwork\Entity\Factory\RoomFactory;
use Polidog\Chatwork\Entity\Factory\TaskFactory;
use Polidog\Chatwork\Entity\Room;
use Polidog\Chatwork\Exception\InvalidArgumentException;

class Rooms
{
    const ACTION_TYPE_LEAVE = 'leave';
    const ACTION_TYPE_DELETE = 'delete';

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

    /**
     * @var RoomFactory
     */
    private $factory;

    /**
     * Rooms constructor.
     *
     * @param ClientInterface $client
     * @param RoomFactory     $factory
     */
    public function __construct(ClientInterface $client, RoomFactory $factory)
    {
        $this->client = $client;
        $this->factory = $factory;
    }

    /**
     * 自分のチャット一覧の取得.
     *
     * @return CollectionInterface
     */
    public function show()
    {
        return $this->factory->collection(
            $this->client->get('rooms')
        );
    }

    /**
     * @param $id
     *
     * @return Room
     */
    public function detail($id)
    {
        return $this->factory->entity(
            $this->client->get("rooms/{$id}")
        );
    }

    /**
     * グループチャットを新規作成.
     *
     * @param Room             $room
     * @param MemberCollection $members
     *
     * @return Room
     */
    public function create(Room $room, MemberCollection $members)
    {
        $result = $this->client->post('rooms', [
            'name' => $room->name,
            'description' => $room->description,
            'members_admin_ids' => implode(',', $members->getAdminIds()),
            'members_member_ids' => implode(',', $members->getMemberIds()),
            'members_readonly_ids' => implode(',', $members->getReadonlyIds()),
        ]);
        $room->roomId = $result['room_id'];

        return $room;
    }

    /**
     * チャットの名前、アイコンをアップデート.
     *
     * @param Room $room
     */
    public function update(Room $room)
    {
        $this->client->put(
            "rooms/{$room->roomId}",
            [$room->toArray()]
        );
    }

    /**
     * グループチャットを退席/削除する.
     *
     * @param Room   $room
     * @param string $actionType leave or delete
     *
     * @throws InvalidArgumentException
     */
    public function remove(Room $room, $actionType)
    {
        if ('leave' !== $actionType && 'delete' !== $actionType) {
            throw new InvalidArgumentException('ActionType is only leave or delete');
        }

        $this->client->delete(
            "rooms/{$room->roomId}",
            [
                'action_type' => $actionType,
            ]
        );
    }

    /**
     * @param $roomId
     *
     * @return Rooms\Members
     */
    public function members(int $roomId)
    {
        return new Rooms\Members($this->client, new MemberFactory(), $roomId);
    }

    /**
     * @param $roomId
     *
     * @return Rooms\Messages
     */
    public function messages(int $roomId)
    {
        return new Rooms\Messages($this->client, new MessageFactory(), $roomId);
    }

    /**
     * @param $roomId
     *
     * @return Rooms\Tasks
     */
    public function tasks(int $roomId)
    {
        return new Rooms\Tasks($this->client, new TaskFactory(), $roomId);
    }

    /**
     * @param int $roomId
     *
     * @return Rooms\Files
     */
    public function files(int $roomId)
    {
        return new Rooms\Files($this->client, new FileFactory(), $roomId);
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Collection;

use Polidog\Chatwork\Entity\EntityInterface;
use Polidog\Chatwork\Exception\OutOfBoundsException;

abstract class AbstractCollection implements \IteratorAggregate, \Countable, CollectionInterface
{
    protected $entities = [];

    /**
     * @param array $entities
     */
    public function __construct(array $entities = [])
    {
        $this->entities = $entities;
    }

    /**
     * {@inheritdoc}
     */
    public function add(EntityInterface $entity)
    {
        $this->entities[] = $entity;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function get($idx)
    {
        if (!array_key_exists($idx, $this->entities)) {
            throw new OutOfBoundsException('index not found, index:'.$idx);
        }

        return $this->entities[$idx];
    }

    /**
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        foreach ($this->entities as $key => $entity) {
            yield $key => $entity;
        }
    }

    public function count()
    {
        return count($this->entities);
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Factory;

use Cake\Utility\Inflector;
use Polidog\Chatwork\Entity\Message;

class MessageFactory extends AbstractFactory
{
    /**
     * @param array $data
     *
     * @return Message
     */
    public function entity(array $data = [])
    {
        // @todo ここでnewするのなんとかしたい・・・
        $userFactory = new UserFactory();
        $message = new Message();

        $message->account = $userFactory->entity($data['account']);
        unset($data['account']);

        foreach ($data as $key => $value) {
            $property = Inflector::variable($key);
            $message->$property = $value;
        }

        return $message;
    }
}


<?php

declare(strict_types=1);
/**
 * Created by PhpStorm.
 * User: polidog
 * Date: 14/12/24
 * Time: 19:05.
 */

namespace Polidog\Chatwork\Entity\Factory;

use Cake\Utility\Inflector;
use Polidog\Chatwork\Entity\Task;

class TaskFactory extends AbstractFactory
{
    /**
     * @param array $data
     *
     * @return Task
     */
    public function entity(array $data = [])
    {
        // @todo あとでroomオブジェクトの生成方法とかを見直す
        $roomFactory = new RoomFactory();
        $userFactory = new UserFactory();

        $task = new Task();
        foreach ($data as $key => $value) {
            $property = Inflector::variable($key);
            if ('room' == $property) {
                $task->$property = $roomFactory->entity($value);
            } elseif ('assignedByAccount' == $property || 'account' == $property) {
                $task->$property = $userFactory->entity($value);
            } else {
                $task->$property = $value;
            }
        }

        return $task;
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Factory;

use Cake\Utility\Inflector;
use Polidog\Chatwork\Entity\File;

class FileFactory extends AbstractFactory
{
    /**
     * @param array $data
     *
     * @return File
     */
    public function entity(array $data = [])
    {
        $userFactory = new UserFactory();
        $file = new File();
        $file->account = $userFactory->entity($data['account']);
        unset($data['account']);

        foreach ($data as $key => $value) {
            $property = Inflector::variable($key);
            $file->$property = $value;
        }

        return $file;
    }
}


<?php

namespace Incenteev\HashedAssetBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;

class IncenteevHashedAssetExtension extends ConfigurableExtension
{
    /**
     * {@inheritdoc}
     */
    protected function loadInternal(array $config, ContainerBuilder $container)
    {
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.xml');

        $container->getDefinition('incenteev_hashed_asset.file_hasher')
            ->replaceArgument(0, $config['web_root']);

        $container->getDefinition('incenteev_hashed_asset.strategy')
            ->replaceArgument(1, $config['version_format']);

        if (!$container->getParameter('kernel.debug')) {
            $loader->load('cache.xml');

            $container->getDefinition('incenteev_hashed_asset.asset_finder')
                ->replaceArgument(0, $config['web_root']);
        }
    }
}


<?php

declare(strict_types=1);

namespace Polidog\Chatwork\Entity\Collection;

use Polidog\Chatwork\Entity\EntityInterface;
use Polidog\Chatwork\Entity\Member;

class MemberCollection extends EntityCollection
{
    public function add(EntityInterface $entity)
    {
        assert($entity instanceof Member);

        return parent::add($entity);
    }

    /**
     * @return array
     */
    public function getAdminIds()
    {
        return $this->_getIds('admin');
    }

    /**
     * @return array
     */
    public function getMemberIds()
    {
        return $this->_getIds('member');
    }

    /**
     * @return array
     */
    public function getReadonlyIds()
    {
        return $this->_getIds('readonly');
    }

    /**
     * @param string $type
     *
     * @return array
     */
    private function _getIds($type)
    {
        $ids = [];
        foreach ($this->entities as $entity) {
            if ($entity->role == $type) {
                $ids[] = $entity->account->accountId;
            }
        }

        return $ids;
    }
}


<?php namespace Laraplus\Data;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Query\Grammars\Grammar;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Query\Grammars\SqlServerGrammar;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;

class TranslatableScope implements Scope
{
    protected $table;

    protected $i18nTable;

    protected $locale;

    protected $fallback;

    protected $joinType = 'leftJoin';

    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder $builder
     * @param  \Illuminate\Database\Eloquent\Model $model
     * @return void
     */
    public function apply(EloquentBuilder $builder, Eloquent $model)
    {
        $this->table = $model->getTable();
        $this->locale = $model->getLocale();
        $this->i18nTable = $model->getI18nTable();
        $this->fallback = $model->getFallbackLocale();

        if(!starts_with($this->table, 'laravel_reserved_')) {
            $this->createJoin($builder, $model);
            $this->createWhere($builder, $model);
            $this->createSelect($builder, $model);
        }
    }

    /**
     * @param  \Illuminate\Database\Eloquent\Builder $builder
     * @param  \Illuminate\Database\Eloquent\Model $model
     */
    protected function createJoin(EloquentBuilder $builder, Eloquent $model)
    {
        $joinType = $this->getJoinType($model);

        $clause = $this->getJoinClause($model, $this->locale, $this->i18nTable);
        $builder->$joinType($this->i18nTable, $clause);

        if($model->shouldFallback()) {
            $clause = $this->getJoinClause($model, $this->fallback, $this->i18nTable . '_fallback');
            $builder->$joinType("{$this->i18nTable} as {$this->i18nTable}_fallback", $clause);
        }
    }

    /**
     * @param \Illuminate\Database\Eloquent\Model $model
     * @return string
     */
    protected function getJoinType(Eloquent $model)
    {
        $innerJoin = !$model->shouldFallback() && $model->getOnlyTranslated();

        return $innerJoin ? 'join' : 'leftJoin';
    }

    /**
     * @param \Illuminate\Database\Eloquent\Model $model
     * @param string $locale
     * @param string $alias
     * @return callable
     */
    protected function getJoinClause(Eloquent $model, $locale, $alias)
    {
        return function (JoinClause $join) use ($model, $locale, $alias) {
            $primary = $model->getKeyName();
            $foreign = $model->getForeignKey();
            $langKey = $model->getLocaleKey();

            $join->on($alias . '.' . $foreign, '=', $this->table . '.' . $primary)
                ->where($alias . '.' . $langKey, '=', $locale);
        };
    }

    /**
     * @param \Illuminate\Database\Eloquent\Builder $builder
     * @param \Illuminate\Database\Eloquent\Model $model
     */
    protected function createWhere(EloquentBuilder $builder, Eloquent $model)
    {
        if($model->getOnlyTranslated() && $model->shouldFallback()) {
            $key = $model->getForeignKey();
            $primary = "{$this->i18nTable}.{$key}";
            $fallback = "{$this->i18nTable}_fallback.{$key}";

            $ifNull = $builder->getQuery()->compileIfNull($primary, $fallback);

            $builder->whereRaw("$ifNull is not null");
        }
    }

    /**
     * @param \Illuminate\Database\Eloquent\Builder $builder
     * @param \Illuminate\Database\Eloquent\Model $model
     */
    protected function createSelect(EloquentBuilder $builder, Eloquent $model)
    {
        if($builder->getQuery()->columns) {
            return;
        }

        $select = $this->formatColumns($builder, $model);

        $builder->select(array_merge([$this->table . '.*'], $select));
    }

    /**
     * @param \Illuminate\Database\Eloquent\Builder $builder
     * @param \Illuminate\Database\Eloquent\Model $model
     * @return array
     */
    protected function formatColumns(EloquentBuilder $builder, Eloquent $model)
    {
        $map = function ($field) use ($builder, $model) {
            if (!$model->shouldFallback()) {
                return "{$this->i18nTable}.{$field}";
            }

            $primary = "{$this->i18nTable}.{$field}";
            $fallback = "{$this->i18nTable}_fallback.{$field}";
            $alias = $field;

            return new Expression($builder->getQuery()->compileIfNull($primary, $fallback, $alias));
        };

        return array_map($map, $model->translatableAttributes());
    }

    /**
     * @param Grammar $grammar
     * @return string
     */
    protected function getIfNull(Grammar $grammar)
    {
        return $grammar instanceof SqlServerGrammar ? 'isnull' : 'ifnull';
    }

    /**
     * Extend the builder.
     * @param Builder $builder
     */
    public function extend(EloquentBuilder $builder)
    {
        $builder->macro('onlyTranslated', function (EloquentBuilder $builder, $locale = null) {
            $builder->getModel()->setOnlyTranslated(true);

            if($locale) {
                $builder->getModel()->setLocale($locale);
            }

            return $builder;
        });

        $builder->macro('withUntranslated', function (EloquentBuilder $builder) {
            $builder->getModel()->setOnlyTranslated(false);

            return $builder;
        });

        $builder->macro('withFallback', function (EloquentBuilder $builder, $fallbackLocale = null) {
            $builder->getModel()->setWithFallback(true);

            if($fallbackLocale) {
                $builder->getModel()->setFallbackLocale($fallbackLocale);
            }

            return $builder;
        });

        $builder->macro('withoutFallback', function (EloquentBuilder $builder) {
            $builder->getModel()->setWithFallback(false);

            return $builder;
        });

        $builder->macro('translateInto', function (EloquentBuilder $builder, $locale) {
            if($locale) {
                $builder->getModel()->setLocale($locale);
            }

            return $builder;
        });

        $builder->macro('withoutTranslations', function (EloquentBuilder $builder) {
            $builder->withoutGlobalScope(static::class);

            return $builder;
        });

        $builder->macro('withAllTranslations', function (EloquentBuilder $builder) {
            $builder->withoutGlobalScope(static::class)
                ->with('translations');

            return $builder;
        });
    }
}


<?php

namespace SilverStripe\SelectUpload;

use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\TreeDropdownField;
use SilverStripe\Assets\Folder;
use SilverStripe\Control\Session;

/**
 * Represents a TreeDropdownField for folders which remembers the last folder selected
 */
class FolderDropdownField extends TreeDropdownField
{

    public function __construct(
        $name,
        $title = null,
        $sourceObject = Folder::class,
        $keyField = 'ID',
        $labelField = 'TreeTitle',
        $showSearch = true
    ) {
        parent::__construct($name, $title, $sourceObject, $keyField, $labelField, $showSearch);
        $this->setValue($this->getLastFolderID());
    }

    /**
     * Set the last folder selected
     *
     * @param int $folderID Folder ID
     */
    public function setLastFolderID($folderID)
    {
        $request = Controller::curr()->getRequest();
        $session = $request->getSession();
        $session->set(get_class() . '.FolderID', $folderID);
    }

    /**
     * Get the last folder selected
     *
     * @return int
     */
    public function getLastFolderID()
    {
        $request = Controller::curr()->getRequest();
        $session = $request->getSession();
        return $session->get(self::class . '.FolderID');
    }

    public function setValue($value, $data = null)
    {
        if ($value) {
            $this->setLastFolderID($value);
        }
        parent::setValue($value);
    }
}