Files
WP-LIT-MCP/src/Integrations/Divi/Design/DiviThemeOptionsGetAbility.php
T

174 lines
6.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\Divi\Design;
use LIT\MCP\Abilities\Ability;
use LIT\MCP\Abilities\Annotations;
use LIT\MCP\Abilities\Errors;
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
use LIT\MCP\Integrations\Divi\Release\RequiresDiviFeatures;
use WP_Error;
defined('ABSPATH') || exit;
/**
* lit/divi-theme-options-get (spec §8.1, rulings W2, W3): the 20 whitelisted Divi theme options – per key the stored
* value (null = unset), the value in effect (an unset tablet/phone size inherits the larger size), Divi's default,
* type, range or choices – and the design_rev of the area. Reading needs what lit/divi-design-system needs.
*/
final class DiviThemeOptionsGetAbility implements Ability, RequiresDiviFeatures
{
/** Fields of ThemeOptionSpec::definition() passed through as they are. */
private const RULES = ['min', 'max', 'step', 'choices', 'inherits'];
private const NOTES = [
'Ändern mit lit/divi-theme-options-update: set {Schlüssel: Wert}; null setzt auf den Divi-Standard zurück.',
'Ohne eigenen Wert erben Tablet- und Smartphone-Größen die nächstgrößere Größe (inherits).',
'Die fünf Hauptfarben ändert lit/divi-colors-set; Buttons, Logo und SEO bleiben im Divi-Customizer.',
];
public function __construct(private ThemeOptionsAdapter $options)
{
}
public function name(): string
{
return 'lit/divi-theme-options-get';
}
public function label(): string
{
return 'Divi-Theme-Optionen lesen';
}
public function description(): string
{
return 'Liest Divis Theme-Optionen für Schriften, Schriftgrößen und Layout: je Schlüssel den gespeicherten '
. 'Wert (null = nicht gesetzt), den wirksamen Wert, den Divi-Standard, Typ und Bereich oder Auswahl, dazu '
. 'die design_rev. keys grenzt auf einzelne Schlüssel ein. Ändern mit lit/divi-theme-options-update.';
}
public function inputSchema(): array
{
return [
'type' => 'object',
'properties' => [
'keys' => [
'type' => 'array',
'items' => ['type' => 'string', 'enum' => ThemeOptionSpec::KEYS],
'description' => 'Nur diese Theme-Optionen liefern (Standard: alle).',
],
],
'additionalProperties' => false,
];
}
public function outputSchema(): ?array
{
return [
'type' => 'object',
'properties' => [
'options' => [
'type' => 'array',
'items' => [
'type' => 'object',
'properties' => [
'key' => ['type' => 'string'],
'label' => ['type' => 'string'],
'type' => ['type' => 'string'],
'is_default' => ['type' => 'boolean'],
],
'required' => ['key', 'label', 'type', 'value', 'effective', 'default', 'is_default'],
],
],
'design_rev' => ['type' => 'string'],
'notes' => ['type' => 'array', 'items' => ['type' => 'string']],
],
'required' => ['options', 'design_rev', 'notes'],
];
}
public function annotations(): array
{
return Annotations::read();
}
public function isDirect(): bool
{
return false;
}
/** Reads et_divi and the Customizer table (ThemeOptionsAdapter, ThemeOptionSpec mirror). */
public function diviFeatures(array $input): array
{
return [DiviFeature::ThemeOptionRead];
}
public function permission(array $input): bool|WP_Error
{
return self::rights() ?? true;
}
public function execute(array $input): mixed
{
$refusal = self::rights();
if ($refusal !== null) {
return $refusal;
}
$keys = self::keys($input);
if ($keys instanceof WP_Error) {
return $keys;
}
$stored = ThemeOptionBook::fromDivi($this->options->read());
$book = $stored->withoutFalse();
$rows = [];
foreach ($keys as $key) {
$definition = ThemeOptionSpec::definition($key);
$rows[] = [
'key' => $key,
'label' => $definition['label'],
'type' => $definition['type'],
'value' => $book->get($key),
'effective' => ThemeOptionSpec::effective($key, $book->data()),
'default' => ThemeOptionSpec::effective($key, $book->without($key)->data()),
'is_default' => ! $book->has($key),
] + array_intersect_key($definition, array_flip(self::RULES));
}
return ['options' => $rows, 'design_rev' => $stored->rev(), 'notes' => self::NOTES];
}
/** Like lit/divi-design-system: whoever edits content may read the design. */
private static function rights(): ?WP_Error
{
return current_user_can('edit_posts') ? null : Errors::forbidden('Keine Berechtigung, Inhalte zu bearbeiten.');
}
/**
* @param array<string, mixed> $input
* @return list<string>|WP_Error The requested keys in ThemeOptionSpec::KEYS order (all without "keys").
*/
private static function keys(array $input): array|WP_Error
{
$keys = $input['keys'] ?? ThemeOptionSpec::KEYS;
if (! is_array($keys) || ! array_is_list($keys)) {
return Errors::invalidInput('keys muss eine Liste sein.', ['keys' => 'Liste von Theme-Optionen']);
}
$errors = [];
foreach ($keys as $index => $key) {
if (! is_string($key) || ! ThemeOptionSpec::isKey($key)) {
$errors[sprintf('keys[%d]', $index)] = 'unbekannte Theme-Option; erlaubt sind die Schlüssel aus '
. 'lit/divi-theme-options-get ohne keys';
}
}
if ($errors !== []) {
return Errors::invalidInput('Unbekannte Theme-Optionen.', $errors);
}
return array_values(array_intersect(ThemeOptionSpec::KEYS, $keys));
}
}