first commit

This commit is contained in:
2024-07-15 12:33:27 +02:00
commit ce50ae282b
22084 changed files with 2623791 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\Access;
{% apply sort_namespaces %}
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\Access\AccessInterface;
use Symfony\Component\Routing\Route;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* Checks if passed parameter matches the route configuration.
*
* Usage example:
* @code
* foo.example:
* path: '/example/{parameter}'
* defaults:
* _title: 'Example'
* _controller: '\Drupal\{{ machine_name }}\Controller\{{ machine_name|camelize }}Controller'
* requirements:
* {{ requirement }}: 'some value'
* @endcode
*/
final class {{ class }} implements AccessInterface {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* Access callback.
*
* @DCG
* Drupal does some magic when resolving arguments for this callback. Make
* sure the parameter name matches the name of the placeholder defined in the
* route, and it is of the same type.
* The following additional parameters are resolved automatically.
* - \Drupal\Core\Routing\RouteMatchInterface
* - \Drupal\Core\Session\AccountInterface
* - \Symfony\Component\HttpFoundation\Request
* - \Symfony\Component\Routing\Route
*/
public function access(Route $route, mixed $parameter): AccessResult {
return AccessResult::allowedIf($parameter === $route->getRequirement('{{ requirement }}'));
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
access_check.{{ machine_name }}.{{ requirement|trim('_') }}:
class: Drupal\{{ machine_name }}\Access\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: access_check, applies_to: {{ requirement }} }

View File

@@ -0,0 +1,54 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }};
{% apply sort_namespaces %}
use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* @todo Add description for this breadcrumb builder.
*/
final class {{ class }} implements BreadcrumbBuilderInterface {
use StringTranslationTrait;
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function applies(RouteMatchInterface $route_match): bool {
return $route_match->getRouteName() === 'example';
}
/**
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match): Breadcrumb {
$breadcrumb = new Breadcrumb();
$links[] = Link::createFromRoute($this->t('Home'), '<front>');
$links[] = Link::createFromRoute($this->t('Example'), '<none>');
return $breadcrumb->setLinks($links);
}
}

View File

@@ -0,0 +1,11 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.breadcrumb:
class: Drupal\{{ machine_name }}\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
# In order to override breadcrumbs built with PathBasedBreadcrumbBuilder
# set the priority higher than zero.
- { name: breadcrumb_builder, priority: 1000 }

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\Cache\Context;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\{{ interface }};
{% if base_class %}
use Drupal\Core\Cache\Context\{{ base_class }};
{% endif %}
/**
* @todo Add a description for the cache context.
*
* Cache context ID: '{{ context_id }}'.
*
* @DCG
* Check out the core/lib/Drupal/Core/Cache/Context directory for examples of
* cache contexts provided by Drupal core.
*/
final class {{ class }} {% if base_class %}extends {{ base_class }} {% endif %}implements {{ interface }} {
/**
* {@inheritdoc}
*/
public static function getLabel(): string {
return (string) t('{{ context_label }}');
}
/**
* {@inheritdoc}
*/
public function getContext({% if calculated %}$parameter = NULL{% endif %}): string {
// @todo Calculate the cache context here.
$context = 'some_string_value';
return $context;
}
/**
* {@inheritdoc}
*/
public function getCacheableMetadata({% if calculated %}$parameter = NULL{% endif %}): CacheableMetadata {
return new CacheableMetadata();
}
}

View File

@@ -0,0 +1,10 @@
services:
cache_context.{{ context_id }}:
class: Drupal\{{ machine_name }}\Cache\Context\{{ class }}
{% if base_class == 'RequestStackCacheContextBase' %}
arguments: ['@request_stack']
{% elseif base_class == 'UserCacheContextBase' %}
arguments: ['@current_user']
{% endif %}
tags:
- { name: cache.context}

View File

@@ -0,0 +1,32 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }};
{% if services %}
{{ di.use(services) }}
{% endif %}
/**
* @todo Add class description.
*/
final class {{ class }}{{ interface ? ' implements ' ~ interface }} {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {{ interface ? '{@inheritdoc}' : '@todo Add method description.'}}
*/
public function doSomething(): void {
// @todo Place your code here.
}
}

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }};
/**
* @todo Add interface description.
*/
interface {{ interface }} {
/**
* @todo Add method description.
*/
public function doSomething(): void;
}

View File

@@ -0,0 +1,7 @@
{% import '@lib/di.twig' as di %}
services:
{{ service_name }}:
class: Drupal\{{ machine_name }}\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}

View File

@@ -0,0 +1,62 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\EventSubscriber;
{% apply sort_namespaces %}
{% if services %}
{{ di.use(services) }}
{% endif %}
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
{% endapply %}
/**
* @todo Add description for this subscriber.
*/
final class {{ class }} implements EventSubscriberInterface {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* Kernel request event handler.
*/
public function onKernelRequest(RequestEvent $event): void {
// @todo Place your code here.
{% if SUT_TEST %}
$this->messenger->addStatus(__FUNCTION__);
{% endif %}
}
/**
* Kernel response event handler.
*/
public function onKernelResponse(ResponseEvent $event): void {
// @todo Place your code here.
{% if SUT_TEST %}
$this->messenger->addStatus(__FUNCTION__);
{% endif %}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
KernelEvents::REQUEST => ['onKernelRequest'],
KernelEvents::RESPONSE => ['onKernelResponse'],
];
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.event_subscriber:
class: Drupal\{{ machine_name }}\EventSubscriber\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: event_subscriber }

View File

@@ -0,0 +1,48 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\Logger;
{% apply sort_namespaces %}
use Drupal\Core\Logger\LogMessageParserInterface;
use Drupal\Core\Logger\RfcLoggerTrait;
use Psr\Log\LoggerInterface;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* @todo Add a description for the logger.
*/
final class {{ class }} implements LoggerInterface {
use RfcLoggerTrait;
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function log($level, string|\Stringable $message, array $context = []): void {
// Convert PSR3-style messages to \Drupal\Component\Render\FormattableMarkup
// style, so they can be translated too.
$placeholders = $this->parser->parseMessagePlaceholders($message, $context);
// @see \Drupal\Core\Logger\LoggerChannel::log() for all available contexts.
$rendered_message = strtr($message, $placeholders);
// @todo Log the rendered message here.
{% if SUT_TEST %}
\file_put_contents('temporary://logger_test.log', $level . ' -> ' . $rendered_message);
{% endif %}
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
logger.{{ service_id }}:
class: Drupal\{{ machine_name }}\Logger\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: logger }

View File

@@ -0,0 +1,44 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }};
{% apply sort_namespaces %}
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* @todo Add a description for the middleware.
*/
final class {{ class }} implements HttpKernelInterface {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MAIN_REQUEST, $catch = TRUE): Response {
// @todo Modify the request here.
$response = $this->httpKernel->handle($request, $type, $catch);
// @todo Modify the response here.
{% if SUT_TEST %}
$response->headers->set('x-middleware-handle-test', NULL);
{% endif %}
return $response;
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.middleware:
class: Drupal\{{ machine_name }}\{{ class }}
{% if service_arguments %}
arguments: [{{ di.arguments(service_arguments) }}]
{% endif %}
tags:
- { name: http_middleware, priority: 10 }

View File

@@ -0,0 +1,71 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }};
{% apply sort_namespaces %}
use Drupal\Core\ParamConverter\ParamConverterInterface;
use Symfony\Component\Routing\Route;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* @todo Add description for the converter.
*
* @DCG
* To use this converter specify parameter type in a relevant route as follows:
* @code
* {{ machine_name }}.{{ parameter_type }}_parameter_converter:
* path: example/{record}
* defaults:
* _controller: '\Drupal\{{ machine_name }}\Controller\{{ controller_class }}::build'
* requirements:
* _access: 'TRUE'
* options:
* parameters:
* record:
* type: {{ parameter_type }}
* @endcode
*
* Note that parameter converter for entities already exists in Drupal core.
* @see \Drupal\Core\ParamConverter\EntityConverter
*/
final class {{ class }} implements ParamConverterInterface {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function convert($value, $definition, $name, array $defaults): ?string {
// @DCG
// If the converter returns something different rather than strings make
// sure to define an appropriate return type for this method.
return match ($value) {
'1' => 'alpha',
'2' => 'beta',
'3' => 'gamma',
// NULL will trigger 404 HTTP error.
default => NULL,
};
}
/**
* {@inheritdoc}
*/
public function applies($definition, $name, Route $route): bool {
return isset($definition['type']) && $definition['type'] === 'example';
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.{{ parameter_type }}_param_converter:
class: Drupal\{{ machine_name }}\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: paramconverter }

View File

@@ -0,0 +1,49 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\PathProcessor;
{% apply sort_namespaces %}
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Symfony\Component\HttpFoundation\Request;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* Path processor to replace 'node' with 'content' in URLs.
*
* @DCG In case you need to implement only one processor (inbound or outbound)
* remove the corresponding interface, method and service tag.
*/
final class {{ class }} implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function processInbound($path, Request $request): string {
return preg_replace('#^/content/#i', '/node/', $path);
}
/**
* {@inheritdoc}
*/
public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL): string {
return preg_replace('#^/node/#i', '/content/', $path);
}
}

View File

@@ -0,0 +1,10 @@
{% import '@lib/di.twig' as di %}
services:
path_processor.{{ machine_name }}:
class: Drupal\{{ machine_name }}\PathProcessor\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: path_processor_inbound }
- { name: path_processor_outbound }

View File

@@ -0,0 +1,41 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\PageCache;
{% apply sort_namespaces %}
use Drupal\Core\PageCache\RequestPolicyInterface;
use Symfony\Component\HttpFoundation\Request;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* @todo Add policy description here.
*/
final class {{ class }} implements RequestPolicyInterface {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function check(Request $request): ?string {
// @DCG
// Return self::ALLOW or self::DENY to indicate whether delivery of a cached
// page should be attempted for this request. Return NULL if the policy does
// not apply to the given request.
return NULL;
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.page_cache_request_policy.{{ class|c2m }}:
class: Drupal\{{ machine_name }}\PageCache\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: page_cache_request_policy }

View File

@@ -0,0 +1,41 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\PageCache;
{% apply sort_namespaces %}
use Drupal\Core\PageCache\ResponsePolicyInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* @todo Add policy description here.
*/
final class {{ class }} implements ResponsePolicyInterface {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function check(Response $response, Request $request): ?string {
// @DCG
// Return self::DENY to indicate that the response should not be stored in
// the cache. Return NULL if the policy does not apply to the given request.
return NULL;
}
}

View File

@@ -0,0 +1,11 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.page_cache_response_policy.{{ class|c2m }}:
class: Drupal\{{ machine_name }}\PageCache\{{ class }}
public: false
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: page_cache_response_policy }
- { name: dynamic_page_cache_response_policy }

View File

@@ -0,0 +1,37 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\EventSubscriber;
{% apply sort_namespaces %}
{% if services %}
{{ di.use(services) }}
{% endif %}
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
{% endapply %}
/**
* Route subscriber.
*/
final class {{ class }} extends RouteSubscriberBase {
{% if services %}
/**
* Constructs {{ class|article }} object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection): void {
// @see https://www.drupal.org/node/2187643
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.route_subscriber:
class: Drupal\{{ machine_name }}\EventSubscriber\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: event_subscriber }

View File

@@ -0,0 +1,6 @@
services:
theme.negotiator.{{ machine_name }}.example:
class: Drupal\{{ machine_name }}\Theme\{{ class }}
arguments: ['@request_stack']
tags:
- { name: theme_negotiator, priority: 1000 }

View File

@@ -0,0 +1,45 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }}\Theme;
{% apply sort_namespaces %}
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Theme\ThemeNegotiatorInterface;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* Defines a theme negotiator that deals with the active theme on example page.
*/
final class {{ class }} implements ThemeNegotiatorInterface {
{% if services %}
/**
* Constructs the negotiator object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function applies(RouteMatchInterface $route_match): bool {
return $route_match->getRouteName() === '{{ machine_name }}.example';
}
/**
* {@inheritdoc}
*/
public function determineActiveTheme(RouteMatchInterface $route_match): ?string {
// @DCG Here you can determine the active theme for the request.
return 'claro';
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.twig_extension:
class: Drupal\{{ machine_name }}\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: twig.extension }

View File

@@ -0,0 +1,71 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }};
{% apply sort_namespaces %}
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* Twig extension.
*/
final class {{ class }} extends AbstractExtension {
{% if services %}
/**
* Constructs the extension object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function getFunctions(): array {
$functions[] = new TwigFunction(
'example',
static function (string $argument): string {
return 'Example: ' . $argument;
},
);
return $functions;
}
/**
* {@inheritdoc}
*/
public function getFilters(): array {
$filters[] = new TwigFilter(
'example',
static function (string $text): string {
return str_replace('example', 'EXAMPLE', $text);
},
);
return $filters;
}
/**
* {@inheritdoc}
*/
public function getTests(): array {
$tests[] = new TwigTest(
'example',
static function (string $text): bool {
return $text === 'example';
},
);
return $tests;
}
}

View File

@@ -0,0 +1,9 @@
{% import '@lib/di.twig' as di %}
services:
{{ machine_name }}.uninstall_validator:
class: Drupal\{{ machine_name }}\{{ class }}
{% if services %}
arguments: [{{ di.arguments(services) }}]
{% endif %}
tags:
- { name: module_install.uninstall_validator }

View File

@@ -0,0 +1,43 @@
{% import '@lib/di.twig' as di %}
<?php
declare(strict_types=1);
namespace Drupal\{{ machine_name }};
{% apply sort_namespaces %}
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
{% if services %}
{{ di.use(services) }}
{% endif %}
{% endapply %}
/**
* @todo Add validator description here.
*/
final class {{ class }} implements ModuleUninstallValidatorInterface {
use StringTranslationTrait;
{% if services %}
/**
* Constructs the object.
*/
public function __construct(
{{ di.signature(services) }}
) {}
{% endif %}
/**
* {@inheritdoc}
*/
public function validate($module): array {
$reasons = [];
if ($module === '{{ machine_name }}') {
$reasons[] = $this->t('Some good reason.');
}
return $reasons;
}
}