fix: Task 3 review round 1 - strip control chars before the sanitizer (S32a), S34 minors
Control characters now go on the raw value in AttrWriter::cleanNode and
VariableRef::setting; after the sanitizer only rejection remains ('<' + VT/FF
no longer turns into a tag). VALUE_KEY anchored with D, exact column-width
fractions, nested rows in columns, specialty columns refused, fullwidth by
catalog category, preset ambiguity in DesignSystem::modulePresetId().
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -10,15 +10,19 @@ use LIT\MCP\Integrations\Divi\Schema\ModuleCatalog;
|
||||
|
||||
/**
|
||||
* Which node may sit where (spec §5.4 "Struktur"): page → sections; regular section → rows;
|
||||
* fullwidth section → fullwidth modules; column/group → modules; parent module → its child
|
||||
* modules (catalog). Columns only come with their row; specialty sections and read-only
|
||||
* nodes (opaque, non-Divi) are not rebuilt. Pure PHP.
|
||||
* fullwidth section → fullwidth modules (catalog category "fullwidth-module"); column/group →
|
||||
* modules; column → a nested row too (Divi 5 row is "nestable": true; neither the server
|
||||
* – BlockParserStore::is_nested_module() only adds et_pb_row_nested – nor the builder's drop
|
||||
* rules limit the depth); parent module → its child modules (catalog). Columns only come with
|
||||
* their row; specialty sections, specialty columns (module.advanced.specialtyColumns or
|
||||
* row-inner children, ColumnModule.php:306-324) and read-only nodes (opaque, non-Divi) are not
|
||||
* rebuilt. Pure PHP.
|
||||
*/
|
||||
final class StructureRules
|
||||
{
|
||||
private const COLUMNS = ['divi/column', 'divi/column-inner'];
|
||||
private const ROWS = ['divi/row', 'divi/row-inner'];
|
||||
private const FULLWIDTH = 'divi/fullwidth-';
|
||||
private const FULLWIDTH = 'fullwidth-module';
|
||||
|
||||
public function __construct(private ModuleCatalog $catalog)
|
||||
{
|
||||
@@ -49,8 +53,15 @@ final class StructureRules
|
||||
. 'einfügen';
|
||||
}
|
||||
|
||||
if (in_array($parentName, self::COLUMNS, true) && self::isSpecialtyColumn($parent)) {
|
||||
return 'Spezialspalten (specialty) nur im Divi-Builder umbauen; Module in ihre inneren Spalten einfügen';
|
||||
}
|
||||
|
||||
if (in_array($parentName, self::COLUMNS, true) || $parentName === 'divi/group') {
|
||||
return $this->isPlainModule($child) || $child === 'divi/group'
|
||||
// A nested row: only straight into a (Divi 5) column – like the builder's drop target.
|
||||
$nestedRow = $child === 'divi/row' && $parentName === 'divi/column';
|
||||
|
||||
return $this->isPlainModule($child) || $child === 'divi/group' || $nestedRow
|
||||
? null
|
||||
: sprintf('%s passt nicht in eine Spalte', $child);
|
||||
}
|
||||
@@ -65,10 +76,27 @@ final class StructureRules
|
||||
: sprintf('%s erlaubt nur %s als Kind', $parentName, implode(', ', $children));
|
||||
}
|
||||
|
||||
/** A module that goes into a column: category "module", not a fullwidth module. */
|
||||
/** A module that goes into a column: catalog category "module" (not "fullwidth-module"). */
|
||||
public function isPlainModule(string $name): bool
|
||||
{
|
||||
return $this->catalog->category($name) === 'module' && ! str_starts_with($name, self::FULLWIDTH);
|
||||
return $this->catalog->category($name) === 'module';
|
||||
}
|
||||
|
||||
/** A column of a specialty section: it holds inner rows, not modules. */
|
||||
private static function isSpecialtyColumn(Block $column): bool
|
||||
{
|
||||
$specialty = AttrMap::desktop($column->attrs(), 'module.advanced.specialtyColumns');
|
||||
if ($specialty !== null && $specialty !== '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($column->children() as $child) {
|
||||
if ($child->name() === 'divi/row-inner') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function inSection(Block $section, string $child): ?string
|
||||
@@ -79,7 +107,7 @@ final class StructureRules
|
||||
}
|
||||
|
||||
if ($type === 'fullwidth') {
|
||||
return str_starts_with($child, self::FULLWIDTH)
|
||||
return $this->catalog->category($child) === self::FULLWIDTH
|
||||
? null
|
||||
: 'in einen Vollbreite-Abschnitt passen nur fullwidth-Module';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user