* @phpstan-template T of array */ class Languages extends WP_Widget { /** * @var PLL_Links|null */ private ?PLL_Links $links; /** * Constructor. * * @since 3.9 * * @param PLL_Base $polylang The Polylang object. */ public function __construct( PLL_Base &$polylang ) { $this->links = &$polylang->links; parent::__construct( 'polylang', __( 'Language switcher', 'polylang' ), array( 'description' => __( 'Displays a language switcher', 'polylang' ), 'customize_selective_refresh' => true, ) ); if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) { add_action( 'wp_enqueue_scripts', array( Assets::class, 'enqueue_frontend_styles' ) ); } add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'filter_legacy_widgets' ) ); } /** * Hides this legacy widget in the block widgets screen. * * @since 3.9 * * @param array $widget_ids An array of hidden widget ids. * @return array */ public function filter_legacy_widgets( $widget_ids ) { return array_merge( $widget_ids, array( $this->id_base ) ); } /** * Displays the widget. * * @since 3.9 * * @param array $args Arguments, including `before_title`, `after_title`, `before_widget`, and `after_widget`. * @param array $instance The settings for the particular instance of the widget. * @return void * * @phpstan-param array{ * name: string, * id: string, * description: string, * class: string, * before_widget: string, * after_widget: string, * before_title: string, * after_title: string, * before_sidebar: string, * after_sidebar: string, * show_in_rest: boolean, * widget_id: string, * widget_name: string * } $args * @phpstan-param NewInstance|OldInstance $instance */ public function widget( $args, $instance ): void { if ( empty( $this->links ) ) { return; } $instance['unique_id'] = "pll-switcher-widget-{$this->number}"; $instance = Fields::remove_legacy_settings( $instance ); $settings = new Settings( $instance ); $list = ( new Switcher( $settings, $this->links ) )->get(); if ( empty( $list ) ) { return; } echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $instance['title'] ?? '', $instance, $this->id_base ); if ( ! empty( $title ) ) { echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput } echo $list; // phpcs:ignore WordPress.Security.EscapeOutput echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput } /** * Updates the widget options. * * @since 3.9 * * @param array $new_instance New settings for this instance as input by the user via `form()`. * @param array $old_instance Old settings for this instance. * @return array|bool Settings to save or bool false to cancel saving. * * @phpstan-param NewInstance $new_instance * @phpstan-param OldInstance $old_instance */ public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $validated = Fields::filter( new Settings( $new_instance ) ); $validated = Fields::add_legacy_settings( $validated ); $validated['title'] = ! empty( $new_instance['title'] ) && is_string( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : ''; return $validated; } /** * Displays the widget form. * * @since 3.9 * * @param array $instance Current settings. * @return null * * @phpstan-param NewInstance|OldInstance $instance */ public function form( $instance ) { $settings = new Settings( Fields::remove_legacy_settings( $instance ) ); $title = ! empty( $instance['title'] ) && is_string( $instance['title'] ) ? $instance['title'] : ''; // Title. printf( '

', esc_attr( $this->get_field_id( 'title' ) ), esc_html__( 'Title:', 'polylang' ), esc_attr( $this->get_field_name( 'title' ) ), esc_attr( $title ) ); echo ''; foreach ( Fields::get() as $key => $field ) { if ( ! empty( $field['section'] ) ) { $this->print_section_heading( $key, $field, $settings ); } if ( ! empty( $field['choices'] ) ) { $this->print_select( $key, $field, $settings ); } else { $this->print_checkbox( $key, $field, $settings ); } } echo ''; return null; } /** * Prints a section heading. * * @since 3.9 * * @param string $key Setting key. * @param array $field Field labels and other data. * @param Settings $settings Widget's settings. * @return void */ private function print_section_heading( string $key, array $field, Settings $settings ): void { // We take a shortcut here with the "hide_if" classes: we're in a specific case where, if the layout is `select`, // the whole "Flags Settings" section will be hidden. The "hide_if" of the 1st row is applied to the whole section. // This is to keep things simple, as long as we don't need more complex behaviors. printf( '', $this->get_wrapper_class_attr( $key, $field, $settings ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped printf( '

%s

', esc_html( $field['section'] ) ); } /** * Prints a `', esc_attr( $key ), esc_attr( $this->get_field_id( $key ) ), esc_attr( $this->get_field_name( $key ) ) ); foreach ( $field['choices'] as $value => $label ) { printf( '', esc_attr( $value ), selected( $settings->$key, $value, false ), esc_html( $label ) ); } echo ''; } /** * Prints a checkbox setting. * * @since 3.9 * * @param string $key Setting key. * @param array $field Field labels and other data. * @param Settings $settings Widget's settings. * @return void */ private function print_checkbox( string $key, array $field, Settings $settings ): void { printf( '', $this->get_wrapper_class_attr( $key, $field, $settings ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped printf( '', esc_attr( $key ), esc_attr( $this->get_field_id( $key ) ), esc_attr( $this->get_field_name( $key ) ), checked( $settings->$key, true, false ), esc_html( $field['label'] ) ); echo ''; } /** * Returns the outer wrapper's `class` attribute. * * @since 3.9 * * @param string $key Setting key. * @param array $field Field labels and other data. * @param Settings $settings Widget's settings. * @return string */ private function get_wrapper_class_attr( string $key, array $field, Settings $settings ): string { if ( empty( $field['hide_if'] ) ) { return ''; } $classes = array(); foreach ( $field['hide_if'] as $k => $value ) { if ( $settings->$k === $value ) { $classes[] = "pll-hidden-by-{$k}"; } if ( is_bool( $value ) ) { $value = $value ? 'true' : 'false'; } $classes[] = "pll-hidden-if-{$k}-{$value}"; } return sprintf( ' class="%s"', esc_attr( implode( ' ', $classes ) ) ); } }