Skip to content
108 changes: 108 additions & 0 deletions src/bundle/DependencyInjection/Compiler/RichTextDocbookSchemaPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\FieldTypeRichText\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class RichTextDocbookSchemaPass implements CompilerPassInterface
{
public const string SCHEMA_FRAGMENTS_PARAM = 'ibexa.field_type.richtext.docbook.schema_fragments';
private const string VALIDATOR_RESOURCES_PARAM = 'ibexa.field_type.richtext.validator.docbook.resources';
private const string BASE_SCHEMA = 'ezpublish.rng';

public function process(ContainerBuilder $container): void
{
/** @var array<string> $fragments */
$fragments = $container->hasParameter(self::SCHEMA_FRAGMENTS_PARAM)
? (array)$container->getParameter(self::SCHEMA_FRAGMENTS_PARAM)
: [];

if (empty($fragments)) {
return;
}

/** @var string $cacheDir */
$cacheDir = $container->getParameter('kernel.cache_dir');
$combinedSchemaPath = $cacheDir . '/richtext/docbook_combined.rng';

// Generate combined schema
$this->generateCombinedSchema($container, $fragments, $combinedSchemaPath);

// Replace base schema with combined in validator resources
/** @var array<string> $resources */
$resources = $container->getParameter(self::VALIDATOR_RESOURCES_PARAM);
$resources = array_map(
static fn (string $path): string => str_contains($path, self::BASE_SCHEMA) ? $combinedSchemaPath : $path,
$resources
);
$container->setParameter(self::VALIDATOR_RESOURCES_PARAM, $resources);
}

/**
* @param array<string> $fragments
*/
private function generateCombinedSchema(
ContainerBuilder $container,
array $fragments,
string $outputPath
): void {
/** @var string $projectDir */
$projectDir = $container->getParameter('kernel.project_dir');
$baseSchemaPath = $projectDir . '/vendor/ibexa/fieldtype-richtext/src/bundle/Resources/richtext/schemas/docbook/ezpublish.rng';

$includes = [
'<include href="' . $baseSchemaPath . '">',
' <!-- Override extension points to make them combinable if they are not already -->',
' <define name="ez.extension.blocks" combine="choice">',
' <notAllowed/>',
' </define>',
' <define name="ez.extension.inlines" combine="choice">',
' <notAllowed/>',
' </define>',
'</include>',
];
foreach ($fragments as $fragment) {
$resolvedPath = str_replace('%kernel.project_dir%', $projectDir, $fragment);
$includes[] = '<include href="' . $resolvedPath . '"/>';
}

$schema = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:db="http://docbook.org/ns/docbook"
xmlns:ez="http://ibexa.co/xmlns/ezpublish/docbook"
xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml"
xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:a="http://ibexa.co/xmlns/annotation"
xmlns:m="http://ibexa.co/xmlns/module"
ns="http://docbook.org/ns/docbook"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">

{$this->formatIncludes($includes)}

</grammar>
XML;

Comment on lines 79 to 96
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Increase readability by indenting the XML. (Heredoc allows this since some PHP 7 version)

Suggested change
$schema = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:db="http://docbook.org/ns/docbook"
xmlns:ez="http://ibexa.co/xmlns/ezpublish/docbook"
xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml"
xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:a="http://ibexa.co/xmlns/annotation"
xmlns:m="http://ibexa.co/xmlns/module"
ns="http://docbook.org/ns/docbook"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
{$this->formatIncludes($includes)}
</grammar>
XML;
$schema = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:db="http://docbook.org/ns/docbook"
xmlns:ez="http://ibexa.co/xmlns/ezpublish/docbook"
xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml"
xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:a="http://ibexa.co/xmlns/annotation"
xmlns:m="http://ibexa.co/xmlns/module"
ns="http://docbook.org/ns/docbook"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
{$this->formatIncludes($includes)}
</grammar>
XML;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, it just throws XML declaration should precede all document contents inside IDE, and I didnt like the red underline ;)

$dir = dirname($outputPath);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents($outputPath, $schema);
}

/**
* @param array<string> $includes
*/
private function formatIncludes(array $includes): string
{
return implode("\n ", $includes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public function addFieldTypeSemanticConfig(NodeBuilder $nodeBuilder): void
{
$nodeBuilder
->arrayNode('embed')
->info('RichText embed tags configuration.')
->info('RichText embed tags configuration. Custom embed types can be added by other bundles.')
->ignoreExtraKeys(false)
->children()
->arrayNode('content')
->info('Configuration for RichText block-level Content embed tags.')
Expand Down Expand Up @@ -168,6 +169,34 @@ public function addFieldTypeSemanticConfig(NodeBuilder $nodeBuilder): void
->end()
->end()
->end()
->validate()
->always(static function (array $embeds): array {
$coreTypes = [
'content', 'content_denied', 'content_inline', 'content_inline_denied',
'location', 'location_denied', 'location_inline', 'location_inline_denied',
];

foreach ($embeds as $type => $config) {
if (in_array($type, $coreTypes, true)) {
continue;
}

if (!is_array($config)) {
throw new InvalidArgumentException(
sprintf("Custom embed type '%s' configuration must be an array.", $type)
);
}

if (!isset($config['template']) || empty($config['template'])) {
throw new InvalidArgumentException(
sprintf("Custom embed type '%s' must have a non-empty 'template' key.", $type)
);
}
}

return $embeds;
})
->end()
->end();

// RichText Custom Tags configuration (list of Custom Tags enabled for current SiteAccess scope)
Expand Down
3 changes: 3 additions & 0 deletions src/bundle/IbexaFieldTypeRichTextBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
namespace Ibexa\Bundle\FieldTypeRichText;

use Ibexa\Bundle\Core\DependencyInjection\IbexaCoreExtension;
use Ibexa\Bundle\FieldTypeRichText\DependencyInjection\Compiler\RichTextDocbookSchemaPass;
use Ibexa\Bundle\FieldTypeRichText\DependencyInjection\Compiler\RichTextHtml5ConverterPass;
use Ibexa\Bundle\FieldTypeRichText\DependencyInjection\Configuration\Parser\FieldType\RichText;
use Ibexa\Bundle\FieldTypeRichText\DependencyInjection\IbexaFieldTypeRichTextExtension;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
Expand All @@ -30,6 +32,7 @@ public function build(ContainerBuilder $container): void
$core->addDefaultSettings(__DIR__ . '/Resources/config', ['default_settings.yaml']);

$container->addCompilerPass(new RichTextHtml5ConverterPass());
$container->addCompilerPass(new RichTextDocbookSchemaPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -100);
$this->registerConfigParser($container);
}

Expand Down
4 changes: 4 additions & 0 deletions src/bundle/Resources/config/fieldtype_services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ parameters:
ibexa.field_type.richtext.validator.docbook.resources:
- '%ibexa.field_type.richtext.resources%/schemas/docbook/ezpublish.rng'
- '%ibexa.field_type.richtext.resources%/schemas/docbook/docbook.iso.sch.xsl'

# Schema fragments to include (paths to RNG files)
ibexa.field_type.richtext.docbook.schema_fragments: []

ibexa.field_type.richtext.validator.output.xhtml5.resources:
- '%ibexa.field_type.richtext.resources%/schemas/ezxhtml5/output/ezxhtml5.xsd'

Expand Down
Loading