refactor: Divi library adapters with manifest and submit-layout mirror
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,10 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Integrations\Divi\Library;
|
||||
|
||||
use LIT\MCP\Abilities\WriteFailed;
|
||||
use LIT\MCP\Integrations\Divi\Release\Call;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviAdapter;
|
||||
use LIT\MCP\Integrations\Divi\Release\Manifest;
|
||||
use LIT\MCP\Integrations\Divi\Release\Mirror;
|
||||
use LIT\MCP\Integrations\WordPress\Content\NewDraft;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
@@ -15,15 +19,15 @@ use WP_Term;
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Adapter for the Divi Library (E4, E15): post type et_pb_layout, the taxonomies scope,
|
||||
* layout_type, module_width, layout_category, layout_tag and the meta Divi's et_pb_submit_layout()
|
||||
* writes (F1, F2, fixtures stage3). "Global" is Divi's own definition
|
||||
* Adapter "Bibliothek" (spec §3) for the Divi Library (E4, E15): post type et_pb_layout, the taxonomies scope,
|
||||
* layout_type, module_width, layout_category, layout_tag and the terms and meta Divi's et_pb_submit_layout() writes
|
||||
* (mirror library-submit-layout: metaFor()) (F1, F2, fixtures stage3). "Global" is Divi's own definition
|
||||
* GlobalLayout::is_global_layout_template() (F5) with the scope term as fallback when the class is
|
||||
* gone. find() filters by the scope term only, so an entry Divi counts as global through its D4
|
||||
* meta fallback can show up under scope "normal" (preflight M-12, accepted). Library entries are
|
||||
* always "publish" (F2). Not final so abilities can be tested with a mock.
|
||||
*/
|
||||
class DiviLibraryStore
|
||||
class DiviLibraryStore implements DiviAdapter
|
||||
{
|
||||
public const POST_TYPE = 'et_pb_layout';
|
||||
|
||||
@@ -34,9 +38,67 @@ class DiviLibraryStore
|
||||
/** Taxonomies every new entry needs (et_pb_submit_layout). */
|
||||
public const TAXONOMIES = ['scope', 'layout_type', 'module_width'];
|
||||
|
||||
public const MODULE_TYPE = '_et_pb_module_type';
|
||||
|
||||
public const EXCLUDED_OPTIONS = '_et_pb_excluded_global_options';
|
||||
|
||||
public const BUILT_FOR = '_et_pb_built_for_post_type';
|
||||
|
||||
public const TEMPLATE_TYPE = '_et_pb_template_type';
|
||||
|
||||
/** Client text when the terms or meta of a new entry could not be written. */
|
||||
public const REASON_CREATE = 'Der Bibliothekseintrag konnte nicht vollständig angelegt werden.';
|
||||
|
||||
public static function manifest(): Manifest
|
||||
{
|
||||
return new Manifest(
|
||||
Manifest::LIBRARY,
|
||||
calls: [new Call(self::GLOBAL_LAYOUT . '::is_global_layout_template', 1)],
|
||||
postTypes: [self::POST_TYPE],
|
||||
taxonomies: [...self::TAXONOMIES, 'layout_category', 'layout_tag'],
|
||||
keys: [self::MODULE_TYPE, self::EXCLUDED_OPTIONS, self::BUILT_FOR, self::TEMPLATE_TYPE],
|
||||
mirrors: [
|
||||
new Mirror(
|
||||
'library-submit-layout',
|
||||
'includes/builder/shortcode-core.php',
|
||||
'et_pb_submit_layout',
|
||||
null,
|
||||
'Terme und Meta neuer Bibliothekseinträge (scope, layout_type, module_width, _et_pb_module_type, '
|
||||
. '_et_pb_excluded_global_options, _et_pb_built_for_post_type, _et_pb_template_type)'
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The meta of a new entry as et_pb_submit_layout() writes it (compared with the real builder entries, fixtures
|
||||
* stage3): _et_pb_module_type (modules), _et_pb_excluded_global_options = "[]" (global modules),
|
||||
* _et_pb_built_for_post_type, _et_pb_template_type. _et_pb_row_layout is left out – Divi 5.2.1 stores
|
||||
* "[object Object]" there (finding 5).
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function metaFor(LibraryItemSpec $spec): array
|
||||
{
|
||||
$meta = [];
|
||||
if ($spec->moduleType !== null) {
|
||||
$meta[self::MODULE_TYPE] = $spec->moduleType;
|
||||
}
|
||||
if ($spec->global && $spec->layoutType === 'module') {
|
||||
$meta[self::EXCLUDED_OPTIONS] = '[]';
|
||||
}
|
||||
$meta[self::BUILT_FOR] = $spec->builtFor;
|
||||
$meta[self::TEMPLATE_TYPE] = $spec->layoutType;
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/** _et_pb_built_for_post_type of entry $id ('' when unset): the type an entry was built for. */
|
||||
public static function builtFor(int $id): string
|
||||
{
|
||||
return (string) get_post_meta($id, self::BUILT_FOR, true);
|
||||
}
|
||||
|
||||
public function isGlobal(int $id): bool
|
||||
{
|
||||
if (method_exists(self::GLOBAL_LAYOUT, 'is_global_layout_template')) {
|
||||
@@ -127,7 +189,7 @@ class DiviLibraryStore
|
||||
public function describe(WP_Post $post): array
|
||||
{
|
||||
$id = (int) $post->ID;
|
||||
$moduleType = (string) get_post_meta($id, '_et_pb_module_type', true);
|
||||
$moduleType = (string) get_post_meta($id, self::MODULE_TYPE, true);
|
||||
|
||||
return [
|
||||
'id' => $id,
|
||||
@@ -209,7 +271,7 @@ class DiviLibraryStore
|
||||
throw new RuntimeException(sprintf('terms %s: %s', $taxonomy, $result->get_error_message()));
|
||||
}
|
||||
}
|
||||
foreach ($spec->meta() as $key => $value) {
|
||||
foreach (self::metaFor($spec) as $key => $value) {
|
||||
update_post_meta($id, $key, $value);
|
||||
if ((string) get_post_meta($id, $key, true) !== $value) {
|
||||
throw new RuntimeException(sprintf('meta %s of library entry %d not stored', $key, $id));
|
||||
|
||||
@@ -6,18 +6,21 @@ namespace LIT\MCP\Integrations\Divi\Library;
|
||||
|
||||
use Closure;
|
||||
use LIT\MCP\Abilities\Errors;
|
||||
use LIT\MCP\Integrations\Divi\Release\Call;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviAdapter;
|
||||
use LIT\MCP\Integrations\Divi\Release\Manifest;
|
||||
use WP_Error;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Adapter for Divi's role editor (E3, E15): et_pb_is_allowed() (Divi 5.2.1 builder/core.php:1128)
|
||||
* Adapter "Bibliothek" (spec §3) for Divi's role editor (E3, E15): et_pb_is_allowed() (Divi's builder/core.php)
|
||||
* answers for the current user's role, "dangerous" options (theme_builder,
|
||||
* read_dynamic_content_custom_fields) default to off for every role but administrator. When
|
||||
* Divi no longer has the function, nothing is allowed – DiviCompatibility reports it.
|
||||
* Not final so tests can pass a check.
|
||||
*/
|
||||
class DiviRoles
|
||||
class DiviRoles implements DiviAdapter
|
||||
{
|
||||
private const LABELS = [
|
||||
'divi_library' => 'Divi-Bibliothek',
|
||||
@@ -28,6 +31,11 @@ class DiviRoles
|
||||
'theme_builder' => 'Theme Builder',
|
||||
];
|
||||
|
||||
public static function manifest(): Manifest
|
||||
{
|
||||
return new Manifest(Manifest::LIBRARY, calls: [new Call('et_pb_is_allowed', 1)]);
|
||||
}
|
||||
|
||||
/** @param (Closure(string): bool)|null $check Test seam; null = Divi's et_pb_is_allowed(). */
|
||||
public function __construct(private ?Closure $check = null)
|
||||
{
|
||||
|
||||
@@ -13,12 +13,9 @@ use LIT\MCP\Integrations\Divi\Schema\ModuleCatalog;
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Title, terms and meta of a new library entry, like Divi's et_pb_submit_layout() writes them
|
||||
* (builder/shortcode-core.php:654-734; compared with the real builder entries, fixtures stage3):
|
||||
* terms scope (global / non_global), layout_type, module_width, optional layout_category/layout_tag;
|
||||
* meta _et_pb_module_type (modules), _et_pb_excluded_global_options = "[]" (global modules),
|
||||
* _et_pb_built_for_post_type, _et_pb_template_type. _et_pb_row_layout is left out – Divi 5.2.1 stores
|
||||
* "[object Object]" there (finding 5). Pure PHP.
|
||||
* Title and terms of a new library entry, like Divi's et_pb_submit_layout() writes them (compared with the real
|
||||
* builder entries, fixtures stage3): terms scope (global / non_global), layout_type, module_width, optional
|
||||
* layout_category/layout_tag. The meta follows from DiviLibraryStore::metaFor(). Pure PHP.
|
||||
*/
|
||||
final class LibraryItemSpec
|
||||
{
|
||||
@@ -129,20 +126,4 @@ final class LibraryItemSpec
|
||||
|
||||
return $terms;
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function meta(): array
|
||||
{
|
||||
$meta = [];
|
||||
if ($this->moduleType !== null) {
|
||||
$meta['_et_pb_module_type'] = $this->moduleType;
|
||||
}
|
||||
if ($this->global && $this->layoutType === 'module') {
|
||||
$meta['_et_pb_excluded_global_options'] = '[]';
|
||||
}
|
||||
$meta['_et_pb_built_for_post_type'] = $this->builtFor;
|
||||
$meta['_et_pb_template_type'] = $this->layoutType;
|
||||
|
||||
return $meta;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ final class NewLibraryEntry
|
||||
return (string) $page->post->post_type;
|
||||
}
|
||||
|
||||
$builtFor = (string) get_post_meta((int) $page->post->ID, '_et_pb_built_for_post_type', true);
|
||||
$builtFor = DiviLibraryStore::builtFor((int) $page->post->ID);
|
||||
|
||||
return $builtFor !== '' ? $builtFor : 'page';
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Release;
|
||||
|
||||
use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore;
|
||||
use LIT\MCP\Integrations\Divi\Library\DiviRoles;
|
||||
use LIT\MCP\Integrations\Divi\Pages\DiviPages;
|
||||
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
|
||||
use LIT\MCP\Integrations\Divi\Schema\DiviModules;
|
||||
@@ -16,6 +18,8 @@ final class DiviAdapters
|
||||
/** @var list<class-string<DiviAdapter>> */
|
||||
public const CLASSES = [
|
||||
DiviPages::class,
|
||||
DiviLibraryStore::class,
|
||||
DiviRoles::class,
|
||||
DiviModules::class,
|
||||
DynamicContentMap::class,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user