Widget.php
471 lines
| 1 | <?php |
| 2 | |
| 3 | namespace WordPressPopularPosts\Widget; |
| 4 | |
| 5 | use WordPressPopularPosts\{ Helper, Image, Output, Themer, Translate }; |
| 6 | use WordPressPopularPosts\Traits\QueriesPosts; |
| 7 | |
| 8 | class Widget extends \WP_Widget { |
| 9 | |
| 10 | use QueriesPosts; |
| 11 | |
| 12 | /** |
| 13 | * Default options. |
| 14 | * |
| 15 | * @since 5.0.0 |
| 16 | * @var array |
| 17 | */ |
| 18 | private $defaults = []; |
| 19 | |
| 20 | /** |
| 21 | * Administrative settings. |
| 22 | * |
| 23 | * @since 2.3.3 |
| 24 | * @var array |
| 25 | */ |
| 26 | private $config = []; |
| 27 | |
| 28 | /** |
| 29 | * Image object. |
| 30 | * |
| 31 | * @since 5.0.0 |
| 32 | * @var WordPressPopularPosts\Image |
| 33 | */ |
| 34 | private $thumbnail; |
| 35 | |
| 36 | /** |
| 37 | * Output object. |
| 38 | * |
| 39 | * @var \WordPressPopularPosts\Output |
| 40 | * @access private |
| 41 | */ |
| 42 | private $output; |
| 43 | |
| 44 | /** |
| 45 | * Translate object. |
| 46 | * |
| 47 | * @var \WordPressPopularPosts\Translate $translate |
| 48 | * @access private |
| 49 | */ |
| 50 | private $translate; |
| 51 | |
| 52 | /** |
| 53 | * Themer object. |
| 54 | * |
| 55 | * @var \WordPressPopularPosts\Themer $themer |
| 56 | * @access private |
| 57 | */ |
| 58 | private $themer; |
| 59 | |
| 60 | /** |
| 61 | * Construct. |
| 62 | * |
| 63 | * @since 1.0.0 |
| 64 | * @param array $options |
| 65 | * @param array $config |
| 66 | * @param \WordPressPopularPosts\Output $output |
| 67 | * @param \WordPressPopularPosts\Image $image |
| 68 | * @param \WordPressPopularPosts\Translate $translate |
| 69 | * @param \WordPressPopularPosts\Themer $themer |
| 70 | */ |
| 71 | public function __construct(array $options, array $config, Output $output, Image $thumbnail, Translate $translate, Themer $themer) |
| 72 | { |
| 73 | // Create the widget |
| 74 | parent::__construct( |
| 75 | 'wpp', |
| 76 | 'WordPress Popular Posts', |
| 77 | [ |
| 78 | 'classname' => 'popular-posts', |
| 79 | 'description' => __('The most Popular Posts on your blog.', 'wordpress-popular-posts') |
| 80 | ] |
| 81 | ); |
| 82 | |
| 83 | $this->defaults = $options; |
| 84 | $this->config = $config; |
| 85 | $this->output = $output; |
| 86 | $this->thumbnail = $thumbnail; |
| 87 | $this->translate = $translate; |
| 88 | $this->themer = $themer; |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Widget hooks. |
| 93 | * |
| 94 | * @since 5.0.0 |
| 95 | */ |
| 96 | public function hooks() |
| 97 | { |
| 98 | // Register the widget |
| 99 | add_action('widgets_init', [$this, 'register']); |
| 100 | // Remove widget from Legacy Widget block |
| 101 | add_filter('widget_types_to_hide_from_legacy_widget_block', [$this, 'remove_from_legacy_widget_block']); |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Registers the widget. |
| 106 | * |
| 107 | * @since 5.0.0 |
| 108 | */ |
| 109 | public function register() |
| 110 | { |
| 111 | register_widget($this); |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * Outputs the content of the widget. |
| 116 | * |
| 117 | * @since 1.0.0 |
| 118 | * @param array $args The array of form elements. |
| 119 | * @param array $instance The current instance of the widget. |
| 120 | */ |
| 121 | public function widget($args, $instance) |
| 122 | { |
| 123 | /** |
| 124 | * @var string $name |
| 125 | * @var string $id |
| 126 | * @var string $description |
| 127 | * @var string $class |
| 128 | * @var string $before_widget |
| 129 | * @var string $after_widget |
| 130 | * @var string $before_title |
| 131 | * @var string $after_title |
| 132 | * @var string $widget_id |
| 133 | * @var string $widget_name |
| 134 | */ |
| 135 | extract($args, EXTR_SKIP); |
| 136 | |
| 137 | $instance = Helper::merge_array_r( |
| 138 | $this->defaults, |
| 139 | (array) $instance |
| 140 | ); |
| 141 | |
| 142 | echo "\n" . $before_widget . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 143 | |
| 144 | // Has user set a title? |
| 145 | if ( '' != $instance['title'] ) { |
| 146 | $title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base); |
| 147 | |
| 148 | if ( |
| 149 | $instance['markup']['custom_html'] |
| 150 | && $instance['markup']['title-start'] != '' |
| 151 | && $instance['markup']['title-end'] != '' |
| 152 | ) { |
| 153 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 154 | echo htmlspecialchars_decode($instance['markup']['title-start'], ENT_QUOTES) . $title . htmlspecialchars_decode($instance['markup']['title-end'], ENT_QUOTES); |
| 155 | } else { |
| 156 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 157 | echo $before_title . $title . $after_title; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | // Expose Widget ID & base for customization |
| 162 | $instance['widget_id'] = $widget_id; |
| 163 | $instance['id_base'] = $this->id_base; |
| 164 | |
| 165 | // Get posts |
| 166 | if ( $this->config['tools']['ajax'] && ! is_customize_preview() ) { |
| 167 | ?> |
| 168 | <div class="wpp-widget-placeholder" data-widget-id="<?php echo esc_attr($widget_id); ?>"></div> |
| 169 | <?php |
| 170 | } else { |
| 171 | $notice = ''; |
| 172 | |
| 173 | if ( is_user_logged_in() && current_user_can('manage_options') ) { |
| 174 | ob_start(); |
| 175 | ?> |
| 176 | <style> |
| 177 | .wpp-notice { |
| 178 | margin: 0 0 22px; |
| 179 | padding: 18px 22px; |
| 180 | background: #fcfcf7; |
| 181 | border: #ffff63 4px solid; |
| 182 | } |
| 183 | |
| 184 | .wpp-notice p:nth-child(2n) { |
| 185 | margin: 0; |
| 186 | font-size: 0.85em; |
| 187 | } |
| 188 | </style> |
| 189 | <div class="wpp-notice"> |
| 190 | <p><strong>Important notice for administrators:</strong> The WordPress Popular Posts "classic" widget is going away!</p> |
| 191 | <p>This widget has been deprecated and will be removed in version 7.0 to be released sometime around June 2024. Please use either the WordPress Popular Posts block or the wpp shortcode instead.</p> |
| 192 | </div> |
| 193 | <?php |
| 194 | $notice = ob_get_clean() . "\n"; |
| 195 | } |
| 196 | |
| 197 | echo $notice; |
| 198 | |
| 199 | $this->get_popular($instance); |
| 200 | } |
| 201 | |
| 202 | echo "\n" . $after_widget . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 203 | } |
| 204 | |
| 205 | /** |
| 206 | * Generates the administration form for the widget. |
| 207 | * |
| 208 | * @since 1.0.0 |
| 209 | * @param array $instance The array of keys and values for the widget. |
| 210 | */ |
| 211 | public function form($instance) |
| 212 | { |
| 213 | $instance = Helper::merge_array_r( |
| 214 | $this->defaults, |
| 215 | (array) $instance |
| 216 | ); |
| 217 | require plugin_dir_path(__FILE__) . '/form.php'; |
| 218 | } |
| 219 | |
| 220 | /** |
| 221 | * Processes the widget's options to be saved. |
| 222 | * |
| 223 | * @since 1.0.0 |
| 224 | * @param array $new_instance The previous instance of values before the update. |
| 225 | * @param array $old_instance The new instance of values to be generated via the update. |
| 226 | * @return array $instance Updated instance. |
| 227 | */ |
| 228 | public function update($new_instance, $old_instance) |
| 229 | { |
| 230 | if ( empty($old_instance) ) { |
| 231 | $old_instance = $this->defaults; |
| 232 | } else { |
| 233 | $old_instance = Helper::merge_array_r( |
| 234 | $this->defaults, |
| 235 | (array) $old_instance |
| 236 | ); |
| 237 | } |
| 238 | |
| 239 | $instance = $old_instance; |
| 240 | |
| 241 | $instance['title'] = htmlspecialchars(stripslashes_deep(strip_tags($new_instance['title'])), ENT_QUOTES); |
| 242 | $instance['limit'] = ( Helper::is_number($new_instance['limit']) && $new_instance['limit'] > 0 ) |
| 243 | ? $new_instance['limit'] |
| 244 | : 10; |
| 245 | $instance['range'] = $new_instance['range']; |
| 246 | $instance['time_quantity'] = ( Helper::is_number($new_instance['time_quantity']) && $new_instance['time_quantity'] > 0 ) |
| 247 | ? $new_instance['time_quantity'] |
| 248 | : 24; |
| 249 | $instance['time_unit'] = $new_instance['time_unit']; |
| 250 | $instance['order_by'] = $new_instance['order_by']; |
| 251 | |
| 252 | // FILTERS |
| 253 | // user did not set a post type name, so we fall back to default |
| 254 | $instance['post_type'] = ( '' == $new_instance['post_type'] ) |
| 255 | ? 'post' |
| 256 | : $new_instance['post_type']; |
| 257 | |
| 258 | $instance['freshness'] = isset($new_instance['freshness']); |
| 259 | |
| 260 | // Post / Page / CTP filter |
| 261 | $ids = array_filter(explode(',', rtrim(preg_replace('|[^0-9,]|', '', $new_instance['pid']), ',')), 'is_numeric'); |
| 262 | // Got no valid IDs, clear |
| 263 | if ( empty($ids) ) { |
| 264 | $instance['pid'] = ''; |
| 265 | } |
| 266 | else { |
| 267 | $instance['pid'] = implode(',', $ids); |
| 268 | } |
| 269 | |
| 270 | // Taxonomy filter |
| 271 | $taxonomies = $new_instance['taxonomy']; |
| 272 | |
| 273 | if ( isset($taxonomies['names']) ) { |
| 274 | // Remove taxonomies that don't have any valid term IDs |
| 275 | foreach( $taxonomies['terms'] as $taxonomy => $terms ) { |
| 276 | $taxonomies['terms'][$taxonomy] = array_filter( |
| 277 | explode(',', trim(preg_replace('|[^0-9,-]|', '', $taxonomies['terms'][$taxonomy]), ', ')), |
| 278 | 'is_numeric' |
| 279 | ); |
| 280 | |
| 281 | if ( |
| 282 | empty($taxonomies['terms'][$taxonomy]) |
| 283 | || ! in_array($taxonomy, $taxonomies['names']) |
| 284 | ) { |
| 285 | unset($taxonomies['terms'][$taxonomy]); |
| 286 | } else { |
| 287 | $taxonomies['terms'][$taxonomy] = implode(',', $taxonomies['terms'][$taxonomy]); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | if ( ! empty($taxonomies['terms']) ) { |
| 292 | $instance['taxonomy'] = implode(';', array_keys($taxonomies['terms'])); |
| 293 | $instance['term_id'] = implode(';', array_values($taxonomies['terms'])); |
| 294 | } |
| 295 | } // Discard everything |
| 296 | else { |
| 297 | $instance['taxonomy'] = ''; |
| 298 | $instance['term_id'] = ''; |
| 299 | } |
| 300 | |
| 301 | // Author filter |
| 302 | $ids = array_filter(explode(',', rtrim(preg_replace('|[^0-9,]|', '', $new_instance['uid']), ',')), 'is_numeric'); |
| 303 | // Got no valid IDs, clear |
| 304 | if ( empty($ids) ) { |
| 305 | $instance['author'] = ''; |
| 306 | } |
| 307 | else { |
| 308 | $instance['author'] = implode( ',', $ids ); |
| 309 | } |
| 310 | |
| 311 | $instance['shorten_title']['words'] = $new_instance['shorten_title-words']; |
| 312 | $instance['shorten_title']['active'] = isset($new_instance['shorten_title-active']); |
| 313 | $instance['shorten_title']['length'] = ( Helper::is_number($new_instance['shorten_title-length']) && $new_instance['shorten_title-length'] > 0 ) |
| 314 | ? $new_instance['shorten_title-length'] |
| 315 | : 25; |
| 316 | |
| 317 | $instance['post-excerpt']['keep_format'] = isset($new_instance['post-excerpt-format']); |
| 318 | $instance['post-excerpt']['words'] = $new_instance['post-excerpt-words']; |
| 319 | $instance['post-excerpt']['active'] = isset($new_instance['post-excerpt-active']); |
| 320 | $instance['post-excerpt']['length'] = ( Helper::is_number($new_instance['post-excerpt-length']) && $new_instance['post-excerpt-length'] > 0 ) |
| 321 | ? $new_instance['post-excerpt-length'] |
| 322 | : 55; |
| 323 | |
| 324 | $instance['thumbnail']['active'] = isset($new_instance['thumbnail-active']); |
| 325 | $instance['thumbnail']['build'] = $new_instance['thumbnail-size-source']; |
| 326 | $instance['thumbnail']['width'] = 75; |
| 327 | $instance['thumbnail']['height'] = 75; |
| 328 | |
| 329 | // Use predefined thumbnail sizes |
| 330 | if ( 'predefined' == $new_instance['thumbnail-size-source'] ) { |
| 331 | $default_thumbnail_sizes = $this->thumbnail->get_sizes(null); |
| 332 | $size = $default_thumbnail_sizes[$new_instance['thumbnail-size']]; |
| 333 | |
| 334 | $instance['thumbnail']['width'] = $size['width']; |
| 335 | $instance['thumbnail']['height'] = $size['height']; |
| 336 | $instance['thumbnail']['crop'] = $size['crop']; |
| 337 | } // Set thumbnail size manually |
| 338 | else { |
| 339 | if ( Helper::is_number($new_instance['thumbnail-width']) && Helper::is_number($new_instance['thumbnail-height']) ) { |
| 340 | $instance['thumbnail']['width'] = $new_instance['thumbnail-width']; |
| 341 | $instance['thumbnail']['height'] = $new_instance['thumbnail-height']; |
| 342 | $instance['thumbnail']['crop'] = true; |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | $instance['rating'] = isset($new_instance['rating']); |
| 347 | $instance['stats_tag']['comment_count'] = isset($new_instance['comment_count']); |
| 348 | $instance['stats_tag']['views'] = isset($new_instance['views']); |
| 349 | $instance['stats_tag']['author'] = isset($new_instance['author']); |
| 350 | $instance['stats_tag']['date']['active'] = isset($new_instance['date']); |
| 351 | $instance['stats_tag']['date']['format'] = empty($new_instance['date_format']) |
| 352 | ? 'F j, Y' |
| 353 | : $new_instance['date_format']; |
| 354 | |
| 355 | $instance['stats_tag']['taxonomy']['active'] = isset($new_instance['stats_taxonomy']); |
| 356 | $instance['stats_tag']['taxonomy']['name'] = isset($new_instance['stats_taxonomy_name']) ? $new_instance['stats_taxonomy_name'] : 'category'; |
| 357 | $instance['stats_tag']['category'] = isset($new_instance['stats_taxonomy'] ); // Deprecated in 4.0.0! |
| 358 | |
| 359 | $instance['markup']['custom_html'] = isset($new_instance['custom_html']); |
| 360 | $instance['markup']['wpp-start'] = empty($new_instance['wpp-start']) |
| 361 | ? ! $old_instance['markup']['custom_html'] && $instance['markup']['custom_html'] ? htmlspecialchars('<ul class="wpp-list">', ENT_QUOTES) : '' |
| 362 | : htmlspecialchars($new_instance['wpp-start'], ENT_QUOTES); |
| 363 | |
| 364 | $instance['markup']['wpp-end'] = empty($new_instance['wpp-end']) |
| 365 | ? ! $old_instance['markup']['custom_html'] && $instance['markup']['custom_html'] ? htmlspecialchars('</ul>', ENT_QUOTES) : '' |
| 366 | : htmlspecialchars($new_instance['wpp-end'], ENT_QUOTES); |
| 367 | |
| 368 | $instance['markup']['post-html'] = empty($new_instance['post-html']) |
| 369 | ? htmlspecialchars('<li>{thumb} {title} {stats}</li>', ENT_QUOTES) |
| 370 | : htmlspecialchars($new_instance['post-html'], ENT_QUOTES); |
| 371 | |
| 372 | $instance['markup']['title-start'] = empty($new_instance['title-start']) |
| 373 | ? ! $old_instance['markup']['custom_html'] && $instance['markup']['custom_html'] ? '<h2>' : '' |
| 374 | : htmlspecialchars($new_instance['title-start'], ENT_QUOTES); |
| 375 | |
| 376 | $instance['markup']['title-end'] = empty($new_instance['title-end']) |
| 377 | ? ! $old_instance['markup']['custom_html'] && $instance['markup']['custom_html'] ? '</h2>' : '' : |
| 378 | htmlspecialchars($new_instance['title-end'], ENT_QUOTES); |
| 379 | |
| 380 | $instance['theme'] = [ |
| 381 | 'name' => isset($new_instance['theme']) ? $new_instance['theme'] : '', |
| 382 | 'applied' => isset($new_instance['theme']) ? (bool) $new_instance['theme-applied'] : false |
| 383 | ]; |
| 384 | |
| 385 | if ( ! isset($new_instance['theme']) || $old_instance['theme']['name'] != $new_instance['theme'] ) { |
| 386 | $instance['theme']['applied'] = false; |
| 387 | } |
| 388 | |
| 389 | // On the new Widgets screen $new_instance['theme'] is |
| 390 | // an array for some reason, let's grab the theme name |
| 391 | // from the array and move on |
| 392 | if ( is_array($instance['theme']['name']) ) { |
| 393 | $instance['theme']['name'] = $instance['theme']['name']['name']; |
| 394 | } |
| 395 | |
| 396 | $theme = $instance['theme']['name'] ? $this->themer->get_theme($instance['theme']['name']) : null; |
| 397 | |
| 398 | if ( |
| 399 | is_array($theme) |
| 400 | && isset($theme['json']) |
| 401 | && isset($theme['json']['config']) |
| 402 | && is_array($theme['json']['config']) |
| 403 | && ! $instance['theme']['applied'] |
| 404 | ) { |
| 405 | $instance = Helper::merge_array_r( |
| 406 | $instance, |
| 407 | $theme['json']['config'] |
| 408 | ); |
| 409 | $instance['markup']['custom_html'] = true; |
| 410 | $instance['theme']['applied'] = true; |
| 411 | |
| 412 | $current_sidebar_data = $this->get_sidebar_data(); |
| 413 | |
| 414 | if ( $current_sidebar_data ) { |
| 415 | $instance['markup']['title-start'] = htmlspecialchars($current_sidebar_data['before_title'], ENT_QUOTES); |
| 416 | $instance['markup']['title-end'] = htmlspecialchars($current_sidebar_data['after_title'], ENT_QUOTES); |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | return $instance; |
| 421 | } |
| 422 | |
| 423 | /** |
| 424 | * Returns HTML list. |
| 425 | * |
| 426 | * @since 2.3.3 |
| 427 | */ |
| 428 | public function get_popular(array $instance) |
| 429 | { |
| 430 | $popular_posts = $this->maybe_query($instance); |
| 431 | |
| 432 | $this->output->set_data($popular_posts->get_posts()); |
| 433 | $this->output->set_public_options($instance); |
| 434 | $this->output->build_output(); |
| 435 | $this->output->output(); |
| 436 | } |
| 437 | |
| 438 | /** |
| 439 | * Returns data on the current sidebar. |
| 440 | * |
| 441 | * @since 5.0.0 |
| 442 | * @access private |
| 443 | * @return array|null |
| 444 | */ |
| 445 | private function get_sidebar_data() |
| 446 | { |
| 447 | global $wp_registered_sidebars; |
| 448 | $sidebars = wp_get_sidebars_widgets(); |
| 449 | |
| 450 | foreach ( (array) $sidebars as $sidebar_id => $sidebar ) { |
| 451 | if ( in_array($this->id, (array) $sidebar, true ) ) { |
| 452 | return $wp_registered_sidebars[$sidebar_id]; |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | return null; |
| 457 | } |
| 458 | |
| 459 | /** |
| 460 | * Removes the standard widget from the Legacy Widget block. |
| 461 | * |
| 462 | * @param array |
| 463 | * @return array |
| 464 | */ |
| 465 | public function remove_from_legacy_widget_block(array $widget_types) |
| 466 | { |
| 467 | $widget_types[] = 'wpp'; |
| 468 | return $widget_types; |
| 469 | } |
| 470 | } |
| 471 |