PluginProbe
Extendify / 3.1.1
Extendify v3.1.1
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / app / QuickEdit / Schemas / Cover.php

Cover.php in Extendify 3.1.1, at app/QuickEdit/Schemas/Cover.php

123 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Extendify\QuickEdit\Schemas;
4
5 defined('ABSPATH') || die('No direct access.');
6
7 class Cover implements Schema
8 {
9 public function fields(): array
10 {
11 return [
12 [
13 'key' => 'background',
14 'control' => 'image',
15 'label' => __('Background image', 'extendify-local'),
16 ],
17 ];
18 }
19
20 public function apply(array $block, string $fieldKey, $value): array
21 {
22 if ($fieldKey !== 'background' || !is_array($value)) {
23 return $block;
24 }
25
26 $id = isset($value['id']) ? (int) $value['id'] : null;
27
28 // Mirror of Image::apply — a positive id must be a real image
29 // attachment, and the server-derived URL wins over the client's.
30 if ($id !== null && $id > 0) {
31 if (!wp_attachment_is_image($id)) {
32 return $block;
33 }
34 $url = wp_get_attachment_image_url($id, 'full') ?: null;
35 } else {
36 $url = isset($value['url']) ? esc_url_raw((string) $value['url']) : null;
37 }
38 if (!$url) {
39 return $block;
40 }
41
42 $attrs = $block['attrs'] ?? [];
43 $attrs['url'] = $url;
44 if ($id !== null && $id > 0) {
45 $attrs['id'] = $id;
46 } else {
47 unset($attrs['id']);
48 }
49 unset($attrs['hasParallax']);
50 $block['attrs'] = $attrs;
51
52 // Patch the string chunks in innerContent — NOT innerHTML. innerContent
53 // for a cover with inner blocks is `[wrapper_open, null, wrapper_close]`;
54 // those nulls map to innerBlocks at serialize time. Stuffing innerHTML
55 // into [0] closes the wrapper early and the inner blocks land outside
56 // the cover, vanishing from __inner-container.
57 // Color-only / video covers don't have an image-background <img>;
58 // skip rather than synthesizing one.
59 $innerContent = $block['innerContent'] ?? [];
60 foreach ($innerContent as $i => $chunk) {
61 if (!is_string($chunk)) {
62 continue;
63 }
64 $tp = new \WP_HTML_Tag_Processor($chunk);
65 $patched = false;
66 while ($tp->next_tag('img')) {
67 $cls = (string) ($tp->get_attribute('class') ?? '');
68 if (str_contains($cls, 'wp-block-cover__image-background')) {
69 $tp->set_attribute('src', $url);
70 if ($id === null || $id <= 0) {
71 $tp->remove_attribute('srcset');
72 $tp->remove_attribute('sizes');
73 }
74
75 // Gutenberg's core/cover save() derives the wp-image-{id}
76 // class from attrs.id; mismatch trips the block validator.
77 $newCls = trim((string) preg_replace(
78 '/\bwp-image-\d+\b/',
79 '',
80 $cls
81 ));
82 if ($id !== null && $id > 0) {
83 $newCls = trim($newCls . ' wp-image-' . $id);
84 }
85 $newCls = (string) preg_replace('/\s{2,}/', ' ', $newCls);
86 $tp->set_attribute('class', $newCls);
87
88 $patched = true;
89 break;
90 }
91 }
92 if ($patched) {
93 $patchedChunk = $tp->get_updated_html();
94 $patchedChunk = $this->stripImportMarker($patchedChunk, 'div');
95 $innerContent[$i] = $patchedChunk;
96 $block['innerContent'] = $innerContent;
97 // Inspection-only; the serializer rebuilds innerHTML from innerContent.
98 $block['innerHTML'] = implode('', array_map(
99 static fn($c) => is_string($c) ? $c : '',
100 $innerContent
101 ));
102 break;
103 }
104 }
105
106 return $block;
107 }
108
109 // Mirror of Image::stripImportMarker.
110 private function stripImportMarker(string $html, string $tagName): string
111 {
112 $tp = new \WP_HTML_Tag_Processor($html);
113 if (!$tp->next_tag($tagName)) {
114 return $html;
115 }
116 if (!$tp->has_class('extendify-image-import')) {
117 return $html;
118 }
119 $tp->remove_class('extendify-image-import');
120 return $tp->get_updated_html();
121 }
122 }
123