PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
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 / Image.php

Image.php in Extendify 3.1.5, at app/QuickEdit/Schemas/Image.php

110 lines 3.6 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 // Mirrors attrs.url and the inner <img src> so the parsed-block tree
8 // stays internally consistent across re-renders.
9 class Image implements Schema
10 {
11 public function fields(): array
12 {
13 return [
14 [
15 'key' => 'image',
16 'control' => 'image',
17 'label' => __('Image', 'extendify-local'),
18 ],
19 ];
20 }
21
22 public function apply(array $block, string $fieldKey, $value): array
23 {
24 if ($fieldKey !== 'image' || !is_array($value)) {
25 return $block;
26 }
27
28 $id = isset($value['id']) ? (int) $value['id'] : null;
29 $alt = isset($value['alt']) ? wp_strip_all_tags((string) $value['alt']) : null;
30
31 // A positive id must resolve to a real image attachment; when it does,
32 // trust the server's URL over the client's. The raw client URL is only
33 // honored for the id-less external-image case.
34 if ($id !== null && $id > 0) {
35 if (!wp_attachment_is_image($id)) {
36 return $block;
37 }
38 $url = wp_get_attachment_image_url($id, 'full') ?: null;
39 } else {
40 $url = isset($value['url']) ? esc_url_raw((string) $value['url']) : null;
41 }
42
43 if (!$url) {
44 return $block;
45 }
46
47 $attrs = $block['attrs'] ?? [];
48 $attrs['url'] = $url;
49 if ($id !== null && $id > 0) {
50 $attrs['id'] = $id;
51 } else {
52 unset($attrs['id']);
53 }
54 $block['attrs'] = $attrs;
55
56 // wp_filter_content_tags rebuilds srcset on render when an
57 // attachment id is present; URL-only sources stay srcset-less.
58 $existing = (string) ($block['innerHTML'] ?? '');
59 $tp = new \WP_HTML_Tag_Processor($existing);
60 if ($tp->next_tag('img')) {
61 $tp->set_attribute('src', $url);
62 if ($alt !== null) {
63 $tp->set_attribute('alt', $alt);
64 }
65
66 // Sync wp-image-{id} with attrs.id; Gutenberg's save() derives the
67 // class from attrs.id and a mismatch trips the validator's "Attempt
68 // Recovery" prompt. Strip pre-existing first so re-edits don't stack.
69 $cls = (string) ($tp->get_attribute('class') ?? '');
70 $cls = trim((string) preg_replace('/\bwp-image-\d+\b/', '', $cls));
71 if ($id !== null && $id > 0) {
72 $cls = trim($cls . ' wp-image-' . $id);
73 }
74 if ($cls === '') {
75 $tp->remove_attribute('class');
76 } else {
77 $tp->set_attribute('class', $cls);
78 }
79
80 if ($id === null || $id <= 0) {
81 $tp->remove_attribute('srcset');
82 $tp->remove_attribute('sizes');
83 }
84 $existing = $tp->get_updated_html();
85 }
86
87 // The Launch-import marker class survives on the rendered figure but not
88 // in attrs.className; that mismatch trips the block validator after edit.
89 $existing = $this->stripImportMarker($existing, 'figure');
90
91 $block['innerHTML'] = $existing;
92 $block['innerContent'] = [$existing];
93
94 return $block;
95 }
96
97 private function stripImportMarker(string $html, string $tagName): string
98 {
99 $tp = new \WP_HTML_Tag_Processor($html);
100 if (!$tp->next_tag($tagName)) {
101 return $html;
102 }
103 if (!$tp->has_class('extendify-image-import')) {
104 return $html;
105 }
106 $tp->remove_class('extendify-image-import');
107 return $tp->get_updated_html();
108 }
109 }
110