Category Posts Widget - Version 4.9.1

Version Description

  • Mai 17th 2019 =
  • 'Load more' button / Ajax API
  • Data range filter
  • Text, if there is no post
  • Excerpt length in lines (line-clamp)
  • Image crop with CSS (object-fit)
  • Fluid images (max-width in %).
  • One thumb dimension can be left empty
  • Admin UI: Button to easy add post details placeholder
  • Admin UI: Set / find thumbnail size buttons: +, , , 2x, -, ratio and Media sizes
Download this release

Release Info

Developer Kometschuh
Plugin Icon 128x128 Category Posts Widget
Version 4.9.1
Comparing to
See all releases

Code changes from version 4.8.5 to 4.9.1

cat-posts.php CHANGED
@@ -12,7 +12,7 @@ Plugin Name: Category Posts Widget
12
  Plugin URI: https://wordpress.org/plugins/category-posts/
13
  Description: Adds a widget that shows the most recent posts from a single category.
14
  Author: TipTopPress
15
- Version: 4.8.5
16
  Author URI: http://tiptoppress.com
17
  Text Domain: category-posts
18
  Domain Path: /languages
@@ -25,14 +25,20 @@ if ( ! defined( 'ABSPATH' ) ) {
25
  exit;
26
  }
27
 
28
- const VERSION = '4.8.5';
29
- const DOC_URL = 'http://tiptoppress.com/category-posts-widget/documentation-4-8?utm_source=widget_cpw&utm_campaign=documentation_4_8_cpw&utm_medium=form';
30
- const PRO_URL = 'http://tiptoppress.com/term-and-category-based-posts-widget/?utm_source=widget_cpw&utm_campaign=get_pro_cpw&utm_medium=action_link';
31
- const SUPPORT_URL = 'https://wordpress.org/support/plugin/category-posts';
32
  const SHORTCODE_NAME = 'catposts';
33
  const SHORTCODE_META = 'categoryPosts-shorcode';
34
  const WIDGET_BASE_ID = 'category-posts';
35
 
 
 
 
 
 
 
36
  /**
37
  * Adds the "Customize" link to the Toolbar on edit mode.
38
  *
@@ -79,7 +85,7 @@ add_action( 'admin_bar_menu', __NAMESPACE__ . '\wp_admin_bar_customize_menu', 35
79
  */
80
  function wp_head() {
81
 
82
- $widget_repository = new virtualWidgetsRepository();
83
 
84
  $styles = array();
85
 
@@ -93,7 +99,7 @@ function wp_head() {
93
 
94
  if ( ! empty( $styles ) ) {
95
  ?>
96
- <style type="text/css">
97
  <?php
98
  foreach ( $styles as $rules ) {
99
  foreach ( $rules as $rule ) {
@@ -119,20 +125,20 @@ function register_virtual_widgets() {
119
  global $post;
120
  global $wp_registered_widgets;
121
 
122
- $repository = new virtualWidgetsRepository();
123
 
124
  // check first for shortcode settings.
125
  if ( is_singular() ) {
126
  $names = shortcode_names( SHORTCODE_NAME, $post->post_content );
127
 
128
  foreach ( $names as $name ) {
129
- $meta = shortcode_settings( $name );
130
  if ( is_array( $meta ) ) {
131
  $id = WIDGET_BASE_ID . '-shortcode-' . get_the_ID(); // needed to make a unique id for the widget html element.
132
  if ( '' !== $name ) { // if not default name append to the id.
133
  $id .= '-' . sanitize_title( $name ); // sanitize to be on the safe side, not sure where when and how this will be used.
134
  }
135
- $repository->addShortcode( $name, new virtualWidget( $id, WIDGET_BASE_ID . '-shortcode', $meta ) );
136
  }
137
  }
138
  }
@@ -153,7 +159,7 @@ function register_virtual_widgets() {
153
  $widgetclass = new $class();
154
  $allsettings = $widgetclass->get_settings();
155
  $settings = isset( $allsettings[ str_replace( $widget_base . '-', '', $widget ) ] ) ? $allsettings[ str_replace( $widget_base . '-', '', $widget ) ] : false;
156
- $repository->addWidget( $widget, new virtualWidget( $widget, $widget, $settings ) );
157
  }
158
  }
159
  }
@@ -163,6 +169,34 @@ function register_virtual_widgets() {
163
 
164
  add_action( 'wp_head', __NAMESPACE__ . '\wp_head' );
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  /**
167
  * Enqueue widget related scripts for the widget admin page and customizer.
168
  *
@@ -170,10 +204,18 @@ add_action( 'wp_head', __NAMESPACE__ . '\wp_head' );
170
  */
171
  function admin_scripts( $hook ) {
172
 
173
- if ( 'widgets.php' === $hook || 'post.php' === $hook ) { // enqueue only for widget admin and customizer. (add if post.php: fix make widget SiteOrigin Page Builder plugin, GH issue #181)
174
 
175
- // control open and close the widget section.
176
- wp_register_script( 'category-posts-widget-admin-js', plugins_url( 'js/admin/category-posts-widget.js', __FILE__ ), array( 'jquery' ), VERSION, true );
 
 
 
 
 
 
 
 
177
  wp_enqueue_script( 'category-posts-widget-admin-js' );
178
 
179
  $js_data = array( 'accordion' => false );
@@ -209,125 +251,19 @@ function load_textdomain() {
209
  load_plugin_textdomain( 'category-posts', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' );
210
  }
211
 
212
- /*
213
- * Add styles for widget sections
214
- */
215
-
216
- add_action( 'admin_print_styles-widgets.php', __NAMESPACE__ . '\admin_styles' );
217
-
218
- // fix make widget SiteOrigin Page Builder plugin, GH issue #181
219
- add_action('siteorigin_panel_enqueue_admin_scripts', __NAMESPACE__ . '\admin_styles' );
220
-
221
  /**
222
- * Add required admin styles.
223
  *
224
  * @since 4.1
225
  **/
226
  function admin_styles() {
227
- ?>
228
- <style>
229
- .category-widget-cont h4 {
230
- padding: 12px 15px;
231
- cursor: pointer;
232
- margin: 5px 0;
233
- border: 1px solid #E5E5E5;
234
- }
235
- .category-widget-cont h4:first-child {
236
- margin-top: 10px;
237
- }
238
- .category-widget-cont h4:last-of-type {
239
- margin-bottom: 10px;
240
- }
241
- .category-widget-cont h4:after {
242
- float:right;
243
- font-family: "dashicons";
244
- content: '\f140';
245
- -ms-transform: translate(-1px,1px);
246
- -webkit-transform: translate(-1px,1px);
247
- -moz-transform: translate(-1px,1px);
248
- transform: translate(-1px,1px);
249
- -ms-transition: all 600ms;
250
- -webkit-transition: all 600ms;
251
- -moz-transition: all 600ms;
252
- transition: all 600ms;
253
- }
254
- .category-widget-cont h4.open:after {
255
- -ms-transition: all 600ms;
256
- -webkit-transition: all 600ms;
257
- -moz-transition: all 600ms;
258
- transition: all 600ms;
259
- -ms-transform: rotate(180deg);
260
- -webkit-transform: rotate(180deg);
261
- -moz-transform: rotate(180deg);
262
- transform: rotate(180deg);
263
- }
264
- .category-widget-cont > div {
265
- display:none;
266
- }
267
- .category-widget-cont > div.open {
268
- display:block;
269
- }
270
- .category-widget-cont th,
271
- .category-widget-cont tr {
272
- vertical-align: baseline;
273
- text-align:start;
274
- }
275
-
276
- .cat-post-template-help {display:none;}
277
-
278
- .categoryPosts-template textarea {width:100%}
279
-
280
- .category-widget-cont .open-template-help {
281
- border:0;
282
- padding:0;
283
- cursor: pointer;
284
  }
285
 
286
- </style>
287
- <?php
288
- }
289
-
290
- /**
291
- * Get image size
292
- *
293
- * @param int $thumb_w The width of the thumbnail in the widget settings.
294
- * @param int $thumb_h The height of the thumbnail in the widget settings.
295
- * @param int $image_w The width of the actual image being displayed.
296
- * @param int $image_h The height of the actual image being displayed.
297
- *
298
- * @return array An array with the width and height of the element containing the image
299
- */
300
- function get_image_size( $thumb_w, $thumb_h, $image_w, $image_h ) {
301
-
302
- $image_size = array(
303
- 'image_h' => $thumb_h,
304
- 'image_w' => $thumb_w,
305
- 'marginAttr' => '',
306
- 'marginVal' => '',
307
- );
308
- $relation_thumbnail = $thumb_w / $thumb_h;
309
- $relation_cropped = $image_w / $image_h;
310
-
311
- if ( $relation_thumbnail < $relation_cropped ) {
312
- // crop left and right site
313
- // thumbnail width/height ration is smaller, need to inflate the height of the image to thumb height
314
- // and adjust width to keep aspect ration of image.
315
- $image_size['image_h'] = $thumb_h;
316
- $image_size['image_w'] = $thumb_h / $image_h * $image_w;
317
- $image_size['marginAttr'] = 'margin-left';
318
- $image_size['marginVal'] = ( $image_size['image_w'] - $thumb_w ) / 2;
319
- } else {
320
- // crop top and bottom
321
- // thumbnail width/height ration is bigger, need to inflate the width of the image to thumb width
322
- // and adjust height to keep aspect ration of image.
323
- $image_size['image_w'] = $thumb_w;
324
- $image_size['image_h'] = $thumb_w / $image_w * $image_h;
325
- $image_size['marginAttr'] = 'margin-top';
326
- $image_size['marginVal'] = ( $image_size['image_h'] - $thumb_h ) / 2;
327
- }
328
 
329
- return $image_size;
330
- }
331
 
332
  /**
333
  * Get the tags which might be used in the template.
@@ -363,6 +299,45 @@ function get_template_regex() {
363
  return $regexp;
364
  }
365
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  /**
367
  * Convert pre 4.8 settings into template
368
  *
@@ -410,1537 +385,6 @@ function convert_settings_to_template( $instance ) {
410
  return $template;
411
  }
412
 
413
- /**
414
- * Category Posts Widget Class
415
- *
416
- * Shows the single category posts with some configurable options
417
- */
418
- class Widget extends \WP_Widget {
419
-
420
- /**
421
- * Widget constructor.
422
- */
423
- public function __construct() {
424
- $widget_ops = array(
425
- 'classname' => 'cat-post-widget',
426
- 'description' => __( 'List single category posts', 'category-posts' ),
427
- );
428
- parent::__construct( WIDGET_BASE_ID, __( 'Category Posts', 'category-posts' ), $widget_ops );
429
- }
430
-
431
- /**
432
- * Calculate the HTML for showing the thumb of a post item.
433
- *
434
- * Used as a filter for the thumb wordpress API to add css based stretching and cropping
435
- * when the image is not at the requested dimensions
436
- *
437
- * @param string $html The original HTML generated by the core APIS.
438
- * @param int $post_id the ID of the post of which the thumb is a featured image.
439
- * @param int $post_thumbnail_id The id of the featured image attachment.
440
- * @param string|array $size The requested size identified by name or (width, height) array.
441
- * @param mixed $attr ignored in this context.
442
- * @return string The HTML for the thumb related to the post
443
- *
444
- * @since 4.1
445
- */
446
- public function post_thumbnail_html( $html, $post_id, $post_thumbnail_id, $size, $attr ) {
447
-
448
- $use_css_cropping = isset( $this->instance['use_css_cropping'] ) && $this->instance['use_css_cropping'];
449
- $empty_dimensions = empty( $this->instance['thumb_w'] ) || empty( $this->instance['thumb_h'] );
450
- $thumb = isset( $this->instance['template'] ) && preg_match( '/%thumb%/', $this->instance['template'] );
451
-
452
- if ( ! ( $use_css_cropping && ! $empty_dimensions && $thumb ) ) {
453
- return $html; // If no full dimensions defined, just do not cropping for that image
454
- }
455
- $meta = image_get_intermediate_size( $post_thumbnail_id, $size );
456
-
457
- if ( empty( $meta ) ) {
458
- $post_img = wp_get_attachment_metadata( $post_thumbnail_id, $size );
459
- $meta['file'] = basename( $post_img['file'] );
460
- }
461
-
462
- $origfile = get_attached_file( $post_thumbnail_id, true ); // the location of the full file.
463
- $file = dirname( $origfile ) . '/' . $meta['file']; // the location of the file displayed as thumb.
464
- if ( file_exists( $file ) ) {
465
- list( $width, $height ) = getimagesize( $file ); // get actual size of the thumb file.
466
-
467
- if ( isset( $this->instance['use_css_cropping'] ) && $this->instance['use_css_cropping'] ) {
468
- $image = get_image_size( $this->instance['thumb_w'], $this->instance['thumb_h'], $width, $height );
469
-
470
- // replace srcset.
471
- $array = array();
472
- preg_match( '/width="([^"]*)"/i', $html, $array );
473
- $pattern = '/' . $array[1] . 'w/';
474
- $html = preg_replace( $pattern, $image['image_w'] . 'w', $html );
475
- // replace size.
476
- $pattern = '/' . $array[1] . 'px/';
477
- $html = preg_replace( $pattern, $image['image_w'] . 'px', $html );
478
- // replace width.
479
- $pattern = '/width="[0-9]*"/';
480
- $html = preg_replace( $pattern, "width='" . $image['image_w'] . "'", $html );
481
- // replace height.
482
- $pattern = '/height="[0-9]*"/';
483
- $html = preg_replace( $pattern, "height='" . $image['image_h'] . "'", $html );
484
- // set margin.
485
- $html = str_replace( '<img ', '<img style="' . $image['marginAttr'] . ':-' . $image['marginVal'] . 'px;height:' . $image['image_h']
486
- . 'px;clip:rect(auto,' . ( $this->instance['thumb_w'] + $image['marginVal'] ) . 'px,auto,' . $image['marginVal']
487
- . 'px);width:auto;max-width:initial;" ', $html );
488
- // wrap span with post format.
489
- $show_post_format = isset( $this->instance['show_post_format'] ) && ( 'none' !== $this->instance['show_post_format'] );
490
- if ( $show_post_format || $this->instance['thumb_hover'] ) {
491
- $format = get_post_format() ? : 'standard';
492
- $post_format_class = 'cat-post-format cat-post-format-' . $format;
493
- }
494
- $html = '<span class="cat-post-crop ' . $post_format_class . '" style="width:' . $this->instance['thumb_w'] . 'px;height:' . $this->instance['thumb_h'] . 'px;">'
495
- . $html . '</span>';
496
- } else {
497
- // use_css_cropping is not used.
498
- // wrap span.
499
- $html = '<span>' . $html . '</span>';
500
- }
501
- }
502
- return $html;
503
- }
504
-
505
- /*
506
- * wrapper to execute the the_post_thumbnail with filters.
507
- */
508
- /**
509
- * Calculate the HTML for showing the thumb of a post item.
510
- *
511
- * It is a wrapper to execute the the_post_thumbnail with filters
512
- *
513
- * @param string|array $size The requested size identified by name or (width, height) array.
514
- *
515
- * @return string The HTML for the thumb related to the post and empty string if it can not be calculated
516
- *
517
- * @since 4.1
518
- */
519
- public function the_post_thumbnail( $size = 'post-thumbnail' ) {
520
- if ( empty( $size ) ) { // if junk value, make it a normal thumb.
521
- $size = 'post-thumbnail';
522
- } elseif ( is_array( $size ) && ( 2 === count( $size ) ) ) { // good format at least.
523
- // normalize to ints first.
524
- $size[0] = (int) $size[0];
525
- $size[1] = (int) $size[1];
526
- if ( ( 0 === $size[0] ) && ( 0 === $size[1] ) ) { // Both values zero then revert to thumbnail.
527
- $size = array( get_option( 'thumbnail_size_w', 150 ), get_option( 'thumbnail_size_h', 150 ) );
528
- } elseif ( ( 0 === $size[0] ) && ( 0 !== $size[1] ) ) {
529
- // if one value is zero make a square using the other value.
530
- $size[0] = $size[1];
531
- } elseif ( ( 0 !== $size[0] ) && ( 0 === $size[1] ) ) {
532
- $size[1] = $size[0];
533
- }
534
- } else {
535
- $size = array( get_option( 'thumbnail_size_w', 150 ), get_option( 'thumbnail_size_h', 150 ) ); // yet another form of junk.
536
- }
537
-
538
- $post_thumbnail_id = get_post_thumbnail_id( get_the_ID() );
539
- if ( ! $post_thumbnail_id && $this->instance['default_thunmbnail'] ) {
540
- $post_thumbnail_id = $this->instance['default_thunmbnail'];
541
- }
542
-
543
- do_action( 'begin_fetch_post_thumbnail_html', get_the_ID(), $post_thumbnail_id, $size );
544
- $html = wp_get_attachment_image( $post_thumbnail_id, $size, false, '' );
545
- if ( ! $html ) {
546
- $ret = '';
547
- } else {
548
- $ret = $this->post_thumbnail_html( $html, get_the_ID(), $post_thumbnail_id, $size, '' );
549
- }
550
- do_action( 'end_fetch_post_thumbnail_html', get_the_ID(), $post_thumbnail_id, $size );
551
-
552
- return $ret;
553
- }
554
-
555
- /**
556
- * Excerpt more link filter
557
- *
558
- * @param string $more The "more" text passed by the filter.
559
- *
560
- * @return string The link to the post with the "more" text configured in the widget.
561
- */
562
- public function excerpt_more_filter( $more ) {
563
- return ' <a class="cat-post-excerpt-more more-link" href="' . get_permalink() . '">' . esc_html( $this->instance['excerpt_more_text'] ) . '</a>';
564
- }
565
-
566
- /**
567
- * Apply the_content filter for excerpt
568
- * This should show sharing buttons which comes with other widgets in the widget output in the same way as on the main content
569
- *
570
- * @param string $text The HTML with other applied excerpt filters.
571
- *
572
- * @return string If option hide_social_buttons is unchecked applay the_content filter.
573
- *
574
- * @since 4.6
575
- */
576
- public function apply_the_excerpt( $text ) {
577
- $ret = '';
578
- if ( isset( $this->instance['hide_social_buttons'] ) && $this->instance['hide_social_buttons'] ) {
579
- $ret = $text;
580
- } else {
581
- $ret = apply_filters( 'the_content', $text );
582
- }
583
- return $ret;
584
- }
585
-
586
- /**
587
- * Excerpt allow HTML
588
- *
589
- * @param string $text The HTML with other applied excerpt filters.
590
- */
591
- public function allow_html_excerpt( $text ) {
592
- global $post, $wp_filter;
593
- $new_excerpt_length = ( isset( $this->instance['excerpt_length'] ) && $this->instance['excerpt_length'] > 0 ) ? $this->instance['excerpt_length'] : 55;
594
- if ( '' === $text ) {
595
- $text = get_the_content( '' );
596
- $text = strip_shortcodes( $text );
597
- $text = apply_filters( 'the_content', $text );
598
- $text = str_replace( '\]\]\>', ']]&gt;', $text );
599
- $text = preg_replace( '@<script[^>]*?>.*?</script>@si', '', $text );
600
- $cphtml = array(
601
- '&lt;a&gt;',
602
- '&lt;br&gt;',
603
- '&lt;em&gt;',
604
- '&lt;i&gt;',
605
- '&lt;ul&gt;',
606
- '&lt;ol&gt;',
607
- '&lt;li&gt;',
608
- '&lt;p&gt;',
609
- '&lt;img&gt;',
610
- '&lt;script&gt;',
611
- '&lt;style&gt;',
612
- '&lt;video&gt;',
613
- '&lt;audio&gt;',
614
- );
615
- $allowed_HTML = '';
616
- foreach ( $cphtml as $index => $name ) {
617
- if ( in_array( (string) ( $index ), $this->instance['excerpt_allowed_elements'], true ) ) {
618
- $allowed_HTML .= $cphtml[ $index ];
619
- }
620
- }
621
- $text = strip_tags( $text, htmlspecialchars_decode( $allowed_HTML ) );
622
- $excerpt_length = $new_excerpt_length;
623
-
624
- if ( ! empty( $this->instance['excerpt_more_text'] ) ) {
625
- $excerpt_more = $this->excerpt_more_filter( $this->instance['excerpt_more_text'] );
626
- } elseif ( $filterName = key( $wp_filter['excerpt_more'][10] ) ) {
627
- $excerpt_more = $wp_filter['excerpt_more'][10][ $filterName ]['function']( 0 );
628
- } else {
629
- $excerpt_more = '[...]';
630
- }
631
-
632
- $words = explode( ' ', $text, $excerpt_length + 1 );
633
- if ( count( $words ) > $excerpt_length ) {
634
- array_pop( $words );
635
- array_push( $words, $excerpt_more );
636
- $text = implode( ' ', $words );
637
- }
638
- }
639
-
640
- return '<p>' . $text . '</p>';
641
- }
642
-
643
- /**
644
- * Calculate the wp-query arguments matching the filter settings of the widget
645
- *
646
- * @param array $instance Array which contains the various settings.
647
- * @return array The array that can be fed to wp_Query to get the relevant posts
648
- *
649
- * @since 4.6
650
- */
651
- public function queryArgs( $instance ) {
652
-
653
- $valid_sort_orders = array( 'date', 'title', 'comment_count', 'rand' );
654
- if ( isset( $instance['sort_by'] ) && in_array( $instance['sort_by'], $valid_sort_orders, true ) ) {
655
- $sort_by = $instance['sort_by'];
656
- } else {
657
- $sort_by = 'date';
658
- }
659
- $sort_order = ( isset( $instance['asc_sort_order'] ) && $instance['asc_sort_order'] ) ? 'ASC' : 'DESC';
660
-
661
- // Get array of post info.
662
- $args = array(
663
- 'orderby' => $sort_by,
664
- 'order' => $sort_order,
665
- 'ignore_sticky_posts' => 1, // Make sure we do not get stickies out of order.
666
- );
667
-
668
- $non_default_valid_status = array(
669
- 'publish',
670
- 'future',
671
- 'publish,future',
672
- 'private',
673
- 'private,publish',
674
- 'private,publish,future',
675
- );
676
- if ( isset( $instance['status'] ) && in_array( $instance['status'], $non_default_valid_status, true ) ) {
677
- $args['post_status'] = $instance['status'];
678
- }
679
-
680
- if ( isset( $instance['num'] ) ) {
681
- $args['showposts'] = (int) $instance['num'];
682
- }
683
-
684
- if ( isset( $instance['offset'] ) && ( (int) $instance['offset'] > 1 ) ) {
685
- $args['offset'] = (int) $instance['offset'] - 1;
686
- }
687
- if ( isset( $instance['cat'] ) ) {
688
- if ( isset( $instance['no_cat_childs'] ) && $instance['no_cat_childs'] ) {
689
- $args['category__in'] = (int) $instance['cat'];
690
- } else {
691
- $args['cat'] = (int) $instance['cat'];
692
- }
693
- }
694
-
695
- if ( is_singular() && isset( $instance['exclude_current_post'] ) && $instance['exclude_current_post'] ) {
696
- $args['post__not_in'] = array( get_the_ID() );
697
- }
698
-
699
- if ( isset( $instance['hideNoThumb'] ) && $instance['hideNoThumb'] ) {
700
- $args = array_merge( $args, array(
701
- 'meta_query' => array(
702
- array(
703
- 'key' => '_thumbnail_id',
704
- 'compare' => 'EXISTS',
705
- ),
706
- ),
707
- ) );
708
- }
709
-
710
- return $args;
711
- }
712
-
713
- /**
714
- * Calculate the HTML of the title based on the widget settings
715
- *
716
- * @param string $before_title The sidebar configured HTML that should come
717
- * before the title itself.
718
- * @param string $after_title The sidebar configured HTML that should come
719
- * after the title itself.
720
- * @param array $instance Array which contains the various settings.
721
- * @return string The HTML for the title area
722
- *
723
- * @since 4.6
724
- */
725
- public function titleHTML( $before_title, $after_title, $instance ) {
726
- $ret = '';
727
-
728
- if ( ! isset( $instance['cat'] ) ) {
729
- $instance['cat'] = 0;
730
- }
731
-
732
- // If no title, use the name of the category.
733
- if ( ! isset( $instance['title'] ) || ! $instance['title'] ) {
734
- $instance['title'] = '';
735
- if ( 0 !== $instance['cat'] ) {
736
- $category_info = get_category( $instance['cat'] );
737
- if ( $category_info && ! is_wp_error( $category_info ) ) {
738
- $instance['title'] = $category_info->name;
739
- } else {
740
- $instance['cat'] = 0; // For further processing treat it like "all categories".
741
- $instance['title'] = __( 'Recent Posts', 'category-posts' );
742
- }
743
- } else {
744
- $instance['title'] = __( 'Recent Posts', 'category-posts' );
745
- }
746
- }
747
-
748
- if ( ! ( isset( $instance['hide_title'] ) && $instance['hide_title'] ) ) {
749
- $ret = $before_title;
750
- if ( isset( $instance['is_shortcode'] ) ) {
751
- $title = esc_html( $instance['title'] );
752
- } else {
753
- $title = apply_filters( 'widget_title', $instance['title'] );
754
- }
755
-
756
- if ( isset( $instance['title_link'] ) && $instance['title_link'] ) {
757
- if ( 0 !== $instance['cat'] ) {
758
- $ret .= '<a href="' . get_category_link( $instance['cat'] ) . '">' . $title . '</a>';
759
- } elseif ( isset( $instance['title_link_url'] ) && $instance['title_link_url'] ) {
760
- $ret .= '<a href="' . esc_url( $instance['title_link_url'] ) . '">' . $title . '</a>';
761
- } else {
762
- $ret .= '<a href="' . esc_url( $this->blog_page_url() ) . '">' . $title . '</a>';
763
- }
764
- } else {
765
- $ret .= $title;
766
- }
767
-
768
- $ret .= $after_title;
769
- }
770
-
771
- return $ret;
772
- }
773
-
774
- /**
775
- * Get the URL of the blog page or home page if no explicit blog page is defined.
776
- *
777
- * @return string The URL of the blog page
778
- *
779
- * @since 4.8
780
- */
781
- private function blog_page_url() {
782
-
783
- $blog_page = get_option( 'page_for_posts' );
784
- if ( $blog_page ) {
785
- $url = get_permalink( $blog_page );
786
- } else {
787
- $url = home_url();
788
- }
789
-
790
- return $url;
791
- }
792
-
793
- /**
794
- * Calculate the HTML of the footer based on the widget settings
795
- *
796
- * @param array $instance Array which contains the various settings.
797
- * @return string The HTML for the footer area
798
- *
799
- * @since 4.6
800
- */
801
- public function footerHTML( $instance ) {
802
-
803
- $ret = '';
804
- $url = '';
805
- $text = '';
806
-
807
- if ( isset( $instance['footer_link'] ) ) {
808
- $url = $instance['footer_link'];
809
- }
810
-
811
- if ( isset( $instance['footer_link_text'] ) ) {
812
- $text = $instance['footer_link_text'];
813
- }
814
-
815
- // if url is set, but no text, just use the url as text.
816
- if ( empty( $text ) && ! empty( $url ) ) {
817
- $text = $url;
818
- }
819
-
820
- // if no url is set but just text, assume the url should be to the relevant archive page
821
- // category archive for categories filter and home page or blog page when "all categories"
822
- // is used.
823
- if ( ! empty( $text ) && empty( $url ) ) {
824
- if ( isset( $instance['cat'] ) && ( 0 !== $instance['cat'] ) && ( null !== get_category( $instance['cat'] ) ) ) {
825
- $url = get_category_link( $instance['cat'] );
826
- } else {
827
- $url = $this->blog_page_url();
828
- }
829
- }
830
-
831
- if ( ! empty( $url ) ) {
832
- $ret .= '<a class="cat-post-footer-link" href="' . esc_url( $url ) . '">' . esc_html( $text ) . '</a>';
833
- }
834
-
835
- return $ret;
836
- }
837
-
838
- /**
839
- * Current post item date string based on the format requested in the settings
840
- *
841
- * @param array $instance Array which contains the various settings.
842
- * @param bool $everything_is_link Indicates whether the return string should avoid links.
843
- *
844
- * @since 4.8
845
- */
846
- public function itemDate( $instance, $everything_is_link ) {
847
- $ret = '';
848
-
849
- if ( ! isset( $instance['preset_date_format'] ) ) {
850
- $preset_date_format = 'other';
851
- } else {
852
- $preset_date_format = $instance['preset_date_format'];
853
- }
854
-
855
- switch ( $preset_date_format ) {
856
- case 'sitedateandtime':
857
- $date = get_the_time( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) );
858
- break;
859
- case 'sitedate':
860
- $date = get_the_time( get_option( 'date_format' ) );
861
- break;
862
- case 'sincepublished':
863
- $date = human_time_diff( get_the_time( 'U' ), current_time( 'timestamp' ) );
864
- break;
865
- default:
866
- if ( isset( $instance['date_format'] ) && strlen( trim( $instance['date_format'] ) ) > 0 ) {
867
- $date_format = $instance['date_format'];
868
- } else {
869
- $date_format = 'j M Y';
870
- }
871
- $date = get_the_time( $date_format );
872
- break;
873
- }
874
- $ret .= '<span class="cat-post-date">';
875
- if ( isset( $instance['date_link'] ) && $instance['date_link'] && ! $everything_is_link ) {
876
- $ret .= '<a href="' . \get_the_permalink() . '">';
877
- }
878
- $ret .= $date;
879
-
880
- if ( isset( $instance['date_link'] ) && $instance['date_link'] && ! $everything_is_link ) {
881
- $ret .= '</a>';
882
- }
883
- $ret .= '</span>';
884
- return $ret;
885
- }
886
-
887
-
888
- /**
889
- * Calculate the HTML for showing the thumb of a post item.
890
- * Expected to be called from a loop with globals properly set.
891
- *
892
- * @param array $instance Array which contains the various settings.
893
- * @param bool $no_link indicates whether the thumb should be wrapped in a link or a span.
894
- * @return string The HTML for the thumb related to the post
895
- *
896
- * @since 4.6
897
- */
898
- public function itemThumb( $instance, $no_link ) {
899
- $ret = '';
900
-
901
- if ( ( isset( $instance['default_thunmbnail'] ) && ( 0 !== $instance['default_thunmbnail'] ) ) || has_post_thumbnail() ) {
902
- $class = '';
903
- $use_css_cropping = isset( $this->instance['use_css_cropping'] ) && $this->instance['use_css_cropping'];
904
- $disable_css = isset( $instance['disable_css'] ) && $instance['disable_css'];
905
-
906
- if ( isset( $this->instance['thumb_hover'] ) && ! $disable_css ) {
907
- $class = 'class="cat-post-thumbnail cat-post-' . $instance['thumb_hover'] . '"';
908
- } else {
909
- $class = 'class="cat-post-thumbnail"';
910
- }
911
-
912
- $title_args = array( 'echo' => false );
913
-
914
- if ( $no_link ) {
915
- $ret .= '<span ' . $class . '>';
916
- } else {
917
- $ret .= '<a ' . $class . ' href="' . get_the_permalink() . '" title="' . the_title_attribute( $title_args ) . '">';
918
- }
919
-
920
- $ret .= $this->the_post_thumbnail( array( $this->instance['thumb_w'], $this->instance['thumb_h'] ) );
921
-
922
- if ( $no_link ) {
923
- $ret .= '</span>';
924
- } else {
925
- $ret .= '</a>';
926
- }
927
- }
928
-
929
- return $ret;
930
- }
931
-
932
- /**
933
- * Current post item categories string
934
- *
935
- * @param array $instance Array which contains the various settings.
936
- * @param bool $everything_is_link Indicates whether the return string should avoid links.
937
- *
938
- * @since 4.8
939
- */
940
- public function itemCategories( $instance, $everything_is_link ) {
941
-
942
- $ret = '<span class="cat-post-tax-category">';
943
- $cat_ids = wp_get_post_categories( get_the_ID(), array( 'number' => 0 ) );
944
- foreach ( $cat_ids as $cat_id ) {
945
- if ( $everything_is_link ) {
946
- $ret .= ' ' . get_cat_name( $cat_id );
947
- } else {
948
- $ret .= " <a href='" . get_category_link( $cat_id ) . "'>" . get_cat_name( $cat_id ) . '</a>';
949
- }
950
- }
951
- $ret .= '</span>';
952
- return $ret;
953
- }
954
-
955
- /**
956
- * Current post item tags string
957
- *
958
- * @param array $instance Array which contains the various settings.
959
- * @param bool $everything_is_link Indicates whether the return string should avoid links.
960
- *
961
- * @since 4.8
962
- */
963
- public function itemTags( $instance, $everything_is_link ) {
964
-
965
- $ret = '<span class="cat-post-tax-post_tag">';
966
- $tag_ids = wp_get_post_tags( get_the_ID(), array( 'number' => 0 ) );
967
- foreach ( $tag_ids as $tag_id ) {
968
- if ( $everything_is_link ) {
969
- $ret .= ' ' . $tag_id->name;
970
- } else {
971
- $ret .= " <a href='" . get_tag_link( $tag_id->term_id ) . "'>" . $tag_id->name . '</a>';
972
- }
973
- }
974
- $ret .= '</span>';
975
- return $ret;
976
- }
977
-
978
- /**
979
- * Current post item comment number string
980
- *
981
- * @param array $instance Array which contains the various settings.
982
- * @param bool $everything_is_link Indicates whether the return string should avoid links.
983
- *
984
- * @since 4.8
985
- */
986
- public function itemCommentNum( $instance, $everything_is_link ) {
987
- global $post;
988
-
989
- $ret = '<span class="cat-post-comment-num">';
990
-
991
- if ( $everything_is_link ) {
992
- $ret .= '(' . \get_comments_number() . ')';
993
- } else {
994
- $link = sprintf(
995
- '<a href="%1$s" title="%2$s">(%3$d)</a>',
996
- esc_url( get_comments_link( $post->ID ) ),
997
- esc_attr( sprintf( __( '(%d) comments to this post' ), get_comments_number() ) ),
998
- get_comments_number()
999
- );
1000
- $ret .= $link;
1001
- }
1002
-
1003
- $ret .= '</span>';
1004
- return $ret;
1005
- }
1006
-
1007
- /**
1008
- * Current post item author string
1009
- *
1010
- * @param array $instance Array which contains the various settings.
1011
- * @param bool $everything_is_link Indicates whether the return string should avoid links.
1012
- *
1013
- * @since 4.8
1014
- */
1015
- public function itemAuthor( $instance, $everything_is_link ) {
1016
-
1017
- $ret = '<span class="cat-post-author">';
1018
-
1019
- if ( $everything_is_link ) {
1020
- $ret .= get_the_author();
1021
- } else {
1022
- $link = get_the_author_posts_link();
1023
- $ret .= $link;
1024
- }
1025
- $ret .= '</span>';
1026
- return $ret;
1027
- }
1028
-
1029
- /**
1030
- * Current post item excerpt string
1031
- *
1032
- * @param array $instance Array which contains the various settings.
1033
- * @param bool $everything_is_link Indicates whether the return string should avoid links.
1034
- *
1035
- * @since 4.8
1036
- */
1037
- public function itemExcerpt( $instance, $everything_is_link ) {
1038
- global $post;
1039
-
1040
- // use the_excerpt filter to get the "normal" excerpt of the post
1041
- // then apply our filter to let users customize excerpts in their own way.
1042
- if ( isset( $instance['excerpt_length'] ) && ( $instance['excerpt_length'] > 0 ) ) {
1043
- $length = (int) $instance['excerpt_length'];
1044
- } else {
1045
- $length = 55; // Use the wordpress default.
1046
- }
1047
-
1048
- if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
1049
- $excerpt = apply_filters( 'the_excerpt', \get_the_excerpt() );
1050
- } else { // if filters off replicate functionality of core generating excerpt.
1051
- $more_text = '[&hellip;]';
1052
- if ( isset( $instance['excerpt_more_text'] ) && $instance['excerpt_more_text'] ) {
1053
- $more_text = ltrim( $instance['excerpt_more_text'] );
1054
- }
1055
-
1056
- if ( $everything_is_link ) {
1057
- $excerpt_more_text = ' <span class="cat-post-excerpt-more">' . $more_text . '</span>';
1058
- } else {
1059
- $excerpt_more_text = ' <a class="cat-post-excerpt-more" href="' . get_permalink() . '" title="' . sprintf( __( 'Continue reading %s' ), get_the_title() ) . '">' . $more_text . '</a>';
1060
- }
1061
- if ( '' === $post->post_excerpt ) {
1062
- $text = get_the_content( '' );
1063
- $text = strip_shortcodes( $text );
1064
- $excerpt = \wp_trim_words( $text, $length, $excerpt_more_text );
1065
- // adjust html output same way as for the normal excerpt,
1066
- // just force all functions depending on the_excerpt hook.
1067
- $excerpt = shortcode_unautop( wpautop( convert_chars( convert_smilies( wptexturize( $excerpt ) ) ) ) );
1068
- } else {
1069
- $excerpt = $post->post_excerpt . $excerpt_more_text;
1070
- $excerpt = shortcode_unautop( wpautop( convert_chars( convert_smilies( wptexturize( $excerpt ) ) ) ) );
1071
- }
1072
- }
1073
- $ret = apply_filters( 'cpw_excerpt', $excerpt, $this );
1074
- return $ret;
1075
- }
1076
-
1077
- /**
1078
- * Current post item title string
1079
- *
1080
- * @param array $instance Array which contains the various settings.
1081
- * @param bool $everything_is_link Indicates whether the return string should avoid links.
1082
- *
1083
- * @since 4.8
1084
- */
1085
- public function itemTitle( $instance, $everything_is_link ) {
1086
-
1087
- $ret = '';
1088
-
1089
- if ( $everything_is_link ) {
1090
- $ret .= '<span class="cat-post-title">' . get_the_title() . '</span>';
1091
- } else {
1092
- $ret .= '<a class="cat-post-title"';
1093
- $ret .= ' href="' . get_the_permalink() . '" rel="bookmark">' . get_the_title();
1094
- $ret .= '</a>';
1095
- }
1096
- return $ret;
1097
- }
1098
-
1099
- /**
1100
- * Calculate the HTML for a post item based on the widget settings and post.
1101
- * Expected to be called in an active loop with all the globals set.
1102
- *
1103
- * @param array $instance Array which contains the various settings.
1104
- * @param null|integer $current_post_id If on singular page specifies the id of
1105
- * the post, otherwise null.
1106
- * @return string The HTML for item related to the post
1107
- *
1108
- * @since 4.6
1109
- */
1110
- public function itemHTML( $instance, $current_post_id ) {
1111
- global $post;
1112
-
1113
- $everything_is_link = isset( $instance['everything_is_link'] ) && $instance['everything_is_link'];
1114
- $wrap = isset( $instance['text_do_not_wrap_thumb'] ) && $instance['text_do_not_wrap_thumb'];
1115
-
1116
- $template = '';
1117
- if ( isset( $instance['template'] ) ) {
1118
- $template = $instance['template'];
1119
- } else {
1120
- $template = convert_settings_to_template( $instance );
1121
- }
1122
- $ret = '<li ';
1123
-
1124
- // Current post.
1125
- if ( $current_post_id === $post->ID ) {
1126
- $ret .= "class='cat-post-item cat-post-current'";
1127
- } else {
1128
- $ret .= "class='cat-post-item'";
1129
- }
1130
- $ret .= '>'; // close the li opening tag.
1131
-
1132
- if ( $everything_is_link ) {
1133
- $ret .= '<a class="cat-post-everything-is-link" href="' . get_the_permalink() . '" title="">';
1134
- }
1135
-
1136
- // Try to do smart formatting for floating thumb based on its location.
1137
- if ( $wrap ) {
1138
- if( preg_match( '#(\%thumb\%)#', $template ) && ! preg_match( '#(\%thumb\%$)#', $template ) ) {
1139
- $thumb_flex = explode( '%thumb%', $template );
1140
- if( count( $thumb_flex ) == 1) {
1141
- $template = '<div class="cat-post-do-not-wrap-thumbnail">%thumb%<div>' . $thumb_flex[0] . '</div></div>';
1142
- }
1143
- if( count( $thumb_flex ) == 2) {
1144
- $template = $thumb_flex[0] . '<div class="cat-post-do-not-wrap-thumbnail">%thumb%<div>' . $thumb_flex[1] . '</div></div>';
1145
- }
1146
- }
1147
- }
1148
-
1149
- // Post details (Template).
1150
- $widget = $this;
1151
- $template_res = preg_replace_callback( get_template_regex(), function ( $matches ) use ( $widget, $instance, $everything_is_link ) {
1152
- switch ( $matches[0] ) {
1153
- case '%title%':
1154
- return $widget->itemTitle( $instance, $everything_is_link );
1155
- case '%author%':
1156
- return $widget->itemAuthor( $instance, $everything_is_link );
1157
- case '%commentnum%':
1158
- return $widget->itemCommentNum( $instance, $everything_is_link );
1159
- case '%date%':
1160
- return $widget->itemDate( $instance, $everything_is_link );
1161
- case '%thumb%':
1162
- return $widget->itemThumb( $instance, $everything_is_link );
1163
- case '%post_tag%':
1164
- return $widget->itemTags( $instance, $everything_is_link );
1165
- case '%category%':
1166
- return $widget->itemCategories( $instance, $everything_is_link );
1167
- case '%excerpt%':
1168
- return $widget->itemExcerpt( $instance, $everything_is_link );
1169
- default:
1170
- return $matches[0];
1171
- }
1172
- }, $template );
1173
-
1174
- // Replace empty line with closing and opening DIV.
1175
- $template_res = trim( $template_res );
1176
- $template_res = str_replace( "\n\r", '</div><div>', $template_res ); // in widget areas
1177
- $template_res = str_replace( "\n\n", '</div><div>', $template_res ); // as shortcode
1178
- $template_res = '<div>' . $template_res . '</div>';
1179
-
1180
- // replace new lines with spaces.
1181
- $template_res = str_replace( "\n\r", ' ', $template_res ); // in widget areas
1182
- $template_res = str_replace( "\n\n", ' ', $template_res ); // as shortcode
1183
-
1184
- $ret .= $template_res;
1185
-
1186
- if ( $everything_is_link ) {
1187
- $ret .= '</a>';
1188
- }
1189
-
1190
- $ret .= '</li>';
1191
- return $ret;
1192
- }
1193
-
1194
- /**
1195
- * Filter to set the number of words in an excerpt
1196
- *
1197
- * @param int $length The number of words as configured by wordpress core or set by previous filters.
1198
- * @return int The number of words configured for the widget,
1199
- * or the $length parameter if it is not configured or garbage value.
1200
- *
1201
- * @since 4.6
1202
- */
1203
- public function excerpt_length_filter( $length ) {
1204
- if ( isset( $this->instance['excerpt_length'] ) && $this->instance['excerpt_length'] > 0 ) {
1205
- $length = $this->instance['excerpt_length'];
1206
- }
1207
- return $length;
1208
- }
1209
-
1210
- /**
1211
- * Set the proper excerpt filters based on the settings
1212
- *
1213
- * @param array $instance widget settings.
1214
- * @return void
1215
- *
1216
- * @since 4.6
1217
- */
1218
- public function setExcerpFilters( $instance ) {
1219
-
1220
- if ( isset( $instance['excerpt'] ) && $instance['excerpt'] ) {
1221
-
1222
- // Excerpt length filter.
1223
- if ( isset( $instance['excerpt_length'] ) && ( (int) $instance['excerpt_length'] ) > 0 ) {
1224
- add_filter( 'excerpt_length', array( $this, 'excerpt_length_filter' ) );
1225
- }
1226
-
1227
- if ( isset( $instance['excerpt_more_text'] ) && ( '' !== ltrim( $instance['excerpt_more_text'] ) ) ) {
1228
- add_filter( 'excerpt_more', array( $this, 'excerpt_more_filter' ) );
1229
- }
1230
-
1231
- if ( isset( $instance['excerpt_allow_html'] ) ) {
1232
- remove_filter( 'get_the_excerpt', 'wp_trim_excerpt' );
1233
- add_filter( 'the_excerpt', array( $this, 'allow_html_excerpt' ) );
1234
- } else {
1235
- add_filter( 'the_excerpt', array( $this, 'apply_the_excerpt' ) );
1236
- }
1237
- }
1238
- }
1239
-
1240
- /**
1241
- * Remove the excerpt filter
1242
- *
1243
- * @param array $instance widget settings.
1244
- * @return void
1245
- *
1246
- * @since 4.6
1247
- */
1248
- public function removeExcerpFilters( $instance ) {
1249
- remove_filter( 'excerpt_length', array( $this, 'excerpt_length_filter' ) );
1250
- remove_filter( 'excerpt_more', array( $this, 'excerpt_more_filter' ) );
1251
- add_filter( 'get_the_excerpt', 'wp_trim_excerpt' );
1252
- remove_filter( 'the_excerpt', array( $this, 'allow_html_excerpt' ) );
1253
- remove_filter( 'the_excerpt', array( $this, 'apply_the_excerpt' ) );
1254
- }
1255
-
1256
- /**
1257
- * The main widget display controller
1258
- *
1259
- * Called by the sidebar processing core logic to display the widget.
1260
- *
1261
- * @param array $args An array containing the "environment" setting for the widget,
1262
- * namely, the enclosing tags for the widget and its title.
1263
- * @param array $instance The settings associate with the widget.
1264
- *
1265
- * @since 4.1
1266
- */
1267
- public function widget( $args, $instance ) {
1268
-
1269
- if ( 0 === count( $instance ) ) {
1270
- $instance = default_settings();
1271
- }
1272
-
1273
- extract( $args );
1274
- $this->instance = $instance;
1275
-
1276
- $args = $this->queryArgs( $instance );
1277
- $cat_posts = new \WP_Query( $args );
1278
-
1279
- if ( ! isset( $instance['hide_if_empty'] ) || ! $instance['hide_if_empty'] || $cat_posts->have_posts() ) {
1280
- echo $before_widget; // Xss ok. This is how widget actually expected to behave.
1281
- echo $this->titleHTML( $before_title, $after_title, $instance );
1282
-
1283
- $current_post_id = null;
1284
- if ( is_singular() ) {
1285
- $current_post_id = get_the_ID();
1286
- }
1287
-
1288
- if ( ! ( isset( $instance['is_shortcode'] ) && $instance['is_shortcode'] ) ) { // the internal id is needed only for widgets.
1289
- echo '<ul id="' . esc_attr( WIDGET_BASE_ID ) . '-' . esc_attr( $this->number ) . '-internal" class="' . esc_attr( WIDGET_BASE_ID ) . '-internal' . "\">\n";
1290
- } else {
1291
- echo '<ul>';
1292
- }
1293
-
1294
- // set widget filters.
1295
- if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
1296
- $this->setExcerpFilters( $instance );
1297
- }
1298
-
1299
- while ( $cat_posts->have_posts() ) {
1300
- $cat_posts->the_post();
1301
- echo $this->itemHTML( $instance, $current_post_id );
1302
- }
1303
- echo "</ul>\n";
1304
-
1305
- echo $this->footerHTML( $instance );
1306
- echo $after_widget; // Xss ok. This is how widget actually expected to behave.
1307
-
1308
- // remove widget filters.
1309
- if ( ! isset( $instance['excerpt_filters'] ) || $instance['excerpt_filters'] ) { // pre 4.7 widgets has filters on.
1310
- $this->removeExcerpFilters( $instance );
1311
- }
1312
-
1313
- wp_reset_postdata();
1314
-
1315
- $use_css_cropping = isset( $this->instance['use_css_cropping'] ) && $this->instance['use_css_cropping'];
1316
- $empty_dimensions = empty( $this->instance['thumb_w'] ) || empty( $this->instance['thumb_h'] );
1317
- $thumb = isset( $this->instance['template'] ) && preg_match( '/%thumb%/', $this->instance['template'] );
1318
-
1319
- if ( $use_css_cropping && ! $empty_dimensions && $thumb ) {
1320
- // enqueue relevant scripts and parameters to perform cropping
1321
- // once we support only 4.5+ it can be refactored to use wp_add_inline_script.
1322
- $number = $this->number;
1323
- // a temporary hack to handle difference in the number in a true widget
1324
- // and the number format expected at the rest of the places.
1325
- if ( is_numeric( $number ) ) {
1326
- $number = WIDGET_BASE_ID . '-' . $number;
1327
- }
1328
-
1329
- // add Javascript to change change cropped image dimensions on load and window resize.
1330
- $thumb_w = $this->instance['thumb_w'];
1331
- $thumb_h = $this->instance['thumb_h'];
1332
- add_filter( 'cpw_crop_widgets',
1333
- function ( $a ) use ( $number, $thumb_w, $thumb_h ) {
1334
- $a[ $number ] = $thumb_w / $thumb_h;
1335
- return $a;
1336
- }
1337
- );
1338
- wp_enqueue_script( 'jquery' ); // just in case the theme or other plugins didn't enqueue it.
1339
- add_action('wp_footer',function () use ($number,$instance) { __NAMESPACE__ . '\\' . change_cropped_image_dimensions($number,$instance);},100);
1340
- }
1341
- }
1342
- }
1343
-
1344
- /**
1345
- * Update the options.
1346
- *
1347
- * @param array $new_instance The new settings of the widget.
1348
- * @param array $old_instance The current settings of the widget.
1349
- * @return array
1350
- */
1351
- public function update( $new_instance, $old_instance ) {
1352
-
1353
- $new_instance['title'] = sanitize_text_field( $new_instance['title'] ); // sanitize the title like core widgets do.
1354
- if ( ! isset( $new_instance['excerpt_filters'] ) ) {
1355
- $new_instance['excerpt_filters'] = '';
1356
- }
1357
- if ( current_user_can( 'unfiltered_html' ) ) {
1358
- $instance['text'] = $new_instance['template'];
1359
- } else {
1360
- $instance['text'] = wp_kses_post( $new_instance['template'] );
1361
- }
1362
- return $new_instance;
1363
- }
1364
-
1365
- /**
1366
- * Output the title panel of the widget configuration form.
1367
- *
1368
- * @param array $instance The widget's settings.
1369
- * @return void
1370
- *
1371
- * @since 4.6
1372
- */
1373
- public function formTitlePanel( $instance ) {
1374
- if ( isset( $instance['cat'] ) ) {
1375
- $cat = $instance['cat'];
1376
- } else {
1377
- $cat = 0;
1378
- }
1379
-
1380
- $hide_title = false;
1381
- if ( isset( $instance['hide_title'] ) && $instance['hide_title'] ) {
1382
- $hide_title = true;
1383
- }
1384
- ?>
1385
- <h4 data-panel="title"><?php esc_html_e( 'Title', 'category-posts' ); ?></h4>
1386
- <div>
1387
- <?php echo $this->get_checkbox_block_html( $instance, 'hide_title', esc_html__( 'Hide title', 'category-posts' ), false, true ); ?>
1388
- <div class="cpwp_ident categoryposts-data-panel-title-settings" <?php if ( $hide_title ) { echo 'style="display:none"'; } ?>>
1389
- <?php echo $this->get_text_input_block_html( $instance, 'title', esc_html__( 'Title', 'category-posts' ), '', __( 'Recent Posts', 'category-posts' ), true ); ?>
1390
- <?php echo $this->get_checkbox_block_html( $instance, 'title_link', esc_html__( 'Make widget title link', 'category-posts' ), false, 0 !== $cat ); ?>
1391
- <?php echo $this->get_text_input_block_html( $instance, 'title_link_url', esc_html__( 'Title link URL', 'category-posts' ), '', '', 0 === $cat ); ?>
1392
- </div>
1393
- </div>
1394
- <?php
1395
- }
1396
-
1397
- /**
1398
- * Output the filter panel of the widget configuration form.
1399
- *
1400
- * @param array $instance The parameters configured for the widget.
1401
- * @return void
1402
- *
1403
- * @since 4.6
1404
- */
1405
- public function formFilterPanel( $instance ) {
1406
- $instance = wp_parse_args( (array) $instance, array( 'cat' => 0 ) );
1407
- $cat = $instance['cat'];
1408
- ?>
1409
- <h4 data-panel="filter"><?php esc_html_e( 'Filter', 'category-posts' ); ?></h4>
1410
- <div>
1411
- <p>
1412
- <label>
1413
- <?php esc_html_e( 'Category', 'category-posts' ); ?>:
1414
- <?php
1415
- wp_dropdown_categories( array(
1416
- 'show_option_all' => __( 'All categories', 'category-posts' ),
1417
- 'hide_empty' => 0,
1418
- 'name' => $this->get_field_name( 'cat' ),
1419
- 'selected' => $instance['cat'],
1420
- 'class' => 'categoryposts-data-panel-filter-cat',
1421
- ) );
1422
- ?>
1423
- </label>
1424
- </p>
1425
- <?php
1426
- echo $this->get_checkbox_block_html( $instance, 'no_cat_childs', esc_html__( 'Exclude child categories', 'category-posts' ), false, ! empty( $instance['cat'] ) );
1427
- echo $this->get_select_block_html( $instance, 'status', esc_html__( 'Status', 'category-posts' ), array(
1428
- 'default' => esc_html__( 'WordPress Default', 'category-posts' ),
1429
- 'publish' => esc_html__( 'Published', 'category-posts' ),
1430
- 'future' => esc_html__( 'Scheduled', 'category-posts' ),
1431
- 'private' => esc_html__( 'Private', 'category-posts' ),
1432
- 'publish,future' => esc_html__( 'Published or Scheduled', 'category-posts' ),
1433
- 'private,publish' => esc_html__( 'Published or Private', 'category-posts' ),
1434
- 'private,future' => esc_html__( 'Private or Scheduled', 'category-posts' ),
1435
- 'private,publish,future' => esc_html__( 'Published, Private or Scheduled', 'category-posts' ),
1436
- ), 'default', true );
1437
- echo $this->get_number_input_block_html( $instance, 'num', esc_html__( 'Number of posts to show', 'category-posts' ), get_option( 'posts_per_page' ), 1, '', '', true );
1438
- echo $this->get_number_input_block_html( $instance, 'offset', esc_html__( 'Start with post', 'category-posts' ), 1, 1, '', '', true );
1439
- echo $this->get_select_block_html( $instance, 'sort_by', esc_html__( 'Sort by', 'category-posts' ), array(
1440
- 'date' => esc_html__( 'Date', 'category-posts' ),
1441
- 'title' => esc_html__( 'Title', 'category-posts' ),
1442
- 'comment_count' => esc_html__( 'Number of comments', 'category-posts' ),
1443
- 'rand' => esc_html__( 'Random', 'category-posts' ),
1444
- ), 'date', true );
1445
- echo $this->get_checkbox_block_html( $instance, 'asc_sort_order', esc_html__( 'Reverse sort order (ascending)', 'category-posts' ), false, true );
1446
- echo $this->get_checkbox_block_html( $instance, 'exclude_current_post', esc_html__( 'Exclude current post', 'category-posts' ), false, true );
1447
- echo $this->get_checkbox_block_html( $instance, 'hideNoThumb', esc_html__( 'Exclude posts which have no thumbnail', 'category-posts' ), false, true );
1448
- ?>
1449
- </div>
1450
- <?php
1451
- }
1452
-
1453
- /**
1454
- * Generate the wrapper P around a form input element
1455
- *
1456
- * @since 4.8
1457
- * @param string $html The HTML to wrap.
1458
- * @param string $key The key to use as the prefix to the class.
1459
- * @param bool $visible Indicates if the element should be visible when rendered.
1460
- *
1461
- * @return string HTML with P element contaning the html being passed with class based on the key
1462
- * and style set to display:none if visibility is off.
1463
- */
1464
- private function get_wrap_block_html( $html, $key, $visible ) {
1465
-
1466
- $cl = ' class="' . __NAMESPACE__ . '-' . esc_attr( $key ) . '"';
1467
-
1468
- $style = '';
1469
- if ( ! $visible ) {
1470
- $style = ' style="display:none"';
1471
- }
1472
- $ret = '<p' . $cl . $style . ">\n" . $html . "</p>\n";
1473
-
1474
- return $ret;
1475
- }
1476
-
1477
- /**
1478
- * Generate a form P element containing a select element
1479
- *
1480
- * @since 4.8
1481
- * @param array $instance The instance.
1482
- * @param string $key The key in the instance array.
1483
- * @param string $label The label to display and associate with the input.
1484
- * @param array $list An array of pairs value (index) => label to be used for the options.
1485
- * The labels are expected to be html escaped.
1486
- * @param int $default The value to use if the key is not set in the instance.
1487
- * @param bool $visible Indicates if the element should be visible when rendered.
1488
- *
1489
- * @return string HTML a P element contaning the select, its label, class based on the key
1490
- * and style set to display:none if visibility is off.
1491
- */
1492
- private function get_select_block_html( $instance, $key, $label, $list, $default, $visible ) {
1493
- $value = $default;
1494
-
1495
- if ( isset( $instance[ $key ] ) ) {
1496
- $value = $instance[ $key ];
1497
- }
1498
-
1499
- if ( ! array_key_exists( $value, $list ) ) {
1500
- $value = $default;
1501
- }
1502
-
1503
- $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1504
- $label .
1505
- "</label>\n" .
1506
- '<select id="' . $this->get_field_id( $key ) . '" name="' . $this->get_field_name( $key ) . '" autocomplete="off">' . "\n";
1507
- foreach ( $list as $v => $l ) {
1508
- $ret .= '<option value="' . esc_attr( $v ) . '" ' . selected( $v, $value, false ) . '>' . $l . "</option>\n";
1509
- }
1510
- $ret .= "</select>\n";
1511
-
1512
- return $this->get_wrap_block_html( $ret, $key, $visible );
1513
- }
1514
-
1515
- /**
1516
- * Generate a form P element containing a textarea input
1517
- *
1518
- * @since 4.8
1519
- * @param array $instance The instance.
1520
- * @param string $key The key in the instance array.
1521
- * @param string $label The label to display and associate with the input (should be html escaped).
1522
- * @param int $default The value to use if the key is not set in the instance.
1523
- * @param string $placeholder The placeholder to use in the input (should be attribute escaped).
1524
- * @param bool $visible Indicates if the element should be visible when rendered.
1525
- * @param int $num_rows Number of rows.
1526
- *
1527
- * @return string HTML a P element containing the input, its label, class based on the key
1528
- * and style set to display:none if visibility is off.
1529
- */
1530
- private function get_textarea_html( $instance, $key, $label, $default, $placeholder, $visible, $num_rows ) {
1531
-
1532
- $value = $default;
1533
-
1534
- if ( isset( $instance[ $key ] ) ) {
1535
- $value = $instance[ $key ];
1536
- }
1537
-
1538
- $ret = '<label for="' . esc_attr( $this->get_field_id( $key ) ) . '">' . $label . '</label>' .
1539
- '<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>';
1540
-
1541
- return $this->get_wrap_block_html( $ret, $key, $visible );
1542
- }
1543
-
1544
- /**
1545
- * Generate a form P element containing a text input
1546
- *
1547
- * @since 4.8
1548
- * @param array $instance The instance.
1549
- * @param string $key The key in the instance array.
1550
- * @param string $label The label to display and associate with the input.
1551
- * Should be html escaped.
1552
- * @param int $default The value to use if the key is not set in the instance.
1553
- * @param string $placeholder The placeholder to use in the input. should be attribute escaped.
1554
- * @param bool $visible Indicates if the element should be visible when rendered.
1555
- *
1556
- * @return string HTML a P element contaning the input, its label, class based on the key
1557
- * and style set to display:none if visibility is off.
1558
- */
1559
- private function get_text_input_block_html( $instance, $key, $label, $default, $placeholder, $visible ) {
1560
-
1561
- $value = $default;
1562
-
1563
- if ( isset( $instance[ $key ] ) ) {
1564
- $value = $instance[ $key ];
1565
- }
1566
-
1567
- $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1568
- $label .
1569
- '<input placeholder="' . $placeholder . '" id="' . $this->get_field_id( $key ) . '" name="' . $this->get_field_name( $key ) . '" type="text" value="' . esc_attr( $value ) . '" autocomplete="off"/>' . "\n" .
1570
- "</label>\n";
1571
-
1572
- return $this->get_wrap_block_html( $ret, $key, $visible );
1573
- }
1574
-
1575
- /**
1576
- * Generate a form P element containing a number input
1577
- *
1578
- * @since 4.8
1579
- * @param array $instance The instance.
1580
- * @param string $key The key in the instance array.
1581
- * @param string $label The label to display and associate with the input.
1582
- * expected to be escaped.
1583
- * @param int $default The value to use if the key is not set in the instance.
1584
- * @param int $min The minimum value allowed to be input.
1585
- * @param int $max The maximum value allowed to be input.
1586
- * @param string $placeholder The placeholder string to be used. expected to be escaped.
1587
- * @param bool $visible Indicates if the element should be visible when rendered.
1588
- *
1589
- * @return string HTML a P element contaning the input, its label, class based on the key
1590
- * and style set to display:none if visibility is off.
1591
- */
1592
- private function get_number_input_block_html( $instance, $key, $label, $default, $min, $max, $placeholder, $visible ) {
1593
-
1594
- $value = $default;
1595
-
1596
- if ( isset( $instance[ $key ] ) ) {
1597
- $value = $instance[ $key ];
1598
- }
1599
-
1600
- $minmax = '';
1601
- if ( '' !== $min ) {
1602
- $minmax .= ' min="' . $min . '"';
1603
- }
1604
- if ( '' !== $max ) {
1605
- $minmax .= ' max="' . $max . '"';
1606
- }
1607
-
1608
- $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1609
- esc_html( $label ) . "\n" .
1610
- '<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" .
1611
- "</label>\n";
1612
-
1613
- return $this->get_wrap_block_html( $ret, $key, $visible );
1614
- }
1615
-
1616
- /**
1617
- * Generate a form P element containing a checkbox input
1618
- *
1619
- * @since 4.8
1620
- * @param array $instance The instance.
1621
- * @param string $key The key in the instance array.
1622
- * @param string $label The label to display and associate with the checkbox.
1623
- * should be escaped string.
1624
- * @param bool $default The value to use if the key is not set in the instance.
1625
- * @param bool $visible Indicates if the element should be visible when rendered.
1626
- *
1627
- * @return string HTML a P element contaning the checkbox, its label, class based on the key
1628
- * and style set to display:none if visibility is off.
1629
- */
1630
- private function get_checkbox_block_html( $instance, $key, $label, $default, $visible ) {
1631
-
1632
- $value = $default;
1633
-
1634
- if ( array_key_exists( $key, $instance ) ) {
1635
- if ( $instance[ $key ] ) {
1636
- $value = true;
1637
- } else {
1638
- $value = false;
1639
- }
1640
- }
1641
- $ret = '<label for="' . esc_attr( $this->get_field_id( $key ) ) . "\">\n" .
1642
- '<input id="' . esc_attr( $this->get_field_id( $key ) ) . '" name="' . esc_attr( $this->get_field_name( $key ) ) . '" type="checkbox" ' . checked( $value, true, false ) . '/>' . "\n" .
1643
- $label .
1644
- "</label>\n";
1645
-
1646
- return $this->get_wrap_block_html( $ret, $key, $visible );
1647
- }
1648
-
1649
- /**
1650
- * The widget configuration form back end.
1651
- *
1652
- * @param array $instance The parameters associated with the widget.
1653
- * @return void
1654
- */
1655
- public function form( $instance ) {
1656
- if ( 0 === count( $instance ) ) { // new widget, use defaults.
1657
- $instance = default_settings();
1658
- } else { // updated widgets come from =< 4.6 excerpt filter is on.
1659
- if ( ! isset( $instance['excerpt_filters'] ) ) {
1660
- $instance['excerpt_filters'] = 'on';
1661
- }
1662
- }
1663
-
1664
- $instance = wp_parse_args( (array) $instance, array(
1665
- 'hide_post_titles' => '',
1666
- 'excerpt' => '',
1667
- 'excerpt_more_text' => '',
1668
- 'excerpt_filters' => '',
1669
- 'date' => '',
1670
- 'date_format' => '',
1671
- 'disable_css' => '',
1672
- 'disable_font_styles' => '',
1673
- 'hide_if_empty' => '',
1674
- 'hide_social_buttons' => '',
1675
- 'preset_date_format' => 'other',
1676
- 'thumb' => false,
1677
- 'thumb_w' => get_option( 'thumbnail_size_w', 150 ),
1678
- 'thumb_h' => get_option( 'thumbnail_size_h', 150 ),
1679
- 'default_thunmbnail' => 0,
1680
- 'use_css_cropping' => true,
1681
- 'text_do_not_wrap_thumb' => false,
1682
- ) );
1683
-
1684
- $hide_post_titles = $instance['hide_post_titles'];
1685
- $excerpt = $instance['excerpt'];
1686
- $excerpt_more_text = $instance['excerpt_more_text'];
1687
- $excerpt_filters = $instance['excerpt_filters'];
1688
- $date = $instance['date'];
1689
- $date_format = $instance['date_format'];
1690
- $disable_css = $instance['disable_css'];
1691
- $disable_font_styles = $instance['disable_font_styles'];
1692
- $hide_if_empty = $instance['hide_if_empty'];
1693
- $preset_date_format = $instance['preset_date_format'];
1694
- $thumb = ! empty( $instance['thumb'] );
1695
- $thumb_w = $instance['thumb_w'];
1696
- $thumb_h = $instance['thumb_h'];
1697
- $default_thunmbnail = $instance['default_thunmbnail'];
1698
- $use_css_cropping = $instance['use_css_cropping'];
1699
- $text_do_not_wrap_thumb = $instance['text_do_not_wrap_thumb'];
1700
-
1701
- $cat = $instance['cat'];
1702
-
1703
- if ( ! isset( $style_done ) ) { // what an ugly hack, but can't figure out how to do it nicer on 4.3.
1704
- ?>
1705
- <style type="text/css">
1706
- .cpwp_ident {
1707
- color: #6A6A6A;
1708
- background: #F1F1F1;
1709
- padding: 5px;
1710
- }
1711
- .cpwp_ident > .cpwp_ident {
1712
- border-left:5px solid #B3B3B3;
1713
- padding: 0 10px;
1714
- }
1715
- .cpwp_ident > p {
1716
- margin: 5px 0;
1717
- }
1718
- .cpwp_ident > label {
1719
- line-height: 2.75;
1720
- display: inline-block;
1721
- }
1722
- .cpwp_ident_top {
1723
- margin-top:-1em;
1724
- padding-top:1em;
1725
- }
1726
-
1727
- .category-widget-cont input[type="number"] {
1728
- width:5em;
1729
- text-align:center;
1730
- }
1731
-
1732
- .categoryposts-template-help th {
1733
- text-align:start;
1734
- font-weight:bold;
1735
- }
1736
-
1737
- .categoryposts-template-help td {
1738
- padding:2px;
1739
- }
1740
-
1741
- .categoryPosts-template textarea {
1742
- font-size:16px;
1743
- line-height:20px;
1744
- }
1745
- </style>
1746
-
1747
- <?php
1748
- $style_done = true;
1749
- }
1750
- ?>
1751
-
1752
- <div class="category-widget-cont">
1753
- <p><a target="_blank" href="http://tiptoppress.com/term-and-category-based-posts-widget/?utm_source=widget_cpw&utm_campaign=get_pro_cpw&utm_medium=form"><?php esc_html_e( 'Get the Pro version', 'category-posts' ); ?></a></p>
1754
- <?php
1755
- $this->formTitlePanel( $instance );
1756
- $this->formFilterPanel( $instance );
1757
- ?>
1758
- <h4 data-panel="details"><?php esc_html_e( 'Post details', 'category-posts' ); ?></h4>
1759
- <div>
1760
- <?php
1761
- $template = '';
1762
- if ( ! isset( $instance['template'] ) ) {
1763
- $template = convert_settings_to_template( $instance );
1764
- } else {
1765
- $template = $instance['template'];
1766
- }
1767
- ?>
1768
- <p><?php esc_html_e( 'Displayed parts', 'category-posts' ); ?></p>
1769
- <div class="cpwp_ident">
1770
- <?php
1771
- echo $this->get_textarea_html( $instance, 'template', esc_html__( 'Template', 'category-posts' ) . ' <a href="#" class="dashicons toggle-template-help dashicons-editor-help imgedit-help-toggle"><span class="screen-reader-text">' . esc_html__( 'Show template help', 'category-posts' ) . '</span></a>', $template, '', true, 8 );
1772
- preg_match_all( get_template_regex(), $template, $matches );
1773
- $tags = array();
1774
- if ( ! empty( $matches[0] ) ) {
1775
- $tags = array_flip( $matches[0] );
1776
- }
1777
- ?>
1778
- <div class="cat-post-template-help">
1779
- <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' ); ?>
1780
- </p>
1781
- <table>
1782
- <tr>
1783
- <th><?php esc_html_e( 'New line', 'category-posts' ); ?></th>
1784
- <td><?php esc_html_e( 'Space', 'category-posts' ); ?></td>
1785
- </tr>
1786
- <tr>
1787
- <th><?php esc_html_e( 'Empty line', 'category-posts' ); ?></th>
1788
- <td><?php esc_html_e( 'Next line is a paragraph', 'category-posts' ); ?></td>
1789
- </tr>
1790
- <tr>
1791
- <th>%title%</th>
1792
- <td><?php esc_html_e( 'Post title', 'category-posts' ); ?></td>
1793
- </tr>
1794
- <tr>
1795
- <th>%thumb%</th>
1796
- <td><?php esc_html_e( 'Post thumbnail possibly wrapped by text', 'category-posts' ); ?></td>
1797
- </tr>
1798
- <tr>
1799
- <th>%date%</th>
1800
- <td><?php esc_html_e( 'Post publish date', 'category-posts' ); ?></td>
1801
- </tr>
1802
- <tr>
1803
- <th>%excerpt%</th>
1804
- <td><?php esc_html_e( 'Post excerpt', 'category-posts' ); ?></td>
1805
- </tr>
1806
- <tr>
1807
- <th>%author%</th>
1808
- <td><?php esc_html_e( 'Post author', 'category-posts' ); ?></td>
1809
- </tr>
1810
- <tr>
1811
- <th>%commentnum%</th>
1812
- <td><?php esc_html_e( 'The number of comments to the post', 'category-posts' ); ?></td>
1813
- </tr>
1814
- <tr>
1815
- <th>%post_tag%</th>
1816
- <td><?php esc_html_e( 'Post tags', 'category-posts' ); ?></td>
1817
- </tr>
1818
- <tr>
1819
- <th>%category%</th>
1820
- <td><?php esc_html_e( 'Post categories', 'category-posts' ); ?></td>
1821
- </tr>
1822
- </table>
1823
- </div>
1824
- <div class="cat-post-premade_templates">
1825
- <p><label><?php esc_html_e( 'Select premade Template', 'category-posts' ); ?></label></p>
1826
- <select>
1827
- <option value="title"><?php esc_html_e( 'Only the title', 'category-posts' ); ?></option>
1828
- <option value="title_excerpt"><?php esc_html_e( 'Title and Excerpt', 'category-posts' ); ?></option>
1829
- <option value="title_thumb"><?php esc_html_e( 'Title and Thumbnail', 'category-posts' ); ?></option>
1830
- <option value="title_thum_excerpt"><?php esc_html_e( 'Title, Thumbnail and Excerpt', 'category-posts' ); ?></option>
1831
- <option value="everything"><?php esc_html_e( 'All with icons', 'category-posts' ); ?></option>
1832
- </select>
1833
- <p><button type="button" class="button"><?php esc_html_e( 'Select this template', 'category-posts' ); ?></button></p>
1834
- </div>
1835
- </div>
1836
-
1837
- <?php // Excerpt settings. ?>
1838
- <div class="categoryposts-data-panel-excerpt" style="display:<?php echo ( isset( $tags['%excerpt%'] ) ) ? 'block' : 'none'; ?>">
1839
- <p><?php esc_html_e( 'Excerpt settings', 'category-posts' ); ?></p>
1840
- <div class="cpwp_ident">
1841
- <?php
1842
- echo $this->get_number_input_block_html( $instance, 'excerpt_length', esc_html__( 'Excerpt length (words):', 'category-posts' ), get_option( 'posts_per_page' ), 1, 200, '', true );
1843
- echo $this->get_text_input_block_html( $instance, 'excerpt_more_text', esc_html__( 'Excerpt \'more\' text:', 'category-posts' ), '', esc_attr__( '...', 'category-posts' ), true );
1844
- echo $this->get_checkbox_block_html( $instance, 'excerpt_filters', esc_html__( 'Don\'t override Themes and plugin filters', 'category-posts' ), false, true );
1845
- ?>
1846
- </div>
1847
- </div>
1848
- <div class="categoryposts-data-panel-date" style="display:<?php echo ( isset( $tags['%date%'] ) ) ? 'block' : 'none'; ?>">
1849
- <p><?php esc_html_e( 'Date format settings', 'category-posts' ); ?></p>
1850
- <div class="cpwp_ident">
1851
- <?php
1852
- echo $this->get_select_block_html( $instance, 'preset_date_format', esc_html__( 'Date format', 'category-posts' ), array(
1853
- 'sitedateandtime' => esc_html__( 'Site date and time', 'category-posts' ),
1854
- 'sitedate' => esc_html__( 'Site date', 'category-posts' ),
1855
- 'sincepublished' => esc_html__( 'Time since published', 'category-posts' ),
1856
- 'other' => esc_html__( 'PHP style format', 'category-posts' ),
1857
- ), 'sitedateandtime', true );
1858
- 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 );
1859
- ?>
1860
- </div>
1861
- </div>
1862
-
1863
- <?php // Thumbnail settings. ?>
1864
- <div class="categoryposts-data-panel-thumb" style="display:<?php echo ( isset( $tags['%thumb%'] ) ) ? 'block' : 'none'; ?>">
1865
- <p><?php esc_html_e( 'Thumbnail settings', 'category-posts' ); ?></p>
1866
- <div class="cpwp_ident">
1867
- <p><?php esc_html_e( 'Thumbnail dimensions (pixel)', 'category-posts' ); ?></p>
1868
- <?php
1869
- echo $this->get_number_input_block_html( $instance, 'thumb_w', esc_html__( 'Width:', 'category-posts' ), get_option( 'thumbnail_size_w', 150 ), 1, '', '', true );
1870
- echo $this->get_number_input_block_html( $instance, 'thumb_h', esc_html__( 'Height:', 'category-posts' ), get_option( 'thumbnail_size_h', 150 ), 1, '', '', true );
1871
-
1872
- echo $this->get_checkbox_block_html( $instance, 'text_do_not_wrap_thumb', esc_html__( 'Do not wrap thumbnail with overflowing text', 'category-posts' ), false, true );
1873
- echo $this->get_checkbox_block_html( $instance, 'use_css_cropping', esc_html__( 'CSS crop to requested size', 'category-posts' ), false, false );
1874
- echo $this->get_select_block_html( $instance, 'thumb_hover', esc_html__( 'Animation on mouse hover:', 'category-posts' ), array(
1875
- 'none' => esc_html__( 'None', 'category-posts' ),
1876
- 'dark' => esc_html__( 'Darker', 'category-posts' ),
1877
- 'white' => esc_html__( 'Brighter', 'category-posts' ),
1878
- 'scale' => esc_html__( 'Zoom in', 'category-posts' ),
1879
- 'blur' => esc_html__( 'Blur', 'category-posts' ),
1880
- 'icon' => esc_html__( 'Icon', 'category-posts' ),
1881
- ), 'none', true);
1882
- echo $this->get_select_block_html( $instance, 'show_post_format', esc_html__( 'Indicate post format and position', 'category-posts' ), array(
1883
- 'none' => esc_html__( 'None', 'category-posts' ),
1884
- 'topleft' => esc_html__( 'Top left', 'category-posts' ),
1885
- 'bottomleft' => esc_html__( 'Bottom left', 'category-posts' ),
1886
- 'ceter' => esc_html__( 'Center', 'category-posts' ),
1887
- 'topright' => esc_html__( 'Top right', 'category-posts' ),
1888
- 'bottomright' => esc_html__( 'Bottom right', 'category-posts' ),
1889
- 'nocss' => esc_html__( 'HTML without styling', 'category-posts' ),
1890
- ), 'none', true );
1891
- ?>
1892
- <p>
1893
- <label style="display:block">
1894
- <?php esc_html_e( 'Default thumbnail: ', 'category-posts' ); ?>
1895
- </label>
1896
- <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 ); ?>"/>
1897
- <span class="default_thumb_img">
1898
- <?php
1899
- if ( ! $default_thunmbnail ) {
1900
- esc_html_e( 'None', 'category-posts' );
1901
- } else {
1902
- $img = wp_get_attachment_image_src( $default_thunmbnail );
1903
- echo '<img width="60" height="60" src="' . esc_url( $img[0] ) . '" />';
1904
- }
1905
- ?>
1906
- </span>
1907
- </p>
1908
- <p>
1909
- <button type="button" class="cwp_default_thumb_select button upload-button">
1910
- <?php esc_html_e( 'Select image', 'category-posts' ); ?>
1911
- </button>
1912
- <button type="button" class="cwp_default_thumb_remove button upload-button" <?php if ( ! $default_thunmbnail ) { echo 'style="display:none"'; } ?> >
1913
- <?php esc_html_e( 'No default', 'category-posts' ); ?>
1914
- </button>
1915
- </p>
1916
- </div>
1917
- </div>
1918
- <?php
1919
- echo $this->get_checkbox_block_html( $instance, 'everything_is_link', esc_html__( 'Everything is a link', 'category-posts' ), false, true );
1920
- ?>
1921
- </div>
1922
- <h4 data-panel="general"><?php esc_html_e( 'General', 'category-posts' ); ?></h4>
1923
- <div>
1924
- <div class="cpwp_ident">
1925
- <?php echo $this->get_checkbox_block_html( $instance, 'disable_css', esc_html__( 'Disable the built-in CSS', 'category-posts' ), false, true ); ?>
1926
- <?php echo $this->get_checkbox_block_html( $instance, 'disable_font_styles', esc_html__( 'Disable only font styles', 'category-posts' ), false, true ); ?>
1927
- </div>
1928
- <?php echo $this->get_checkbox_block_html( $instance, 'hide_if_empty', esc_html__( 'Hide if there are no matching posts', 'category-posts' ), false, true ); ?>
1929
- </div>
1930
- <h4 data-panel="footer"><?php esc_html_e( 'Footer', 'category-posts' ); ?></h4>
1931
- <div>
1932
- <?php echo $this->get_text_input_block_html( $instance, 'footer_link_text', esc_html__( 'Footer link text', 'category-posts' ), '', '', true ); ?>
1933
- <?php echo $this->get_text_input_block_html( $instance, 'footer_link', esc_html__( 'Footer link URL', 'category-posts' ), '', '', true ); ?>
1934
- </div>
1935
- <p><a href="<?php echo esc_url( get_edit_user_link() ) . '#' . __NAMESPACE__; ?>"><?php esc_html_e( 'Widget admin behaviour settings', 'category-posts' ); ?></a></p>
1936
- <p><a target="_blank" href="<?php echo esc_url( DOC_URL ); ?>"><?php esc_html_e( 'Documentation', 'category-posts' ); ?></a></p>
1937
- <p><a target="_blank" href="<?php echo esc_url( SUPPORT_URL ); ?>"><?php esc_html_e( 'Support', 'category-posts' ); ?></a></p>
1938
- <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>
1939
- </div>
1940
- <?php
1941
- }
1942
- }
1943
-
1944
  /*
1945
  * Plugin action links section
1946
  */
@@ -1961,11 +405,14 @@ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), __NAMESPACE__
1961
  * @param array $links The current links about to be displayed.
1962
  */
1963
  function add_action_links( $links ) {
1964
- $pro_link = array(
1965
- '<a target="_blank" href="' . esc_url( PRO_URL ) . '">' . esc_html__( 'Get the Pro version', 'category-posts' ) . '</a>',
1966
- );
1967
 
1968
- $links = array_merge( $pro_link, $links );
 
 
 
 
 
 
1969
 
1970
  return $links;
1971
  }
@@ -1979,125 +426,26 @@ function register_widget() {
1979
 
1980
  add_action( 'widgets_init', __NAMESPACE__ . '\register_widget' );
1981
 
1982
- /**
1983
- * Output js code to handle responsive thumbnails
1984
- *
1985
- * @return void
1986
- *
1987
- * @since 4.7
1988
- */
1989
- function change_cropped_image_dimensions($number,$widgetsettings) { ?>
1990
- <script type="text/javascript">
1991
-
1992
- if (typeof jQuery !== 'undefined') {
1993
-
1994
- jQuery( document ).ready(function () {
1995
-
1996
- <?php // namespace. ?>
1997
- var cwp_namespace = window.cwp_namespace || {};
1998
- cwp_namespace.fluid_images = cwp_namespace.fluid_images || {};
1999
-
2000
- cwp_namespace.fluid_images = {
2001
-
2002
- <?php // variables. ?>
2003
- Widgets : {},
2004
- widget : null,
2005
-
2006
- <?php // class. ?>
2007
- Span : function (_self, _imageRatio) {
2008
-
2009
- <?php // variables. ?>
2010
- this.self = _self;
2011
- this.imageRatio = _imageRatio;
2012
- },
2013
-
2014
- <?php // class. ?>
2015
- WidgetPosts : function (widget, ratio) {
2016
-
2017
- <?php // variables. ?>
2018
- this.Spans = {};
2019
- this.allSpans = widget.find( '.cat-post-crop' );
2020
- this.firstSpan = this.allSpans.first();
2021
- this.maxSpanWidth = this.firstSpan.width();
2022
- this.firstListItem = this.firstSpan.closest( 'li' );
2023
- this.ratio = ratio;
2024
-
2025
- for( var i = 0; i < this.allSpans.length; i++ ){
2026
- var imageRatio = this.firstSpan.width() / jQuery(this.allSpans[i]).find( 'img' ).height();
2027
- this.Spans[i] = new cwp_namespace.fluid_images.Span( jQuery(this.allSpans[i]), imageRatio );
2028
- }
2029
-
2030
- <?php // functions. ?>
2031
- this.changeImageSize = function changeImageSize() {
2032
-
2033
- this.listItemWidth = this.firstListItem.width();
2034
- this.SpanWidth = this.firstSpan.width();
2035
-
2036
- if(this.listItemWidth < this.SpanWidth || <?php /* if the layout-width have not enough space to show the regular source-width */ echo "\r\n"; ?>
2037
- this.listItemWidth < this.maxSpanWidth) { <?php /* defined start and stop working width for the image: Accomplish only the image width will be get smaller as the source-width */ echo "\r\n"; ?>
2038
- this.allSpans.width( this.listItemWidth );
2039
- var spanHeight = this.listItemWidth / this.ratio;
2040
- this.allSpans.height( spanHeight );
2041
-
2042
- for( var index in this.Spans ){
2043
- var imageHeight = this.listItemWidth / this.Spans[index].imageRatio;
2044
- jQuery(this.Spans[index].self).find( 'img' ).css({
2045
- height: imageHeight,
2046
- marginTop: -(imageHeight - spanHeight) / 2
2047
- });
2048
- };
2049
- }
2050
- }
2051
- },
2052
- }
2053
-
2054
- <?php
2055
- /**
2056
- * The cpw_crop_widgets is an internal filter that is used
2057
- * to gather the ids of the widgets to which apply cropping
2058
- *
2059
- * For easier prevention of duplication, the widget id number should be an index
2060
- * in the array while the ratio of width/height be the value
2061
- */
2062
- $widgets_ids = apply_filters( 'cpw_crop_widgets', array() );
2063
- foreach ( $widgets_ids as $num => $ratio ) {
2064
- if($num != $number) {
2065
- continue;
2066
- } ?>
2067
- cwp_namespace.fluid_images.widget = jQuery('#<?php echo esc_attr( $num ); ?>');
2068
- cwp_namespace.fluid_images.Widgets['<?php echo esc_attr( $num ); ?>'] = new cwp_namespace.fluid_images.WidgetPosts(cwp_namespace.fluid_images.widget,<?php echo esc_attr( $ratio ); ?>);
2069
- <?php } ?>
2070
-
2071
- <?php /* do on page load or on resize the browser window */ echo "\r\n"; ?>
2072
- jQuery(window).on('load resize', function() {
2073
- for (var widget in cwp_namespace.fluid_images.Widgets) {
2074
- cwp_namespace.fluid_images.Widgets[widget].changeImageSize();
2075
- }
2076
- });
2077
- });
2078
- }
2079
- </script>
2080
- <?php
2081
- }
2082
-
2083
  /*
2084
  * shortcode section.
2085
  */
2086
 
2087
  /**
2088
- * Get shortcode settings taking into account if it is being customized
2089
  *
2090
- * When not customized returns the settings as stored in the meta, but when
2091
- * it is customized returns the setting stored in the virtual option used by the customizer
2092
  *
2093
- * @param string $name The name of the shortcode to retun, empty string indicates the nameless.
 
2094
  *
2095
- * @return array the shortcode settings if a short code exists or empty string, empty array if name not found
 
2096
  *
2097
- * @since 4.6
2098
  */
2099
- function shortcode_settings( $name ) {
2100
- $meta = get_post_meta( get_the_ID(), SHORTCODE_META, true );
2101
 
2102
  if ( ! empty( $meta ) && ! is_array( reset( $meta ) ) ) {
2103
  $meta = array( '' => $meta ); // the conversion.
@@ -2111,16 +459,19 @@ function shortcode_settings( $name ) {
2111
  if ( is_customize_preview() ) {
2112
  $o = get_option( '_virtual-' . WIDGET_BASE_ID );
2113
  if ( is_array( $o ) ) {
2114
- $instance = $o[ get_the_ID() ][ $name ];
 
2115
  }
2116
  }
2117
-
2118
  if ( isset( $instance['template'] ) && $instance['template'] ) {
2119
  ;
2120
  } else {
2121
  $instance['template'] = convert_settings_to_template( $instance );
2122
  }
2123
 
 
 
2124
  return $instance;
2125
  }
2126
 
@@ -2133,7 +484,7 @@ function shortcode_settings( $name ) {
2133
  * @return string An HTML of the "widget" based on its settings, actual or customized
2134
  */
2135
  function shortcode( $attr, $content = null ) {
2136
- $repository = new virtualWidgetsRepository();
2137
 
2138
  $shortcodes = $repository->getShortcodes();
2139
 
@@ -2192,41 +543,52 @@ function shortcode_names( $shortcode_name, $content ) {
2192
  */
2193
  function default_settings() {
2194
  return array(
2195
- 'title' => __( 'Recent Posts', 'category-posts' ),
2196
- 'title_link' => false,
2197
- 'title_link_url' => '',
2198
- 'hide_title' => false,
2199
- 'cat' => 0,
2200
- 'num' => get_option( 'posts_per_page' ),
2201
- 'offset' => 1,
2202
- 'sort_by' => 'date',
2203
- 'status' => 'publish',
2204
- 'asc_sort_order' => false,
2205
- 'exclude_current_post' => false,
2206
- 'hideNoThumb' => false,
2207
- 'footer_link_text' => '',
2208
- 'footer_link' => '',
2209
- 'thumb_w' => get_option( 'thumbnail_size_w', 150 ),
2210
- 'thumb_h' => get_option( 'thumbnail_size_h', 150 ),
2211
- 'use_css_cropping' => true,
2212
- 'thumb_hover' => 'none',
2213
- 'hide_post_titles' => false,
2214
- 'excerpt_length' => 55,
2215
- 'excerpt_more_text' => __( '...', 'category-posts' ),
2216
- 'excerpt_filters' => false,
2217
- 'comment_num' => false,
2218
- 'date_link' => false,
2219
- 'date_format' => '',
2220
- 'disable_css' => false,
2221
- 'disable_font_styles' => false,
2222
- 'hide_if_empty' => false,
2223
- 'show_post_format' => 'none',
2224
- 'hide_social_buttons' => '',
2225
- 'no_cat_childs' => false,
2226
- 'everything_is_link' => false,
2227
- 'preset_date_format' => 'sitedateandtime',
2228
- 'template' => "%title%\n\n%thumb%",
2229
- 'text_do_not_wrap_thumb' => false,
 
 
 
 
 
 
 
 
 
 
 
2230
  );
2231
  }
2232
 
@@ -2288,26 +650,7 @@ add_action( 'save_post', __NAMESPACE__ . '\save_post', 10, 2 );
2288
  */
2289
  function customize_register( $wp_customize ) {
2290
 
2291
- class shortCodeControl extends \WP_Customize_Control {
2292
- public $form;
2293
- public $title_postfix;
2294
-
2295
- public function render_content() {
2296
- $widget_title = 'Category Posts Shortcode' . $this->title_postfix;
2297
- ?>
2298
- <div class="widget-top">
2299
- <div class="widget-title"><h3><?php echo esc_html( $widget_title ); ?><span class="in-widget-title"></span></h3></div>
2300
- </div>
2301
- <div class="widget-inside" style="display: block;">
2302
- <div class="form">
2303
- <div class="widget-content">
2304
- <?php echo $this->form; ?>
2305
- </div>
2306
- </div>
2307
- </div>
2308
- <?php
2309
- }
2310
- }
2311
 
2312
  $args = array(
2313
  'post_type' => 'any',
@@ -2343,14 +686,12 @@ function customize_register( $wp_customize ) {
2343
  }
2344
 
2345
  foreach ( $meta as $k => $m ) {
2346
-
2347
  if ( isset( $m['template'] ) && $m['template'] ) {
2348
  ;
2349
  } else {
2350
  $m['template'] = convert_settings_to_template( $m );
2351
  }
2352
-
2353
- $m = wp_parse_args( $m, default_settings() );
2354
 
2355
  if ( 0 === count( $meta ) ) { // new widget, use defaults.
2356
  ;
@@ -2360,6 +701,8 @@ function customize_register( $wp_customize ) {
2360
  }
2361
  }
2362
 
 
 
2363
  $section_title = $k;
2364
  if ( '' === $section_title ) {
2365
  $section_title = __( '[shortcode]', 'category-posts' );
@@ -2415,7 +758,7 @@ function customize_register( $wp_customize ) {
2415
  };
2416
  }
2417
 
2418
- $sc = new shortCodeControl(
2419
  $wp_customize,
2420
  '_virtual-' . WIDGET_BASE_ID . '[' . $p->ID . '][' . $k . '][title]',
2421
  $args
@@ -2501,7 +844,7 @@ function mce_external_plugins( $plugin_array ) {
2501
  if ( is_array( $meta ) && isset( $meta['editor'] ) ) {
2502
  ;
2503
  } else {
2504
- $plugin_array[ __NAMESPACE__ ] = plugins_url( 'js/admin/tinymce.js?ver=' . VERSION, __FILE__ );
2505
  }
2506
  }
2507
 
@@ -2606,7 +949,7 @@ function show_user_profile( $user ) {
2606
  <th><label for="<?php echo __NAMESPACE__; ?>[panels]"><?php esc_html_e( 'Open panels behavior', 'category-posts' ); ?></label></th>
2607
  <td>
2608
  <input type="checkbox" name="<?php echo __NAMESPACE__; ?>[panels]" id="<?php echo __NAMESPACE__; ?>[panels]" <?php checked( $accordion ); ?>>
2609
- <label for=<?php echo __NAMESPACE__; ?>[panels]><?php esc_html_e( 'Close the curremtly open panel when opening a new one', 'category-posts' ); ?></label>
2610
  </td>
2611
  </tr>
2612
  <tr>
@@ -2647,426 +990,6 @@ function personal_options_update( $user_id ) {
2647
  }
2648
  }
2649
 
2650
- /*
2651
- * external API.
2652
- */
2653
-
2654
- /**
2655
- * Class that represent a virtual widget. Each widget being created will have relevant
2656
- * CSS output in the header, but still requires a call for getHTML method or renderHTML
2657
- * to get or output the HTML
2658
- *
2659
- * @since 4.7
2660
- */
2661
- class virtualWidget {
2662
-
2663
- /**
2664
- * A container for all the "active" objects
2665
- *
2666
- * @var Array
2667
- *
2668
- * @since 4.7
2669
- */
2670
- private static $collection = array();
2671
-
2672
- /**
2673
- * The identifier use as the id of the root html element when the HTML is generated.
2674
- *
2675
- * @var string
2676
- *
2677
- * @since 4.7
2678
- */
2679
- private $id;
2680
-
2681
- /**
2682
- * A container for all the "active" objects
2683
- *
2684
- * @var string The class name to be use us the class attribute on the root html element.
2685
- *
2686
- * @since 4.7
2687
- */
2688
- private $class;
2689
-
2690
- /**
2691
- * Construct the virtual widget. This should happen before wp_head action with priority
2692
- * 10 is executed if any CSS output should be generated.
2693
- *
2694
- * @param string $id The identifier use as the id of the root html element when the HTML
2695
- * is generated.
2696
- *
2697
- * @param string $class The class name to be use us the class attribute on the root html element.
2698
- *
2699
- * @param array $args The setting to be applied to the widget.
2700
- *
2701
- * @since 4.7
2702
- */
2703
- public function __construct( $id, $class, $args ) {
2704
- $this->id = $id;
2705
- $this->class = $class;
2706
- self::$collection[ $id ] = wp_parse_args( $args, default_settings() );
2707
- }
2708
-
2709
- /**
2710
- * Do what ever cleanup needed when the object is destroyed.
2711
- *
2712
- * @since 4.7
2713
- */
2714
- public function __destruct() {
2715
- unset( self::$collection[ $this->id ] );
2716
- }
2717
-
2718
- /**
2719
- * Return the HTML of the widget as is generated based on the settings passed at construction time
2720
- *
2721
- * @return string
2722
- *
2723
- * @since 4.7
2724
- */
2725
- public function getHTML() {
2726
-
2727
- $widget = new Widget();
2728
- $widget->number = $this->id; // needed to make a unique id for the widget html element.
2729
- ob_start();
2730
- $args = self::$collection[ $this->id ];
2731
- $args['is_shortcode'] = true; // indicate that we are doing shortcode processing to outputting funtions.
2732
- $widget->widget(array(
2733
- 'before_widget' => '',
2734
- 'after_widget' => '',
2735
- 'before_title' => '',
2736
- 'after_title' => '',
2737
- ), $args);
2738
- $ret = ob_get_clean();
2739
- $ret = '<div id="' . esc_attr( $this->id ) . '" class="' . esc_attr( $this->class ) . '">' . $ret . '</div>';
2740
- return $ret;
2741
- }
2742
-
2743
- /**
2744
- * Output the widget HTML
2745
- *
2746
- * Just a wrapper that output getHTML
2747
- *
2748
- * @return void
2749
- *
2750
- * @since 4.7
2751
- */
2752
- public function renderHTML() {
2753
- echo $this->getHTML();
2754
- }
2755
-
2756
- /**
2757
- * Calculate the CSS rules required for the widget as is generated based on the settings passed at construction time
2758
- *
2759
- * @param bool $is_shortcode Indicated if rules are generated for a shortcode.
2760
- * @param array $ret "returned" Collection of CSS rules.
2761
- *
2762
- * @since 4.7
2763
- */
2764
- public function getCSSRules( $is_shortcode, &$rules ) {
2765
- $ret = array();
2766
- $settings = self::$collection[ $this->id ];
2767
- $widget_id = $this->id;
2768
- if ( ! $is_shortcode ) {
2769
- $widget_id .= '-internal';
2770
- }
2771
- $disable_css = isset( $settings['disable_css'] ) && $settings['disable_css'];
2772
-
2773
- if ( ! $disable_css ) { // checks if css disable is not set.
2774
-
2775
- $styles = array( // styles that should be applied to all widgets.
2776
- 'thumb_clenup' => '.cat-post-item img {max-width: initial; max-height: initial; margin: initial;}',
2777
- 'author_clenup' => '.cat-post-author {margin-bottom: 0;}',
2778
- 'thumb' => '.cat-post-thumbnail {margin: 5px 10px 5px 0;}',
2779
- 'item_clenup' => '.cat-post-item:before {content: ""; clear: both;}',
2780
- );
2781
-
2782
- if ( ! ( isset( $settings['disable_font_styles'] ) && $settings['disable_font_styles'] ) ) { // checks if disable font styles is not set.
2783
- // add general styles which apply to font styling.
2784
- $styles['title_font'] = '.cat-post-title {font-size: 15px;}';
2785
- $styles['current_title_font'] = '.cat-post-current .cat-post-title {font-weight: bold; text-transform: uppercase;}';
2786
- $styles['date_font'] = '.cat-post-date {font-size: 14px; line-height: 18px; font-style: italic; margin-bottom: 5px;}';
2787
- $styles['comment_num_font'] = '.cat-post-comment-num {font-size: 14px; line-height: 18px;}';
2788
- }
2789
-
2790
- /*
2791
- * The twenty seventeen theme have a border between the LI elements of a widget,
2792
- * so remove our border if we detect its use to avoid conflicting styling.
2793
- */
2794
- if ( ! $is_shortcode && function_exists( 'twentyseventeen_setup' ) ) {
2795
- $styles['item_style'] = '.cat-post-item {list-style: none; list-style-type: none; margin: 0; padding: 3px 0;}';
2796
- } else {
2797
- $styles['item_style'] = '.cat-post-item {border-bottom: 1px solid #ccc; list-style: none; list-style-type: none; margin: 3px 0; padding: 3px 0;}';
2798
- $styles['last_item_style'] = '.cat-post-item:last-child {border-bottom: none;}';
2799
- }
2800
-
2801
- // everything link related styling
2802
- // if we are dealing with "everything is a link" option, we need to add the clear:both to the a element, not the div.
2803
- if ( isset( $settings['everything_is_link'] ) && $settings['everything_is_link'] ) {
2804
- $styles['after_item'] = '.cat-post-item a:after {content: ""; display: table; clear: both;}';
2805
- } else {
2806
- $styles['after_item'] = '.cat-post-item:after {content: ""; display: table; clear: both;}';
2807
- }
2808
-
2809
- // add post format css if needed.
2810
- if ( isset( $settings['template'] ) && preg_match( '/%thumb%/', $settings['template'] ) ) {
2811
- if ( ! isset( $settings['show_post_format'] ) || ( ( 'none' !== $settings['show_post_format'] ) && ( 'nocss' !== $settings['show_post_format'] ) ) ) {
2812
- static $fonts_added = false;
2813
- if ( ! $fonts_added ) {
2814
- $fonturl = esc_url( plugins_url( 'icons/font', __FILE__ ) );
2815
- $ret['post_format_font'] = "@font-face {\n" .
2816
- "font-family: 'cat_post';\n" .
2817
- "src: url('$fonturl/cat_post.eot?58348147');\n" .
2818
- "src: url('$fonturl/cat_post.eot?58348147#iefix') format('embedded-opentype'),\n" .
2819
- " url('$fonturl/cat_post.woff2?58348147') format('woff2'),\n" .
2820
- " url('$fonturl/cat_post.woff?58348147') format('woff'),\n" .
2821
- " url('$fonturl/cat_post.ttf?58348147') format('truetype');\n" .
2822
- " font-weight: normal;\n" .
2823
- " font-style: normal;\n" .
2824
- "}\n";
2825
- }
2826
- $fonts_added = true;
2827
-
2828
- $placement = '';
2829
- switch ( $settings['show_post_format'] ) {
2830
- case 'topleft':
2831
- $placement = 'top:10%; left:10%;';
2832
- break;
2833
- case 'bottomleft':
2834
- $placement = 'bottom:10%; left:10%;';
2835
- break;
2836
- case 'ceter':
2837
- $placement = 'top:calc(50% - 34px); left:calc(50% - 34px);';
2838
- break;
2839
- case 'topright':
2840
- $placement = 'top:10%; right:10%;';
2841
- break;
2842
- case 'bottomright':
2843
- $placement = 'bottom:10%; right:10%;';
2844
- break;
2845
- }
2846
- $styles['post_format_thumb'] = '.cat-post-thumbnail span {position:relative}';
2847
- $styles['post_format_icon_styling'] = '.cat-post-format:before {font-family: "cat_post"; position:absolute; color:#FFFFFF; font-size:64px; line-height: 1; ' . $placement . '}';
2848
-
2849
- $styles['post_format_icon_aside'] = ".cat-post-format-aside:before { content: '\\f0f6'; }";
2850
- $styles['post_format_icon_chat'] = ".cat-post-format-chat:before { content: '\\e802'; }";
2851
- $styles['post_format_icon_gallery'] = ".cat-post-format-gallery:before { content: '\\e805'; }";
2852
- $styles['post_format_icon_link'] = ".cat-post-format-link:before { content: '\\e809'; }";
2853
- $styles['post_format_icon_image'] = ".cat-post-format-image:before { content: '\\e800'; }";
2854
- $styles['post_format_icon_quote'] = ".cat-post-format-quote:before { content: '\\f10d'; }";
2855
- $styles['post_format_icon_status'] = ".cat-post-format-status:before { content: '\\e80a'; }";
2856
- $styles['post_format_icon_video'] = ".cat-post-format-video:before { content: '\\e801'; }";
2857
- $styles['post_format_icon_audio'] = ".cat-post-format-audio:before { content: '\\e803'; }";
2858
-
2859
- }
2860
- }
2861
-
2862
- foreach ( $styles as $key => $style ) {
2863
- $ret[ $key ] = '#' . $widget_id . ' ' . $style;
2864
- }
2865
-
2866
- if ( $is_shortcode ) {
2867
- // Twenty Sixteen Theme adds underlines to links with box whadow wtf ...
2868
- $ret['twentysixteen_thumb'] = '#' . $widget_id . ' .cat-post-thumbnail {box-shadow:none}'; // this for the thumb link.
2869
- if ( ! ( isset( $settings['disable_font_styles'] ) && $settings['disable_font_styles'] ) ) { // checks if disable font styles is not set.
2870
- $ret['twentysixteen_tag_link'] = '#' . $widget_id . ' .cat-post-tax-post_tag a {box-shadow:none}'; // this for the tag link.
2871
- $ret['twentysixteen_tag_span'] = '#' . $widget_id . ' .cat-post-tax-post_tag span {box-shadow:none}'; // this for the tag link.
2872
- }
2873
- // Twenty Fifteen Theme adds border ...
2874
- $ret['twentyfifteen_thumb'] = '#' . $widget_id . ' .cat-post-thumbnail {border:0}'; // this for the thumb link.
2875
- if ( ! ( isset( $settings['disable_font_styles'] ) && $settings['disable_font_styles'] ) ) { // checks if disable font styles is not set.
2876
- $ret['twentysixteen_tag_link'] = '#' . $widget_id . ' .cat-post-tax-post_tag a {border:0}'; // this for the tag link.
2877
- $ret['twentysixteen_tag_span'] = '#' . $widget_id . ' .cat-post-tax-post_tag span {border:0}'; // this for the tag link.
2878
- }
2879
- }
2880
-
2881
- // probably all Themes have too much margin on their p element when used in the shortcode or widget.
2882
- $ret['p_styling'] = '#' . $widget_id . ' p {margin:5px 0 0 0}'; // since on bottom it will make the spacing on cover
2883
- // bigger (add to the padding) use only top for now.
2884
- $ret['div_styling'] = '#' . $widget_id . ' li > div {margin:5px 0 0 0; clear:both;}'; // Add margin between the rows.
2885
-
2886
- // use WP dashicons in the template (e.g. for premade Template 'All and icons')
2887
- $ret['dashicons'] = '#' . $widget_id . ' .dashicons {vertical-align:middle;}';
2888
- }
2889
-
2890
- // Regardless if css is disabled we need some styling for the thumbnail
2891
- // to make sure cropping is properly done, and they fit the allocated space.
2892
- if ( isset( $settings['template'] ) && preg_match( '/%thumb%/', $settings['template'], $m, PREG_OFFSET_CAPTURE ) ) {
2893
- if ( isset( $settings['use_css_cropping'] ) && $settings['use_css_cropping'] ) {
2894
- $ret['thumb_crop'] = '#' . $widget_id . ' .cat-post-crop {overflow: hidden; display:inline-block}';
2895
- } else {
2896
- $ret['thumb_overflow'] = '#' . $widget_id . ' .cat-post-thumbnail span {overflow: hidden; display:inline-block}';
2897
- }
2898
- $ret['thumb_styling'] = '#' . $widget_id . ' .cat-post-item img {margin: initial;}';
2899
-
2900
- // Thumbnail related positioning rules.
2901
- $wrap = isset( $settings['text_do_not_wrap_thumb'] ) && $settings['text_do_not_wrap_thumb'];
2902
- if ( $wrap ) {
2903
- $ret['thumb_flex'] = '#' . $widget_id . ' .cat-post-do-not-wrap-thumbnail {display:flex;}'; // Thumbnail container should flex.
2904
- }
2905
- $ret['text_do_not_wrap_thumb'] = '#' . $widget_id . ' .cat-post-thumbnail {float:left;}';
2906
- }
2907
-
2908
- // Some hover effect require css to work, add it even if CSS is disabled.
2909
- if ( isset( $settings['thumb_hover'] ) ) {
2910
- switch ( $settings['thumb_hover'] ) {
2911
- case 'white':
2912
- $ret['white_hover_background'] = '#' . $widget_id . ' .cat-post-white {background-color: white;}';
2913
- $ret['white_hover_thumb'] = '#' . $widget_id . ' .cat-post-white img {padding-bottom: 0 !important; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
2914
- $ret['white_hover_transform'] = '#' . $widget_id . ' .cat-post-white:hover img {opacity: 0.8;}';
2915
- break;
2916
- case 'dark':
2917
- $ret['dark_hover_thumb'] = '#' . $widget_id . ' .cat-post-dark img {padding-bottom: 0 !important; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
2918
- $ret['dark_hover_transform'] = '#' . $widget_id . ' .cat-post-dark:hover img {-webkit-filter: brightness(75%); -moz-filter: brightness(75%); -ms-filter: brightness(75%); -o-filter: brightness(75%); filter: brightness(75%);}';
2919
- break;
2920
- case 'scale':
2921
- $ret['scale_hover_thumb'] = '#' . $widget_id . ' .cat-post-scale img {margin: initial; padding-bottom: 0 !important; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
2922
- $ret['scale_hover_transform'] = '#' . $widget_id . ' .cat-post-scale:hover img {-webkit-transform: scale(1.1, 1.1); -ms-transform: scale(1.1, 1.1); transform: scale(1.1, 1.1);}';
2923
- break;
2924
- case 'blur':
2925
- $ret['blur_hover_thumb'] = '#' . $widget_id . ' .cat-post-blur img {padding-bottom: 0 !important; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
2926
- $ret['blur_hover_transform'] = '#' . $widget_id . ' .cat-post-blur:hover img {-webkit-filter: blur(2px); -moz-filter: blur(2px); -o-filter: blur(2px); -ms-filter: blur(2px); filter: blur(2px);}';
2927
- break;
2928
- case 'icon':
2929
- $fonturl = esc_url( plugins_url( 'icons/font', __FILE__ ) );
2930
- $ret['icon_hover_font'] = "@font-face {\n" .
2931
- "font-family: 'cat_post';\n" .
2932
- "src: url('$fonturl/cat_post.eot?58348147');\n" .
2933
- "src: url('$fonturl/cat_post.eot?58348147#iefix') format('embedded-opentype'),\n" .
2934
- " url('$fonturl/cat_post.woff2?58348147') format('woff2'),\n" .
2935
- " url('$fonturl/cat_post.woff?58348147') format('woff'),\n" .
2936
- " url('$fonturl/cat_post.ttf?58348147') format('truetype');\n" .
2937
- " font-weight: normal;\n" .
2938
- " font-style: normal;\n" .
2939
- "}\n";
2940
-
2941
- $ret['icon_hover_thumb'] = '#' . $widget_id . ' .cat-post-format-standard:before {opacity:0; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
2942
- $ret['icon_hover_transform'] = '#' . $widget_id . ' .cat-post-thumbnail:hover .cat-post-format-standard:before {opacity:1;}';
2943
- if ( isset( $settings['show_post_format'] ) && ( 'none' === $settings['show_post_format'] ) ) {
2944
- $ret[] = '#' . $widget_id . ' .cat-post-thumbnail {position:relative}';
2945
- $ret[] = '#' . $widget_id . ' .cat-post-icon .cat-post-format:before {font-family: "cat_post"; position:absolute; color:#FFFFFF; font-size:64px; line-height: 1; ' .
2946
- 'top:calc(50% - 34px); left:calc(50% - 34px);}';
2947
- }
2948
- $ret[] = '#' . $widget_id . " .cat-post-format-standard:before {padding-left:12px; content: '\\e806'; }";
2949
- break;
2950
- }
2951
- }
2952
- $rules[] = $ret;
2953
- }
2954
-
2955
- /**
2956
- * Output the widget CSS
2957
- *
2958
- * Just a wrapper that output getCSSRules
2959
- *
2960
- * @param bool $is_shortcode Indicates if we are in the context os a shortcode.
2961
- *
2962
- * @since 4.7
2963
- */
2964
- public function outputCSS( $is_shortcode ) {
2965
- $rules = array();
2966
- getCSSRules( $is_shortcode, $rules );
2967
- foreach ( $rules as $rule ) {
2968
- echo "$rule\n";
2969
- }
2970
- }
2971
-
2972
- /**
2973
- * Get the id the virtual widget was registered with
2974
- *
2975
- * @return string
2976
- *
2977
- * @since 4.7
2978
- */
2979
- public function id() {
2980
- return $this->id;
2981
- }
2982
-
2983
- /**
2984
- * Get all the setting of the virtual widgets in an array
2985
- *
2986
- * @return array
2987
- *
2988
- * @since 4.7
2989
- */
2990
- public static function getAllSettings() {
2991
- return self::$collection;
2992
- }
2993
-
2994
- }
2995
-
2996
- /**
2997
- * Class that implement a simple repository for the virtual widgets representing
2998
- * actuall shortcode and widgets
2999
- *
3000
- * @since 4.7
3001
- */
3002
- class virtualWidgetsRepository {
3003
-
3004
- /**
3005
- * Collection of objects representing shortcodes.
3006
- *
3007
- * @var array
3008
- *
3009
- * @since 4.7
3010
- */
3011
- private static $shortcodeCollection = array();
3012
-
3013
- /**
3014
- * Collection of objects representing widgets.
3015
- *
3016
- * @var array
3017
- *
3018
- * @since 4.7
3019
- */
3020
- private static $widgetCollection = array();
3021
-
3022
- /**
3023
- * Add a virtual widget representing a shortcode to the repository
3024
- *
3025
- * @param string $index A name to identify the specific shortcode.
3026
- * @param virtualWidget $widget The virtual widget for it.
3027
- *
3028
- * @since 4.7
3029
- */
3030
- public function addShortcode( $index, $widget ) {
3031
- self::$shortcodeCollection[ $index ] = $widget;
3032
- }
3033
-
3034
- /**
3035
- * Get all the virtual widgets representing actual shortcodes
3036
- *
3037
- * @return array
3038
- *
3039
- * @since 4.7
3040
- */
3041
- public function getShortcodes() {
3042
- return self::$shortcodeCollection;
3043
- }
3044
-
3045
- /**
3046
- * Add a virtual widget representing awidget to the repository
3047
- *
3048
- * @param string $index A name to identify the specific widget.
3049
- * @param virtualWidget $widget The virstual widget for it.
3050
- *
3051
- * @since 4.7
3052
- */
3053
- public function addWidget( $index, $widget ) {
3054
- self::$widgetCollection[ $index ] = $widget;
3055
- }
3056
-
3057
- /**
3058
- * Get all the virtual widgets representing actual widgets
3059
- *
3060
- * @return array
3061
- *
3062
- * @since 4.7
3063
- */
3064
- public function getWidgets() {
3065
- return self::$widgetCollection;
3066
- }
3067
-
3068
- }
3069
-
3070
  add_action( 'wp_loaded', __NAMESPACE__ . '\wp_loaded' );
3071
 
3072
  /**
12
  Plugin URI: https://wordpress.org/plugins/category-posts/
13
  Description: Adds a widget that shows the most recent posts from a single category.
14
  Author: TipTopPress
15
+ Version: 4.9.1
16
  Author URI: http://tiptoppress.com
17
  Text Domain: category-posts
18
  Domain Path: /languages
25
  exit;
26
  }
27
 
28
+ const VERSION = '4.9.1';
29
+ const DOC_URL = 'http://tiptoppress.com/category-posts-widget/documentation-4-9/';
30
+ const PRO_URL = 'http://tiptoppress.com/term-and-category-based-posts-widget/';
31
+ const SUPPORT_URL = 'https://wordpress.org/support/plugin/category-posts/';
32
  const SHORTCODE_NAME = 'catposts';
33
  const SHORTCODE_META = 'categoryPosts-shorcode';
34
  const WIDGET_BASE_ID = 'category-posts';
35
 
36
+ require_once __DIR__ . '/class-virtual-widget.php';
37
+ require_once __DIR__ . '/class-virtual-widgets-repository.php';
38
+ require_once __DIR__ . '/class-widget.php';
39
+ require_once __DIR__ . '/loadmore.php';
40
+ require_once __DIR__ . '/localizeddate.php';
41
+
42
  /**
43
  * Adds the "Customize" link to the Toolbar on edit mode.
44
  *
85
  */
86
  function wp_head() {
87
 
88
+ $widget_repository = new Virtual_Widgets_Repository();
89
 
90
  $styles = array();
91
 
99
 
100
  if ( ! empty( $styles ) ) {
101
  ?>
102
+ <style>
103
  <?php
104
  foreach ( $styles as $rules ) {
105
  foreach ( $rules as $rule ) {
125
  global $post;
126
  global $wp_registered_widgets;
127
 
128
+ $repository = new Virtual_Widgets_Repository();
129
 
130
  // check first for shortcode settings.
131
  if ( is_singular() ) {
132
  $names = shortcode_names( SHORTCODE_NAME, $post->post_content );
133
 
134
  foreach ( $names as $name ) {
135
+ $meta = shortcode_settings( get_the_ID(), $name );
136
  if ( is_array( $meta ) ) {
137
  $id = WIDGET_BASE_ID . '-shortcode-' . get_the_ID(); // needed to make a unique id for the widget html element.
138
  if ( '' !== $name ) { // if not default name append to the id.
139
  $id .= '-' . sanitize_title( $name ); // sanitize to be on the safe side, not sure where when and how this will be used.
140
  }
141
+ $repository->addShortcode( $name, new Virtual_Widget( $id, WIDGET_BASE_ID . '-shortcode', $meta ) );
142
  }
143
  }
144
  }
159
  $widgetclass = new $class();
160
  $allsettings = $widgetclass->get_settings();
161
  $settings = isset( $allsettings[ str_replace( $widget_base . '-', '', $widget ) ] ) ? $allsettings[ str_replace( $widget_base . '-', '', $widget ) ] : false;
162
+ $repository->addWidget( $widget, new Virtual_Widget( $widget, $widget, $settings ) );
163
  }
164
  }
165
  }
169
 
170
  add_action( 'wp_head', __NAMESPACE__ . '\wp_head' );
171
 
172
+ /**
173
+ * Enqueue widget related scripts for the widget front-end
174
+ *
175
+ * @since 4.8
176
+ */
177
+ function frontend_script() {
178
+ $suffix = 'min.js';
179
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
180
+ $suffix = 'js';
181
+ }
182
+ wp_enqueue_script( 'cat-posts-frontend-js', plugins_url( 'js/frontend/category-posts-frontend.' . $suffix, __FILE__ ), array( 'jquery' ), VERSION, true );
183
+ }
184
+
185
+ /**
186
+ * Embed the front end JS in the HTML footer.
187
+ *
188
+ * @since 4.9
189
+ */
190
+ function embed_front_end_scripts() {
191
+ $suffix = 'min.js';
192
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
193
+ $suffix = 'js';
194
+ }
195
+ echo '<script>';
196
+ include __DIR__ . '/js/frontend/category-posts-frontend.' . $suffix;
197
+ echo '</script>';
198
+ }
199
+
200
  /**
201
  * Enqueue widget related scripts for the widget admin page and customizer.
202
  *
204
  */
205
  function admin_scripts( $hook ) {
206
 
207
+ if ( 'widgets.php' === $hook || 'post.php' === $hook ) { // enqueue only for widget admin and customizer. (add if post.php: fix make widget SiteOrigin Page Builder plugin, GH issue #181)
208
 
209
+ /*
210
+ * Add script to control admin UX.
211
+ */
212
+
213
+ // Use unminified version of JS when debuging, and minified when not.
214
+ $suffix = 'min.js';
215
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
216
+ $suffix = 'js';
217
+ }
218
+ wp_register_script( 'category-posts-widget-admin-js', plugins_url( 'js/admin/category-posts-widget.' . $suffix, __FILE__ ), array( 'jquery' ), VERSION, true );
219
  wp_enqueue_script( 'category-posts-widget-admin-js' );
220
 
221
  $js_data = array( 'accordion' => false );
251
  load_plugin_textdomain( 'category-posts', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' );
252
  }
253
 
 
 
 
 
 
 
 
 
 
254
  /**
255
+ * Add styles for widget admin sections
256
  *
257
  * @since 4.1
258
  **/
259
  function admin_styles() {
260
+ wp_enqueue_style( 'cat-posts-admin-styles', plugins_url( 'styles/admin/category-posts-widget.css', __FILE__ ), array(), VERSION, false );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  }
262
 
263
+ add_action( 'admin_print_styles-widgets.php', __NAMESPACE__ . '\admin_styles' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
+ // fix make widget SiteOrigin Page Builder plugin, GH issue #181
266
+ add_action('siteorigin_panel_enqueue_admin_scripts', __NAMESPACE__ . '\admin_styles' );
267
 
268
  /**
269
  * Get the tags which might be used in the template.
299
  return $regexp;
300
  }
301
 
302
+ /**
303
+ * The convert old data structure to current one.
304
+ *
305
+ * @param array $settings The settings to upgrade.
306
+ *
307
+ * @return array The settings matching current version standard.
308
+ *
309
+ * @since 4.8
310
+ */
311
+ function upgrade_settings( $settings ) {
312
+
313
+ if ( 0 === count( $settings ) ) {
314
+ return default_settings();
315
+ }
316
+
317
+ if ( ! isset( $settings['ver'] ) ) {
318
+ /*
319
+ * Pre 4.9 version.
320
+ */
321
+
322
+ // Upgrade the hide if empty option.
323
+ if ( isset( $settings['hide_if_empty'] ) && $settings['hide_if_empty'] ) {
324
+ $settings['no_match_handling'] = 'hide';
325
+ } else {
326
+ $settings['no_match_handling'] = 'nothing';
327
+ }
328
+ if ( isset( $settings['hide_if_empty'] ) ) {
329
+ unset( $settings['hide_if_empty'] );
330
+ }
331
+ }
332
+
333
+ $settings['ver'] = VERSION;
334
+
335
+ // Make sure all "empty" settings have default value.
336
+ $settings = wp_parse_args( $settings, default_settings() );
337
+
338
+ return $settings;
339
+ }
340
+
341
  /**
342
  * Convert pre 4.8 settings into template
343
  *
385
  return $template;
386
  }
387
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  /*
389
  * Plugin action links section
390
  */
405
  * @param array $links The current links about to be displayed.
406
  */
407
  function add_action_links( $links ) {
 
 
 
408
 
409
+ if ( ! class_exists( '\\termcategoryPostsPro\\Widget' ) ) {
410
+ $pro_link = array(
411
+ '<a target="_blank" href="' . esc_url( PRO_URL ) . '">' . esc_html__( 'Get the Pro version', 'category-posts' ) . '</a>',
412
+ );
413
+
414
+ $links = array_merge( $pro_link, $links );
415
+ }
416
 
417
  return $links;
418
  }
426
 
427
  add_action( 'widgets_init', __NAMESPACE__ . '\register_widget' );
428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  /*
430
  * shortcode section.
431
  */
432
 
433
  /**
434
+ * Get shortcode settings taking into account if it is being customized
435
  *
436
+ * When not customized returns the settings as stored in the meta, but when
437
+ * it is customized returns the setting stored in the virtual option used by the customizer
438
  *
439
+ * @param string $pid The ID of the post in which the shortcode is.
440
+ * @param string $name The name of the shortcode to retun, empty string indicates the nameless.
441
  *
442
+ * @return array The shortcode settings if a short code name exists or is an empty string,
443
+ * empty array if name not found.
444
  *
445
+ * @since 4.6
446
  */
447
+ function shortcode_settings( $pid, $name ) {
448
+ $meta = get_post_meta( $pid, SHORTCODE_META, true );
449
 
450
  if ( ! empty( $meta ) && ! is_array( reset( $meta ) ) ) {
451
  $meta = array( '' => $meta ); // the conversion.
459
  if ( is_customize_preview() ) {
460
  $o = get_option( '_virtual-' . WIDGET_BASE_ID );
461
  if ( is_array( $o ) ) {
462
+ $instance = $o[ $pid ][ $name ];
463
+ $instance['ver'] = VERSION;
464
  }
465
  }
466
+
467
  if ( isset( $instance['template'] ) && $instance['template'] ) {
468
  ;
469
  } else {
470
  $instance['template'] = convert_settings_to_template( $instance );
471
  }
472
 
473
+ $instance = upgrade_settings( $instance );
474
+
475
  return $instance;
476
  }
477
 
484
  * @return string An HTML of the "widget" based on its settings, actual or customized
485
  */
486
  function shortcode( $attr, $content = null ) {
487
+ $repository = new Virtual_Widgets_Repository();
488
 
489
  $shortcodes = $repository->getShortcodes();
490
 
543
  */
544
  function default_settings() {
545
  return array(
546
+ 'title' => __( 'Recent Posts', 'category-posts' ),
547
+ 'title_link' => false,
548
+ 'title_link_url' => '',
549
+ 'hide_title' => false,
550
+ 'cat' => 0,
551
+ 'num' => get_option( 'posts_per_page' ),
552
+ 'offset' => 1,
553
+ 'sort_by' => 'date',
554
+ 'status' => 'publish',
555
+ 'asc_sort_order' => false,
556
+ 'exclude_current_post' => false,
557
+ 'hideNoThumb' => false,
558
+ 'footer_link_text' => '',
559
+ 'footer_link' => '',
560
+ 'thumb_w' => get_option( 'thumbnail_size_w', 150 ),
561
+ 'thumb_fluid_width' => 100,
562
+ 'thumb_h' => get_option( 'thumbnail_size_h', 150 ),
563
+ 'use_css_cropping' => true,
564
+ 'thumb_hover' => 'none',
565
+ 'hide_post_titles' => false,
566
+ 'excerpt_lines' => 5,
567
+ 'excerpt_length' => 0,
568
+ 'excerpt_more_text' => __( '...', 'category-posts' ),
569
+ 'excerpt_filters' => false,
570
+ 'comment_num' => false,
571
+ 'date_link' => false,
572
+ 'date_format' => '',
573
+ 'disable_css' => false,
574
+ 'disable_font_styles' => false,
575
+ 'show_post_format' => 'none',
576
+ 'no_cat_childs' => false,
577
+ 'everything_is_link' => false,
578
+ 'preset_date_format' => 'sitedateandtime',
579
+ 'template' => "%title%\n\n%thumb%",
580
+ 'text_do_not_wrap_thumb' => false,
581
+ 'enable_loadmore' => false,
582
+ 'loadmore_text' => __( 'Load More', 'category-posts' ),
583
+ 'loading_text' => __( 'Loading...', 'category-posts' ),
584
+ 'date_range' => 'off',
585
+ 'start_date' => '',
586
+ 'end_date' => '',
587
+ 'days_ago' => 30,
588
+ 'no_match_handling' => 'nothing',
589
+ 'no_match_text' => '',
590
+ 'default_thunmbnail' => 0,
591
+ 'ver' => VERSION,
592
  );
593
  }
594
 
650
  */
651
  function customize_register( $wp_customize ) {
652
 
653
+ require_once __DIR__ . '/class-shortcode-control.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
 
655
  $args = array(
656
  'post_type' => 'any',
686
  }
687
 
688
  foreach ( $meta as $k => $m ) {
689
+
690
  if ( isset( $m['template'] ) && $m['template'] ) {
691
  ;
692
  } else {
693
  $m['template'] = convert_settings_to_template( $m );
694
  }
 
 
695
 
696
  if ( 0 === count( $meta ) ) { // new widget, use defaults.
697
  ;
701
  }
702
  }
703
 
704
+ $m = upgrade_settings( $m );
705
+
706
  $section_title = $k;
707
  if ( '' === $section_title ) {
708
  $section_title = __( '[shortcode]', 'category-posts' );
758
  };
759
  }
760
 
761
+ $sc = new ShortCode_Control(
762
  $wp_customize,
763
  '_virtual-' . WIDGET_BASE_ID . '[' . $p->ID . '][' . $k . '][title]',
764
  $args
844
  if ( is_array( $meta ) && isset( $meta['editor'] ) ) {
845
  ;
846
  } else {
847
+ $plugin_array[ __NAMESPACE__ ] = plugins_url( 'js/admin/tinymce.min.js?ver=' . VERSION, __FILE__ );
848
  }
849
  }
850
 
949
  <th><label for="<?php echo __NAMESPACE__; ?>[panels]"><?php esc_html_e( 'Open panels behavior', 'category-posts' ); ?></label></th>
950
  <td>
951
  <input type="checkbox" name="<?php echo __NAMESPACE__; ?>[panels]" id="<?php echo __NAMESPACE__; ?>[panels]" <?php checked( $accordion ); ?>>
952
+ <label for=<?php echo __NAMESPACE__; ?>[panels]><?php esc_html_e( 'Close the currently open panel when opening a new one', 'category-posts' ); ?></label>
953
  </td>
954
  </tr>
955
  <tr>
990
  }
991
  }
992
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
993
  add_action( 'wp_loaded', __NAMESPACE__ . '\wp_loaded' );
994
 
995
  /**
class-shortcode-control.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Costumizer Shortcode control class implementation.
4
+ *
5
+ * @package categoryposts.
6
+ *
7
+ * @since 4.9
8
+ */
9
+
10
+ namespace categoryPosts;
11
+
12
+ // Don't call the file directly.
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * Costumizer Shortcode control.
19
+ *
20
+ * @since 4.9
21
+ */
22
+ class ShortCode_Control extends \WP_Customize_Control {
23
+
24
+ /**
25
+ * The form that should be displayed in the control.
26
+ *
27
+ * @var string
28
+ *
29
+ * @since 4.7
30
+ */
31
+ public $form;
32
+
33
+ /**
34
+ * The suffix of the title to be displayed in the control (unescaped).
35
+ *
36
+ * @var string
37
+ *
38
+ * @since 4.7
39
+ */
40
+ public $title_postfix;
41
+
42
+ /**
43
+ * Render the control.
44
+ *
45
+ * @since 4.6
46
+ */
47
+ public function render_content() {
48
+ $widget_title = 'Category Posts Shortcode' . $this->title_postfix;
49
+ ?>
50
+ <div class="widget-top">
51
+ <div class="widget-title"><h3><?php echo esc_html( $widget_title ); ?><span class="in-widget-title"></span></h3></div>
52
+ </div>
53
+ <div class="widget-inside" style="display: block;">
54
+ <div class="form">
55
+ <div class="widget-content">
56
+ <?php echo $this->form; // Xss off. ?>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ <?php
61
+ }
62
+ }
class-virtual-widget.php ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Implementation of virtual widget.
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
+ * Class that represent a virtual widget. Each widget being created will have relevant
19
+ * CSS output in the header, but still requires a call for getHTML method or renderHTML
20
+ * to get or output the HTML
21
+ *
22
+ * @since 4.7
23
+ */
24
+ class Virtual_Widget {
25
+
26
+ /**
27
+ * A container for all the "active" objects
28
+ *
29
+ * @var Array
30
+ *
31
+ * @since 4.7
32
+ */
33
+ private static $collection = array();
34
+
35
+ /**
36
+ * The identifier use as the id of the root html element when the HTML is generated.
37
+ *
38
+ * @var string
39
+ *
40
+ * @since 4.7
41
+ */
42
+ private $id;
43
+
44
+ /**
45
+ * The class name to be use us the class attribute on the root html element.
46
+ *
47
+ * @var string
48
+ *
49
+ * @since 4.7
50
+ */
51
+ private $class;
52
+
53
+ /**
54
+ * Construct the virtual widget. This should happen before wp_head action with priority
55
+ * 10 is executed if any CSS output should be generated.
56
+ *
57
+ * @param string $id The identifier use as the id of the root html element when the HTML
58
+ * is generated.
59
+ *
60
+ * @param string $class The class name to be use us the class attribute on the root html element.
61
+ *
62
+ * @param array $args The setting to be applied to the widget.
63
+ *
64
+ * @since 4.7
65
+ */
66
+ public function __construct( $id, $class, $args ) {
67
+ $this->id = $id;
68
+ $this->class = $class;
69
+ self::$collection[ $id ] = upgrade_settings( $args );
70
+ }
71
+
72
+ /**
73
+ * Do what ever cleanup needed when the object is destroyed.
74
+ *
75
+ * @since 4.7
76
+ */
77
+ public function __destruct() {
78
+ unset( self::$collection[ $this->id ] );
79
+ }
80
+
81
+ /**
82
+ * Return the HTML of the widget as is generated based on the settings passed at construction time
83
+ *
84
+ * @return string
85
+ *
86
+ * @since 4.7
87
+ */
88
+ public function getHTML() {
89
+
90
+ $widget = new Widget();
91
+ $widget->number = $this->id; // needed to make a unique id for the widget html element.
92
+ ob_start();
93
+ $args = self::$collection[ $this->id ];
94
+ $args['is_shortcode'] = true; // indicate that we are doing shortcode processing to outputting funtions.
95
+ $widget->widget(array(
96
+ 'before_widget' => '',
97
+ 'after_widget' => '',
98
+ 'before_title' => '',
99
+ 'after_title' => '',
100
+ ), $args);
101
+ $ret = ob_get_clean();
102
+ $ret = '<div id="' . esc_attr( $this->id ) . '" class="' . esc_attr( $this->class ) . '">' . $ret . '</div>';
103
+ return $ret;
104
+ }
105
+
106
+ /**
107
+ * Get an array of HTML pre item, for item starting from a specific position.
108
+ *
109
+ * @since 4.9
110
+ *
111
+ * @param int $start The start element (0 based).
112
+ * @param int $number The maximal number of elements to return. A value of 0
113
+ * Indicates to use the widget settings for that.
114
+ * @param string $context The ID of the post in which the items will be displayed.
115
+ * A empty string or any value which is not of an ID
116
+ * of actual post will be treated as if there is no context.
117
+ *
118
+ * @return string[] Array of HTML per element with the $start element first
119
+ * $start+1 next etc. An empty array is returned if there
120
+ * are no applicable items.
121
+ */
122
+ public function get_elements_HTML( $start, $number, $context ) {
123
+ $ret = array();
124
+
125
+ $widget = new Widget();
126
+ $widget->number = $this->id; // needed to make a unique id for the widget html element.
127
+
128
+ $ret = $widget->get_elements_HTML( self::$collection[ $this->id ], $context, $start, $number );
129
+ return $ret;
130
+ }
131
+
132
+ /**
133
+ * Output the widget HTML
134
+ *
135
+ * Just a wrapper that output getHTML
136
+ *
137
+ * @return void
138
+ *
139
+ * @since 4.7
140
+ */
141
+ public function renderHTML() {
142
+ echo $this->getHTML(); // Xss off. Raw HTML is generated elsewhre.
143
+ }
144
+
145
+ /**
146
+ * Calculate the CSS rules required for the widget as is generated based on the settings passed at construction time
147
+ *
148
+ * @param bool $is_shortcode Indicated if rules are generated for a shortcode.
149
+ * @param array $rules "returned" Collection of CSS rules.
150
+ *
151
+ * @since 4.7
152
+ */
153
+ public function getCSSRules( $is_shortcode, &$rules ) {
154
+ $ret = array();
155
+ $settings = self::$collection[ $this->id ];
156
+ $widget_id = $this->id;
157
+ if ( ! $is_shortcode ) {
158
+ $widget_id .= '-internal';
159
+ }
160
+ $disable_css = isset( $settings['disable_css'] ) && $settings['disable_css'];
161
+
162
+ if ( ! $disable_css ) { // checks if css disable is not set.
163
+
164
+ $styles = array( // styles that should be applied to all widgets.
165
+ 'normalize' => 'ul {padding: 0;}',
166
+ 'thumb_clenup' => '.cat-post-item img {max-width: initial; max-height: initial; margin: initial;}',
167
+ 'author_clenup' => '.cat-post-author {margin-bottom: 0;}',
168
+ 'thumb' => '.cat-post-thumbnail {margin: 5px 10px 5px 0;}',
169
+ 'item_clenup' => '.cat-post-item:before {content: ""; clear: both;}',
170
+ );
171
+
172
+ if ( ! ( isset( $settings['disable_font_styles'] ) && $settings['disable_font_styles'] ) ) { // checks if disable font styles is not set.
173
+ // add general styles which apply to font styling.
174
+ $styles['title_font'] = '.cat-post-title {font-size: 15px;}';
175
+ $styles['current_title_font'] = '.cat-post-current .cat-post-title {font-weight: bold; text-transform: uppercase;}';
176
+ $styles['date_font'] = '.cat-post-date {font-size: 14px; line-height: 18px; font-style: italic; margin-bottom: 5px;}';
177
+ $styles['comment_num_font'] = '.cat-post-comment-num {font-size: 14px; line-height: 18px;}';
178
+ }
179
+
180
+ /*
181
+ * The twenty seventeen theme have a border between the LI elements of a widget,
182
+ * so remove our border if we detect its use to avoid conflicting styling.
183
+ */
184
+ if ( ! $is_shortcode && function_exists( 'twentyseventeen_setup' ) ) {
185
+ $styles['item_style'] = '.cat-post-item {list-style: none; list-style-type: none; margin: 0; padding: 3px 0;}';
186
+ } else {
187
+ $styles['item_style'] = '.cat-post-item {border-bottom: 1px solid #ccc; list-style: none; list-style-type: none; margin: 3px 0; padding: 3px 0;}';
188
+ $styles['last_item_style'] = '.cat-post-item:last-child {border-bottom: none;}';
189
+ }
190
+
191
+ // everything link related styling
192
+ // if we are dealing with "everything is a link" option, we need to add the clear:both to the a element, not the div.
193
+ if ( isset( $settings['everything_is_link'] ) && $settings['everything_is_link'] ) {
194
+ $styles['after_item'] = '.cat-post-item a:after {content: ""; display: table; clear: both;}';
195
+ } else {
196
+ $styles['after_item'] = '.cat-post-item:after {content: ""; display: table; clear: both;}';
197
+ }
198
+
199
+
200
+ if ( isset( $settings['template'] ) && preg_match( '/%excerpt%/', $settings['template'] ) ) {
201
+ if ( isset( $settings['excerpt_lines'] ) && $settings['excerpt_lines'] != 0 ) {
202
+ $styles['excerpt_lines'] = '.cat-post-item p {overflow: hidden;text-overflow: ellipsis;white-space: initial;'.
203
+ 'display: -webkit-box;-webkit-line-clamp: '.$settings['excerpt_lines'].';-webkit-box-orient: vertical;}';
204
+ }
205
+ }
206
+
207
+ // add post format css if needed.
208
+ if ( isset( $settings['template'] ) && preg_match( '/%thumb%/', $settings['template'] ) ) {
209
+ if ( ! isset( $settings['show_post_format'] ) || ( ( 'none' !== $settings['show_post_format'] ) && ( 'nocss' !== $settings['show_post_format'] ) ) ) {
210
+ static $fonts_added = false;
211
+ if ( ! $fonts_added ) {
212
+ $fonturl = esc_url( plugins_url( 'icons/font', __FILE__ ) );
213
+ $ret['post_format_font'] = "@font-face {\n" .
214
+ "font-family: 'cat_post';\n" .
215
+ "src: url('$fonturl/cat_post.eot?58348147');\n" .
216
+ "src: url('$fonturl/cat_post.eot?58348147#iefix') format('embedded-opentype'),\n" .
217
+ " url('$fonturl/cat_post.woff2?58348147') format('woff2'),\n" .
218
+ " url('$fonturl/cat_post.woff?58348147') format('woff'),\n" .
219
+ " url('$fonturl/cat_post.ttf?58348147') format('truetype');\n" .
220
+ " font-weight: normal;\n" .
221
+ " font-style: normal;\n" .
222
+ "}\n";
223
+ }
224
+ $fonts_added = true;
225
+
226
+ $placement = '';
227
+ switch ( $settings['show_post_format'] ) {
228
+ case 'topleft':
229
+ $placement = 'top:10%; left:10%;';
230
+ break;
231
+ case 'bottomleft':
232
+ $placement = 'bottom:10%; left:10%;';
233
+ break;
234
+ case 'ceter':
235
+ $placement = 'top:calc(50% - 34px); left:calc(50% - 34px);';
236
+ break;
237
+ case 'topright':
238
+ $placement = 'top:10%; right:10%;';
239
+ break;
240
+ case 'bottomright':
241
+ $placement = 'bottom:10%; right:10%;';
242
+ break;
243
+ }
244
+ $styles['post_format_thumb'] = '.cat-post-thumbnail span {position:relative}';
245
+ $styles['post_format_icon_styling'] = '.cat-post-format:before {font-family: "cat_post"; position:absolute; color:#FFFFFF; font-size:64px; line-height: 1; ' . $placement . '}';
246
+
247
+ $styles['post_format_icon_aside'] = ".cat-post-format-aside:before { content: '\\f0f6'; }";
248
+ $styles['post_format_icon_chat'] = ".cat-post-format-chat:before { content: '\\e802'; }";
249
+ $styles['post_format_icon_gallery'] = ".cat-post-format-gallery:before { content: '\\e805'; }";
250
+ $styles['post_format_icon_link'] = ".cat-post-format-link:before { content: '\\e809'; }";
251
+ $styles['post_format_icon_image'] = ".cat-post-format-image:before { content: '\\e800'; }";
252
+ $styles['post_format_icon_quote'] = ".cat-post-format-quote:before { content: '\\f10d'; }";
253
+ $styles['post_format_icon_status'] = ".cat-post-format-status:before { content: '\\e80a'; }";
254
+ $styles['post_format_icon_video'] = ".cat-post-format-video:before { content: '\\e801'; }";
255
+ $styles['post_format_icon_audio'] = ".cat-post-format-audio:before { content: '\\e803'; }";
256
+
257
+ }
258
+ }
259
+
260
+ foreach ( $styles as $key => $style ) {
261
+ $ret[ $key ] = '#' . $widget_id . ' ' . $style;
262
+ }
263
+
264
+ if ( $is_shortcode ) {
265
+ // Twenty Sixteen Theme adds underlines to links with box whadow wtf ...
266
+ $ret['twentysixteen_thumb'] = '#' . $widget_id . ' .cat-post-thumbnail {box-shadow:none}'; // this for the thumb link.
267
+ if ( ! ( isset( $settings['disable_font_styles'] ) && $settings['disable_font_styles'] ) ) { // checks if disable font styles is not set.
268
+ $ret['twentysixteen_tag_link'] = '#' . $widget_id . ' .cat-post-tax-tag a {box-shadow:none}'; // this for the tag link.
269
+ $ret['twentysixteen_tag_span'] = '#' . $widget_id . ' .cat-post-tax-tag span {box-shadow:none}'; // this for the tag link.
270
+ }
271
+ // Twenty Fifteen Theme adds border ...
272
+ $ret['twentyfifteen_thumb'] = '#' . $widget_id . ' .cat-post-thumbnail {border:0}'; // this for the thumb link.
273
+ if ( ! ( isset( $settings['disable_font_styles'] ) && $settings['disable_font_styles'] ) ) { // checks if disable font styles is not set.
274
+ $ret['twentysixteen_tag_link'] = '#' . $widget_id . ' .cat-post-tax-tag a {border:0}'; // this for the tag link.
275
+ $ret['twentysixteen_tag_span'] = '#' . $widget_id . ' .cat-post-tax-tag span {border:0}'; // this for the tag link.
276
+ }
277
+ }
278
+
279
+ // probably all Themes have too much margin on their p element when used in the shortcode or widget.
280
+ $ret['p_styling'] = '#' . $widget_id . ' p {margin:5px 0 0 0}'; // since on bottom it will make the spacing on cover
281
+ // bigger (add to the padding) use only top for now.
282
+ $ret['div_styling'] = '#' . $widget_id . ' li > div {margin:5px 0 0 0; clear:both;}'; // Add margin between the rows.
283
+
284
+ // use WP dashicons in the template (e.g. for premade Template 'All and icons').
285
+ $ret['dashicons'] = '#' . $widget_id . ' .dashicons {vertical-align:middle;}';
286
+ }
287
+
288
+ // Regardless if css is disabled we need some styling for the thumbnail
289
+ // to make sure cropping is properly done, and they fit the allocated space.
290
+ if ( isset( $settings['template'] ) && preg_match( '/%thumb%/', $settings['template'], $m, PREG_OFFSET_CAPTURE ) ) {
291
+ $wrap = isset( $settings['text_do_not_wrap_thumb'] ) && $settings['text_do_not_wrap_thumb'];
292
+ if ( isset( $settings['use_css_cropping'] ) && $settings['use_css_cropping'] ) {
293
+ if ( isset( $settings['thumb_w'] ) && $settings['thumb_w'] != 0 ) {
294
+ $ret['thumb_empty_w'] = '#' . $widget_id .' .cat-post-thumbnail .cat-post-crop img {width: '.$settings['thumb_w'].'px;}';
295
+ }
296
+ if ( isset( $settings['thumb_h'] ) && $settings['thumb_h'] != 0 ) {
297
+ $ret['thumb_crop_h'] = '#' . $widget_id . ' .cat-post-thumbnail .cat-post-crop img {height: '.$settings['thumb_h'].'px;}';
298
+ }
299
+ $ret['thumb_crop'] = '#' . $widget_id . ' .cat-post-thumbnail .cat-post-crop img {object-fit: cover;max-width:100%;}';
300
+
301
+ $ret['thumb_crop_not_supported'] = '#' . $widget_id .' .cat-post-thumbnail .cat-post-crop-not-supported img {width:100%;}';
302
+
303
+ if ( ! $wrap ) {
304
+ $ret['thumb_fluid_width'] = '#' . $widget_id . ' .cat-post-thumbnail {max-width:' . $settings['thumb_fluid_width'] . '%;}';
305
+ } else {
306
+ $ret['thumb_fluid_width'] = '#' . $widget_id . ' .cat-post-thumbnail {flex-basis:' . $settings['thumb_fluid_width'] . '%;}';
307
+ }
308
+ } else {
309
+ $ret['thumb_overflow'] = '#' . $widget_id . ' .cat-post-thumbnail span {overflow: hidden; display:inline-block}';
310
+ }
311
+ $ret['thumb_styling'] = '#' . $widget_id . ' .cat-post-item img {margin: initial;}';
312
+
313
+ // Thumbnail related positioning rules.
314
+ if ( $wrap ) {
315
+ $ret['thumb_flex'] = '#' . $widget_id . ' .cat-post-do-not-wrap-thumbnail {display:flex;}'; // Thumbnail container should flex.
316
+ $ret['thumb_flex_length'] = '#' . $widget_id . ' .cat-post-do-not-wrap-thumbnail > div {-webkit-flex: 1; -ms-flex: 1; flex: 1;}'; // Thumbnail container should flex.
317
+ }
318
+ $ret['text_do_not_wrap_thumb'] = '#' . $widget_id . ' .cat-post-thumbnail {float:left;}';
319
+ }
320
+
321
+ // Some hover effect require css to work, add it even if CSS is disabled.
322
+ if ( isset( $settings['thumb_hover'] ) ) {
323
+ switch ( $settings['thumb_hover'] ) {
324
+ case 'white':
325
+ $ret['white_hover_background'] = '#' . $widget_id . ' .cat-post-white span {background-color: white;}';
326
+ $ret['white_hover_thumb'] = '#' . $widget_id . ' .cat-post-white img {padding-bottom: 0 !important; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
327
+ $ret['white_hover_transform'] = '#' . $widget_id . ' .cat-post-white:hover img {opacity: 0.8;}';
328
+ break;
329
+ case 'dark':
330
+ $ret['dark_hover_thumb'] = '#' . $widget_id . ' .cat-post-dark img {padding-bottom: 0 !important; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
331
+ $ret['dark_hover_transform'] = '#' . $widget_id . ' .cat-post-dark:hover img {-webkit-filter: brightness(75%); -moz-filter: brightness(75%); -ms-filter: brightness(75%); -o-filter: brightness(75%); filter: brightness(75%);}';
332
+ break;
333
+ case 'scale':
334
+ $ret['scale_hover_thumb'] = '#' . $widget_id . ' .cat-post-scale img {margin: initial; padding-bottom: 0 !important; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
335
+ $ret['scale_hover_transform'] = '#' . $widget_id . ' .cat-post-scale:hover img {-webkit-transform: scale(1.1, 1.1); -ms-transform: scale(1.1, 1.1); transform: scale(1.1, 1.1);}';
336
+ break;
337
+ case 'blur':
338
+ $ret['blur_hover_thumb'] = '#' . $widget_id . ' .cat-post-blur img {padding-bottom: 0 !important; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
339
+ $ret['blur_hover_transform'] = '#' . $widget_id . ' .cat-post-blur:hover img {-webkit-filter: blur(2px); -moz-filter: blur(2px); -o-filter: blur(2px); -ms-filter: blur(2px); filter: blur(2px);}';
340
+ break;
341
+ case 'icon':
342
+ $fonturl = esc_url( plugins_url( 'icons/font', __FILE__ ) );
343
+ $ret['icon_hover_font'] = "@font-face {\n" .
344
+ "font-family: 'cat_post';\n" .
345
+ "src: url('$fonturl/cat_post.eot?58348147');\n" .
346
+ "src: url('$fonturl/cat_post.eot?58348147#iefix') format('embedded-opentype'),\n" .
347
+ " url('$fonturl/cat_post.woff2?58348147') format('woff2'),\n" .
348
+ " url('$fonturl/cat_post.woff?58348147') format('woff'),\n" .
349
+ " url('$fonturl/cat_post.ttf?58348147') format('truetype');\n" .
350
+ " font-weight: normal;\n" .
351
+ " font-style: normal;\n" .
352
+ "}\n";
353
+
354
+ $ret['icon_hover_thumb'] = '#' . $widget_id . ' .cat-post-format-standard:before {opacity:0; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -ms-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease;}';
355
+ $ret['icon_hover_transform'] = '#' . $widget_id . ' .cat-post-thumbnail:hover .cat-post-format-standard:before {opacity:1;}';
356
+ if ( isset( $settings['show_post_format'] ) && ( 'none' === $settings['show_post_format'] ) ) {
357
+ $ret[] = '#' . $widget_id . ' .cat-post-thumbnail {position:relative}';
358
+ $ret[] = '#' . $widget_id . ' .cat-post-icon .cat-post-format:before {font-family: "cat_post"; position:absolute; color:#FFFFFF; font-size:64px; line-height: 1; ' .
359
+ 'top:calc(50% - 34px); left:calc(50% - 34px);}';
360
+ }
361
+ $ret[] = '#' . $widget_id . " .cat-post-format-standard:before {padding-left:12px; content: '\\e806'; }";
362
+ break;
363
+ }
364
+
365
+ if ( $settings['enable_loadmore'] ) {
366
+ // $this->id is ued over $widget_id because we need the id of the outer div, not the UL itself.
367
+ $ret['loadmore'] = '#' . $this->id . ' .' . __NAMESPACE__ . '-loadmore {text-align:center;margin-top:10px}';
368
+ }
369
+ }
370
+ $rules[] = $ret;
371
+ }
372
+
373
+ /**
374
+ * Output the widget CSS
375
+ *
376
+ * Just a wrapper that output getCSSRules
377
+ *
378
+ * @param bool $is_shortcode Indicates if we are in the context os a shortcode.
379
+ *
380
+ * @since 4.7
381
+ */
382
+ public function outputCSS( $is_shortcode ) {
383
+ $rules = array();
384
+ getCSSRules( $is_shortcode, $rules );
385
+ foreach ( $rules as $rule ) {
386
+ echo "$rule\n"; // Xss off - raw css can not be html escaped.
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Get the id the virtual widget was registered with
392
+ *
393
+ * @return string
394
+ *
395
+ * @since 4.7
396
+ */
397
+ public function id() {
398
+ return $this->id;
399
+ }
400
+
401
+ /**
402
+ * Get all the setting of the virtual widgets in an array
403
+ *
404
+ * @return array
405
+ *
406
+ * @since 4.7
407
+ */
408
+ public static function getAllSettings() {
409
+ return self::$collection;
410
+ }
411
+
412
+ }
class-virtual-widgets-repository.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Implementation of virtual widget repository.
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
+ * Class that implement a simple repository for the virtual widgets representing
19
+ * actuall shortcode and widgets
20
+ *
21
+ * @since 4.7
22
+ */
23
+ class Virtual_Widgets_Repository {
24
+
25
+ /**
26
+ * Collection of objects representing shortcodes.
27
+ *
28
+ * @var array
29
+ *
30
+ * @since 4.7
31
+ */
32
+ private static $shortcodeCollection = array();
33
+
34
+ /**
35
+ * Collection of objects representing widgets.
36
+ *
37
+ * @var array
38
+ *
39
+ * @since 4.7
40
+ */
41
+ private static $widgetCollection = array();
42
+
43
+ /**
44
+ * Add a virtual widget representing a shortcode to the repository
45
+ *
46
+ * @param string $index A name to identify the specific shortcode.
47
+ * @param Virtual_Widget $widget The virtual widget for it.
48
+ *
49
+ * @since 4.7
50
+ */
51
+ public function addShortcode( $index, $widget ) {
52
+ self::$shortcodeCollection[ $index ] = $widget;
53
+ }
54
+
55
+ /**
56
+ * Get all the virtual widgets representing actual shortcodes
57
+ *
58
+ * @return array
59
+ *
60
+ * @since 4.7
61
+ */
62
+ public function getShortcodes() {
63
+ return self::$shortcodeCollection;
64
+ }
65
+
66
+ /**
67
+ * Add a virtual widget representing awidget to the repository
68
+ *
69
+ * @param string $index A name to identify the specific widget.
70
+ * @param Virtual_Widget $widget The virstual widget for it.
71
+ *
72
+ * @since 4.7
73
+ */
74
+ public function addWidget( $index, $widget ) {
75
+ self::$widgetCollection[ $index ] = $widget;
76
+ }
77
+
78
+ /**
79
+ * Get all the virtual widgets representing actual widgets
80
+ *
81
+ * @return array
82
+ *
83
+ * @since 4.7
84
+ */
85
+ public function getWidgets() {
86
+ return self::$widgetCollection;
87
+ }
88
+
89
+ }
class-widget.php ADDED
@@ -0,0 +1,1685 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 = '<figure class="cat-post-crop">' . $html . '</figure>';
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'] ) && ( 0 !== $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
+ $instance['offset'] = $start;
964
+ $number = (int) $number; // sanitize number with the side effect of non
965
+ // numbers are converted to zero.
966
+ if ( 0 < $number ) {
967
+ $instance['num'] = $number;
968
+ }
969
+ $args = $this->queryArgs( $instance );
970
+ $cat_posts = new \WP_Query( $args );
971
+ $current_post_id = null;
972
+ if ( '' !== $singular_id ) {
973
+ $current_post_id = (int) $singular_id;
974
+ }
975
+
976
+ while ( $cat_posts->have_posts() ) {
977
+ $cat_posts->the_post();
978
+ $ret[] = $this->itemHTML( $instance, $current_post_id );
979
+ }
980
+
981
+ wp_reset_postdata();
982
+
983
+ return $ret;
984
+ }
985
+
986
+ /**
987
+ * Update the options.
988
+ *
989
+ * @param array $new_instance The new settings of the widget.
990
+ * @param array $old_instance The current settings of the widget.
991
+ * @return array
992
+ */
993
+ public function update( $new_instance, $old_instance ) {
994
+
995
+ $new_instance['title'] = sanitize_text_field( $new_instance['title'] ); // sanitize the title like core widgets do.
996
+ if ( ! isset( $new_instance['excerpt_filters'] ) ) {
997
+ $new_instance['excerpt_filters'] = '';
998
+ }
999
+ if ( current_user_can( 'unfiltered_html' ) ) {
1000
+ $instance['text'] = $new_instance['template'];
1001
+ } else {
1002
+ $instance['text'] = wp_kses_post( $new_instance['template'] );
1003
+ }
1004
+
1005
+ // Set the version of the DB structure.
1006
+ $new_instance['ver'] = VERSION;
1007
+ return $new_instance;
1008
+ }
1009
+
1010
+ /**
1011
+ * Output the title panel of the widget configuration form.
1012
+ *
1013
+ * @param array $instance The widget's settings.
1014
+ * @return void
1015
+ *
1016
+ * @since 4.6
1017
+ */
1018
+ public function formTitlePanel( $instance ) {
1019
+ $cat = (int) $instance['cat'];
1020
+
1021
+ $hide_title = false;
1022
+ if ( isset( $instance['hide_title'] ) && $instance['hide_title'] ) {
1023
+ $hide_title = true;
1024
+ }
1025
+ ?>
1026
+ <h4 data-panel="title"><?php esc_html_e( 'Title', 'category-posts' ); ?></h4>
1027
+ <div class="cpwp_ident">
1028
+ <?php echo $this->get_checkbox_block_html( $instance, 'hide_title', esc_html__( 'Hide title', 'category-posts' ), true ); ?>
1029
+ <div class="categoryposts-data-panel-title-settings" <?php echo ( $hide_title ) ? 'style="display:none"' : ''; ?>>
1030
+ <?php echo $this->get_text_input_block_html( $instance, 'title', esc_html__( 'Title', 'category-posts' ), '', true ); ?>
1031
+ <?php echo $this->get_checkbox_block_html( $instance, 'title_link', esc_html__( 'Make widget title link', 'category-posts' ), 0 !== $cat ); ?>
1032
+ <?php echo $this->get_text_input_block_html( $instance, 'title_link_url', esc_html__( 'Title link URL', 'category-posts' ), '', 0 === $cat ); ?>
1033
+ </div>
1034
+ </div>
1035
+ <?php
1036
+ }
1037
+
1038
+ /**
1039
+ * Output the filter panel of the widget configuration form.
1040
+ *
1041
+ * @param array $instance The parameters configured for the widget.
1042
+ * @return void
1043
+ *
1044
+ * @since 4.6
1045
+ */
1046
+ public function formFilterPanel( $instance ) {
1047
+ $cat = $instance['cat'];
1048
+ ?>
1049
+ <h4 data-panel="filter"><?php esc_html_e( 'Filter', 'category-posts' ); ?></h4>
1050
+ <div>
1051
+ <p>
1052
+ <label>
1053
+ <?php esc_html_e( 'Category', 'category-posts' ); ?>:
1054
+ <?php
1055
+ wp_dropdown_categories( array(
1056
+ 'show_option_all' => __( 'All categories', 'category-posts' ),
1057
+ 'hide_empty' => 0,
1058
+ 'name' => $this->get_field_name( 'cat' ),
1059
+ 'selected' => $instance['cat'],
1060
+ 'class' => 'categoryposts-data-panel-filter-cat',
1061
+ ) );
1062
+ ?>
1063
+ </label>
1064
+ </p>
1065
+ <?php
1066
+ echo $this->get_checkbox_block_html( $instance, 'no_cat_childs', esc_html__( 'Exclude child categories', 'category-posts' ), ! empty( $instance['cat'] ) );
1067
+ echo $this->get_select_block_html( $instance, 'status', esc_html__( 'Status', 'category-posts' ), array(
1068
+ 'default' => esc_html__( 'WordPress Default', 'category-posts' ),
1069
+ 'publish' => esc_html__( 'Published', 'category-posts' ),
1070
+ 'future' => esc_html__( 'Scheduled', 'category-posts' ),
1071
+ 'private' => esc_html__( 'Private', 'category-posts' ),
1072
+ 'publish,future' => esc_html__( 'Published or Scheduled', 'category-posts' ),
1073
+ 'private,publish' => esc_html__( 'Published or Private', 'category-posts' ),
1074
+ 'private,future' => esc_html__( 'Private or Scheduled', 'category-posts' ),
1075
+ 'private,publish,future' => esc_html__( 'Published, Private or Scheduled', 'category-posts' ),
1076
+ ), 'default', true );
1077
+ echo $this->get_number_input_block_html( $instance, 'num', esc_html__( 'Number of posts to show', 'category-posts' ), 1, '', '', true );
1078
+ echo $this->get_number_input_block_html( $instance, 'offset', esc_html__( 'Start with post', 'category-posts' ), 1, '', '', true );
1079
+ echo $this->get_select_block_html( $instance, 'date_range', esc_html__( 'Date Range', 'category-posts' ), array(
1080
+ 'off' => esc_html__( 'Off', 'category-posts' ),
1081
+ 'days_ago' => esc_html__( 'Days ago', 'category-posts' ),
1082
+ 'between_dates' => esc_html__( 'Between dates', 'category-posts' ),
1083
+ ), 'off', true );
1084
+ ?>
1085
+ <div class="cpwp_ident categoryPosts-date-range" style="display:<?php echo 'off' === $instance['date_range'] ? 'none' : 'block'; ?>">
1086
+ <?php
1087
+ echo $this->get_number_input_block_html( $instance, 'days_ago', esc_html__( 'Up to', 'category-posts' ), 1, '', '', 'days_ago' === $instance['date_range'] );
1088
+ echo $this->get_date_input_block_html( $instance, 'start_date', esc_html__( 'After', 'category-posts' ), 'between_dates' === $instance['date_range'] );
1089
+ echo $this->get_date_input_block_html( $instance, 'end_date', esc_html__( 'Before', 'category-posts' ), 'between_dates' === $instance['date_range'] );
1090
+ ?>
1091
+ </div>
1092
+ <?php
1093
+ echo $this->get_select_block_html( $instance, 'sort_by', esc_html__( 'Sort by', 'category-posts' ), array(
1094
+ 'date' => esc_html__( 'Date', 'category-posts' ),
1095
+ 'title' => esc_html__( 'Title', 'category-posts' ),
1096
+ 'comment_count' => esc_html__( 'Number of comments', 'category-posts' ),
1097
+ 'rand' => esc_html__( 'Random', 'category-posts' ),
1098
+ ), 'date', true );
1099
+ echo $this->get_checkbox_block_html( $instance, 'asc_sort_order', esc_html__( 'Reverse sort order (ascending)', 'category-posts' ), true );
1100
+ echo $this->get_checkbox_block_html( $instance, 'exclude_current_post', esc_html__( 'Exclude current post', 'category-posts' ), true );
1101
+ echo $this->get_checkbox_block_html( $instance, 'hideNoThumb', esc_html__( 'Exclude posts which have no thumbnail', 'category-posts' ), true );
1102
+ ?>
1103
+ </div>
1104
+ <?php
1105
+ }
1106
+
1107
+ /**
1108
+ * Generate the wrapper P around a form input element
1109
+ *
1110
+ * @since 4.8
1111
+ * @param string $html The HTML to wrap.
1112
+ * @param string $key The key to use as the prefix to the class.
1113
+ * @param bool $visible Indicates if the element should be visible when rendered.
1114
+ *
1115
+ * @return string HTML with P element contaning the html being passed with class based on the key
1116
+ * and style set to display:none if visibility is off.
1117
+ */
1118
+ private function get_wrap_block_html( $html, $key, $visible ) {
1119
+
1120
+ $cl = ' class="' . __NAMESPACE__ . '-' . esc_attr( $key ) . '"';
1121
+
1122
+ $style = '';
1123
+ if ( ! $visible ) {
1124
+ $style = ' style="display:none"';
1125
+ }
1126
+ $ret = '<p' . $cl . $style . ">\n" . $html . "</p>\n";
1127
+
1128
+ return $ret;
1129
+ }
1130
+
1131
+ /**
1132
+ * Generate a form P element containing a select element
1133
+ *
1134
+ * @since 4.8
1135
+ * @param array $instance The instance.
1136
+ * @param string $key The key in the instance array.
1137
+ * @param string $label The label to display and associate with the input.
1138
+ * @param array $list An array of pairs value (index) => label to be used for the options.
1139
+ * The labels are expected to be html escaped.
1140
+ * @param int $default The value to use if the key is not set in the instance.
1141
+ * @param bool $visible Indicates if the element should be visible when rendered.
1142
+ *
1143
+ * @return string HTML a P element contaning the select, its label, class based on the key
1144
+ * and style set to display:none if visibility is off.
1145
+ */
1146
+ private function get_select_block_html( $instance, $key, $label, $list, $default, $visible ) {
1147
+ $value = $instance[ $key ];
1148
+
1149
+ if ( ! array_key_exists( $value, $list ) ) {
1150
+ $value = $default;
1151
+ }
1152
+
1153
+ $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1154
+ $label .
1155
+ "</label>\n" .
1156
+ '<select id="' . $this->get_field_id( $key ) . '" name="' . $this->get_field_name( $key ) . '" autocomplete="off">' . "\n";
1157
+ foreach ( $list as $v => $l ) {
1158
+ $ret .= '<option value="' . esc_attr( $v ) . '" ' . selected( $v, $value, false ) . '>' . $l . "</option>\n";
1159
+ }
1160
+ $ret .= "</select>\n";
1161
+
1162
+ return $this->get_wrap_block_html( $ret, $key, $visible );
1163
+ }
1164
+
1165
+ /**
1166
+ * Generate a form P element containing a textarea input
1167
+ *
1168
+ * @since 4.8
1169
+ * @param array $instance The instance.
1170
+ * @param string $key The key in the instance array.
1171
+ * @param string $label The label to display and associate with the input (should be html escaped).
1172
+ * @param string $placeholder The placeholder to use in the input (should be attribute escaped).
1173
+ * @param bool $visible Indicates if the element should be visible when rendered.
1174
+ * @param int $num_rows Number of rows.
1175
+ *
1176
+ * @return string HTML a P element containing the input, its label, class based on the key
1177
+ * and style set to display:none if visibility is off.
1178
+ */
1179
+ private function get_textarea_html( $instance, $key, $label, $placeholder, $visible, $num_rows ) {
1180
+
1181
+ $value = $instance[ $key ];
1182
+
1183
+ $ret = '<label for="' . esc_attr( $this->get_field_id( $key ) ) . '">' . $label .
1184
+ '<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>' .
1185
+ '</label>';
1186
+
1187
+ return $this->get_wrap_block_html( $ret, $key, $visible );
1188
+ }
1189
+
1190
+ /**
1191
+ * Generate a form P element containing a text input
1192
+ *
1193
+ * @since 4.8
1194
+ * @param array $instance The instance.
1195
+ * @param string $key The key in the instance array.
1196
+ * @param string $label The label to display and associate with the input.
1197
+ * Should be html escaped.
1198
+ * @param string $placeholder The placeholder to use in the input. should be attribute escaped.
1199
+ * @param bool $visible Indicates if the element should be visible when rendered.
1200
+ *
1201
+ * @return string HTML a P element contaning the input, its label, class based on the key
1202
+ * and style set to display:none if visibility is off.
1203
+ */
1204
+ private function get_text_input_block_html( $instance, $key, $label, $placeholder, $visible ) {
1205
+
1206
+ $value = $instance[ $key ];
1207
+
1208
+ $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1209
+ $label . "\n" .
1210
+ '<input placeholder="' . $placeholder . '" id="' . $this->get_field_id( $key ) . '" name="' . $this->get_field_name( $key ) . '" type="text" value="' . esc_attr( $value ) . '" autocomplete="off"/>' . "\n" .
1211
+ "</label>\n";
1212
+
1213
+ return $this->get_wrap_block_html( $ret, $key, $visible );
1214
+ }
1215
+
1216
+ /**
1217
+ * Generate a form P element containing a range input
1218
+ *
1219
+ * @since 4.8
1220
+ * @param array $instance The instance.
1221
+ * @param string $key The key in the instance array.
1222
+ * @param string $label The label to display and associate with the input.
1223
+ * expected to be escaped.
1224
+ * @param int $min The minimum value allowed to be input.
1225
+ * @param int $max The maximum value allowed to be input.
1226
+ * @param string $value The start value
1227
+ * @param string $step The range of each step
1228
+ * @param bool $visible Indicates if the element should be visible when rendered.
1229
+ *
1230
+ * @return string HTML a P element contaning the input, its label, class based on the key
1231
+ * and style set to display:none if visibility is off.
1232
+ */
1233
+ private function get_range_input_block_html( $instance, $key, $label, $min, $max, $value, $step, $visible ) {
1234
+
1235
+ $value = $instance[ $key ];
1236
+
1237
+ $minMaxStep = '';
1238
+ if ( '' !== $min ) {
1239
+ $minMaxStep .= ' min="' . $min . '"';
1240
+ }
1241
+ if ( '' !== $max ) {
1242
+ $minMaxStep .= ' max="' . $max . '"';
1243
+ }
1244
+ if ( '' !== $step ) {
1245
+ $minMaxStep .= ' step="' . $step . '"';
1246
+ }
1247
+
1248
+ $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1249
+ esc_html( $label ) . " <span>" . $value . "%</span>\n" .
1250
+ '<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" .
1251
+ "</label>\n";
1252
+
1253
+ return $this->get_wrap_block_html( $ret, $key, $visible );
1254
+ }
1255
+
1256
+ /**
1257
+ * Generate a form P element containing a number input
1258
+ *
1259
+ * @since 4.8
1260
+ * @param array $instance The instance.
1261
+ * @param string $key The key in the instance array.
1262
+ * @param string $label The label to display and associate with the input.
1263
+ * expected to be escaped.
1264
+ * @param int $min The minimum value allowed to be input.
1265
+ * @param int $max The maximum value allowed to be input.
1266
+ * @param string $placeholder The placeholder string to be used. expected to be escaped.
1267
+ * @param bool $visible Indicates if the element should be visible when rendered.
1268
+ *
1269
+ * @return string HTML a P element contaning the input, its label, class based on the key
1270
+ * and style set to display:none if visibility is off.
1271
+ */
1272
+ private function get_number_input_block_html( $instance, $key, $label, $min, $max, $placeholder, $visible ) {
1273
+
1274
+ $value = $instance[ $key ];
1275
+
1276
+ $minmax = '';
1277
+ if ( '' !== $min ) {
1278
+ $minmax .= ' min="' . $min . '"';
1279
+ }
1280
+ if ( '' !== $max ) {
1281
+ $minmax .= ' max="' . $max . '"';
1282
+ }
1283
+
1284
+ $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1285
+ esc_html( $label ) . "\n" .
1286
+ '<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" .
1287
+ "</label>\n";
1288
+
1289
+ return $this->get_wrap_block_html( $ret, $key, $visible );
1290
+ }
1291
+
1292
+ /**
1293
+ * Generate a form P element containing a date input
1294
+ *
1295
+ * @since 4.9
1296
+ * @param array $instance The instance.
1297
+ * @param string $key The key in the instance array.
1298
+ * @param string $label The label to display and associate with the input.
1299
+ * expected to be escaped.
1300
+ * @param bool $visible Indicates if the element should be visible when rendered.
1301
+ *
1302
+ * @return string HTML a P element containing the input, its label, class based on the key
1303
+ * and style set to display:none if visibility is off.
1304
+ */
1305
+ private function get_date_input_block_html( $instance, $key, $label, $visible ) {
1306
+
1307
+ $value = $instance[ $key ];
1308
+
1309
+ $ret = '<label for="' . $this->get_field_id( $key ) . "\">\n" .
1310
+ esc_html( $label ) . "\n" .
1311
+ '<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" .
1312
+ "</label>\n";
1313
+
1314
+ return $this->get_wrap_block_html( $ret, $key, $visible );
1315
+ }
1316
+
1317
+ /**
1318
+ * Generate a form P element containing a checkbox input
1319
+ *
1320
+ * @since 4.8
1321
+ * @param array $instance The instance.
1322
+ * @param string $key The key in the instance array.
1323
+ * @param string $label The label to display and associate with the checkbox.
1324
+ * should be escaped string.
1325
+ * @param bool $visible Indicates if the element should be visible when rendered.
1326
+ *
1327
+ * @return string HTML a P element contaning the checkbox, its label, class based on the key
1328
+ * and style set to display:none if visibility is off.
1329
+ */
1330
+ private function get_checkbox_block_html( $instance, $key, $label, $visible ) {
1331
+
1332
+ if ( array_key_exists( $key, $instance ) ) {
1333
+ if ( $instance[ $key ] ) {
1334
+ $value = true;
1335
+ } else {
1336
+ $value = false;
1337
+ }
1338
+ }
1339
+ $ret = '<label class="checkbox" for="' . esc_attr( $this->get_field_id( $key ) ) . "\">\n" .
1340
+ '<input id="' . esc_attr( $this->get_field_id( $key ) ) . '" name="' . esc_attr( $this->get_field_name( $key ) ) . '" type="checkbox" ' . checked( $value, true, false ) . '/>' . "\n" .
1341
+ $label .
1342
+ "</label>\n";
1343
+
1344
+ return $this->get_wrap_block_html( $ret, $key, $visible );
1345
+ }
1346
+
1347
+ /**
1348
+ * Generate a form button element containing
1349
+ *
1350
+ * @since 4.9
1351
+ * @param array $instance The instance.
1352
+ * @param string $key The key in the instance array.
1353
+ * @param string $label The label to display and associate with the button.
1354
+ * should be escaped string.
1355
+ *
1356
+ * @return string HTML a button element and class based on the key.
1357
+ */
1358
+ private function get_button_thumb_size_html( $instance, $key, $label ) {
1359
+
1360
+ $datas = "";
1361
+
1362
+ switch ( $key ) {
1363
+ case "thumb":
1364
+ $datas = 'data-thumb-w="' . get_option( "thumbnail_size_w" ) . '" data-thumb-h="' . get_option( "thumbnail_size_h" ) . '"';
1365
+ break;
1366
+ case "medium":
1367
+ $datas = 'data-thumb-w="' . get_option( "medium_size_w" ) . '" data-thumb-h="' . get_option( "medium_size_h" ) . '"';
1368
+ break;
1369
+ case "large":
1370
+ $datas = 'data-thumb-w="' . get_option( "large_size_w" ) . '" data-thumb-h="' . get_option( "large_size_h" ) . '"';
1371
+ break;
1372
+ }
1373
+ $ret = '<button type="button" ' . $datas . ' class="' . $key . ' button">' . esc_html( $label ) . "</button>\n";
1374
+
1375
+ return $ret;
1376
+ }
1377
+
1378
+ /**
1379
+ * The widget configuration form back end.
1380
+ *
1381
+ * @param array $instance The parameters associated with the widget.
1382
+ * @return void
1383
+ */
1384
+ public function form( $instance ) {
1385
+ if ( 0 === count( $instance ) ) { // new widget, use defaults.
1386
+ $instance = default_settings();
1387
+ } else { // updated widgets come from =< 4.6 excerpt filter is on.
1388
+ if ( ! isset( $instance['excerpt_filters'] ) ) {
1389
+ $instance['excerpt_filters'] = 'on';
1390
+ }
1391
+ }
1392
+
1393
+ $instance = upgrade_settings( $instance );
1394
+
1395
+ $hide_post_titles = $instance['hide_post_titles'];
1396
+ $excerpt_more_text = $instance['excerpt_more_text'];
1397
+ $excerpt_filters = $instance['excerpt_filters'];
1398
+ $date_format = $instance['date_format'];
1399
+ $disable_css = $instance['disable_css'];
1400
+ $disable_font_styles = $instance['disable_font_styles'];
1401
+ $preset_date_format = $instance['preset_date_format'];
1402
+ $thumb = ! empty( $instance['thumb'] );
1403
+ $thumb_w = $instance['thumb_w'];
1404
+ $thumb_fluid_width = $instance['thumb_fluid_width'];
1405
+ $thumb_h = $instance['thumb_h'];
1406
+ $default_thunmbnail = $instance['default_thunmbnail'];
1407
+ $use_css_cropping = $instance['use_css_cropping'];
1408
+ $text_do_not_wrap_thumb = $instance['text_do_not_wrap_thumb'];
1409
+ ?>
1410
+
1411
+ <div class="category-widget-cont">
1412
+ <?php if ( ! class_exists( '\\termcategoryPostsPro\\Widget' ) ) { ?>
1413
+ <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>
1414
+ <?php
1415
+ }
1416
+ $this->formTitlePanel( $instance );
1417
+ $this->formFilterPanel( $instance );
1418
+ ?>
1419
+ <h4 data-panel="details"><?php esc_html_e( 'Post details', 'category-posts' ); ?></h4>
1420
+ <div class="cpwp-sub-panel">
1421
+ <?php
1422
+ $template = '';
1423
+ if ( ! isset( $instance['template'] ) ) {
1424
+ $template = convert_settings_to_template( $instance );
1425
+ } else {
1426
+ $template = $instance['template'];
1427
+ }
1428
+ ?>
1429
+ <p><?php esc_html_e( 'Displayed parts', 'category-posts' ); ?></p>
1430
+ <div class="cpwp_ident">
1431
+ <?php
1432
+ $label = esc_html__( 'Template', 'category-posts' ) .
1433
+ ' <a href="#" class="dashicons toggle-template-help dashicons-editor-help imgedit-help-toggle"><span class="screen-reader-text">' .
1434
+ esc_html__( 'Show template help', 'category-posts' ) . '</span></a>';
1435
+ $class_placement = "";
1436
+ if ( is_customize_preview() ) {
1437
+ $class_placement = "customizer";
1438
+ } else {
1439
+ $class_placement = "admin-panel";
1440
+ }
1441
+ $label .= '<span class="cat-post-add_premade_templates ' . $class_placement . '">' .
1442
+ '<button type="button" class="button cpwp-open-placholder-dropdown-menu"> + ' . esc_html__( 'Add Placeholder', 'category-posts' ) . '</button>' .
1443
+ '<span class="cpwp-placeholder-dropdown-menu">' .
1444
+ '<span data-value="NewLine">' . esc_html__( 'New line', 'category-posts' ) . '</span>' .
1445
+ '<span data-value="EmptyLine">' . esc_html__( 'Empty line', 'category-posts' ) . '</span>' .
1446
+ '<span data-value="title">' . esc_html__( '%title%', 'category-posts' ) . '</span>' .
1447
+ '<span data-value="thumb">' . esc_html__( '%thumb%', 'category-posts' ) . '</span>' .
1448
+ '<span data-value="date">' . esc_html__( '%date%', 'category-posts' ) . '</span>' .
1449
+ '<span data-value="excerpt">' . esc_html__( '%excerpt%', 'category-posts' ) . '</span>' .
1450
+ '<span data-value="author">' . esc_html__( '%author%', 'category-posts' ) . '</span>' .
1451
+ '<span data-value="commentnum">' . esc_html__( '%commentnum%', 'category-posts' ) . '</span>' .
1452
+ '<span data-value="post_tag">' . esc_html__( '%post_tag%', 'category-posts' ) . '</span>' .
1453
+ '<span data-value="category">' . esc_html__( '%category%', 'category-posts' ) . '</span>' .
1454
+ '</span>' .
1455
+ '</span>';
1456
+ ?>
1457
+ <?php
1458
+ echo $this->get_textarea_html( $instance, 'template', $label , '', true, 8 );
1459
+ preg_match_all( get_template_regex(), $template, $matches );
1460
+ $tags = array();
1461
+ if ( ! empty( $matches[0] ) ) {
1462
+ $tags = array_flip( $matches[0] );
1463
+ }
1464
+ ?>
1465
+ <div class="cat-post-template-help">
1466
+ <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' ); ?>
1467
+ </p>
1468
+ <table>
1469
+ <tr>
1470
+ <th><?php esc_html_e( 'New line', 'category-posts' ); ?></th>
1471
+ <td><?php esc_html_e( 'Space', 'category-posts' ); ?></td>
1472
+ </tr>
1473
+ <tr>
1474
+ <th><?php esc_html_e( 'Empty line', 'category-posts' ); ?></th>
1475
+ <td><?php esc_html_e( 'Next line is a paragraph', 'category-posts' ); ?></td>
1476
+ </tr>
1477
+ <tr>
1478
+ <th>%title%</th>
1479
+ <td><?php esc_html_e( 'Post title', 'category-posts' ); ?></td>
1480
+ </tr>
1481
+ <tr>
1482
+ <th>%thumb%</th>
1483
+ <td><?php esc_html_e( 'Post thumbnail possibly wrapped by text', 'category-posts' ); ?></td>
1484
+ </tr>
1485
+ <tr>
1486
+ <th>%date%</th>
1487
+ <td><?php esc_html_e( 'Post publish date', 'category-posts' ); ?></td>
1488
+ </tr>
1489
+ <tr>
1490
+ <th>%excerpt%</th>
1491
+ <td><?php esc_html_e( 'Post excerpt', 'category-posts' ); ?></td>
1492
+ </tr>
1493
+ <tr>
1494
+ <th>%author%</th>
1495
+ <td><?php esc_html_e( 'Post author', 'category-posts' ); ?></td>
1496
+ </tr>
1497
+ <tr>
1498
+ <th>%commentnum%</th>
1499
+ <td><?php esc_html_e( 'The number of comments to the post', 'category-posts' ); ?></td>
1500
+ </tr>
1501
+ <tr>
1502
+ <th>%post_tag%</th>
1503
+ <td><?php esc_html_e( 'Post tags', 'category-posts' ); ?></td>
1504
+ </tr>
1505
+ <tr>
1506
+ <th>%category%</th>
1507
+ <td><?php esc_html_e( 'Post categories', 'category-posts' ); ?></td>
1508
+ </tr>
1509
+ </table>
1510
+ </div>
1511
+ <div class="cat-post-premade_templates">
1512
+ <p><label><?php esc_html_e( 'Select premade Template', 'category-posts' ); ?></label></p>
1513
+ <select>
1514
+ <option value="title"><?php esc_html_e( 'Only the title', 'category-posts' ); ?></option>
1515
+ <option value="title_excerpt"><?php esc_html_e( 'Title and Excerpt', 'category-posts' ); ?></option>
1516
+ <option value="title_thumb"><?php esc_html_e( 'Title and Thumbnail', 'category-posts' ); ?></option>
1517
+ <option value="title_thum_excerpt"><?php esc_html_e( 'Title, Thumbnail and Excerpt', 'category-posts' ); ?></option>
1518
+ <option value="everything"><?php esc_html_e( 'All with icons', 'category-posts' ); ?></option>
1519
+ </select>
1520
+ <p><button type="button" class="button"><?php esc_html_e( 'Select this template', 'category-posts' ); ?></button></p>
1521
+ </div>
1522
+ </div>
1523
+ <?php // Excerpt settings. ?>
1524
+ <div class="cpwp-sub-panel categoryposts-data-panel-excerpt" style="display:<?php echo ( isset( $tags['%excerpt%'] ) ) ? 'block' : 'none'; ?>">
1525
+ <p><?php esc_html_e( 'Excerpt settings', 'category-posts' ); ?></p>
1526
+ <div class="cpwp_ident">
1527
+ <?php
1528
+ echo $this->get_number_input_block_html( $instance, 'excerpt_lines', esc_html__( 'Lines (responsive):', 'category-posts' ), 0, '', '', true );
1529
+ echo $this->get_number_input_block_html( $instance, 'excerpt_length', esc_html__( 'Length (words):', 'category-posts' ), 0, '', '', true );
1530
+ echo $this->get_text_input_block_html( $instance, 'excerpt_more_text', esc_html__( '\'More ...\' text:', 'category-posts' ), esc_attr__( '...', 'category-posts' ), true );
1531
+ ?>
1532
+ </div>
1533
+ </div>
1534
+ <?php // Data settings ?>
1535
+ <div class="cpwp-sub-panel categoryposts-data-panel-date" style="display:<?php echo ( isset( $tags['%date%'] ) ) ? 'block' : 'none'; ?>">
1536
+ <p><?php esc_html_e( 'Date format settings', 'category-posts' ); ?></p>
1537
+ <div class="cpwp_ident">
1538
+ <?php
1539
+ echo $this->get_select_block_html( $instance, 'preset_date_format', esc_html__( 'Date format', 'category-posts' ), array(
1540
+ 'sitedateandtime' => esc_html__( 'Site date and time', 'category-posts' ),
1541
+ 'sitedate' => esc_html__( 'Site date', 'category-posts' ),
1542
+ 'sincepublished' => esc_html__( 'Time since published', 'category-posts' ),
1543
+ 'localsitedateandtime' => esc_html__( 'Reader\'s local date and time', 'category-posts' ),
1544
+ 'localsitedate' => esc_html__( 'Reader\'s local date', 'category-posts' ),
1545
+ 'other' => esc_html__( 'PHP style format', 'category-posts' ),
1546
+ ), 'sitedateandtime', true );
1547
+ 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 );
1548
+ ?>
1549
+ </div>
1550
+ </div>
1551
+ <?php // Thumbnail settings. ?>
1552
+ <div class="cpwp-sub-panel categoryposts-data-panel-thumb" style="display:<?php echo ( isset( $tags['%thumb%'] ) ) ? 'block' : 'none'; ?>">
1553
+ <p><?php esc_html_e( 'Thumbnail settings', 'category-posts' ); ?></p>
1554
+ <div class="cpwp_ident">
1555
+ <p><?php esc_html_e( 'Thumbnail dimensions (pixel)', 'category-posts' ); ?></p>
1556
+ <?php
1557
+ echo $this->get_number_input_block_html( $instance, 'thumb_w', esc_html__( 'Width:', 'category-posts' ), 1, '', '', true );
1558
+ echo $this->get_range_input_block_html( $instance, 'thumb_fluid_width', esc_html__( 'Max-width:', 'category-posts' ), 2, 100, 100, 2, true );
1559
+ echo $this->get_number_input_block_html( $instance, 'thumb_h', esc_html__( 'Height:', 'category-posts' ), 1, '', '', true );
1560
+ ?>
1561
+ <div class="cat-post-thumb-change-size">
1562
+ <p>
1563
+ <label><?php esc_html_e( 'Change size', 'category-posts' ); ?>: </label>
1564
+ <span class="cpwp-right">
1565
+ <?php
1566
+ echo $this->get_button_thumb_size_html( $instance, 'smaller', esc_html__( '-', 'category-posts' ) );
1567
+ echo $this->get_button_thumb_size_html( $instance, 'quarter', esc_html__( '1/4', 'category-posts' ) );
1568
+ echo $this->get_button_thumb_size_html( $instance, 'half', esc_html__( '1/2', 'category-posts' ) );
1569
+ echo $this->get_button_thumb_size_html( $instance, 'double', esc_html__( '2x', 'category-posts' ) );
1570
+ echo $this->get_button_thumb_size_html( $instance, 'bigger', esc_html__( '+', 'category-posts' ) );
1571
+ ?>
1572
+ </span>
1573
+ </p>
1574
+ <p>
1575
+ <label><?php esc_html_e( 'Ratio', 'category-posts' ); ?>: </label>
1576
+ <span class="cpwp-right">
1577
+ <?php
1578
+ echo $this->get_button_thumb_size_html( $instance, 'square', esc_html__( '1:1', 'category-posts' ) );
1579
+ echo $this->get_button_thumb_size_html( $instance, 'standard', esc_html__( '4:3', 'category-posts' ) );
1580
+ echo $this->get_button_thumb_size_html( $instance, 'wide', esc_html__( '16:9', 'category-posts' ) );
1581
+ echo $this->get_button_thumb_size_html( $instance, 'switch', esc_html__( 'switch', 'category-posts' ) );
1582
+ ?>
1583
+ </span>
1584
+ </p>
1585
+ <p>
1586
+ <label><?php esc_html_e( 'Available', 'category-posts' ); ?>: </label>
1587
+ <span class="cpwp-right">
1588
+ <?php
1589
+ echo $this->get_button_thumb_size_html( $instance, 'thumb', esc_html__( 'Thumb', 'category-posts' ) );
1590
+ echo $this->get_button_thumb_size_html( $instance, 'medium', esc_html__( 'Medium', 'category-posts' ) );
1591
+ echo $this->get_button_thumb_size_html( $instance, 'large', esc_html__( 'Large', 'category-posts' ) );
1592
+ ?>
1593
+ </span>
1594
+ </p>
1595
+ </div>
1596
+ <?php
1597
+ echo $this->get_checkbox_block_html( $instance, 'text_do_not_wrap_thumb', esc_html__( 'Do not wrap thumbnail with overflowing text', 'category-posts' ), true );
1598
+ echo $this->get_checkbox_block_html( $instance, 'use_css_cropping', esc_html__( 'CSS crop to requested size', 'category-posts' ), false );
1599
+ echo $this->get_select_block_html( $instance, 'thumb_hover', esc_html__( 'Animation on mouse hover:', 'category-posts' ), array(
1600
+ 'none' => esc_html__( 'None', 'category-posts' ),
1601
+ 'dark' => esc_html__( 'Darker', 'category-posts' ),
1602
+ 'white' => esc_html__( 'Brighter', 'category-posts' ),
1603
+ 'scale' => esc_html__( 'Zoom in', 'category-posts' ),
1604
+ 'blur' => esc_html__( 'Blur', 'category-posts' ),
1605
+ 'icon' => esc_html__( 'Icon', 'category-posts' ),
1606
+ ), 'none', true);
1607
+ echo $this->get_select_block_html( $instance, 'show_post_format', esc_html__( 'Indicate post format and position', 'category-posts' ), array(
1608
+ 'none' => esc_html__( 'None', 'category-posts' ),
1609
+ 'topleft' => esc_html__( 'Top left', 'category-posts' ),
1610
+ 'bottomleft' => esc_html__( 'Bottom left', 'category-posts' ),
1611
+ 'ceter' => esc_html__( 'Center', 'category-posts' ),
1612
+ 'topright' => esc_html__( 'Top right', 'category-posts' ),
1613
+ 'bottomright' => esc_html__( 'Bottom right', 'category-posts' ),
1614
+ 'nocss' => esc_html__( 'HTML without styling', 'category-posts' ),
1615
+ ), 'none', true );
1616
+ ?>
1617
+ <p>
1618
+ <label style="display:block">
1619
+ <?php esc_html_e( 'Default thumbnail: ', 'category-posts' ); ?>
1620
+ </label>
1621
+ <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 ); ?>"/>
1622
+ <span class="default_thumb_img">
1623
+ <?php
1624
+ if ( ! $default_thunmbnail ) {
1625
+ esc_html_e( 'None', 'category-posts' );
1626
+ } else {
1627
+ $img = wp_get_attachment_image_src( $default_thunmbnail );
1628
+ echo '<img width="60" height="60" src="' . esc_url( $img[0] ) . '" />';
1629
+ }
1630
+ ?>
1631
+ </span>
1632
+ </p>
1633
+ <p>
1634
+ <button type="button" class="cwp_default_thumb_select button upload-button">
1635
+ <?php esc_html_e( 'Select image', 'category-posts' ); ?>
1636
+ </button>
1637
+ <button type="button" class="cwp_default_thumb_remove button upload-button" <?php echo ( ! $default_thunmbnail ) ? 'style="display:none"' : ''; ?> >
1638
+ <?php esc_html_e( 'No default', 'category-posts' ); ?>
1639
+ </button>
1640
+ </p>
1641
+ <?php
1642
+ echo $this->get_checkbox_block_html( $instance, 'everything_is_link', esc_html__( 'Everything is a link', 'category-posts' ), true );
1643
+ ?>
1644
+ </div>
1645
+ </div>
1646
+ </div>
1647
+ <h4 data-panel="general"><?php esc_html_e( 'General', 'category-posts' ); ?></h4>
1648
+ <div>
1649
+ <?php echo $this->get_checkbox_block_html( $instance, 'disable_css', esc_html__( 'Disable the built-in CSS', 'category-posts' ), true ); ?>
1650
+ <?php echo $this->get_checkbox_block_html( $instance, 'disable_font_styles', esc_html__( 'Disable only font styles', 'category-posts' ), true ); ?>
1651
+ <div class="cpwp_ident">
1652
+ <?php
1653
+ echo $this->get_select_block_html( $instance, 'no_match_handling', esc_html__( 'When there are no matches', 'category-posts' ), array(
1654
+ 'nothing' => esc_html__( 'Display empty widget', 'category-posts' ),
1655
+ 'hide' => esc_html__( 'Hide Widget', 'category-posts' ),
1656
+ 'text' => esc_html__( 'Show text', 'category-posts' ),
1657
+ ), 'nothing', true );
1658
+ ?>
1659
+ <div class="categoryPosts-no-match-text" style="display:<?php echo ( 'text' === $instance['no_match_handling'] ) ? 'block' : 'none'; ?>">
1660
+ <?php echo $this->get_textarea_html( $instance, 'no_match_text', esc_html__( 'Text', 'category-posts' ), '', true, 4 ); ?>
1661
+ </div>
1662
+ </div>
1663
+ <div>
1664
+ <?php
1665
+ echo $this->get_checkbox_block_html( $instance, 'enable_loadmore', esc_html__( 'Enable Load More', 'category-posts' ), true );
1666
+ ?>
1667
+ <div class="loadmore-settings" style="display:<?php echo ( $instance['enable_loadmore'] ) ? 'block' : 'none'; ?>">
1668
+ <?php echo $this->get_text_input_block_html( $instance, 'loadmore_text', esc_html__( 'Button text', 'category-posts' ), '', true ); ?>
1669
+ <?php echo $this->get_text_input_block_html( $instance, 'loading_text', esc_html__( 'Loading text', 'category-posts' ), '', true ); ?>
1670
+ </div>
1671
+ </div>
1672
+ </div>
1673
+ <h4 data-panel="footer"><?php esc_html_e( 'Footer', 'category-posts' ); ?></h4>
1674
+ <div>
1675
+ <?php echo $this->get_text_input_block_html( $instance, 'footer_link_text', esc_html__( 'Footer link text', 'category-posts' ), '', true ); ?>
1676
+ <?php echo $this->get_text_input_block_html( $instance, 'footer_link', esc_html__( 'Footer link URL', 'category-posts' ), '', true ); ?>
1677
+ </div>
1678
+ <p><a href="<?php echo esc_url( get_edit_user_link() ) . '#' . __NAMESPACE__; ?>"><?php esc_html_e( 'Widget admin behaviour settings', 'category-posts' ); ?></a></p>
1679
+ <p><a target="_blank" href="<?php echo esc_url( DOC_URL ); ?>"><?php esc_html_e( 'Documentation', 'category-posts' ); ?></a></p>
1680
+ <p><a target="_blank" href="<?php echo esc_url( SUPPORT_URL ); ?>"><?php esc_html_e( 'Support', 'category-posts' ); ?></a></p>
1681
+ <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>
1682
+ </div>
1683
+ <?php
1684
+ }
1685
+ }
gulpfile.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var gulp = require('gulp');
2
+ var uglify = require('gulp-uglify');
3
+ var rename = require("gulp-rename");
4
+
5
+ gulp.task( 'default', [ 'scripts' ] )
6
+
7
+ // Gulp task to minify JavaScript files
8
+ gulp.task('scripts', function() {
9
+ return gulp.src(['./js/**/*.js', '!./js/**/*.min.js'])
10
+ // Minify the file
11
+ .pipe(uglify())
12
+ .pipe(rename({suffix: '.min'}))
13
+ .pipe(gulp.dest(function(file) {
14
+ return file.base;
15
+ }));
16
+ });
js/admin/category-posts-widget.js CHANGED
@@ -74,6 +74,37 @@
74
  }
75
  },
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  // Show template help
78
  toggleTemplateHelp: function(item,event) {
79
  event.preventDefault();
@@ -102,6 +133,16 @@
102
  }
103
  },
104
 
 
 
 
 
 
 
 
 
 
 
105
  selectPremadeTemplate: function(item) {
106
  var panel = item.parentElement.parentElement.parentElement;
107
  var div = item.parentElement.parentElement;
@@ -127,14 +168,13 @@
127
  template += '%thumb%\n';
128
  template += '<span class="dashicons dashicons-admin-comments"></span> %commentnum% ';
129
  template += '<span class="dashicons dashicons-admin-users"></span> %author%\n';
130
- template += '%excerpt%\n';
131
  template += 'Categories: %category% ';
132
  template += '<span class="dashicons dashicons-tag"></span> %post_tag%';
133
  }
134
  var textarea = jQuery(panel).find('textarea');
135
  textarea.val(template);
136
- textarea.trigger('input');
137
- textarea.trigger('change');
138
  },
139
 
140
  // Close all open panels if open
@@ -189,11 +229,11 @@
189
  var template = jQuery(elem).val();
190
  var tags = tiptoppress[ this.php_settings_var ].template_tags;
191
  var widget_cont = jQuery(elem.parentElement.parentElement.parentElement.parentElement);
192
- for (var i = 0; i < tags.length; i++) {
193
- if ( -1 !== template.indexOf( tags[i] ) ) {
194
- widget_cont.find(this.template_panel_prefix + tags[i] ).show();
195
  } else {
196
- widget_cont.find(this.template_panel_prefix + tags[i] ).hide();
197
  }
198
  }
199
  }
@@ -205,6 +245,142 @@
205
 
206
  },
207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  }
209
 
210
  jQuery(document).ready( function () {
@@ -263,15 +439,27 @@ jQuery(document).ready( function () {
263
  cwp_namespace.toggleAssignedCategoriesTop(this);
264
  });
265
 
266
- jQuery(document).on('click', class_namespace+' .hide_title', function () {
267
  cwp_namespace.toggleHideTitle(this);
268
  });
269
 
 
 
 
 
270
  jQuery(document).on('change', class_namespace+' .categoryPosts-preset_date_format select', function () { // change date format
271
  cwp_namespace.toggleDateFormat(this);
272
  });
273
 
274
- jQuery(document).off('click', class_namespace+' a.toggle-template-help').on('click', class_namespace+' a.toggle-template-help', function (event) { // show template help
 
 
 
 
 
 
 
 
275
  cwp_namespace.toggleTemplateHelp(this, event);
276
  });
277
 
@@ -287,6 +475,28 @@ jQuery(document).ready( function () {
287
  jQuery(document).on('input', class_namespace+' .categoryPosts-template textarea', function () { // prevent refresh ontemplate selection
288
  cwp_namespace.templateChange(this);
289
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  }
291
 
292
  setChangeHandlers();
74
  }
75
  },
76
 
77
+ // Show hide other date range settings
78
+ toggleDateRange: function(item) {
79
+ var value = jQuery(item).val();
80
+ var panel = item.parentElement.parentElement;
81
+ jQuery(panel).find('.categoryPosts-date-range p').hide();
82
+ jQuery(panel).find('.categoryPosts-date-range').show();
83
+ switch ( value ) {
84
+ case 'off':
85
+ jQuery(panel).find('.categoryPosts-date-range').hide();
86
+ break;
87
+ case 'days_ago':
88
+ jQuery(panel).find('.categoryPosts-days_ago').show();
89
+ break;
90
+ case 'between_dates':
91
+ jQuery(panel).find('.categoryPosts-start_date').show();
92
+ jQuery(panel).find('.categoryPosts-end_date').show();
93
+ break;
94
+ }
95
+ },
96
+
97
+ // Show/hide no match related settings
98
+ toggleNoMatch: function(item) {
99
+ var value = jQuery(item).val();
100
+ var panel = item.parentElement.parentElement;
101
+ if ( 'text' == value ) {
102
+ jQuery(panel).find('.categoryPosts-no-match-text').show();
103
+ } else {
104
+ jQuery(panel).find('.categoryPosts-no-match-text').hide();
105
+ }
106
+ },
107
+
108
  // Show template help
109
  toggleTemplateHelp: function(item,event) {
110
  event.preventDefault();
133
  }
134
  },
135
 
136
+ toggleLoadMore: function(item) {
137
+ var value = jQuery(item).attr('checked');
138
+ var panel = item.parentElement.parentElement.parentElement;
139
+ if (value != 'checked') {
140
+ jQuery(panel).find('.loadmore-settings').hide();
141
+ } else {
142
+ jQuery(panel).find('.loadmore-settings').show();
143
+ }
144
+ },
145
+
146
  selectPremadeTemplate: function(item) {
147
  var panel = item.parentElement.parentElement.parentElement;
148
  var div = item.parentElement.parentElement;
168
  template += '%thumb%\n';
169
  template += '<span class="dashicons dashicons-admin-comments"></span> %commentnum% ';
170
  template += '<span class="dashicons dashicons-admin-users"></span> %author%\n';
171
+ template += '%excerpt%';
172
  template += 'Categories: %category% ';
173
  template += '<span class="dashicons dashicons-tag"></span> %post_tag%';
174
  }
175
  var textarea = jQuery(panel).find('textarea');
176
  textarea.val(template);
177
+ textarea.trigger('input', 'change');
 
178
  },
179
 
180
  // Close all open panels if open
229
  var template = jQuery(elem).val();
230
  var tags = tiptoppress[ this.php_settings_var ].template_tags;
231
  var widget_cont = jQuery(elem.parentElement.parentElement.parentElement.parentElement);
232
+ for (var key in tags) {
233
+ if ( -1 !== template.indexOf( tags[key] ) ) {
234
+ widget_cont.find(this.template_panel_prefix + tags[key] ).show();
235
  } else {
236
+ widget_cont.find(this.template_panel_prefix + tags[key] ).hide();
237
  }
238
  }
239
  }
245
 
246
  },
247
 
248
+ thumbnailSizeChange : function (elem) {
249
+
250
+ var _that = jQuery(elem),
251
+ thumb_h,
252
+ thumb_w,
253
+ _input_thumb_h = _that.closest('.categoryposts-data-panel-thumb').find('.thumb_h'),
254
+ _input_thumb_w = _that.closest('.categoryposts-data-panel-thumb').find('.thumb_w');
255
+
256
+ if (_that.hasClass('smaller')) {
257
+ thumb_w = _input_thumb_w.val() / 1.015;
258
+ thumb_h = _input_thumb_h.val() / 1.015;
259
+ } else if (_that.hasClass('quarter')) {
260
+ thumb_w = _input_thumb_w.val() / 4;
261
+ thumb_h = _input_thumb_h.val() / 4;
262
+ } else if (_that.hasClass('half')){
263
+ thumb_h = _input_thumb_h.val() / 2;
264
+ thumb_w = _input_thumb_w.val() / 2;
265
+ } else if (_that.hasClass('double')){
266
+ thumb_h = _input_thumb_h.val() * 2;
267
+ thumb_w = _input_thumb_w.val() * 2;
268
+ } else if (_that.hasClass('bigger')) {
269
+ thumb_w = _input_thumb_w.val() * 1.02;
270
+ thumb_h = _input_thumb_h.val() * 1.02;
271
+ } else if (_that.hasClass('square')) {
272
+ if (_input_thumb_w.val() >= _input_thumb_h.val() ) {
273
+ thumb_w = _input_thumb_h.val();
274
+ thumb_h = _input_thumb_h.val();
275
+ } else{
276
+ thumb_w = _input_thumb_w.val();
277
+ thumb_h = _input_thumb_w.val();
278
+ }
279
+ } else if (_that.hasClass('standard')) {
280
+ if (_input_thumb_w.val() >= _input_thumb_h.val() ) {
281
+ thumb_w = _input_thumb_h.val() * 4 / 3;
282
+ thumb_h = _input_thumb_h.val();
283
+ } else {
284
+ thumb_w = _input_thumb_h.val() / 4 * 3
285
+ thumb_h = _input_thumb_h.val();
286
+ }
287
+ } else if (_that.hasClass('wide')) {
288
+ if (_input_thumb_w.val() >= _input_thumb_h.val() ) {
289
+ thumb_w = _input_thumb_h.val() * 16 / 9;
290
+ thumb_h = _input_thumb_h.val();
291
+ } else {
292
+ thumb_w = _input_thumb_h.val() / 16 * 9;
293
+ thumb_h = _input_thumb_h.val();
294
+ }
295
+ } else if (_that.hasClass('switch')){
296
+ thumb_h = _input_thumb_w.val();
297
+ thumb_w = _input_thumb_h.val();
298
+ } else {
299
+ thumb_w = _that.data("thumb-w");
300
+ thumb_h = _that.data("thumb-h");
301
+ }
302
+ _input_thumb_w.val(Math.floor(thumb_w));
303
+ _input_thumb_h.val(Math.floor(thumb_h));
304
+ _input_thumb_w.trigger('input', 'change');
305
+ _input_thumb_h.trigger('input', 'change');
306
+
307
+ return false;
308
+ },
309
+
310
+ thumbnailFluidWidthChange : function (elem) {
311
+
312
+ var _that = jQuery(elem),
313
+ _input_thumb_h = _that.closest('.categoryposts-data-panel-thumb').find('.thumb_h');
314
+
315
+ _that.closest( 'label' ).find( 'span' ).html( _that.val() + '%' );
316
+
317
+ _input_thumb_h.val(0);
318
+ _input_thumb_h.trigger('input', 'change');
319
+
320
+ return false;
321
+ },
322
+
323
+ openAddPlaceholder : function (elem) {
324
+
325
+ var _that = jQuery(elem);
326
+
327
+ _that.closest( '.cat-post-add_premade_templates' ).find( '.cpwp-placeholder-dropdown-menu' ).toggle();
328
+
329
+ _that.closest( '.cat-post-add_premade_templates' ).find( '.cpwp-placeholder-dropdown-menu span' ).off('click').on('click', function() {
330
+ var text = jQuery( this ).data( 'value' );
331
+ switch( text ){
332
+ case 'NewLine':
333
+ text = '\n';
334
+ break;
335
+ case 'EmptyLine':
336
+ text = '\n\n';
337
+ break;
338
+ default:
339
+ text = '%' + text + '%';
340
+ break;
341
+ }
342
+ var _div = this.parentElement.parentElement.parentElement;
343
+ var textarea = jQuery( _div ).find( 'textarea' );
344
+ var textareaPos = textarea[0].selectionStart;
345
+ var textareaTxt = textarea.val();
346
+ textarea.val( textareaTxt.substring(0, textareaPos) + text + textareaTxt.substring(textareaPos) );
347
+
348
+ textarea[0].selectionStart = textareaPos + text.length;
349
+ textarea[0].selectionEnd = textareaPos + text.length;
350
+ textarea.focus();
351
+ textarea.trigger('input', 'change');
352
+
353
+ //_that.closest( '.cat-post-add_premade_templates' ).find( '.cpwp-placeholder-dropdown-menu' ).hide();
354
+ });
355
+
356
+ _that.closest( '.cat-post-add_premade_templates' ).find( '.cpwp-placeholder-dropdown-menu' ).on('mouseenter', function(){
357
+ jQuery(this).addClass('cpw-doNotClose');
358
+ });
359
+ _that.closest( '.cat-post-add_premade_templates' ).find( '.cpwp-placeholder-dropdown-menu' ).on('mouseleave', function(){
360
+ jQuery(this).removeClass('cpw-doNotClose');
361
+ });
362
+
363
+ return false;
364
+ },
365
+
366
+ selectPlaceholderHelper : function (elem) {
367
+
368
+ var textarea = jQuery(elem);
369
+ var textareaPos = textarea[0].selectionStart;
370
+ var textareaTxt = textarea.val();
371
+
372
+ var nStartSel = textareaTxt.substring(0, textareaPos).lastIndexOf( '%' );
373
+ var nEndSel = textareaPos + textareaTxt.substring(textareaPos).indexOf( '%' ) + 1;
374
+
375
+ var strSelTxt = textareaTxt.substring(nStartSel, nEndSel);
376
+ if( strSelTxt.indexOf( '\n' ) >= 0 || strSelTxt.indexOf( ' ' ) >= 0 || strSelTxt.length <=2 ) {
377
+ return false;
378
+ }
379
+
380
+ textarea[0].selectionStart = nStartSel;
381
+ textarea[0].selectionEnd = nEndSel;
382
+ return false;
383
+ },
384
  }
385
 
386
  jQuery(document).ready( function () {
439
  cwp_namespace.toggleAssignedCategoriesTop(this);
440
  });
441
 
442
+ jQuery(document).on('click', class_namespace+' .categoryPosts-hide_title input', function () {
443
  cwp_namespace.toggleHideTitle(this);
444
  });
445
 
446
+ jQuery(document).on('click', class_namespace+' .categoryPosts-enable_loadmore input', function () {
447
+ cwp_namespace.toggleLoadMore(this);
448
+ });
449
+
450
  jQuery(document).on('change', class_namespace+' .categoryPosts-preset_date_format select', function () { // change date format
451
  cwp_namespace.toggleDateFormat(this);
452
  });
453
 
454
+ jQuery(document).on('change', class_namespace+' .categoryPosts-date_range select', function () { // change date range
455
+ cwp_namespace.toggleDateRange(this);
456
+ });
457
+
458
+ jQuery(document).on('change', class_namespace+' .categoryPosts-no_match_handling select', function () { // change date range
459
+ cwp_namespace.toggleNoMatch(this);
460
+ });
461
+
462
+ jQuery(class_namespace+' a.toggle-template-help').off('click').on('click', function (event) { // show template help
463
  cwp_namespace.toggleTemplateHelp(this, event);
464
  });
465
 
475
  jQuery(document).on('input', class_namespace+' .categoryPosts-template textarea', function () { // prevent refresh ontemplate selection
476
  cwp_namespace.templateChange(this);
477
  });
478
+
479
+ jQuery(document).on('click', class_namespace+' .cat-post-thumb-change-size button', function () { // find a thumbnail size
480
+ cwp_namespace.thumbnailSizeChange(this);
481
+ });
482
+
483
+ jQuery(document).on('change', class_namespace+' .thumb_fluid_width', function () { // select a thumbnail fluid size
484
+ cwp_namespace.thumbnailFluidWidthChange(this);
485
+ });
486
+
487
+ jQuery(class_namespace+' .cpwp-open-placholder-dropdown-menu').off('click').on('click', function () { // open drop down and add placeholder
488
+ cwp_namespace.openAddPlaceholder(this);
489
+ });
490
+
491
+ jQuery(document).on('onfocusout, blur', class_namespace+' .cpwp-open-placholder-dropdown-menu,'+class_namespace+' .categoryPosts-template textarea', function () { // close drop down placeholder, if not used
492
+ jQuery(this).closest( class_namespace+' .categoryPosts-template' ).parent().find( '.cpwp-placeholder-dropdown-menu' ).not('.cpw-doNotClose').hide();
493
+ });
494
+
495
+ jQuery(document).on('mousedown', class_namespace+' .categoryPosts-template textarea', function () { // help to select the placeholder
496
+ var _that = this;
497
+ setTimeout(function(){ cwp_namespace.selectPlaceholderHelper(_that); }, 0);
498
+ ;
499
+ });
500
  }
501
 
502
  setChangeHandlers();
js/admin/category-posts-widget.min.js ADDED
@@ -0,0 +1 @@
 
1
+ var cwp_namespace={php_settings_var:"categoryPosts",widget_class:".category-widget-cont",template_panel_prefix:".categoryposts-data-panel-",open_panels:{},template_change_timer:null,clickHandler:function(e){jQuery(e).toggleClass("open").next().stop().slideToggle();var t=jQuery(e).data("panel"),a=jQuery(e).parent().parent().parent().parent().parent().attr("id"),n={};this.open_panels.hasOwnProperty(a)&&(n=this.open_panels[a]),n.hasOwnProperty(t)?delete n[t]:n[t]=!0,this.open_panels[a]=n},toggleCatSelection:function(e){var t=jQuery(e).find("option:selected").attr("value"),a=e.parentElement.parentElement.parentElement.parentElement;"0"==t?(jQuery(a).find(".categoryPosts-title_link").hide(),jQuery(a).find(".categoryPosts-title_link_url").show(),jQuery(a).find(".categoryPosts-no_cat_childs").hide()):(jQuery(a).find(".categoryPosts-title_link").show(),jQuery(a).find(".categoryPosts-title_link_url").hide(),jQuery(a).find(".categoryPosts-no_cat_childs").show())},toggleDisableFontStyles:function(e){var t=jQuery(e).find("input").attr("checked"),a=e.parentElement.parentElement;"checked"==t?jQuery(a).find(".categoryposts-data-panel-general-disable-font-styles").hide():jQuery(a).find(".categoryposts-data-panel-general-disable-font-styles").show()},toggleDateFormat:function(e){var t=jQuery(e).val(),a=e.parentElement.parentElement;"other"!=t?jQuery(a).find(".categoryPosts-date_format").hide():jQuery(a).find(".categoryPosts-date_format").show()},toggleDateRange:function(e){var t=jQuery(e).val(),a=e.parentElement.parentElement;switch(jQuery(a).find(".categoryPosts-date-range p").hide(),jQuery(a).find(".categoryPosts-date-range").show(),t){case"off":jQuery(a).find(".categoryPosts-date-range").hide();break;case"days_ago":jQuery(a).find(".categoryPosts-days_ago").show();break;case"between_dates":jQuery(a).find(".categoryPosts-start_date").show(),jQuery(a).find(".categoryPosts-end_date").show()}},toggleNoMatch:function(e){var t=jQuery(e).val(),a=e.parentElement.parentElement;"text"==t?jQuery(a).find(".categoryPosts-no-match-text").show():jQuery(a).find(".categoryPosts-no-match-text").hide()},toggleTemplateHelp:function(e,t){t.preventDefault();var a=e.parentElement.parentElement.parentElement.parentElement;jQuery(a).find(".cat-post-template-help").toggle("slow")},toggleAssignedCategoriesTop:function(e){var t=jQuery(e).find("input").attr("checked"),a=e.parentElement.parentElement;"checked"==t?jQuery(a).find(".categoryposts-details-panel-assigned-cat-top").show():jQuery(a).find(".categoryposts-details-panel-assigned-cat-top").hide()},toggleHideTitle:function(e){var t=jQuery(e).attr("checked"),a=e.parentElement.parentElement.parentElement;"checked"!=t?jQuery(a).find(".categoryposts-data-panel-title-settings").show():jQuery(a).find(".categoryposts-data-panel-title-settings").hide()},toggleLoadMore:function(e){var t=jQuery(e).attr("checked"),a=e.parentElement.parentElement.parentElement;"checked"!=t?jQuery(a).find(".loadmore-settings").hide():jQuery(a).find(".loadmore-settings").show()},selectPremadeTemplate:function(e){var t=e.parentElement.parentElement.parentElement,a=e.parentElement.parentElement,n=jQuery(a).find("select"),o="%title%";switch(value=n.val(),value){case"title":o="%title%";break;case"title_excerpt":o="%title%\n\n%excerpt%";break;case"title_thumb":o="%title%\n\n%thumb%";break;case"title_thum_excerpt":o="%title%\n\n%thumb%\n%excerpt%";break;case"everything":o="%title%\n\n",o+="%date%\n\n",o+="%thumb%\n",o+='<span class="dashicons dashicons-admin-comments"></span> %commentnum% ',o+='<span class="dashicons dashicons-admin-users"></span> %author%\n',o+="%excerpt%",o+="Categories: %category% ",o+='<span class="dashicons dashicons-tag"></span> %post_tag%'}var l=jQuery(t).find("textarea");l.val(o),l.trigger("input","change")},autoCloseOpenPanels:function(e){if(tiptoppress[this.php_settings_var].accordion&&!jQuery(e).hasClass("open")){var t=jQuery(e).parent().find(".open");this.clickHandler(t)}},defaultThumbnailSelection:function(e,t,a){var n=wp.media({title:t,multiple:!1,library:{type:"image"},button:{text:a}});return n.on("close",function(){var t=n.state().get("selection").toJSON();if(1==t.length){var a=t[0],o='<img src="'+a.url+'" ';o+='width="60" ',o+='height="60" ',o+="/>",jQuery(e).parent().prev().find(".default_thumb_img").html(o),jQuery(e).parent().find(".cwp_default_thumb_remove").show(),jQuery(e).parent().prev().find(".default_thumb_id").val(a.id).change()}}),n.open(),!1},removeDefaultThumbnailSelection:function(e){return jQuery(e).parent().prev().find(".default_thumb_img").html(cwp_default_thumb_selection.none),jQuery(e).hide(),jQuery(e).parent().prev().find(".default_thumb_id").val(0).change(),!1},templateChange:function(e){function t(){var t=jQuery(e).val(),a=tiptoppress[this.php_settings_var].template_tags,n=jQuery(e.parentElement.parentElement.parentElement.parentElement);for(var o in a)-1!==t.indexOf(a[o])?n.find(this.template_panel_prefix+a[o]).show():n.find(this.template_panel_prefix+a[o]).hide()}null!=this.template_change_timer&&clearTimeout(this.template_change_timer),this.template_change_timer=setTimeout(t.bind(this),250)},thumbnailSizeChange:function(e){var t,a,n=jQuery(e),o=n.closest(".categoryposts-data-panel-thumb").find(".thumb_h"),l=n.closest(".categoryposts-data-panel-thumb").find(".thumb_w");return n.hasClass("smaller")?(a=l.val()/1.015,t=o.val()/1.015):n.hasClass("quarter")?(a=l.val()/4,t=o.val()/4):n.hasClass("half")?(t=o.val()/2,a=l.val()/2):n.hasClass("double")?(t=2*o.val(),a=2*l.val()):n.hasClass("bigger")?(a=1.02*l.val(),t=1.02*o.val()):n.hasClass("square")?l.val()>=o.val()?(a=o.val(),t=o.val()):(a=l.val(),t=l.val()):n.hasClass("standard")?l.val()>=o.val()?(a=4*o.val()/3,t=o.val()):(a=o.val()/4*3,t=o.val()):n.hasClass("wide")?l.val()>=o.val()?(a=16*o.val()/9,t=o.val()):(a=o.val()/16*9,t=o.val()):n.hasClass("switch")?(t=l.val(),a=o.val()):(a=n.data("thumb-w"),t=n.data("thumb-h")),l.val(Math.floor(a)),o.val(Math.floor(t)),l.trigger("input","change"),o.trigger("input","change"),!1},thumbnailFluidWidthChange:function(e){var t=jQuery(e),a=t.closest(".categoryposts-data-panel-thumb").find(".thumb_h");return t.closest("label").find("span").html(t.val()+"%"),a.val(0),a.trigger("input","change"),!1},openAddPlaceholder:function(e){var t=jQuery(e);return t.closest(".cat-post-add_premade_templates").find(".cpwp-placeholder-dropdown-menu").toggle(),t.closest(".cat-post-add_premade_templates").find(".cpwp-placeholder-dropdown-menu span").off("click").on("click",function(){var e=jQuery(this).data("value");switch(e){case"NewLine":e="\n";break;case"EmptyLine":e="\n\n";break;default:e="%"+e+"%"}var t=this.parentElement.parentElement.parentElement,a=jQuery(t).find("textarea"),n=a[0].selectionStart,o=a.val();a.val(o.substring(0,n)+e+o.substring(n)),a[0].selectionStart=n+e.length,a[0].selectionEnd=n+e.length,a.focus(),a.trigger("input","change")}),t.closest(".cat-post-add_premade_templates").find(".cpwp-placeholder-dropdown-menu").on("mouseenter",function(){jQuery(this).addClass("cpw-doNotClose")}),t.closest(".cat-post-add_premade_templates").find(".cpwp-placeholder-dropdown-menu").on("mouseleave",function(){jQuery(this).removeClass("cpw-doNotClose")}),!1},selectPlaceholderHelper:function(e){var t=jQuery(e),a=t[0].selectionStart,n=t.val(),o=n.substring(0,a).lastIndexOf("%"),l=a+n.substring(a).indexOf("%")+1,s=n.substring(o,l);return!(s.indexOf("\n")>=0||s.indexOf(" ")>=0||s.length<=2)&&(t[0].selectionStart=o,t[0].selectionEnd=l,!1)}};jQuery(document).ready(function(){function e(){jQuery(".cwp_default_thumb_select").off("click").on("click",function(){cwp_namespace.defaultThumbnailSelection(this,cwp_default_thumb_selection.frame_title,cwp_default_thumb_selection.button_title)}),jQuery(document).on("change",t+" .categoryposts-data-panel-filter-cat",function(){cwp_namespace.toggleCatSelection(this)}),jQuery(".cwp_default_thumb_remove").off("click").on("click",function(){cwp_namespace.removeDefaultThumbnailSelection(this)}),jQuery(t+"-assigned_categories").off("click").on("click",function(){cwp_namespace.toggleAssignedCategoriesTop(this)}),jQuery(document).on("click",t+" .categoryPosts-hide_title input",function(){cwp_namespace.toggleHideTitle(this)}),jQuery(document).on("click",t+" .categoryPosts-enable_loadmore input",function(){cwp_namespace.toggleLoadMore(this)}),jQuery(document).on("change",t+" .categoryPosts-preset_date_format select",function(){cwp_namespace.toggleDateFormat(this)}),jQuery(document).on("change",t+" .categoryPosts-date_range select",function(){cwp_namespace.toggleDateRange(this)}),jQuery(document).on("change",t+" .categoryPosts-no_match_handling select",function(){cwp_namespace.toggleNoMatch(this)}),jQuery(t+" a.toggle-template-help").off("click").on("click",function(e){cwp_namespace.toggleTemplateHelp(this,e)}),jQuery(document).on("click",t+" .cat-post-premade_templates button",function(){cwp_namespace.selectPremadeTemplate(this)}),jQuery(document).on("change",t+" .cat-post-premade_templates select",function(e){e.preventDefault(),e.stopPropagation()}),jQuery(document).on("input",t+" .categoryPosts-template textarea",function(){cwp_namespace.templateChange(this)}),jQuery(document).on("click",t+" .cat-post-thumb-change-size button",function(){cwp_namespace.thumbnailSizeChange(this)}),jQuery(document).on("change",t+" .thumb_fluid_width",function(){cwp_namespace.thumbnailFluidWidthChange(this)}),jQuery(t+" .cpwp-open-placholder-dropdown-menu").off("click").on("click",function(){cwp_namespace.openAddPlaceholder(this)}),jQuery(document).on("onfocusout, blur",t+" .cpwp-open-placholder-dropdown-menu,"+t+" .categoryPosts-template textarea",function(){jQuery(this).closest(t+" .categoryPosts-template").parent().find(".cpwp-placeholder-dropdown-menu").not(".cpw-doNotClose").hide()}),jQuery(document).on("mousedown",t+" .categoryPosts-template textarea",function(){var e=this;setTimeout(function(){cwp_namespace.selectPlaceholderHelper(e)},0)})}var t=".category-widget-cont";jQuery(".category-widget-cont h4").click(function(){cwp_namespace.autoCloseOpenPanels(this),cwp_namespace.clickHandler(this)}),jQuery(document).on("widget-added widget-updated panelsopen",function(t,a){jQuery(".category-widget-cont h4").off("click").on("click",function(){cwp_namespace.autoCloseOpenPanels(this),cwp_namespace.clickHandler(this)}),jQuery(".cwp_default_thumb_select").off("click").on("click",function(){cwp_namespace.defaultThumbnailSelection(this,cwp_default_thumb_selection.frame_title,cwp_default_thumb_selection.button_title)}),jQuery(".cwp_default_thumb_remove").off("click").on("click",function(){cwp_namespace.removeDefaultThumbnailSelection(this)});var n=jQuery(a).attr("id");if(cwp_namespace.open_panels.hasOwnProperty(n)){var o=cwp_namespace.open_panels[n];for(var l in o)jQuery(a).find("[data-panel="+l+"]").toggleClass("open").next().stop().show()}e()}),e()});
js/admin/tinymce.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(t){var e="categoryPosts",o="category-posts";tinymce.create("tinymce.plugins."+e,{init:function(n,t){n.addButton(e,{title:n.getLang(o+".tooltip"),cmd:"categoryPosts_shortcode",text:"+[CP]"}),n.addCommand("categoryPosts_shortcode",function(){n.windowManager.open({title:n.getLang(o+".title"),body:[{type:"textbox",name:"title",label:n.getLang(o+".name")},{type:"container",html:'<a style="color:blue;textdecoration:underline;cursor:pointer" href="'+n.getLang(o+".profiile_url")+'">'+n.getLang(o+".hide_message")+"</a>"}],onsubmit:function(t){var e="[catposts";""!=t.data.title&&(e+=' name="'+t.data.title+'"'),e+="]",n.selection.setContent(e)}})})},createControl:function(t,e){return null},getInfo:function(){return{longname:"Insert category post shortcode",author:"TipTopPress",version:"4.7"}}}),tinymce.PluginManager.add("categoryPosts",tinymce.plugins.categoryPosts)}(jQuery);
js/frontend/category-posts-frontend.min.js ADDED
@@ -0,0 +1 @@
 
1
+ "undefined"!=typeof jQuery&&jQuery(document).ready(function(){"objectFit"in document.documentElement.style==!1&&(jQuery(".cat-post-item figure").removeClass("cat-post-crop"),jQuery(".cat-post-item figure").addClass("cat-post-crop-not-supported")),(document.documentMode||/Edge/.test(navigator.userAgent))&&(jQuery(".cat-post-item figure img").height("+=1"),window.setTimeout(function(){jQuery(".cat-post-item figure img").height("-=1")},0))});
js/frontend/date.js ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Category Posts Widget
3
+ * https://github.com/tiptoppress/category-posts-widget
4
+ *
5
+ * JS for the localized date functionality.
6
+ *
7
+ * Released under the GPLv2 license or later - http://www.gnu.org/licenses/gpl-2.0.html
8
+ */
9
+
10
+ if (typeof jQuery !== 'undefined') {
11
+
12
+ jQuery( document ).ready(function () {
13
+ var $elements = jQuery('.cat-post-item .cat-post-date[data-publishtime]');
14
+ if ( 0 !== $elements) {
15
+ /**
16
+ * Adjust the dates for the items indicated in the $elements
17
+ * array.
18
+ *
19
+ * @param array $elements array of dom elements.
20
+ */
21
+ var adjustlocalizeddate = function ( $elements ) {
22
+ $elements.each(function ( ) {
23
+ var $this = jQuery( this );
24
+ var time = $this.data( 'publishtime' ) * 1000; // new Date() requires time in ms.
25
+ var format = $this.data( 'format' );
26
+ var orig_date = new Date( time );
27
+ switch ( format ) {
28
+ case 'date' :
29
+ $this.text( orig_date.toLocaleDateString() );
30
+ break;
31
+ case 'time' :
32
+ $this.text( orig_date.toLocaleDateString() + ' '
33
+ + orig_date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}) // Eliminate seconds.
34
+ );
35
+ break;
36
+ }
37
+ });
38
+ }
39
+
40
+ adjustlocalizeddate( $elements );
41
+
42
+ // Wait for catposts.load_more event that load more triggers when for
43
+ // mewly added item, and localize the date if needed.
44
+ jQuery( 'ul' ).on('catposts.load_more', '.cat-post-item', function () {
45
+ adjustlocalizeddate( jQuery( this ).find( '.cat-post-date[data-publishtime]' ) );
46
+ });
47
+ }
48
+ });
49
+ }
js/frontend/date.min.js ADDED
@@ -0,0 +1 @@
 
1
+ "undefined"!=typeof jQuery&&jQuery(document).ready(function(){var t=jQuery(".cat-post-item .cat-post-date[data-publishtime]");if(0!==t){var e=function(t){t.each(function(){var t=jQuery(this),e=1e3*t.data("publishtime"),a=t.data("format"),i=new Date(e);switch(a){case"date":t.text(i.toLocaleDateString());break;case"time":t.text(i.toLocaleDateString()+" "+i.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}))}})};e(t),jQuery("ul").on("catposts.load_more",".cat-post-item",function(){e(jQuery(this).find(".cat-post-date[data-publishtime]"))})}});
js/frontend/loadmore.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Category Posts Widget
3
+ * https://github.com/tiptoppress/category-posts-widget
4
+ *
5
+ * JS for the "load more" functionality.
6
+ *
7
+ * Released under the GPLv2 license or later - http://www.gnu.org/licenses/gpl-2.0.html
8
+ */
9
+
10
+ if (typeof jQuery !== 'undefined') {
11
+
12
+ var php_settings_var = 'categoryPosts'; // should be identical to namespace.
13
+
14
+ jQuery( document ).ready(function () {
15
+
16
+ // Handle the click of load more.
17
+ jQuery(document).on('click', '.' + php_settings_var + '-loadmore button', function() {
18
+ var $this = jQuery(this);
19
+ var id = $this.data( 'id' );
20
+ var number = $this.data( 'number' );
21
+ var start = $this.data( 'start' );
22
+ var context = $this.data( 'context' );
23
+ var url = tiptoppress[php_settings_var].json_root_url;
24
+ var $ul = jQuery(this.parentElement.parentElement).find('ul'); // The UL of the widget.
25
+ var orig_text = $this.text();
26
+ var loading_text = $this.data( 'loading' );
27
+
28
+ // Change the button text to indicate loading.
29
+ $this.text( loading_text );
30
+ // Get the data from the server
31
+ jQuery.getJSON(url + '/' + id + '/' + start + '/' + number + '/' + context + '/', function ( data ) {
32
+ // appened the returned data to the UL in the returned order.
33
+ jQuery.each(data, function (key, li) {
34
+ $ul.append(li);
35
+ // apend returns the $ul, therefor we need to actualy find
36
+ // the newly added item.
37
+ $ul.children().last().trigger('catposts.load_more');
38
+ });
39
+ if (data.length != number) {
40
+ $this.hide();
41
+ } else {
42
+ $this.data( 'start', start+number );
43
+ }
44
+ }).always( function () {
45
+ // Revert to original text.
46
+ $this.text( orig_text );
47
+ });
48
+ });
49
+ });
50
+ }
js/frontend/loadmore.min.js ADDED
@@ -0,0 +1 @@
 
1
+ if("undefined"!=typeof jQuery){var php_settings_var="categoryPosts";jQuery(document).ready(function(){jQuery(document).on("click","."+php_settings_var+"-loadmore button",function(){var e=jQuery(this),t=e.data("id"),a=e.data("number"),n=e.data("start"),r=e.data("context"),o=tiptoppress[php_settings_var].json_root_url,i=jQuery(this.parentElement.parentElement).find("ul"),d=e.text(),s=e.data("loading");e.text(s),jQuery.getJSON(o+"/"+t+"/"+n+"/"+a+"/"+r+"/",function(t){jQuery.each(t,function(t,e){i.append(e),i.children().last().trigger("catposts.load_more")}),t.length!=a?e.hide():e.data("start",n+a)}).always(function(){e.text(d)})})})}
loadmore.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Server side implementation of load more handling.
4
+ *
5
+ * @package categoryposts.
6
+ *
7
+ * @since 4.9
8
+ */
9
+
10
+ namespace categoryPosts;
11
+
12
+ // Don't call the file directly.
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * Embed the front end JS for load more.
19
+ *
20
+ * @since 4.9
21
+ */
22
+ function embed_loadmore_scripts() {
23
+ echo '<script>{';
24
+ $suffix = 'min.js';
25
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
26
+ $suffix = 'js';
27
+ }
28
+ echo 'var tiptoppress = Array();';
29
+ echo 'tiptoppress["' . esc_js( __NAMESPACE__ ) . '"] = { json_root_url : "' . esc_js( rest_url( __NAMESPACE__ . '/loadmore' ) ) . '"};';
30
+ include __DIR__ . '/js/frontend/loadmore.' . $suffix;
31
+ echo '}</script>';
32
+ }
33
+
34
+ /**
35
+ * Generate the JSON response which includes additional element as a response
36
+ * to a "load more" request.
37
+ *
38
+ * @param \WP_REST_Request $request The rest request with widget aand start point info.
39
+ */
40
+ function get_next_elements( \WP_REST_Request $request ) {
41
+ $id = (string) $request['id'];
42
+ $start = (int) $request['start'];
43
+ $number = (int) $request['number'];
44
+ $context = (string) $request['context'];
45
+
46
+ $ret = [];
47
+
48
+ $id_components = explode( '-', $id );
49
+ if ( 2 <= count( $id_components ) ) {
50
+ switch ( $id_components[0] ) {
51
+ case 'shortcode':
52
+ if ( 3 === count( $id_components ) ) {
53
+ $pid = $id_components[1]; // The ID of the relevant post.
54
+ $name = $id_components[2]; // The shortcode "name".
55
+ $settings = shortcode_settings( $pid, $name );
56
+ if ( ! empty( $settings ) ) {
57
+ $virtual_widget = new Virtual_Widget( '', '', $settings );
58
+ $ret = $virtual_widget->get_elements_HTML( $start, $number, $context );
59
+ }
60
+ }
61
+ break;
62
+ case 'widget':
63
+ if ( 2 === count( $id_components ) ) {
64
+ $id = $id_components[1]; // The ID of the widget.
65
+ $class = __NAMESPACE__ . '\Widget';
66
+ $widgetclass = new $class();
67
+ $allsettings = $widgetclass->get_settings();
68
+ if ( isset( $allsettings[ $id ] ) ) {
69
+ $virtual_widget = new Virtual_Widget( '', '', $allsettings[ $id ] );
70
+ $ret = $virtual_widget->get_elements_HTML( $start, $number, $context );
71
+ }
72
+ }
73
+ break;
74
+ }
75
+ }
76
+
77
+ return new \WP_REST_Response( $ret );
78
+ }
79
+
80
+ /**
81
+ * This function is where we register our routes for our example endpoint.
82
+ */
83
+ function register_route() {
84
+ register_rest_route( __NAMESPACE__, '/loadmore/(?P<id>[\w-]+)/(?P<start>[\d]+)/(?P<number>[\d]+)/(?P<context>[\w]+)', array(
85
+ 'methods' => 'GET',
86
+ 'callback' => __NAMESPACE__ . '\get_next_elements',
87
+ ) );
88
+ }
89
+
90
+ add_action( 'rest_api_init', __NAMESPACE__ . '\register_route' );
localizeddate.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Server side implementation of localized dates handling.
4
+ *
5
+ * @package categoryposts.
6
+ *
7
+ * @since 4.9
8
+ */
9
+
10
+ namespace categoryPosts;
11
+
12
+ // Don't call the file directly.
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * Embed the front end JS for load more.
19
+ *
20
+ * @since 4.9
21
+ */
22
+ function embed_date_scripts() {
23
+ echo '<script>{';
24
+ $suffix = 'min.js';
25
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
26
+ $suffix = 'js';
27
+ }
28
+ include __DIR__ . '/js/frontend/date.' . $suffix;
29
+ echo '}</script>';
30
+ }
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === Category Posts Widget ===
2
  Contributors: mark-k, kometschuh, mkrdip
3
- Donate link: http://mkrdip.me/donate
4
  Tags: category, categories, posts, widget, posts widget, recent posts, category recent posts, shortcode, sidebar, excerpt, multiple widgets
5
  Requires at least: 2.8
6
- Tested up to: 4.9
7
- Stable tag: 4.8.5
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -14,68 +14,52 @@ Adds a widget that shows the most recent posts from a single category.
14
  Category Posts Widget is a light widget designed to do one thing and do it well: display the most recent posts from a certain category.
15
 
16
  = Term and Category based Posts Widget =
17
- A premium version of this free widget here and available on [tiptoppress.com](http://tiptoppress.com/?utm_source=wordpress_org&utm_campaign=premium_widget_cpw&utm_medium=web) created for big Wordpress sites.
18
- We've also started creating free widget Add-ons for the Premium Widget here: [Widget extensions](http://tiptoppress.com/extensions/?utm_source=wordpress_org&utm_campaign=premium_widget_cpw&utm_medium=web).
19
 
20
  = Premium features =
21
- * "Post List Alterations"
22
- * Post details alignment
23
  * Full background post images
24
- * Slider (Vertical and horizontal News-Ticker)
25
- * Grid and Column full page layouts
26
- * Masonry (Responsive grid layouts)
27
- * Custom Post Types, Events, Products
28
- * Multi selection
29
- * More complex ways to filter (OR, AND, Exclude, ALL, Childes)
30
- * Mouse hover effects
31
- * Widget Extensions
32
- * Custom filter- and action hooks (for development)
33
  * All free features
34
  * E-Mail support
35
- * Free trail on localhost
36
- * More examples on the [demo pages](http://demo.tiptoppress.com/?utm_source=wordpress_org&utm_campaign=demo_cpw&utm_medium=web)
37
 
38
  = Features =
39
- * Show SVG font-icon for "post formats"
40
- * [Template](http://tiptoppress.com/template-arrange-post-details/) to arrange the post details
41
- * Premade Templates
42
- * The Template text can be a post details placeholder, plain text, HTML or HTML for SVG font-icons
43
- * Mouse hover effects for post thumbnail, e.g, zoom, SVG font-icon
44
  * Shortcode (Easily change all Shortcode options in the customizer).
45
- * Support multiple shortcodes at the same post.
46
- * Option for post offset (use two or more widgets after another).
47
- * UI buttons in the editor toolbar to insert shortcode.
 
 
 
 
48
  * Option to touch device friendly "everything is a link".
49
- * Additional customizer link at the admin-bar ("With one click to the Customizer").
50
- * Thumbnail width & height (<a target="_blank" href="http://tiptoppress.com/css-image-crop/?utm_source=wordpress_org&utm_campaign=features_crop_cpw&utm_medium=web">CSS cropping</a>).
51
- * Fluid images (Responsive layout support).
52
- * Set default thumbnail.
53
- * Option to change ordering of posts.
54
- * Add dropdownbox entry for 'all' categories.
55
- * Set how many posts to show.
56
- * Option to hide widget, if category have currently no posts.
57
- * Add option to disable subcategories.
58
  * Option to hide posts which have no thumbnail.
59
  * Option exclude current post.
60
- * Option to hide the widget title and post titles.
61
- * Option to make the widget title link to the category page.
62
- * Option to show the post excerpt, set the length, allow HTML and change 'more' text.
63
- * Option show post author.
64
- * Option to show the comment count.
65
- * Option to show the post date and set the date format, e.g. "Time since published"
66
- * Filter by post status: Published, scheduled, private (like events)
67
- * Option to make the widget date link to the category page.
68
- * Option to format the outputted date string.
69
- * Option to link to the category page below posts list.
70
- * Option to disable the widget CSS or more granular control of default styling with not apply the font styles.
71
- * Multiple widgets.
72
  * Multi sites support.
73
- * Support localization with translate.wordpress.org
74
- * Use WP user profile for settings ('auto close' and if the shortcode button appears in the editor toolbar).
75
 
76
  = Documentation =
77
- * Full [documentation](http://tiptoppress.com/category-posts-widget/documentation-4-8/?utm_source=wordpress_org&utm_campaign=documentation_4_8_cpw&utm_medium=web)
78
- * Shortcode: Use [catposts] [in the content and edit options in the customizer](http://tiptoppress.com/use-shortcode-to-add-category-posts-widget-to-the-content/?utm_source=wordpress_org&utm_campaign=documentation_shortcode_cpw&utm_medium=web)
79
  * Formatting date and time: See <a target="_blank" href="https://codex.wordpress.org/Formatting_Date_and_Time">Formatting Date and Time</a>
80
 
81
  = Contribute =
@@ -105,7 +89,7 @@ Automatic installation is the easiest option as WordPress handles the file trans
105
 
106
  == Frequently Asked Questions ==
107
  = Template, placeholder and post detail =
108
- Here You can control the [Post Detail parts](http://tiptoppress.com/category-posts-widget/documentation-4-8/#Post_details), which appears as part of the post item. All post detail will placed as placeholder. The text in the Template area can be a post details placeholder, plain text, HTML or HTML for SVG icons.
109
 
110
  [How it works? and examples.](http://tiptoppress.com/template-arrange-post-details/)
111
 
@@ -114,23 +98,8 @@ For more layout options please try our premium widget: [Term and Category based
114
  = Use SVG icons =
115
  For SVG font-icon HTML we recommend the [WordPress Dashicons](https://developer.wordpress.org/resource/dashicons/), which are included as default and can be used without any font-icon including.
116
 
117
- = How Category Posts Widget work with SiteOrigin Page Builder =
118
- Please add the widget with shortcode [catposts] inside a editor widget which is placed to a SiteOrigin Page Builder row.
119
- We also create a YouTube video: [How Category Posts Widget work with SiteOrigin Page Builder](http://tiptoppress.com/how-category-posts-widget-work-with-siteorigin-page-builder/)
120
-
121
- = How Category Posts Widget work with Divi Builder =
122
- You can create a new Divi widget area and put our widgets into a Divi Sidebar Module: [How to put a widget into Divi Module](https://divibooster.com/how-to-put-a-widget-into-divi-module/).
123
-
124
- = How our widgets works with other page builder plugins like Elementor, Thrive Architect etc. =
125
- We recommend for all other page builder plugins like Elementor or Thrive Architect to use the [WP Custom Widget Area](https://wordpress.org/plugins/wp-custom-widget-area/) plugin in additional.
126
-
127
- Use the [WP Custom Widget Area](https://wordpress.org/plugins/wp-custom-widget-area/) plugin and create a new custom area with this plugin and place the shortcode (from the new created widget area) to a post or page. Then add our widget to the new created custom widget area the widgets admin site (Dashboard > Appearance > Widgets > … ).
128
-
129
- = The font-size is different from that of other widgets or Theme elements? =
130
- Please use the option: "Disable widget CSS".
131
-
132
- = I want the title as a link pointing to the selected Categorie page? =
133
- Enable the check box "Make widget title link".
134
 
135
  = Parse error: syntax error, unexpected T_FUNCTION in /home/www/blog/wp-content/plugins/category-posts/cat-posts.php on line 58 =
136
  Some of the features that were used in that version needs PHP 5.3+.
@@ -141,6 +110,11 @@ We know there are peopel how use PHP 5.2 [wordpress.org/about/stats](https://wor
141
  = You check the PHP version with phpversion(), but the widget don't work =
142
  Check also the .htaccess file, if there is an entry for an older PHP version.
143
 
 
 
 
 
 
144
  == Screenshots ==
145
  1. Front end of the widget with SVG font-icon support for post formats, hover effects and the Template text-area.
146
  2. Template to arrange the post details with placeholders.
@@ -150,7 +124,18 @@ Check also the .htaccess file, if there is an entry for an older PHP version.
150
  6. Widget behaviour settings for each user.
151
 
152
  == Changelog ==
153
- [Read more on our blog ...](http://tiptoppress.com/category/category-posts-widget?utm_source=wordpress_org&utm_campaign=changelog_cpw&utm_medium=web)
 
 
 
 
 
 
 
 
 
 
 
154
 
155
  = 4.8.5 - April 02nd 2018 =
156
  * Fixed Tabs not working
1
  === Category Posts Widget ===
2
  Contributors: mark-k, kometschuh, mkrdip
3
+ Donate link: https://wordpress.org/support/plugin/category-posts/reviews/
4
  Tags: category, categories, posts, widget, posts widget, recent posts, category recent posts, shortcode, sidebar, excerpt, multiple widgets
5
  Requires at least: 2.8
6
+ Tested up to: 5.2
7
+ Stable tag: 4.9.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
14
  Category Posts Widget is a light widget designed to do one thing and do it well: display the most recent posts from a certain category.
15
 
16
  = Term and Category based Posts Widget =
17
+ A premium version of that free widget available at [tiptoppress.com](http://tiptoppress.com/) created for big Wordpress sites.
 
18
 
19
  = Premium features =
20
+ * Image-Slider
21
+ * Post List "Alterations"
22
  * Full background post images
23
+ * Masonry (responsive layouts), Grid and Column full page layouts
24
+ * More complex ways to filter (ANY, NOT, AND, not include children)
25
+ * Custom Post Types, Events, Products support
26
+ * Add-ons
 
 
 
 
 
27
  * All free features
28
  * E-Mail support
29
+ * Free trial on localhost
30
+ * More examples on the [demo pages](http://demo.tiptoppress.com/)
31
 
32
  = Features =
33
+ * [Template](http://tiptoppress.com/template-arrange-post-details/) to arrange the post details.
34
+ * The Template text can be a post details placeholder, plain text, HTML or a font-icons.
35
+ * Font-icon support.
36
+ * 'Load more' button / Ajax API
 
37
  * Shortcode (Easily change all Shortcode options in the customizer).
38
+ * Date range filter
39
+ * New date format: Time since plublished
40
+ * Filter by post status: Published, scheduled, private.
41
+ * Multiple shortcodes at the same site or post.
42
+ * Add option for post offset (use two or more widgets after another).
43
+ * Admin UI: Buttons in the editor toolbar to insert shortcode.
44
+ * Excerpt length in lines (line-clamp)
45
  * Option to touch device friendly "everything is a link".
46
+ * For editing shortcode adds a Customizer link to the admin-bar ("With one click to the Customizer").
47
+ * Set thumbnail width & height / image crop with CSS (object-fit).
48
+ * Fluid images (max-width in %).
49
+ * One thumb dimension can be left empty.
50
+ * Option to set mouse hover effects for post thumbnail.
51
+ * Set a default thumbnail.
52
+ * Hide widget text or text, if there is no post.
 
 
53
  * Option to hide posts which have no thumbnail.
54
  * Option exclude current post.
55
+ * Option show post author, comment's count, post date.
56
+ * Admin UI: Set / find thumbnail size buttons: +, ¼, ½, 2x, -, ratio and Media sizes
57
+ * Admin UI: Buttons to easy add post details placeholder.
 
 
 
 
 
 
 
 
 
58
  * Multi sites support.
 
 
59
 
60
  = Documentation =
61
+ * Full [documentation](http://tiptoppress.com/category-posts-widget/documentation-4-9)
62
+ * Shortcode: Use [catposts] in the content and [edit in the customizer](http://tiptoppress.com/use-shortcode-to-add-category-posts-widget-to-the-content/)
63
  * Formatting date and time: See <a target="_blank" href="https://codex.wordpress.org/Formatting_Date_and_Time">Formatting Date and Time</a>
64
 
65
  = Contribute =
89
 
90
  == Frequently Asked Questions ==
91
  = Template, placeholder and post detail =
92
+ Here You can control the [Post Detail parts](http://tiptoppress.com/category-posts-widget/documentation-4-9/#Post_details), which appears as part of the post item. All post detail will placed as placeholder. The text in the Template area can be a post details placeholder, plain text, HTML or HTML for SVG icons.
93
 
94
  [How it works? and examples.](http://tiptoppress.com/template-arrange-post-details/)
95
 
98
  = Use SVG icons =
99
  For SVG font-icon HTML we recommend the [WordPress Dashicons](https://developer.wordpress.org/resource/dashicons/), which are included as default and can be used without any font-icon including.
100
 
101
+ = How to use Page Bilder plugins like Divi, SiteOrigin Page Builder or Elementor =
102
+ Please read more about Page Builder plugins at our [FAQs](http://tiptoppress.com/faqs/)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  = Parse error: syntax error, unexpected T_FUNCTION in /home/www/blog/wp-content/plugins/category-posts/cat-posts.php on line 58 =
105
  Some of the features that were used in that version needs PHP 5.3+.
110
  = You check the PHP version with phpversion(), but the widget don't work =
111
  Check also the .htaccess file, if there is an entry for an older PHP version.
112
 
113
+ = Excerpt length in lines don't work with FireFox and IE =
114
+ We use for Excerpt length in lines a CSS feature called "line-clamp" which isn't supported in FireFox and IE at the moment.
115
+ But since it is supported by all other browsers, we think it will be supported in FireFox and IE soon.
116
+ The FireFox browser has a coverage of 5%, which we find little. For this 5%, we have included a polyfill in our [Excerpt Extension add-on](https://wordpress.org/plugins/excerpt-extension/).
117
+
118
  == Screenshots ==
119
  1. Front end of the widget with SVG font-icon support for post formats, hover effects and the Template text-area.
120
  2. Template to arrange the post details with placeholders.
124
  6. Widget behaviour settings for each user.
125
 
126
  == Changelog ==
127
+ [Read more on our blog ...](http://tiptoppress.com/category/category-posts-widget)
128
+
129
+ = 4.9.1 - Mai 17th 2019 =
130
+ * 'Load more' button / Ajax API
131
+ * Data range filter
132
+ * Text, if there is no post
133
+ * Excerpt length in lines (line-clamp)
134
+ * Image crop with CSS (object-fit)
135
+ * Fluid images (max-width in %).
136
+ * One thumb dimension can be left empty
137
+ * Admin UI: Button to easy add post details placeholder
138
+ * Admin UI: Set / find thumbnail size buttons: +, ¼, ½, 2x, -, ratio and Media sizes
139
 
140
  = 4.8.5 - April 02nd 2018 =
141
  * Fixed Tabs not working
styles/admin/category-posts-widget.css ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ .cpwp_ident {
4
+ color: #6A6A6A;
5
+ background: #efefef;
6
+ padding: 5px;
7
+ }
8
+ .cpwp_ident ~ .cpwp_ident {
9
+ margin-top: 10px;
10
+ }
11
+ .cpwp_ident:last-child {
12
+ display: block;
13
+ }
14
+ /* .cpwp_ident div:last-child {
15
+ display: block;
16
+ } */
17
+ .cpwp_ident > .cpwp_ident {
18
+ border-left:5px solid #B3B3B3;
19
+ padding: 0 10px;
20
+ }
21
+ .cpwp_ident p {
22
+ margin: 5px 0;
23
+ clear: both;
24
+ }
25
+ .cpwp_ident > label {
26
+ line-height: 2.75;
27
+ display: inline-block;
28
+ }
29
+ .cpwp_ident_top {
30
+ margin-top:-1em;
31
+ padding-top:1em;
32
+ }
33
+ .cpwp-sub-panel > p {
34
+ font-weight: lighter;
35
+ padding-bottom: 10px;
36
+ margin-bottom: 0px;
37
+ text-align: center;
38
+ }
39
+ .cpwp-sub-panel > .cpwp_ident {
40
+ background: #efefef;
41
+ color: #6A6A6A;
42
+ padding: 5px;
43
+ }
44
+ .category-widget-cont label.checkbox {
45
+ margin-left: -25px;
46
+ left: 25px;
47
+ position: relative;
48
+ }
49
+ .category-widget-cont [data-panel] + div label {
50
+ vertical-align: sub;
51
+ }
52
+ .category-widget-cont [data-panel="filter"] + div select,
53
+ .category-widget-cont input[type="number"],
54
+ .category-widget-cont input[type="text"],
55
+ .category-widget-cont input[type="range"] {
56
+ text-align:center;
57
+ float: right;
58
+ clear: both;
59
+ margin-top: 2px;
60
+ margin-bottom: 2px;
61
+ width: auto;
62
+ }
63
+ .category-widget-cont input[type="range"] {
64
+ padding-left: 0;
65
+ padding-right: 0;
66
+ }
67
+ .category-widget-cont .cpwp-right {
68
+ float: right;
69
+ }
70
+ .category-widget-cont input[type="number"] {
71
+ width:5em;
72
+ }
73
+ .category-widget-cont textarea {
74
+ width:100%;
75
+ margin-top: 5px;
76
+ }
77
+
78
+ .category-widget-cont .dashicons-editor-help {
79
+ vertical-align: sub;
80
+ }
81
+
82
+ .category-widget-cont h4 {
83
+ padding: 12px 15px;
84
+ cursor: pointer;
85
+ margin: 5px 0;
86
+ border: 1px solid #E5E5E5;
87
+ background: #f9f9f9;
88
+ }
89
+ .category-widget-cont h4:first-child {
90
+ margin-top: 10px;
91
+ }
92
+ .category-widget-cont h4:last-of-type {
93
+ margin-bottom: 10px;
94
+ }
95
+ .category-widget-cont h4:after {
96
+ float:right;
97
+ font-family: "dashicons";
98
+ content: '\f140';
99
+ -ms-transform: translate(-1px,1px);
100
+ -webkit-transform: translate(-1px,1px);
101
+ -moz-transform: translate(-1px,1px);
102
+ transform: translate(-1px,1px);
103
+ -ms-transition: all 600ms;
104
+ -webkit-transition: all 600ms;
105
+ -moz-transition: all 600ms;
106
+ transition: all 600ms;
107
+ }
108
+ .category-widget-cont h4.open:after {
109
+ -ms-transition: all 600ms;
110
+ -webkit-transition: all 600ms;
111
+ -moz-transition: all 600ms;
112
+ transition: all 600ms;
113
+ -ms-transform: rotate(180deg);
114
+ -webkit-transform: rotate(180deg);
115
+ -moz-transform: rotate(180deg);
116
+ transform: rotate(180deg);
117
+ }
118
+ .category-widget-cont > div {
119
+ display:none;
120
+ }
121
+ .category-widget-cont > div.open {
122
+ display:block;
123
+ }
124
+ .category-widget-cont th,
125
+ .category-widget-cont tr {
126
+ vertical-align: baseline;
127
+ text-align:start;
128
+ }
129
+
130
+ .categoryposts-template-help th {
131
+ text-align:start;
132
+ font-weight:bold;
133
+ }
134
+
135
+ .categoryposts-template-help td {
136
+ padding:2px;
137
+ }
138
+
139
+ .cat-post-template-help {display:none;}
140
+
141
+ .category-widget-cont .open-template-help {
142
+ border:0;
143
+ padding:0;
144
+ cursor: pointer;
145
+ }
146
+
147
+ .cat-post-thumb-change-size button.button {
148
+ line-height: normal;
149
+ height: auto;
150
+ padding: 2px 7px;
151
+ vertical-align: sub;
152
+ }
153
+ /* placeholder dropdown */
154
+ .categoryPosts-template {
155
+ float: left;
156
+ }
157
+ .cat-post-add_premade_templates {
158
+ position: relative;
159
+ }
160
+ .cat-post-add_premade_templates > button {
161
+ float: right;
162
+ }
163
+ .cat-post-add_premade_templates > .cpwp-placeholder-dropdown-menu {
164
+ display: none;
165
+ }
166
+ .cat-post-add_premade_templates > .cpwp-placeholder-dropdown-menu {
167
+ position: absolute;
168
+ right: 0;
169
+ top: 0;
170
+ z-index: 1000;
171
+ background-color: #fff;
172
+ padding: 15px 0;
173
+ border-radius: .25rem;
174
+ border: 1px solid rgba(0,0,0,.15);
175
+ white-space: nowrap;
176
+ }
177
+ .cat-post-add_premade_templates.customizer > .cpwp-placeholder-dropdown-menu {
178
+ transform: translate3d(56px, -277px, 0px);
179
+ }
180
+ .cat-post-add_premade_templates.admin-panel > .cpwp-placeholder-dropdown-menu {
181
+ transform: translate3d(322px, -4px, 0px);
182
+ }
183
+ .cpwp-placeholder-dropdown-menu > span {
184
+ display: block;
185
+ padding: 4px 24px;
186
+ }
187
+ .cpwp-placeholder-dropdown-menu > span:hover {
188
+ background-color: #f8f9fa;
189
+ cursor: pointer;
190
+ }