refactor: Kern ohne Divi – Ports BuilderDetector, MediaUsageSource, EnvironmentComponent und ausgeschlossene Post-Typen im ContentSchema; Divi-Implementierungen und Verdrahtung über DiviIntegration/Plugin; CoreIndependenceTest
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
abeb4b0eed
commit
06148fb8c1
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Media;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/** The stored file of an attachment, as far as usage checks need it. */
|
||||
final class AttachmentFile
|
||||
{
|
||||
/**
|
||||
* Upload-relative path without extension and without WordPress' suffixes for big, rotated or edited images:
|
||||
* 2026/09/foto-scaled.jpg → 2026/09/foto. '' when the attachment has no file.
|
||||
*/
|
||||
public static function stem(int $attachmentId): string
|
||||
{
|
||||
$file = ltrim((string) get_post_meta($attachmentId, '_wp_attached_file', true), '/');
|
||||
$stem = (string) preg_replace('/\.[^.\/]+$/', '', $file);
|
||||
|
||||
return (string) preg_replace('/(?:-scaled|-rotated|-e\d{10,})+$/', '', $stem);
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,13 @@ defined('ABSPATH') || exit;
|
||||
*/
|
||||
final class MediaIntegration implements Integration
|
||||
{
|
||||
public function __construct(private ContentSchema $schema, private ChangeLog $log, private Limits $limits)
|
||||
{
|
||||
/** @param list<MediaUsageSource> $usageSources Further places an attachment can be used (e.g. the Divi logo). */
|
||||
public function __construct(
|
||||
private ContentSchema $schema,
|
||||
private ChangeLog $log,
|
||||
private Limits $limits,
|
||||
private array $usageSources = []
|
||||
) {
|
||||
}
|
||||
|
||||
public function id(): string
|
||||
@@ -39,7 +44,11 @@ final class MediaIntegration implements Integration
|
||||
public function abilities(): array
|
||||
{
|
||||
$presenter = new MediaPresenter();
|
||||
$finder = new MediaUsageFinder(fn (): array => $this->schema->mediaFieldKeys());
|
||||
$finder = new MediaUsageFinder(
|
||||
fn (): array => $this->schema->mediaFieldKeys(),
|
||||
null,
|
||||
$this->usageSources
|
||||
);
|
||||
$importer = new MediaImporter($this->limits);
|
||||
$uploads = new MediaUploads($presenter, $this->log, $this->schema);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Integrations\Media;
|
||||
|
||||
use Closure;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
||||
use LIT\MCP\Integrations\WordPress\PostStatuses;
|
||||
use LIT\MCP\Log;
|
||||
|
||||
@@ -59,9 +58,14 @@ class MediaUsageFinder
|
||||
|
||||
/**
|
||||
* @param Closure(): list<string> $mediaMetaKeys Keys of file/image fields (ContentSchema::mediaFieldKeys()).
|
||||
* @param list<MediaUsageSource> $sources Further site-wide places (e.g. the Divi logo), asked after the
|
||||
* core options and theme mods.
|
||||
*/
|
||||
public function __construct(private Closure $mediaMetaKeys, private ?\wpdb $db = null)
|
||||
{
|
||||
public function __construct(
|
||||
private Closure $mediaMetaKeys,
|
||||
private ?\wpdb $db = null,
|
||||
private array $sources = []
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +106,7 @@ class MediaUsageFinder
|
||||
private function collect(int $attachmentId, int $max, int $limit): array
|
||||
{
|
||||
$wpdb = $this->db ?? $GLOBALS['wpdb'];
|
||||
$stem = $this->fileStem($attachmentId);
|
||||
$stem = AttachmentFile::stem($attachmentId);
|
||||
$fileRegex = $stem === '' ? null : ContentMediaReferences::fileRegex($stem);
|
||||
|
||||
$found = [];
|
||||
@@ -257,14 +261,10 @@ class MediaUsageFinder
|
||||
);
|
||||
}
|
||||
|
||||
$diviLogo = DesignSystemReader::diviLogo();
|
||||
if ($diviLogo !== '' && ContentMediaReferences::matches($fileRegex, $diviLogo) !== false) {
|
||||
$found['site:divi_logo'] = self::siteUsage(
|
||||
'Divi-Logo',
|
||||
'option',
|
||||
'divi_logo',
|
||||
self::POSSIBLE
|
||||
);
|
||||
foreach ($this->sources as $source) {
|
||||
foreach ($source->usagesOf($attachmentId) as $usage) {
|
||||
$found['site:' . $usage['where']] = $usage;
|
||||
}
|
||||
}
|
||||
|
||||
$this->addWidgetUsages($found, $attachmentId, $fileRegex);
|
||||
@@ -420,18 +420,6 @@ class MediaUsageFinder
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload-relative path without extension and without WordPress' suffixes for big,
|
||||
* rotated or edited images: 2026/09/foto-scaled.jpg → 2026/09/foto.
|
||||
*/
|
||||
private function fileStem(int $attachmentId): string
|
||||
{
|
||||
$file = ltrim((string) get_post_meta($attachmentId, '_wp_attached_file', true), '/');
|
||||
$stem = (string) preg_replace('/\.[^.\/]+$/', '', $file);
|
||||
|
||||
return (string) preg_replace('/(?:-scaled|-rotated|-e\d{10,})+$/', '', $stem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, mixed>> $found
|
||||
* @param list<object> $rows
|
||||
@@ -470,8 +458,12 @@ class MediaUsageFinder
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array{post_id: int, title: string, post_type: string, where: string, certainty: string} */
|
||||
private static function siteUsage(string $title, string $type, string $where, string $certainty): array
|
||||
/**
|
||||
* A site-wide usage (post_id 0): option, theme mod, widget or a MediaUsageSource's place.
|
||||
*
|
||||
* @return array{post_id: int, title: string, post_type: string, where: string, certainty: string}
|
||||
*/
|
||||
public static function siteUsage(string $title, string $type, string $where, string $certainty): array
|
||||
{
|
||||
return ['post_id' => 0, 'title' => $title, 'post_type' => $type, 'where' => $where, 'certainty' => $certainty];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Media;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Port: a site-wide place outside the media library's own knowledge where an attachment can be used (e.g. the
|
||||
* logo in Divi's theme options). MediaUsageFinder asks every source it is given after the core options and
|
||||
* theme mods; the integration that owns the place implements it, Plugin wires it in.
|
||||
*/
|
||||
interface MediaUsageSource
|
||||
{
|
||||
/**
|
||||
* @return list<array{post_id: int, title: string, post_type: string, where: string, certainty: string}>
|
||||
* Usages of attachment $attachmentId in the format of MediaUsageFinder::usages(); use
|
||||
* MediaUsageFinder::siteUsage() for site-wide ones (post_id 0). "where" names the place and is
|
||||
* unique per source (it keys the usage, so a place is reported once).
|
||||
*/
|
||||
public function usagesOf(int $attachmentId): array;
|
||||
}
|
||||
Reference in New Issue
Block a user