<commit_before><?php
/**
 * Shopware 5
 * Copyright (c) shopware AG
 *
 * According to our dual licensing model, this program can be used either
 * under the terms of the GNU Affero General Public License, version 3,
 * or under a proprietary license.
 *
 * The texts of the GNU Affero General Public License with an additional
 * permission and of our proprietary license can be found at and
 * in the LICENSE file you have received along with this program.
 *
 * This program 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 Affero General Public License for more details.
 *
 * "Shopware" is a registered trademark of shopware AG.
 * The licensing of the program under the AGPLv3 does not imply a
 * trademark license. Therefore any rights, title and interest in
 * our trademarks remain entirely with us.
 */

namespace Shopware\Bundle\MediaBundle;

use League\Flysystem\FilesystemInterface;
use Shopware\Bundle\MediaBundle\Strategy\StrategyInterface;
use Shopware\Components\DependencyInjection\Container;
use Shopware\Models\Shop\Shop;

/**
 * Class MediaService
 * @package Shopware\Bundle\MediaBundle
 */
class MediaService implements MediaServiceInterface
{
    /**
     * @var FilesystemInterface
     */
    private $filesystem;

    /**
     * @var Container
     */
    private $container;

    /**
     * @var StrategyInterface
     */
    private $strategy;

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

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

    /**
     * @param FilesystemInterface $filesystem
     * @param StrategyInterface $strategy
     * @param Container $container
     * @param array $config
     * @throws \Exception
     */
    public function __construct(FilesystemInterface $filesystem, StrategyInterface $strategy, Container $container, array $config)
    {
        $this->filesystem = $filesystem;
        $this->container = $container;
        $this->strategy = $strategy;
        $this->config = $config;

        if (!isset($config['mediaUrl'])) {
            throw new \Exception(sprintf("Please provide a 'mediaUrl' in your %s adapter.", $config['type']));
        }

        $this->mediaUrl = $config['mediaUrl'] ?: $this->createFallbackMediaUrl();
    }

    /**
     * @inheritdoc
     */
    public function read($path)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->filesystem->read($path);
    }

    /**
     * @inheritdoc
     */
    public function readStream($path)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->filesystem->readStream($path);
    }

    /**
     * @inheritdoc
     */
    public function getUrl($path)
    {
        if (empty($path)) {
            return null;
        }

        if ($this->strategy->isEncoded($path)) {
            return $this->mediaUrl . $path;
        }

        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->mediaUrl . $path;
    }

    /**
     * @inheritdoc
     */
    public function write($path, $contents, $append = false)
    {
        $path = $this->strategy->encode($path);

        if ($append === false && $this->filesystem->has($path)) {
            $this->filesystem->delete($path);
        }

        $this->filesystem->put($path, $contents);
    }

    /**
     * @inheritdoc
     */
    public function writeStream($path, $resource, $append = false)
    {
        $path = $this->strategy->encode($path);

        if ($append === false && $this->filesystem->has($path)) {
            $this->filesystem->delete($path);
        }

        $this->filesystem->putStream($path, $resource);
    }

    /**
     * @inheritdoc
     */
    public function has($path)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->filesystem->has($path);
    }

    /**
     * @inheritdoc
     */
    public function delete($path)
    {
        $path = $this->strategy->encode($path);

        return $this->filesystem->delete($path);
    }

    /**
     * @inheritdoc
     */
    public function getSize($path)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->filesystem->getSize($path);
    }

    /**
     * @inheritdoc
     */
    public function rename($path, $newPath)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);
        $newPath = $this->strategy->encode($newPath);

        return $this->filesystem->rename($path, $newPath);
    }

    /**
     * @inheritdoc
     */
    public function normalize($path)
    {
        return $this->strategy->normalize($path);
    }

    /**
     * Generates a mediaUrl based on the request or router
     *
     * @return string
     * @throws \Exception
     */
    private function createFallbackMediaUrl()
    {
        $request = $this->container->get('front')->Request();

        if ($request && $request->getHttpHost()) {
            return ($request->isSecure() ? 'https' : 'http') . '://' . $request->getHttpHost() . $request->getBasePath() . "/";
        }

        if ($this->container->has('Shop')) {
            /** @var Shop $shop */
            $shop = $this->container->get('Shop');
        } else {
            /** @var Shop $shop */
            $shop = $this->container->get('models')->getRepository(Shop::class)->getActiveDefault();
        }

        if ($shop->getMain()) {
            $shop = $shop->getMain();
        }

        if ($shop->getAlwaysSecure()) {
            return 'https://' . $shop->getSecureHost() . $shop->getSecureBasePath() . '/';
        }

        return 'http://' . $shop->getHost() . $shop->getBasePath() . '/';
    }

    /**
     * @inheritdoc
     */
    public function getAdapterType()
    {
        return $this->config['type'];
    }

    /**
     * @inheritdoc
     */
    public function listFiles($directory = '')
    {
        $files = [];
        foreach ($this->filesystem->listContents($directory, true) as $file) {
            if ($file['type'] == 'dir' || strstr($file['path'], '/.') !== false) {
                continue;
            }

            $files[] = $file['path'];
        }

        return $files;
    }

    /**
     * @inheritdoc
     */
    public function createDir($dirname)
    {
        return $this->filesystem->createDir($dirname);
    }

    /**
     * Migrates a file to the new strategy if it's not present
     *
     * @internal
     * @param $path
     * @return void
     */
    public function migrateFile($path)
    {
        if ($this->getAdapterType() !== 'local' || $this->isEncoded($path)) {
            return;
        }

        $encodedPath = $this->strategy->encode($path);

        if ($this->filesystem->has($path) && !$this->filesystem->has($encodedPath)) {
            $this->filesystem->rename($path, $encodedPath);
        }
    }

    /**
     * @inheritdoc
     */
    public function encode($path)
    {
        return $this->strategy->encode($path);
    }

    /**
     * @inheritdoc
     */
    public function isEncoded($path)
    {
        return $this->strategy->isEncoded($path);
    }
}
<commit_msg>SW-17261 - Fix wrong media fallback url with encoded paths<commit_after><?php
/**
 * Shopware 5
 * Copyright (c) shopware AG
 *
 * According to our dual licensing model, this program can be used either
 * under the terms of the GNU Affero General Public License, version 3,
 * or under a proprietary license.
 *
 * The texts of the GNU Affero General Public License with an additional
 * permission and of our proprietary license can be found at and
 * in the LICENSE file you have received along with this program.
 *
 * This program 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 Affero General Public License for more details.
 *
 * "Shopware" is a registered trademark of shopware AG.
 * The licensing of the program under the AGPLv3 does not imply a
 * trademark license. Therefore any rights, title and interest in
 * our trademarks remain entirely with us.
 */

namespace Shopware\Bundle\MediaBundle;

use League\Flysystem\FilesystemInterface;
use Shopware\Bundle\MediaBundle\Strategy\StrategyInterface;
use Shopware\Components\DependencyInjection\Container;
use Shopware\Models\Shop\Shop;

/**
 * Class MediaService
 * @package Shopware\Bundle\MediaBundle
 */
class MediaService implements MediaServiceInterface
{
    /**
     * @var FilesystemInterface
     */
    private $filesystem;

    /**
     * @var Container
     */
    private $container;

    /**
     * @var StrategyInterface
     */
    private $strategy;

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

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

    /**
     * @param FilesystemInterface $filesystem
     * @param StrategyInterface $strategy
     * @param Container $container
     * @param array $config
     * @throws \Exception
     */
    public function __construct(FilesystemInterface $filesystem, StrategyInterface $strategy, Container $container, array $config)
    {
        $this->filesystem = $filesystem;
        $this->container = $container;
        $this->strategy = $strategy;
        $this->config = $config;

        if (!isset($config['mediaUrl'])) {
            throw new \Exception(sprintf("Please provide a 'mediaUrl' in your %s adapter.", $config['type']));
        }

        $mediaUrl = $config['mediaUrl'] ?: $this->createFallbackMediaUrl();
        $this->mediaUrl = rtrim($mediaUrl, '/');
    }

    /**
     * @inheritdoc
     */
    public function read($path)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->filesystem->read($path);
    }

    /**
     * @inheritdoc
     */
    public function readStream($path)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->filesystem->readStream($path);
    }

    /**
     * @inheritdoc
     */
    public function getUrl($path)
    {
        if (empty($path)) {
            return null;
        }

        if ($this->strategy->isEncoded($path)) {
            return $this->mediaUrl . '/' . ltrim($path, '/');
        }

        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->mediaUrl . '/' . ltrim($path, '/');
    }

    /**
     * @inheritdoc
     */
    public function write($path, $contents, $append = false)
    {
        $path = $this->strategy->encode($path);

        if ($append === false && $this->filesystem->has($path)) {
            $this->filesystem->delete($path);
        }

        $this->filesystem->put($path, $contents);
    }

    /**
     * @inheritdoc
     */
    public function writeStream($path, $resource, $append = false)
    {
        $path = $this->strategy->encode($path);

        if ($append === false && $this->filesystem->has($path)) {
            $this->filesystem->delete($path);
        }

        $this->filesystem->putStream($path, $resource);
    }

    /**
     * @inheritdoc
     */
    public function has($path)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->filesystem->has($path);
    }

    /**
     * @inheritdoc
     */
    public function delete($path)
    {
        $path = $this->strategy->encode($path);

        return $this->filesystem->delete($path);
    }

    /**
     * @inheritdoc
     */
    public function getSize($path)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);

        return $this->filesystem->getSize($path);
    }

    /**
     * @inheritdoc
     */
    public function rename($path, $newPath)
    {
        $this->migrateFile($path);
        $path = $this->strategy->encode($path);
        $newPath = $this->strategy->encode($newPath);

        return $this->filesystem->rename($path, $newPath);
    }

    /**
     * @inheritdoc
     */
    public function normalize($path)
    {
        return $this->strategy->normalize($path);
    }

    /**
     * Generates a mediaUrl based on the request or router
     *
     * @return string
     * @throws \Exception
     */
    private function createFallbackMediaUrl()
    {
        $request = $this->container->get('front')->Request();

        if ($request && $request->getHttpHost()) {
            return ($request->isSecure() ? 'https' : 'http') . '://' . $request->getHttpHost() . $request->getBasePath() . "/";
        }

        if ($this->container->has('Shop')) {
            /** @var Shop $shop */
            $shop = $this->container->get('Shop');
        } else {
            /** @var Shop $shop */
            $shop = $this->container->get('models')->getRepository(Shop::class)->getActiveDefault();
        }

        if ($shop->getMain()) {
            $shop = $shop->getMain();
        }

        if ($shop->getAlwaysSecure()) {
            return 'https://' . $shop->getSecureHost() . $shop->getSecureBasePath() . '/';
        }

        return 'http://' . $shop->getHost() . $shop->getBasePath() . '/';
    }

    /**
     * @inheritdoc
     */
    public function getAdapterType()
    {
        return $this->config['type'];
    }

    /**
     * @inheritdoc
     */
    public function listFiles($directory = '')
    {
        $files = [];
        foreach ($this->filesystem->listContents($directory, true) as $file) {
            if ($file['type'] == 'dir' || strstr($file['path'], '/.') !== false) {
                continue;
            }

            $files[] = $file['path'];
        }

        return $files;
    }

    /**
     * @inheritdoc
     */
    public function createDir($dirname)
    {
        return $this->filesystem->createDir($dirname);
    }

    /**
     * Migrates a file to the new strategy if it's not present
     *
     * @internal
     * @param $path
     * @return void
     */
    public function migrateFile($path)
    {
        if ($this->getAdapterType() !== 'local' || $this->isEncoded($path)) {
            return;
        }

        $encodedPath = $this->strategy->encode($path);

        if ($this->filesystem->has($path) && !$this->filesystem->has($encodedPath)) {
            $this->filesystem->rename($path, $encodedPath);
        }
    }

    /**
     * @inheritdoc
     */
    public function encode($path)
    {
        return $this->strategy->encode($path);
    }

    /**
     * @inheritdoc
     */
    public function isEncoded($path)
    {
        return $this->strategy->isEncoded($path);
    }
}

{'fraction_non_alphanumeric': 0.15604365988411265, 'fraction_numerical': 0.0007411400080851638, 'mean_word_length': 2.1467034131863474, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 209, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4549637', 'n_tokens_mistral': 4353, 'n_tokens_neox': 4237, 'n_words': 956}