PluginProbe
Polylang / 3.8.10
Polylang v3.8.10
3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 All 234 releases
polylang / src / modules / Blocks / Language_Switcher / Navigation / Block.php

Block.php in Polylang 3.8.10, at src/modules/Blocks/Language_Switcher/Navigation/Block.php

333 lines 9.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 namespace WP_Syntex\Polylang\Blocks\Language_Switcher\Navigation;
7
8 use WP_Block;
9 use PLL_Switcher;
10 use SplObjectStorage;
11 use WP_HTML_Tag_Processor;
12 use WP_Syntex\Polylang\Blocks\Language_Switcher\Abstract_Block;
13
14 /**
15 * Language switcher block for navigation.
16 *
17 * @since 3.2
18 * @since 3.8 Moved to Polylang Core and renamed to Language_Switcher\Navigation\Block.
19 */
20 class Block extends Abstract_Block {
21 /**
22 * Placeholder used to add language name or flag after WordPress renders the link labels.
23 *
24 * @var string
25 */
26 const PLACEHOLDER = '%pll%';
27
28 /**
29 * Trusted locale and label, keyed by the inner navigation blocks created by this switcher.
30 * Blocks parsed from saved content are never the same instances, so they can't be found here.
31 *
32 * @var SplObjectStorage
33 * @phpstan-var SplObjectStorage<WP_Block, array{locale: string, label: string}>
34 */
35 private $internal_blocks;
36
37 /**
38 * Constructor.
39 *
40 * @since 3.8.10
41 *
42 * @param \PLL_Base $polylang Polylang object.
43 */
44 public function __construct( &$polylang ) {
45 parent::__construct( $polylang );
46
47 $this->internal_blocks = new SplObjectStorage();
48 }
49
50 /**
51 * Adds the required hooks specific to the navigation language switcher.
52 *
53 * @since 3.2
54 *
55 * @return self
56 */
57 public function init() {
58 parent::init();
59
60 add_action( 'rest_api_init', array( $this, 'register_switcher_menu_item_options_meta_rest_field' ) );
61 add_filter( 'render_block_core/navigation-link', array( $this, 'render_custom_attributes' ), 10, 3 );
62 add_filter( 'render_block_core/navigation-submenu', array( $this, 'render_custom_attributes' ), 10, 3 );
63 add_action( 'init', array( $this, 'register_editor_style' ) );
64
65 return $this;
66 }
67
68 /**
69 * Registers the editor style for the navigation language switcher block.
70 *
71 * @since 3.8
72 *
73 * @return void
74 */
75 public function register_editor_style(): void {
76 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
77
78 wp_register_style(
79 'pll-navigation-language-switcher-editor-style',
80 plugins_url( 'css/build/navigation-language-switcher-editor-style' . $suffix . '.css', POLYLANG_ROOT_FILE ),
81 array(),
82 POLYLANG_VERSION
83 );
84 }
85
86 /**
87 * Returns the navigation language switcher block name with the Polylang's namespace.
88 *
89 * @since 3.2
90 *
91 * @return string The block name.
92 */
93 protected function get_block_name() {
94 return 'polylang/navigation-language-switcher';
95 }
96
97 /**
98 * Renders the `polylang/navigation-language-switcher` block on server.
99 *
100 * @since 3.1
101 * @since 3.3 Accepts two new parameters, $content and $block.
102 *
103 * @param array $attributes The block attributes.
104 * @param string $content The saved content. Unused.
105 * @param WP_Block $block The parsed block.
106 * @return string The HTML string output to serve.
107 */
108 public function render( $attributes, $content, $block ) {
109 $attributes = $this->set_attributes_for_block( $attributes );
110 $switcher = new PLL_Switcher();
111 $switcher_elements = (array) $switcher->the_languages( $this->links, array_merge( $attributes, array( 'raw' => true ) ) );
112
113 if ( empty( $switcher_elements ) ) {
114 return '';
115 }
116
117 $generated_class = wp_apply_generated_classname_support( $block->block_type )['class'];
118
119 if ( $attributes['dropdown'] ) {
120 $inner_nav_link_blocks = array();
121 $top_level_lang = reset( $switcher_elements );
122 foreach ( $switcher_elements as $switcher_element ) {
123 $inner_nav_link_blocks[] = $this->create_inner_block(
124 'core/navigation-link',
125 $this->get_core_block_attributes( $switcher_element ),
126 $switcher_element,
127 $block->context,
128 $attributes
129 );
130
131 if ( $switcher_element['current_lang'] && ! $attributes['hide_current'] ) {
132 $top_level_lang = $switcher_element;
133 }
134 }
135
136 $submenu_attributes = $this->get_core_block_attributes( $top_level_lang );
137 $submenu_attributes['className'] .= ' ' . $generated_class;
138 $submenu_block = $this->create_inner_block(
139 'core/navigation-submenu',
140 $submenu_attributes,
141 $top_level_lang,
142 $block->context,
143 $attributes,
144 $inner_nav_link_blocks
145 );
146 $output = $submenu_block->render();
147 } else {
148 $output = '';
149
150 foreach ( $switcher_elements as $switcher_element ) {
151 $link_attributes = $this->get_core_block_attributes( $switcher_element );
152 $link_attributes['className'] .= ' ' . $generated_class;
153 $output .= $this->create_inner_block(
154 'core/navigation-link',
155 $link_attributes,
156 $switcher_element,
157 $block->context,
158 $attributes
159 )->render();
160 }
161 }
162
163 return $output;
164 }
165
166 /**
167 * Register switcher menu item meta options as a REST API field.
168 *
169 * @since 3.2
170 *
171 * @return void
172 */
173 public function register_switcher_menu_item_options_meta_rest_field() {
174 register_post_meta(
175 'nav_menu_item',
176 '_pll_menu_item',
177 array(
178 'object_subtype' => 'nav_menu_item',
179 'description' => __( 'Language switcher settings', 'polylang' ),
180 'single' => true,
181 'show_in_rest' => array(
182 'schema' => array(
183 'type' => 'object',
184 'additionalProperties' => array(
185 'type' => 'boolean',
186 ),
187 ),
188 ),
189 )
190 );
191 }
192
193 /**
194 * Renders a core/naviagation-link or core/naviagation-submenu block by adding hreflang and lang attributes to the <a> tag
195 * and also the language flag if required.
196 *
197 * @since 3.6
198 *
199 * @param string $block_content The block content.
200 * @param array $block The full block, including name and attributes.
201 * @param WP_Block $instance The block instance.
202 *
203 * @return string A formatted HTML string representing the core/navigation-link or core/navigation-submenu block.
204 */
205 public function render_custom_attributes( $block_content, $block, $instance ) {
206 if ( ! $this->internal_blocks->offsetExists( $instance ) ) {
207 return $block_content;
208 }
209
210 $snapshot = $this->internal_blocks->offsetGet( $instance );
211 $this->internal_blocks->offsetUnset( $instance );
212
213 $content_tags = new WP_HTML_Tag_Processor( $block_content );
214
215 if ( 'core/navigation-submenu' === $instance->name ) {
216 // If `openSubmenusOnClick`, the submenu is rendered as a button, so there are no `<a>` to process.
217 if ( empty( $instance->context['openSubmenusOnClick'] ) && $content_tags->next_tag( array( 'tag_name' => 'a' ) ) ) {
218 $content_tags->set_attribute( 'hreflang', $snapshot['locale'] );
219 $content_tags->set_attribute( 'lang', $snapshot['locale'] );
220 }
221 if ( $content_tags->next_tag( array( 'tag_name' => 'button' ) ) ) {
222 $content_tags->set_attribute(
223 'aria-label',
224 str_replace(
225 static::PLACEHOLDER,
226 __( 'Languages', 'polylang' ),
227 (string) $content_tags->get_attribute( 'aria-label' )
228 )
229 );
230 }
231 } elseif ( $content_tags->next_tag( array( 'tag_name' => 'a' ) ) ) {
232 $content_tags->set_attribute( 'hreflang', $snapshot['locale'] );
233 $content_tags->set_attribute( 'lang', $snapshot['locale'] );
234 }
235
236 $overridden_block_content = $content_tags->get_updated_html();
237
238 return str_replace(
239 static::PLACEHOLDER,
240 $snapshot['label'],
241 $overridden_block_content
242 );
243 }
244
245 /**
246 * Returns the path to the block JSON file directory.
247 * The directory name being used to register a block.
248 *
249 * @since 3.8
250 *
251 * @return string The path to the block.
252 */
253 protected function get_path(): string {
254 return __DIR__;
255 }
256
257 /**
258 * Creates an inner navigation block and stores the locale and label to render for it.
259 *
260 * @since 3.8.10
261 *
262 * @param string $block_name Core block name.
263 * @param array $core_attributes Attributes to be rendered by core.
264 * @param array $switcher_item Array of a switcher item data.
265 * @param array $context Block context.
266 * @param array $attributes Array of polylang/navigation-language-switcher attributes.
267 * @param WP_Block[] $inner_blocks Optional inner blocks.
268 * @return WP_Block
269 */
270 private function create_inner_block( $block_name, $core_attributes, $switcher_item, $context, $attributes, $inner_blocks = array() ) {
271 $block = new WP_Block(
272 array(
273 'blockName' => $block_name,
274 'attrs' => $core_attributes,
275 'innerBlocks' => $inner_blocks,
276 ),
277 $context
278 );
279
280 $this->internal_blocks->offsetSet(
281 $block,
282 array(
283 'locale' => $switcher_item['locale'],
284 'label' => $this->get_link_label( $switcher_item, $attributes ),
285 )
286 );
287
288 return $block;
289 }
290
291 /**
292 * Builds the language switcher link label from a trusted switcher item.
293 *
294 * @since 3.8.10
295 *
296 * @param array $switcher_item Array of a switcher item data.
297 * @param array $attributes Array of polylang/navigation-language-switcher attributes.
298 * @return string
299 */
300 private function get_link_label( $switcher_item, $attributes ) {
301 $show_flags = ! empty( $attributes['show_flags'] );
302 $show_names = ! empty( $attributes['show_names'] );
303 $link_label = '';
304
305 if ( $show_flags ) {
306 $link_label .= $switcher_item['flag'];
307 }
308
309 if ( $show_names ) {
310 $name = esc_html( $switcher_item['name'] );
311 $link_label .= $show_flags ? ' ' . $name : $name;
312 }
313
314 return $link_label;
315 }
316
317 /**
318 * Returns attributes that fit for core/navigation-link or core/navigation-submenu and specific to polylang/navigation-language-switcher.
319 *
320 * @since 3.6
321 *
322 * @param array $switcher_item Array of a switcher item data.
323 * @return array Attributes to be rendered by core.
324 */
325 private function get_core_block_attributes( $switcher_item ) {
326 return array(
327 'label' => static::PLACEHOLDER,
328 'url' => $switcher_item['url'],
329 'className' => trim( implode( ' ', (array) $switcher_item['classes'] ) ),
330 );
331 }
332 }
333