class-alphalisting-widget.php
630 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Definition for the alphalisting's main widget |
| 4 | * |
| 5 | * @package alphalisting |
| 6 | */ |
| 7 | |
| 8 | declare(strict_types=1); |
| 9 | |
| 10 | namespace eslin87\AlphaListing; |
| 11 | |
| 12 | if ( ! defined( 'ABSPATH' ) ) { |
| 13 | exit; |
| 14 | } |
| 15 | |
| 16 | /** |
| 17 | * Definition for the AlphaListing_Widget which displays alphabetically-ordered list of latin letters linking to the AlphaListing page |
| 18 | * |
| 19 | * @since 0.1 |
| 20 | */ |
| 21 | class AlphaListing_Widget extends \WP_Widget { |
| 22 | /** |
| 23 | * Register the widget's meta information |
| 24 | * |
| 25 | * @since 0.1 |
| 26 | * @param string $id_base Optional Base ID for the widget, lowercase and unique. If left empty, |
| 27 | * a portion of the widget's class name will be used Has to be unique. |
| 28 | * @param string $name Name for the widget displayed on the configuration page. |
| 29 | * @param array<string,mixed> $widget_options Optional. Widget options. See wp_register_sidebar_widget() for information |
| 30 | * on accepted arguments. Default empty array. |
| 31 | * @param array<string,mixed> $control_options Optional. Widget control options. See wp_register_widget_control() for |
| 32 | * information on accepted arguments. Default empty array. |
| 33 | */ |
| 34 | public function __construct( $id_base = '', $name = '', $widget_options = array(), $control_options = array() ) { |
| 35 | $widget_options['classname'] = $widget_options['classname'] ?? 'alphalisting-widget'; |
| 36 | $widget_options['description'] = $widget_options['description'] ?? __( |
| 37 | 'Alphabetized links to the A-Z site map', |
| 38 | 'alphalisting' |
| 39 | ); |
| 40 | |
| 41 | parent::__construct( |
| 42 | 'alphalisting_az_widget', |
| 43 | __( 'A-Z Site Map', 'alphalisting' ), |
| 44 | $widget_options, |
| 45 | $control_options |
| 46 | ); |
| 47 | |
| 48 | add_action( 'admin_enqueue_scripts', 'alphalisting_enqueue_widget_admin_script' ); |
| 49 | add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_if_active' ) ); |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * Enqueue scripts and stylesheets if the widget is active on the page |
| 54 | * |
| 55 | * @since 4.0.0 |
| 56 | * @return void |
| 57 | */ |
| 58 | public function enqueue_if_active() { |
| 59 | if ( false !== is_active_widget( false, false, $this->id_base, true ) ) { |
| 60 | alphalisting_do_enqueue(); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * Print-out the configuration form for the widget |
| 66 | * |
| 67 | * @since 0.1 |
| 68 | * @param array<string,mixed> $instance Widget instance as provided by WordPress core. |
| 69 | * @return string |
| 70 | */ |
| 71 | public function form( $instance ) { |
| 72 | $args = array( |
| 73 | 'public' => true, |
| 74 | ); |
| 75 | |
| 76 | $public_post_types = get_post_types( $args, 'objects' ); |
| 77 | $public_taxonomies = get_taxonomies( $args, 'objects' ); |
| 78 | |
| 79 | $widget_title = $instance['title'] ?? ''; |
| 80 | $widget_title_id = $this->get_field_id( 'title' ); |
| 81 | $widget_title_name = $this->get_field_name( 'title' ); |
| 82 | |
| 83 | $display_type = ( isset( $instance['type'] ) && 'terms' === $instance['type'] ) ? 'terms' : 'posts'; |
| 84 | $display_type_id = $this->get_field_id( 'type' ); |
| 85 | $display_type_name = $this->get_field_name( 'type' ); |
| 86 | |
| 87 | $target_post = isset( $instance['post'] ) ? intval( $instance['post'] ) : ( ( isset( $instance['page'] ) ? intval( $instance['page'] ) : 0 ) ); |
| 88 | $target_post_id = $this->get_field_id( 'post' ); |
| 89 | $target_post_name = $this->get_field_name( 'post' ); |
| 90 | $target_post_title = isset( $instance['target_post'] ) ? $instance['target_post'] : ( ( 0 < $target_post ) ? get_the_title( $target_post ) : '' ); |
| 91 | $target_post_title_id = $this->get_field_id( 'target_post_title' ); |
| 92 | $target_post_title_name = $this->get_field_name( 'target_post_title' ); |
| 93 | |
| 94 | $listing_post_type = $instance['post_type'] ?? 'page'; |
| 95 | $listing_post_type_id = $this->get_field_id( 'post_type' ); |
| 96 | $listing_post_type_name = $this->get_field_name( 'post_type' ); |
| 97 | |
| 98 | $listing_parent_post = $instance['parent_post'] ?? ''; |
| 99 | $listing_parent_post_id = $this->get_field_id( 'parent_post' ); |
| 100 | $listing_parent_post_name = $this->get_field_name( 'parent_post' ); |
| 101 | $listing_parent_post_title = isset( $instance['parent_post_title'] ) ? $instance['parent_post_title'] : ( ( 0 < $listing_parent_post ) ? get_the_title( $listing_parent_post ) : '' ); |
| 102 | $listing_parent_post_title_id = $this->get_field_id( 'parent_post_title' ); |
| 103 | $listing_parent_post_title_name = $this->get_field_name( 'parent_post_title' ); |
| 104 | |
| 105 | $listing_all_children = $instance['all_children'] ?? 'true'; |
| 106 | $listing_all_children_id = $this->get_field_id( 'all_children' ); |
| 107 | $listing_all_children_name = $this->get_field_name( 'all_children' ); |
| 108 | |
| 109 | $listing_taxonomy = $instance['taxonomy'] ?? 'page'; |
| 110 | $listing_taxonomy_id = $this->get_field_id( 'taxonomy' ); |
| 111 | $listing_taxonomy_name = $this->get_field_name( 'taxonomy' ); |
| 112 | |
| 113 | $listing_parent_term = $instance['parent_term'] ?? ''; |
| 114 | $listing_parent_term_id = $this->get_field_id( 'parent_term' ); |
| 115 | $listing_parent_term_name = $this->get_field_name( 'parent_term' ); |
| 116 | |
| 117 | $listing_terms_include = $instance['terms'] ?? ''; |
| 118 | $listing_terms_include_id = $this->get_field_id( 'terms' ); |
| 119 | $listing_terms_include_name = $this->get_field_name( 'terms' ); |
| 120 | |
| 121 | $listing_terms_exclude = $instance['exclude_terms'] ?? ( $instance['terms_exclude'] ?? '' ); |
| 122 | $listing_terms_exclude_id = $this->get_field_id( 'exclude_terms' ); |
| 123 | $listing_terms_exclude_name = $this->get_field_name( 'exclude_terms' ); |
| 124 | |
| 125 | $listing_hide_empty_terms = $instance['hide_empty_terms'] ?? ''; |
| 126 | $listing_hide_empty_terms_id = $this->get_field_id( 'hide_empty_terms' ); |
| 127 | $listing_hide_empty_terms_name = $this->get_field_name( 'hide_empty_terms' ); |
| 128 | ?> |
| 129 | |
| 130 | <div class="alphalisting-widget"> |
| 131 | <?php wp_nonce_field( 'posts-by-title', '_posts_by_title_wpnonce', false, true ); ?> |
| 132 | <div class="alphalisting-widget-title-wrapper"> |
| 133 | <p> |
| 134 | <label for="<?php echo esc_attr( $widget_title_id ); ?>"> |
| 135 | <?php esc_html_e( 'Widget Title', 'alphalisting' ); ?> |
| 136 | </label> |
| 137 | <input type="text" class="widefat alphalisting-title" |
| 138 | id="<?php echo esc_attr( $widget_title_id ); ?>" |
| 139 | name="<?php echo esc_attr( $widget_title_name ); ?>" |
| 140 | placeholder="<?php esc_attr_e( 'Widget Title', 'alphalisting' ); ?>" |
| 141 | value="<?php echo esc_attr( $widget_title ); ?>" /> |
| 142 | </p> |
| 143 | <p style="color: #333;"> |
| 144 | <?php esc_html_e( 'Leave the title field blank, above, to use the title from the page set in the next field', 'alphalisting' ); ?> |
| 145 | </p> |
| 146 | </div> |
| 147 | |
| 148 | <div class="alphalisting-target-post-wrapper"> |
| 149 | <p> |
| 150 | <label for="<?php echo esc_attr( $target_post_title_id ); ?>"> |
| 151 | <?php esc_html_e( 'Sitemap A-Z page', 'alphalisting' ); ?> |
| 152 | </label> |
| 153 | <input type="text" class="widefat alphalisting-target-post-title" |
| 154 | id="<?php echo esc_attr( $target_post_title_id ); ?>" |
| 155 | name="<?php echo esc_attr( $target_post_title_name ); ?>" |
| 156 | value="<?php echo esc_attr( $target_post_title ); ?>" /> |
| 157 | <input type="hidden" |
| 158 | id="<?php echo esc_attr( $target_post_id ); ?>" |
| 159 | name="<?php echo esc_attr( $target_post_name ); ?>" |
| 160 | value="<?php echo esc_attr( $target_post ); ?>" /> |
| 161 | </p> |
| 162 | <p> |
| 163 | <?php esc_html_e( 'Type some or all of the title of the page you want links to point at. Ensure this input field is not selected when you save the settings.', 'alphalisting' ); ?> |
| 164 | <?php esc_html_e( 'Matching posts will be shown as you type. Click on the correct post from the matches to update the setting.', 'alphalisting' ); ?> |
| 165 | </p> |
| 166 | </div> |
| 167 | |
| 168 | <div class="alphalisting-display-type-wrapper"> |
| 169 | <p> |
| 170 | <label for="<?php echo esc_attr( $display_type_id ); ?>"> |
| 171 | <?php esc_html_e( 'Display posts or terms?', 'alphalisting' ); ?> |
| 172 | </label> |
| 173 | <select class="widefat alphalisting-display-type" |
| 174 | id="<?php echo esc_attr( $display_type_id ); ?>" |
| 175 | name="<?php echo esc_attr( $display_type_name ); ?>"> |
| 176 | <option value="posts" |
| 177 | <?php echo ( 'terms' !== $display_type ) ? 'selected' : ''; ?>> |
| 178 | <?php esc_html_e( 'Posts', 'alphalisting' ); ?> |
| 179 | </option> |
| 180 | <option value="terms" |
| 181 | <?php echo ( 'terms' === $display_type ) ? 'selected' : ''; ?>> |
| 182 | <?php esc_html_e( 'Taxonomy terms', 'alphalisting' ); ?> |
| 183 | </option> |
| 184 | </select> |
| 185 | </p> |
| 186 | </div> |
| 187 | |
| 188 | <div class="alphalisting-post-type-wrapper" <?php echo ( 'terms' !== $display_type ) ? '' : 'style="display: none;"'; ?>> |
| 189 | <p> |
| 190 | <label for="<?php echo esc_attr( $listing_post_type_id ); ?>"> |
| 191 | <?php esc_html_e( 'Post-type to display', 'alphalisting' ); ?> |
| 192 | </label> |
| 193 | <select class="widefat alphalisting-post-type" |
| 194 | id="<?php echo esc_attr( $listing_post_type_id ); ?>" |
| 195 | name="<?php echo esc_attr( $listing_post_type_name ); ?>" |
| 196 | <?php echo ( 'terms' !== $display_type ) ? '' : 'disabled'; ?>> |
| 197 | <?php foreach ( $public_post_types as $k => $t ) : ?> |
| 198 | <option value="<?php echo esc_attr( $k ); ?>" |
| 199 | <?php echo ( $k === $listing_post_type ) ? 'selected' : ''; ?>> |
| 200 | <?php echo esc_html( $t->labels->name ); ?> |
| 201 | </option> |
| 202 | <?php endforeach; ?> |
| 203 | </select> |
| 204 | </p> |
| 205 | </div> |
| 206 | |
| 207 | <div class="alphalisting-parent-post-wrapper" <?php echo ( 'terms' !== $display_type ) ? '' : 'style="display: none"'; ?>> |
| 208 | <p> |
| 209 | <label for="<?php echo esc_attr( $listing_parent_post_title_id ); ?>"> |
| 210 | <?php esc_html_e( 'Show only children of this post (ID)', 'alphalisting' ); ?> |
| 211 | </label> |
| 212 | <input type="text" class="widefat alphalisting-parent-post-title" |
| 213 | id="<?php echo esc_attr( $listing_parent_post_title_id ); ?>" |
| 214 | name="<?php echo esc_attr( $listing_parent_post_title_name ); ?>" |
| 215 | <?php echo ( 'terms' !== $display_type ) ? '' : 'disabled'; ?> |
| 216 | value="<?php echo esc_attr( $listing_parent_post_title ); ?>" /> |
| 217 | <input type="hidden" |
| 218 | id="<?php echo esc_attr( $listing_parent_post_id ); ?>" |
| 219 | name="<?php echo esc_attr( $listing_parent_post_name ); ?>" |
| 220 | value="<?php echo esc_attr( $listing_parent_post ); ?>" /> |
| 221 | </p> |
| 222 | <p> |
| 223 | <?php esc_html_e( 'Type some or all of the title of the post to limit the listing to only the children of that post. Ensure this input field is not selected when you save the settings.', 'alphalisting' ); ?> |
| 224 | <?php esc_html_e( 'Matching posts will be shown as you type. Click on the correct post from the matches to update the setting.', 'alphalisting' ); ?> |
| 225 | </p> |
| 226 | <p> |
| 227 | <label for="<?php echo esc_attr( $listing_all_children_id ); ?>"> |
| 228 | <?php esc_html_e( 'Include grand-children?', 'alphalisting' ); ?> |
| 229 | </label> |
| 230 | <input type="checkbox" class="alphalisting-all-children" |
| 231 | id="<?php echo esc_attr( $listing_all_children_id ); ?>" |
| 232 | name="<?php echo esc_attr( $listing_all_children_name ); ?>" |
| 233 | <?php echo ( isset( $listing_all_children ) && 'true' === $listing_all_children ) ? 'checked' : ''; ?> /> |
| 234 | </p> |
| 235 | </div> |
| 236 | |
| 237 | <div class="alphalisting-taxonomy-wrapper" <?php echo ( 'terms' === $display_type ) ? '' : 'style="display: none;"'; ?>> |
| 238 | <p> |
| 239 | <label for="<?php echo esc_attr( $listing_taxonomy_id ); ?>"> |
| 240 | <?php esc_html_e( 'Taxonomy to display', 'alphalisting' ); ?> |
| 241 | </label> |
| 242 | <select class="widefat alphalisting-taxonomy" |
| 243 | id="<?php echo esc_attr( $listing_taxonomy_id ); ?>" |
| 244 | name="<?php echo esc_attr( $listing_taxonomy_name ); ?>" |
| 245 | <?php echo ( 'terms' === $display_type ) ? '' : 'disabled'; ?>> |
| 246 | <?php foreach ( $public_taxonomies as $k => $t ) : ?> |
| 247 | <option value="<?php echo esc_attr( $k ); ?>" |
| 248 | <?php echo ( $k === $listing_taxonomy ) ? 'selected' : ''; ?>> |
| 249 | <?php echo esc_html( $t->labels->name ); ?> |
| 250 | </option> |
| 251 | <?php endforeach; ?> |
| 252 | </select> |
| 253 | </p> |
| 254 | </div> |
| 255 | |
| 256 | <div class="alphalisting-parent-term-wrapper" <?php echo ( 'terms' === $display_type ) ? '' : 'style="display: none;"'; ?>> |
| 257 | <p> |
| 258 | <label for="<?php echo esc_attr( $listing_parent_term_id ); ?>"> |
| 259 | <?php esc_html_e( 'Parent term to display children of', 'alphalisting' ); ?> |
| 260 | </label> |
| 261 | <input type="text" class="widefat alphalisting-parent-term" |
| 262 | id="<?php echo esc_attr( $listing_parent_term_id ); ?>" |
| 263 | name="<?php echo esc_attr( $listing_parent_term_name ); ?>" |
| 264 | value="<?php echo esc_attr( $listing_parent_term ); ?>" /> |
| 265 | </p> |
| 266 | </div> |
| 267 | |
| 268 | <div class="alphalisting-include-terms-wrapper"> |
| 269 | <p> |
| 270 | <label for="<?php echo esc_attr( $listing_terms_include_id ); ?>"> |
| 271 | <?php esc_html_e( 'Terms to include (IDs)', 'alphalisting' ); ?> |
| 272 | </label> |
| 273 | <input type="text" class="widefat alphalisting-include-terms" |
| 274 | id="<?php echo esc_attr( $listing_terms_include_id ); ?>" |
| 275 | name="<?php echo esc_attr( $listing_terms_include_name ); ?>" |
| 276 | value="<?php echo esc_attr( $listing_terms_include ); ?>" /> |
| 277 | </p> |
| 278 | </div> |
| 279 | |
| 280 | <div class="alphalisting-exclude-terms-wrapper" <?php echo ( 'terms' === $display_type ) ? '' : 'style="display: none;"'; ?>> |
| 281 | <p> |
| 282 | <label for="<?php echo esc_attr( $listing_terms_exclude_id ); ?>"> |
| 283 | <?php esc_html_e( 'Terms to exclude (IDs)', 'alphalisting' ); ?> |
| 284 | </label> |
| 285 | <input type="text" class="widefat alphalisting-exclude-terms" |
| 286 | id="<?php echo esc_attr( $listing_terms_exclude_id ); ?>" |
| 287 | name="<?php echo esc_attr( $listing_terms_exclude_name ); ?>" |
| 288 | value="<?php echo esc_attr( $listing_terms_exclude ); ?>" /> |
| 289 | </p> |
| 290 | </div> |
| 291 | |
| 292 | <div class="alphalisting-hide-empty-terms-wrapper" <?php echo ( 'terms' === $display_type ) ? '' : 'style="display: none;"'; ?>> |
| 293 | <p> |
| 294 | <label for="<?php echo esc_attr( $listing_hide_empty_terms_id ); ?>"> |
| 295 | <?php esc_html_e( 'Hide empty terms', 'alphalisting' ); ?> |
| 296 | </label> |
| 297 | <input type="checkbox" class="alphalisting-hide-empty-terms" |
| 298 | id="<?php echo esc_attr( $listing_hide_empty_terms_id ); ?>" |
| 299 | name="<?php echo esc_attr( $listing_hide_empty_terms_name ); ?>" |
| 300 | <?php echo ( isset( $listing_hide_empty_terms ) && 'true' === $listing_hide_empty_terms ) ? 'checked' : ''; ?> /> |
| 301 | </p> |
| 302 | </div> |
| 303 | </div> |
| 304 | <?php |
| 305 | return ''; |
| 306 | } |
| 307 | |
| 308 | /** |
| 309 | * Called by WordPress core. Sanitizes changes to the Widget's configuration |
| 310 | * |
| 311 | * @since 0.1 |
| 312 | * @param array<string,mixed> $new_instance the new configuration values. |
| 313 | * @param array<string,mixed> $old_instance the previous configuration values. |
| 314 | * @return array<string,mixed> sanitized version of the new configuration values to be saved |
| 315 | */ |
| 316 | public function update( $new_instance, $old_instance ) { |
| 317 | $instance = $old_instance; |
| 318 | |
| 319 | $target_post_title = wp_strip_all_tags( $new_instance['target_post_title'] ?? '' ); |
| 320 | $parent_post_title = wp_strip_all_tags( $new_instance['parent_post_title'] ?? '' ); |
| 321 | $all_children_input = $new_instance['all_children'] ?? ''; |
| 322 | $hide_empty_input = $new_instance['hide_empty_terms'] ?? ''; |
| 323 | |
| 324 | // The field was historically named `terms_exclude` in form(); read it as a |
| 325 | // fallback so values stored under the old key survive an upgrade. |
| 326 | $exclude_terms_input = $new_instance['exclude_terms'] ?? ( $new_instance['terms_exclude'] ?? '' ); |
| 327 | |
| 328 | $instance['title'] = wp_strip_all_tags( $new_instance['title'] ?? '' ); |
| 329 | $instance['type'] = wp_strip_all_tags( $new_instance['type'] ?? '' ); |
| 330 | $instance['post'] = (int) ( $new_instance['post'] ?? 0 ); // target. |
| 331 | $instance['target_post_title'] = $target_post_title; |
| 332 | $instance['post_type'] = wp_strip_all_tags( $new_instance['post_type'] ?? '' ); |
| 333 | $instance['taxonomy'] = wp_strip_all_tags( $new_instance['taxonomy'] ?? '' ); |
| 334 | $instance['parent_post'] = (int) ( $new_instance['parent_post'] ?? 0 ); |
| 335 | $instance['all_children'] = 'on' === $all_children_input ? 'true' : 'false'; |
| 336 | $instance['parent_term'] = wp_strip_all_tags( $new_instance['parent_term'] ?? '' ); |
| 337 | $instance['terms'] = wp_strip_all_tags( $new_instance['terms'] ?? '' ); |
| 338 | $instance['exclude_terms'] = wp_strip_all_tags( $exclude_terms_input ); |
| 339 | $instance['hide_empty_terms'] = 'on' === $hide_empty_input ? 'true' : 'false'; |
| 340 | |
| 341 | if ( '' === $target_post_title ) { |
| 342 | $instance['post'] = 0; |
| 343 | } |
| 344 | |
| 345 | if ( '' === $parent_post_title ) { |
| 346 | $instance['parent_post'] = 0; |
| 347 | } |
| 348 | |
| 349 | return $instance; |
| 350 | } |
| 351 | |
| 352 | /** |
| 353 | * Print the user-visible widget to the page |
| 354 | * |
| 355 | * @since 0.1 |
| 356 | * @param array<string,mixed> $args General widget configuration. Often shared between all widgets on the site. |
| 357 | * @param array<string,mixed> $instance Configuration of this Widget. Unique to this invocation. |
| 358 | * @return void |
| 359 | */ |
| 360 | public function widget( $args, $instance ) { |
| 361 | the_section_a_z_widget( $args, $instance ); |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | /** |
| 366 | * Print the user-visible widget to the page implementation |
| 367 | * |
| 368 | * @since 0.1 |
| 369 | * @since 0.8.0 deprecated. |
| 370 | * @see A_Z_Widget::the_section_a_z_widget() |
| 371 | * @deprecated use the_section_a_z_widget() |
| 372 | * @param array<string,mixed> $args General widget configuration. Often shared between all widgets on the site. |
| 373 | * @param array<string,mixed> $instance Configuration of this Widget. Unique to this invocation. |
| 374 | * @return void |
| 375 | */ |
| 376 | function the_section_az_widget( array $args, array $instance ) { |
| 377 | _deprecated_function( __FUNCTION__, '0.8.0', 'the_section_a_z_widget' ); |
| 378 | the_section_a_z_widget( $args, $instance ); |
| 379 | } |
| 380 | |
| 381 | /** |
| 382 | * Print the user-visible widget to the page implementation |
| 383 | * |
| 384 | * @since 0.8.0 |
| 385 | * @param array<string,mixed> $args General widget configuration. Often shared between all widgets on the site. |
| 386 | * @param array<string,mixed> $instance Configuration of this Widget. Unique to this invocation. |
| 387 | * @return void |
| 388 | */ |
| 389 | function the_section_a_z_widget( array $args, array $instance ) { //phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound |
| 390 | echo get_the_section_a_z_widget( $args, $instance ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 391 | } |
| 392 | |
| 393 | /** |
| 394 | * Deprecated print the user-visible widget to the page implementation. |
| 395 | * |
| 396 | * @since 0.1 |
| 397 | * @since 0.8.0 deprecated. |
| 398 | * @see A_Z_Widget::get_the_section_a_z_widget() |
| 399 | * @deprecated use get_the_section_a_z_widget() |
| 400 | * @param array<string,mixed> $args General widget configuration. Often shared between all widgets on the site. |
| 401 | * @param array<string,mixed> $instance Configuration of this Widget. Unique to this invocation. |
| 402 | * @return string The complete A-Z Widget HTML ready for echoing to the page. |
| 403 | */ |
| 404 | function get_the_section_az_widget( array $args, array $instance ): string { |
| 405 | _deprecated_function( __FUNCTION__, '0.8.0', 'get_the_section_a_z_widget' ); |
| 406 | return get_the_section_a_z_widget( $args, $instance ); |
| 407 | } |
| 408 | |
| 409 | /** |
| 410 | * Get the user-visible widget html |
| 411 | * |
| 412 | * @since 0.8.0 |
| 413 | * @param array<string,mixed> $args General widget configuration. Often shared between all widgets on the site. |
| 414 | * @param array<string,mixed> $instance Configuration of this Widget. Unique to this invocation. |
| 415 | * @return string The complete A-Z Widget HTML ready for echoing to the page. |
| 416 | */ |
| 417 | function get_the_section_a_z_widget( array $args, array $instance ): string { //phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound |
| 418 | do_action( 'alphalisting_log', 'AlphaListing: Running widget' ); |
| 419 | |
| 420 | $instance = wp_parse_args( |
| 421 | $instance, |
| 422 | array( |
| 423 | 'all_children' => 'true', |
| 424 | 'exclude_terms' => '', |
| 425 | 'hide_empty_terms' => false, |
| 426 | 'parent_post' => '', |
| 427 | 'parent_term' => '', |
| 428 | 'post' => -1, // target. |
| 429 | 'page' => -1, // obsolete target. |
| 430 | 'post_type' => 'page', |
| 431 | 'taxonomy' => '', |
| 432 | 'terms' => '', |
| 433 | 'title' => '', |
| 434 | 'type' => 'posts', |
| 435 | ) |
| 436 | ); |
| 437 | |
| 438 | $title = esc_html( $instance['title'] ); |
| 439 | $target_url = ''; |
| 440 | if ( 0 < $instance['post'] || 0 < $instance['page'] ) { // target. |
| 441 | $target_id = (int) $instance['post']; // target. |
| 442 | if ( ! ( 0 < $instance['post'] ) ) { |
| 443 | $target_id = (int) $instance['page']; // obsolete target. |
| 444 | } |
| 445 | |
| 446 | $target_url = get_the_permalink( $target_id ); |
| 447 | if ( empty( $title ) ) { |
| 448 | $title = get_the_title( $target_id ); |
| 449 | } |
| 450 | } elseif ( empty( $title ) ) { |
| 451 | $title = esc_html__( 'A-Z Site Map', 'alphalisting' ); |
| 452 | } |
| 453 | |
| 454 | // `update()` stores this as the string 'true'/'false' while `wp_parse_args()` |
| 455 | // above defaults it to a boolean, so compare truthiness rather than identity. |
| 456 | $hide_empty_terms = alphalisting_is_truthy( $instance['hide_empty_terms'] ) ? 'true' : 'false'; |
| 457 | |
| 458 | $ret = ''; |
| 459 | $ret .= $args['before_widget']; |
| 460 | $ret .= '<div class="alphalisting-widget widget">'; |
| 461 | |
| 462 | $ret .= $args['before_title']; |
| 463 | $ret .= $title; |
| 464 | $ret .= $args['after_title']; |
| 465 | |
| 466 | /* |
| 467 | * Pass the attributes to the shortcode handler directly rather than building a |
| 468 | * `[alphalisting ...]` string. Interpolating stored widget values into shortcode |
| 469 | * syntax is unsafe -- a `'` closes the attribute early and a `]` terminates the |
| 470 | * shortcode, letting following text be parsed as new shortcodes -- and no escaper |
| 471 | * covers both characters. This is the same mechanism GutenBlock::render() uses, |
| 472 | * so the widget, block, and shortcode all share one render path. |
| 473 | */ |
| 474 | $attributes = array( |
| 475 | 'alphabet' => '', |
| 476 | 'display' => (string) $instance['type'], |
| 477 | 'exclude-posts' => '', |
| 478 | 'exclude-terms' => (string) $instance['exclude_terms'], |
| 479 | 'get-all-children' => (string) $instance['all_children'], |
| 480 | 'group-numbers' => '', |
| 481 | 'grouping' => '', |
| 482 | 'hide-empty-terms' => $hide_empty_terms, |
| 483 | 'numbers' => 'hide', |
| 484 | 'parent-post' => (string) $instance['parent_post'], |
| 485 | 'parent-term' => (string) $instance['parent_term'], |
| 486 | 'post-type' => (string) $instance['post_type'], |
| 487 | 'return' => 'letters', |
| 488 | 'target' => (string) $target_url, |
| 489 | 'taxonomy' => (string) $instance['taxonomy'], |
| 490 | 'terms' => (string) $instance['terms'], |
| 491 | ); |
| 492 | |
| 493 | global $shortcode_tags; |
| 494 | if ( isset( $shortcode_tags['alphalisting'] ) ) { |
| 495 | $ret .= call_user_func( $shortcode_tags['alphalisting'], $attributes ); |
| 496 | } |
| 497 | |
| 498 | $ret .= '</div>'; |
| 499 | $ret .= $args['after_widget']; |
| 500 | |
| 501 | return $ret; |
| 502 | } |
| 503 | |
| 504 | /** |
| 505 | * Replace the WP_Query search parameters to search just the title. |
| 506 | * |
| 507 | * @since 4.0.0 |
| 508 | * @param string $search The search database query snippet. |
| 509 | * @param WP_Query $wp_query The WP_Query. |
| 510 | * @return string The updated search database query snippet. |
| 511 | */ |
| 512 | function alphalisting_search_titles_only( $search, $wp_query ) { |
| 513 | if ( empty( $search ) || empty( $wp_query->query_vars['search_terms'] ) ) { |
| 514 | return $search; |
| 515 | } |
| 516 | |
| 517 | global $wpdb; |
| 518 | $search = array(); |
| 519 | $params = $wp_query->query_vars; |
| 520 | |
| 521 | $n = empty( $params['exact'] ) ? '%' : ''; |
| 522 | foreach ( $params['search_terms'] as $term ) { |
| 523 | $search[] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $n . $wpdb->esc_like( $term ) . $n ); |
| 524 | } |
| 525 | |
| 526 | if ( ! is_user_logged_in() ) { |
| 527 | $search[] = "$wpdb->posts.post_password = ''"; |
| 528 | } |
| 529 | |
| 530 | return ' AND ' . implode( ' AND ', $search ); |
| 531 | } |
| 532 | |
| 533 | /** |
| 534 | * Retrieve posts by title. |
| 535 | * |
| 536 | * @since 2.1.0 |
| 537 | * @since 4.0.0 Use WP_Query |
| 538 | * @param string $post_title the title to search for. |
| 539 | * @param string $post_type the post type to search within. |
| 540 | * @return array<int,object> the post IDs that are found. |
| 541 | */ |
| 542 | function alphalisting_get_posts_by_title( string $post_title, string $post_type = '' ): array { |
| 543 | $params = array( |
| 544 | 's' => $post_title, |
| 545 | 'update_post_meta_cache' => false, |
| 546 | 'update_post_term_cache' => false, |
| 547 | ); |
| 548 | if ( ! empty( $post_type ) ) { |
| 549 | $params['post_type'] = $post_type; |
| 550 | } |
| 551 | |
| 552 | add_filter( 'posts_search', __NAMESPACE__ . '\\alphalisting_search_titles_only', 10, 2 ); |
| 553 | $query = new \WP_Query( $params ); |
| 554 | $results = $query->posts; |
| 555 | remove_filter( 'posts_search', __NAMESPACE__ . '\\alphalisting_search_titles_only' ); |
| 556 | |
| 557 | return $results; |
| 558 | } |
| 559 | |
| 560 | /** |
| 561 | * Ajax responder for AlphaListing_Widget configuration |
| 562 | * |
| 563 | * @since 2.0.0 |
| 564 | * @return void |
| 565 | */ |
| 566 | function alphalisting_get_autocomplete_post_titles() { |
| 567 | $nonce = ''; |
| 568 | if ( isset( $_REQUEST['_posts_by_title_wpnonce'] ) ) { |
| 569 | $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_posts_by_title_wpnonce'] ) ); |
| 570 | } |
| 571 | if ( ! check_ajax_referer( 'posts-by-title', '_posts_by_title_wpnonce', false ) || ! wp_verify_nonce( $nonce, 'posts-by-title' ) ) { |
| 572 | wp_send_json_error( |
| 573 | array( |
| 574 | 'message' => __( 'Security check failed.', 'alphalisting' ), |
| 575 | ), |
| 576 | 403 |
| 577 | ); |
| 578 | } |
| 579 | |
| 580 | if ( ! current_user_can( 'edit_theme_options' ) ) { |
| 581 | wp_send_json_error( |
| 582 | array( |
| 583 | 'message' => __( 'You are not allowed to perform this action.', 'alphalisting' ), |
| 584 | ), |
| 585 | 403 |
| 586 | ); |
| 587 | } |
| 588 | |
| 589 | $post_title = ''; |
| 590 | $post_type = ''; |
| 591 | if ( isset( $_POST['post_title']['term'] ) ) { |
| 592 | $post_title = sanitize_text_field( wp_unslash( $_POST['post_title']['term'] ) ); |
| 593 | } else { |
| 594 | wp_send_json_error( |
| 595 | array( |
| 596 | 'message' => __( 'Invalid request payload.', 'alphalisting' ), |
| 597 | ), |
| 598 | 400 |
| 599 | ); |
| 600 | } |
| 601 | if ( isset( $_POST['post_type'] ) ) { |
| 602 | $post_type = sanitize_text_field( wp_unslash( $_POST['post_type'] ) ); |
| 603 | } |
| 604 | |
| 605 | $results = alphalisting_get_posts_by_title( $post_title, $post_type ); |
| 606 | |
| 607 | $titles = array(); |
| 608 | foreach ( $results as $result ) { |
| 609 | $titles[] = array( |
| 610 | 'value' => intval( $result->ID ), |
| 611 | 'label' => $result->post_title, |
| 612 | ); |
| 613 | } |
| 614 | |
| 615 | wp_send_json( $titles ); |
| 616 | } |
| 617 | |
| 618 | add_action( 'wp_ajax_alphalisting_get_autocomplete_post_titles', __NAMESPACE__ . '\\alphalisting_get_autocomplete_post_titles' ); |
| 619 | |
| 620 | /** |
| 621 | * Register the A_Z_Widget widget |
| 622 | * |
| 623 | * @since 2.0.0 |
| 624 | * @return void |
| 625 | */ |
| 626 | function alphalisting_widget() { |
| 627 | register_widget( __NAMESPACE__ . '\\AlphaListing_Widget' ); |
| 628 | } |
| 629 | add_action( 'widgets_init', __NAMESPACE__ . '\\alphalisting_widget' ); |
| 630 |