PluginProbe
Category Posts Block / 5.1.0
Category Posts Block v5.1.0
5.1.0 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 All 63 releases
category-posts / class-widget.php

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

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