PluginProbe
Polylang / trunk
Polylang vtrunk
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 3.0.3 All 233 releases
polylang / src / Widgets / Languages.php

Languages.php in Polylang trunk, at src/Widgets/Languages.php

317 lines 9.1 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\Widgets;
7
8 use PLL_Base;
9 use PLL_Links;
10 use WP_Widget;
11 use WP_Syntex\Polylang\Switcher\Assets;
12 use WP_Syntex\Polylang\Switcher\Switcher;
13 use WP_Syntex\Polylang\Switcher\Fields\Widget as Fields;
14 use WP_Syntex\Polylang\Switcher\Settings\Settings;
15
16 /**
17 * The advanced language switcher widget.
18 *
19 * @since 3.9
20 *
21 * @phpstan-type NewInstance array{
22 * title: string,
23 * layout: 'horizontal'|'vertical'|'dropdown'|'select',
24 * alignment: 'left'|'center'|'right'|'stretched',
25 * show_flags: bool,
26 * flag_aspect_ratio: '3:2'|'1:1',
27 * show_labels: ''|'names'|'codes',
28 * hide_if_no_translation: bool,
29 * hide_current: bool,
30 * force_home: bool,
31 * dropdown: 0|1,
32 * show_names: 0|1
33 * }
34 * @phpstan-type OldInstance array{
35 * title: string,
36 * dropdown: 0|1,
37 * show_flags: 0|1,
38 * show_names: 0|1,
39 * hide_if_no_translation: 0|1,
40 * hide_current: 0|1,
41 * force_home: 0|1
42 * }
43 * @extends WP_Widget<T>
44 * @phpstan-template T of array
45 */
46 class Languages extends WP_Widget {
47 /**
48 * @var PLL_Links|null
49 */
50 private ?PLL_Links $links;
51
52 /**
53 * Constructor.
54 *
55 * @since 3.9
56 *
57 * @param PLL_Base $polylang The Polylang object.
58 */
59 public function __construct( PLL_Base &$polylang ) {
60 $this->links = &$polylang->links;
61
62 parent::__construct(
63 'polylang',
64 __( 'Language switcher', 'polylang' ),
65 array(
66 'description' => __( 'Displays a language switcher', 'polylang' ),
67 'customize_selective_refresh' => true,
68 )
69 );
70
71 if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) {
72 add_action( 'wp_enqueue_scripts', array( Assets::class, 'enqueue_frontend_styles' ) );
73 }
74
75 add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'filter_legacy_widgets' ) );
76 }
77
78 /**
79 * Hides this legacy widget in the block widgets screen.
80 *
81 * @since 3.9
82 *
83 * @param array $widget_ids An array of hidden widget ids.
84 * @return array
85 */
86 public function filter_legacy_widgets( $widget_ids ) {
87 return array_merge( $widget_ids, array( $this->id_base ) );
88 }
89
90 /**
91 * Displays the widget.
92 *
93 * @since 3.9
94 *
95 * @param array $args Arguments, including `before_title`, `after_title`, `before_widget`, and `after_widget`.
96 * @param array $instance The settings for the particular instance of the widget.
97 * @return void
98 *
99 * @phpstan-param array{
100 * name: string,
101 * id: string,
102 * description: string,
103 * class: string,
104 * before_widget: string,
105 * after_widget: string,
106 * before_title: string,
107 * after_title: string,
108 * before_sidebar: string,
109 * after_sidebar: string,
110 * show_in_rest: boolean,
111 * widget_id: string,
112 * widget_name: string
113 * } $args
114 * @phpstan-param NewInstance|OldInstance $instance
115 */
116 public function widget( $args, $instance ): void {
117 if ( empty( $this->links ) ) {
118 return;
119 }
120
121 $instance['unique_id'] = "pll-switcher-widget-{$this->number}";
122
123 $instance = Fields::remove_legacy_settings( $instance );
124 $settings = new Settings( $instance );
125 $list = ( new Switcher( $settings, $this->links ) )->get();
126
127 if ( empty( $list ) ) {
128 return;
129 }
130
131 echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
132
133 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
134 $title = apply_filters( 'widget_title', $instance['title'] ?? '', $instance, $this->id_base );
135
136 if ( ! empty( $title ) ) {
137 echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput
138 }
139
140 echo $list; // phpcs:ignore WordPress.Security.EscapeOutput
141
142 echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
143 }
144
145 /**
146 * Updates the widget options.
147 *
148 * @since 3.9
149 *
150 * @param array $new_instance New settings for this instance as input by the user via `form()`.
151 * @param array $old_instance Old settings for this instance.
152 * @return array|bool Settings to save or bool false to cancel saving.
153 *
154 * @phpstan-param NewInstance $new_instance
155 * @phpstan-param OldInstance $old_instance
156 */
157 public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
158 $validated = Fields::filter( new Settings( $new_instance ) );
159 $validated = Fields::add_legacy_settings( $validated );
160
161 $validated['title'] = ! empty( $new_instance['title'] ) && is_string( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '';
162
163 return $validated;
164 }
165
166 /**
167 * Displays the widget form.
168 *
169 * @since 3.9
170 *
171 * @param array $instance Current settings.
172 * @return null
173 *
174 * @phpstan-param NewInstance|OldInstance $instance
175 */
176 public function form( $instance ) {
177 $settings = new Settings( Fields::remove_legacy_settings( $instance ) );
178 $title = ! empty( $instance['title'] ) && is_string( $instance['title'] ) ? $instance['title'] : '';
179
180 // Title.
181 printf(
182 '<p><label for="%1$s">%2$s</label><input class="widefat" id="%1$s" name="%3$s" type="text" value="%4$s" /></p>',
183 esc_attr( $this->get_field_id( 'title' ) ),
184 esc_html__( 'Title:', 'polylang' ),
185 esc_attr( $this->get_field_name( 'title' ) ),
186 esc_attr( $title )
187 );
188
189 echo '<table role="presentation" class="polylang-language-switcher-widget-content"><tbody>';
190
191 foreach ( Fields::get() as $key => $field ) {
192 if ( ! empty( $field['section'] ) ) {
193 $this->print_section_heading( $key, $field, $settings );
194 }
195 if ( ! empty( $field['choices'] ) ) {
196 $this->print_select( $key, $field, $settings );
197 } else {
198 $this->print_checkbox( $key, $field, $settings );
199 }
200 }
201
202 echo '</tbody></table>';
203 return null;
204 }
205
206 /**
207 * Prints a section heading.
208 *
209 * @since 3.9
210 *
211 * @param string $key Setting key.
212 * @param array $field Field labels and other data.
213 * @param Settings $settings Widget's settings.
214 * @return void
215 */
216 private function print_section_heading( string $key, array $field, Settings $settings ): void {
217 // We take a shortcut here with the "hide_if" classes: we're in a specific case where, if the layout is `select`,
218 // the whole "Flags Settings" section will be hidden. The "hide_if" of the 1st row is applied to the whole section.
219 // This is to keep things simple, as long as we don't need more complex behaviors.
220 printf( '<tr%s>', $this->get_wrapper_class_attr( $key, $field, $settings ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
221 printf(
222 '<td colspan="2"><h4>%s</h4></td></tr>',
223 esc_html( $field['section'] )
224 );
225 }
226
227 /**
228 * Prints a `<select>` setting.
229 *
230 * @since 3.9
231 *
232 * @param string $key Setting key.
233 * @param array $field Field labels and other data.
234 * @param Settings $settings Widget's settings.
235 * @return void
236 */
237 private function print_select( string $key, array $field, Settings $settings ): void {
238 printf( '<tr%s>', $this->get_wrapper_class_attr( $key, $field, $settings ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
239 printf(
240 '<th><label for="%s">%s</label></th>',
241 esc_attr( $this->get_field_id( $key ) ),
242 esc_html( $field['label'] )
243 );
244 printf(
245 '<td><select data-key="%1$s" id="%2$s" name="%3$s">',
246 esc_attr( $key ),
247 esc_attr( $this->get_field_id( $key ) ),
248 esc_attr( $this->get_field_name( $key ) )
249 );
250 foreach ( $field['choices'] as $value => $label ) {
251 printf(
252 '<option value="%s"%s>%s</option>',
253 esc_attr( $value ),
254 selected( $settings->$key, $value, false ),
255 esc_html( $label )
256 );
257 }
258 echo '</select></td></tr>';
259 }
260
261 /**
262 * Prints a checkbox setting.
263 *
264 * @since 3.9
265 *
266 * @param string $key Setting key.
267 * @param array $field Field labels and other data.
268 * @param Settings $settings Widget's settings.
269 * @return void
270 */
271 private function print_checkbox( string $key, array $field, Settings $settings ): void {
272 printf( '<tr%s>', $this->get_wrapper_class_attr( $key, $field, $settings ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
273 printf(
274 '<td colspan="2"><input type="checkbox" data-key="%1$s" class="checkbox" id="%2$s" name="%3$s"%4$s/><label for="%2$s">%5$s</label></td>',
275 esc_attr( $key ),
276 esc_attr( $this->get_field_id( $key ) ),
277 esc_attr( $this->get_field_name( $key ) ),
278 checked( $settings->$key, true, false ),
279 esc_html( $field['label'] )
280 );
281 echo '</tr>';
282 }
283
284 /**
285 * Returns the outer wrapper's `class` attribute.
286 *
287 * @since 3.9
288 *
289 * @param string $key Setting key.
290 * @param array $field Field labels and other data.
291 * @param Settings $settings Widget's settings.
292 * @return string
293 */
294 private function get_wrapper_class_attr( string $key, array $field, Settings $settings ): string {
295 if ( empty( $field['hide_if'] ) ) {
296 return '';
297 }
298
299 $classes = array();
300
301 foreach ( $field['hide_if'] as $k => $value ) {
302 if ( $settings->$k === $value ) {
303 $classes[] = "pll-hidden-by-{$k}";
304 }
305 if ( is_bool( $value ) ) {
306 $value = $value ? 'true' : 'false';
307 }
308 $classes[] = "pll-hidden-if-{$k}-{$value}";
309 }
310
311 return sprintf(
312 ' class="%s"',
313 esc_attr( implode( ' ', $classes ) )
314 );
315 }
316 }
317