PluginProbe
Category Posts Block / trunk
Category Posts Block vtrunk
5.0.0 trunk 1.0 1.1 1.2 1.2.1 1.3 1.3.1 1.3.2 1.3.3 2.0 2.1 2.2 2.3 3.1 3.2 3.3 4.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 All 62 releases
category-posts / class-widget.php

class-widget.php in Category Posts Block trunk, at class-widget.php

2,116 lines 82.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Implementation of the widget class.
4 *
5 * @package categoryposts.
6 *
7 * @since 4.7
8 */
9
10 namespace categoryPosts;
11
12 // Don't call the file directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Category Posts Widget Class
19 *
20 * Shows the single category posts with some configurable options
21 */
22 class Widget extends \WP_Widget {
23
24 /**
25 * The settings for the instance currently being rendered/configured.
26 *
27 * Several helper methods (thumbnail rendering, excerpt filters, etc.) read
28 * this before it is necessarily populated by widget()/form(); declaring it
29 * (instead of relying on it being created as a dynamic property) means those
30 * reads get an empty array instead of an "undefined property" warning.
31 *
32 * @var array
33 */
34 public $instance = array();
35
36 /**
37 * Widget constructor.
38 */
39 public function __construct() {
40 $widget_ops = array(
41 'show_instance_in_rest' => true,
42 'classname' => 'cat-post-widget',
43 'description' => __( 'List single category posts', 'category-posts' ),
44 );
45 parent::__construct( WIDGET_BASE_ID, __( 'Category Posts', 'category-posts' ), $widget_ops );
46 }
47
48 /**
49 * Calculate the attachment size for showing the feature image
50 *
51 * The attachment size should for width and height greater than the widget image width and height settings.
52 *
53 * @param int $post_thumbnail_id The id of the featured image attachment.
54 * @return string The HTML for the thumb related to the post
55 *
56 * @since 4.9.22
57 */
58 public function post_thumbnail_attachment_size( $post_thumbnail_id ) {
59 // $this->instance is only populated once the widget/shortcode/block render path has
60 // set it (e.g. via widget()); guard against it being unset to avoid PHP warnings.
61 $thumb_w = isset( $this->instance['thumb_w'] ) ? intval( $this->instance['thumb_w'] ) : 0;
62 $thumb_h = isset( $this->instance['thumb_h'] ) ? intval( $this->instance['thumb_h'] ) : 0;
63
64 // Get attachment image by size
65 $image_sizes = array('thumbnail', 'medium', 'large', 'full');
66 foreach ($image_sizes as $img_size) {
67 $attachment_img_size = wp_get_attachment_image_src($post_thumbnail_id, $img_size);
68 if ($attachment_img_size[1] >= $thumb_w && $attachment_img_size[2] >= $thumb_h && ! (0 === $thumb_w && 0 === $thumb_h) ||
69 'full' === $img_size) {
70 $html = wp_get_attachment_image(
71 $post_thumbnail_id,
72 $img_size,
73 false,
74 array
75 (
76 "data-cat-posts-width" => $thumb_w,
77 "data-cat-posts-height" => $thumb_h
78 )
79 );
80 break;
81 }
82 }
83 if ( ! $html ) {
84 return '';
85 }
86 return $html;
87 }
88
89 /**
90 * Calculate the HTML for showing the feature image of a post item.
91 *
92 * Used as a filter for the thumb wordpress API to add css based stretching and cropping
93 * when the image is not at the requested dimensions
94 *
95 * @param int $post_id the ID of the post of which the thumb is a featured image.
96 * @param int $post_thumbnail_id The id of the featured image attachment.
97 * @param string|array $size The requested size identified by name or (width, height) array.
98 * @param mixed $attr ignored in this context.
99 * @return string The HTML for the thumb related to the post
100 *
101 * @since 4.1
102 */
103 public function post_thumbnail_html( $post_id, $post_thumbnail_id, $size, $attr ) {
104
105 $html = $this->post_thumbnail_attachment_size($post_thumbnail_id);
106
107 // normalize style
108 $html = preg_replace( '/style="([^"]*)"/i', '', $html );
109
110 // replace size.
111 $array = array();
112 preg_match( '/width="([^"]*)"/i', $html, $array );
113 $pattern = '/\s' . $array[1] . 'px/';
114 $html = preg_replace( $pattern, ' ' . $size[0] . 'px', $html );
115
116 // replace width.
117 $pattern = '/width="' . $array[1] . '"/';
118 if ( ! empty( $this->instance['thumb_w'] ) ) {
119 $html = preg_replace( $pattern, 'width="' . $size[0] . '"', $html );
120 } else {
121 $html = preg_replace( $pattern, '', $html );
122 }
123
124 // replace height
125 $array = array();
126 preg_match( '/height="([^"]*)"/i', $html, $array );
127 $pattern = '/height="' . $array[1] . '"/';
128 if ( ! empty( $this->instance['thumb_h'] ) ) {
129 $html = preg_replace( $pattern, 'height="' . $size[1] . '"', $html );
130 } else {
131 $html = preg_replace( $pattern, '', $html );
132 }
133
134 $show_post_format = isset( $this->instance['show_post_format'] ) && ( 'none' !== $this->instance['show_post_format'] );
135 $post_format_class = '';
136 if ( $show_post_format || ! empty( $this->instance['thumb_hover'] ) ) {
137 $format = get_post_format() ? : 'standard';
138 $post_format_class = 'cat-post-format cat-post-format-' . $format;
139 }
140 $html = '<span class="cat-post-crop ' . $post_format_class . '">' . $html . '</span>';
141
142 return $html;
143 }
144
145 /*
146 * wrapper to execute the the_post_thumbnail with filters.
147 */
148 /**
149 * Calculate the HTML for showing the thumb of a post item.
150 *
151 * It is a wrapper to execute the the_post_thumbnail with filters
152 *
153 * @param string|array $size The requested size identified by name or (width, height) array.
154 *
155 * @return string The HTML for the thumb related to the post and empty string if it can not be calculated
156 *
157 * @since 4.1
158 */
159 public function the_post_thumbnail( $size = 'post-thumbnail' ) {
160 if ( empty( $size ) ) { // if junk value, make it a normal thumb.
161 // post_thumbnail_html() below always expects a [width, height] pixel
162 // pair to rewrite the width/height/sizes attributes with, not a named
163 // size string - passing through the string 'post-thumbnail' made it
164 // index into the string itself (e.g. $size[0] === 'p'), corrupting the
165 // generated markup (a "ppx" sizes attribute, wrong width/height).
166 $size = array( get_option( 'thumbnail_size_w', 150 ), get_option( 'thumbnail_size_h', 150 ) );
167 } elseif ( is_array( $size ) && ( 2 === count( $size ) ) ) { // good format at least.
168 // normalize to ints first.
169 list( $width, $height ) = array_map('intval', $size);
170 if ( ( 0 === $width ) && ( 0 === $height ) ) { // Both values zero then revert to ratio from the orig with large.
171 $size = array( get_option( 'large_size_w', 150 ), get_option( 'large_size_h', 150 ) );
172 } elseif ( ( 0 === $width ) && ( 0 !== $height ) ) {
173 // if thumb width 0 set to max/full widths for wp rendering.
174 $post_thumb = get_the_post_thumbnail( get_the_ID(), 'full' );
175 preg_match( '/(?<=width=")[\d]*/', $post_thumb, $thumb_full_w );
176 $size[0] = $thumb_full_w[0];
177 } elseif ( ( 0 !== $width ) && ( 0 === $height ) ) {
178 // if thumb height 0 get full thumb for ratio and calc height with ratio.
179 $post_thumb = get_the_post_thumbnail( get_the_ID(), 'full' );
180 preg_match( '/(?<=width=")[\d]*/', $post_thumb, $thumb_full_w );
181 preg_match( '/(?<=height=")[\d]*/', $post_thumb, $thumb_full_h );
182 $ratio = $thumb_full_w[0] / $thumb_full_h[0];
183 $size[1] = intval( $width / $ratio );
184 }
185 } else {
186 $size = array( get_option( 'thumbnail_size_w', 150 ), get_option( 'thumbnail_size_h', 150 ) ); // yet another form of junk.
187 }
188
189 $post_thumbnail_id = get_post_thumbnail_id( get_the_ID() );
190 if ( ! $post_thumbnail_id && ! empty( $this->instance['default_thunmbnail'] ) ) {
191 $post_thumbnail_id = $this->instance['default_thunmbnail'];
192 }
193
194 do_action( 'begin_fetch_post_thumbnail_html', get_the_ID(), $post_thumbnail_id, $size );
195 $ret = $this->post_thumbnail_html( get_the_ID(), $post_thumbnail_id, $size, '' );
196 do_action( 'end_fetch_post_thumbnail_html', get_the_ID(), $post_thumbnail_id, $size );
197
198 return $ret;
199 }
200
201 /**
202 * Excerpt more link filter
203 *
204 * @param string $more The "more" text passed by the filter.
205 *
206 * @return string The link to the post with the "more" text configured in the widget.
207 */
208 public function excerpt_more_filter( $more ) {
209 return ' <a class="cat-post-excerpt-more more-link" href="' . get_permalink() . '">' . esc_html( $this->instance['excerpt_more_text'] ) . '</a>';
210 }
211
212 /**
213 * Apply the_content filter for excerpt
214 * This should show sharing buttons which comes with other widgets in the widget output in the same way as on the main content
215 *
216 * @param string $text The HTML with other applied excerpt filters.
217 *
218 * @return string If option hide_social_buttons is unchecked applay the_content filter.
219 *
220 * @since 4.6
221 */
222 public function apply_the_excerpt( $text ) {
223 $ret = apply_filters( 'the_content', $text );
224
225 return $ret;
226 }
227
228 /**
229 * Calculate the wp-query arguments matching the filter settings of the widget
230 *
231 * @param array $instance Array which contains the various settings.
232 * @return array The array that can be fed to wp_Query to get the relevant posts
233 *
234 * @since 4.6
235 */
236 public function queryArgs( $instance ) {
237 $valid_sort_orders = array(
238 'date', 'title', 'comment_count', 'rand'
239 );
240
241 if ( isset( $instance['sort_by'] ) && in_array( $instance['sort_by'], $valid_sort_orders, true ) ) {
242 $sort_by = $instance['sort_by'];
243 } else {
244 $sort_by = 'date';
245 }
246 $sort_order = ( isset( $instance['asc_sort_order'] ) && $instance['asc_sort_order'] ) ? 'ASC' : 'DESC';
247
248 // start sticky posts
249 $ignore_sticky = isset( $instance['sticky'] ) && $instance['sticky'] ? false : true;
250
251 // Get array of post info.
252 $args = array(
253 'orderby' => $sort_by,
254 'order' => $sort_order,
255 'ignore_sticky_posts' => $ignore_sticky, // Make sure we do not get stickies out of order.
256 'no_found_rows' => true, // Do not count the total numbers of rows by default.
257 );
258
259 $non_default_valid_status = array(
260 'publish',
261 'future',
262 'publish,future',
263 'private',
264 'private,publish',
265 'private,publish,future',
266 );
267 if ( isset( $instance['status'] ) && in_array( $instance['status'], $non_default_valid_status, true ) ) {
268 $args['post_status'] = $instance['status'];
269 }
270
271 if ( isset( $instance['num'] ) ) {
272 $args['showposts'] = (int) $instance['num'];
273 }
274
275 if ( isset( $instance['offset'] ) && ( (int) $instance['offset'] > 1 ) ) {
276 $args['offset'] = (int) $instance['offset'] - 1;
277 }
278 if ( isset( $instance['cat'] ) ) {
279 if ( isset( $instance['no_cat_childs'] ) && $instance['no_cat_childs'] ) {
280 $args['category__in'] = (int) $instance['cat'];
281 } else {
282 $args['cat'] = (int) $instance['cat'];
283 }
284 }
285
286 if ( is_singular() && isset( $instance['exclude_current_post'] ) && $instance['exclude_current_post'] ) {
287 $args['post__not_in'] = array( get_the_ID() );
288 }
289
290 if ( isset( $instance['hideNoThumb'] ) && $instance['hideNoThumb'] ) {
291 $args = array_merge(
292 $args,
293 array(
294 'meta_query' => array(
295 array(
296 'key' => '_thumbnail_id',
297 'compare' => 'EXISTS',
298 ),
299 ),
300 )
301 );
302 }
303
304 switch ( isset( $instance['date_range'] ) ? $instance['date_range'] : 'off' ) {
305 case 'days_ago':
306 $ago = (int) $instance['days_ago'];
307
308 // If there is no valid integer value given, bail.
309 if ( 0 === $ago ) {
310 break;
311 }
312
313 $date = date( 'Y-m-d', strtotime( '-' . $ago . ' days' ) );
314 $args['date_query'] = array(
315 'after' => $date,
316 'inclusive' => true,
317 );
318 break;
319 case 'between_dates':
320 // Validation note - not doing any, assuming the query will
321 // fail gracefully enough for now as it is not clear what Should
322 // the validation be right now.
323 $start_date = $instance['start_date'];
324 $end_date = $instance['end_date'];
325 $args['date_query'] = array(
326 'after' => $start_date,
327 'before' => $end_date,
328 'inclusive' => true,
329 );
330 break;
331 }
332
333 return $args;
334 }
335
336 /**
337 * Calculate the HTML of the title based on the widget settings
338 *
339 * @param string $before_title The sidebar configured HTML that should come
340 * before the title itself.
341 * @param string $after_title The sidebar configured HTML that should come
342 * after the title itself.
343 * @param array $instance Array which contains the various settings.
344 * @return string The HTML for the title area
345 *
346 * @since 4.6
347 */
348 public function titleHTML( $before_title, $after_title, $instance ) {
349 $ret = '';
350
351 if ( isset( $instance['title_level'] ) && in_array( $instance['title_level'], array( 'H1','H2', 'H3', 'H4', 'H5', 'H6') ) ) {
352 $before_title = '';
353 $after_title = '';
354 }
355
356 // If no title, use the name of the category.
357 if ( ! isset( $instance['title'] ) || ! $instance['title'] ) {
358 $instance['title'] = '';
359 if ( isset( $instance['cat'] ) && 0 !== (int) $instance['cat'] ) {
360 $category_info = get_category( $instance['cat'] );
361 if ( $category_info && ! is_wp_error( $category_info ) ) {
362 $instance['title'] = $category_info->name;
363 } else {
364 $instance['cat'] = 0; // For further processing treat it like "all categories".
365 $instance['title'] = __( 'Category Posts', 'category-posts' );
366 }
367 } else {
368 $instance['title'] = __( 'Category Posts', 'category-posts' );
369 }
370 }
371
372 if ( ! ( isset( $instance['hide_title'] ) && $instance['hide_title'] ) ) {
373 $ret = $before_title;
374 if ( isset( $instance['is_shortcode'] ) ) {
375 $title = esc_html( $instance['title'] );
376 } else {
377 $title = apply_filters( 'widget_title', $instance['title'], $instance, WIDGET_BASE_ID );
378 }
379
380 if ( isset( $instance['title_link'] ) && $instance['title_link'] ) {
381 $new_tab_attr = '';
382 if ( ! empty( $instance['title_link_target'] ) ) {
383 $new_tab_attr = ' target="_blank" rel="noopener noreferrer"';
384 }
385 if ( isset( $instance['cat'] ) && 0 !== (int) $instance['cat'] ) {
386 $ret .= '<a href="' . get_category_link( $instance['cat'] ) . '"' . $new_tab_attr . '>' . $title . '</a>';
387 } elseif ( isset( $instance['title_link_url'] ) && $instance['title_link_url'] ) {
388 $ret .= '<a href="' . esc_url( $instance['title_link_url'] ) . '"' . $new_tab_attr . '>' . $title . '</a>';
389 } else {
390 $ret .= '<a href="' . esc_url( $this->blog_page_url() ) . '"' . $new_tab_attr . '>' . $title . '</a>';
391 }
392 } else {
393 $ret .= $title;
394 }
395
396 $ret .= $after_title;
397 }
398
399 $ret = $this->add_heading_level( $instance, $ret, 'title_level' );
400
401 return $ret;
402 }
403
404 /**
405 * Add a heading level
406 *
407 * @return string The title string
408 *
409 * @since 5.0
410 */
411 public function add_heading_level( $instance, $ret, $key ) {
412
413 $class = ( isset( $instance[ 'disable_theme_styles' ] ) && $instance[ 'disable_theme_styles' ] ) ? '' : ' class="widget-title"';
414
415 switch ( isset( $instance[ $key ] ) ? $instance[ $key ] : '' ) {
416 case 'H1':
417 $ret = '<h1' . $class . '>' . $ret . '</h1>';
418 break;
419 case 'H2':
420 $ret = '<h2' . $class . '>' . $ret . '</h2>';
421 break;
422 case 'H3':
423 $ret = '<h3' . $class . '>' . $ret . '</h3>';
424 break;
425 case 'H4':
426 $ret = '<h4' . $class . '>' . $ret . '</h4>';
427 break;
428 case 'H5':
429 $ret = '<h5' . $class . '>' . $ret . '</h5>';
430 break;
431 case 'H6':
432 $ret = '<h6' . $class . '>' . $ret . '</h6>';
433 break;
434 }
435
436 return $ret;
437 }
438
439 /**
440 * Get the URL of the blog page or home page if no explicit blog page is defined.
441 *
442 * @return string The URL of the blog page
443 *
444 * @since 4.8
445 */
446 private function blog_page_url() {
447
448 $blog_page = get_option( 'page_for_posts' );
449 if ( $blog_page ) {
450 $url = get_permalink( $blog_page );
451 } else {
452 $url = home_url();
453 }
454
455 return $url;
456 }
457
458 /**
459 * Calculate the HTML of the load more button based on the widget settings
460 *
461 * @param array $instance Array which contains the various settings.
462 *
463 * @return string The HTML for the load more area
464 *
465 * @since 4.9
466 */
467 public function loadMoreHTML( $instance ) {
468 global $post_count;
469
470 if ( ! $instance['enable_loadmore'] || $post_count <= $instance['num'] ) {
471 return '';
472 }
473
474 $ret = '<div class="' . __NAMESPACE__ . '-loadmore">';
475 $context = 0;
476 if ( is_singular() ) {
477 $context = get_the_ID();
478 }
479
480 wp_enqueue_script( 'jquery' );
481 add_action( 'wp_footer', __NAMESPACE__ . '\embed_loadmore_scripts', 100 );
482
483 // We rely on the widget number to be properly set.
484 // but need a slight different handling for proper widgets.
485 if ( is_int( $this->number ) ) {
486 // it is a proper widget, add the prefix.
487 $id = 'widget-' . $this->number;
488 } else {
489 $id = str_replace( WIDGET_BASE_ID . '-', '', $this->number );
490 }
491 if ( 0 === strpos( $this->number, 'block-' ) ) {
492 $id = 'block-' . substr( $this->number, 6 );
493 }
494
495 // Placeholder
496 $placeholder_text = $instance['loadmore_text'] !== '' ? $instance['loadmore_text'] : sprintf( esc_attr__( 'Load More (%s/%s)', 'category-posts' ), '%step%', '%all%');
497 $pattern = '/%step%/';
498 $loadmore_text = preg_replace( $pattern, $instance['num'], $placeholder_text );
499 $pattern = '/%all%/';
500 $loadmore_text = preg_replace( $pattern, $post_count, $loadmore_text );
501
502 // Load more
503 $number = $instance['num'];
504 $start = $instance['offset'] + $number;
505 $loading = $instance['loading_text'] !== '' ? $instance['loading_text'] : esc_attr__( 'Loading...', 'category-posts' );
506 $scrollTo = isset( $instance['loadmore_scrollTo'] ) && $instance['loadmore_scrollTo'];
507
508 $ret .= '<button type="button" data-loading="' . esc_attr( $loading ) . '" data-id="' . esc_attr( $id ) .
509 '" data-start="' . esc_attr( $start ) . '" data-context="' . esc_attr( $context ) . '" data-number="' . esc_attr( $number ) .
510 '" data-post-count="' . esc_attr( $post_count ) . '" data-placeholder="' . esc_attr( $placeholder_text ) . '" data-scrollto="' . esc_html( $scrollTo ) . '">' .
511 esc_html( $loadmore_text ) .
512 '</button>';
513 $ret .= '</div>';
514 return $ret;
515 }
516
517 /**
518 * Calculate the HTML of the footer based on the widget settings
519 *
520 * @param array $instance Array which contains the various settings.
521 * @return string The HTML for the footer area
522 *
523 * @since 4.6
524 */
525 public function footerHTML( $instance ) {
526
527 $ret = '';
528 $url = '';
529 $text = '';
530
531 if ( isset( $instance['footer_link'] ) ) {
532 $url = $instance['footer_link'];
533 }
534
535 if ( isset( $instance['footer_link_text'] ) ) {
536 $text = $instance['footer_link_text'];
537 }
538
539 // if url is set, but no text, just use the url as text.
540 if ( empty( $text ) && ! empty( $url ) ) {
541 $text = $url;
542 }
543
544 // if no url is set but just text, assume the url should be to the relevant archive page
545 // category archive for categories filter and home page or blog page when "all categories"
546 // is used.
547 if ( ! empty( $text ) && empty( $url ) ) {
548 if ( isset( $instance['cat'] ) && ( 0 !== (int) $instance['cat'] ) && ( null !== get_category( $instance['cat'] ) ) ) {
549 $url = get_category_link( $instance['cat'] );
550 } else {
551 $url = $this->blog_page_url();
552 }
553 }
554
555 if ( ! empty( $url ) ) {
556 $new_tab_attr = '';
557 if ( ! empty( $instance['footer_link_target'] ) ) {
558 $new_tab_attr = ' target="_blank" rel="noopener noreferrer"';
559 }
560 $ret .= '<a class="cat-post-footer-link" href="' . esc_url( $url ) . '"' . $new_tab_attr . '>' . esc_html( $text ) . '</a>';
561 }
562
563 return $ret;
564 }
565
566 /**
567 * Current post item date string based on the format requested in the settings
568 *
569 * @param array $instance Array which contains the various settings.
570 * @param bool $everything_is_link Indicates whether the return string should avoid links.
571 *
572 * @since 4.8
573 */
574 public function itemDate( $instance, $everything_is_link ) {
575 global $post;
576 $ret = '';
577
578 if ( ! isset( $instance['preset_date_format'] ) ) {
579 $preset_date_format = 'other';
580 } else {
581 $preset_date_format = $instance['preset_date_format'];
582 }
583
584 $attr = '';
585 switch ( $preset_date_format ) {
586 case 'sitedateandtime':
587 $date = get_the_time( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) );
588 break;
589 case 'localsitedateandtime':
590 $date = get_the_time( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) . ' GMT';
591 $time = get_post_time( 'U', true );
592 $attr = ' data-publishtime="' . $time . '" data-format="time"';
593 add_action( 'wp_footer', __NAMESPACE__ . '\embed_date_scripts' );
594 break;
595 case 'sitedate':
596 $date = get_the_time( get_option( 'date_format' ) );
597 break;
598 case 'localsitedate':
599 $date = get_the_time( get_option( 'date_format' ) ) . ' GMT';
600 $time = get_post_time( 'U', true );
601 $attr = ' data-publishtime="' . $time . '" data-format="date"';
602 add_action( 'wp_footer', __NAMESPACE__ . '\embed_date_scripts' );
603 break;
604 default:
605 if ( isset( $instance['date_format'] ) && strlen( trim( $instance['date_format'] ) ) > 0 ) {
606 $date_format = $instance['date_format'];
607 } else {
608 $date_format = 'j M Y';
609 }
610 $date = get_the_time( $date_format );
611 break;
612 }
613
614 if ( isset( $instance['date_past_time'] ) && 0 < $instance['date_past_time'] && $post ) {
615 $post_date = get_the_time( "Y-m-d H:i:s", $post->ID );
616 $current_date = current_time( "Y-m-d H:i:s" );
617 $past_days = date_diff(
618 date_create( $post_date ),
619 date_create( $current_date )
620 )->days;
621 if ( $past_days <= $instance['date_past_time'] ) {
622 $date = human_time_diff( get_the_time( 'U' ), current_time( 'timestamp' ) );
623 $attr = ' data-publishtime="" data-format="sincepublished"';
624 }
625 }
626
627 $post_date_class = ( isset( $instance[ 'disable_theme_styles' ] ) && $instance[ 'disable_theme_styles' ] ) ? "" : " post-date";
628 $ret .= '<span class="cat-post-date' . $post_date_class . '"' . $attr . '>' . $date . '</span>';
629
630 return $ret;
631 }
632
633
634 /**
635 * Calculate the HTML for showing the thumb of a post item.
636 * Expected to be called from a loop with globals properly set.
637 *
638 * @param array $instance Array which contains the various settings.
639 * @param bool $no_link indicates whether the thumb should be wrapped in a link or a span.
640 * @return string The HTML for the thumb related to the post
641 *
642 * @since 4.6
643 */
644 public function itemThumb( $instance, $no_link ) {
645 $ret = '';
646
647 if ( ( isset( $instance['default_thunmbnail'] ) && ( $instance['default_thunmbnail'] ) ) || has_post_thumbnail() ) {
648 $class = '';
649 $disable_css = isset( $instance['disable_css'] ) && $instance['disable_css'];
650
651 if ( isset( $this->instance['thumb_hover'] ) && ! $disable_css ) {
652 $class = 'class="cat-post-thumbnail cat-post-' . $instance['thumb_hover'] . '"';
653 } else {
654 $class = 'class="cat-post-thumbnail"';
655 }
656
657 $title_args = array( 'echo' => false );
658
659 if ( $no_link ) {
660 $ret .= '<span ' . $class . '>';
661 } else {
662 $ret .= '<a ' . $class . ' href="' . get_the_permalink() . '" title="' . the_title_attribute( $title_args ) . '">';
663 }
664
665 $ret .= $this->the_post_thumbnail( array( intval($this->instance['thumb_w']), intval($this->instance['thumb_h']) ) );
666
667
668 if ( $no_link ) {
669 $ret .= '</span>';
670 } else {
671 $ret .= '</a>';
672 }
673 }
674
675 return $ret;
676 }
677
678 /**
679 * Current post item categories string
680 *
681 * @param array $instance Array which contains the various settings.
682 * @param bool $everything_is_link Indicates whether the return string should avoid links.
683 *
684 * @since 4.8
685 */
686 public function itemCategories( $instance, $everything_is_link ) {
687
688 $post_category_class = ( isset( $instance[ 'disable_theme_styles' ] ) && $instance[ 'disable_theme_styles' ] ) ? "" : " entry-categories post-categories";
689
690 $ret = '<span class="cat-post-tax-category' . $post_category_class . '">';
691 $cat_ids = wp_get_post_categories( get_the_ID(), array( 'number' => 0 ) );
692 foreach ( $cat_ids as $cat_id ) {
693 if ( $everything_is_link ) {
694 $ret .= ' ' . get_cat_name( $cat_id );
695 } else {
696 $ret .= " <a href='" . get_category_link( $cat_id ) . "'>" . get_cat_name( $cat_id ) . '</a>';
697 }
698 }
699 $ret .= '</span>';
700 return $ret;
701 }
702
703 /**
704 * Current post item tags string
705 *
706 * @param array $instance Array which contains the various settings.
707 * @param bool $everything_is_link Indicates whether the return string should avoid links.
708 *
709 * @since 4.8
710 */
711 public function itemTags( $instance, $everything_is_link ) {
712
713 $post_tag_class = ( isset( $instance[ 'disable_theme_styles' ] ) && $instance[ 'disable_theme_styles' ] ) ? "" : " widget_tag_cloud tagcloud post-tags";
714
715 $ret = '<span class="cat-post-tax-tag' . $post_tag_class . '">';
716 $tag_ids = wp_get_post_tags( get_the_ID(), array( 'number' => 0 ) );
717 foreach ( $tag_ids as $tag_id ) {
718 if ( $everything_is_link ) {
719 $ret .= ' ' . $tag_id->name;
720 } else {
721 $ret .= " <a href='" . get_tag_link( $tag_id->term_id ) . "'>" . $tag_id->name . '</a>';
722 }
723 }
724 $ret .= '</span>';
725 return $ret;
726 }
727
728 /**
729 * Current post item comment number string
730 *
731 * @param array $instance Array which contains the various settings.
732 * @param bool $everything_is_link Indicates whether the return string should avoid links.
733 *
734 * @since 4.8
735 */
736 public function itemCommentNum( $instance, $everything_is_link ) {
737 global $post;
738
739 $ret = '<span class="cat-post-comment-num comment-meta">';
740
741 if ( $everything_is_link ) {
742 $ret .= '(' . \get_comments_number() . ')';
743 } else {
744 $link = sprintf(
745 '<a href="%1$s" title="%2$s">(%3$d)</a>',
746 esc_url( get_comments_link( $post->ID ) ),
747 esc_attr( sprintf( __( '(%d) comments to this post' ), get_comments_number() ) ),
748 get_comments_number()
749 );
750 $ret .= $link;
751 }
752
753 $ret .= '</span>';
754 return $ret;
755 }
756
757 /**
758 * Current post item author string
759 *
760 * @param array $instance Array which contains the various settings.
761 * @param bool $everything_is_link Indicates whether the return string should avoid links.
762 *
763 * @since 4.8
764 */
765 public function itemAuthor( $instance, $everything_is_link ) {
766
767 $post_author_class = ( isset( $instance[ 'disable_theme_styles' ] ) && $instance[ 'disable_theme_styles' ] ) ? "" : " post-author";
768
769 $ret = '<span class="cat-post-author' . $post_author_class . '">';
770
771 if ( $everything_is_link ) {
772 $ret .= get_the_author();
773 } else {
774 $link = get_the_author_posts_link();
775 $ret .= $link;
776 }
777 $ret .= '</span>';
778 return $ret;
779 }
780
781 /**
782 * Current post item excerpt string
783 *
784 * @param array $instance Array which contains the various settings.
785 * @param bool $everything_is_link Indicates whether the return string should avoid links.
786 *
787 * @since 4.8
788 */
789 public function itemExcerpt( $instance, $everything_is_link ) {
790 global $post;
791
792 $context = isset( $instance['context'] ) ? $instance['context'] : CONTEXT_WIDGET;
793 $excerpt = '';
794
795 if ( CONTEXT_BLOCK === $context ) {
796 if ( isset( $instance['excerpt_radio'] ) && 'excerpt' === $instance['excerpt_radio'] ) {
797 $cpw_excerpt_length = function() { return 999; };
798 add_filter( 'excerpt_length', $cpw_excerpt_length, 20 );
799 $excerpt = wpautop(get_the_excerpt());
800 remove_filter( 'excerpt_length', $cpw_excerpt_length, 20 );
801
802 }
803
804 if ( isset( $instance['excerpt_radio'] ) && 'full_post' === $instance['excerpt_radio'] ) {
805 $excerpt = get_the_content();
806 }
807 }
808
809 if ( CONTEXT_WIDGET === $context || CONTEXT_SHORTCODE === $context ) {
810 // use the_excerpt filter to get the "normal" excerpt of the post
811 // then apply our filter to let users customize excerpts in their own way.
812 if ( isset( $instance['excerpt_length'] ) && ( $instance['excerpt_length'] > 0 ) ) {
813 $length = (int) $instance['excerpt_length'];
814 } else {
815 $length = 999; // Use the wordpress default.
816 }
817
818 if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
819 $excerpt = apply_filters( 'the_excerpt', \get_the_excerpt() );
820 } else { // if filters off replicate functionality of core generating excerpt.
821 $more_text = '[&hellip;]';
822 if ( isset( $instance['excerpt_more_text'] ) && $instance['excerpt_more_text'] ) {
823 $more_text = ltrim( $instance['excerpt_more_text'] );
824 }
825
826 if ( $everything_is_link ) {
827 $excerpt_more_text = ' <span class="cat-post-excerpt-more">' . $more_text . '</span>';
828 } else {
829 $excerpt_more_text = ' <a class="cat-post-excerpt-more" href="' . get_permalink() . '" title="' . sprintf( __( 'Continue reading %s' ), get_the_title() ) . '">' . $more_text . '</a>';
830 }
831 if ( '' === $post->post_excerpt ) {
832 $text = get_the_content( '' );
833 $text = strip_shortcodes( $text );
834 $excerpt = \wp_trim_words( $text, $length, $excerpt_more_text );
835 // adjust html output same way as for the normal excerpt,
836 // just force all functions depending on the_excerpt hook.
837 $excerpt = shortcode_unautop( wpautop( convert_chars( convert_smilies( wptexturize( $excerpt ) ) ) ) );
838 } else {
839 $text = $post->post_excerpt;
840 $excerpt = \wp_trim_words( $text, $length, $excerpt_more_text );
841 $excerpt = shortcode_unautop( wpautop( convert_chars( convert_smilies( wptexturize( $excerpt ) ) ) ) );
842 }
843 }
844 }
845
846 // 'cpwp-wrap-text' is what the excerpt-lines CSS hooks on. It is normally set by
847 // equal_cover_content_height(), but that script never runs in the block editor
848 // preview (REST request, no wp_footer), so set it server side as the baseline.
849 // The script still moves it to the stage wrapper later when the text wraps.
850 $excerpt = str_replace( '<p>', '<p class="cpwp-excerpt-text cpwp-wrap-text">', $excerpt );
851 $ret = apply_filters( 'cpw_excerpt', $excerpt, $this );
852 return $ret;
853 }
854
855 /**
856 * Current post item More Link string
857 *
858 * @param array $instance Array which contains the various settings.
859 * @param bool $everything_is_link Indicates whether the return string should avoid links.
860 *
861 * @since 5.0
862 */
863 public function itemMoreLink( $instance, $everything_is_link ) {
864 global $post;
865
866 $more_text = '[&hellip;]';
867
868 if ( isset( $instance['excerpt_more_text'] ) && '' !== $instance['excerpt_more_text'] ) {
869 $more_text = sanitize_text_field( $instance['excerpt_more_text'] );
870 }
871
872 $more_link_class = ( isset( $instance[ 'disable_theme_styles' ] ) && $instance[ 'disable_theme_styles' ] ) ? "" : " more-link";
873
874 if ( $everything_is_link ) {
875 $ret = ' <span class="cat-post-excerpt-more' . $more_link_class . '">' . $more_text . '</span>';
876 } else {
877 $ret = ' <a class="cat-post-excerpt-more' . $more_link_class . '" href="' . get_permalink() . '">' . $more_text . '</a>';
878 }
879
880 if ( isset( $instance['excerpt_more_text'] ) && '' === $instance['excerpt_more_text'] ) {
881 $ret = '' !== apply_filters( 'excerpt_more', '' ) ? apply_filters( 'excerpt_more', '' ) : $ret;
882 }
883 return $ret;
884 }
885
886 /**
887 * Current post item title string
888 *
889 * @param array $instance Array which contains the various settings.
890 * @param bool $everything_is_link Indicates whether the return string should avoid links.
891 *
892 * @since 4.8
893 */
894 public function itemTitle( $instance, $everything_is_link ) {
895
896 $ret = '';
897
898 if ( $everything_is_link ) {
899 $ret .= '<span class="cat-post-title">' . get_the_title() . '</span>';
900 } else {
901 $ret .= '<a class="cat-post-title"';
902 $ret .= ' href="' . get_the_permalink() . '" rel="bookmark">' . get_the_title();
903 $ret .= '</a>';
904 }
905
906 $ret = $this->add_heading_level( $instance, $ret, 'item_title_level' );
907
908 return $ret;
909 }
910
911 /**
912 * Calculate the HTML for a post item based on the widget settings and post.
913 * Expected to be called in an active loop with all the globals set.
914 *
915 * @param array $instance Array which contains the various settings.
916 * @param null|integer $current_post_id If on singular page specifies the id of
917 * the post, otherwise null.
918 * @return string The HTML for item related to the post
919 *
920 * @since 4.6
921 */
922 public function itemHTML( $instance, $current_post_id ) {
923 global $post;
924
925 $everything_is_link = isset( $instance['everything_is_link'] ) && $instance['everything_is_link'];
926 $no_wrap = isset( $instance['text_do_not_wrap_thumb'] ) && $instance['text_do_not_wrap_thumb'];
927
928 $template = '';
929 if ( isset( $instance['template'] ) ) {
930 $template = $instance['template'];
931 } else {
932 $template = convert_settings_to_template( $instance );
933 }
934 $ret = '<li ';
935
936 // Current post.
937 if ( $current_post_id === $post->ID ) {
938 $ret .= "class='cat-post-item cat-post-current'";
939 } else {
940 $ret .= "class='cat-post-item'";
941 }
942 $ret .= '>'; // close the li opening tag.
943
944 if ( $everything_is_link ) {
945 $ret .= '<a class="cat-post-everything-is-link" href="' . get_the_permalink() . '" title="">';
946 }
947
948 // Post details (Template).
949 $widget = $this;
950 $template_res = preg_replace_callback(
951 get_template_regex(),
952 function ( $matches ) use ( $widget, $instance, $everything_is_link ) {
953 switch ( $matches[0] ) {
954 case '%title%':
955 return $widget->itemTitle( $instance, $everything_is_link );
956 case '%author%':
957 return $widget->itemAuthor( $instance, $everything_is_link );
958 case '%commentnum%':
959 return $widget->itemCommentNum( $instance, $everything_is_link );
960 case '%date%':
961 return $widget->itemDate( $instance, $everything_is_link );
962 case '%thumb%':
963 return $widget->itemThumb( $instance, $everything_is_link );
964 case '%post_tag%':
965 return $widget->itemTags( $instance, $everything_is_link );
966 case '%category%':
967 return $widget->itemCategories( $instance, $everything_is_link );
968 case '%excerpt%':
969 return $widget->itemExcerpt( $instance, $everything_is_link );
970 case '%more-link%':
971 return $widget->itemMoreLink( $instance, $everything_is_link );
972 default:
973 return $matches[0];
974 }
975 },
976 $template
977 );
978
979 // Replace empty line with closing and opening DIV.
980 $template_res = trim( $template_res );
981
982 // An empty line in the template starts a new DIV. The DIV holding the excerpt is
983 // additionally wrapped in the 'cpwp-wrap-text-stage' element the excerpt lines CSS
984 // needs, so that it is part of the markup right away -- also for items delivered by
985 // the load more REST route and for the block editor preview, where no script runs.
986 $blocks = preg_split( '/\n\r|\n\n/', $template_res ); // "\n\r" in widget areas, "\n\n" as shortcode.
987 $template_res = '';
988 foreach ( $blocks as $block ) {
989 $div = '<div>' . $block . '</div>';
990 if ( false !== strpos( $block, 'cpwp-excerpt-text' ) ) {
991 $div = '<div class="cpwp-wrap-text-stage">' . $div . '</div>';
992 }
993 $template_res .= $div;
994 }
995
996 // replace new lines with spaces.
997 $template_res = str_replace( "\n\r", ' ', $template_res ); // in widget areas.
998 $template_res = str_replace( "\n\n", ' ', $template_res ); // as shortcode.
999
1000 $ret .= $template_res;
1001
1002 if ( $everything_is_link ) {
1003 $ret .= '</a>';
1004 }
1005
1006 $ret .= '</li>';
1007 return wp_kses_post($ret);
1008 }
1009
1010 /**
1011 * Filter to set the number of words in an excerpt
1012 *
1013 * @param int $length The number of words as configured by wordpress core or set by previous filters.
1014 * @return int The number of words configured for the widget,
1015 * or the $length parameter if it is not configured or garbage value.
1016 *
1017 * @since 4.6
1018 */
1019 public function excerpt_length_filter( $length ) {
1020 if ( isset( $this->instance['excerpt_length'] ) && $this->instance['excerpt_length'] > 0 ) {
1021 $length = $this->instance['excerpt_length'];
1022 }
1023 return $length;
1024 }
1025
1026 /**
1027 * Set the proper excerpt filters based on the settings
1028 *
1029 * @param array $instance widget settings.
1030 * @return void
1031 *
1032 * @since 4.6
1033 */
1034 public function setExcerpFilters( $instance ) {
1035
1036 if ( isset( $instance['excerpt'] ) && $instance['excerpt'] ) {
1037
1038 // Excerpt length filter.
1039 if ( isset( $instance['excerpt_length'] ) && ( (int) $instance['excerpt_length'] ) > 0 ) {
1040 add_filter( 'excerpt_length', array( $this, 'excerpt_length_filter' ) );
1041 }
1042
1043 if ( isset( $instance['excerpt_more_text'] ) && ( '' !== ltrim( $instance['excerpt_more_text'] ) ) ) {
1044 add_filter( 'excerpt_more', array( $this, 'excerpt_more_filter' ) );
1045 }
1046
1047 add_filter( 'the_excerpt', array( $this, 'apply_the_excerpt' ) );
1048 }
1049 }
1050
1051 /**
1052 * Remove the excerpt filter
1053 *
1054 * @param array $instance widget settings.
1055 * @return void
1056 *
1057 * @since 4.6
1058 */
1059 public function removeExcerpFilters( $instance ) {
1060 remove_filter( 'excerpt_length', array( $this, 'excerpt_length_filter' ) );
1061 remove_filter( 'excerpt_more', array( $this, 'excerpt_more_filter' ) );
1062 add_filter( 'get_the_excerpt', 'wp_trim_excerpt' );
1063 remove_filter( 'the_excerpt', array( $this, 'apply_the_excerpt' ) );
1064 }
1065
1066 /**
1067 * The main widget display controller
1068 *
1069 * Called by the sidebar processing core logic to display the widget.
1070 *
1071 * @param array $args An array containing the "environment" setting for the widget,
1072 * namely, the enclosing tags for the widget and its title.
1073 * @param array $instance The settings associate with the widget.
1074 *
1075 * @since 4.1
1076 */
1077 public function widget( $args, $instance ) {
1078 global $before_title, $after_title;
1079
1080 $instance = upgrade_settings( $instance );
1081
1082 // A shortcode is rendered through this method as well, in that case the context
1083 // was already set by Virtual_Widget::getHTML(), so do not overwrite it.
1084 if ( ! isset( $instance['context'] ) ) {
1085 $instance['context'] = CONTEXT_WIDGET;
1086 }
1087
1088 extract( $args );
1089
1090 $this->instance = $instance;
1091
1092 $current_post_id = '';
1093 if ( is_singular() ) {
1094 $current_post_id = get_the_ID();
1095 }
1096
1097 $items = $this->get_elements_HTML( $instance, $current_post_id, 0, 0 );
1098
1099 if ( ( 'nothing' === $instance['no_match_handling'] ) || ! empty( $items ) ) {
1100 echo $before_widget; // Xss ok. This is how widget actually expected to behave.
1101
1102 echo $this->titleHTML( $before_title, $after_title, $instance );
1103
1104 $thumb = isset( $this->instance['template'] ) && preg_match( '/%thumb%/', $this->instance['template'] );
1105
1106 if ( ! ( isset( $instance['is_shortcode'] ) && $instance['is_shortcode'] ) ) { // the internal id is needed only for widgets.
1107 echo '<ul id="' . esc_attr( WIDGET_BASE_ID ) . '-' . esc_attr( $this->number ) . '-internal" class="' . esc_attr( WIDGET_BASE_ID ) . '-internal' . "\">\n";
1108 } else {
1109 echo '<ul>';
1110 }
1111
1112 // image crop browser fallback and workaround, no polyfill.
1113 if ( $thumb ) {
1114 if ( apply_filters( 'cpw_enqueue_resources', false ) ) {
1115 frontend_script();
1116 } else {
1117 wp_enqueue_script( 'jquery' );
1118 add_action( 'wp_footer', __NAMESPACE__ . '\embed_front_end_scripts', 100 );
1119 }
1120 }
1121
1122 // set widget filters.
1123 if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
1124 $this->setExcerpFilters( $instance );
1125 }
1126
1127 foreach ( $items as $item ) {
1128 echo $item;
1129 }
1130 echo "</ul>\n";
1131
1132 // Load more only if we think we have more items.
1133 if ( count( $items ) === (int) $instance['num'] ) {
1134 echo $this->loadMoreHTML( $instance );
1135 }
1136
1137 echo $this->footerHTML( $instance );
1138 echo $after_widget; // Xss ok. This is how widget actually expected to behave.
1139
1140 // remove widget filters.
1141 if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
1142 $this->removeExcerpFilters( $instance );
1143 }
1144
1145 wp_reset_postdata();
1146
1147 $number = $this->number;
1148 // a temporary hack to handle difference in the number in a true widget
1149 // and the number format expected at the rest of the places.
1150 if ( is_numeric( $number ) ) {
1151 $number = WIDGET_BASE_ID . '-' . $number;
1152 }
1153 if ( ! ( isset( $instance['is_shortcode'] ) && $instance['is_shortcode'] ) ) { // the internal id is needed only for widgets.
1154 $number .= '-internal';
1155 }
1156
1157 // enque relevant scripts and parameters to ensure correct image dimentions.
1158 if ( isset( $instance['template'] ) && preg_match( '/%thumb%|%excerpt%/', $instance['template'] ) ) {
1159 wp_enqueue_script( 'jquery' ); // just in case the theme or other plugins didn't enqueue it.
1160 add_action(
1161 'wp_footer',
1162 function () use ( $number, $instance ) {
1163 __NAMESPACE__ . '\\' . equal_cover_content_height( $number, $instance );
1164 },
1165 100
1166 );
1167 }
1168 } elseif ( 'text' === $instance['no_match_handling'] ) {
1169 echo $before_widget; // Xss ok. This is how widget actually expected to behave.
1170 echo $this->titleHTML( $before_title, $after_title, $instance );
1171 echo wp_kses_post( $instance['no_match_text'] );
1172 echo $this->footerHTML( $instance );
1173 echo $after_widget; // Xss ok. This is how widget actually expected to behave.
1174 }
1175 }
1176
1177 /**
1178 * Get an array of HTML pre item, for item starting from a specific position.
1179 *
1180 * @since 4.9
1181 *
1182 * @param array $instance An array containing the settings of the widget.
1183 * @param string $singular_id The ID of the post in which the widget is rendered,
1184 * an empty string indicates the rendering context
1185 * is not singular.
1186 * @param int $start The start element (0 based).
1187 * @param int $number The maximal number of elements to return. A value of 0
1188 * Indicates to use the widget settings for that.
1189 *
1190 * @return string[] Array of HTML per element with the $start element first
1191 * $start+1 next etc. An empty array is returned if there
1192 * are no applicable items.
1193 */
1194 public function get_elements_HTML( $instance, $singular_id, $start, $number ) {
1195 global $post_count;
1196
1197 $ret = array();
1198
1199 if ( 0 === count( $instance ) ) {
1200 $instance = default_settings();
1201 }
1202
1203 $this->instance = $instance;
1204
1205 if ( $start > 0 ) {
1206 $instance['offset'] = $start;
1207 }
1208 $number = (int) $number; // sanitize number with the side effect of non
1209 // numbers are converted to zero.
1210 if ( 0 < $number ) {
1211 $instance['num'] = $number;
1212 }
1213 $args = $this->queryArgs( $instance );
1214 $cat_posts = new \WP_Query( $args );
1215
1216 if( isset( $instance['enable_loadmore'] ) && $instance['enable_loadmore'] ) {
1217 $args['showposts'] = 0;
1218 $args['nopaging'] = true;
1219 $post_count = ( new \WP_Query( $args ) )->post_count; // May there is a better workflow to count the loadable items
1220 }
1221
1222 $current_post_id = null;
1223 if ( '' !== $singular_id ) {
1224 $current_post_id = (int) $singular_id;
1225 }
1226
1227 $postCount = 0;
1228
1229 while ( $cat_posts->have_posts() ) {
1230 $postCount++;
1231
1232 // If sticky posts are added, break anyway after the set number of posts.
1233 if ( isset( $instance['sticky'] ) && $instance['sticky'] && $postCount > $instance['num'] ) {
1234 break;
1235 }
1236
1237 $cat_posts->the_post();
1238 $ret[] = $this->itemHTML( $instance, $current_post_id );
1239 }
1240
1241 wp_reset_postdata();
1242
1243 return $ret;
1244 }
1245
1246 /**
1247 * Update the options.
1248 *
1249 * @param array $new_instance The new settings of the widget.
1250 * @param array $old_instance The current settings of the widget.
1251 * @return array
1252 */
1253 public function update( $new_instance, $old_instance ) {
1254
1255 $new_instance['title'] = sanitize_text_field( $new_instance['title'] ); // sanitize the title like core widgets do.
1256 if ( ! isset( $new_instance['excerpt_filters'] ) ) {
1257 $new_instance['excerpt_filters'] = '';
1258 }
1259 if ( current_user_can( 'unfiltered_html' ) ) {
1260 $instance['text'] = $new_instance['template'];
1261 } else {
1262 $instance['text'] = wp_kses_post( $new_instance['template'] );
1263 }
1264
1265 // Set the version of the DB structure.
1266 $new_instance['ver'] = VERSION;
1267 return $new_instance;
1268 }
1269
1270 /**
1271 * Output the title panel of the widget configuration form.
1272 *
1273 * @param array $instance The widget's settings.
1274 * @return void
1275 *
1276 * @since 4.6
1277 */
1278 public function formTitlePanel( $instance ) {
1279 $cat = isset( $instance['cat'] ) ? (int) $instance['cat'] : 0;
1280
1281 $hide_title = false;
1282 if ( isset( $instance['hide_title'] ) && $instance['hide_title'] ) {
1283 $hide_title = true;
1284 }
1285 ?>
1286 <h4 data-panel="title"><?php esc_html_e( 'Title', 'category-posts' ); ?></h4>
1287 <div class="cpwp_ident">
1288 <?php echo $this->get_checkbox_block_html( $instance, 'hide_title', esc_html__( 'Hide title', 'category-posts' ), true ); ?>
1289 <div class="categoryposts-data-panel-title-settings" <?php echo ( $hide_title ) ? 'style="display:none"' : ''; ?>>
1290 <?php echo $this->get_text_input_block_html( $instance, 'title', esc_html__( 'Title', 'category-posts' ), '', true ); ?>
1291 <?php echo $this->get_checkbox_block_html( $instance, 'title_link', esc_html__( 'Make widget title link', 'category-posts' ), 0 !== $cat ); ?>
1292 <?php echo $this->get_text_input_block_html( $instance, 'title_link_url', esc_html__( 'Title link URL', 'category-posts' ), '', 0 === $cat ); ?>
1293 <?php echo $this->get_radio_buttons_block_html( $instance, 'title_level', array( 'Initial', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), esc_html__( 'Heading Level', 'category-posts' ) . ":" .
1294 ' <a href="#" class="dashicons toggle-title-level-help dashicons-paperclip"><span class="screen-reader-text">' .
1295 esc_html__( 'Show title level help', 'category-posts' ) . '</span></a>', true ); ?>
1296 <div class="cat-post-title-level-help" style="display:none;">
1297 <p><?php echo __( 'Also, try \'Disable Theme\'s styles\' on General tab to avoid rendering commonly used CSS classes such here widget-title, which often used in Themes to write their CSS selectors and may affect the design.', 'category-posts' ); ?>
1298 </p>
1299 </div>
1300 </div>
1301 </div>
1302 <?php
1303 }
1304
1305 /**
1306 * Output the filter panel of the widget configuration form.
1307 *
1308 * @param array $instance The parameters configured for the widget.
1309 * @return void
1310 *
1311 * @since 4.6
1312 */
1313 public function formFilterPanel( $instance ) {
1314 $cat = (int) $instance['cat'];
1315 ?>
1316 <h4 data-panel="filter"><?php esc_html_e( 'Filter', 'category-posts' ); ?></h4>
1317 <div>
1318 <p>
1319 <label>
1320 <?php esc_html_e( 'Category', 'category-posts' ); ?>:
1321 <?php
1322 wp_dropdown_categories(
1323 array(
1324 'show_option_all' => __( 'All categories', 'category-posts' ),
1325 'hide_empty' => 0,
1326 'name' => $this->get_field_name( 'cat' ),
1327 'selected' => $instance['cat'],
1328 'class' => 'categoryposts-data-panel-filter-cat',
1329 )
1330 );
1331 ?>
1332 </label>
1333 </p>
1334 <?php
1335 echo $this->get_checkbox_block_html( $instance, 'no_cat_childs', esc_html__( 'Exclude child categories', 'category-posts' ), ! empty( $instance['cat'] ) );
1336 echo $this->get_select_block_html(
1337 $instance,
1338 'status',
1339 esc_html__( 'Status', 'category-posts' ), array(
1340 'default' => esc_html__( 'WordPress Default', 'category-posts' ),
1341 'publish' => esc_html__( 'Published', 'category-posts' ),
1342 'future' => esc_html__( 'Scheduled', 'category-posts' ),
1343 'private' => esc_html__( 'Private', 'category-posts' ),
1344 'publish,future' => esc_html__( 'Published or Scheduled', 'category-posts' ),
1345 'private,publish' => esc_html__( 'Published or Private', 'category-posts' ),
1346 'private,future' => esc_html__( 'Private or Scheduled', 'category-posts' ),
1347 'private,publish,future' => esc_html__( 'Published, Private or Scheduled', 'category-posts' ),
1348 ),
1349 'default',
1350 true
1351 );
1352 echo $this->get_number_input_block_html( $instance, 'num', esc_html__( 'Number of posts to show', 'category-posts' ), 1, '', '', true );
1353 echo $this->get_number_input_block_html( $instance, 'offset', esc_html__( 'Start with post', 'category-posts' ), 1, '', '', true );
1354 echo $this->get_select_block_html(
1355 $instance,
1356 'date_range',
1357 esc_html__( 'Date Range', 'category-posts' ),
1358 array(
1359 'off' => esc_html__( 'Off', 'category-posts' ),
1360 'days_ago' => esc_html__( 'Days ago', 'category-posts' ),
1361 'between_dates' => esc_html__( 'Between dates', 'category-posts' ),
1362 ),
1363 'off',
1364 true
1365 );
1366 ?>
1367 <div class="cpwp_ident categoryPosts-date-range" style="display:<?php echo 'off' === $instance['date_range'] ? 'none' : 'block'; ?>">
1368 <?php
1369 echo $this->get_number_input_block_html( $instance, 'days_ago', esc_html__( 'Up to', 'category-posts' ), 1, '', '', 'days_ago' === $instance['date_range'] );
1370 echo $this->get_date_input_block_html( $instance, 'start_date', esc_html__( 'After', 'category-posts' ), 'between_dates' === $instance['date_range'] );
1371 echo $this->get_date_input_block_html( $instance, 'end_date', esc_html__( 'Before', 'category-posts' ), 'between_dates' === $instance['date_range'] );
1372 ?>
1373 </div>
1374 <?php
1375 echo $this->get_select_block_html(
1376 $instance,
1377 'sort_by',
1378 esc_html__( 'Sort by', 'category-posts' ),
1379 array(
1380 'date' => esc_html__( 'Date', 'category-posts' ),
1381 'title' => esc_html__( 'Title', 'category-posts' ),
1382 'comment_count' => esc_html__( 'Number of comments', 'category-posts' ),
1383 'rand' => esc_html__( 'Random', 'category-posts' ),
1384 ),
1385 'date',
1386 true
1387 );
1388 echo $this->get_checkbox_block_html( $instance, 'asc_sort_order', esc_html__( 'Reverse sort order (ascending)', 'category-posts' ), true );
1389 echo $this->get_checkbox_block_html( $instance, 'exclude_current_post', esc_html__( 'Exclude current post', 'category-posts' ), true );
1390 echo $this->get_checkbox_block_html( $instance, 'hideNoThumb', esc_html__( 'Exclude posts which have no thumbnail', 'category-posts' ), true );
1391 echo $this->get_checkbox_block_html( $instance, 'sticky', esc_html__( 'Start with sticky posts', 'category-posts' ), true );
1392 ?>
1393 </div>
1394 <?php
1395 }
1396
1397 /**
1398 * Generate the wrapper P around a form input element
1399 *
1400 * @since 4.8
1401 * @param string $html The HTML to wrap.
1402 * @param string $key The key to use as the prefix to the class.
1403 * @param bool $visible Indicates if the element should be visible when rendered.
1404 *
1405 * @return string HTML with P element contaning the html being passed with class based on the key
1406 * and style set to display:none if visibility is off.
1407 */
1408 private function get_wrap_block_html( $html, $key, $visible ) {
1409
1410 $cl = ' class="' . __NAMESPACE__ . '-' . esc_attr( $key ) . '"';
1411
1412 $style = '';
1413 if ( ! $visible ) {
1414 $style = ' style="display:none"';
1415 }
1416 $ret = '<p' . $cl . $style . ">\n" . $html . "</p>\n";
1417
1418 return $ret;
1419 }
1420
1421 /**
1422 * Generate a form P element containing a select element
1423 *
1424 * @since 4.8
1425 * @param array $instance The instance.
1426 * @param string $key The key in the instance array.
1427 * @param string $label The label to display and associate with the input.
1428 * @param array $list An array of pairs value (index) => label to be used for the options.
1429 * The labels are expected to be html escaped.
1430 * @param int $default The value to use if the key is not set in the instance.
1431 * @param bool $visible Indicates if the element should be visible when rendered.
1432 *
1433 * @return string HTML a P element contaning the select, its label, class based on the key
1434 * and style set to display:none if visibility is off.
1435 */
1436 private function get_select_block_html( $instance, $key, $label, $list, $default, $visible ) {
1437 $value = $instance[ $key ];
1438
1439 if ( ! array_key_exists( $value, $list ) ) {
1440 $value = $default;
1441 }
1442
1443 if ( '' !== $label ) {
1444 $label .= ':';
1445 }
1446
1447 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1448 $label .
1449 "</label>\n" .
1450 '<select id="' . $this->get_field_id( $key ) . '" name="' . $this->get_field_name( $key ) . '" autocomplete="off">' . "\n";
1451 foreach ( $list as $v => $l ) {
1452 $ret .= '<option value="' . esc_attr( $v ) . '" ' . selected( $v, $value, false ) . '>' . $l . "</option>\n";
1453 }
1454 $ret .= "</select>\n";
1455
1456 return $this->get_wrap_block_html( $ret, $key, $visible );
1457 }
1458
1459 /**
1460 * Generate a form P element containing a textarea input
1461 *
1462 * @since 4.8
1463 * @param array $instance The instance.
1464 * @param string $key The key in the instance array.
1465 * @param string $label The label to display and associate with the input (should be html escaped).
1466 * @param string $placeholder The placeholder to use in the input (should be attribute escaped).
1467 * @param bool $visible Indicates if the element should be visible when rendered.
1468 * @param int $num_rows Number of rows.
1469 *
1470 * @return string HTML a P element containing the input, its label, class based on the key
1471 * and style set to display:none if visibility is off.
1472 */
1473 private function get_textarea_html( $instance, $key, $label, $placeholder, $visible, $num_rows ) {
1474
1475 $value = $instance[ $key ];
1476
1477 $ret = '<label for="' . esc_attr( $this->get_field_id( $key ) ) . '">' . $label .
1478 '<textarea rows="' . esc_attr( $num_rows ) . '" placeholder="' . $placeholder . '" id="' . esc_attr( $this->get_field_id( $key ) ) . '" name="' . esc_attr( $this->get_field_name( $key ) ) . '" autocomplete="off">' . esc_textarea( $value ) . '</textarea>' .
1479 '</label>';
1480
1481 return $this->get_wrap_block_html( $ret, $key, $visible );
1482 }
1483
1484 /**
1485 * Generate a form P element containing a text input
1486 *
1487 * @since 4.8
1488 * @param array $instance The instance.
1489 * @param string $key The key in the instance array.
1490 * @param string $label The label to display and associate with the input.
1491 * Should be html escaped.
1492 * @param string $placeholder The placeholder to use in the input. should be attribute escaped.
1493 * @param bool $visible Indicates if the element should be visible when rendered.
1494 *
1495 * @return string HTML a P element contaning the input, its label, class based on the key
1496 * and style set to display:none if visibility is off.
1497 */
1498 private function get_text_input_block_html( $instance, $key, $label, $placeholder, $visible ) {
1499
1500 $value = isset( $instance[ $key ] ) ? $instance[ $key ] : '';
1501
1502 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1503 $label . ":\n" .
1504 '<input placeholder="' . $placeholder . '" id="' . $this->get_field_id( $key ) . '" name="' . $this->get_field_name( $key ) . '" type="text" value="' . esc_attr( $value ) . '" autocomplete="off"/>' . "\n" .
1505 "</label>\n";
1506
1507 return $this->get_wrap_block_html( $ret, $key, $visible );
1508 }
1509
1510 /**
1511 * Generate a form P element containing a range input
1512 *
1513 * @since 4.8
1514 * @param array $instance The instance.
1515 * @param string $key The key in the instance array.
1516 * @param string $label The label to display and associate with the input.
1517 * expected to be escaped.
1518 * @param int $min The minimum value allowed to be input.
1519 * @param int $max The maximum value allowed to be input.
1520 * @param string $value The start value.
1521 * @param string $step The range of each step.
1522 * @param bool $visible Indicates if the element should be visible when rendered.
1523 *
1524 * @return string HTML a P element contaning the input, its label, class based on the key
1525 * and style set to display:none if visibility is off.
1526 */
1527 private function get_range_input_block_html( $instance, $key, $label, $min, $max, $value, $step, $visible ) {
1528
1529 $value = $instance[ $key ];
1530
1531 $minMaxStep = '';
1532 if ( '' !== $min ) {
1533 $minMaxStep .= ' min="' . $min . '"';
1534 }
1535 if ( '' !== $max ) {
1536 $minMaxStep .= ' max="' . $max . '"';
1537 }
1538 if ( '' !== $step ) {
1539 $minMaxStep .= ' step="' . $step . '"';
1540 }
1541
1542 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1543 esc_html( $label ) . ' <span>' . $value . "%</span>\n" .
1544 '<input id="' . esc_attr( $this->get_field_id( $key ) ) . '" value="' . $value . '" name="' . esc_attr( $this->get_field_name( $key ) ) . '" class="' . esc_attr( $key ) . '" type="range"' . $minMaxStep . ' />' . "\n" .
1545 "</label>\n";
1546
1547 return $this->get_wrap_block_html( $ret, $key, $visible );
1548 }
1549
1550 /**
1551 * Generate a form P element containing a number input
1552 *
1553 * @since 4.8
1554 * @param array $instance The instance.
1555 * @param string $key The key in the instance array.
1556 * @param string $label The label to display and associate with the input.
1557 * expected to be escaped.
1558 * @param int $min The minimum value allowed to be input.
1559 * @param int $max The maximum value allowed to be input.
1560 * @param string $placeholder The placeholder string to be used. expected to be escaped.
1561 * @param bool $visible Indicates if the element should be visible when rendered.
1562 *
1563 * @return string HTML a P element contaning the input, its label, class based on the key
1564 * and style set to display:none if visibility is off.
1565 */
1566 private function get_number_input_block_html( $instance, $key, $label, $min, $max, $placeholder, $visible ) {
1567
1568 $value = $instance[ $key ];
1569
1570 $minmax = '';
1571 if ( '' !== $min ) {
1572 $minmax .= ' min="' . $min . '"';
1573 }
1574 if ( '' !== $max ) {
1575 $minmax .= ' max="' . $max . '"';
1576 }
1577
1578 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1579 esc_html( $label ) . "\n" .
1580 '<input placeholder="' . $placeholder . '" id="' . esc_attr( $this->get_field_id( $key ) ) . '" name="' . esc_attr( $this->get_field_name( $key ) ) . '" class="' . esc_attr( $key ) . '" type="number"' . $minmax . ' value="' . esc_attr( $value ) . '" autocomplete="off" />' . "\n" .
1581 "</label>\n";
1582
1583 return $this->get_wrap_block_html( $ret, $key, $visible );
1584 }
1585
1586 /**
1587 * Generate a form P element containing a date input
1588 *
1589 * @since 4.9
1590 * @param array $instance The instance.
1591 * @param string $key The key in the instance array.
1592 * @param string $label The label to display and associate with the input.
1593 * expected to be escaped.
1594 * @param bool $visible Indicates if the element should be visible when rendered.
1595 *
1596 * @return string HTML a P element containing the input, its label, class based on the key
1597 * and style set to display:none if visibility is off.
1598 */
1599 private function get_date_input_block_html( $instance, $key, $label, $visible ) {
1600
1601 $value = $instance[ $key ];
1602
1603 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1604 esc_html( $label ) . "\n" .
1605 '<input id="' . esc_attr( $this->get_field_id( $key ) ) . '" name="' . esc_attr( $this->get_field_name( $key ) ) . '" class="' . esc_attr( $key ) . '" type="date" value="' . esc_attr( $value ) . '" autocomplete="off" />' . "\n" .
1606 "</label>\n";
1607
1608 return $this->get_wrap_block_html( $ret, $key, $visible );
1609 }
1610
1611 /**
1612 * Generate a form P element containing a checkbox input
1613 *
1614 * @since 4.8
1615 * @param array $instance The instance.
1616 * @param string $key The key in the instance array.
1617 * @param string $label The label to display and associate with the checkbox.
1618 * should be escaped string.
1619 * @param bool $visible Indicates if the element should be visible when rendered.
1620 *
1621 * @return string HTML a P element contaning the checkbox, its label, class based on the key
1622 * and style set to display:none if visibility is off.
1623 */
1624 private function get_checkbox_block_html( $instance, $key, $label, $visible ) {
1625
1626 $value = false;
1627 if ( array_key_exists( $key, $instance ) && $instance[ $key ] ) {
1628 $value = true;
1629 }
1630 $ret = '<label class="checkbox" for="' . esc_attr( $this->get_field_id( $key ) ) . "\">\n" .
1631 '<input id="' . esc_attr( $this->get_field_id( $key ) ) . '" name="' . esc_attr( $this->get_field_name( $key ) ) . '" type="checkbox" ' . checked( $value, true, false ) . '/>' . "\n" .
1632 $label .
1633 "</label>\n";
1634
1635 return $this->get_wrap_block_html( $ret, $key, $visible );
1636 }
1637
1638 /**
1639 * Generate a form button element containing
1640 *
1641 * @since 4.9
1642 * @param array $instance The instance.
1643 * @param string $key The key in the instance array.
1644 * @param string $label The label to display and associate with the button.
1645 * should be escaped string.
1646 *
1647 * @return string HTML a button element and class based on the key.
1648 */
1649 private function get_button_thumb_size_html( $instance, $key, $label ) {
1650
1651 $datas = '';
1652
1653 switch ( $key ) {
1654 case 'thumb':
1655 $datas = 'data-thumb-w="' . get_option( 'thumbnail_size_w' ) . '" data-thumb-h="' . get_option( 'thumbnail_size_h' ) . '"';
1656 break;
1657 case 'medium':
1658 $datas = 'data-thumb-w="' . get_option( 'medium_size_w' ) . '" data-thumb-h="' . get_option( 'medium_size_h' ) . '"';
1659 break;
1660 case 'large':
1661 $datas = 'data-thumb-w="' . get_option( 'large_size_w' ) . '" data-thumb-h="' . get_option( 'large_size_h' ) . '"';
1662 break;
1663 }
1664 $ret = '<button type="button" ' . $datas . ' class="' . $key . ' button">' . esc_html( $label ) . "</button>\n";
1665
1666 return $ret;
1667 }
1668
1669 /**
1670 * Generate a form element containing native styled radio buttons
1671 *
1672 * @since 5.0
1673 * @param array $instance The instance.
1674 * @param string $key The key in the instance array.
1675 * @param string $label The label to display and associate with the checkbox.
1676 * should be escaped string.
1677 * @param bool $visible Indicates if the element should be visible when rendered.
1678 *
1679 * @return string HTML a element contaning the radio buttons, it's label, class based on the key
1680 * and style set to display:none if visibility is off.
1681 */
1682 private function get_radio_buttons_block_html( $instance, $key, $values, $label, $visible ) {
1683
1684 $ret = '<label for="' . esc_attr( $this->get_field_id( $key ) ) . "\">" . $label . "</label>\n";
1685 $ret .= '<span class="cpwp-right">';
1686
1687 array_map ( function( $value ) use ( &$ret, $instance, $key ) {
1688 if ( isset( $instance[ $key ] ) && $instance[ $key ] == $value ) {
1689 $checked = true;
1690 } else {
1691 $checked = false;
1692 }
1693
1694 $ret .= '<input class="' . $value . ' button" id="' . esc_attr( $this->get_field_id( $key . $value ) ) . '" name="' . esc_attr( $this->get_field_name( $key ) ) .
1695 '" value="' . $value . '" type="radio" ' . checked( $checked, true, false ) . '/>' . "\n";
1696 }, $values );
1697
1698 $ret .= "</span>\n";
1699
1700 return $this->get_wrap_block_html( $ret, $key, $visible );
1701 }
1702
1703 /**
1704 * The widget configuration form back end.
1705 *
1706 * @param array $instance The parameters associated with the widget.
1707 * @return void
1708 */
1709 public function form( $instance ) {
1710 if ( 0 === count( $instance ) ) { // new widget, use defaults.
1711 $instance = default_settings();
1712 } else { // updated widgets come from =< 4.6 excerpt filter is on.
1713 if ( ! isset( $instance['excerpt_filters'] ) ) {
1714 $instance['excerpt_filters'] = 'on';
1715 }
1716 }
1717
1718 $instance = upgrade_settings( $instance );
1719
1720 $item_title_level = $instance['item_title_level'];
1721 $hide_post_titles = $instance['hide_post_titles'];
1722 $excerpt_more_text = $instance['excerpt_more_text'];
1723 $excerpt_filters = $instance['excerpt_filters'];
1724 $date_format = $instance['date_format'];
1725 $disable_css = $instance['disable_css'];
1726 $disable_font_styles = $instance['disable_font_styles'];
1727 $preset_date_format = $instance['preset_date_format'];
1728 $thumb = ! empty( $instance['thumb'] );
1729 $thumb_w = $instance['thumb_w'];
1730 $thumb_fluid_width = $instance['thumb_fluid_width'];
1731 $thumb_h = $instance['thumb_h'];
1732 $default_thunmbnail = $instance['default_thunmbnail'];
1733 $text_do_not_wrap_thumb = $instance['text_do_not_wrap_thumb'];
1734 ?>
1735
1736 <div class="category-widget-cont">
1737 <?php if ( ! class_exists( '\\termcategoryPostsPro\\Widget' ) ) { ?>
1738 <p><a target="_blank" href="https://tiptoppress.com/term-and-category-based-posts-widget/"><?php esc_html_e( 'Get the Pro version', 'category-posts' ); ?></a></p>
1739 <?php
1740 }
1741 $this->formTitlePanel( $instance );
1742 $this->formFilterPanel( $instance );
1743 ?>
1744 <h4 data-panel="details"><?php esc_html_e( 'Post details', 'category-posts' ); ?></h4>
1745 <div class="cpwp-sub-panel">
1746 <?php
1747 $template = '';
1748 if ( ! isset( $instance['template'] ) ) {
1749 $template = convert_settings_to_template( $instance );
1750 } else {
1751 $template = $instance['template'];
1752 }
1753 ?>
1754 <p><?php esc_html_e( 'Displayed parts', 'category-posts' ); ?></p>
1755 <div class="cpwp_ident">
1756 <?php
1757 $label = esc_html__( 'Template', 'category-posts' ) .
1758 ' <a href="#" class="dashicons toggle-template-help dashicons-editor-help"><span class="screen-reader-text">' .
1759 esc_html__( 'Show template help', 'category-posts' ) . '</span></a>';
1760 $class_placement = '';
1761 if ( is_customize_preview() ) {
1762 $class_placement = 'customizer';
1763 } else {
1764 $class_placement = 'admin-panel';
1765 }
1766 $label .= '<span class="cat-post-add_premade_templates ' . $class_placement . '">' .
1767 '<button type="button" class="button cpwp-open-placholder-dropdown-menu"> + ' . esc_html__( 'Add Placeholder', 'category-posts' ) . '</button>' .
1768 '<span class="cpwp-placeholder-dropdown-menu">' .
1769 '<i class="cpwp-close-placeholder-dropdown-menu dashicons dashicons-no-alt"></i>' .
1770 '<span data-value="NewLine">' . esc_html__( 'New line', 'category-posts' ) . '</span>' .
1771 '<span data-value="EmptyLine">' . esc_html__( 'Empty line', 'category-posts' ) . '</span>' .
1772 '<span data-value="title">' . esc_html__( '%title%', 'category-posts' ) . '</span>' .
1773 '<span data-value="thumb">' . esc_html__( '%thumb%', 'category-posts' ) . '</span>' .
1774 '<span data-value="date">' . esc_html__( '%date%', 'category-posts' ) . '</span>' .
1775 '<span data-value="excerpt">' . esc_html__( '%excerpt%', 'category-posts' ) . '</span>' .
1776 '<span data-value="more-link">' . esc_html__( '%more-link%', 'category-posts' ) . '</span>' .
1777 '<span data-value="author">' . esc_html__( '%author%', 'category-posts' ) . '</span>' .
1778 '<span data-value="commentnum">' . esc_html__( '%commentnum%', 'category-posts' ) . '</span>' .
1779 '<span data-value="post_tag">' . esc_html__( '%post_tag%', 'category-posts' ) . '</span>' .
1780 '<span data-value="category">' . esc_html__( '%category%', 'category-posts' ) . '</span>' .
1781 '</span>' .
1782 '</span>';
1783 ?>
1784 <?php
1785 echo $this->get_textarea_html( $instance, 'template', $label, '', true, 8 );
1786 preg_match_all( get_template_regex(), $template, $matches );
1787 $tags = array();
1788 if ( ! empty( $matches[0] ) ) {
1789 $tags = array_flip( $matches[0] );
1790 }
1791 ?>
1792 <div class="cat-post-template-help" style="display:none;">
1793 <p><?php echo sprintf( __( 'The following placeholders will be replaced with the relevant information. In addition you can use text, HTML and <a target="_blank" href="%s">Dashicons</a>.', 'category-posts' ),
1794 'https://developer.wordpress.org/resource/dashicons/'); ?>
1795 </p>
1796 <table>
1797 <tr>
1798 <th><?php esc_html_e( 'New line', 'category-posts' ); ?></th>
1799 <td><?php esc_html_e( 'Space', 'category-posts' ); ?></td>
1800 </tr>
1801 <tr>
1802 <th><?php esc_html_e( 'Empty line', 'category-posts' ); ?></th>
1803 <td><?php esc_html_e( 'Next line is a paragraph', 'category-posts' ); ?></td>
1804 </tr>
1805 <tr>
1806 <th>%title%</th>
1807 <td><?php esc_html_e( 'Post title', 'category-posts' ); ?></td>
1808 </tr>
1809 <tr>
1810 <th>%thumb%</th>
1811 <td><?php esc_html_e( 'Post thumbnail possibly wrapped by text', 'category-posts' ); ?></td>
1812 </tr>
1813 <tr>
1814 <th>%date%</th>
1815 <td><?php esc_html_e( 'Post publish date', 'category-posts' ); ?></td>
1816 </tr>
1817 <tr>
1818 <th>%excerpt%</th>
1819 <td><?php esc_html_e( 'Post excerpt', 'category-posts' ); ?></td>
1820 </tr>
1821 <tr>
1822 <th>%more-link%</th>
1823 <td><?php esc_html_e( 'Read more text', 'category-posts' ); ?></td>
1824 </tr>
1825 <tr>
1826 <th>%author%</th>
1827 <td><?php esc_html_e( 'Post author', 'category-posts' ); ?></td>
1828 </tr>
1829 <tr>
1830 <th>%commentnum%</th>
1831 <td><?php esc_html_e( 'The number of comments to the post', 'category-posts' ); ?></td>
1832 </tr>
1833 <tr>
1834 <th>%post_tag%</th>
1835 <td><?php esc_html_e( 'Post tags', 'category-posts' ); ?></td>
1836 </tr>
1837 <tr>
1838 <th>%category%</th>
1839 <td><?php esc_html_e( 'Post categories', 'category-posts' ); ?></td>
1840 </tr>
1841 </table>
1842 </div>
1843 <div class="cat-post-premade_templates">
1844 <p><label><?php esc_html_e( 'Select premade Template', 'category-posts' ); ?></label></p>
1845 <select>
1846 <option value="title"><?php esc_html_e( 'Title', 'category-posts' ); ?></option>
1847 <option value="title_excerpt"><?php esc_html_e( 'Title, Excerpt, More Link', 'category-posts' ); ?></option>
1848 <option value="title_thumb"><?php esc_html_e( 'Title, Thumbnail', 'category-posts' ); ?></option>
1849 <option value="title_thum_excerpt"><?php esc_html_e( 'Title, Thumbnail, Excerpt, More Link', 'category-posts' ); ?></option>
1850 <option value="everything"><?php esc_html_e( 'All with icons', 'category-posts' ); ?></option>
1851 </select>
1852 <p><button type="button" class="button"><?php esc_html_e( 'Select this template', 'category-posts' ); ?></button></p>
1853 <?php
1854 echo $this->get_checkbox_block_html( $instance, 'everything_is_link', esc_html__( 'Everything is a link', 'category-posts' ), true );
1855 ?>
1856 </div>
1857 </div>
1858 <?php // Title settings. ?>
1859 <div class="cpwp-sub-panel categoryposts-data-panel-title" style="display:<?php echo ( isset( $tags['%title%'] ) ) ? 'block' : 'none'; ?>">
1860 <p><?php esc_html_e( 'Title settings', 'category-posts' ); ?></p>
1861 <div class="cpwp_ident">
1862 <?php
1863 echo $this->get_number_input_block_html( $instance, 'item_title_lines', esc_html__( 'Lines (responsive):', 'category-posts' ), 0, '', '', true );
1864 echo $this->get_radio_buttons_block_html( $instance, 'item_title_level', array( 'Inline', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), esc_html__( 'Heading Level:', 'category-posts' ), true );
1865 ?>
1866 </div>
1867 </div>
1868 <?php // Excerpt settings. ?>
1869 <div class="cpwp-sub-panel categoryposts-data-panel-excerpt" style="display:<?php echo ( isset( $tags['%excerpt%'] ) ) ? 'block' : 'none'; ?>">
1870 <p><?php esc_html_e( 'Excerpt settings', 'category-posts' ); ?></p>
1871 <div class="cpwp_ident">
1872 <?php
1873 echo $this->get_number_input_block_html( $instance, 'excerpt_lines', esc_html__( 'Lines (responsive):', 'category-posts' ), 0, '', '', true );
1874 // Remove the UI since free 5.0
1875 // echo $this->get_number_input_block_html( $instance, 'excerpt_length', esc_html__( 'Length (words):', 'category-posts' ), 0, '', '', true );
1876 // Remove the UI since free 5.0
1877 // echo $this->get_text_input_block_html( $instance, 'excerpt_more_text', esc_html__( '\'More ...\' text:', 'category-posts' ), esc_attr__( '...', 'category-posts' ), true );
1878 ?>
1879 </div>
1880 </div>
1881 <?php // More link settings. ?>
1882 <div class="cpwp-sub-panel categoryposts-data-panel-more-link" style="display:<?php echo ( isset( $tags['%more-link%'] ) ) ? 'block' : 'none'; ?>">
1883 <p><?php esc_html_e( 'More Link settings', 'category-posts' ); ?></p>
1884 <div class="cpwp_ident">
1885 <?php
1886 echo $this->get_text_input_block_html
1887 (
1888 $instance, 'excerpt_more_text',
1889 esc_html__( '\'Read more\' text', 'category-posts' ) .
1890 ' <a href="#" class="dashicons toggle-more-link-help dashicons-editor-help">' .
1891 '<span class="screen-reader-text">' . esc_html__( 'Show More Link help', 'category-posts' ) . '</span>' .
1892 '</a>', esc_attr__( '[&hellip;]', 'category-posts' ),
1893 true
1894 );
1895 ?>
1896 <div class="cat-post-more-link-help" style="display:none;">
1897 <p><?php echo sprintf( __( 'Text in the \'more\' link. Can be text, HTML and <a target="_blank" href="%s">Dashicons</a>.', 'category-posts' ),
1898 'https://developer.wordpress.org/resource/dashicons/'); ?>
1899 </p>
1900 </div>
1901 </div>
1902 </div>
1903 <?php // Data settings. ?>
1904 <div class="cpwp-sub-panel categoryposts-data-panel-date" style="display:<?php echo ( isset( $tags['%date%'] ) ) ? 'block' : 'none'; ?>">
1905 <p><?php esc_html_e( 'Date format settings', 'category-posts' ); ?></p>
1906 <div class="cpwp_ident">
1907 <?php
1908 echo $this->get_select_block_html(
1909 $instance,
1910 'preset_date_format',
1911 esc_html__( 'Date format', 'category-posts' ),
1912 array(
1913 'sitedateandtime' => esc_html__( 'Site date and time', 'category-posts' ),
1914 'sitedate' => esc_html__( 'Site date', 'category-posts' ),
1915 'localsitedateandtime' => esc_html__( 'Reader\'s local date and time', 'category-posts' ),
1916 'localsitedate' => esc_html__( 'Reader\'s local date', 'category-posts' ),
1917 'other' => esc_html__( 'PHP style format', 'category-posts' ),
1918 ),
1919 'sitedateandtime',
1920 true
1921 );
1922 echo $this->get_text_input_block_html( $instance, 'date_format', esc_html__( 'PHP Style Date format', 'category-posts' ), 'j M Y', 'other' === $preset_date_format );
1923 echo $this->get_number_input_block_html( $instance, 'date_past_time', esc_html__( 'Show past time up to x-days:', 'category-posts' ), 0, '', '' , true );
1924 ?>
1925 </div>
1926 </div>
1927 <?php // Thumbnail settings. ?>
1928 <div class="cpwp-sub-panel categoryposts-data-panel-thumb" style="display:<?php echo ( isset( $tags['%thumb%'] ) ) ? 'block' : 'none'; ?>">
1929 <p><?php esc_html_e( 'Thumbnail settings', 'category-posts' ); ?></p>
1930 <div class="cpwp_ident">
1931 <p><?php esc_html_e( 'Dimensions (pixel)', 'category-posts' ); ?>
1932 <a href="#" class="dashicons toggle-image-dimensions-help dashicons-editor-help">
1933 <span class="screen-reader-text"><?php esc_html__( 'Show image dimension help', 'category-posts' ); ?></span>
1934 </a>
1935 </p>
1936 <?php
1937 echo $this->get_number_input_block_html( $instance, 'thumb_w', esc_html__( 'Width:', 'category-posts' ), 0, '', '', true );
1938 echo $this->get_range_input_block_html( $instance, 'thumb_fluid_width', esc_html__( 'Max-width:', 'category-posts' ), 2, 100, 100, 2, true );
1939 echo $this->get_number_input_block_html( $instance, 'thumb_h', esc_html__( 'Height:', 'category-posts' ), 0, '', '', true );
1940 ?>
1941 <div class="cat-post-image-dimensions-help" style="display:none;">
1942 <p><?php esc_html_e( 'Set one dimensions to 0 the set dimension will be used with the original image ratio.', 'category-posts' ); ?></p>
1943 <p><?php esc_html_e( 'Set both dimensions to 0 the original image ratio will be used.', 'category-posts' ); ?></p>
1944 <p><?php esc_html_e( 'Max-width limits in relation of the total Post width.', 'category-posts' ); ?></p>
1945 </div>
1946 <div class="cat-post-thumb-change-size">
1947 <p>
1948 <label><?php esc_html_e( 'Change size', 'category-posts' ); ?>: </label>
1949 <span class="cpwp-right">
1950 <?php
1951 echo $this->get_button_thumb_size_html( $instance, 'smaller', esc_html__( '-', 'category-posts' ) );
1952 echo $this->get_button_thumb_size_html( $instance, 'quarter', esc_html__( '1/4', 'category-posts' ) );
1953 echo $this->get_button_thumb_size_html( $instance, 'half', esc_html__( '1/2', 'category-posts' ) );
1954 echo $this->get_button_thumb_size_html( $instance, 'double', esc_html__( '2x', 'category-posts' ) );
1955 echo $this->get_button_thumb_size_html( $instance, 'bigger', esc_html__( '+', 'category-posts' ) );
1956 ?>
1957 </span>
1958 </p>
1959 <p>
1960 <label><?php esc_html_e( 'Ratio', 'category-posts' ); ?>: </label>
1961 <span class="cpwp-right">
1962 <?php
1963 echo $this->get_button_thumb_size_html( $instance, 'square', esc_html__( '1:1', 'category-posts' ) );
1964 echo $this->get_button_thumb_size_html( $instance, 'standard', esc_html__( '4:3', 'category-posts' ) );
1965 echo $this->get_button_thumb_size_html( $instance, 'wide', esc_html__( '16:9', 'category-posts' ) );
1966 echo $this->get_button_thumb_size_html( $instance, 'switch', esc_html__( 'switch', 'category-posts' ) );
1967 ?>
1968 </span>
1969 </p>
1970 <p>
1971 <label><?php esc_html_e( 'Image ratio', 'category-posts' ); ?>: </label>
1972 <span class="cpwp-right">
1973 <?php
1974 echo $this->get_button_thumb_size_html( $instance, 'width', esc_html__( 'Width', 'category-posts' ) );
1975 echo $this->get_button_thumb_size_html( $instance, 'height', esc_html__( 'Height', 'category-posts' ) );
1976 echo $this->get_button_thumb_size_html( $instance, 'both', esc_html__( 'Both', 'category-posts' ) );
1977 ?>
1978 </span>
1979 </p>
1980 <p>
1981 <label><?php esc_html_e( 'Available', 'category-posts' ); ?>: </label>
1982 <span class="cpwp-right">
1983 <?php
1984 echo $this->get_button_thumb_size_html( $instance, 'thumb', esc_html__( 'Thumb', 'category-posts' ) );
1985 echo $this->get_button_thumb_size_html( $instance, 'medium', esc_html__( 'Medium', 'category-posts' ) );
1986 echo $this->get_button_thumb_size_html( $instance, 'large', esc_html__( 'Large', 'category-posts' ) );
1987 ?>
1988 </span>
1989 </p>
1990 </div>
1991 <?php
1992 echo $this->get_checkbox_block_html( $instance, 'text_do_not_wrap_thumb', esc_html__( 'Do not wrap thumbnail with overflowing text', 'category-posts' ), true );
1993 echo $this->get_select_block_html( $instance, 'thumb_hover', esc_html__( 'Animation on mouse hover', 'category-posts' ), array(
1994 'none' => esc_html__( 'None', 'category-posts' ),
1995 'dark' => esc_html__( 'Darker', 'category-posts' ),
1996 'white' => esc_html__( 'Brighter', 'category-posts' ),
1997 'scale' => esc_html__( 'Zoom in', 'category-posts' ),
1998 'blur' => esc_html__( 'Blur', 'category-posts' ),
1999 'icon' => esc_html__( 'Icon', 'category-posts' ),
2000 ), 'none', true);
2001 echo $this->get_select_block_html(
2002 $instance,
2003 'show_post_format',
2004 esc_html__( 'Indicate post format and position', 'category-posts' ),
2005 array(
2006 'none' => esc_html__( 'None', 'category-posts' ),
2007 'topleft' => esc_html__( 'Top left', 'category-posts' ),
2008 'bottomleft' => esc_html__( 'Bottom left', 'category-posts' ),
2009 'ceter' => esc_html__( 'Center', 'category-posts' ),
2010 'topright' => esc_html__( 'Top right', 'category-posts' ),
2011 'bottomright' => esc_html__( 'Bottom right', 'category-posts' ),
2012 'nocss' => esc_html__( 'HTML without styling', 'category-posts' ),
2013 ),
2014 'none',
2015 true
2016 );
2017 ?>
2018 <p>
2019 <label style="display:block">
2020 <?php esc_html_e( 'Default thumbnail: ', 'category-posts' ); ?>
2021 </label>
2022 <input type="hidden" class="default_thumb_id" id="<?php echo esc_attr( $this->get_field_id( 'default_thunmbnail' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'default_thunmbnail' ) ); ?>" value="<?php echo esc_attr( $default_thunmbnail ); ?>"/>
2023 <span class="default_thumb_img">
2024 <?php
2025 if ( ! $default_thunmbnail ) {
2026 esc_html_e( 'None', 'category-posts' );
2027 } else {
2028 $img = wp_get_attachment_image_src( $default_thunmbnail );
2029 echo '<img width="60" height="60" src="' . esc_url( $img[0] ) . '" />';
2030 }
2031 ?>
2032 </span>
2033 </p>
2034 <p>
2035 <button type="button" class="cwp_default_thumb_select button upload-button">
2036 <?php esc_html_e( 'Select image', 'category-posts' ); ?>
2037 </button>
2038 <button type="button" class="cwp_default_thumb_remove button upload-button" <?php echo ( ! $default_thunmbnail ) ? 'style="display:none"' : ''; ?> >
2039 <?php esc_html_e( 'No default', 'category-posts' ); ?>
2040 </button>
2041 </p>
2042 </div>
2043 </div>
2044 </div>
2045 <h4 data-panel="general"><?php esc_html_e( 'General', 'category-posts' ); ?></h4>
2046 <div class="cpwp-sub-panel">
2047 <p><?php esc_html_e( 'Inherited CSS', 'categorypostspro' ); ?></p>
2048 <div class="cpwp_ident">
2049 <?php echo $this->get_checkbox_block_html( $instance, 'disable_css', esc_html__( 'Disable the built-in CSS', 'category-posts' ), true ); ?>
2050 <?php echo $this->get_checkbox_block_html( $instance, 'disable_font_styles', esc_html__( 'Disable only font styles', 'category-posts' ), ! ( isset( $instance['disable_css'] ) && $instance['disable_css'] ) ); ?>
2051 <?php echo $this->get_checkbox_block_html( $instance, 'disable_theme_styles', esc_html__( 'Disable Theme\'s styles', 'category-posts' ), true ); ?>
2052 </div>
2053 <p><?php esc_html_e( 'Interim text', 'categorypostspro' ); ?></p>
2054 <div class="cpwp_ident">
2055 <?php
2056 echo $this->get_select_block_html(
2057 $instance,
2058 'no_match_handling',
2059 esc_html__( 'When there are no matches', 'category-posts' ),
2060 array(
2061 'nothing' => esc_html__( 'Display empty widget', 'category-posts' ),
2062 'hide' => esc_html__( 'Hide Widget', 'category-posts' ),
2063 'text' => esc_html__( 'Show text', 'category-posts' ),
2064 ),
2065 'nothing',
2066 true
2067 );
2068 ?>
2069 <div class="categoryPosts-no-match-text" style="display:<?php echo ( 'text' === $instance['no_match_handling'] ) ? 'block' : 'none'; ?>">
2070 <?php echo $this->get_textarea_html( $instance, 'no_match_text', esc_html__( 'Text', 'category-posts' ), '', true, 4 ); ?>
2071 </div>
2072 </div>
2073 <p><?php esc_html_e( 'Ajax API', 'categorypostspro' ); ?></p>
2074 <div class="cpwp_ident">
2075 <?php echo $this->get_checkbox_block_html( $instance, 'enable_loadmore', esc_html__( 'Enable Load More', 'category-posts' ), true ); ?>
2076 <div class="loadmore-settings" style="display:<?php echo ( $instance['enable_loadmore'] ) ? 'block' : 'none'; ?>">
2077 <?php
2078 echo $this->get_checkbox_block_html( $instance, 'loadmore_scrollTo', esc_html__( 'Scrollbar', 'category-posts' ), true );
2079 echo $this->get_text_input_block_html( $instance, 'loadmore_text',
2080 esc_html__( 'Button text', 'category-posts' ) .
2081 ' <a href="#" class="dashicons toggle-button-text-help dashicons-editor-help">' .
2082 '<span class="screen-reader-text">' . esc_html__( 'Show button text help', 'category-posts' ) . '</span>' .
2083 '</a>', sprintf( esc_attr__( 'Load More (%s/%s)', 'category-posts' ), '%step%', '%all%'), true );
2084 ?>
2085 <div class="cat-post-button-text-help" style="display:none;">
2086 <p><?php echo esc_html__( 'The following placeholders will be replaced with the relevant information.', 'category-posts' ); ?>
2087 </p>
2088 <table>
2089 <tr>
2090 <th><?php esc_html_e( '%step%', 'category-posts' ); ?></th>
2091 <td><?php esc_html_e( 'Loaded items', 'category-posts' ); ?></td>
2092 </tr>
2093 <tr>
2094 <th><?php esc_html_e( '%all%', 'category-posts' ); ?></th>
2095 <td><?php esc_html_e( 'All possible items for the set filter query', 'category-posts' ); ?></td>
2096 </tr>
2097 </table>
2098 </div>
2099 <?php echo $this->get_text_input_block_html( $instance, 'loading_text', esc_html__( 'Loading text', 'category-posts' ), esc_attr__( 'Loading...', 'category-posts' ), true ); ?>
2100 </div>
2101 </div>
2102 </div>
2103 <h4 data-panel="footer"><?php esc_html_e( 'Footer', 'category-posts' ); ?></h4>
2104 <div>
2105 <?php echo $this->get_text_input_block_html( $instance, 'footer_link_text', esc_html__( 'Footer link text', 'category-posts' ), '', true ); ?>
2106 <?php echo $this->get_text_input_block_html( $instance, 'footer_link', esc_html__( 'Footer link URL', 'category-posts' ), '', true ); ?>
2107 </div>
2108 <p><a href="<?php echo esc_url( get_edit_user_link() ) . '#' . __NAMESPACE__; ?>"><?php esc_html_e( 'Widget admin behaviour settings', 'category-posts' ); ?></a></p>
2109 <p><a target="_blank" href="<?php echo esc_url( DOC_URL ); ?>"><?php esc_html_e( 'Documentation', 'category-posts' ); ?></a></p>
2110 <p><a target="_blank" href="<?php echo esc_url( SUPPORT_URL ); ?>"><?php esc_html_e( 'Support', 'category-posts' ); ?></a></p>
2111 <p><?php echo sprintf( wp_kses( __( 'We are on <a href="%1$s">Facebook</a> and <a href="%2$s">Twitter</a>.', 'category-posts' ), array( 'a' => array( 'href' => array() ) ) ), esc_url( 'https://www.facebook.com/TipTopPress' ), esc_url( 'https://twitter.com/TipTopPress' ) ); ?></p>
2112 </div>
2113 <?php
2114 }
2115 }
2116