PluginProbe
Category Posts Block / 4.9.4
Category Posts Block v4.9.4
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 4.9.4, at class-widget.php

1,688 lines 67.1 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 * Widget constructor.
26 */
27 public function __construct() {
28 $widget_ops = array(
29 'classname' => 'cat-post-widget',
30 'description' => __( 'List single category posts', 'category-posts' ),
31 );
32 parent::__construct( WIDGET_BASE_ID, __( 'Category Posts', 'category-posts' ), $widget_ops );
33 }
34
35 /**
36 * Calculate the HTML for showing the thumb of a post item.
37 *
38 * Used as a filter for the thumb wordpress API to add css based stretching and cropping
39 * when the image is not at the requested dimensions
40 *
41 * @param string $html The original HTML generated by the core APIS.
42 * @param int $post_id the ID of the post of which the thumb is a featured image.
43 * @param int $post_thumbnail_id The id of the featured image attachment.
44 * @param string|array $size The requested size identified by name or (width, height) array.
45 * @param mixed $attr ignored in this context.
46 * @return string The HTML for the thumb related to the post
47 *
48 * @since 4.1
49 */
50 public function post_thumbnail_html( $html, $post_id, $post_thumbnail_id, $size, $attr ) {
51
52 $use_css_cropping = isset( $this->instance['use_css_cropping'] ) && $this->instance['use_css_cropping'];
53 $thumb = isset( $this->instance['template'] ) && preg_match( '/%thumb%/', $this->instance['template'] );
54
55 if ( ! ( $use_css_cropping && $thumb ) ) {
56 return $html;
57 }
58 $meta = image_get_intermediate_size( $post_thumbnail_id, $size );
59
60 if ( empty( $meta ) ) {
61 $post_img = wp_get_attachment_metadata( $post_thumbnail_id, $size );
62 $meta['file'] = basename( $post_img['file'] );
63 }
64
65 $origfile = get_attached_file( $post_thumbnail_id, true ); // the location of the full file.
66 $file = dirname( $origfile ) . '/' . $meta['file']; // the location of the file displayed as thumb.
67 if ( file_exists( $file ) ) {
68 list( $width, $height ) = getimagesize( $file ); // get actual size of the thumb file.
69
70 if ( isset( $this->instance['use_css_cropping'] ) && $this->instance['use_css_cropping'] ) {
71 $show_post_format = isset( $this->instance['show_post_format'] ) && ( 'none' !== $this->instance['show_post_format'] );
72 if ( $show_post_format || $this->instance['thumb_hover'] ) {
73 $format = get_post_format() ? : 'standard';
74 $post_format_class = 'cat-post-format cat-post-format-' . $format;
75 }
76 $html = '<span class="cat-post-crop ' . $post_format_class . '">' . $html . '</span>';
77 } else {
78 // use_css_cropping is not used.
79 // wrap span.
80 $html = '<span>' . $html . '</span>';
81 }
82 }
83 return $html;
84 }
85
86 /*
87 * wrapper to execute the the_post_thumbnail with filters.
88 */
89 /**
90 * Calculate the HTML for showing the thumb of a post item.
91 *
92 * It is a wrapper to execute the the_post_thumbnail with filters
93 *
94 * @param string|array $size The requested size identified by name or (width, height) array.
95 *
96 * @return string The HTML for the thumb related to the post and empty string if it can not be calculated
97 *
98 * @since 4.1
99 */
100 public function the_post_thumbnail( $size = 'post-thumbnail' ) {
101 if ( empty( $size ) ) { // if junk value, make it a normal thumb.
102 $size = 'post-thumbnail';
103 } elseif ( is_array( $size ) && ( 2 === count( $size ) ) ) { // good format at least.
104 // normalize to ints first.
105 $size[0] = (int) $size[0];
106 $size[1] = (int) $size[1];
107 if ( ( 0 === $size[0] ) && ( 0 === $size[1] ) ) { // Both values zero then revert to thumbnail.
108 $size = array( get_option( 'thumbnail_size_w', 150 ), get_option( 'thumbnail_size_h', 150 ) );
109 } elseif ( ( 0 === $size[0] ) && ( 0 !== $size[1] ) ) {
110 // if thumb width 0 set to max/full widths for wp rendering
111 $post_thumb = get_the_post_thumbnail( get_the_ID(), 'full' );
112 preg_match( '/(?<=width=")[\d]*/', $post_thumb, $thumb_full_w );
113 $size[0] = $thumb_full_w[0];
114 } elseif ( ( 0 !== $size[0] ) && ( 0 === $size[1] ) ) {
115 // if thumb height 0 get full thumb for ratio and calc height with ratio
116 $post_thumb = get_the_post_thumbnail( get_the_ID(), 'full' );
117 preg_match( '/(?<=width=")[\d]*/', $post_thumb, $thumb_full_w );
118 preg_match( '/(?<=height=")[\d]*/', $post_thumb, $thumb_full_h );
119 $ratio = $thumb_full_w[0] / $thumb_full_h[0];
120 $size[1] = intval( $size[0] / $ratio );
121 }
122 } else {
123 $size = array( get_option( 'thumbnail_size_w', 150 ), get_option( 'thumbnail_size_h', 150 ) ); // yet another form of junk.
124 }
125
126 $post_thumbnail_id = get_post_thumbnail_id( get_the_ID() );
127 if ( ! $post_thumbnail_id && $this->instance['default_thunmbnail'] ) {
128 $post_thumbnail_id = $this->instance['default_thunmbnail'];
129 }
130
131 do_action( 'begin_fetch_post_thumbnail_html', get_the_ID(), $post_thumbnail_id, $size );
132 $html = wp_get_attachment_image( $post_thumbnail_id, $size, false, '' );
133 if ( ! $html ) {
134 $ret = '';
135 } else {
136 $ret = $this->post_thumbnail_html( $html, get_the_ID(), $post_thumbnail_id, $size, '' );
137 }
138 do_action( 'end_fetch_post_thumbnail_html', get_the_ID(), $post_thumbnail_id, $size );
139
140 return $ret;
141 }
142
143 /**
144 * Excerpt more link filter
145 *
146 * @param string $more The "more" text passed by the filter.
147 *
148 * @return string The link to the post with the "more" text configured in the widget.
149 */
150 public function excerpt_more_filter( $more ) {
151 return ' <a class="cat-post-excerpt-more more-link" href="' . get_permalink() . '">' . esc_html( $this->instance['excerpt_more_text'] ) . '</a>';
152 }
153
154 /**
155 * Apply the_content filter for excerpt
156 * This should show sharing buttons which comes with other widgets in the widget output in the same way as on the main content
157 *
158 * @param string $text The HTML with other applied excerpt filters.
159 *
160 * @return string If option hide_social_buttons is unchecked applay the_content filter.
161 *
162 * @since 4.6
163 */
164 public function apply_the_excerpt( $text ) {
165 $ret = apply_filters( 'the_content', $text );
166
167 return $ret;
168 }
169
170 /**
171 * Calculate the wp-query arguments matching the filter settings of the widget
172 *
173 * @param array $instance Array which contains the various settings.
174 * @return array The array that can be fed to wp_Query to get the relevant posts
175 *
176 * @since 4.6
177 */
178 public function queryArgs( $instance ) {
179
180 $valid_sort_orders = array( 'date', 'title', 'comment_count', 'rand' );
181 if ( isset( $instance['sort_by'] ) && in_array( $instance['sort_by'], $valid_sort_orders, true ) ) {
182 $sort_by = $instance['sort_by'];
183 } else {
184 $sort_by = 'date';
185 }
186 $sort_order = ( isset( $instance['asc_sort_order'] ) && $instance['asc_sort_order'] ) ? 'ASC' : 'DESC';
187
188 // Get array of post info.
189 $args = array(
190 'orderby' => $sort_by,
191 'order' => $sort_order,
192 'ignore_sticky_posts' => 1, // Make sure we do not get stickies out of order.
193 'no_found_rows' => true, // Do not count the total numbers of rows by default.
194 );
195
196 $non_default_valid_status = array(
197 'publish',
198 'future',
199 'publish,future',
200 'private',
201 'private,publish',
202 'private,publish,future',
203 );
204 if ( isset( $instance['status'] ) && in_array( $instance['status'], $non_default_valid_status, true ) ) {
205 $args['post_status'] = $instance['status'];
206 }
207
208 if ( isset( $instance['num'] ) ) {
209 $args['showposts'] = (int) $instance['num'];
210 }
211
212 if ( isset( $instance['offset'] ) && ( (int) $instance['offset'] > 1 ) ) {
213 $args['offset'] = (int) $instance['offset'] - 1;
214 }
215 if ( isset( $instance['cat'] ) ) {
216 if ( isset( $instance['no_cat_childs'] ) && $instance['no_cat_childs'] ) {
217 $args['category__in'] = (int) $instance['cat'];
218 } else {
219 $args['cat'] = (int) $instance['cat'];
220 }
221 }
222
223 if ( is_singular() && isset( $instance['exclude_current_post'] ) && $instance['exclude_current_post'] ) {
224 $args['post__not_in'] = array( get_the_ID() );
225 }
226
227 if ( isset( $instance['hideNoThumb'] ) && $instance['hideNoThumb'] ) {
228 $args = array_merge( $args, array(
229 'meta_query' => array(
230 array(
231 'key' => '_thumbnail_id',
232 'compare' => 'EXISTS',
233 ),
234 ),
235 ) );
236 }
237
238 switch ( $instance['date_range'] ) {
239 case 'days_ago':
240 $ago = (int) $instance['days_ago'];
241
242 // If there is no valid integer value given, bail.
243 if ( 0 === $ago ) {
244 break;
245 }
246
247 $date = date( 'Y-m-d', strtotime( '-' . $ago . ' days' ) );
248 $args['date_query'] = array(
249 'after' => $date,
250 'inclusive' => true,
251 );
252 break;
253 case 'between_dates':
254 // Validation note - not doing any, assuming the query will
255 // fail gracefully enough for now as it is not clear what Should
256 // the validation be right now.
257 $start_date = $instance['start_date'];
258 $end_date = $instance['end_date'];
259 $args['date_query'] = array(
260 'after' => $start_date,
261 'before' => $end_date,
262 'inclusive' => true,
263 );
264 break;
265 }
266
267 return $args;
268 }
269
270 /**
271 * Calculate the HTML of the title based on the widget settings
272 *
273 * @param string $before_title The sidebar configured HTML that should come
274 * before the title itself.
275 * @param string $after_title The sidebar configured HTML that should come
276 * after the title itself.
277 * @param array $instance Array which contains the various settings.
278 * @return string The HTML for the title area
279 *
280 * @since 4.6
281 */
282 public function titleHTML( $before_title, $after_title, $instance ) {
283 $ret = '';
284
285 // If no title, use the name of the category.
286 if ( ! isset( $instance['title'] ) || ! $instance['title'] ) {
287 $instance['title'] = '';
288 if ( 0 !== (int) $instance['cat'] ) {
289 $category_info = get_category( $instance['cat'] );
290 if ( $category_info && ! is_wp_error( $category_info ) ) {
291 $instance['title'] = $category_info->name;
292 } else {
293 $instance['cat'] = 0; // For further processing treat it like "all categories".
294 $instance['title'] = __( 'Recent Posts', 'category-posts' );
295 }
296 } else {
297 $instance['title'] = __( 'Recent Posts', 'category-posts' );
298 }
299 }
300
301 if ( ! ( isset( $instance['hide_title'] ) && $instance['hide_title'] ) ) {
302 $ret = $before_title;
303 if ( isset( $instance['is_shortcode'] ) ) {
304 $title = esc_html( $instance['title'] );
305 } else {
306 $title = apply_filters( 'widget_title', $instance['title'] );
307 }
308
309 if ( isset( $instance['title_link'] ) && $instance['title_link'] ) {
310 if ( 0 !== $instance['cat'] ) {
311 $ret .= '<a href="' . get_category_link( $instance['cat'] ) . '">' . $title . '</a>';
312 } elseif ( isset( $instance['title_link_url'] ) && $instance['title_link_url'] ) {
313 $ret .= '<a href="' . esc_url( $instance['title_link_url'] ) . '">' . $title . '</a>';
314 } else {
315 $ret .= '<a href="' . esc_url( $this->blog_page_url() ) . '">' . $title . '</a>';
316 }
317 } else {
318 $ret .= $title;
319 }
320
321 $ret .= $after_title;
322 }
323
324 return $ret;
325 }
326
327 /**
328 * Get the URL of the blog page or home page if no explicit blog page is defined.
329 *
330 * @return string The URL of the blog page
331 *
332 * @since 4.8
333 */
334 private function blog_page_url() {
335
336 $blog_page = get_option( 'page_for_posts' );
337 if ( $blog_page ) {
338 $url = get_permalink( $blog_page );
339 } else {
340 $url = home_url();
341 }
342
343 return $url;
344 }
345
346 /**
347 * Calculate the HTML of the load more button based on the widget settings
348 *
349 * @param array $instance Array which contains the various settings.
350 *
351 * @return string The HTML for the load more area
352 *
353 * @since 4.9
354 */
355 public function loadMoreHTML( $instance ) {
356
357 if ( ! $instance['enable_loadmore'] ) {
358 return '';
359 }
360
361 $ret = '<div class="' . __NAMESPACE__ . '-loadmore">';
362 $context = 0;
363 if ( is_singular() ) {
364 $context = get_the_ID();
365 }
366
367 add_action( 'wp_footer', __NAMESPACE__ . '\embed_loadmore_scripts' );
368
369 // We rely on the widget number to be properly set.
370 // but need a slight different handling for proper widgets.
371 if ( is_int( $this->number ) ) {
372 // it is a proper widget, add the prefix.
373 $id = 'widget-' . $this->number;
374 } else {
375 $id = str_replace( WIDGET_BASE_ID . '-', '', $this->number );
376 }
377
378 $number = $instance['num'];
379 $start = $instance['offset'] + $number;
380 $loading = $instance['loading_text'];
381
382 $ret .= '<button type="button" data-loading="' . esc_attr( $loading ) . '" data-id="' . esc_attr( $id ) . '" data-start="' . esc_attr( $start ) . '" data-context="' . esc_attr( $context ) . '" data-number="' . esc_attr( $number ) . '">' . esc_html( $instance['loadmore_text'] ) . '</button>';
383 $ret .= '</div>';
384 return $ret;
385 }
386
387 /**
388 * Calculate the HTML of the footer based on the widget settings
389 *
390 * @param array $instance Array which contains the various settings.
391 * @return string The HTML for the footer area
392 *
393 * @since 4.6
394 */
395 public function footerHTML( $instance ) {
396
397 $ret = '';
398 $url = '';
399 $text = '';
400
401 if ( isset( $instance['footer_link'] ) ) {
402 $url = $instance['footer_link'];
403 }
404
405 if ( isset( $instance['footer_link_text'] ) ) {
406 $text = $instance['footer_link_text'];
407 }
408
409 // if url is set, but no text, just use the url as text.
410 if ( empty( $text ) && ! empty( $url ) ) {
411 $text = $url;
412 }
413
414 // if no url is set but just text, assume the url should be to the relevant archive page
415 // category archive for categories filter and home page or blog page when "all categories"
416 // is used.
417 if ( ! empty( $text ) && empty( $url ) ) {
418 if ( isset( $instance['cat'] ) && ( 0 !== $instance['cat'] ) && ( null !== get_category( $instance['cat'] ) ) ) {
419 $url = get_category_link( $instance['cat'] );
420 } else {
421 $url = $this->blog_page_url();
422 }
423 }
424
425 if ( ! empty( $url ) ) {
426 $ret .= '<a class="cat-post-footer-link" href="' . esc_url( $url ) . '">' . esc_html( $text ) . '</a>';
427 }
428
429 return $ret;
430 }
431
432 /**
433 * Current post item date string based on the format requested in the settings
434 *
435 * @param array $instance Array which contains the various settings.
436 * @param bool $everything_is_link Indicates whether the return string should avoid links.
437 *
438 * @since 4.8
439 */
440 public function itemDate( $instance, $everything_is_link ) {
441 $ret = '';
442
443 if ( ! isset( $instance['preset_date_format'] ) ) {
444 $preset_date_format = 'other';
445 } else {
446 $preset_date_format = $instance['preset_date_format'];
447 }
448
449 $attr = '';
450 switch ( $preset_date_format ) {
451 case 'sitedateandtime':
452 $date = get_the_time( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) );
453 break;
454 case 'localsitedateandtime':
455 $date = get_the_time( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) . ' GMT';
456 $time = get_post_time( 'U', true );
457 $attr = ' data-publishtime="' . $time . '" data-format="time"';
458 add_action( 'wp_footer', __NAMESPACE__ . '\embed_date_scripts' );
459 break;
460 case 'sitedate':
461 $date = get_the_time( get_option( 'date_format' ) );
462 break;
463 case 'localsitedate':
464 $date = get_the_time( get_option( 'date_format' ) ) . ' GMT';
465 $time = get_post_time( 'U', true );
466 $attr = ' data-publishtime="' . $time . '" data-format="date"';
467 add_action( 'wp_footer', __NAMESPACE__ . '\embed_date_scripts' );
468 break;
469 case 'sincepublished':
470 $date = human_time_diff( get_the_time( 'U' ), current_time( 'timestamp' ) );
471 break;
472 default:
473 if ( isset( $instance['date_format'] ) && strlen( trim( $instance['date_format'] ) ) > 0 ) {
474 $date_format = $instance['date_format'];
475 } else {
476 $date_format = 'j M Y';
477 }
478 $date = get_the_time( $date_format );
479 break;
480 }
481 $ret .= '<span class="cat-post-date"' . $attr . '>';
482 if ( isset( $instance['date_link'] ) && $instance['date_link'] && ! $everything_is_link ) {
483 $ret .= '<a href="' . \get_the_permalink() . '">';
484 }
485 $ret .= $date;
486
487 if ( isset( $instance['date_link'] ) && $instance['date_link'] && ! $everything_is_link ) {
488 $ret .= '</a>';
489 }
490 $ret .= '</span>';
491 return $ret;
492 }
493
494
495 /**
496 * Calculate the HTML for showing the thumb of a post item.
497 * Expected to be called from a loop with globals properly set.
498 *
499 * @param array $instance Array which contains the various settings.
500 * @param bool $no_link indicates whether the thumb should be wrapped in a link or a span.
501 * @return string The HTML for the thumb related to the post
502 *
503 * @since 4.6
504 */
505 public function itemThumb( $instance, $no_link ) {
506 $ret = '';
507
508 if ( ( isset( $instance['default_thunmbnail'] ) && ( $instance['default_thunmbnail'] ) ) || has_post_thumbnail() ) {
509 $class = '';
510 $disable_css = isset( $instance['disable_css'] ) && $instance['disable_css'];
511
512 if ( isset( $this->instance['thumb_hover'] ) && ! $disable_css ) {
513 $class = 'class="cat-post-thumbnail cat-post-' . $instance['thumb_hover'] . '"';
514 } else {
515 $class = 'class="cat-post-thumbnail"';
516 }
517
518 $title_args = array( 'echo' => false );
519
520 if ( $no_link ) {
521 $ret .= '<span ' . $class . '>';
522 } else {
523 $ret .= '<a ' . $class . ' href="' . get_the_permalink() . '" title="' . the_title_attribute( $title_args ) . '">';
524 }
525
526 $ret .= $this->the_post_thumbnail( array( $this->instance['thumb_w'], $this->instance['thumb_h'] ) );
527
528 if ( $no_link ) {
529 $ret .= '</span>';
530 } else {
531 $ret .= '</a>';
532 }
533 }
534
535 return $ret;
536 }
537
538 /**
539 * Current post item categories string
540 *
541 * @param array $instance Array which contains the various settings.
542 * @param bool $everything_is_link Indicates whether the return string should avoid links.
543 *
544 * @since 4.8
545 */
546 public function itemCategories( $instance, $everything_is_link ) {
547
548 $ret = '<span class="cat-post-tax-category">';
549 $cat_ids = wp_get_post_categories( get_the_ID(), array( 'number' => 0 ) );
550 foreach ( $cat_ids as $cat_id ) {
551 if ( $everything_is_link ) {
552 $ret .= ' ' . get_cat_name( $cat_id );
553 } else {
554 $ret .= " <a href='" . get_category_link( $cat_id ) . "'>" . get_cat_name( $cat_id ) . '</a>';
555 }
556 }
557 $ret .= '</span>';
558 return $ret;
559 }
560
561 /**
562 * Current post item tags string
563 *
564 * @param array $instance Array which contains the various settings.
565 * @param bool $everything_is_link Indicates whether the return string should avoid links.
566 *
567 * @since 4.8
568 */
569 public function itemTags( $instance, $everything_is_link ) {
570
571 $ret = '<span class="cat-post-tax-tag">';
572 $tag_ids = wp_get_post_tags( get_the_ID(), array( 'number' => 0 ) );
573 foreach ( $tag_ids as $tag_id ) {
574 if ( $everything_is_link ) {
575 $ret .= ' ' . $tag_id->name;
576 } else {
577 $ret .= " <a href='" . get_tag_link( $tag_id->term_id ) . "'>" . $tag_id->name . '</a>';
578 }
579 }
580 $ret .= '</span>';
581 return $ret;
582 }
583
584 /**
585 * Current post item comment number string
586 *
587 * @param array $instance Array which contains the various settings.
588 * @param bool $everything_is_link Indicates whether the return string should avoid links.
589 *
590 * @since 4.8
591 */
592 public function itemCommentNum( $instance, $everything_is_link ) {
593 global $post;
594
595 $ret = '<span class="cat-post-comment-num">';
596
597 if ( $everything_is_link ) {
598 $ret .= '(' . \get_comments_number() . ')';
599 } else {
600 $link = sprintf(
601 '<a href="%1$s" title="%2$s">(%3$d)</a>',
602 esc_url( get_comments_link( $post->ID ) ),
603 esc_attr( sprintf( __( '(%d) comments to this post' ), get_comments_number() ) ),
604 get_comments_number()
605 );
606 $ret .= $link;
607 }
608
609 $ret .= '</span>';
610 return $ret;
611 }
612
613 /**
614 * Current post item author string
615 *
616 * @param array $instance Array which contains the various settings.
617 * @param bool $everything_is_link Indicates whether the return string should avoid links.
618 *
619 * @since 4.8
620 */
621 public function itemAuthor( $instance, $everything_is_link ) {
622
623 $ret = '<span class="cat-post-author">';
624
625 if ( $everything_is_link ) {
626 $ret .= get_the_author();
627 } else {
628 $link = get_the_author_posts_link();
629 $ret .= $link;
630 }
631 $ret .= '</span>';
632 return $ret;
633 }
634
635 /**
636 * Current post item excerpt string
637 *
638 * @param array $instance Array which contains the various settings.
639 * @param bool $everything_is_link Indicates whether the return string should avoid links.
640 *
641 * @since 4.8
642 */
643 public function itemExcerpt( $instance, $everything_is_link ) {
644 global $post;
645
646 // use the_excerpt filter to get the "normal" excerpt of the post
647 // then apply our filter to let users customize excerpts in their own way.
648 if ( isset( $instance['excerpt_length'] ) && ( $instance['excerpt_length'] > 0 ) ) {
649 $length = (int) $instance['excerpt_length'];
650 } else {
651 $length = 999; // Use the wordpress default.
652 }
653
654 if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
655 $excerpt = apply_filters( 'the_excerpt', \get_the_excerpt() );
656 } else { // if filters off replicate functionality of core generating excerpt.
657 $more_text = '[&hellip;]';
658 if ( isset( $instance['excerpt_more_text'] ) && $instance['excerpt_more_text'] ) {
659 $more_text = ltrim( $instance['excerpt_more_text'] );
660 }
661
662 if ( $everything_is_link ) {
663 $excerpt_more_text = ' <span class="cat-post-excerpt-more">' . $more_text . '</span>';
664 } else {
665 $excerpt_more_text = ' <a class="cat-post-excerpt-more" href="' . get_permalink() . '" title="' . sprintf( __( 'Continue reading %s' ), get_the_title() ) . '">' . $more_text . '</a>';
666 }
667 if ( '' === $post->post_excerpt ) {
668 $text = get_the_content( '' );
669 $text = strip_shortcodes( $text );
670 $excerpt = \wp_trim_words( $text, $length, $excerpt_more_text );
671 // adjust html output same way as for the normal excerpt,
672 // just force all functions depending on the_excerpt hook.
673 $excerpt = shortcode_unautop( wpautop( convert_chars( convert_smilies( wptexturize( $excerpt ) ) ) ) );
674 } else {
675 $text = $post->post_excerpt;
676 $excerpt = \wp_trim_words( $text, $length, $excerpt_more_text );
677 $excerpt = shortcode_unautop( wpautop( convert_chars( convert_smilies( wptexturize( $excerpt ) ) ) ) );
678 }
679 }
680 $ret = apply_filters( 'cpw_excerpt', $excerpt, $this );
681 return $ret;
682 }
683
684 /**
685 * Current post item title string
686 *
687 * @param array $instance Array which contains the various settings.
688 * @param bool $everything_is_link Indicates whether the return string should avoid links.
689 *
690 * @since 4.8
691 */
692 public function itemTitle( $instance, $everything_is_link ) {
693
694 $ret = '';
695
696 if ( $everything_is_link ) {
697 $ret .= '<span class="cat-post-title">' . get_the_title() . '</span>';
698 } else {
699 $ret .= '<a class="cat-post-title"';
700 $ret .= ' href="' . get_the_permalink() . '" rel="bookmark">' . get_the_title();
701 $ret .= '</a>';
702 }
703 return $ret;
704 }
705
706 /**
707 * Calculate the HTML for a post item based on the widget settings and post.
708 * Expected to be called in an active loop with all the globals set.
709 *
710 * @param array $instance Array which contains the various settings.
711 * @param null|integer $current_post_id If on singular page specifies the id of
712 * the post, otherwise null.
713 * @return string The HTML for item related to the post
714 *
715 * @since 4.6
716 */
717 public function itemHTML( $instance, $current_post_id ) {
718 global $post;
719
720 $everything_is_link = isset( $instance['everything_is_link'] ) && $instance['everything_is_link'];
721 $wrap = isset( $instance['text_do_not_wrap_thumb'] ) && $instance['text_do_not_wrap_thumb'];
722
723 $template = '';
724 if ( isset( $instance['template'] ) ) {
725 $template = $instance['template'];
726 } else {
727 $template = convert_settings_to_template( $instance );
728 }
729 $ret = '<li ';
730
731 // Current post.
732 if ( $current_post_id === $post->ID ) {
733 $ret .= "class='cat-post-item cat-post-current'";
734 } else {
735 $ret .= "class='cat-post-item'";
736 }
737 $ret .= '>'; // close the li opening tag.
738
739 if ( $everything_is_link ) {
740 $ret .= '<a class="cat-post-everything-is-link" href="' . get_the_permalink() . '" title="">';
741 }
742
743 // Try to do smart formatting for floating thumb based on its location.
744 if ( $wrap ) {
745 if ( preg_match( '#(\%thumb\%)#', $template ) && ! preg_match( '#(\%thumb\%$)#', $template ) ) {
746 $thumb_flex = explode( '%thumb%', $template );
747 if ( 1 === count( $thumb_flex ) ) {
748 $template = '<div class="cat-post-do-not-wrap-thumbnail">%thumb%<div>' . $thumb_flex[0] . '</div></div>';
749 }
750 if ( 2 === count( $thumb_flex ) ) {
751 $template = $thumb_flex[0] . '<div class="cat-post-do-not-wrap-thumbnail">%thumb%<div>' . $thumb_flex[1] . '</div></div>';
752 }
753 }
754 }
755
756 // Post details (Template).
757 $widget = $this;
758 $template_res = preg_replace_callback( get_template_regex(), function ( $matches ) use ( $widget, $instance, $everything_is_link ) {
759 switch ( $matches[0] ) {
760 case '%title%':
761 return $widget->itemTitle( $instance, $everything_is_link );
762 case '%author%':
763 return $widget->itemAuthor( $instance, $everything_is_link );
764 case '%commentnum%':
765 return $widget->itemCommentNum( $instance, $everything_is_link );
766 case '%date%':
767 return $widget->itemDate( $instance, $everything_is_link );
768 case '%thumb%':
769 return $widget->itemThumb( $instance, $everything_is_link );
770 case '%post_tag%':
771 return $widget->itemTags( $instance, $everything_is_link );
772 case '%category%':
773 return $widget->itemCategories( $instance, $everything_is_link );
774 case '%excerpt%':
775 return $widget->itemExcerpt( $instance, $everything_is_link );
776 default:
777 return $matches[0];
778 }
779 }, $template );
780
781 // Replace empty line with closing and opening DIV.
782 $template_res = trim( $template_res );
783 $template_res = str_replace( "\n\r", '</div><div>', $template_res ); // in widget areas.
784 $template_res = str_replace( "\n\n", '</div><div>', $template_res ); // as shortcode.
785 $template_res = '<div>' . $template_res . '</div>';
786
787 // replace new lines with spaces.
788 $template_res = str_replace( "\n\r", ' ', $template_res ); // in widget areas.
789 $template_res = str_replace( "\n\n", ' ', $template_res ); // as shortcode.
790
791 $ret .= $template_res;
792
793 if ( $everything_is_link ) {
794 $ret .= '</a>';
795 }
796
797 $ret .= '</li>';
798 return $ret;
799 }
800
801 /**
802 * Filter to set the number of words in an excerpt
803 *
804 * @param int $length The number of words as configured by wordpress core or set by previous filters.
805 * @return int The number of words configured for the widget,
806 * or the $length parameter if it is not configured or garbage value.
807 *
808 * @since 4.6
809 */
810 public function excerpt_length_filter( $length ) {
811 if ( isset( $this->instance['excerpt_length'] ) && $this->instance['excerpt_length'] > 0 ) {
812 $length = $this->instance['excerpt_length'];
813 }
814 return $length;
815 }
816
817 /**
818 * Set the proper excerpt filters based on the settings
819 *
820 * @param array $instance widget settings.
821 * @return void
822 *
823 * @since 4.6
824 */
825 public function setExcerpFilters( $instance ) {
826
827 if ( isset( $instance['excerpt'] ) && $instance['excerpt'] ) {
828
829 // Excerpt length filter.
830 if ( isset( $instance['excerpt_length'] ) && ( (int) $instance['excerpt_length'] ) > 0 ) {
831 add_filter( 'excerpt_length', array( $this, 'excerpt_length_filter' ) );
832 }
833
834 if ( isset( $instance['excerpt_more_text'] ) && ( '' !== ltrim( $instance['excerpt_more_text'] ) ) ) {
835 add_filter( 'excerpt_more', array( $this, 'excerpt_more_filter' ) );
836 }
837
838 add_filter( 'the_excerpt', array( $this, 'apply_the_excerpt' ) );
839 }
840 }
841
842 /**
843 * Remove the excerpt filter
844 *
845 * @param array $instance widget settings.
846 * @return void
847 *
848 * @since 4.6
849 */
850 public function removeExcerpFilters( $instance ) {
851 remove_filter( 'excerpt_length', array( $this, 'excerpt_length_filter' ) );
852 remove_filter( 'excerpt_more', array( $this, 'excerpt_more_filter' ) );
853 add_filter( 'get_the_excerpt', 'wp_trim_excerpt' );
854 remove_filter( 'the_excerpt', array( $this, 'apply_the_excerpt' ) );
855 }
856
857 /**
858 * The main widget display controller
859 *
860 * Called by the sidebar processing core logic to display the widget.
861 *
862 * @param array $args An array containing the "environment" setting for the widget,
863 * namely, the enclosing tags for the widget and its title.
864 * @param array $instance The settings associate with the widget.
865 *
866 * @since 4.1
867 */
868 public function widget( $args, $instance ) {
869
870 $instance = upgrade_settings( $instance );
871
872 extract( $args );
873
874 $this->instance = $instance;
875
876 $current_post_id = '';
877 if ( is_singular() ) {
878 $current_post_id = get_the_ID();
879 }
880
881 $items = $this->get_elements_HTML( $instance, $current_post_id, 0, 0 );
882
883 if ( ( 'nothing' === $instance['no_match_handling'] ) || ! empty( $items ) ) {
884 echo $before_widget; // Xss ok. This is how widget actually expected to behave.
885 echo $this->titleHTML( $before_title, $after_title, $instance );
886
887 $thumb = isset( $this->instance['template'] ) && preg_match( '/%thumb%/', $this->instance['template'] );
888
889 if ( ! ( isset( $instance['is_shortcode'] ) && $instance['is_shortcode'] ) ) { // the internal id is needed only for widgets.
890 echo '<ul id="' . esc_attr( WIDGET_BASE_ID ) . '-' . esc_attr( $this->number ) . '-internal" class="' . esc_attr( WIDGET_BASE_ID ) . '-internal' . "\">\n";
891 } else {
892 echo '<ul>';
893 }
894
895 // image crop browser fallback and workaround, no polyfill.
896 if ( $thumb ) {
897 if ( apply_filters( 'cpw_enqueue_resources', false ) ) {
898 frontend_script();
899 } else {
900 add_action( 'wp_footer', __NAMESPACE__ . '\embed_front_end_scripts' );
901 }
902 }
903
904 // set widget filters.
905 if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
906 $this->setExcerpFilters( $instance );
907 }
908
909 foreach ( $items as $item ) {
910 echo $item;
911 }
912 echo "</ul>\n";
913
914 // Load more only if we think we have more items.
915 if ( count( $items ) === (int) $instance['num'] ) {
916 echo $this->loadMoreHTML( $instance );
917 }
918
919 echo $this->footerHTML( $instance );
920 echo $after_widget; // Xss ok. This is how widget actually expected to behave.
921
922 // remove widget filters.
923 if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
924 $this->removeExcerpFilters( $instance );
925 }
926
927 wp_reset_postdata();
928 } elseif ( 'text' === $instance['no_match_handling'] ) {
929 echo $before_widget; // Xss ok. This is how widget actually expected to behave.
930 echo $this->titleHTML( $before_title, $after_title, $instance );
931 echo esc_html( $instance['no_match_text'] );
932 echo $this->footerHTML( $instance );
933 echo $after_widget; // Xss ok. This is how widget actually expected to behave.
934 }
935 }
936
937 /**
938 * Get an array of HTML pre item, for item starting from a specific position.
939 *
940 * @since 4.9
941 *
942 * @param array $instance An array containing the settings of the widget.
943 * @param string $singular_id The ID of the post in which the widget is rendered,
944 * an empty string indicates the rendering context
945 * is not singular.
946 * @param int $start The start element (0 based).
947 * @param int $number The maximal number of elements to return. A value of 0
948 * Indicates to use the widget settings for that.
949 *
950 * @return string[] Array of HTML per element with the $start element first
951 * $start+1 next etc. An empty array is returned if there
952 * are no applicable items.
953 */
954 public function get_elements_HTML( $instance, $singular_id, $start, $number ) {
955 $ret = array();
956
957 if ( 0 === count( $instance ) ) {
958 $instance = default_settings();
959 }
960
961 $this->instance = $instance;
962
963 if($start > 0) {
964 $instance['offset'] = $start;
965 }
966 $number = (int) $number; // sanitize number with the side effect of non
967 // numbers are converted to zero.
968 if ( 0 < $number ) {
969 $instance['num'] = $number;
970 }
971 $args = $this->queryArgs( $instance );
972 $cat_posts = new \WP_Query( $args );
973 $current_post_id = null;
974 if ( '' !== $singular_id ) {
975 $current_post_id = (int) $singular_id;
976 }
977
978 while ( $cat_posts->have_posts() ) {
979 $cat_posts->the_post();
980 $ret[] = $this->itemHTML( $instance, $current_post_id );
981 }
982
983 wp_reset_postdata();
984
985 return $ret;
986 }
987
988 /**
989 * Update the options.
990 *
991 * @param array $new_instance The new settings of the widget.
992 * @param array $old_instance The current settings of the widget.
993 * @return array
994 */
995 public function update( $new_instance, $old_instance ) {
996
997 $new_instance['title'] = sanitize_text_field( $new_instance['title'] ); // sanitize the title like core widgets do.
998 if ( ! isset( $new_instance['excerpt_filters'] ) ) {
999 $new_instance['excerpt_filters'] = '';
1000 }
1001 if ( current_user_can( 'unfiltered_html' ) ) {
1002 $instance['text'] = $new_instance['template'];
1003 } else {
1004 $instance['text'] = wp_kses_post( $new_instance['template'] );
1005 }
1006
1007 // Set the version of the DB structure.
1008 $new_instance['ver'] = VERSION;
1009 return $new_instance;
1010 }
1011
1012 /**
1013 * Output the title panel of the widget configuration form.
1014 *
1015 * @param array $instance The widget's settings.
1016 * @return void
1017 *
1018 * @since 4.6
1019 */
1020 public function formTitlePanel( $instance ) {
1021 $cat = (int) $instance['cat'];
1022
1023 $hide_title = false;
1024 if ( isset( $instance['hide_title'] ) && $instance['hide_title'] ) {
1025 $hide_title = true;
1026 }
1027 ?>
1028 <h4 data-panel="title"><?php esc_html_e( 'Title', 'category-posts' ); ?></h4>
1029 <div class="cpwp_ident">
1030 <?php echo $this->get_checkbox_block_html( $instance, 'hide_title', esc_html__( 'Hide title', 'category-posts' ), true ); ?>
1031 <div class="categoryposts-data-panel-title-settings" <?php echo ( $hide_title ) ? 'style="display:none"' : ''; ?>>
1032 <?php echo $this->get_text_input_block_html( $instance, 'title', esc_html__( 'Title', 'category-posts' ), '', true ); ?>
1033 <?php echo $this->get_checkbox_block_html( $instance, 'title_link', esc_html__( 'Make widget title link', 'category-posts' ), 0 !== $cat ); ?>
1034 <?php echo $this->get_text_input_block_html( $instance, 'title_link_url', esc_html__( 'Title link URL', 'category-posts' ), '', 0 === $cat ); ?>
1035 </div>
1036 </div>
1037 <?php
1038 }
1039
1040 /**
1041 * Output the filter panel of the widget configuration form.
1042 *
1043 * @param array $instance The parameters configured for the widget.
1044 * @return void
1045 *
1046 * @since 4.6
1047 */
1048 public function formFilterPanel( $instance ) {
1049 $cat = $instance['cat'];
1050 ?>
1051 <h4 data-panel="filter"><?php esc_html_e( 'Filter', 'category-posts' ); ?></h4>
1052 <div>
1053 <p>
1054 <label>
1055 <?php esc_html_e( 'Category', 'category-posts' ); ?>:
1056 <?php
1057 wp_dropdown_categories( array(
1058 'show_option_all' => __( 'All categories', 'category-posts' ),
1059 'hide_empty' => 0,
1060 'name' => $this->get_field_name( 'cat' ),
1061 'selected' => $instance['cat'],
1062 'class' => 'categoryposts-data-panel-filter-cat',
1063 ) );
1064 ?>
1065 </label>
1066 </p>
1067 <?php
1068 echo $this->get_checkbox_block_html( $instance, 'no_cat_childs', esc_html__( 'Exclude child categories', 'category-posts' ), ! empty( $instance['cat'] ) );
1069 echo $this->get_select_block_html( $instance, 'status', esc_html__( 'Status', 'category-posts' ), array(
1070 'default' => esc_html__( 'WordPress Default', 'category-posts' ),
1071 'publish' => esc_html__( 'Published', 'category-posts' ),
1072 'future' => esc_html__( 'Scheduled', 'category-posts' ),
1073 'private' => esc_html__( 'Private', 'category-posts' ),
1074 'publish,future' => esc_html__( 'Published or Scheduled', 'category-posts' ),
1075 'private,publish' => esc_html__( 'Published or Private', 'category-posts' ),
1076 'private,future' => esc_html__( 'Private or Scheduled', 'category-posts' ),
1077 'private,publish,future' => esc_html__( 'Published, Private or Scheduled', 'category-posts' ),
1078 ), 'default', true );
1079 echo $this->get_number_input_block_html( $instance, 'num', esc_html__( 'Number of posts to show', 'category-posts' ), 1, '', '', true );
1080 echo $this->get_number_input_block_html( $instance, 'offset', esc_html__( 'Start with post', 'category-posts' ), 1, '', '', true );
1081 echo $this->get_select_block_html( $instance, 'date_range', esc_html__( 'Date Range', 'category-posts' ), array(
1082 'off' => esc_html__( 'Off', 'category-posts' ),
1083 'days_ago' => esc_html__( 'Days ago', 'category-posts' ),
1084 'between_dates' => esc_html__( 'Between dates', 'category-posts' ),
1085 ), 'off', true );
1086 ?>
1087 <div class="cpwp_ident categoryPosts-date-range" style="display:<?php echo 'off' === $instance['date_range'] ? 'none' : 'block'; ?>">
1088 <?php
1089 echo $this->get_number_input_block_html( $instance, 'days_ago', esc_html__( 'Up to', 'category-posts' ), 1, '', '', 'days_ago' === $instance['date_range'] );
1090 echo $this->get_date_input_block_html( $instance, 'start_date', esc_html__( 'After', 'category-posts' ), 'between_dates' === $instance['date_range'] );
1091 echo $this->get_date_input_block_html( $instance, 'end_date', esc_html__( 'Before', 'category-posts' ), 'between_dates' === $instance['date_range'] );
1092 ?>
1093 </div>
1094 <?php
1095 echo $this->get_select_block_html( $instance, 'sort_by', esc_html__( 'Sort by', 'category-posts' ), array(
1096 'date' => esc_html__( 'Date', 'category-posts' ),
1097 'title' => esc_html__( 'Title', 'category-posts' ),
1098 'comment_count' => esc_html__( 'Number of comments', 'category-posts' ),
1099 'rand' => esc_html__( 'Random', 'category-posts' ),
1100 ), 'date', true );
1101 echo $this->get_checkbox_block_html( $instance, 'asc_sort_order', esc_html__( 'Reverse sort order (ascending)', 'category-posts' ), true );
1102 echo $this->get_checkbox_block_html( $instance, 'exclude_current_post', esc_html__( 'Exclude current post', 'category-posts' ), true );
1103 echo $this->get_checkbox_block_html( $instance, 'hideNoThumb', esc_html__( 'Exclude posts which have no thumbnail', 'category-posts' ), true );
1104 ?>
1105 </div>
1106 <?php
1107 }
1108
1109 /**
1110 * Generate the wrapper P around a form input element
1111 *
1112 * @since 4.8
1113 * @param string $html The HTML to wrap.
1114 * @param string $key The key to use as the prefix to the class.
1115 * @param bool $visible Indicates if the element should be visible when rendered.
1116 *
1117 * @return string HTML with P element contaning the html being passed with class based on the key
1118 * and style set to display:none if visibility is off.
1119 */
1120 private function get_wrap_block_html( $html, $key, $visible ) {
1121
1122 $cl = ' class="' . __NAMESPACE__ . '-' . esc_attr( $key ) . '"';
1123
1124 $style = '';
1125 if ( ! $visible ) {
1126 $style = ' style="display:none"';
1127 }
1128 $ret = '<p' . $cl . $style . ">\n" . $html . "</p>\n";
1129
1130 return $ret;
1131 }
1132
1133 /**
1134 * Generate a form P element containing a select element
1135 *
1136 * @since 4.8
1137 * @param array $instance The instance.
1138 * @param string $key The key in the instance array.
1139 * @param string $label The label to display and associate with the input.
1140 * @param array $list An array of pairs value (index) => label to be used for the options.
1141 * The labels are expected to be html escaped.
1142 * @param int $default The value to use if the key is not set in the instance.
1143 * @param bool $visible Indicates if the element should be visible when rendered.
1144 *
1145 * @return string HTML a P element contaning the select, its label, class based on the key
1146 * and style set to display:none if visibility is off.
1147 */
1148 private function get_select_block_html( $instance, $key, $label, $list, $default, $visible ) {
1149 $value = $instance[ $key ];
1150
1151 if ( ! array_key_exists( $value, $list ) ) {
1152 $value = $default;
1153 }
1154
1155 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1156 $label .
1157 "</label>\n" .
1158 '<select id="' . $this->get_field_id( $key ) . '" name="' . $this->get_field_name( $key ) . '" autocomplete="off">' . "\n";
1159 foreach ( $list as $v => $l ) {
1160 $ret .= '<option value="' . esc_attr( $v ) . '" ' . selected( $v, $value, false ) . '>' . $l . "</option>\n";
1161 }
1162 $ret .= "</select>\n";
1163
1164 return $this->get_wrap_block_html( $ret, $key, $visible );
1165 }
1166
1167 /**
1168 * Generate a form P element containing a textarea input
1169 *
1170 * @since 4.8
1171 * @param array $instance The instance.
1172 * @param string $key The key in the instance array.
1173 * @param string $label The label to display and associate with the input (should be html escaped).
1174 * @param string $placeholder The placeholder to use in the input (should be attribute escaped).
1175 * @param bool $visible Indicates if the element should be visible when rendered.
1176 * @param int $num_rows Number of rows.
1177 *
1178 * @return string HTML a P element containing the input, its label, class based on the key
1179 * and style set to display:none if visibility is off.
1180 */
1181 private function get_textarea_html( $instance, $key, $label, $placeholder, $visible, $num_rows ) {
1182
1183 $value = $instance[ $key ];
1184
1185 $ret = '<label for="' . esc_attr( $this->get_field_id( $key ) ) . '">' . $label .
1186 '<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>' .
1187 '</label>';
1188
1189 return $this->get_wrap_block_html( $ret, $key, $visible );
1190 }
1191
1192 /**
1193 * Generate a form P element containing a text input
1194 *
1195 * @since 4.8
1196 * @param array $instance The instance.
1197 * @param string $key The key in the instance array.
1198 * @param string $label The label to display and associate with the input.
1199 * Should be html escaped.
1200 * @param string $placeholder The placeholder to use in the input. should be attribute escaped.
1201 * @param bool $visible Indicates if the element should be visible when rendered.
1202 *
1203 * @return string HTML a P element contaning the input, its label, class based on the key
1204 * and style set to display:none if visibility is off.
1205 */
1206 private function get_text_input_block_html( $instance, $key, $label, $placeholder, $visible ) {
1207
1208 $value = $instance[ $key ];
1209
1210 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1211 $label . "\n" .
1212 '<input placeholder="' . $placeholder . '" id="' . $this->get_field_id( $key ) . '" name="' . $this->get_field_name( $key ) . '" type="text" value="' . esc_attr( $value ) . '" autocomplete="off"/>' . "\n" .
1213 "</label>\n";
1214
1215 return $this->get_wrap_block_html( $ret, $key, $visible );
1216 }
1217
1218 /**
1219 * Generate a form P element containing a range input
1220 *
1221 * @since 4.8
1222 * @param array $instance The instance.
1223 * @param string $key The key in the instance array.
1224 * @param string $label The label to display and associate with the input.
1225 * expected to be escaped.
1226 * @param int $min The minimum value allowed to be input.
1227 * @param int $max The maximum value allowed to be input.
1228 * @param string $value The start value
1229 * @param string $step The range of each step
1230 * @param bool $visible Indicates if the element should be visible when rendered.
1231 *
1232 * @return string HTML a P element contaning the input, its label, class based on the key
1233 * and style set to display:none if visibility is off.
1234 */
1235 private function get_range_input_block_html( $instance, $key, $label, $min, $max, $value, $step, $visible ) {
1236
1237 $value = $instance[ $key ];
1238
1239 $minMaxStep = '';
1240 if ( '' !== $min ) {
1241 $minMaxStep .= ' min="' . $min . '"';
1242 }
1243 if ( '' !== $max ) {
1244 $minMaxStep .= ' max="' . $max . '"';
1245 }
1246 if ( '' !== $step ) {
1247 $minMaxStep .= ' step="' . $step . '"';
1248 }
1249
1250 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1251 esc_html( $label ) . " <span>" . $value . "%</span>\n" .
1252 '<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" .
1253 "</label>\n";
1254
1255 return $this->get_wrap_block_html( $ret, $key, $visible );
1256 }
1257
1258 /**
1259 * Generate a form P element containing a number input
1260 *
1261 * @since 4.8
1262 * @param array $instance The instance.
1263 * @param string $key The key in the instance array.
1264 * @param string $label The label to display and associate with the input.
1265 * expected to be escaped.
1266 * @param int $min The minimum value allowed to be input.
1267 * @param int $max The maximum value allowed to be input.
1268 * @param string $placeholder The placeholder string to be used. expected to be escaped.
1269 * @param bool $visible Indicates if the element should be visible when rendered.
1270 *
1271 * @return string HTML a P element contaning the input, its label, class based on the key
1272 * and style set to display:none if visibility is off.
1273 */
1274 private function get_number_input_block_html( $instance, $key, $label, $min, $max, $placeholder, $visible ) {
1275
1276 $value = $instance[ $key ];
1277
1278 $minmax = '';
1279 if ( '' !== $min ) {
1280 $minmax .= ' min="' . $min . '"';
1281 }
1282 if ( '' !== $max ) {
1283 $minmax .= ' max="' . $max . '"';
1284 }
1285
1286 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1287 esc_html( $label ) . "\n" .
1288 '<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" .
1289 "</label>\n";
1290
1291 return $this->get_wrap_block_html( $ret, $key, $visible );
1292 }
1293
1294 /**
1295 * Generate a form P element containing a date input
1296 *
1297 * @since 4.9
1298 * @param array $instance The instance.
1299 * @param string $key The key in the instance array.
1300 * @param string $label The label to display and associate with the input.
1301 * expected to be escaped.
1302 * @param bool $visible Indicates if the element should be visible when rendered.
1303 *
1304 * @return string HTML a P element containing the input, its label, class based on the key
1305 * and style set to display:none if visibility is off.
1306 */
1307 private function get_date_input_block_html( $instance, $key, $label, $visible ) {
1308
1309 $value = $instance[ $key ];
1310
1311 $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1312 esc_html( $label ) . "\n" .
1313 '<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" .
1314 "</label>\n";
1315
1316 return $this->get_wrap_block_html( $ret, $key, $visible );
1317 }
1318
1319 /**
1320 * Generate a form P element containing a checkbox input
1321 *
1322 * @since 4.8
1323 * @param array $instance The instance.
1324 * @param string $key The key in the instance array.
1325 * @param string $label The label to display and associate with the checkbox.
1326 * should be escaped string.
1327 * @param bool $visible Indicates if the element should be visible when rendered.
1328 *
1329 * @return string HTML a P element contaning the checkbox, its label, class based on the key
1330 * and style set to display:none if visibility is off.
1331 */
1332 private function get_checkbox_block_html( $instance, $key, $label, $visible ) {
1333
1334 if ( array_key_exists( $key, $instance ) ) {
1335 if ( $instance[ $key ] ) {
1336 $value = true;
1337 } else {
1338 $value = false;
1339 }
1340 }
1341 $ret = '<label class="checkbox" for="' . esc_attr( $this->get_field_id( $key ) ) . "\">\n" .
1342 '<input id="' . esc_attr( $this->get_field_id( $key ) ) . '" name="' . esc_attr( $this->get_field_name( $key ) ) . '" type="checkbox" ' . checked( $value, true, false ) . '/>' . "\n" .
1343 $label .
1344 "</label>\n";
1345
1346 return $this->get_wrap_block_html( $ret, $key, $visible );
1347 }
1348
1349 /**
1350 * Generate a form button element containing
1351 *
1352 * @since 4.9
1353 * @param array $instance The instance.
1354 * @param string $key The key in the instance array.
1355 * @param string $label The label to display and associate with the button.
1356 * should be escaped string.
1357 *
1358 * @return string HTML a button element and class based on the key.
1359 */
1360 private function get_button_thumb_size_html( $instance, $key, $label ) {
1361
1362 $datas = "";
1363
1364 switch ( $key ) {
1365 case "thumb":
1366 $datas = 'data-thumb-w="' . get_option( "thumbnail_size_w" ) . '" data-thumb-h="' . get_option( "thumbnail_size_h" ) . '"';
1367 break;
1368 case "medium":
1369 $datas = 'data-thumb-w="' . get_option( "medium_size_w" ) . '" data-thumb-h="' . get_option( "medium_size_h" ) . '"';
1370 break;
1371 case "large":
1372 $datas = 'data-thumb-w="' . get_option( "large_size_w" ) . '" data-thumb-h="' . get_option( "large_size_h" ) . '"';
1373 break;
1374 }
1375 $ret = '<button type="button" ' . $datas . ' class="' . $key . ' button">' . esc_html( $label ) . "</button>\n";
1376
1377 return $ret;
1378 }
1379
1380 /**
1381 * The widget configuration form back end.
1382 *
1383 * @param array $instance The parameters associated with the widget.
1384 * @return void
1385 */
1386 public function form( $instance ) {
1387 if ( 0 === count( $instance ) ) { // new widget, use defaults.
1388 $instance = default_settings();
1389 } else { // updated widgets come from =< 4.6 excerpt filter is on.
1390 if ( ! isset( $instance['excerpt_filters'] ) ) {
1391 $instance['excerpt_filters'] = 'on';
1392 }
1393 }
1394
1395 $instance = upgrade_settings( $instance );
1396
1397 $hide_post_titles = $instance['hide_post_titles'];
1398 $excerpt_more_text = $instance['excerpt_more_text'];
1399 $excerpt_filters = $instance['excerpt_filters'];
1400 $date_format = $instance['date_format'];
1401 $disable_css = $instance['disable_css'];
1402 $disable_font_styles = $instance['disable_font_styles'];
1403 $preset_date_format = $instance['preset_date_format'];
1404 $thumb = ! empty( $instance['thumb'] );
1405 $thumb_w = $instance['thumb_w'];
1406 $thumb_fluid_width = $instance['thumb_fluid_width'];
1407 $thumb_h = $instance['thumb_h'];
1408 $default_thunmbnail = $instance['default_thunmbnail'];
1409 $use_css_cropping = $instance['use_css_cropping'];
1410 $text_do_not_wrap_thumb = $instance['text_do_not_wrap_thumb'];
1411 ?>
1412
1413 <div class="category-widget-cont">
1414 <?php if ( ! class_exists( '\\termcategoryPostsPro\\Widget' ) ) { ?>
1415 <p><a target="_blank" href="http://tiptoppress.com/term-and-category-based-posts-widget/"><?php esc_html_e( 'Get the Pro version', 'category-posts' ); ?></a></p>
1416 <?php
1417 }
1418 $this->formTitlePanel( $instance );
1419 $this->formFilterPanel( $instance );
1420 ?>
1421 <h4 data-panel="details"><?php esc_html_e( 'Post details', 'category-posts' ); ?></h4>
1422 <div class="cpwp-sub-panel">
1423 <?php
1424 $template = '';
1425 if ( ! isset( $instance['template'] ) ) {
1426 $template = convert_settings_to_template( $instance );
1427 } else {
1428 $template = $instance['template'];
1429 }
1430 ?>
1431 <p><?php esc_html_e( 'Displayed parts', 'category-posts' ); ?></p>
1432 <div class="cpwp_ident">
1433 <?php
1434 $label = esc_html__( 'Template', 'category-posts' ) .
1435 ' <a href="#" class="dashicons toggle-template-help dashicons-editor-help imgedit-help-toggle"><span class="screen-reader-text">' .
1436 esc_html__( 'Show template help', 'category-posts' ) . '</span></a>';
1437 $class_placement = "";
1438 if ( is_customize_preview() ) {
1439 $class_placement = "customizer";
1440 } else {
1441 $class_placement = "admin-panel";
1442 }
1443 $label .= '<span class="cat-post-add_premade_templates ' . $class_placement . '">' .
1444 '<button type="button" class="button cpwp-open-placholder-dropdown-menu"> + ' . esc_html__( 'Add Placeholder', 'category-posts' ) . '</button>' .
1445 '<span class="cpwp-placeholder-dropdown-menu">' .
1446 '<span data-value="NewLine">' . esc_html__( 'New line', 'category-posts' ) . '</span>' .
1447 '<span data-value="EmptyLine">' . esc_html__( 'Empty line', 'category-posts' ) . '</span>' .
1448 '<span data-value="title">' . esc_html__( '%title%', 'category-posts' ) . '</span>' .
1449 '<span data-value="thumb">' . esc_html__( '%thumb%', 'category-posts' ) . '</span>' .
1450 '<span data-value="date">' . esc_html__( '%date%', 'category-posts' ) . '</span>' .
1451 '<span data-value="excerpt">' . esc_html__( '%excerpt%', 'category-posts' ) . '</span>' .
1452 '<span data-value="author">' . esc_html__( '%author%', 'category-posts' ) . '</span>' .
1453 '<span data-value="commentnum">' . esc_html__( '%commentnum%', 'category-posts' ) . '</span>' .
1454 '<span data-value="post_tag">' . esc_html__( '%post_tag%', 'category-posts' ) . '</span>' .
1455 '<span data-value="category">' . esc_html__( '%category%', 'category-posts' ) . '</span>' .
1456 '</span>' .
1457 '</span>';
1458 ?>
1459 <?php
1460 echo $this->get_textarea_html( $instance, 'template', $label , '', true, 8 );
1461 preg_match_all( get_template_regex(), $template, $matches );
1462 $tags = array();
1463 if ( ! empty( $matches[0] ) ) {
1464 $tags = array_flip( $matches[0] );
1465 }
1466 ?>
1467 <div class="cat-post-template-help">
1468 <p><?php esc_html_e( 'The following text will be replaced with the relevant information. In addition you can use any text and html (if you have the permisions) anywhere you want', 'category-posts' ); ?>
1469 </p>
1470 <table>
1471 <tr>
1472 <th><?php esc_html_e( 'New line', 'category-posts' ); ?></th>
1473 <td><?php esc_html_e( 'Space', 'category-posts' ); ?></td>
1474 </tr>
1475 <tr>
1476 <th><?php esc_html_e( 'Empty line', 'category-posts' ); ?></th>
1477 <td><?php esc_html_e( 'Next line is a paragraph', 'category-posts' ); ?></td>
1478 </tr>
1479 <tr>
1480 <th>%title%</th>
1481 <td><?php esc_html_e( 'Post title', 'category-posts' ); ?></td>
1482 </tr>
1483 <tr>
1484 <th>%thumb%</th>
1485 <td><?php esc_html_e( 'Post thumbnail possibly wrapped by text', 'category-posts' ); ?></td>
1486 </tr>
1487 <tr>
1488 <th>%date%</th>
1489 <td><?php esc_html_e( 'Post publish date', 'category-posts' ); ?></td>
1490 </tr>
1491 <tr>
1492 <th>%excerpt%</th>
1493 <td><?php esc_html_e( 'Post excerpt', 'category-posts' ); ?></td>
1494 </tr>
1495 <tr>
1496 <th>%author%</th>
1497 <td><?php esc_html_e( 'Post author', 'category-posts' ); ?></td>
1498 </tr>
1499 <tr>
1500 <th>%commentnum%</th>
1501 <td><?php esc_html_e( 'The number of comments to the post', 'category-posts' ); ?></td>
1502 </tr>
1503 <tr>
1504 <th>%post_tag%</th>
1505 <td><?php esc_html_e( 'Post tags', 'category-posts' ); ?></td>
1506 </tr>
1507 <tr>
1508 <th>%category%</th>
1509 <td><?php esc_html_e( 'Post categories', 'category-posts' ); ?></td>
1510 </tr>
1511 </table>
1512 </div>
1513 <div class="cat-post-premade_templates">
1514 <p><label><?php esc_html_e( 'Select premade Template', 'category-posts' ); ?></label></p>
1515 <select>
1516 <option value="title"><?php esc_html_e( 'Only the title', 'category-posts' ); ?></option>
1517 <option value="title_excerpt"><?php esc_html_e( 'Title and Excerpt', 'category-posts' ); ?></option>
1518 <option value="title_thumb"><?php esc_html_e( 'Title and Thumbnail', 'category-posts' ); ?></option>
1519 <option value="title_thum_excerpt"><?php esc_html_e( 'Title, Thumbnail and Excerpt', 'category-posts' ); ?></option>
1520 <option value="everything"><?php esc_html_e( 'All with icons', 'category-posts' ); ?></option>
1521 </select>
1522 <p><button type="button" class="button"><?php esc_html_e( 'Select this template', 'category-posts' ); ?></button></p>
1523 <?php
1524 echo $this->get_checkbox_block_html( $instance, 'everything_is_link', esc_html__( 'Everything is a link', 'category-posts' ), true );
1525 ?>
1526 </div>
1527 </div>
1528 <?php // Excerpt settings. ?>
1529 <div class="cpwp-sub-panel categoryposts-data-panel-excerpt" style="display:<?php echo ( isset( $tags['%excerpt%'] ) ) ? 'block' : 'none'; ?>">
1530 <p><?php esc_html_e( 'Excerpt settings', 'category-posts' ); ?></p>
1531 <div class="cpwp_ident">
1532 <?php
1533 echo $this->get_number_input_block_html( $instance, 'excerpt_lines', esc_html__( 'Lines (responsive):', 'category-posts' ), 0, '', '', true );
1534 echo $this->get_number_input_block_html( $instance, 'excerpt_length', esc_html__( 'Length (words):', 'category-posts' ), 0, '', '', true );
1535 echo $this->get_text_input_block_html( $instance, 'excerpt_more_text', esc_html__( '\'More ...\' text:', 'category-posts' ), esc_attr__( '...', 'category-posts' ), true );
1536 ?>
1537 </div>
1538 </div>
1539 <?php // Data settings ?>
1540 <div class="cpwp-sub-panel categoryposts-data-panel-date" style="display:<?php echo ( isset( $tags['%date%'] ) ) ? 'block' : 'none'; ?>">
1541 <p><?php esc_html_e( 'Date format settings', 'category-posts' ); ?></p>
1542 <div class="cpwp_ident">
1543 <?php
1544 echo $this->get_select_block_html( $instance, 'preset_date_format', esc_html__( 'Date format', 'category-posts' ), array(
1545 'sitedateandtime' => esc_html__( 'Site date and time', 'category-posts' ),
1546 'sitedate' => esc_html__( 'Site date', 'category-posts' ),
1547 'sincepublished' => esc_html__( 'Time since published', 'category-posts' ),
1548 'localsitedateandtime' => esc_html__( 'Reader\'s local date and time', 'category-posts' ),
1549 'localsitedate' => esc_html__( 'Reader\'s local date', 'category-posts' ),
1550 'other' => esc_html__( 'PHP style format', 'category-posts' ),
1551 ), 'sitedateandtime', true );
1552 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 );
1553 ?>
1554 </div>
1555 </div>
1556 <?php // Thumbnail settings. ?>
1557 <div class="cpwp-sub-panel categoryposts-data-panel-thumb" style="display:<?php echo ( isset( $tags['%thumb%'] ) ) ? 'block' : 'none'; ?>">
1558 <p><?php esc_html_e( 'Thumbnail settings', 'category-posts' ); ?></p>
1559 <div class="cpwp_ident">
1560 <p><?php esc_html_e( 'Thumbnail dimensions (pixel)', 'category-posts' ); ?></p>
1561 <?php
1562 echo $this->get_number_input_block_html( $instance, 'thumb_w', esc_html__( 'Width:', 'category-posts' ), 1, '', '', true );
1563 echo $this->get_range_input_block_html( $instance, 'thumb_fluid_width', esc_html__( 'Max-width:', 'category-posts' ), 2, 100, 100, 2, true );
1564 echo $this->get_number_input_block_html( $instance, 'thumb_h', esc_html__( 'Height:', 'category-posts' ), 1, '', '', true );
1565 ?>
1566 <div class="cat-post-thumb-change-size">
1567 <p>
1568 <label><?php esc_html_e( 'Change size', 'category-posts' ); ?>: </label>
1569 <span class="cpwp-right">
1570 <?php
1571 echo $this->get_button_thumb_size_html( $instance, 'smaller', esc_html__( '-', 'category-posts' ) );
1572 echo $this->get_button_thumb_size_html( $instance, 'quarter', esc_html__( '1/4', 'category-posts' ) );
1573 echo $this->get_button_thumb_size_html( $instance, 'half', esc_html__( '1/2', 'category-posts' ) );
1574 echo $this->get_button_thumb_size_html( $instance, 'double', esc_html__( '2x', 'category-posts' ) );
1575 echo $this->get_button_thumb_size_html( $instance, 'bigger', esc_html__( '+', 'category-posts' ) );
1576 ?>
1577 </span>
1578 </p>
1579 <p>
1580 <label><?php esc_html_e( 'Ratio', 'category-posts' ); ?>: </label>
1581 <span class="cpwp-right">
1582 <?php
1583 echo $this->get_button_thumb_size_html( $instance, 'square', esc_html__( '1:1', 'category-posts' ) );
1584 echo $this->get_button_thumb_size_html( $instance, 'standard', esc_html__( '4:3', 'category-posts' ) );
1585 echo $this->get_button_thumb_size_html( $instance, 'wide', esc_html__( '16:9', 'category-posts' ) );
1586 echo $this->get_button_thumb_size_html( $instance, 'switch', esc_html__( 'switch', 'category-posts' ) );
1587 ?>
1588 </span>
1589 </p>
1590 <p>
1591 <label><?php esc_html_e( 'Available', 'category-posts' ); ?>: </label>
1592 <span class="cpwp-right">
1593 <?php
1594 echo $this->get_button_thumb_size_html( $instance, 'thumb', esc_html__( 'Thumb', 'category-posts' ) );
1595 echo $this->get_button_thumb_size_html( $instance, 'medium', esc_html__( 'Medium', 'category-posts' ) );
1596 echo $this->get_button_thumb_size_html( $instance, 'large', esc_html__( 'Large', 'category-posts' ) );
1597 ?>
1598 </span>
1599 </p>
1600 </div>
1601 <?php
1602 echo $this->get_checkbox_block_html( $instance, 'text_do_not_wrap_thumb', esc_html__( 'Do not wrap thumbnail with overflowing text', 'category-posts' ), true );
1603 echo $this->get_checkbox_block_html( $instance, 'use_css_cropping', esc_html__( 'CSS crop to requested size', 'category-posts' ), false );
1604 echo $this->get_select_block_html( $instance, 'thumb_hover', esc_html__( 'Animation on mouse hover:', 'category-posts' ), array(
1605 'none' => esc_html__( 'None', 'category-posts' ),
1606 'dark' => esc_html__( 'Darker', 'category-posts' ),
1607 'white' => esc_html__( 'Brighter', 'category-posts' ),
1608 'scale' => esc_html__( 'Zoom in', 'category-posts' ),
1609 'blur' => esc_html__( 'Blur', 'category-posts' ),
1610 'icon' => esc_html__( 'Icon', 'category-posts' ),
1611 ), 'none', true);
1612 echo $this->get_select_block_html( $instance, 'show_post_format', esc_html__( 'Indicate post format and position', 'category-posts' ), array(
1613 'none' => esc_html__( 'None', 'category-posts' ),
1614 'topleft' => esc_html__( 'Top left', 'category-posts' ),
1615 'bottomleft' => esc_html__( 'Bottom left', 'category-posts' ),
1616 'ceter' => esc_html__( 'Center', 'category-posts' ),
1617 'topright' => esc_html__( 'Top right', 'category-posts' ),
1618 'bottomright' => esc_html__( 'Bottom right', 'category-posts' ),
1619 'nocss' => esc_html__( 'HTML without styling', 'category-posts' ),
1620 ), 'none', true );
1621 ?>
1622 <p>
1623 <label style="display:block">
1624 <?php esc_html_e( 'Default thumbnail: ', 'category-posts' ); ?>
1625 </label>
1626 <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 ); ?>"/>
1627 <span class="default_thumb_img">
1628 <?php
1629 if ( ! $default_thunmbnail ) {
1630 esc_html_e( 'None', 'category-posts' );
1631 } else {
1632 $img = wp_get_attachment_image_src( $default_thunmbnail );
1633 echo '<img width="60" height="60" src="' . esc_url( $img[0] ) . '" />';
1634 }
1635 ?>
1636 </span>
1637 </p>
1638 <p>
1639 <button type="button" class="cwp_default_thumb_select button upload-button">
1640 <?php esc_html_e( 'Select image', 'category-posts' ); ?>
1641 </button>
1642 <button type="button" class="cwp_default_thumb_remove button upload-button" <?php echo ( ! $default_thunmbnail ) ? 'style="display:none"' : ''; ?> >
1643 <?php esc_html_e( 'No default', 'category-posts' ); ?>
1644 </button>
1645 </p>
1646 </div>
1647 </div>
1648 </div>
1649 <h4 data-panel="general"><?php esc_html_e( 'General', 'category-posts' ); ?></h4>
1650 <div>
1651 <?php echo $this->get_checkbox_block_html( $instance, 'disable_css', esc_html__( 'Disable the built-in CSS', 'category-posts' ), true ); ?>
1652 <?php echo $this->get_checkbox_block_html( $instance, 'disable_font_styles', esc_html__( 'Disable only font styles', 'category-posts' ), true ); ?>
1653 <div class="cpwp_ident">
1654 <?php
1655 echo $this->get_select_block_html( $instance, 'no_match_handling', esc_html__( 'When there are no matches', 'category-posts' ), array(
1656 'nothing' => esc_html__( 'Display empty widget', 'category-posts' ),
1657 'hide' => esc_html__( 'Hide Widget', 'category-posts' ),
1658 'text' => esc_html__( 'Show text', 'category-posts' ),
1659 ), 'nothing', true );
1660 ?>
1661 <div class="categoryPosts-no-match-text" style="display:<?php echo ( 'text' === $instance['no_match_handling'] ) ? 'block' : 'none'; ?>">
1662 <?php echo $this->get_textarea_html( $instance, 'no_match_text', esc_html__( 'Text', 'category-posts' ), '', true, 4 ); ?>
1663 </div>
1664 </div>
1665 <div>
1666 <?php
1667 echo $this->get_checkbox_block_html( $instance, 'enable_loadmore', esc_html__( 'Enable Load More', 'category-posts' ), true );
1668 ?>
1669 <div class="loadmore-settings" style="display:<?php echo ( $instance['enable_loadmore'] ) ? 'block' : 'none'; ?>">
1670 <?php echo $this->get_text_input_block_html( $instance, 'loadmore_text', esc_html__( 'Button text', 'category-posts' ), '', true ); ?>
1671 <?php echo $this->get_text_input_block_html( $instance, 'loading_text', esc_html__( 'Loading text', 'category-posts' ), '', true ); ?>
1672 </div>
1673 </div>
1674 </div>
1675 <h4 data-panel="footer"><?php esc_html_e( 'Footer', 'category-posts' ); ?></h4>
1676 <div>
1677 <?php echo $this->get_text_input_block_html( $instance, 'footer_link_text', esc_html__( 'Footer link text', 'category-posts' ), '', true ); ?>
1678 <?php echo $this->get_text_input_block_html( $instance, 'footer_link', esc_html__( 'Footer link URL', 'category-posts' ), '', true ); ?>
1679 </div>
1680 <p><a href="<?php echo esc_url( get_edit_user_link() ) . '#' . __NAMESPACE__; ?>"><?php esc_html_e( 'Widget admin behaviour settings', 'category-posts' ); ?></a></p>
1681 <p><a target="_blank" href="<?php echo esc_url( DOC_URL ); ?>"><?php esc_html_e( 'Documentation', 'category-posts' ); ?></a></p>
1682 <p><a target="_blank" href="<?php echo esc_url( SUPPORT_URL ); ?>"><?php esc_html_e( 'Support', 'category-posts' ); ?></a></p>
1683 <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>
1684 </div>
1685 <?php
1686 }
1687 }
1688