Add InPost Pay integration to admin templates

- Created a new template for the cart rule form with custom label, switch, and choice widgets.
- Implemented the InPost Pay block in the order details template for displaying delivery method, APM, and VAT invoice request.
- Added legacy support for the order details template to maintain compatibility with older PrestaShop versions.
This commit is contained in:
2025-09-14 14:38:09 +02:00
parent d895f86a03
commit 4066f6fa31
1086 changed files with 76598 additions and 6 deletions

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace izi\prestashop\Log\Handler;
use Monolog\Handler\HandlerInterface;
use Monolog\Logger;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
abstract class AbstractHandlerFactory implements HandlerFactoryInterface
{
private $environment;
private $optionsResolver;
public function __construct(string $environment)
{
$this->environment = $environment;
}
public function create(array $options): HandlerInterface
{
$options = $this->getOptionsResolver()->resolve($options);
return $this->doCreate($options);
}
protected function createOptionsResolver(): OptionsResolver
{
return (new OptionsResolver())
->setDefaults([
'level' => 'prod' === $this->environment ? Logger::INFO : Logger::DEBUG,
'bubble' => true,
])
->setNormalizer('level', static function (Options $options, $value) {
if (null === $value || is_numeric($value)) {
return $value;
}
$upper = strtoupper($value);
$constName = sprintf('%s::%s', Logger::class, $upper);
if (defined($constName)) {
return constant($constName);
}
throw new \InvalidArgumentException(sprintf('Could not match "%s" to a log level.', $value));
});
}
abstract protected function doCreate(array $options): HandlerInterface;
private function getOptionsResolver(): OptionsResolver
{
return $this->optionsResolver ?? ($this->optionsResolver = $this->createOptionsResolver());
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace izi\prestashop\Log\Handler;
use Monolog\Handler\HandlerInterface;
interface HandlerFactoryInterface
{
public function create(array $options): HandlerInterface;
public function supports(string $type): bool;
}

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace izi\prestashop\Log\Handler;
use Monolog\Handler\HandlerInterface;
use Monolog\Handler\RotatingFileHandler;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class RotatingFileHandlerFactory extends AbstractHandlerFactory
{
public function supports(string $type): bool
{
return 'rotating_file' === $type;
}
protected function doCreate(array $options): HandlerInterface
{
$handler = new RotatingFileHandler(
$options['path'],
$options['max_files'],
$options['level'],
$options['bubble'],
$options['file_permission'],
$options['use_locking']
);
$handler->setFilenameFormat($options['filename_format'], $options['date_format']);
return $handler;
}
protected function createOptionsResolver(): OptionsResolver
{
return parent::createOptionsResolver()
->setRequired('path')
->setDefaults([
'max_files' => 0,
'file_permission' => null,
'use_locking' => false,
'filename_format' => '{filename}-{date}',
'date_format' => 'Y-m-d',
])
->setNormalizer('file_permission', static function (Options $options, $value) {
if (!is_string($value)) {
return $value;
}
if (0 === strpos($value, '0')) {
return octdec($value);
}
return (int) $value;
});
}
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace izi\prestashop\Log;
use Psr\Log\LoggerInterface;
interface LoggerFactoryInterface
{
public function create(string $name, array $options): LoggerInterface;
}

View File

@@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
namespace izi\prestashop\Log;
use izi\prestashop\Log\Handler\HandlerFactoryInterface;
use Monolog\Formatter\JsonFormatter;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\HandlerInterface;
use Monolog\Logger;
use Monolog\Processor\PsrLogMessageProcessor;
use Psr\Log\LoggerInterface;
final class MonologLoggerFactory implements LoggerFactoryInterface
{
private static $decoratePsrProcessor;
/**
* @param iterable<HandlerFactoryInterface> $factories
*/
private $factories;
/**
* @var array<string, HandlerInterface>
*/
private $handlers = [];
/**
* @param iterable<HandlerFactoryInterface> $factories
*/
public function __construct(iterable $factories)
{
$this->factories = $factories;
}
public function create(string $name, array $options): LoggerInterface
{
$handler = $this->getHandler($name, $options);
return new Logger($name, [$handler]);
}
private function getHandler(string $name, array $options): HandlerInterface
{
return $this->handlers[$name] ?? ($this->handlers[$name] = $this->createHandler($options));
}
private function createHandler(array $options): HandlerInterface
{
if (!isset($options['type']) || !is_string($options['type'])) {
throw new \InvalidArgumentException('A required "type" option is missing.');
}
$handler = $this
->getHandlerFactory($options['type'])
->create($this->filterHandlerOptions($options));
if (is_array($options['channels'] ?? null)) {
foreach ($options['channels'] as $channel) {
$this->handlers[$channel] = $handler;
}
}
if (null === ($options['process_psr_3_messages']['enabled'] ?? null)) {
$options['process_psr_3_messages']['enabled'] = !isset($options['handler']) && empty($options['members']);
}
if ($options['process_psr_3_messages']['enabled']) {
$this->processPsrMessages($handler, $options['process_psr_3_messages']);
}
if ($options['include_stacktraces'] ?? false) {
$this->includeStacktraces($handler);
}
return $handler;
}
private function filterHandlerOptions(array $options): array
{
unset($options['type'], $options['channels'], $options['include_stacktraces'], $options['process_psr_3_messages']);
return $options;
}
private function getHandlerFactory(string $type): HandlerFactoryInterface
{
foreach ($this->factories as $factory) {
if ($factory->supports($type)) {
return $factory;
}
}
throw new \RuntimeException(sprintf('No log handler factory registered for type "%s".', $type));
}
private function includeStacktraces(HandlerInterface $handler): void
{
$formatter = $handler->getFormatter();
if ($formatter instanceof LineFormatter || $formatter instanceof JsonFormatter) {
$formatter->includeStacktraces();
}
}
private function processPsrMessages(HandlerInterface $handler, array $options): void
{
$removeContextFields = $options['remove_used_context_fields'] ?? false;
$processor = new PsrLogMessageProcessor($options['date_format'] ?? null, $removeContextFields);
if ($removeContextFields && self::shouldDecoratePsrProcessor()) {
$processor = new UsedContextFieldsRemovingProcessor($processor);
}
$handler->pushProcessor($processor);
}
private static function shouldDecoratePsrProcessor(): bool
{
if (isset(self::$decoratePsrProcessor)) {
return self::$decoratePsrProcessor;
}
$class = new \ReflectionClass(PsrLogMessageProcessor::class);
if (null === $constructor = $class->getConstructor()) {
return self::$decoratePsrProcessor = true;
}
return self::$decoratePsrProcessor = 2 > count($constructor->getParameters());
}
}
/**
* @internal
*/
final class UsedContextFieldsRemovingProcessor
{
/**
* @var callable
*/
private $processor;
public function __construct(callable $processor)
{
$this->processor = $processor;
}
public function __invoke(array $record): array
{
if (false === strpos($record['message'], '{')) {
return ($this->processor)($record);
}
$toRemove = [];
foreach ($record['context'] as $key => $val) {
$placeholder = '{' . $key . '}';
if (false === strpos($record['message'], $placeholder)) {
continue;
}
$toRemove[] = $key;
}
$record = ($this->processor)($record);
foreach ($toRemove as $key) {
unset($record['context'][$key]);
}
return $record;
}
}