feat: lit/divi-insert and lit/divi-update
All-or-nothing at ability level (S39), duplicate ids refused (S14), set and attrs on the same path refused (S38), invalid_attr_path error. 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:
@@ -116,6 +116,7 @@ final class Composer
|
||||
}
|
||||
}
|
||||
|
||||
$this->overlaps($block->shortName(), $change, $field);
|
||||
$attrs = $this->presetAndAttrs($block->name(), $attrs, $change, $field);
|
||||
if (array_key_exists('label', $change)) {
|
||||
$attrs = $change['label'] === null || $change['label'] === ''
|
||||
@@ -126,6 +127,39 @@ final class Composer
|
||||
return ['attrs' => $attrs, 'errors' => $this->errors, 'invalid_paths' => $this->invalidPaths];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruling S38: one change may not reach the same attribute path through "set" and "attrs" –
|
||||
* composite values are replaced whole per breakpoint, so combining them would silently lose
|
||||
* sub-keys. Paths overlap when equal or when one lies below the other.
|
||||
*
|
||||
* @param array<array-key, mixed> $change
|
||||
*/
|
||||
private function overlaps(string $type, array $change, string $field): void
|
||||
{
|
||||
$set = $change['set'] ?? null;
|
||||
$full = $change['attrs'] ?? null;
|
||||
if (! is_array($set) || ! is_array($full) || $set === [] || $full === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$short = $this->shortform->paths($type, array_map('strval', array_keys($set)));
|
||||
foreach (array_keys($full) as $path) {
|
||||
$path = (string) $path;
|
||||
foreach ($short as $name => $shortPath) {
|
||||
if (
|
||||
$path === $shortPath || str_starts_with($path, $shortPath . '.')
|
||||
|| str_starts_with($shortPath, $path . '.')
|
||||
) {
|
||||
$this->errors[$field . '.attrs.' . $path] = sprintf(
|
||||
'derselbe Attributpfad wird auch über set.%s geändert; bitte als zwei Änderungen senden',
|
||||
$name
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Admin label for a new module: "<Abschnitt> – <Modultitel>" or the title alone. */
|
||||
public function autoLabel(string $name, string $context): string
|
||||
{
|
||||
|
||||
@@ -72,6 +72,25 @@ final class ShortformWriter
|
||||
return ['attrs' => $attrs, 'errors' => $errors];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute paths the given short-form fields of $type write to (unknown fields left out).
|
||||
*
|
||||
* @param list<string> $names Field names.
|
||||
* @return array<string, string> Field => attribute path.
|
||||
*/
|
||||
public function paths(string $type, array $names): array
|
||||
{
|
||||
$fields = $this->modules === null ? ShortformMap::fields($type) : ($this->modules[$type] ?? []);
|
||||
$paths = [];
|
||||
foreach ($names as $name) {
|
||||
if (isset($fields[$name]['path'])) {
|
||||
$paths[$name] = (string) $fields[$name]['path'];
|
||||
}
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* The short-form color check (ruling S13; Task 3 reuses it for background_color): a
|
||||
* placeholder or reference that is a color reference, or a plain CSS color – checked
|
||||
|
||||
Reference in New Issue
Block a user