PluginProbe
BeyondWords – AI audio for publishers / 4.1.1
BeyondWords – AI audio for publishers v4.1.1
7.1.0 trunk 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.3.0 4.4.0 4.5.0 4.5.1 4.6.0 4.6.1 4.6.2 4.7.0 All 43 releases
speechkit / src / Component / Post / BlockAttributes / BlockAttributes.php

BlockAttributes.php in BeyondWords – AI audio for publishers 4.1.1, at src/Component/Post/BlockAttributes/BlockAttributes.php

206 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 /**
6 * BeyondWords support for Gutenberg blocks.
7 *
8 * @package Beyondwords\Wordpress
9 * @author Stuart McAlpine <stu@beyondwords.io>
10 * @since 3.7.0
11 * @since 4.0.0 Renamed from BlockAudioAttribute.php to BlockAttributes.php to support multiple attributes
12 */
13
14 namespace Beyondwords\Wordpress\Component\Post\BlockAttributes;
15
16 use Beyondwords\Wordpress\Component\Post\PostMetaUtils;
17 use Beyondwords\Wordpress\Component\Settings\PlayerUI\PlayerUI;
18 use Beyondwords\Wordpress\Core\CoreUtils;
19
20 /**
21 * BlockAttributes setup
22 *
23 * @since 3.7.0
24 * @since 4.0.0 Renamed from BlockAudioAttribute to BlockAttributes to support multiple attributes
25 */
26 class BlockAttributes
27 {
28 /**
29 * Constructor
30 */
31 public function __construct()
32 {
33 add_filter('register_block_type_args', array($this, 'registerAudioAttribute'));
34 add_filter('register_block_type_args', array($this, 'registerMarkerAttribute'));
35 add_filter('render_block', array($this, 'addMarkerAttributeToBlocks'), 10, 2);
36 }
37
38 /**
39 * Register "Audio" attribute for Gutenberg blocks.
40 */
41 public function registerAudioAttribute($args)
42 {
43 // Setup attributes if needed.
44 if (! isset($args['attributes'])) {
45 $args['attributes'] = array();
46 }
47
48 if (! array_key_exists('beyondwordsAudio', $args['attributes'])) {
49 $args['attributes']['beyondwordsAudio'] = array(
50 'type' => 'boolean',
51 'default' => true,
52 );
53 }
54
55 return $args;
56 }
57
58 /**
59 * Register "Segment marker" attribute for Gutenberg blocks.
60 */
61 public function registerMarkerAttribute($args)
62 {
63 // Setup attributes if needed.
64 if (! isset($args['attributes'])) {
65 $args['attributes'] = array();
66 }
67
68 if (! array_key_exists('beyondwordsMarker', $args['attributes'])) {
69 $args['attributes']['beyondwordsMarker'] = array(
70 'type' => 'string',
71 'default' => '',
72 );
73 }
74
75 return $args;
76 }
77
78 /**
79 * Add data-beyondwords-marker attribute to root element of Gutenberg blocks.
80 *
81 * @since 4.0.0
82 *
83 * @param string $blockContent Block Content (HTML).
84 * @param string $block Block object.
85 *
86 * @return string Block Content (HTML).
87 */
88 public function addMarkerAttributeToBlocks($blockContent, $block)
89 {
90 // Is Player UI enabled?
91 if (get_option('beyondwords_player_ui', PlayerUI::ENABLED) === PlayerUI::DISABLED) {
92 return $blockContent;
93 }
94
95 // Does parent post have audio?
96 if (! PostMetaUtils::getContentId(get_the_ID())) {
97 return $blockContent;
98 }
99
100 // Does this block have a beyondwordsMarker attribute?
101 if (empty($block['attrs']) || empty($block['attrs']['beyondwordsMarker'])) {
102 return $blockContent;
103 }
104
105 // Prefer WP_HTML_Tag_Processor, introduced in WordPress 6.2
106 if (class_exists('WP_HTML_Tag_Processor')) {
107 $blockContent = $this->addMarkerAttributeWithHTMLTagProcessor(
108 $blockContent,
109 $block['attrs']['beyondwordsMarker']
110 );
111 } else {
112 $blockContent = $this->addMarkerAttributeWithDOMDocument(
113 $blockContent,
114 $block['attrs']['beyondwordsMarker']
115 );
116 }
117
118 return $blockContent;
119 }
120
121 /**
122 * Add data-beyondwords-marker attribute to the root elements in a HTML
123 * string using WP_HTML_Tag_Processor.
124 *
125 * @since 4.0.0
126 *
127 * @param string $html HTML.
128 * @param string $marker Marker UUID.
129 *
130 * @return string HTML.
131 */
132 public function addMarkerAttributeWithHTMLTagProcessor($html, $marker)
133 {
134 // https://github.com/WordPress/gutenberg/pull/42485
135 $tags = new \WP_HTML_Tag_Processor($html);
136
137 if ($tags->next_tag()) {
138 $tags->set_attribute('data-beyondwords-marker', $marker);
139 }
140
141 return strval($tags);
142 }
143
144 /**
145 * Add data-beyondwords-marker attribute to the root elements in a HTML
146 * string using DOMDocument.
147 *
148 * This is a fallback, since WP_HTML_Tag_Processor was only shipped with
149 * WordPress 6.2 on 19 April 2023.
150 *
151 * https://make.wordpress.org/core/2022/10/13/whats-new-in-gutenberg-14-3-12-october/
152 *
153 * Note: It is not ideal to do all the $bodyElement/$fullHtml processing
154 * in this method, but without it DOMDocument does not work as expected if
155 * there is more than 1 root element. The approach here has been taken from
156 * some historic Gutenberg code before they implemented WP_HTML_Tag_Processor:
157 *
158 * https://github.com/WordPress/gutenberg/blob/6671cef1179412a2bbd4969cbbc82705c7f69bac/lib/block-supports/index.php
159 *
160 * @since 4.0.0
161 *
162 * @param string $html HTML.
163 * @param string $marker Marker UUID.
164 *
165 * @return string HTML.
166 */
167 public function addMarkerAttributeWithDOMDocument($html, $marker)
168 {
169 $dom = new \DOMDocument('1.0', 'utf-8');
170
171 $wrappedHtml =
172 '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body>'
173 . $html
174 . '</body></html>';
175
176 $success = $dom->loadHTML($wrappedHtml, LIBXML_HTML_NODEFDTD | LIBXML_COMPACT);
177
178 if (! $success) {
179 return $html;
180 }
181
182 // Structure is like `<html><head/><body/></html>`, so body is the `lastChild` of our document.
183 $bodyElement = $dom->documentElement->lastChild;
184
185 $xpath = new \DOMXPath($dom);
186 $blockRoot = $xpath->query('./*', $bodyElement)[0];
187
188 if (empty($blockRoot)) {
189 return $html;
190 }
191
192 $blockRoot->setAttribute('data-beyondwords-marker', $marker);
193
194 // Avoid using `$dom->saveHtml( $node )` because the node results may not produce consistent
195 // whitespace. Saving the root HTML `$dom->saveHtml()` prevents this behavior.
196 $fullHtml = $dom->saveHtml();
197
198 // Find the <body> open/close tags. The open tag needs to be adjusted so we get inside the tag
199 // and not the tag itself.
200 $start = strpos($fullHtml, '<body>', 0) + strlen('<body>');
201 $end = strpos($fullHtml, '</body>', $start);
202
203 return trim(substr($fullHtml, $start, $end - $start));
204 }
205 }
206