<commit_before><?php

namespace LAG\AdminBundle\Field;

use LAG\AdminBundle\Configuration\ActionConfiguration;
use LAG\AdminBundle\Field\Traits\EntityAwareTrait;
use LAG\AdminBundle\Field\Traits\TwigAwareTrait;
use LAG\AdminBundle\Routing\RoutingLoader;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccess;

class ActionCollectionField extends CollectionField implements TwigAwareFieldInterface, EntityAwareFieldInterface
{
    use TwigAwareTrait, EntityAwareTrait;

    public function configureOptions(OptionsResolver $resolver, ActionConfiguration $actionConfiguration)
    {
        parent::configureOptions($resolver, $actionConfiguration);

        $actions = $actionConfiguration
            ->getAdminConfiguration()
            ->getParameter('actions')
        ;
        $defaultActions = [];

        if (key_exists('edit', $actions)) {
            $defaultActions['edit'] = $actions['edit'];
        }

        if (key_exists('delete', $actions)) {
            $defaultActions['delete'] = $actions['delete'];
        }

        $resolver
            ->setDefaults([
                'template' => '@LAGAdmin/Field/actionCollection.html.twig',
                'actions' => [],
            ])
            ->setNormalizer('actions', function (Options $options, $value) use ($actionConfiguration, $defaultActions) {
                if (!is_array($value) || 0 === count($value)) {
                    $value = $defaultActions;
                }
                $data = [];

                foreach ($value as $name => $action) {
                    $actionData = [
                        'admin' => $actionConfiguration->getAdminName(),
                        'action' => $name,
                        'title' => $actionConfiguration->getParameter('title'),
                        'text' => $actionConfiguration->getParameter('title'),
                        'parameters' => [
                            // TODO get dynamic id key
                            'id' => '',
                        ],
                    ];

                    $data[$name] = $this->resolveActionConfiguration($actionConfiguration, $actionData);
                }

                return $data;
            })
        ;
    }

    public function isSortable(): bool
    {
        return false;
    }

    public function render($value = null): string
    {
        $accessor = PropertyAccess::createPropertyAccessor();

        if (key_exists('edit', $this->options['actions'])) {
            $this->options['actions']['edit']['parameters']['id'] = $accessor->getValue($this->entity, 'id');
        }

        if (key_exists('delete', $this->options['actions'])) {
            $this->options['actions']['delete']['parameters']['id'] = $accessor->getValue($this->entity, 'id');
        }

        return $this->twig->render($this->options['template'], [
            'actions' => $this->options['actions'],
        ]);
    }

    protected function resolveActionConfiguration(ActionConfiguration $actionConfiguration, $value)
    {
        $resolver = new OptionsResolver();
        $resolver
            ->setDefaults([
                'title' => '',
                'icon' => '',
                'target' => '_self',
                'route' => '',
                'parameters' => [],
                'url' => '',
                'text' => '',
                'admin' => null,
                'action' => null,
                'class' => '',
            ])
            ->setAllowedTypes('route', 'string')
            ->setAllowedTypes('parameters', 'array')
            ->setAllowedTypes('url', 'string')
            ->setAllowedValues('target', [
                '_self',
                '_blank',
            ])
            ->setNormalizer('route', function (Options $options, $value) use ($actionConfiguration) {
                // route or url should be defined
                if (!$value && !$options->offsetGet('url') && !$options->offsetGet('admin')) {
                    throw new InvalidOptionsException(
                        'You must set either an url or a route for the property'
                    );
                }

                if ($options->offsetGet('admin')) {
                    $value = RoutingLoader::generateRouteName(
                        $options->offsetGet('admin'),
                        $options->offsetGet('action'),
                        $actionConfiguration->getAdminConfiguration()->getParameter('routing_name_pattern')
                    );
                }

                return $value;
            })
            ->setNormalizer('admin', function (Options $options, $value) {
                // if a Admin is defined, an Action should be defined too
                if ($value && !$options->offsetGet('action')) {
                    throw new InvalidOptionsException(
                        'An Action should be provided if an Admin is provided'
                    );
                }

                return $value;
            })
            ->setNormalizer('parameters', function (Options $options, $values) {
                $cleanedValues = [];

                foreach ($values as $name => $method) {
                    if (null === $method) {
                        $method = $name;
                    }
                    $cleanedValues[$name] = $method;
                }


                return $cleanedValues;
            })
        ;

        return $resolver->resolve($value);
    }
}<commit_msg>Improve the action collection field by adding default parameters for some CRUD actions<commit_after><?php

namespace LAG\AdminBundle\Field;

use LAG\AdminBundle\Configuration\ActionConfiguration;
use LAG\AdminBundle\Field\Traits\EntityAwareTrait;
use LAG\AdminBundle\Field\Traits\TwigAwareTrait;
use LAG\AdminBundle\Routing\RoutingLoader;
use LAG\AdminBundle\Utils\StringUtils;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccess;

class ActionCollectionField extends CollectionField implements TwigAwareFieldInterface, EntityAwareFieldInterface
{
    use TwigAwareTrait, EntityAwareTrait;

    public function configureOptions(OptionsResolver $resolver, ActionConfiguration $actionConfiguration)
    {
        parent::configureOptions($resolver, $actionConfiguration);

        $actions = $actionConfiguration
            ->getAdminConfiguration()
            ->getParameter('actions')
        ;
        $defaultActions = [];

        if (key_exists('edit', $actions)) {
            $defaultActions['edit'] = $actions['edit'];
        }

        if (key_exists('delete', $actions)) {
            $defaultActions['delete'] = $actions['delete'];
        }

        $resolver
            ->setDefaults([
                'template' => '@LAGAdmin/Field/actionCollection.html.twig',
                'actions' => [],
            ])
            ->setNormalizer('actions', function (Options $options, $value) use ($actionConfiguration, $defaultActions) {
                if (!is_array($value) || 0 === count($value)) {
                    $value = [
                        'edit' => [],
                        'delete' => [],
                    ];
                }
                $data = [];

                foreach ($value as $name => $actionLinkConfiguration) {
                    $data[$name] = $this->resolveActionLinkConfiguration(
                        $actionConfiguration,
                        $name,
                        $actionLinkConfiguration
                    );
                }

                return $data;
            })
        ;
    }

    public function isSortable(): bool
    {
        return false;
    }

    public function render($value = null): string
    {
        $accessor = PropertyAccess::createPropertyAccessor();

        if (key_exists('edit', $this->options['actions'])) {
            $this->options['actions']['edit']['parameters']['id'] = $accessor->getValue($this->entity, 'id');
        }

        if (key_exists('delete', $this->options['actions'])) {
            $this->options['actions']['delete']['parameters']['id'] = $accessor->getValue($this->entity, 'id');
        }

        return $this->twig->render($this->options['template'], [
            'actions' => $this->options['actions'],
        ]);
    }

    protected function resolveActionLinkConfiguration(
        ActionConfiguration $actionConfiguration,
        string $actionName,
        array $actionLinkConfiguration = []
    ): array {
        $translationPattern = $actionConfiguration
            ->getAdminConfiguration()
            ->getParameter('translation_pattern')
        ;

        $icon = '';
        $cssClass = '';
        $routeParameters = [];

        if ('delete' === $actionName) {
            $icon = 'remove';
            $cssClass = 'btn btn-danger btn-sm';
            $routeParameters = [
                'id' => ''
            ];
        }
        if ('edit' === $actionName) {
            $icon = 'pencil';
            $cssClass = 'btn btn-secondary btn-sm';
        }

        $resolver = new OptionsResolver();
        $resolver
            ->setDefaults([
                'title' => StringUtils::getTranslationKey(
                    $translationPattern,
                    $actionConfiguration->getAdminName(),
                    $actionName
                ),
                'icon' => $icon,
                'target' => '_self',
                'route' => '',
                'parameters' => $routeParameters,
                'url' => '',
                'text' => StringUtils::getTranslationKey(
                    $translationPattern,
                    $actionConfiguration->getAdminName(),
                    $actionName
                ),
                'admin' => $actionConfiguration->getAdminName(),
                'action' => $actionConfiguration->getActionName(),
                'class' => $cssClass,
            ])
            ->setAllowedTypes('route', 'string')
            ->setAllowedTypes('parameters', 'array')
            ->setAllowedTypes('url', 'string')
            ->setAllowedValues('target', [
                '_self',
                '_blank',
            ])
            ->setNormalizer('route', function (Options $options, $value) use ($actionConfiguration) {
                // route or url should be defined
                if (!$value && !$options->offsetGet('url') && !$options->offsetGet('admin')) {
                    throw new InvalidOptionsException(
                        'You must set either an url or a route for the property'
                    );
                }

                if ($options->offsetGet('admin')) {
                    $value = RoutingLoader::generateRouteName(
                        $options->offsetGet('admin'),
                        $options->offsetGet('action'),
                        $actionConfiguration->getAdminConfiguration()->getParameter('routing_name_pattern')
                    );
                }

                return $value;
            })
            ->setNormalizer('admin', function (Options $options, $value) {
                // if a Admin is defined, an Action should be defined too
                if ($value && !$options->offsetGet('action')) {
                    throw new InvalidOptionsException(
                        'An Action should be provided if an Admin is provided'
                    );
                }

                return $value;
            })
            ->setNormalizer('parameters', function (Options $options, $values) {
                $cleanedValues = [];

                foreach ($values as $name => $method) {
                    if (null === $method) {
                        $method = $name;
                    }
                    $cleanedValues[$name] = $method;
                }


                return $cleanedValues;
            })
        ;

        return $resolver->resolve($actionLinkConfiguration);
    }
}
{'fraction_non_alphanumeric': 0.1289241334205363, 'fraction_numerical': 0.00016350555918901244, 'mean_word_length': 1.6478354978354979, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 119, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4769043', 'n_tokens_mistral': 2955, 'n_tokens_neox': 2796, 'n_words': 556}