Current File : //var/www/vinorea/modules/psxdesign/src/Controller/Admin/AdminPsxDesignLogosController.php
<?php
/**
 * Copyright since 2007 PrestaShop SA and Contributors
 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is bundled with this package in the file LICENSE.md.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/OSL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to https://devdocs.prestashop.com/ for more information.
 *
 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
 * @copyright Since 2007 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
 */

declare(strict_types=1);

namespace PrestaShop\Module\PsxDesign\Controller\Admin;

if (!defined('_PS_VERSION_')) {
    exit;
}

use Exception;
use PrestaShop\Module\PsxDesign\Config\PsxDesignConfig;
use PrestaShop\Module\PsxDesign\DTO\PsxDesignLogoData;
use PrestaShop\Module\PsxDesign\DTO\PsxDesignLogoTextData;
use PrestaShop\Module\PsxDesign\Exception\PsxDesignApiException;
use PrestaShop\Module\PsxDesign\Exception\PsxDesignException;
use PrestaShop\Module\PsxDesign\Exception\PsxDesignLogoImportException;
use PrestaShop\Module\PsxDesign\Exception\PsxDesignTextToImageConvertException;
use PrestaShop\Module\PsxDesign\Provider\FontDataProvider;
use PrestaShop\Module\PsxDesign\Repository\PsxdesignLogoRepository;
use PrestaShop\Module\PsxDesign\Traits\UpgradeNotification\UpgradeNotificationTrait;
use PrestaShop\Module\PsxDesign\VO\Logo\LogoDestination;
use PrestaShop\PrestaShop\Core\Domain\Exception\FileUploadException;
use PrestaShop\PrestaShop\Core\Domain\Shop\DTO\ShopLogoSettings;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\NotSupportedFaviconExtensionException;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\NotSupportedLogoImageExtensionException;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\NotSupportedMailAndInvoiceImageExtensionException;
use PrestaShop\PrestaShop\Core\Domain\Shop\Query\GetLogosPaths;
use PrestaShop\PrestaShop\Core\Domain\Shop\QueryResult\LogosPaths;
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
use PrestaShopBundle\Security\Annotation\AdminSecurity;
use PrestaShopException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Throwable;

class AdminPsxDesignLogosController extends FrameworkBundleAdminController
{
    use UpgradeNotificationTrait;

    private const UPLOAD_LOGO_TYPE_IMAGE = 'image';
    private const UPLOAD_LOGO_TYPE_TEXT = 'text';
    private const HEADER = 'header';

    /**
     * Show logos page.
     *
     * @AdminSecurity(
     *     "is_granted('read', request.get('_legacy_controller'))",
     *     message="You do not have permission to edit this."
     * )
     *
     * @return Response
     */
    public function indexAction(): Response
    {
        try {
            $this->showUpgradeNotification();
        } catch (Exception $exception) {
            // Avoid fatal errors on ServiceNotFoundException
        }

        /** @var LogosPaths $logoProvider */
        $logoProvider = $this->getQueryBus()->handle(new GetLogosPaths());
        $shopLogoSettings = new ShopLogoSettings();
        $fontsProvider = $this->get('prestashop.module.psxdesign.provider.font_data_provider');

        //added random number at each path on every page load as sometimes heavy caching, caches previous images
        $faviconPath = $logoProvider->getFaviconPath() . '?' . random_int(1, 100);
        $invoiceLogoPath = $logoProvider->getInvoiceLogoPath() . '?' . random_int(1, 100);
        $mailLogoPath = $logoProvider->getMailLogoPath() . '?' . random_int(1, 100);
        $headerLogoPath = $logoProvider->getHeaderLogoPath() . '?' . random_int(1, 100);

        return $this->render('@Modules/psxdesign/views/templates/admin/logos/index.html.twig', [
            'shopName' => $this->get('prestashop.adapter.shop.context')->getShopName(),
            'isSingleShopContext' => $this->get('prestashop.adapter.shop.context')->isSingleShopContext(),
            'faviconPath' => $faviconPath,
            'invoiceLogoPath' => $invoiceLogoPath,
            'mailLogoPath' => $mailLogoPath,
            'headerLogoPath' => $headerLogoPath,
            'baseShopUrl' => $this->get('prestashop.adapter.shop.url.base_url_provider')->getUrl(),
            'headerFormats' => $shopLogoSettings->getLogoImageExtensionsWithDot(),
            'otherFormats' => $shopLogoSettings->getLogoImageExtensionsWithDot('PS_LOGO_MAIL'),
            'iconFormat' => $shopLogoSettings->getIconImageExtensionWithDot(),
            'logos' => $this->getLogosData(),
            'fonts' => $fontsProvider->provideDefaultFonts(),
            'fontVariants' => $fontsProvider->provideFontsVariants(),
        ]);
    }

    /**
     * @throws PsxDesignLogoImportException
     */
    public function importLogoImageAction(Request $request): Response
    {
        $logoUploadHandler = $this->get('prestashop.module.psxdesign.handler.logo_image_uploader');

        try {
            $logoData = PsxDesignLogoData::createFromRequest($request);
            $destination = $logoUploadHandler->uploadLogo($logoData, self::UPLOAD_LOGO_TYPE_IMAGE);
            $this->addFlash('success', $this->trans('The %destination% logo has been added.', 'Modules.Psxdesign.Admin', ['%destination%' => $this->translateDestination($destination)]));

            // After logo upload temporary file is deleted.
            // Kernel fails to load tmp file because file do not exist.
            // Error is thrown if files variable is not reseted
            // Suggestion to do it found on https://github.com/laravel/framework/issues/12350
            $_FILES = [];

            $this->get('prestashop.module.psxdesign.tracker.segment')
                ->track(
                    'Logo Uploaded', ['logo_type' => self::UPLOAD_LOGO_TYPE_IMAGE, 'logo_location' => $destination], $request->server);
        } catch (Throwable $e) {
            $errorHandler = $this->get('prestashop.module.psxdesign.exception.handler.sentry_exception_error_handler');
            $errorHandler->handle($e, $this->getErrorCode($e));

            $this->addFlash('error', $this->getMessage($e, $request->request->get('logo_for')));
        }

        return $this->redirectToRoute('admin_logos_index');
    }

    public function applyHeaderLogoAction(Request $request): Response
    {
        if (!($request->request->get('use_same_as_header'))) {
            $this->addFlash('error', $this->trans('Use same header logo is unavailable.', 'Modules.Psxdesign.Admin'));

            return $this->redirectToRoute('admin_logos_index');
        }

        $logoUploadHandler = $this->get('prestashop.module.psxdesign.handler.logo_image_uploader');

        try {
            $logoDestination = new LogoDestination($request->get('logo_for'));
            $logoUploadHandler->applyHeaderLogoImage($logoDestination);
            $this->addFlash('success', $this->trans('Header logo applied successfully.', 'Modules.Psxdesign.Admin'));
        } catch (Throwable $e) {
            $errorHandler = $this->get('prestashop.module.psxdesign.exception.handler.sentry_exception_error_handler');
            $errorHandler->handle($e, $this->getErrorCode($e));

            $this->addFlash('error', $this->getMessage($e));
        }

        return $this->redirectToRoute('admin_logos_index');
    }

    /**
     * @param Request $request
     *
     * @return Response
     */
    public function uploadFaviconAction(Request $request): Response
    {
        $uploader = $this->get('prestashop.core.shop.logo_uploader');
        $logoUtility = $this->get('prestashop.module.psxdesign.utility.logo_utility');

        /** @var UploadedFile $favicon */
        $favicon = $request->files->get(ShopLogoSettings::FAVICON_FILE_NAME);

        try {
            $logoUtility->assertFaviconType($favicon->guessClientExtension());
            $uploader->updateFavicon();
            $this->get('prestashop.module.psxdesign.tracker.segment')->track('Favicon Uploaded', [], $request->server);
            $this->addFlash('success', $this->trans('Favicon updated successfully', 'Modules.Psxdesign.Admin'));
        } catch (Throwable $e) {
            $errorHandler = $this->get('prestashop.module.psxdesign.exception.handler.sentry_exception_error_handler');
            $errorHandler->handle($e, $this->getErrorCode($e));
            $this->addFlash('error', $this->getMessage($e));
        }

        return $this->redirectToRoute('admin_logos_index');
    }

    /**
     * @throws PsxDesignLogoImportException
     */
    public function importLogoTextAction(Request $request): Response
    {
        $fontProvider = $this->get('prestashop.module.psxdesign.provider.font_data_provider');

        try {
            $logoTextData = PsxDesignLogoTextData::createFromRequest($request);
            $fontContent = $fontProvider->getFontContent($logoTextData->getFamily(), $logoTextData->getText(), $logoTextData->getStyle());
            $fontProvider->saveFontDataIntoTemporaryFolder($fontContent);

            $converter = $this->get('prestashop.module.psxdesign.converter.text_to_logo_converter');
            $imagePath = $converter->convertToImage($logoTextData);

            $logoTextUploader = $this->get('prestashop.module.psxdesign.handler.logo_text_upload_handler');
            $destination = $logoTextUploader->uploadLogoImage($imagePath, $logoTextData);
            $this->get('prestashop.module.psxdesign.tracker.segment')
                ->track(
                    'Logo Uploaded', ['logo_type' => self::UPLOAD_LOGO_TYPE_TEXT, 'logo_location' => $destination], $request->server);
            $this->addFlash('success', $this->trans('The %destination% logo has been added.', 'Modules.Psxdesign.Admin', ['%destination%' => $this->translateDestination($destination)]));
        } catch (Throwable $e) {
            $this->addFlash('error', $this->getErrorMessageForLogoText($e));

            $errorHandler = $this->get('prestashop.module.psxdesign.exception.handler.sentry_exception_error_handler');
            $errorHandler->handle($e, $this->getErrorCode($e));
        }

        $tmpFontFile = _PS_MODULE_DIR_ . 'psxdesign/' . PsxDesignConfig::TMP_DIR_NAME . '/' . FontDataProvider::TEMPORARY_FONT_NAME;

        if (file_exists($tmpFontFile)) {
            unlink($tmpFontFile);
        }

        return $this->redirectToRoute('admin_logos_index');
    }

    /**
     * @param string $destination
     *
     * @return string
     */
    private function translateDestination(string $destination): string
    {
        switch ($destination) {
            case 'header':
                return $this->trans('header', 'Modules.Psxdesign.Admin');
            case 'email':
                return $this->trans('email', 'Modules.Psxdesign.Admin');
            case 'invoice':
                return $this->trans('invoice', 'Modules.Psxdesign.Admin');
            default:
                return $destination;
        }
    }

    /**
     * @param Throwable $e
     *
     * @return string
     */
    private function getMessage(Throwable $e, $destination = null): string
    {
        $availableLogoFormatsImploded = implode(', .', ShopLogoSettings::AVAILABLE_LOGO_IMAGE_EXTENSIONS);
        $availableMailAndInvoiceFormatsImploded = implode(', .', ShopLogoSettings::AVAILABLE_MAIL_AND_INVOICE_LOGO_IMAGE_EXTENSIONS);
        $availableIconFormat = ShopLogoSettings::AVAILABLE_ICON_IMAGE_EXTENSION;

        $logoImageFormatError = $this->trans(
            'The image format is not recognized, allowed formats are: %formats%.',
            'Modules.Psxdesign.Admin',
            ['%formats%' => $availableLogoFormatsImploded]
        );

        $mailAndInvoiceImageFormatError = $this->trans(
            'The image format is not recognized, allowed formats are: %formats%.',
            'Modules.Psxdesign.Admin',
            ['%formats%' => $availableMailAndInvoiceFormatsImploded]
        );

        $iconFormatError = $this->trans(
            'The image format is not recognized, allowed format is: .%format%.',
            'Modules.Psxdesign.Admin',
            ['%format%' => $availableIconFormat]
        );

        $map = [
            PsxDesignLogoImportException::class => [
                PsxDesignLogoImportException::INVALID_FORMAT => $this->trans('The format is invalid, allowed formats are %allowed_formats%.', 'Modules.Psxdesign.Admin', ['%allowed_formats%' => implode(', ', ShopLogoSettings::AVAILABLE_MAIL_AND_INVOICE_LOGO_IMAGE_EXTENSIONS)]),
                PsxDesignLogoImportException::INVALID_DESTINATION => $this->trans('The logo destination is invalid. Please try again.', 'Modules.Psxdesign.Admin'),
                PsxDesignLogoImportException::LOGO_INCOMPATIBILITY => $this->trans('The .svg format is not compatible.', 'Modules.Psxdesign.Admin'),
                PsxDesignLogoImportException::INSERT_LOGO_FAILED => $this->trans('The logo upload failed. Please try again.', 'Modules.Psxdesign.Admin'),
            ],

            NotSupportedLogoImageExtensionException::class => $logoImageFormatError,
            NotSupportedMailAndInvoiceImageExtensionException::class => $mailAndInvoiceImageFormatError,
            NotSupportedFaviconExtensionException::class => $iconFormatError,
            FileUploadException::class => [
                UPLOAD_ERR_INI_SIZE => $this->trans(
                    'The file is too large (limit of %size% bytes).',
                    'Modules.Psxdesign.Admin',
                    [
                        '%size%' => UploadedFile::getMaxFilesize(),
                    ]
                ),
            ],
        ];

        if (isset($map[get_class($e)]) && is_array($map[get_class($e)])) {
            return $map[get_class($e)][$e->getCode()] ?? $this->trans('The logo upload failed. Please try again.', 'Modules.Psxdesign.Admin');
        }

        if (get_class($e) === PrestaShopException::class) {
            return $destination === self::HEADER
                ? $logoImageFormatError
                : $mailAndInvoiceImageFormatError;
        }

        return $map[get_class($e)] ?? $this->getFallbackErrorMessage(get_class($e), $e->getCode());
    }

    /**
     * @param Throwable $e
     *
     * @return int
     */
    private function getErrorCode(Throwable $e): int
    {
        if ($e instanceof PsxDesignException) {
            return $e->getCode();
        }

        $map = [
            NotSupportedLogoImageExtensionException::class => PsxDesignException::INFO_SEVERITY,
            NotSupportedMailAndInvoiceImageExtensionException::class => PsxDesignException::INFO_SEVERITY,
            NotSupportedFaviconExtensionException::class => PsxDesignException::INFO_SEVERITY,
            FileUploadException::class => [
                UPLOAD_ERR_INI_SIZE => PsxDesignException::INFO_SEVERITY,
            ],
        ];

        $map[get_class($e)] = $map[get_class($e)] ?? null;

        /* If undefined error occurred, and it is not in the list then we want to send it */
        if (is_array($map[get_class($e)])) {
            return $map[get_class($e)][$e->getCode()] ?? PsxDesignException::WARNING_SEVERITY;
        }

        return $map[get_class($e)] ?? PsxDesignException::WARNING_SEVERITY;
    }

    /**
     * @param Throwable $e
     *
     * @return string
     */
    private function getErrorMessageForLogoText(Throwable $e): string
    {
        $map = [
            PsxDesignTextToImageConvertException::class => [
                PsxDesignTextToImageConvertException::FAILED_INIT_GD_STREAM => $this->trans('Failed to initialize image creation. PHP GD extension missing.', 'Modules.Psxdesign.Admin'),
                PsxDesignTextToImageConvertException::FAILED_TO_CONVERT => $this->trans('Failed convert text to image. Please select different font.', 'Modules.Psxdesign.Admin'),
                PsxDesignTextToImageConvertException::FAILED_ADD_TEXT => $this->trans('Failed convert text to image. Please select different font.', 'Modules.Psxdesign.Admin'),
                PsxDesignTextToImageConvertException::FAILED_FETCH_FONT => $this->trans('This font does not exist. Please choose another font.', 'Modules.Psxdesign.Admin'),
                PsxDesignTextToImageConvertException::FAILED_CREATE_TMP_DIR => $this->trans('The creation of a temporary directory to store the uploaded font has failed. Please check the user permissions on your system.', 'Modules.Psxdesign.Admin'),
            ],
            PrestaShopException::class => $this->trans('Failed to convert text to logo. Please try again.', 'Modules.Psxdesign.Admin'),
            PsxDesignApiException::class => [
                PsxDesignApiException::FAILED_FETCH_FONT => $this->trans('Failed to retrieve font. Please use different font.', 'Modules.Psxdesign.Admin'),
            ],
            PsxDesignLogoImportException::class => [
                PsxDesignLogoImportException::TEXT_LENGTH_EXCEEDED => $this->trans('The text may not be longer than 64 characters.', 'Modules.Psxdesign.Admin'),
            ],
        ];

        if (isset($map[get_class($e)]) && is_array($map[get_class($e)])) {
            return $map[get_class($e)][$e->getCode()] ?? $this->trans('The logo upload failed. Please try again.', 'Modules.Psxdesign.Admin');
        }

        return $map[get_class($e)] ?? $this->getFallbackErrorMessage(get_class($e), $e->getCode());
    }

    /**
     * @return array
     */
    private function getLogosData(): array
    {
        /** @var LogosPaths $logoProvider */
        $logoProvider = $this->getQueryBus()->handle(new GetLogosPaths());

        /** @var PsxdesignLogoRepository $logosRepository */
        $logosRepository = $this->get('prestashop.module.psxdesign.repository.psxdesign_logo_repository');
        $headerLogo = $logosRepository->getHeaderLogo();
        $invoiceLogo = $logosRepository->getInvoiceLogo();
        $emailLogo = $logosRepository->getEmailLogo();

        $logos = [];

        if ($headerLogo !== null) {
            $logos['header'] = $headerLogo->toArray();
        }

        if ($invoiceLogo !== null) {
            $logos['invoice'] = $invoiceLogo->toArray();
        }

        if ($emailLogo !== null) {
            $logos['email'] = $emailLogo->toArray();
        }

        if ($logoProvider->getHeaderLogoPath() === $logoProvider->getMailLogoPath()) {
            $logos['email']['useHeaderLogo'] = true;
        }

        if ($logoProvider->getHeaderLogoPath() === $logoProvider->getInvoiceLogoPath()) {
            $logos['invoice']['useHeaderLogo'] = true;
        }

        return $logos;
    }
}