PluginProbe
Extendify / 3.2.1
Extendify v3.2.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 / Heading.php

Heading.php in Extendify 3.2.1, at app/QuickEdit/Schemas/Heading.php

100 lines 3.7 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 // Quick Edit intentionally doesn't expose `attrs.level` — changing h2 → h1
8 // has SEO/a11y implications and belongs in the full editor.
9 class Heading implements Schema
10 {
11 public function fields(): array
12 {
13 return [
14 [
15 'key' => 'content',
16 'control' => 'text',
17 'label' => __('Heading text', 'extendify-local'),
18 ],
19 [
20 'key' => 'align',
21 'control' => 'alignment',
22 'label' => __('Alignment', 'extendify-local'),
23 'options' => [
24 ['value' => 'left', 'label' => __('Left', 'extendify-local')],
25 ['value' => 'center', 'label' => __('Center', 'extendify-local')],
26 ['value' => 'right', 'label' => __('Right', 'extendify-local')],
27 ],
28 ],
29 ];
30 }
31
32 public function apply(array $block, string $fieldKey, $value): array
33 {
34 switch ($fieldKey) {
35 case 'content':
36 $text = is_string($value) ? $value : '';
37 $existing = (string) ($block['innerHTML'] ?? '');
38 $escaped = wp_kses_post($text);
39
40 if (preg_match('/<(h[1-6])\b/i', $existing, $m)) {
41 $tag = strtolower($m[1]);
42 // Callback (not a replacement string) so user text containing
43 // $1 / \1 / $0 is spliced literally, not parsed as a backref.
44 $newInner = preg_replace_callback(
45 '/(<' . $tag . '\b[^>]*>)(.*?)(<\/' . $tag . '>)/is',
46 static function ($parts) use ($escaped) {
47 return $parts[1] . $escaped . $parts[3];
48 },
49 $existing,
50 1
51 );
52 } else {
53 $level = (int) ($block['attrs']['level'] ?? 2);
54 $level = max(1, min(6, $level));
55 $newInner = '<h' . $level . '>' . $escaped . '</h' . $level . '>';
56 }
57
58 $block['innerHTML'] = $newInner;
59 $block['innerContent'] = [$newInner];
60 return $block;
61
62 case 'align':
63 $allowed = ['left', 'center', 'right'];
64 $next = in_array($value, $allowed, true) ? $value : null;
65 $attrs = $block['attrs'] ?? [];
66
67 if ($next === null) {
68 unset($attrs['textAlign']);
69 } else {
70 $attrs['textAlign'] = $next;
71 }
72 $block['attrs'] = $attrs;
73 $block['innerHTML'] = $this->updateAlignClass((string) ($block['innerHTML'] ?? ''), $next);
74 $block['innerContent'] = [$block['innerHTML']];
75 return $block;
76 }
77 return $block;
78 }
79
80 private function updateAlignClass(string $html, $align): string
81 {
82 $tp = new \WP_HTML_Tag_Processor($html);
83 if (!$tp->next_tag()) {
84 return $html;
85 }
86 $classes = (string) ($tp->get_attribute('class') ?? '');
87 $classes = trim((string) preg_replace('/\bhas-text-align-(left|center|right)\b/', '', $classes));
88 $classes = preg_replace('/\s+/', ' ', $classes);
89 if ($align !== null) {
90 $classes = trim($classes . ' has-text-align-' . $align);
91 }
92 if ($classes === '') {
93 $tp->remove_attribute('class');
94 } else {
95 $tp->set_attribute('class', $classes);
96 }
97 return $tp->get_updated_html();
98 }
99 }
100