feat: lit/divi-variables-set prüft Schrift-Variablen gegen Divis Schriftliste (mit Vorschlägen)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
dab4a9ba2e
commit
449ec5c33f
@@ -22,8 +22,9 @@ defined('ABSPATH') || exit;
|
||||
* pipeline (VariableArea). An item names an existing variable in id (ID or name) or creates one from type, label and
|
||||
* value. Variables are never deleted – Divi refuses an inventory that lacks a stored ID (V1) –, only archived (order
|
||||
* "") and activated again (appended at the end of their type); the type of a variable stays, gradients are left to the
|
||||
* builder. Values are checked per type (VariableValue); an active variable needs one. The dry run lists the posts that
|
||||
* reference a changed, archived or activated variable (ReferenceUsageQuery) and warns about presets that use it.
|
||||
* builder. Values are checked per type (VariableValue), font variables also against Divi's font list (FontCheck, ruling
|
||||
* W12); an active variable needs one. The dry run lists the posts that reference a changed, archived or activated
|
||||
* variable (ReferenceUsageQuery) and warns about presets that use it.
|
||||
*/
|
||||
final class DiviVariablesSetAbility implements Ability, RequiresDiviFeatures
|
||||
{
|
||||
@@ -52,6 +53,7 @@ final class DiviVariablesSetAbility implements Ability, RequiresDiviFeatures
|
||||
private GlobalVariablesAdapter $variables,
|
||||
private DesignChange $change,
|
||||
private GlobalUsages $usages,
|
||||
private FontCatalog $fonts,
|
||||
private ?Closure $random = null
|
||||
) {
|
||||
}
|
||||
@@ -229,7 +231,7 @@ final class DiviVariablesSetAbility implements Ability, RequiresDiviFeatures
|
||||
try {
|
||||
$entry[$key] = match ($key) {
|
||||
'label' => self::cleanLabel($book, $item['label'], $id),
|
||||
'value' => self::value($type, $item['value']),
|
||||
'value' => $this->value($type, $item['value']),
|
||||
default => self::status($item['status']),
|
||||
};
|
||||
} catch (InvalidArgumentException $e) {
|
||||
@@ -349,7 +351,7 @@ final class DiviVariablesSetAbility implements Ability, RequiresDiviFeatures
|
||||
}
|
||||
|
||||
/** @throws InvalidArgumentException */
|
||||
private static function value(string $type, mixed $value): string
|
||||
private function value(string $type, mixed $value): string
|
||||
{
|
||||
if (is_int($value)) {
|
||||
$value = (string) $value; // an attachment ID for images
|
||||
@@ -358,6 +360,9 @@ final class DiviVariablesSetAbility implements Ability, RequiresDiviFeatures
|
||||
throw new InvalidArgumentException('Wert als Text erwartet');
|
||||
}
|
||||
$problem = VariableValue::check($type, $value);
|
||||
if ($problem === null && $type === 'fonts') {
|
||||
$problem = FontCheck::problem($this->fonts, $value);
|
||||
}
|
||||
if ($problem !== null) {
|
||||
throw new InvalidArgumentException($problem);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,9 @@ defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Checks the value of a global variable before it goes to Divi (ruling V6, D7 principle). The type "gradients"
|
||||
* (new in Divi 5.15) is passed through by VariableBook, but the tools never create or change it. The check of fonts
|
||||
* against Divi's font list follows with 4d. Pure PHP.
|
||||
* (new in Divi 5.15) is passed through by VariableBook, but the tools never create or change it. Font names are only
|
||||
* checked for their form here; DiviVariablesSetAbility checks them against Divi's font list (FontCheck, ruling W12).
|
||||
* Pure PHP.
|
||||
*/
|
||||
final class VariableValue
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ use LIT\MCP\Integrations\Divi\Design\DiviPresetsAssignAbility;
|
||||
use LIT\MCP\Integrations\Divi\Design\DiviPresetsDeleteAbility;
|
||||
use LIT\MCP\Integrations\Divi\Design\DiviPresetsSetAbility;
|
||||
use LIT\MCP\Integrations\Divi\Design\DiviVariablesSetAbility;
|
||||
use LIT\MCP\Integrations\Divi\Design\FontCatalog;
|
||||
use LIT\MCP\Integrations\Divi\Design\PageRewriter;
|
||||
use LIT\MCP\Integrations\Divi\Design\PresetAssignment;
|
||||
use LIT\MCP\Integrations\Divi\Design\PresetComposer;
|
||||
@@ -304,7 +305,8 @@ final class DiviIntegration implements Integration, Bootable
|
||||
$this->designWriter,
|
||||
$this->variablesAdapter,
|
||||
$this->designChange(),
|
||||
$this->globalUsages()
|
||||
$this->globalUsages(),
|
||||
new FontCatalog()
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use LIT\MCP\Abilities\WriteFailed;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignChange;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter;
|
||||
use LIT\MCP\Integrations\Divi\Design\DiviVariablesSetAbility;
|
||||
use LIT\MCP\Integrations\Divi\Design\FontCatalog;
|
||||
use LIT\MCP\Integrations\Divi\Design\GlobalVariablesAdapter;
|
||||
use LIT\MCP\Integrations\Divi\Design\ReferenceUsageQuery;
|
||||
use LIT\MCP\Integrations\Divi\Design\SnapshotStore;
|
||||
@@ -48,7 +49,8 @@ final class DiviVariablesSetAbilityTest extends TestCase
|
||||
?SnapshotStore $snapshots = null,
|
||||
array $presets = [],
|
||||
?GlobalVariablesAdapter $variables = null,
|
||||
bool $manage = true
|
||||
bool $manage = true,
|
||||
?FontCatalog $fonts = null
|
||||
): DiviVariablesSetAbility {
|
||||
$writer = Mockery::mock(DesignSystemWriter::class);
|
||||
$writer->allows('canUseVisualBuilder')->andReturn(true);
|
||||
@@ -60,6 +62,7 @@ final class DiviVariablesSetAbilityTest extends TestCase
|
||||
$variables ?? VariableAreaTest::adapter($this->stored, $this->saves, $manage),
|
||||
new DesignChange($snapshots ?? Mockery::mock(SnapshotStore::class), new FakeChangeLog(3)),
|
||||
$usages ?? self::usages(self::unused()),
|
||||
$fonts ?? FontCheckTest::catalog(),
|
||||
static function (int $length) use (&$counter): string {
|
||||
return str_pad((string) ++$counter, $length, '0', STR_PAD_LEFT);
|
||||
}
|
||||
@@ -377,4 +380,56 @@ final class DiviVariablesSetAbilityTest extends TestCase
|
||||
|
||||
self::assertSame([], $dry['warnings']);
|
||||
}
|
||||
|
||||
/** Ruling W12: a font variable names a font Divi knows exactly; unknown names get suggestions. */
|
||||
public function testFontVariablesNeedAFontDiviKnows(): void
|
||||
{
|
||||
$error = $this->ability()
|
||||
->execute(['items' => [['type' => 'fonts', 'label' => 'Marke', 'value' => 'robotto']]]);
|
||||
|
||||
self::assertToolError($error, 'validation_failed');
|
||||
$fields = self::errorFields($error);
|
||||
self::assertSame(['items[0].value'], array_keys($fields));
|
||||
self::assertStringStartsWith('unbekannte Schrift „robotto“; ähnlich: ', $fields['items[0].value']);
|
||||
self::assertStringContainsString('Roboto', $fields['items[0].value']);
|
||||
self::assertSame([], $this->saves);
|
||||
}
|
||||
|
||||
public function testAFontVariableWithAKnownFontIsPlanned(): void
|
||||
{
|
||||
$dry = self::assertToolError(
|
||||
$this->ability()->execute(['items' => [['type' => 'fonts', 'label' => 'Marke', 'value' => 'Roboto Slab']]]),
|
||||
'confirm_required'
|
||||
);
|
||||
|
||||
self::assertSame(['added'], array_column($dry['diff'], 'action'));
|
||||
}
|
||||
|
||||
/** The font list is loaded only for font variables (it costs 1.3 MB, ruling W5). */
|
||||
public function testOtherTypesDoNotLoadTheFontList(): void
|
||||
{
|
||||
$fonts = Mockery::mock(FontCatalog::class);
|
||||
$fonts->shouldNotReceive('has', 'names');
|
||||
|
||||
self::assertToolError(
|
||||
$this->ability(fonts: $fonts)->execute(['items' => [
|
||||
['type' => 'strings', 'label' => 'Gruß', 'value' => 'Hallo'],
|
||||
['id' => 'Eckenradius', 'value' => '12px'],
|
||||
]]),
|
||||
'confirm_required'
|
||||
);
|
||||
}
|
||||
|
||||
/** VariableValue checks first: a malformed name never reaches the font list. */
|
||||
public function testAMalformedFontNameFailsBeforeTheFontList(): void
|
||||
{
|
||||
$fonts = Mockery::mock(FontCatalog::class);
|
||||
$fonts->shouldNotReceive('has', 'names');
|
||||
|
||||
$error = $this->ability(fonts: $fonts)
|
||||
->execute(['items' => [['type' => 'fonts', 'label' => 'Marke', 'value' => 'Roboto; x']]]);
|
||||
|
||||
self::assertToolError($error, 'validation_failed');
|
||||
self::assertStringStartsWith('Schrift:', self::errorFields($error)['items[0].value']);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user