@@ -69,7 +69,7 @@ class TypeHintResolver
         } elseif ($hint instanceof PhpParser\Node\Name\FullyQualified) {
             $fq_type_string = (string)$hint;

-            $scanner->queueClassLikeForScanning($fq_type_string);
+            $codebase->scanner->queueClassLikeForScanning($fq_type_string);
             $file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
         } else {
             $lower_hint = strtolower($hint->parts[0]);

<?php

namespace Psalm\Internal\PhpVisitor\Reflector;

use PhpParser;
use Psalm\Aliases;
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
use Psalm\Internal\Codebase\Scanner as CodebaseScanner;
use Psalm\Storage\ClassLikeStorage;
use Psalm\Storage\FileStorage;
use Psalm\Type;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Union;
use UnexpectedValueException;

use function implode;
use function strtolower;

/**
 * @internal
 */
class TypeHintResolver
{
    /**
     * @param PhpParser\Node\Identifier|PhpParser\Node\Name|PhpParser\Node\NullableType|PhpParser\Node\UnionType $hint
     */
    public static function resolve(
        PhpParser\NodeAbstract $hint,
        CodebaseScanner $scanner,
        FileStorage $file_storage,
        ?ClassLikeStorage $classlike_storage,
        Aliases $aliases,
        int $analysis_php_version_id
    ): Union {
        if ($hint instanceof PhpParser\Node\UnionType) {
            $type = null;

            if (!$hint->types) {
                throw new UnexpectedValueException('bad');
            }

            foreach ($hint->types as $atomic_typehint) {
                $resolved_type = self::resolve(
                    $atomic_typehint,
                    $scanner,
                    $file_storage,
                    $classlike_storage,
                    $aliases,
                    $analysis_php_version_id
                );

                $type = Type::combineUnionTypes($resolved_type, $type);
            }

            return $type;
        }

        $is_nullable = false;

        if ($hint instanceof PhpParser\Node\NullableType) {
            $is_nullable = true;
            $hint = $hint->type;
        }

        $type_string = null;

        if ($hint instanceof PhpParser\Node\Identifier) {
            $fq_type_string = $hint->name;
        } elseif ($hint instanceof PhpParser\Node\Name\FullyQualified) {
            $fq_type_string = (string)$hint;

            $scanner->queueClassLikeForScanning($fq_type_string);
            $file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
        } else {
            $lower_hint = strtolower($hint->parts[0]);

            if ($classlike_storage
                && ($lower_hint === 'self' || $lower_hint === 'static')
                && !$classlike_storage->is_trait
            ) {
                $fq_type_string = $classlike_storage->name;

                if ($lower_hint === 'static') {
                    $fq_type_string .= '&static';
                }
            } else {
                $type_string = implode('\\', $hint->parts);
                $fq_type_string = ClassLikeAnalyzer::getFQCLNFromNameObject($hint, $aliases);

                $scanner->queueClassLikeForScanning($fq_type_string);
                $file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
            }
        }

        $type = Type::parseString(
            $fq_type_string,
            $analysis_php_version_id,
            []
        );

        if ($type_string) {
            $atomic_type = $type->getSingleAtomic();
            $atomic_type->text = $type_string;
        }

        if ($is_nullable) {
            $type->addType(new TNull);
        }

        return $type;
    }
}


@@ -17,14 +17,26 @@ use Ergonode\Attribute\Domain\Repository\AttributeRepositoryInterface;
 use Webmozart\Assert\Assert;
 use Ergonode\Workflow\Infrastructure\Exception\WorkflowConditionCalculatorException;
 use Ergonode\Attribute\Domain\Entity\AbstractAttribute;
+use Ergonode\Product\Infrastructure\Calculator\TranslationInheritanceCalculator;
+use Ergonode\Core\Domain\Query\LanguageQueryInterface;
+use Ergonode\Value\Domain\ValueObject\ValueInterface;

 class AttributeExistsWorkflowConditionCalculator implements WorkflowConditionCalculatorInterface
 {
     private AttributeRepositoryInterface $repository;

-    public function __construct(AttributeRepositoryInterface $repository)
-    {
+    private TranslationInheritanceCalculator $calculator;
+
+    private LanguageQueryInterface $languageQuery;
+
+    public function __construct(
+        AttributeRepositoryInterface $repository,
+        TranslationInheritanceCalculator $calculator,
+        LanguageQueryInterface $languageQuery
+    ) {
         $this->repository = $repository;
+        $this->calculator = $calculator;
+        $this->languageQuery = $languageQuery;
     }

     public function supports(WorkflowConditionInterface $condition): bool

<?php
/**
 * Copyright © Ergonode Sp. z o.o. All rights reserved.
 * See LICENSE.txt for license details.
 */

declare(strict_types=1);

namespace Ergonode\Workflow\Infrastructure\Condition\Calculator;

use Ergonode\Workflow\Domain\Condition\WorkflowConditionCalculatorInterface;
use Ergonode\Product\Domain\Entity\AbstractProduct;
use Ergonode\Core\Domain\ValueObject\Language;
use Ergonode\Workflow\Domain\Condition\WorkflowConditionInterface;
use Ergonode\Workflow\Infrastructure\Condition\AttributeExistsWorkflowCondition;
use Ergonode\Attribute\Domain\Repository\AttributeRepositoryInterface;
use Webmozart\Assert\Assert;
use Ergonode\Workflow\Infrastructure\Exception\WorkflowConditionCalculatorException;
use Ergonode\Attribute\Domain\Entity\AbstractAttribute;

class AttributeExistsWorkflowConditionCalculator implements WorkflowConditionCalculatorInterface
{
    private AttributeRepositoryInterface $repository;

    public function __construct(AttributeRepositoryInterface $repository)
    {
        $this->repository = $repository;
    }

    public function supports(WorkflowConditionInterface $condition): bool
    {
        return $condition instanceof AttributeExistsWorkflowCondition;
    }

    /**
     * @param AttributeExistsWorkflowCondition $condition
     */
    public function calculate(AbstractProduct $product, WorkflowConditionInterface $condition, Language $language): bool
    {
        if (!$this->supports($condition)) {
            throw new WorkflowConditionCalculatorException(
                sprintf(
                    'Expected an instance of %s. %s received.',
                    AttributeExistsWorkflowCondition::class,
                    get_debug_type($condition)
                )
            );
        }

        $attributeId = $condition->getAttribute();

        $attribute = $this->repository->load($attributeId);

        Assert::isInstanceOf($attribute, AbstractAttribute::class);

        return $product->hasAttribute($attribute->getCode());
    }
}


@@ -1,9 +1,16 @@
 <?php

+declare(strict_types=1);
+
 namespace Shopsys\FrameworkBundle\Model\Product\Search\Export;

+use BadMethodCallException;
+use Doctrine\ORM\EntityManagerInterface;
+use Shopsys\FrameworkBundle\Component\Console\ProgressBarFactory;
+use Shopsys\FrameworkBundle\Component\Doctrine\SqlLoggerFacade;
 use Shopsys\FrameworkBundle\Model\Product\Search\ProductElasticsearchConverter;
 use Shopsys\FrameworkBundle\Model\Product\Search\ProductElasticsearchRepository;
+use Symfony\Component\Console\Style\SymfonyStyle;

 class ProductSearchExporter
 {

<?php

namespace Shopsys\FrameworkBundle\Model\Product\Search\Export;

use Shopsys\FrameworkBundle\Model\Product\Search\ProductElasticsearchConverter;
use Shopsys\FrameworkBundle\Model\Product\Search\ProductElasticsearchRepository;

class ProductSearchExporter
{
    /** @access protected */
    const BATCH_SIZE = 100;

    /**
     * @var \Shopsys\FrameworkBundle\Model\Product\Search\Export\ProductSearchExportRepository
     */
    protected $productSearchExportRepository;

    /**
     * @var \Shopsys\FrameworkBundle\Model\Product\Search\ProductElasticsearchRepository
     */
    protected $productElasticsearchRepository;

    /**
     * @var \Shopsys\FrameworkBundle\Model\Product\Search\ProductElasticsearchConverter
     */
    protected $productElasticsearchConverter;

    /**
     * @param \Shopsys\FrameworkBundle\Model\Product\Search\Export\ProductSearchExportRepository $productSearchExportRepository
     * @param \Shopsys\FrameworkBundle\Model\Product\Search\ProductElasticsearchRepository $productElasticsearchRepository
     * @param \Shopsys\FrameworkBundle\Model\Product\Search\ProductElasticsearchConverter $productElasticsearchConverter
     */
    public function __construct(
        ProductSearchExportRepository $productSearchExportRepository,
        ProductElasticsearchRepository $productElasticsearchRepository,
        ProductElasticsearchConverter $productElasticsearchConverter
    ) {
        $this->productSearchExportRepository = $productSearchExportRepository;
        $this->productElasticsearchRepository = $productElasticsearchRepository;
        $this->productElasticsearchConverter = $productElasticsearchConverter;
    }

    /**
     * @param int $domainId
     * @param string $locale
     */
    public function export(int $domainId, string $locale): void
    {
        $startFrom = 0;
        $exportedIds = [];
        do {
            $batchExportedIds = $this->exportBatch($domainId, $locale, $startFrom);
            $exportedIds = array_merge($exportedIds, $batchExportedIds);
            $startFrom += static::BATCH_SIZE;
        } while (!empty($batchExportedIds));
        $this->removeNotUpdated((string)$domainId, $exportedIds);
    }

    /**
     * @param int $domainId
     * @param string $locale
     * @param int $startFrom
     * @return int[]
     */
    protected function exportBatch(int $domainId, string $locale, int $startFrom): array
    {
        $productsData = $this->productSearchExportRepository->getProductsData($domainId, $locale, $startFrom, static::BATCH_SIZE);
        if (count($productsData) === 0) {
            return [];
        }

        $data = $this->productElasticsearchConverter->convertExportBulk($productsData);
        $this->productElasticsearchRepository->bulkUpdate($domainId, $data);

        return $this->productElasticsearchConverter->extractIds($productsData);
    }

    /**
     * @param int $domainId
     * @param int[] $exportedIds
     */
    protected function removeNotUpdated(int $domainId, array $exportedIds): void
    {
        $this->productElasticsearchRepository->deleteNotPresent($domainId, $exportedIds);
    }
}


@@ -11,11 +11,6 @@ use Symfony\Component\Routing\RouteCompiler;

 class FriendlyUrlGenerator extends BaseUrlGenerator
 {
-    /**
-     * @var \Symfony\Component\Routing\RouteCompiler
-     */
-    private $routeCompiler;
-
     /**
      * @var FriendlyUrlRepository
      */

<?php

namespace Shopsys\ShopBundle\Component\Router\FriendlyUrl;

use Shopsys\ShopBundle\Component\Domain\Config\DomainConfig;
use Symfony\Component\Routing\Generator\UrlGenerator as BaseUrlGenerator;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouteCompiler;

class FriendlyUrlGenerator extends BaseUrlGenerator
{
    /**
     * @var \Symfony\Component\Routing\RouteCompiler
     */
    private $routeCompiler;

    /**
     * @var FriendlyUrlRepository
     */
    private $friendlyUrlRepository;

    public function __construct(
        RequestContext $context,
        RouteCompiler $routeCompiler,
        FriendlyUrlRepository $friendlyUrlRepository
    ) {
        parent::__construct(new RouteCollection(), $context, null);

        $this->routeCompiler = $routeCompiler;
        $this->friendlyUrlRepository = $friendlyUrlRepository;
    }

    /**
     * @param \Symfony\Component\Routing\RouteCollection $routeCollection
     * @param \Shopsys\ShopBundle\Component\Domain\Config\DomainConfig $domainConfig
     * @param string $routeName
     * @param array $parameters
     * @param int $referenceType
     * @return string
     */
    public function generateFromRouteCollection(
        RouteCollection $routeCollection,
        DomainConfig $domainConfig,
        $routeName,
        array $parameters = [],
        $referenceType = self::ABSOLUTE_PATH
    ) {
        $route = $routeCollection->get($routeName);
        if ($route === null) {
            $message = 'Unable to generate a URL for the named route "' . $routeName . '" as such route does not exist.';
            throw new \Symfony\Component\Routing\Exception\RouteNotFoundException($message);
        }
        if (!array_key_exists('id', $parameters)) {
            $message = 'Missing mandatory parameter "id" for route ' . $routeName . '.';
            throw new \Symfony\Component\Routing\Exception\MissingMandatoryParametersException($message);
        }
        $entityId = $parameters['id'];
        unset($parameters['id']);

        try {
            $friendlyUrl = $this->friendlyUrlRepository->getMainFriendlyUrl(
                $domainConfig->getId(),
                $routeName,
                $entityId
            );
        } catch (\Shopsys\ShopBundle\Component\Router\FriendlyUrl\Exception\FriendlyUrlNotFoundException $e) {
            $message = 'Unable to generate a URL for the named route "' . $routeName . '" as such route does not exist.';
            throw new \Symfony\Component\Routing\Exception\RouteNotFoundException($message, 0, $e);
        }

        return $this->getGeneratedUrl($routeName, $route, $friendlyUrl, $parameters, $referenceType);
    }

    /**
     * @param string $routeName
     * @param \Symfony\Component\Routing\Route $route
     * @param \Shopsys\ShopBundle\Component\Router\FriendlyUrl\FriendlyUrl $friendlyUrl
     * @param array $parameters
     * @param string $referenceType
     * @return string
     */
    public function getGeneratedUrl($routeName, Route $route, FriendlyUrl $friendlyUrl, array $parameters, $referenceType)
    {
        $compiledRoute = RouteCompiler::compile($route);

        $tokens = [
            [
                0 => 'text',
                1 => '/' . $friendlyUrl->getSlug(),
            ],
        ];

        return $this->doGenerate(
            $compiledRoute->getVariables(),
            $route->getDefaults(),
            $route->getRequirements(),
            $tokens,
            $parameters,
            $routeName,
            $referenceType,
            $compiledRoute->getHostTokens(),
            $route->getSchemes()
        );
    }

    /**
     * Not supported method
     */
    public function generate($routeName, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
    {
        throw new \Shopsys\ShopBundle\Component\Router\FriendlyUrl\Exception\MethodGenerateIsNotSupportedException();
    }
}


@@ -73,4 +73,9 @@ class CreateFileExportChannelCommand implements CreateChannelCommandInterface
     {
         return $this->languages;
     }
+
+    public function getSegmentId(): ?SegmentId
+    {
+        return $this->segmentId;
+    }
 }

<?php

/**
 * Copyright © Ergonode Sp. z o.o. All rights reserved.
 * See LICENSE.txt for license details.
 */

declare(strict_types=1);

namespace Ergonode\ExporterFile\Domain\Command;

use Ergonode\Channel\Domain\Command\CreateChannelCommandInterface;
use Ergonode\SharedKernel\Domain\Aggregate\ChannelId;
use Ergonode\Core\Domain\ValueObject\Language;
use Webmozart\Assert\Assert;
use Ergonode\ExporterFile\Domain\Entity\FileExportChannel;

class CreateFileExportChannelCommand implements CreateChannelCommandInterface
{
    protected ChannelId $id;

    protected string $name;

    protected string $format;

    /**
     * @var Language[]
     */
    protected array $languages;

    protected string $exportType;

    /**
     * @param Language[] $languages
     */
    public function __construct(ChannelId $id, string $name, string $format, string $exportType, array $languages = [])
    {
        Assert::allIsInstanceOf($languages, Language::class);
        Assert::oneOf($exportType, FileExportChannel::EXPORT_TYPES);

        $this->id = $id;
        $this->name = $name;
        $this->format = $format;
        $this->exportType = $exportType;
        $this->languages = $languages;
    }

    public function getId(): ChannelId
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getFormat(): string
    {
        return $this->format;
    }

    public function getExportType(): string
    {
        return $this->exportType;
    }

    /**
     * @return Language[]
     */
    public function getLanguages(): array
    {
        return $this->languages;
    }
}


@@ -6,7 +6,6 @@ namespace Bolt\Controller\Async;

 use Bolt\Configuration\Config;
 use Bolt\Content\MediaFactory;
-use Bolt\Media\RequestHandler;
 use Cocur\Slugify\Slugify;
 use Doctrine\Common\Persistence\ObjectManager;
 use Sirius\Upload\Handler;

<?php

declare(strict_types=1);

namespace Bolt\Controller\Async;

use Bolt\Configuration\Config;
use Bolt\Content\MediaFactory;
use Bolt\Media\RequestHandler;
use Cocur\Slugify\Slugify;
use Doctrine\Common\Persistence\ObjectManager;
use Sirius\Upload\Handler;
use Sirius\Upload\Result\File;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Webmozart\PathUtil\Path;

class Uploader
{
    /** @var MediaFactory */
    private $mediaFactory;

    /** @var RequestHandler */
    private $requestHandler;

    /** @var ObjectManager */
    private $manager;

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

    public function __construct(MediaFactory $mediaFactory, RequestHandler $requestHandler, ObjectManager $manager, Config $config)
    {
        $this->mediaFactory = $mediaFactory;
        $this->requestHandler = $requestHandler;
        $this->manager = $manager;
        $this->config = $config;
    }

    /**
     * @Route("/upload", name="bolt_upload_post", methods={"POST"})
     */
    public function upload(Request $request)
    {
//        $uploadHandler = new Handler('/path/to/local_folder');

        $area = $request->query->get('area', '');
        $path = $request->query->get('path', '');

        $target = $this->config->getPath($area, true, $path);

        $uploadHandler = new Handler($target, [
            Handler::OPTION_AUTOCONFIRM => true,
            Handler::OPTION_OVERWRITE => true,
        ]);

        $uploadHandler->addRule('extension', ['allowed' => 'jpg', 'jpeg', 'png'], '{label} should be a valid image (jpg, jpeg, png)', 'Profile picture');
        $uploadHandler->addRule('size', ['max' => '20M'], '{label} should have less than {max}', 'Profile picture');
        $uploadHandler->setSanitizerCallback(function ($name) {
            return $this->sanitiseFilename($name);
        });

        /** @var File $result */
        $result = $uploadHandler->process($_FILES);

        if ($result->isValid()) {
            try {
                $media = $this->mediaFactory->createFromFilename($area, $path, $result->name);
                $this->manager->persist($media);
                $this->manager->flush();

                return new Response($media->getFilenamePath());
            } catch (\Throwable $e) {
                // something wrong happened, we don't need the uploaded files anymore
                $result->clear();
                throw $e;
            }
        } else {
            // image was not moved to the container, where are error messages
            $messages = $result->getMessages();
        }

        return new Response('Not OK');
    }

    private function sanitiseFilename(string $filename): string
    {
        $extensionSlug = new Slugify(['regexp' => '/([^a-z0-9]|-)+/']);
        $filenameSlug = new Slugify(['lowercase' => false]);

        $extension = $extensionSlug->slugify(Path::getExtension($filename));
        $filename = $filenameSlug->slugify(Path::getFilenameWithoutExtension($filename));

        return $filename . '.' . $extension;
    }
}


@@ -58,7 +58,7 @@

-                                <button type="submit" class="btn btn-primary btn-block py-0 font-weight-bold">
+                                <button type="submit" class="btn btn-primary btn-block btn-lg font-weight-bold">
                                     {{ __('Login') }}
                                 </button>

@extends('layouts.app')

@section('content')

{{ __('Login') }}

                    <form method="POST" action="{{ route('login') }}" class="px-5">
                        @csrf

                                <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" placeholder="{{__('Email')}}" required autofocus>

                                @if ($errors->has('email'))

                                        {{ $errors->first('email') }}

                                @endif

                                <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" placeholder="{{__('Password')}}" required>

                                @if ($errors->has('password'))

                                        {{ $errors->first('password') }}

                                @endif

                                        <input type="checkbox" name="remember" {{ old('remember') ? 'checked' : '' }}>

                                            {{ __('Remember Me') }}

                        @if(config('captcha.enabled'))

                            {!! Captcha::display() !!}

                        @endif

                                <button type="submit" class="btn btn-primary btn-block py-0 font-weight-bold">
                                    {{ __('Login') }}
                                </button>

                    </form>

                            {{ __('Forgot Password') }}

@endsection


@@ -26,21 +26,26 @@ func TestCmdSnapshot(t *testing.T) {
 		cleanup()
 	}()

+	// Ensure that there are no snapshots available before we create one
+	args := []string{"snapshot", "--cleanup", "--yes"}
+	_, err = exec.RunCommand(DdevBin, args)
+	assert.NoError(err)
+
 	// Ensure that a snapshot can be created
-	args := []string{"snapshot", "--name", "test-snapshot"}
+	args = []string{"snapshot", "--name", "test-snapshot"}
 	out, err := exec.RunCommand(DdevBin, args)
 	assert.NoError(err)
-	assert.Contains(string(out), "Created snapshot test-snapshot")
+	assert.Contains(out, "Created snapshot test-snapshot")

 	// Try to delete a not existing snapshot
 	args = []string{"snapshot", "--name", "not-existing-snapshot", "--cleanup", "--yes"}
 	out, err = exec.RunCommand(DdevBin, args)
 	assert.Error(err)
-	assert.Contains(string(out), "Failed to delete snapshot")
+	assert.Contains(out, "Failed to delete snapshot")

 	// Ensure that an existing snapshot can be deleted
-	args = []string{"snapshot", "--name", "test-snapshot", "--cleanup"}
+	args = []string{"snapshot", "--name", "test-snapshot", "--cleanup", "--yes"}
 	out, err = exec.RunCommand(DdevBin, args)
 	assert.NoError(err)
-	assert.Contains(string(out), "Deleted database snapshot test-snapshot")
+	assert.Contains(out, "Deleted database snapshot test-snapshot")
 }

package cmd

import (
	"fmt"
	"github.com/drud/ddev/pkg/ddevapp"
	"github.com/drud/ddev/pkg/exec"
	asrt "github.com/stretchr/testify/assert"
	"os"
	"testing"
)

// TestCmdSnapshot runs `ddev snapshot` on the test apps
func TestCmdSnapshot(t *testing.T) {
	assert := asrt.New(t)

	testDir, _ := os.Getwd()
	fmt.Println(testDir)
	site := TestSites[0]
	cleanup := site.Chdir()
	app, err := ddevapp.NewApp(site.Dir, false, "")
	assert.NoError(err)
	defer func() {
		// Make sure all databases are back to default empty
		_ = app.Stop(true, false)
		_ = app.Start()
		cleanup()
	}()

	// Ensure that a snapshot can be created
	args := []string{"snapshot", "--name", "test-snapshot"}
	out, err := exec.RunCommand(DdevBin, args)
	assert.NoError(err)
	assert.Contains(string(out), "Created snapshot test-snapshot")

	// Try to delete a not existing snapshot
	args = []string{"snapshot", "--name", "not-existing-snapshot", "--cleanup", "--yes"}
	out, err = exec.RunCommand(DdevBin, args)
	assert.Error(err)
	assert.Contains(string(out), "Failed to delete snapshot")

	// Ensure that an existing snapshot can be deleted
	args = []string{"snapshot", "--name", "test-snapshot", "--cleanup"}
	out, err = exec.RunCommand(DdevBin, args)
	assert.NoError(err)
	assert.Contains(string(out), "Deleted database snapshot test-snapshot")
}


@@ -1,11 +1,10 @@
 <?php
-
 /**
  * Copyright © Bold Brand Commerce Sp. z o.o. All rights reserved.
  * See LICENSE.txt for license details.
  */

-declare(strict_types = 1);
+declare(strict_types=1);

 namespace Ergonode\Designer\Domain\ValueObject\TemplateElement;

<?php

/**
 * Copyright © Bold Brand Commerce Sp. z o.o. All rights reserved.
 * See LICENSE.txt for license details.
 */

declare(strict_types = 1);

namespace Ergonode\Designer\Domain\ValueObject\TemplateElement;

use Ergonode\Designer\Domain\ValueObject\TemplateElementPropertyInterface;
use Ergonode\SharedKernel\Domain\Aggregate\AttributeId;
use JMS\Serializer\Annotation as JMS;

/**
 */
class AttributeTemplateElementProperty implements TemplateElementPropertyInterface
{
    public const VARIANT = 'attribute';

    /**
     * @var AttributeId
     *
     * @JMS\Type("Ergonode\SharedKernel\Domain\Aggregate\AttributeId")
     * @JMS\SerializedName("attribute_id")
     */
    private AttributeId $attributeId;

    /**
     * @var bool
     *
     * @JMS\Type("bool")
     */
    private bool $required;

    /**
     * @param AttributeId $attributeId
     * @param bool        $required
     */
    public function __construct(AttributeId $attributeId, bool $required = false)
    {
        $this->attributeId = $attributeId;
        $this->required = $required;
    }

    /**
     * {@inheritDoc}
     *
     * @JMS\VirtualProperty()
     */
    public function getVariant(): string
    {
        return self::VARIANT;
    }

    /**
     * @return AttributeId
     */
    public function getAttributeId(): AttributeId
    {
        return $this->attributeId;
    }

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


@@ -13,6 +13,7 @@ import (
 	"github.com/drud/ddev/pkg/dockerutil"
 	"github.com/drud/ddev/pkg/util"
 	"github.com/drud/ddev/pkg/version"
+	"github.com/fatih/color"

 	homedir "github.com/mitchellh/go-homedir"
 )

package platform

import (
	"bytes"
	"html/template"
	"log"
	"os"
	"path"
	"path/filepath"

	"strings"

	"github.com/drud/ddev/pkg/dockerutil"
	"github.com/drud/ddev/pkg/util"
	"github.com/drud/ddev/pkg/version"

	homedir "github.com/mitchellh/go-homedir"
)

const routerProjectName = "ddev-router"

// RouterComposeYAMLPath returns the full filepath to the routers docker-compose yaml file.
func RouterComposeYAMLPath() string {
	userHome, err := homedir.Dir()
	if err != nil {
		log.Fatal("could not get home directory for current user. is it set?")
	}
	routerdir := path.Join(userHome, ".ddev")
	dest := path.Join(routerdir, "router-compose.yaml")

	return dest
}

// StopRouter stops the local router if there are no ddev containers running.
func StopRouter() error {

	containersRunning, err := ddevContainersRunning()
	if err != nil {
		return err
	}

	if !containersRunning {
		dest := RouterComposeYAMLPath()
		return dockerutil.ComposeCmd([]string{dest}, "-p", routerProjectName, "down", "-v")
	}
	return nil
}

// StartDdevRouter ensures the router is running.
func StartDdevRouter() {
	exposedPorts := determineRouterPorts()

	dest := RouterComposeYAMLPath()
	routerdir := filepath.Dir(dest)
	err := os.MkdirAll(routerdir, 0755)
	if err != nil {
		log.Fatalf("unable to create directory for ddev router: %s", err)
	}

	var doc bytes.Buffer
	f, ferr := os.Create(dest)
	if ferr != nil {
		log.Fatal(ferr)
	}
	defer util.CheckClose(f)

	templ := template.New("compose template")
	templ, err = templ.Parse(DdevRouterTemplate)
	if err != nil {
		log.Fatal(ferr)
	}

	templateVars := map[string]interface{}{
		"router_image": version.RouterImage,
		"router_tag":   version.RouterTag,
		"ports":        exposedPorts,
	}

	err = templ.Execute(&doc, templateVars)
	util.CheckErr(err)
	_, err = f.WriteString(doc.String())
	util.CheckErr(err)

	// run docker-compose up -d in the newly created directory
	err = dockerutil.ComposeCmd([]string{dest}, "-p", routerProjectName, "up", "-d")
	if err != nil {
		log.Fatalf("Could not start router: %v", err)
	}
}

// determineRouterPorts returns a list of port mappings retrieved from running site
// containers defining VIRTUAL_PORT env var
func determineRouterPorts() []string {
	var routerPorts []string
	containers, err := dockerutil.GetDockerContainers(false)
	if err != nil {
		log.Fatal("failed to retreive containers for determining port mappings", err)
	}

	// loop through all containers with site-name label
	for _, container := range containers {
		if _, ok := container.Labels["com.ddev.site-name"]; ok {
			var exposePorts []string

			httpPorts := dockerutil.GetContainerEnv("HTTP_EXPOSE", container)
			if httpPorts != "" {
				ports := strings.Split(httpPorts, ",")
				exposePorts = append(exposePorts, ports...)
			}

			for _, exposePort := range exposePorts {
				// ports defined as hostPort:containerPort allow for router to configure upstreams
				// for containerPort, with server listening on hostPort. exposed ports for router
				// should be hostPort:hostPort so router can determine what port a request came from
				// and route the request to the correct upstream
				if strings.Contains(exposePort, ":") {
					ports := strings.Split(exposePort, ":")
					exposePort = ports[0]
				}

				var match bool
				for _, routerPort := range routerPorts {
					if exposePort == routerPort {
						match = true
					}
				}

				// if no match, we are adding a new port mapping
				if !match {
					routerPorts = append(routerPorts, exposePort)
				}
			}
		}
	}
	return routerPorts
}


@@ -58,11 +58,11 @@ class Price
      * @param \Shopsys\FrameworkBundle\Model\Pricing\Price $priceToAdd
      * @return \Shopsys\FrameworkBundle\Model\Pricing\Price
      */
-    public function add(self $priceToAdd)
+    public function add(self $priceToAdd): self
     {
         return new self(
-            $this->priceWithoutVat + $priceToAdd->getPriceWithoutVat(),
-            $this->priceWithVat + $priceToAdd->getPriceWithVat()
+            $this->priceWithoutVat->add($priceToAdd->priceWithoutVat),
+            $this->priceWithVat->add($priceToAdd->priceWithVat)
         );
     }

<?php

namespace Shopsys\FrameworkBundle\Model\Pricing;

class Price
{
    /**
     * @var string
     */
    protected $priceWithoutVat;

    /**
     * @var string
     */
    protected $priceWithVat;

    /**
     * @var string
     */
    protected $vatAmount;

    /**
     * @param string $priceWithoutVat
     * @param string $priceWithVat
     */
    public function __construct($priceWithoutVat, $priceWithVat)
    {
        $this->priceWithoutVat = $priceWithoutVat;
        $this->priceWithVat = $priceWithVat;
        $this->vatAmount = $priceWithVat - $priceWithoutVat;
    }

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

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

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

    /**
     * @param \Shopsys\FrameworkBundle\Model\Pricing\Price $priceToAdd
     * @return \Shopsys\FrameworkBundle\Model\Pricing\Price
     */
    public function add(self $priceToAdd)
    {
        return new self(
            $this->priceWithoutVat + $priceToAdd->getPriceWithoutVat(),
            $this->priceWithVat + $priceToAdd->getPriceWithVat()
        );
    }

    /**
     * @param \Shopsys\FrameworkBundle\Model\Pricing\Price $priceToSubtract
     * @return \Shopsys\FrameworkBundle\Model\Pricing\Price
     */
    public function subtract(self $priceToSubtract)
    {
        return new self(
            $this->priceWithoutVat - $priceToSubtract->getPriceWithoutVat(),
            $this->priceWithVat - $priceToSubtract->getPriceWithVat()
        );
    }
}


@@ -11,7 +11,6 @@ namespace Ergonode\Account\Tests\Domain\Event;

 use Ergonode\SharedKernel\Domain\Aggregate\UserId;
 use Ergonode\Account\Domain\Event\User\UserAvatarChangedEvent;
-use Ergonode\SharedKernel\Domain\Aggregate\MultimediaId;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;

<?php

/**
 * Copyright © Bold Brand Commerce Sp. z o.o. All rights reserved.
 * See LICENSE.txt for license details.
 */

declare(strict_types = 1);

namespace Ergonode\Account\Tests\Domain\Event;

use Ergonode\SharedKernel\Domain\Aggregate\UserId;
use Ergonode\Account\Domain\Event\User\UserAvatarChangedEvent;
use Ergonode\SharedKernel\Domain\Aggregate\MultimediaId;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

/**
 */
class UserAvatarChangedEventTest extends TestCase
{
    /**
     */
    public function testCreateEvent(): void
    {
        /** @var UserId|MockObject $id */
        $id = $this->createMock(UserId::class);
        $multimediaId = $this->createMock(MultimediaId::class);

        $event = new UserAvatarChangedEvent($id, $multimediaId);

        $this->assertEquals($id, $event->getAggregateId());
        $this->assertEquals($multimediaId, $event->getAvatarId());
    }
}


@@ -5,6 +5,8 @@ declare(strict_types=1);
 namespace Bolt\Twig;

 use Bolt\Menu\FrontendMenuBuilderInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
 use Twig\Environment;
 use Twig\Extension\AbstractExtension;
 use Twig\TwigFunction;

<?php

declare(strict_types=1);

namespace Bolt\Twig;

use Bolt\Menu\FrontendMenuBuilderInterface;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class FrontendMenuExtension extends AbstractExtension
{
    /** @var FrontendMenuBuilderInterface */
    private $menuBuilder;

    public function __construct(FrontendMenuBuilderInterface $menuBuilder)
    {
        $this->menuBuilder = $menuBuilder;
    }

    /**
     * {@inheritdoc}
     */
    public function getFunctions(): array
    {
        $safe = [
            'is_safe' => ['html'],
        ];
        $env = ['needs_environment' => true];

        return [
            new TwigFunction('menu', [$this, 'renderMenu'], $env + $safe),
            new TwigFunction('menu_array', [$this, 'getMenu'], $safe),
        ];
    }

    public function getMenu(?string $name = null): array
    {
        return $this->menuBuilder->buildMenu($name);
    }

    public function renderMenu(Environment $twig, ?string $name = null, string $template = 'helpers/_menu.html.twig', string $class = '', bool $withsubmenus = true): string
    {
        $context = [
            'menu' => $this->menuBuilder->buildMenu($name),
            'class' => $class,
            'withsubmenus' => $withsubmenus,
        ];

        return $twig->render($template, $context);
    }
}


@@ -73,7 +73,6 @@ class CmsObjectCollection extends CollectionBase
             $hasComponent = false;

             foreach ((array) $components as $componentName) {
-
                 if (!$componentAlias = $object->hasComponent($componentName)) {
                     continue;
                 }

<?php namespace Cms\Classes;

use October\Rain\Support\Collection as CollectionBase;

/**
 * This class represents a collection of Cms Objects.
 *
 * @package october\cms
 * @author Alexey Bobkov, Samuel Georges
 */
class CmsObjectCollection extends CollectionBase
{
    /**
     * Returns objects that use the supplied component.
     * @param  string|array $components
     * @param null|callback $callback
     * @return static
     */
    public function withComponent($components, $callback = null)
    {
        return $this->filter(function ($object) use ($components, $callback) {

            $hasComponent = false;

            foreach ((array) $components as $componentName) {

                if (!$callback && $object->hasComponent($componentName)) {
                    $hasComponent = true;
                }

                if ($callback && ($component = $object->getComponent($componentName))) {
                    $hasComponent = call_user_func($callback, $component) ?: $hasComponent;
                }
            }

            return $hasComponent;
        });
    }

    /**
     * Returns objects whose properties match the supplied value.
     * @param string $property
     * @param string $value
     * @param bool $strict
     * @return static
     */
    public function where($property, $value, $strict = true)
    {
        return $this->filter(function ($object) use ($property, $value, $strict) {

            if (!array_key_exists($property, $object->settings)) {
                return false;
            }

            return $strict
                ? $object->settings[$property] === $value
                : $object->settings[$property] == $value;
        });
    }

    /**
     * Returns objects whose component properties match the supplied value.
     * @param mixed $components
     * @param string $property
     * @param string $value
     * @param bool $strict
     * @return static
     */
    public function whereComponent($components, $property, $value, $strict = false)
    {
        return $this->filter(function ($object) use ($components, $property, $value, $strict) {

            $hasComponent = false;

            foreach ((array) $components as $componentName) {

                if (!$componentAlias = $object->hasComponent($componentName)) {
                    continue;
                }

                $componentSettings = array_get($object->settings, 'components', []);

                if (!array_key_exists($componentAlias, $componentSettings)) {
                    continue;
                }

                $settings = $componentSettings[$componentAlias];

                if (!array_key_exists($property, $settings)) {
                    continue;
                }

                if (
                    ($strict && $settings[$property] === $value) ||
                    (!$strict && $settings[$property] == $value)
                ) {
                    $hasComponent = true;
                }
            }

            return $hasComponent;
        });
    }

}


@@ -29,7 +29,6 @@ $venue = sanitize_tags(get_str("venue", true));
 $columns = get_str("cols", true);
 $c = $columns?"&cols=$columns":"";
 check_subset($subset);
-
 if ($action) {
     check_tokens($user->authenticator);
     if ($subset == "global") {

<?php
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.

include_once("../inc/db.inc");
include_once("../inc/util.inc");
include_once("../inc/prefs.inc");
include_once("../inc/prefs_project.inc");

$user = get_logged_in_user();

$action = sanitize_tags(get_str("action", true));
$subset = sanitize_tags(get_str("subset"));
$venue = sanitize_tags(get_str("venue", true));
$columns = get_str("cols", true);
$c = $columns?"&cols=$columns":"";
check_subset($subset);

if ($action) {
    check_tokens($user->authenticator);
    if ($subset == "global") {
        $main_prefs = prefs_parse_global($user->global_prefs);
        if ($venue) $prefs = $main_prefs->$venue;
        else $prefs = $main_prefs;
        $error = prefs_global_parse_form($prefs);
        if ($error != false) {
            $title = tra("Edit %1 preferences", subset_name($subset));
            if ($venue) $title = "$title for $venue";
            page_head($title);

            echo PREFS_FORM_DESC1;
            echo PREFS_FORM_ERROR_DESC;

            print_prefs_form(
                "edit", $subset, $venue, $user, $prefs, $columns, $error
            );
        } else {
            if ($venue) $main_prefs->$venue = $prefs;
            else $main_prefs = $prefs;
            global_prefs_update($user, $main_prefs);
            Header("Location: prefs.php?subset=$subset&updated=1$c");
        }
    } else {
        $main_prefs = prefs_parse_project($user->project_prefs);
        if ($venue) $prefs = $main_prefs->$venue;
        else $prefs = $main_prefs;

        $project_error = prefs_project_parse_form($prefs);
        $error = prefs_resource_parse_form($prefs);
        if ($error != false || $project_error != false) {
            $title = tra("Edit %1 preferences", subset_name($subset));
            if ($venue) $title = tra("%1 for %2", $title, $venue);
            page_head($title);

            echo PREFS_FORM_ERROR_DESC;

            print_prefs_form(
                "edit", $subset, $venue, $user, $prefs, $columns, $error,
                $project_error
            );
        } else {
            if ($venue) {
                $main_prefs->$venue = $prefs;
            } else {
                $main_prefs = $prefs;
                prefs_privacy_parse_form($user);
            }

            project_prefs_update($user, $main_prefs);

            if (!$venue) {
                venue_parse_form($user);
                $user->update("venue='$user->venue'");
            }
            Header("Location: prefs.php?subset=$subset&updated=1$c");
        }
    }
} else {
    $title = tra("Edit %1 preferences", subset_name($subset));
    if ($venue) $title = tra("%1 for %2", $title, $venue);
    page_head($title);
    checkbox_clicked_js();

    if ($subset == "global") {
        echo PREFS_FORM_DESC1;
        $prefs = prefs_parse_global($user->global_prefs);
        if ($venue) {
            $prefs = $prefs->$venue;
        }
    } else {
        $prefs = prefs_parse_project($user->project_prefs);
        if ($venue) {
            $prefs = $prefs->$venue;
        }
    }
    print_prefs_form("edit", $subset, $venue, $user, $prefs, $columns);
}
echo "".tra("Back to preferences")."\n";
page_tail();

$cvs_version_tracker[]="\$Id$";  //Generated automatically - do not edit
?>


@@ -4,16 +4,16 @@
  * See LICENSE.txt for license details.
  */

-declare(strict_types = 1);
+declare(strict_types=1);

 namespace Ergonode\ImporterMagento1\Infrastructure\Processor\Step;

 use Ergonode\ImporterMagento1\Infrastructure\Model\ProductModel;
 use Ergonode\ImporterMagento1\Domain\Entity\Magento1CsvSource;
-use Ergonode\Transformer\Domain\Entity\Transformer;
 use Ergonode\Attribute\Domain\Entity\Attribute\ImageAttribute;
 use Ergonode\Core\Domain\ValueObject\TranslatableString;
 use Ergonode\Category\Domain\ValueObject\CategoryCode;
+use Ergonode\Attribute\Domain\Entity\AbstractAttribute;

 abstract class AbstractProductProcessor
 {

<?php
/**
 * Copyright © Bold Brand Commerce Sp. z o.o. All rights reserved.
 * See LICENSE.txt for license details.
 */

declare(strict_types = 1);

namespace Ergonode\ImporterMagento1\Infrastructure\Processor\Step;

use Ergonode\ImporterMagento1\Infrastructure\Model\ProductModel;
use Ergonode\ImporterMagento1\Domain\Entity\Magento1CsvSource;
use Ergonode\Transformer\Domain\Entity\Transformer;
use Ergonode\Attribute\Domain\Entity\Attribute\ImageAttribute;
use Ergonode\Core\Domain\ValueObject\TranslatableString;
use Ergonode\Category\Domain\ValueObject\CategoryCode;

abstract class AbstractProductProcessor
{
    /**
     * @return CategoryCode[]
     */
    protected function getCategories(ProductModel $product): array
    {
        $result = [];

        $default = $product->get('default');
        if ($categories = $default['esa_categories'] ?? null) {
            foreach (explode(',', $categories) as $category) {
                $result[] = new CategoryCode($category);
            }
        }

        return $result;
    }

    /**
     * @return string[]
     */
    protected function getAttributes(
        Transformer $transformer,
        Magento1CsvSource $source,
        ProductModel $product
    ): array {
        $result = [];
        $default = $product->get('default');

        foreach ($default as $field => $value) {
            $translation = [];
            if ($transformer->hasAttribute($field)) {
                $type = $transformer->getAttributeType($field);
                $value = $this->format($type, $value);
                if ($value) {
                    $translation[$source->getDefaultLanguage()->getCode()] = $value;
                }

                foreach ($source->getLanguages() as $key => $language) {
                    if ($product->has($key)) {
                        $translatedVer = $product->get($key);
                        if (array_key_exists($field, $translatedVer) && null !== $translatedVer[$field]) {
                            $code = $language->getCode();
                            $translation[$code] = $this->format($type, $translatedVer[$field]);
                        }
                    }
                }

                $result[$field] = new TranslatableString($translation);
            }
        }

        return $result;
    }

    protected function format(
        string $type,
        ?string $value
    ): ?string {
        if ($value && ImageAttribute::TYPE === $type) {
            $value = pathinfo($value, PATHINFO_FILENAME);
            if ('no_selection' === $value) {
                $value = null;
            }
        }

        return $value;
    }
}


@@ -121,7 +121,7 @@ abstract class BaseMediaEventSubscriber implements EventSubscriber
     }

     /**
-     * @param \Doctrine\Common\EventArgs $args
+     * @param EventArgs $args
      */
     public function preRemove(EventArgs $args)
     {

<?php

/*
 * This file is part of the Sonata project.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\MediaBundle\Listener;

use Doctrine\Common\EventArgs;
use Doctrine\Common\EventSubscriber;
use Sonata\MediaBundle\Model\MediaInterface;
use Sonata\MediaBundle\Provider\Pool;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class BaseMediaEventSubscriber implements EventSubscriber
{
    protected $container;

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
     */
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * @return \Sonata\MediaBundle\Provider\Pool
     */
    public function getPool()
    {
        return $this->container->get('sonata.media.pool');
    }

    /**
     * @abstract
     *
     * @param \Doctrine\Common\EventArgs $args
     */
    abstract protected function recomputeSingleEntityChangeSet(EventArgs $args);

    /**
     * @abstract
     *
     * @param \Doctrine\Common\EventArgs $args
     *
     * @return \Sonata\MediaBundle\Model\MediaInterface
     */
    abstract protected function getMedia(EventArgs $args);

    /**
     * @param \Doctrine\Common\EventArgs $args
     *
     * @return \Sonata\MediaBundle\Provider\MediaProviderInterface
     */
    protected function getProvider(EventArgs $args)
    {
        $media = $this->getMedia($args);

        if (!$media instanceof MediaInterface) {
            return;
        }

        return $this->getPool()->getProvider($media->getProviderName());
    }

    /**
     * @param \Doctrine\Common\EventArgs $args
     */
    public function postUpdate(EventArgs $args)
    {
        if (!($provider = $this->getProvider($args))) {
            return;
        }

        $provider->postUpdate($this->getMedia($args));
    }

    /**
     * @param \Doctrine\Common\EventArgs $args
     */
    public function postRemove(EventArgs $args)
    {
        if (!($provider = $this->getProvider($args))) {
            return;
        }

        $provider->postRemove($this->getMedia($args));
    }

    /**
     * @param \Doctrine\Common\EventArgs $args
     */
    public function postPersist(EventArgs $args)
    {
        if (!($provider = $this->getProvider($args))) {
            return;
        }

        $provider->postPersist($this->getMedia($args));
    }

    /**
     * @param \Doctrine\Common\EventArgs $args
     */
    public function preUpdate(EventArgs $args)
    {
        if (!($provider = $this->getProvider($args))) {
            return;
        }

        $provider->transform($this->getMedia($args));
        $provider->preUpdate($this->getMedia($args));

        $this->recomputeSingleEntityChangeSet($args);
    }

    /**
     * @param \Doctrine\Common\EventArgs $args
     */
    public function preRemove(EventArgs $args)
    {
        if (!($provider = $this->getProvider($args))) {
            return;
        }

        $provider->preRemove($this->getMedia($args));
    }

    /**
     * @param \Doctrine\Common\EventArgs $args
     */
    public function prePersist(EventArgs $args)
    {
        if (!($provider = $this->getProvider($args))) {
            return;
        }

        $provider->transform($this->getMedia($args));
        $provider->prePersist($this->getMedia($args));
    }
}


@@ -46,6 +46,14 @@ class LiipImagineThumbnail implements ThumbnailInterface
             $this->router = $cacheManager;
         }
         $this->cacheManager = $cacheManager;
+        if (!$resolverRegistry instanceof ResolverRegistryInterface) {
+            @trigger_error(sprintf(
+                'Using %s without a %s is deprecated since version 3.16 and will no longer be possible in 4.0.',
+                __CLASS__,
+                ResolverRegistryInterface::class
+            ), E_USER_DEPRECATED);
+        }
+        $this->resolverRegistry = $resolverRegistry;
     }

     /**

<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\MediaBundle\Thumbnail;

use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Sonata\MediaBundle\Model\MediaInterface;
use Sonata\MediaBundle\Provider\MediaProviderInterface;
use Symfony\Component\Routing\RouterInterface;

class LiipImagineThumbnail implements ThumbnailInterface
{
    /**
     * @deprecated Since version 3.3, will be removed in 4.0.
     *
     * @var RouterInterface
     */
    protected $router;

    /**
     * @var CacheManager
     */
    private $cacheManager;

    /**
     * @param RouterInterface|CacheManager $cacheManager
     */
    public function __construct($cacheManager)
    {
        if ($cacheManager instanceof RouterInterface) {
            @trigger_error(sprintf(
                'Using an instance of %s is deprecated since version 3.3 and will be removed in 4.0. Use %s.',
                RouterInterface::class,
                CacheManager::class
            ), E_USER_DEPRECATED);
            $this->router = $cacheManager;
        }
        $this->cacheManager = $cacheManager;
    }

    /**
     * {@inheritdoc}
     */
    public function generatePublicUrl(MediaProviderInterface $provider, MediaInterface $media, $format)
    {
        $path = $provider->getReferenceImage($media);

        if (MediaProviderInterface::FORMAT_ADMIN === $format || MediaProviderInterface::FORMAT_REFERENCE === $format) {
            return $path;
        }
        if ($this->router instanceof RouterInterface && !($this->cacheManager instanceof CacheManager)) {
            $path = $this->router->generate(
                sprintf('_imagine_%s', $format),
                ['path' => sprintf('%s/%s_%s.jpg', $provider->generatePath($media), $media->getId(), $format)]
            );
        }

        $path = $provider->getCdnPath($path, $media->getCdnIsFlushable());
        if ($this->cacheManager instanceof CacheManager) {
            $path = $this->cacheManager->getBrowserPath($path, $format);
        }

        return $path;
    }

    /**
     * {@inheritdoc}
     */
    public function generatePrivateUrl(MediaProviderInterface $provider, MediaInterface $media, $format)
    {
        if (MediaProviderInterface::FORMAT_REFERENCE !== $format) {
            throw new \RuntimeException('No private url for LiipImagineThumbnail');
        }

        $path = $provider->getReferenceImage($media);

        return $path;
    }

    /**
     * {@inheritdoc}
     */
    public function generate(MediaProviderInterface $provider, MediaInterface $media)
    {
        // nothing to generate, as generated on demand
    }

    /**
     * {@inheritdoc}
     */
    public function delete(MediaProviderInterface $provider, MediaInterface $media, $formats = null)
    {
        // feature not available
    }
}


@@ -127,7 +127,7 @@ final class DailyMotionProvider extends BaseVideoProvider
     {
         $this->fixBinaryContent($media);

-        if (!$media->getBinaryContent()) {
+        if (null === $media->getBinaryContent()) {
             return;
         }

<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\MediaBundle\Provider;

use Sonata\MediaBundle\Model\MediaInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;

final class DailyMotionProvider extends BaseVideoProvider
{
    public function getHelperProperties(MediaInterface $media, string $format, array $options = []): array
    {
        // documentation : http://www.dailymotion.com/en/doc/api/player

        $defaults = [
            // Values: 0 or 1. Default is 0. Determines if the player loads related videos when
            // the current video begins playback.
            'related' => 0,

            // Values: 0 or 1. Default is 1. Determines if the player allows explicit content to
            // be played. This parameter may be added to embed code by platforms which do not
            // want explicit content to be posted by their users.
            'explicit' => 0,

            // Values: 0 or 1. Default is 0. Determines if the video will begin playing
            // automatically when the player loads.
            'autoPlay' => 0,

            // Values: 0 or 1. Default is 0. Determines if the video will begin muted.
            'autoMute' => 0,

            // Values: 0 or 1. Default is 0. Determines if the video will unmuted on mouse over.
            // Of course it works only if the player is on automute=1.
            'unmuteOnMouseOver' => 0,

            // Values: a number of seconds. Default is 0. Determines if the video will begin
            // playing the video at a given time.
            'start' => 0,

            // Values: 0 or 1. Default is 0. Enable the Javascript API by setting this parameter
            // to 1. For more information and instructions on using the Javascript API, see the
            // JavaScript API documentation.
            'enableApi' => 0,

            // Values: 0 or 1. Default is 0. Determines if the player should display controls
            // or not during video playback.
            'chromeless' => 0,

            // Values: 0 or 1. Default is 0. Determines if the video should be expended to fit
            // the whole player's size.
            'expendVideo' => 0,
            'color2' => null,

            // Player color changes may be set using color codes. A color is described by its
            // hexadecimal value (eg: FF0000 for red).
            'foreground' => null,
            'background' => null,
            'highlight' => null,
        ];

        $player_parameters = array_merge($defaults, $options['player_parameters'] ?? []);

        $box = $this->getBoxHelperProperties($media, $format, $options);

        $params = [
            'player_parameters' => http_build_query($player_parameters),
            'allowFullScreen' => $options['allowFullScreen'] ?? 'true',
            'allowScriptAccess' => $options['allowScriptAccess'] ?? 'always',
            'width' => $box->getWidth(),
            'height' => $box->getHeight(),
        ];

        return $params;
    }

    public function getProviderMetadata(): MetadataInterface
    {
        return new Metadata($this->getName(), $this->getName().'.description', 'bundles/sonatamedia/dailymotion-icon.png', 'SonataMediaBundle');
    }

    public function updateMetadata(MediaInterface $media, bool $force = false): void
    {
        $url = sprintf('http://www.dailymotion.com/services/oembed?url=%s&format=json', $this->getReferenceUrl($media));

        try {
            $metadata = $this->getMetadata($media, $url);
        } catch (\RuntimeException $e) {
            $media->setEnabled(false);
            $media->setProviderStatus(MediaInterface::STATUS_ERROR);

            return;
        }

        $media->setProviderMetadata($metadata);

        if ($force) {
            $media->setName($metadata['title']);
            $media->setAuthorName($metadata['author_name']);
        }

        $media->setHeight($metadata['height']);
        $media->setWidth($metadata['width']);
    }

    public function getDownloadResponse(MediaInterface $media, string $format, string $mode, array $headers = []): Response
    {
        return new RedirectResponse($this->getReferenceUrl($media), 302, $headers);
    }

    public function getReferenceUrl(MediaInterface $media): string
    {
        return sprintf('http://www.dailymotion.com/video/%s', $media->getProviderReference());
    }

    protected function doTransform(MediaInterface $media): void
    {
        $this->fixBinaryContent($media);

        if (!$media->getBinaryContent()) {
            return;
        }

        $media->setProviderName($this->name);
        $media->setProviderStatus(MediaInterface::STATUS_OK);
        $media->setProviderReference($media->getBinaryContent());

        $this->updateMetadata($media, true);
    }

    private function fixBinaryContent(MediaInterface $media): void
    {
        if (!$media->getBinaryContent()) {
            return;
        }

        if (preg_match('{^(?:https?://)?www.dailymotion.com/video/(?<video_id>[0-9a-zA-Z]*)}', $media->getBinaryContent(), $matches)) {
            $media->setBinaryContent($matches['video_id']);
        }
    }
}


@@ -29,13 +29,14 @@ class AttributeImportFilter
         $this->attributeImportValidator = $attributeImportValidator;
     }

-
     /**
-     * @var TranslatableString[]
+     * @param TranslatableString[] $attributes
+     *
+     * @return TranslatableString[]
      */
-    public function filter(array $attributes): array
+    public function filter(array $attributes, string $skuValue): array
     {
-        $attributesToRedispatch = [];
+        $filteredAttributes = [];
         foreach ($attributes as $codeValue => $attribute) {
             $code = new AttributeCode($codeValue);
             $attributeType = $this->attributeQuery->getAttributeTypeByCode($code);

<?php

/**
 * Copyright © Ergonode Sp. z o.o. All rights reserved.
 * See LICENSE.txt for license details.
 */

declare(strict_types=1);

namespace Ergonode\Importer\Infrastructure\Filter;

use Ergonode\Attribute\Domain\Query\AttributeQueryInterface;
use Ergonode\Attribute\Domain\ValueObject\AttributeCode;
use Ergonode\Core\Domain\ValueObject\TranslatableString;
use Ergonode\Importer\Infrastructure\Exception\ImportException;
use Ergonode\Importer\Infrastructure\Validator\AttributeImportValidator;

class AttributeImportFilter
{
    private AttributeQueryInterface $attributeQuery;

    private AttributeImportValidator $attributeImportValidator;

    public function __construct(
        AttributeQueryInterface $attributeQuery,
        AttributeImportValidator $attributeImportValidator
    ) {
        $this->attributeQuery = $attributeQuery;
        $this->attributeImportValidator = $attributeImportValidator;
    }

    /**
     * @var TranslatableString[]
     */
    public function filter(array $attributes): array
    {
        $attributesToRedispatch = [];
        foreach ($attributes as $codeValue => $attribute) {
            $code = new AttributeCode($codeValue);
            $attributeType = $this->attributeQuery->getAttributeTypeByCode($code);

            if (!$attributeType) {
                throw new ImportException(
                    'Attribute with {code} code does not exist',
                    ['{code}' => $code->getValue()]
                );
            }

            if (!$this->attributeImportValidator->validate($attributeType, $code, $attribute)) {
                $attributesToRedispatch[$codeValue] = $attribute;
            }
        }

        return $attributesToRedispatch;
    }
}


@@ -11,11 +11,14 @@

 namespace EasyCorp\Bundle\EasyAdminBundle\Form\Type;

+use ArrayObject;
 use EasyCorp\Bundle\EasyAdminBundle\Configuration\ConfigManager;
 use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Configurator\TypeConfiguratorInterface;
 use EasyCorp\Bundle\EasyAdminBundle\Form\Util\LegacyFormHelper;
 use Symfony\Component\Form\AbstractType;
 use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormEvent;
+use Symfony\Component\Form\FormEvents;
 use Symfony\Component\Form\FormInterface;
 use Symfony\Component\Form\FormView;
 use Symfony\Component\OptionsResolver\Options;

<?php

/*
 * This file is part of the EasyAdminBundle.
 *
 * (c) Javier Eguiluz <javier.eguiluz@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace EasyCorp\Bundle\EasyAdminBundle\Form\Type;

use EasyCorp\Bundle\EasyAdminBundle\Configuration\ConfigManager;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Configurator\TypeConfiguratorInterface;
use EasyCorp\Bundle\EasyAdminBundle\Form\Util\LegacyFormHelper;
use Symfony\Component\Form\AbstractType;
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;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

/**
 * Custom form type that deals with some of the logic used to render the
 * forms used to create and edit EasyAdmin entities.
 *
 * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
 */
class EasyAdminFormType extends AbstractType
{
    /** @var ConfigManager */
    private $configManager;

    /** @var TypeConfiguratorInterface[] */
    private $configurators;

    /**
     * @param ConfigManager               $configManager
     * @param TypeConfiguratorInterface[] $configurators
     */
    public function __construct(ConfigManager $configManager, array $configurators = array())
    {
        $this->configManager = $configManager;
        $this->configurators = $configurators;
    }

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $entity = $options['entity'];
        $view = $options['view'];
        $entityConfig = $this->configManager->getEntityConfig($entity);
        $entityProperties = isset($entityConfig[$view]['fields']) ? $entityConfig[$view]['fields'] : array();
        $formGroups = array();
        $currentFormGroup = null;

        foreach ($entityProperties as $name => $metadata) {
            $formFieldOptions = $metadata['type_options'];

            // Configure options using the list of registered type configurators:
            foreach ($this->configurators as $configurator) {
                if ($configurator->supports($metadata['fieldType'], $formFieldOptions, $metadata)) {
                    $formFieldOptions = $configurator->configure($name, $formFieldOptions, $metadata, $builder);
                }
            }

            $formFieldType = LegacyFormHelper::getType($metadata['fieldType']);

            // if the form field is a special 'group' design element, don't add it
            // to the form. Instead, consider it the current form group (this is
            // applied to the form fields defined after it) and store its details
            // in a property to get them in form template
            if (in_array($formFieldType, array('easyadmin_group', 'EasyCorp\\Bundle\\EasyAdminBundle\\Form\\Type\\EasyAdminGroupType'))) {
                $currentFormGroup = $metadata['fieldName'];
                $formGroups[$currentFormGroup] = $metadata;

                continue;
            }

            // 'divider' and 'section' are 'fake' form fields used to create the design
            // elements of the complex form layouts: define them as unmapped and non-required
            if (0 === strpos($metadata['property'], '_easyadmin_form_design_element_')) {
                $formFieldOptions['mapped'] = false;
                $formFieldOptions['required'] = false;
            }

            $formField = $builder->getFormFactory()->createNamedBuilder($name, $formFieldType, null, $formFieldOptions);
            $formField->setAttribute('easyadmin_form_group', $currentFormGroup);

            $builder->add($formField);
        }

        $builder->setAttribute('easyadmin_form_groups', $formGroups);
    }

    /**
     * {@inheritdoc}
     */
    public function finishView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['easyadmin_form_groups'] = $form->getConfig()->getAttribute('easyadmin_form_groups');
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $configManager = $this->configManager;

        $resolver
            ->setDefaults(array(
                'allow_extra_fields' => true,
                'data_class' => function (Options $options) use ($configManager) {
                    $entity = $options['entity'];
                    $entityConfig = $configManager->getEntityConfig($entity);

                    return $entityConfig['class'];
                },
            ))
            ->setRequired(array('entity', 'view'));

        // setNormalizer() is available since Symfony 2.6
        if (method_exists($resolver, 'setNormalizer')) {
            $resolver->setNormalizer('attr', $this->getAttributesNormalizer());
        } else {
            // BC for Symfony < 2.6
            $resolver->setNormalizers(array('attr' => $this->getAttributesNormalizer()));
        }
    }

    // BC for SF < 2.7
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $this->configureOptions($resolver);
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'easyadmin';
    }

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

    /**
     * Returns a closure normalizing the form html attributes.
     *
     * @return \Closure
     */
    private function getAttributesNormalizer()
    {
        return function (Options $options, $value) {
            return array_replace(array(
                'id' => sprintf('%s-%s-form', $options['view'], mb_strtolower($options['entity'])),
            ), $value);
        };
    }
}

class_alias('EasyCorp\Bundle\EasyAdminBundle\Form\Type\EasyAdminFormType', 'JavierEguiluz\Bundle\EasyAdminBundle\Form\Type\EasyAdminFormType', false);


@@ -61,7 +61,7 @@ class FixMediaContextCommandTest extends TestCase
      */
     protected function setUp()
     {
-        $this->container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();
+        $this->container = $this->getMockBuilder(ContainerInterface::class)->getMock();

         $this->command = new FixMediaContextCommand();
         $this->command->setContainer($this->container);

<?php

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\MediaBundle\Tests\Command;

use PHPUnit\Framework\TestCase;
use Sonata\ClassificationBundle\Model\ContextManagerInterface;
use Sonata\MediaBundle\Command\FixMediaContextCommand;
use Sonata\MediaBundle\Model\CategoryManagerInterface;
use Sonata\MediaBundle\Provider\Pool;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;

class FixMediaContextCommandTest extends TestCase
{
    /**
     * @var \PHPUnit_Framework_MockObject_MockObject|ContainerInterface
     */
    protected $container;

    /**
     * @var Application
     */
    protected $application;

    /**
     * @var ContainerAwareCommand
     */
    protected $command;

    /**
     * @var CommandTester
     */
    protected $tester;

    /**
     * @var \PHPUnit_Framework_MockObject_MockObject|Pool
     */
    private $pool;

    /**
     * @var \PHPUnit_Framework_MockObject_MockObject|ContextManagerInterface
     */
    private $contextManger;

    /**
     * @var \PHPUnit_Framework_MockObject_MockObject|CategoryManagerInterface
     */
    private $categoryManger;

    /**
     * {@inheritdoc}
     */
    protected function setUp()
    {
        $this->container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();

        $this->command = new FixMediaContextCommand();
        $this->command->setContainer($this->container);

        $this->application = new Application();
        $this->application->add($this->command);

        $this->tester = new CommandTester($this->application->find('sonata:media:fix-media-context'));

        $this->pool = $pool = $this->getMockBuilder('Sonata\MediaBundle\Provider\Pool')->disableOriginalConstructor()->getMock();

        $this->contextManger = $contextManger = $this->getMockBuilder('Sonata\ClassificationBundle\Model\ContextManagerInterface')->getMock();

        $this->categoryManger = $categoryManger = $this->createMock('Sonata\MediaBundle\Model\CategoryManagerInterface');

        $this->container->expects($this->any())
            ->method('get')
            ->will($this->returnCallback(function ($id) use ($pool, $contextManger, $categoryManger) {
                switch ($id) {
                    case 'sonata.media.pool':
                        return $pool;
                    case 'sonata.classification.manager.context':
                        return $contextManger;
                    case 'sonata.media.manager.category':
                        return $categoryManger;
                }
            }));
    }

    public function testExecuteWithDisabledClassfication()
    {
        $this->container->method('has')->with($this->equalTo('sonata.media.manager.category'))
            ->will($this->returnValue(false));

        $this->expectException('\LogicException');

        $this->tester->execute(['command' => $this->command->getName()]);
    }

    public function testExecuteWithExisting()
    {
        $context = [
            'providers' => [],
            'formats' => [],
            'download' => [],
        ];

        $this->container->method('has')->with($this->equalTo('sonata.media.manager.category'))
            ->will($this->returnValue(true));

        $this->pool->expects($this->any())->method('getContexts')->will($this->returnValue(['foo' => $context]));

        $contextModel = $this->getMockBuilder('Sonata\ClassificationBundle\Model\ContextInterface')->getMock();

        $this->contextManger->expects($this->once())->method('findOneBy')->with($this->equalTo(['id' => 'foo']))->will($this->returnValue($contextModel));

        $category = $this->getMockBuilder('Sonata\ClassificationBundle\Model\CategoryInterface')->getMock();

        $this->categoryManger->expects($this->once())->method('getRootCategory')->with($this->equalTo($contextModel))->will($this->returnValue($category));

        $output = $this->tester->execute(['command' => $this->command->getName()]);

        $this->assertRegExp('@Done!@', $this->tester->getDisplay());

        $this->assertSame(0, $output);
    }

    public function testExecuteWithEmptyRoot()
    {
        $context = [
            'providers' => [],
            'formats' => [],
            'download' => [],
        ];

        $this->container->method('has')->with($this->equalTo('sonata.media.manager.category'))
            ->will($this->returnValue(true));

        $this->pool->expects($this->any())->method('getContexts')->will($this->returnValue(['foo' => $context]));

        $contextModel = $this->getMockBuilder('Sonata\ClassificationBundle\Model\ContextInterface')->getMock();

        $this->contextManger->expects($this->once())->method('findOneBy')->with($this->equalTo(['id' => 'foo']))->will($this->returnValue($contextModel));

        $category = $this->getMockBuilder('Sonata\ClassificationBundle\Model\CategoryInterface')->getMock();

        $this->categoryManger->expects($this->once())->method('getRootCategory')->with($this->equalTo($contextModel))->will($this->returnValue(null));
        $this->categoryManger->expects($this->once())->method('create')->will($this->returnValue($category));
        $this->categoryManger->expects($this->once())->method('save')->with($this->equalTo($category));

        $output = $this->tester->execute(['command' => $this->command->getName()]);

        $this->assertRegExp('@ > default category for \'foo\' is missing, creating one\s+Done!@', $this->tester->getDisplay());

        $this->assertSame(0, $output);
    }

    public function testExecuteWithNew()
    {
        $context = [
            'providers' => [],
            'formats' => [],
            'download' => [],
        ];

        $this->container->method('has')->with($this->equalTo('sonata.media.manager.category'))
            ->will($this->returnValue(true));

        $this->pool->expects($this->any())->method('getContexts')->will($this->returnValue(['foo' => $context]));

        $contextModel = $this->getMockBuilder('Sonata\ClassificationBundle\Model\ContextInterface')->getMock();

        $this->contextManger->expects($this->once())->method('findOneBy')->with($this->equalTo(['id' => 'foo']))->will($this->returnValue(null));
        $this->contextManger->expects($this->once())->method('create')->will($this->returnValue($contextModel));
        $this->contextManger->expects($this->once())->method('save')->with($this->equalTo($contextModel));

        $category = $this->getMockBuilder('Sonata\ClassificationBundle\Model\CategoryInterface')->getMock();

        $this->categoryManger->expects($this->once())->method('getRootCategory')->with($this->equalTo($contextModel))->will($this->returnValue(null));
        $this->categoryManger->expects($this->once())->method('create')->will($this->returnValue($category));
        $this->categoryManger->expects($this->once())->method('save')->with($this->equalTo($category));

        $output = $this->tester->execute(['command' => $this->command->getName()]);

        $this->assertRegExp('@ > default category for \'foo\' is missing, creating one\s+Done!@', $this->tester->getDisplay());

        $this->assertSame(0, $output);
    }
}


@@ -117,5 +117,9 @@ class CustomerUserPasswordFacade
         $encoder = $this->encoderFactory->getEncoder($customerUser);
         $passwordHash = $encoder->encodePassword($password, null);
         $customerUser->setPasswordHash($passwordHash);
+
+        $this->em->flush();
+
+        $this->customerUserRefreshTokenChainFacade->removeAllCustomerUserRefreshTokenChains($customerUser);
     }
 }

<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Model\Customer\User;

use Doctrine\ORM\EntityManagerInterface;
use Shopsys\FrameworkBundle\Component\String\HashGenerator;
use Shopsys\FrameworkBundle\Model\Customer\Mail\ResetPasswordMailFacade;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;

class CustomerUserPasswordFacade
{
    public const RESET_PASSWORD_HASH_LENGTH = 50;

    /**
     * @var \Doctrine\ORM\EntityManagerInterface
     */
    protected $em;

    /**
     * @var \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserRepository
     */
    protected $customerUserRepository;

    /**
     * @var \Shopsys\FrameworkBundle\Model\Customer\Mail\ResetPasswordMailFacade
     */
    protected $resetPasswordMailFacade;

    /**
     * @var \Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface
     */
    protected $encoderFactory;

    /**
     * @var \Shopsys\FrameworkBundle\Component\String\HashGenerator
     */
    protected $hashGenerator;

    /**
     * @param \Doctrine\ORM\EntityManagerInterface $em
     * @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUserRepository $customerUserRepository
     * @param \Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface $encoderFactory
     * @param \Shopsys\FrameworkBundle\Model\Customer\Mail\ResetPasswordMailFacade $resetPasswordMailFacade
     * @param \Shopsys\FrameworkBundle\Component\String\HashGenerator $hashGenerator
     */
    public function __construct(
        EntityManagerInterface $em,
        CustomerUserRepository $customerUserRepository,
        EncoderFactoryInterface $encoderFactory,
        ResetPasswordMailFacade $resetPasswordMailFacade,
        HashGenerator $hashGenerator
    ) {
        $this->em = $em;
        $this->customerUserRepository = $customerUserRepository;
        $this->encoderFactory = $encoderFactory;
        $this->resetPasswordMailFacade = $resetPasswordMailFacade;
        $this->hashGenerator = $hashGenerator;
    }

    /**
     * @param string $email
     * @param int $domainId
     */
    public function resetPassword($email, $domainId)
    {
        $customerUser = $this->customerUserRepository->getCustomerUserByEmailAndDomain($email, $domainId);

        $resetPasswordHash = $this->hashGenerator->generateHash(static::RESET_PASSWORD_HASH_LENGTH);
        $customerUser->setResetPasswordHash($resetPasswordHash);

        $this->em->flush($customerUser);
        $this->resetPasswordMailFacade->sendMail($customerUser);
    }

    /**
     * @param string $email
     * @param int $domainId
     * @param string|null $hash
     * @return bool
     */
    public function isResetPasswordHashValid($email, $domainId, $hash)
    {
        $customerUser = $this->customerUserRepository->getCustomerUserByEmailAndDomain($email, $domainId);

        return $customerUser->isResetPasswordHashValid($hash);
    }

    /**
     * @param string $email
     * @param int $domainId
     * @param string|null $resetPasswordHash
     * @param string $newPassword
     *
     * @return \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser
     */
    public function setNewPassword(string $email, int $domainId, ?string $resetPasswordHash, string $newPassword): CustomerUser
    {
        $customerUser = $this->customerUserRepository->getCustomerUserByEmailAndDomain($email, $domainId);

        if (!$customerUser->isResetPasswordHashValid($resetPasswordHash)) {
            throw new \Shopsys\FrameworkBundle\Model\Customer\Exception\InvalidResetPasswordHashUserException();
        }

        $this->changePassword($customerUser, $newPassword);

        return $customerUser;
    }

    /**
     * @param \Shopsys\FrameworkBundle\Model\Customer\User\CustomerUser $customerUser
     * @param string $password
     */
    public function changePassword(CustomerUser $customerUser, string $password): void
    {
        $encoder = $this->encoderFactory->getEncoder($customerUser);
        $passwordHash = $encoder->encodePassword($password, null);
        $customerUser->setPasswordHash($passwordHash);
    }
}


@@ -100,9 +100,9 @@ return [
     'required_if'          => 'The :attribute field is required when :other is :value.',
     'required_unless'      => 'The :attribute field is required unless :other is in :values.',
     'required_with'        => 'The :attribute field is required when :values is present.',
-    'required_with_all'    => 'The :attribute field is required when :values are present.',
     'required_without'     => 'The :attribute field is required when :values is not present.',
     'required_without_all' => 'The :attribute field is required when none of :values are present.',
+    'required_with_all'    => 'The :attribute field is required when :values are present.',
     'same'                 => 'The :attribute and :other must match.',
     'size'                 => [
         'numeric' => 'The :attribute must be :size.',

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Validation Language Lines
    |--------------------------------------------------------------------------
    |
    | The following language lines contain the default error messages used by
    | the validator class. Some of these rules have multiple versions such
    | as the size rules. Feel free to tweak each of these messages here.
    |
    */

    'accepted'             => 'The :attribute must be accepted.',
    'active_url'           => 'The :attribute is not a valid URL.',
    'after'                => 'The :attribute must be a date after :date.',
    'after_or_equal'       => 'The :attribute must be a date after or equal to :date.',
    'alpha'                => 'The :attribute may only contain letters.',
    'alpha_dash'           => 'The :attribute may only contain letters, numbers, dashes and underscores.',
    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
    'array'                => 'The :attribute must be an array.',
    'before'               => 'The :attribute must be a date before :date.',
    'before_or_equal'      => 'The :attribute must be a date before or equal to :date.',
    'between'              => [
        'numeric' => 'The :attribute must be between :min and :max.',
        'file'    => 'The :attribute must be between :min and :max kilobytes.',
        'string'  => 'The :attribute must be between :min and :max characters.',
        'array'   => 'The :attribute must have between :min and :max items.',
    ],
    'boolean'              => 'The :attribute field must be true or false.',
    'confirmed'            => 'The :attribute confirmation does not match.',
    'date'                 => 'The :attribute is not a valid date.',
    'date_equals'          => 'The :attribute must be a date equal to :date.',
    'date_format'          => 'The :attribute does not match the format :format.',
    'different'            => 'The :attribute and :other must be different.',
    'digits'               => 'The :attribute must be :digits digits.',
    'digits_between'       => 'The :attribute must be between :min and :max digits.',
    'dimensions'           => 'The :attribute has invalid image dimensions.',
    'distinct'             => 'The :attribute field has a duplicate value.',
    'email'                => 'The :attribute must be a valid email address.',
    'ends_with'            => 'The :attribute must end with one of the following: :values.',
    'exists'               => 'The selected :attribute is invalid.',
    'file'                 => 'The :attribute must be a file.',
    'filled'               => 'The :attribute field must have a value.',
    'gt'                   => [
        'numeric' => 'The :attribute must be greater than :value.',
        'file'    => 'The :attribute must be greater than :value kilobytes.',
        'string'  => 'The :attribute must be greater than :value characters.',
        'array'   => 'The :attribute must have more than :value items.',
    ],
    'gte'                  => [
        'numeric' => 'The :attribute must be greater than or equal :value.',
        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
        'string'  => 'The :attribute must be greater than or equal :value characters.',
        'array'   => 'The :attribute must have :value items or more.',
    ],
    'image'                => 'The :attribute must be an image.',
    'in'                   => 'The selected :attribute is invalid.',
    'in_array'             => 'The :attribute field does not exist in :other.',
    'integer'              => 'The :attribute must be an integer.',
    'ip'                   => 'The :attribute must be a valid IP address.',
    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
    'json'                 => 'The :attribute must be a valid JSON string.',
    'lt'                   => [
        'numeric' => 'The :attribute must be less than :value.',
        'file'    => 'The :attribute must be less than :value kilobytes.',
        'string'  => 'The :attribute must be less than :value characters.',
        'array'   => 'The :attribute must have less than :value items.',
    ],
    'lte'                  => [
        'numeric' => 'The :attribute must be less than or equal :value.',
        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
        'string'  => 'The :attribute must be less than or equal :value characters.',
        'array'   => 'The :attribute must not have more than :value items.',
    ],
    'max'                  => [
        'numeric' => 'The :attribute may not be greater than :max.',
        'file'    => 'The :attribute may not be greater than :max kilobytes.',
        'string'  => 'The :attribute may not be greater than :max characters.',
        'array'   => 'The :attribute may not have more than :max items.',
    ],
    'mimes'                => 'The :attribute must be a file of type: :values.',
    'mimetypes'            => 'The :attribute must be a file of type: :values.',
    'min'                  => [
        'numeric' => 'The :attribute must be at least :min.',
        'file'    => 'The :attribute must be at least :min kilobytes.',
        'string'  => 'The :attribute must be at least :min characters.',
        'array'   => 'The :attribute must have at least :min items.',
    ],
    'multiple_of'          => 'The :attribute must be a multiple of :value',
    'not_in'               => 'The selected :attribute is invalid.',
    'not_regex'            => 'The :attribute format is invalid.',
    'numeric'              => 'The :attribute must be a number.',
    'password'             => 'The password is incorrect.',
    'present'              => 'The :attribute field must be present.',
    'regex'                => 'The :attribute format is invalid.',
    'required'             => 'The :attribute field is required.',
    'required_if'          => 'The :attribute field is required when :other is :value.',
    'required_unless'      => 'The :attribute field is required unless :other is in :values.',
    'required_with'        => 'The :attribute field is required when :values is present.',
    'required_with_all'    => 'The :attribute field is required when :values are present.',
    'required_without'     => 'The :attribute field is required when :values is not present.',
    'required_without_all' => 'The :attribute field is required when none of :values are present.',
    'same'                 => 'The :attribute and :other must match.',
    'size'                 => [
        'numeric' => 'The :attribute must be :size.',
        'file'    => 'The :attribute must be :size kilobytes.',
        'string'  => 'The :attribute must be :size characters.',
        'array'   => 'The :attribute must contain :size items.',
    ],
    'starts_with'          => 'The :attribute must start with one of the following: :values.',
    'string'               => 'The :attribute must be a string.',
    'timezone'             => 'The :attribute must be a valid zone.',
    'unique'               => 'The :attribute has already been taken.',
    'uploaded'             => 'The :attribute failed to upload.',
    'url'                  => 'The :attribute format is invalid.',
    'uuid'                 => 'The :attribute must be a valid UUID.',

    /*
    |--------------------------------------------------------------------------
    | Custom Validation Language Lines
    |--------------------------------------------------------------------------
    |
    | Here you may specify custom validation messages for attributes using the
    | convention "attribute.rule" to name the lines. This makes it quick to
    | specify a specific custom language line for a given attribute rule.
    |
    */

    'custom' => [
        'attribute-name' => [
            'rule-name' => 'custom-message',
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Custom Validation Attributes
    |--------------------------------------------------------------------------
    |
    | The following language lines are used to swap our attribute placeholder
    | with something more reader friendly such as "E-Mail Address" instead
    | of "email". This simply helps us make our message more expressive.
    |
    */

    'attributes' => [],
];


@@ -22,6 +22,18 @@ use Symfony\Component\Console\Output\OutputInterface;
  */
 class MigrateToJsonTypeCommand extends BaseCommand
 {
+    /**
+     * @var EntityManagerInterface
+     */
+    private $entityManager;
+
+    public function __construct(ManagerInterface $mediaManager, Pool $pool, ?EntityManagerInterface $entityManager = null)
+    {
+        parent::__construct($mediaManager, $pool);
+
+        $this->entityManager = $entityManager;
+    }
+
     public function configure()
     {
         $this->setName('sonata:media:migrate-json');

<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\MediaBundle\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * @final since sonata-project/media-bundle 3.21.0
 */
class MigrateToJsonTypeCommand extends BaseCommand
{
    public function configure()
    {
        $this->setName('sonata:media:migrate-json');
        $this->addOption('table', null, InputOption::VALUE_OPTIONAL, 'Media table', 'media__media');
        $this->addOption('column', null, InputOption::VALUE_OPTIONAL, 'Column name for provider_metadata', 'provider_metadata');
        $this->addOption('column_id', null, InputOption::VALUE_OPTIONAL, 'Column name for id', 'id');
        $this->setDescription('Migrate all media provider metadata to the doctrine JsonType');
    }

    public function execute(InputInterface $input, OutputInterface $output)
    {
        $count = 0;
        $table = $input->getOption('table');
        $column = $input->getOption('column');
        $columnId = $input->getOption('column_id');
        $connection = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection();
        $medias = $connection->fetchAll("SELECT * FROM $table");

        foreach ($medias as $media) {
            // if the row need to migrate
            if (0 !== strpos($media[$column], '{') && '[]' !== $media[$column]) {
                $media[$column] = json_encode(unserialize($media[$column]));
                $connection->update($table, [$column => $media[$column]], [$columnId => $media[$columnId]]);
                ++$count;
            }
        }

        $output->writeln("Migrated $count medias");

        return 0;
    }
}


@@ -52,8 +52,15 @@ class UnitGridFactory implements GridFactoryInterface
             ->from(Unit::class, 'u')
             ->join('u.translations', 'ut', Join::WITH, 'ut.locale = :locale')
             ->setParameter('locale', $this->localization->getAdminLocale());
-        $dataSource = new QueryBuilderDataSource($queryBuilder, 'u.id');
+        return new QueryBuilderDataSource($queryBuilder, 'u.id');
+    }

+    /**
+     * @param \Shopsys\FrameworkBundle\Component\Grid\DataSourceInterface $dataSource
+     * @return \Shopsys\FrameworkBundle\Component\Grid\Grid
+     */
+    protected function getGridForDataSource(DataSourceInterface $dataSource): Grid
+    {
         $grid = $this->gridFactory->create('unitList', $dataSource);
         $grid->setDefaultOrder('name');

<?php

namespace Shopsys\FrameworkBundle\Model\Product\Unit;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr\Join;
use Shopsys\FrameworkBundle\Component\Grid\GridFactory;
use Shopsys\FrameworkBundle\Component\Grid\GridFactoryInterface;
use Shopsys\FrameworkBundle\Component\Grid\QueryBuilderDataSource;
use Shopsys\FrameworkBundle\Model\Localization\Localization;

class UnitGridFactory implements GridFactoryInterface
{
    /**
     * @var \Doctrine\ORM\EntityManagerInterface
     */
    protected $em;

    /**
     * @var \Shopsys\FrameworkBundle\Component\Grid\GridFactory
     */
    protected $gridFactory;

    /**
     * @var \Shopsys\FrameworkBundle\Model\Localization\Localization
     */
    protected $localization;

    /**
     * @param \Doctrine\ORM\EntityManagerInterface $em
     * @param \Shopsys\FrameworkBundle\Component\Grid\GridFactory $gridFactory
     * @param \Shopsys\FrameworkBundle\Model\Localization\Localization $localization
     */
    public function __construct(
        EntityManagerInterface $em,
        GridFactory $gridFactory,
        Localization $localization
    ) {
        $this->em = $em;
        $this->gridFactory = $gridFactory;
        $this->localization = $localization;
    }

    /**
     * @return \Shopsys\FrameworkBundle\Component\Grid\Grid
     */
    public function create()
    {
        $queryBuilder = $this->em->createQueryBuilder();
        $queryBuilder
            ->select('u, ut')
            ->from(Unit::class, 'u')
            ->join('u.translations', 'ut', Join::WITH, 'ut.locale = :locale')
            ->setParameter('locale', $this->localization->getAdminLocale());
        $dataSource = new QueryBuilderDataSource($queryBuilder, 'u.id');

        $grid = $this->gridFactory->create('unitList', $dataSource);
        $grid->setDefaultOrder('name');

        $grid->addColumn('name', 'ut.name', t('Name'), true);

        $grid->setActionColumnClassAttribute('table-col table-col-10');
        $grid->addDeleteActionColumn('admin_unit_deleteconfirm', ['id' => 'u.id'])
            ->setAjaxConfirm();

        $grid->setTheme('@ShopsysFramework/Admin/Content/Unit/listGrid.html.twig');

        return $grid;
    }
}