Display Posts Shortcode - Version 3.0.2

Version Description

Download this release

Release Info

Developer billerickson
Plugin Icon 128x128 Display Posts Shortcode
Version 3.0.2
Comparing to
See all releases

Code changes from version 3.0.1 to 3.0.2

Files changed (4) hide show
  1. CHANGELOG.md +10 -0
  2. README.md +40 -0
  3. display-posts-shortcode.php +893 -827
  4. readme.txt +20 -3
CHANGELOG.md CHANGED
@@ -1,6 +1,16 @@
1
  # Change Log
2
  All notable changes to this project will be documented in this file, formatted via [this recommendation](http://keepachangelog.com/).
3
 
 
 
 
 
 
 
 
 
 
 
4
  ### [3.0.1]
5
  #### Changed
6
  * Don't add empty parameters to the query, see #207
1
  # Change Log
2
  All notable changes to this project will be documented in this file, formatted via [this recommendation](http://keepachangelog.com/).
3
 
4
+ ### [3.0.2]
5
+ #### Added
6
+ * Added `pre_display_posts_shortcode_output` filter before shortcode runs, used for transient caching, see #210
7
+
8
+ #### Changed
9
+ * Updated plugin to pass coding standards, see #214
10
+ * Removed survey admin notice, see #213
11
+ * Don't display empty term list, see #208
12
+ * Improved spacing around excerpt dash, see #219
13
+
14
  ### [3.0.1]
15
  #### Changed
16
  * Don't add empty parameters to the query, see #207
README.md ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # [Display Posts](https://displayposts.com) #
2
+
3
+ ![Plugin Version](https://img.shields.io/wordpress/plugin/v/display-posts-shortcode.svg?style=flat-square) ![Total Downloads](https://img.shields.io/wordpress/plugin/dt/display-posts-shortcode.svg?style=flat-square) ![Plugin Rating](https://img.shields.io/wordpress/plugin/r/display-posts-shortcode.svg?style=flat-square) ![WordPress Compatibility](https://img.shields.io/wordpress/v/display-posts-shortcode.svg?style=flat-square) ![License](https://img.shields.io/badge/license-GPL--2.0%2B-red.svg?style=flat-square)
4
+
5
+ **Contributors:** billerickson
6
+ **Tags:** shortcode, pages, posts, page, query, display, list
7
+ **Requires at least:** 3.0
8
+ **Tested up to:** 5.2
9
+ **Stable tag:** 3.0.2
10
+ **License:** GPLv2 or later
11
+ **License URI:** http://www.gnu.org/licenses/gpl-2.0.html
12
+
13
+ ## Description
14
+
15
+ Display Posts is the simplest way to query and display content in WordPress.
16
+
17
+ Add the `[display-posts]` shortcode in a post or page. Use the [query parameters](https://displayposts.com/docs/#query-parameters) to filter the results by tag, category, post type, and more.
18
+
19
+ You can customize the output using the [display parameters](https://displayposts.com/docs/#display-parameters), or use a [template part](https://displayposts.com/2019/01/04/use-template-parts-to-match-your-themes-styling/) to match your theme exactly.
20
+
21
+ Developers can take it even further using the [available filters](https://displayposts.com/docs/filters/).
22
+
23
+ See [these tutorials](https://displayposts.com/tutorials/) for many examples of customization options.
24
+
25
+ ### Documentation
26
+ * [Parameters](https://displayposts.com/docs/parameters/)
27
+ * [Filters](https://displayposts.com/docs/filters/)
28
+ * [The Output Filter](https://displayposts.com/docs/the-output-filter/)
29
+ * [Template Parts](https://displayposts.com/2019/01/04/use-template-parts-to-match-your-themes-styling/)
30
+
31
+ ### Extensions
32
+ * [Display Posts – Pagination](https://github.com/billerickson/Display-Posts-Pagination) – Allow results of Display Posts to be paginated
33
+ * [Display Posts – Date View](https://wordpress.org/plugins/display-posts-date-view/) – Lets you break your content down by month or year.
34
+ * [Display Posts – Alpha View](https://github.com/billerickson/Display-Posts-Alpha-View) – Display an alphabetical listing of your content, broken down by letter
35
+ * [Display Posts – Transient Cache](https://github.com/billerickson/Display-Posts-Transient-Cache) – Cache the output using transients
36
+ * [Co-Authors Plus Addon](https://github.com/billerickson/dps-coauthor-addon) – multiple authors on posts
37
+ * [Columns Extension](https://github.com/billerickson/dps-columns-extension) – display posts in columns
38
+ * [DPS Exclude Sticky](https://github.com/billerickson/DPS-Exclude-Sticky) – exclude sticky posts unless specifically requested
39
+ * [DPS Pinch Zoomer](https://github.com/shazahm1/Display-Posts-Shortcode-Pinch-Zoomer) – adds support pinch zooming post images on mobile devices and mouse wheel zooming on desktops
40
+ * [Display Posts Shortcode Remote](https://github.com/shazahm1/Display-Posts-Shortcode-Remote) – display posts from a remote WordPress site utilizing the WP REST API.
display-posts-shortcode.php CHANGED
@@ -1,827 +1,893 @@
1
- <?php
2
- /**
3
- * Plugin Name: Display Posts
4
- * Plugin URI: https://displayposts.com
5
- * Description: Display a listing of posts using the [display-posts] shortcode
6
- * Version: 3.0.1
7
- * Author: Bill Erickson
8
- * Author URI: https://www.billerickson.net
9
- *
10
- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
11
- * General Public License version 2, as published by the Free Software Foundation. You may NOT assume
12
- * that you can use any other version of the GPL.
13
- *
14
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
15
- * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
- *
17
- * @package Display Posts
18
- * @version 3.0.1
19
- * @author Bill Erickson <bill@billerickson.net>
20
- * @copyright Copyright (c) 2011, Bill Erickson
21
- * @link http://www.billerickson.net/shortcode-to-display-posts/
22
- * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23
- */
24
-
25
-
26
- /**
27
- * To Customize, use the following filters:
28
- * @link https://displayposts.com/docs/filters/
29
- */
30
-
31
- // Create the shortcode
32
- add_shortcode( 'display-posts', 'be_display_posts_shortcode' );
33
- function be_display_posts_shortcode( $atts ) {
34
-
35
- // Original Attributes, for filters
36
- $original_atts = $atts;
37
-
38
- // Pull in shortcode attributes and set defaults
39
- $atts = shortcode_atts( array(
40
- 'author' => '',
41
- 'author_id' => '',
42
- 'category' => '',
43
- 'category_display' => '',
44
- 'category_id' => false,
45
- 'category_label' => 'Posted in: ',
46
- 'content_class' => 'content',
47
- 'date_format' => '(n/j/Y)',
48
- 'date' => '',
49
- 'date_column' => 'post_date',
50
- 'date_compare' => '=',
51
- 'date_query_before' => '',
52
- 'date_query_after' => '',
53
- 'date_query_column' => '',
54
- 'date_query_compare' => '',
55
- 'display_posts_off' => false,
56
- 'excerpt_length' => false,
57
- 'excerpt_more' => false,
58
- 'excerpt_more_link' => false,
59
- 'exclude' => false,
60
- 'exclude_current' => false,
61
- 'has_password' => null,
62
- 'id' => false,
63
- 'ignore_sticky_posts' => false,
64
- 'image_size' => false,
65
- 'include_author' => false,
66
- 'include_content' => false,
67
- 'include_date' => false,
68
- 'include_date_modified'=> false,
69
- 'include_excerpt' => false,
70
- 'include_excerpt_dash' => true,
71
- 'include_link' => true,
72
- 'include_title' => true,
73
- 'meta_key' => '',
74
- 'meta_value' => '',
75
- 'no_posts_message' => '',
76
- 'offset' => 0,
77
- 'order' => 'DESC',
78
- 'orderby' => 'date',
79
- 'post_parent' => false,
80
- 'post_parent__in' => false,
81
- 'post_parent__not_in' => false,
82
- 'post_status' => 'publish',
83
- 'post_type' => 'post',
84
- 'posts_per_page' => '10',
85
- 's' => false,
86
- 'tag' => '',
87
- 'tax_operator' => 'IN',
88
- 'tax_include_children' => true,
89
- 'tax_term' => false,
90
- 'taxonomy' => false,
91
- 'time' => '',
92
- 'title' => '',
93
- 'wrapper' => 'ul',
94
- 'wrapper_class' => 'display-posts-listing',
95
- 'wrapper_id' => false,
96
- ), $atts, 'display-posts' );
97
-
98
- // End early if shortcode should be turned off
99
- if( $atts['display_posts_off'] )
100
- return;
101
-
102
- $author = sanitize_text_field( $atts['author'] );
103
- $author_id = intval( $atts['author_id'] );
104
- $category = sanitize_text_field( $atts['category'] );
105
- $category_display = 'true' == $atts['category_display'] ? 'category' : sanitize_text_field( $atts['category_display'] );
106
- $category_id = intval( $atts['category_id'] );
107
- $category_label = sanitize_text_field( $atts['category_label'] );
108
- $content_class = array_map( 'sanitize_html_class', ( explode( ' ', $atts['content_class'] ) ) );
109
- $date_format = sanitize_text_field( $atts['date_format'] );
110
- $date = sanitize_text_field( $atts['date'] );
111
- $date_column = sanitize_text_field( $atts['date_column'] );
112
- $date_compare = sanitize_text_field( $atts['date_compare'] );
113
- $date_query_before = sanitize_text_field( $atts['date_query_before'] );
114
- $date_query_after = sanitize_text_field( $atts['date_query_after'] );
115
- $date_query_column = sanitize_text_field( $atts['date_query_column'] );
116
- $date_query_compare = sanitize_text_field( $atts['date_query_compare'] );
117
- $excerpt_length = intval( $atts['excerpt_length'] );
118
- $excerpt_more = sanitize_text_field( $atts['excerpt_more'] );
119
- $excerpt_more_link = filter_var( $atts['excerpt_more_link'], FILTER_VALIDATE_BOOLEAN );
120
- $exclude = $atts['exclude']; // Sanitized later as an array of integers
121
- $exclude_current = filter_var( $atts['exclude_current'], FILTER_VALIDATE_BOOLEAN );
122
- $has_password = null !== $atts['has_password'] ? filter_var( $atts['has_password'], FILTER_VALIDATE_BOOLEAN ) : null;
123
- $id = $atts['id']; // Sanitized later as an array of integers
124
- $ignore_sticky_posts = filter_var( $atts['ignore_sticky_posts'], FILTER_VALIDATE_BOOLEAN );
125
- $image_size = sanitize_key( $atts['image_size'] );
126
- $include_title = filter_var( $atts['include_title'], FILTER_VALIDATE_BOOLEAN );
127
- $include_author = filter_var( $atts['include_author'], FILTER_VALIDATE_BOOLEAN );
128
- $include_content = filter_var( $atts['include_content'], FILTER_VALIDATE_BOOLEAN );
129
- $include_date = filter_var( $atts['include_date'], FILTER_VALIDATE_BOOLEAN );
130
- $include_date_modified= filter_var( $atts['include_date_modified'], FILTER_VALIDATE_BOOLEAN );
131
- $include_excerpt = filter_var( $atts['include_excerpt'], FILTER_VALIDATE_BOOLEAN );
132
- $include_excerpt_dash = filter_var( $atts['include_excerpt_dash'], FILTER_VALIDATE_BOOLEAN );
133
- $include_link = filter_var( $atts['include_link'], FILTER_VALIDATE_BOOLEAN );
134
- $meta_key = sanitize_text_field( $atts['meta_key'] );
135
- $meta_value = sanitize_text_field( $atts['meta_value'] );
136
- $no_posts_message = sanitize_text_field( $atts['no_posts_message'] );
137
- $offset = intval( $atts['offset'] );
138
- $order = sanitize_key( $atts['order'] );
139
- $orderby = sanitize_key( $atts['orderby'] );
140
- $post_parent = $atts['post_parent']; // Validated later, after check for 'current'
141
- $post_parent__in = $atts['post_parent__in'];
142
- $post_parent__not_in = $atts['post_parent__not_in'];
143
- $post_status = $atts['post_status']; // Validated later as one of a few values
144
- $post_type = sanitize_text_field( $atts['post_type'] );
145
- $posts_per_page = intval( $atts['posts_per_page'] );
146
- $s = sanitize_text_field( $atts['s'] );
147
- $tag = sanitize_text_field( $atts['tag'] );
148
- $tax_operator = $atts['tax_operator']; // Validated later as one of a few values
149
- $tax_include_children = filter_var( $atts['tax_include_children'], FILTER_VALIDATE_BOOLEAN );
150
- $tax_term = sanitize_text_field( $atts['tax_term'] );
151
- $taxonomy = sanitize_key( $atts['taxonomy'] );
152
- $time = sanitize_text_field( $atts['time'] );
153
- $shortcode_title = sanitize_text_field( $atts['title'] );
154
- $wrapper = sanitize_text_field( $atts['wrapper'] );
155
- $wrapper_class = array_map( 'sanitize_html_class', ( explode( ' ', $atts['wrapper_class'] ) ) );
156
-
157
- if( !empty( $wrapper_class ) )
158
- $wrapper_class = ' class="' . implode( ' ', $wrapper_class ) . '"';
159
- $wrapper_id = sanitize_html_class( $atts['wrapper_id'] );
160
- if( !empty( $wrapper_id ) )
161
- $wrapper_id = ' id="' . $wrapper_id . '"';
162
-
163
- // Set up initial query for post
164
- $args = array(
165
- 'perm' => 'readable'
166
- );
167
-
168
- // Add args if they aren't empty
169
- if( !empty( $category_id ) )
170
- $args['cat'] = $category_id;
171
- if( !empty( $category ) )
172
- $args['category_name'] = $category;
173
- if( !empty( $order ) )
174
- $args['order'] = $order;
175
- if( !empty( $orderby ) )
176
- $args['orderby'] = $orderby;
177
- if( !empty( $post_type ) )
178
- $args['post_type'] = be_dps_explode( $post_type );
179
- if( !empty( $posts_per_page ) )
180
- $args['posts_per_page'] = $posts_per_page;
181
- if( !empty( $s ) )
182
- $args['s'] = $s;
183
- if( !empty( $tag ) )
184
- $args['tag'] = $tag;
185
-
186
- // Date query.
187
- if ( ! empty( $date ) || ! empty( $time ) || ! empty( $date_query_after ) || ! empty( $date_query_before ) ) {
188
- $initial_date_query = $date_query_top_lvl = array();
189
-
190
- $valid_date_columns = array(
191
- 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt',
192
- 'comment_date', 'comment_date_gmt'
193
- );
194
-
195
- $valid_compare_ops = array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' );
196
-
197
- // Sanitize and add date segments.
198
- $dates = be_sanitize_date_time( $date );
199
- if ( ! empty( $dates ) ) {
200
- if ( is_string( $dates ) ) {
201
- $timestamp = strtotime( $dates );
202
- $dates = array(
203
- 'year' => date( 'Y', $timestamp ),
204
- 'month' => date( 'm', $timestamp ),
205
- 'day' => date( 'd', $timestamp ),
206
- );
207
- }
208
- foreach ( $dates as $arg => $segment ) {
209
- $initial_date_query[ $arg ] = $segment;
210
- }
211
- }
212
-
213
- // Sanitize and add time segments.
214
- $times = be_sanitize_date_time( $time, 'time' );
215
- if ( ! empty( $times ) ) {
216
- foreach ( $times as $arg => $segment ) {
217
- $initial_date_query[ $arg ] = $segment;
218
- }
219
- }
220
-
221
- // Date query 'before' argument.
222
- $before = be_sanitize_date_time( $date_query_before, 'date', true );
223
- if ( ! empty( $before ) ) {
224
- $initial_date_query['before'] = $before;
225
- }
226
-
227
- // Date query 'after' argument.
228
- $after = be_sanitize_date_time( $date_query_after, 'date', true );
229
- if ( ! empty( $after ) ) {
230
- $initial_date_query['after'] = $after;
231
- }
232
-
233
- // Date query 'column' argument.
234
- if ( ! empty( $date_query_column ) && in_array( $date_query_column, $valid_date_columns ) ) {
235
- $initial_date_query['column'] = $date_query_column;
236
- }
237
-
238
- // Date query 'compare' argument.
239
- if ( ! empty( $date_query_compare ) && in_array( $date_query_compare, $valid_compare_ops ) ) {
240
- $initial_date_query['compare'] = $date_query_compare;
241
- }
242
-
243
- //
244
- // Top-level date_query arguments. Only valid arguments will be added.
245
- //
246
-
247
- // 'column' argument.
248
- if ( ! empty( $date_column ) && in_array( $date_column, $valid_date_columns ) ) {
249
- $date_query_top_lvl['column'] = $date_column;
250
- }
251
-
252
- // 'compare' argument.
253
- if ( ! empty( $date_compare ) && in_array( $date_compare, $valid_compare_ops ) ) {
254
- $date_query_top_lvl['compare'] = $date_compare;
255
- }
256
-
257
- // Bring in the initial date query.
258
- if ( ! empty( $initial_date_query ) ) {
259
- $date_query_top_lvl[] = $initial_date_query;
260
- }
261
-
262
- // Date queries.
263
- $args['date_query'] = $date_query_top_lvl;
264
- }
265
-
266
- // Ignore Sticky Posts
267
- if( $ignore_sticky_posts )
268
- $args['ignore_sticky_posts'] = true;
269
-
270
- // Password protected content
271
- if( null !== $has_password )
272
- $args['has_password'] = $has_password;
273
-
274
- // Meta key (for ordering)
275
- if( !empty( $meta_key ) )
276
- $args['meta_key'] = $meta_key;
277
-
278
- // Meta value (for simple meta queries)
279
- if( !empty( $meta_value ) )
280
- $args['meta_value'] = $meta_value;
281
-
282
- // If Post IDs
283
- if( $id ) {
284
- $posts_in = array_map( 'intval', be_dps_explode( $id ) );
285
- $args['post__in'] = $posts_in;
286
- }
287
-
288
- // If Exclude
289
- $post__not_in = array();
290
- if( !empty( $exclude ) ) {
291
- $post__not_in = array_map( 'intval', be_dps_explode( $exclude ) );
292
- }
293
- if( is_singular() && $exclude_current ) {
294
- $post__not_in[] = get_the_ID();
295
- }
296
- if( !empty( $post__not_in ) ) {
297
- $args['post__not_in'] = $post__not_in;
298
- }
299
-
300
- // Post Author
301
- if( !empty( $author ) ) {
302
- if( 'current' == $author && is_user_logged_in() )
303
- $args['author_name'] = wp_get_current_user()->user_login;
304
- elseif( 'current' == $author )
305
- $args['meta_key'] = 'dps_no_results';
306
- else
307
- $args['author_name'] = $author;
308
- } elseif( !empty( $author_id ) ) {
309
- $args['author'] = $author_id;
310
- }
311
-
312
- // Offset
313
- if( !empty( $offset ) )
314
- $args['offset'] = $offset;
315
-
316
- // Post Status
317
- $post_status = be_dps_explode( $post_status );
318
- $validated = array();
319
- $available = array( 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash', 'any' );
320
- foreach ( $post_status as $unvalidated )
321
- if ( in_array( $unvalidated, $available ) )
322
- $validated[] = $unvalidated;
323
- if( !empty( $validated ) )
324
- $args['post_status'] = $validated;
325
-
326
-
327
- // If taxonomy attributes, create a taxonomy query
328
- if ( !empty( $taxonomy ) && !empty( $tax_term ) ) {
329
-
330
- if( 'current' == $tax_term ) {
331
- global $post;
332
- $terms = wp_get_post_terms(get_the_ID(), $taxonomy);
333
- $tax_term = array();
334
- foreach ($terms as $term) {
335
- $tax_term[] = $term->slug;
336
- }
337
- }else{
338
- // Term string to array
339
- $tax_term = be_dps_explode( $tax_term );
340
- }
341
-
342
- // Validate operator
343
- if( !in_array( $tax_operator, array( 'IN', 'NOT IN', 'AND' ) ) )
344
- $tax_operator = 'IN';
345
-
346
- $tax_args = array(
347
- 'tax_query' => array(
348
- array(
349
- 'taxonomy' => $taxonomy,
350
- 'field' => 'slug',
351
- 'terms' => $tax_term,
352
- 'operator' => $tax_operator,
353
- 'include_children' => $tax_include_children,
354
- )
355
- )
356
- );
357
-
358
- // Check for multiple taxonomy queries
359
- $count = 2;
360
- $more_tax_queries = false;
361
- while(
362
- isset( $original_atts['taxonomy_' . $count] ) && !empty( $original_atts['taxonomy_' . $count] ) &&
363
- isset( $original_atts['tax_' . $count . '_term'] ) && !empty( $original_atts['tax_' . $count . '_term'] )
364
- ):
365
-
366
- // Sanitize values
367
- $more_tax_queries = true;
368
- $taxonomy = sanitize_key( $original_atts['taxonomy_' . $count] );
369
- $terms = be_dps_explode( sanitize_text_field( $original_atts['tax_' . $count . '_term'] ) );
370
- $tax_operator = isset( $original_atts['tax_' . $count . '_operator'] ) ? $original_atts['tax_' . $count . '_operator'] : 'IN';
371
- $tax_operator = in_array( $tax_operator, array( 'IN', 'NOT IN', 'AND' ) ) ? $tax_operator : 'IN';
372
- $tax_include_children = isset( $original_atts['tax_' . $count . '_include_children'] ) ? filter_var( $atts['tax_' . $count . '_include_children'], FILTER_VALIDATE_BOOLEAN ) : true;
373
-
374
- $tax_args['tax_query'][] = array(
375
- 'taxonomy' => $taxonomy,
376
- 'field' => 'slug',
377
- 'terms' => $terms,
378
- 'operator' => $tax_operator,
379
- 'include_children' => $tax_include_children,
380
- );
381
-
382
- $count++;
383
-
384
- endwhile;
385
-
386
- if( $more_tax_queries ):
387
- $tax_relation = 'AND';
388
- if( isset( $original_atts['tax_relation'] ) && in_array( $original_atts['tax_relation'], array( 'AND', 'OR' ) ) )
389
- $tax_relation = $original_atts['tax_relation'];
390
- $args['tax_query']['relation'] = $tax_relation;
391
- endif;
392
-
393
- $args = array_merge_recursive( $args, $tax_args );
394
- }
395
-
396
- // If post parent attribute, set up parent
397
- if( $post_parent !== false ) {
398
- if( 'current' == $post_parent ) {
399
- global $post;
400
- $post_parent = get_the_ID();
401
- }
402
- $args['post_parent'] = intval( $post_parent );
403
- }
404
-
405
- if( $post_parent__in !== false )
406
- $args['post_parent__in'] = array_map( 'intval', be_dps_explode( $atts['post_parent__in'] ) );
407
- if( $post_parent__not_in !== false )
408
- $args['post_parent__not_in'] = array_map( 'intval', be_dps_explode( $atts['post_parent__in'] ) );
409
-
410
- // Set up html elements used to wrap the posts.
411
- // Default is ul/li, but can also be ol/li and div/div
412
- $wrapper_options = array( 'ul', 'ol', 'div' );
413
- if( ! in_array( $wrapper, $wrapper_options ) )
414
- $wrapper = 'ul';
415
- $inner_wrapper = 'div' == $wrapper ? 'div' : 'li';
416
-
417
- /**
418
- * Filter the arguments passed to WP_Query.
419
- *
420
- * @since 1.7
421
- *
422
- * @param array $args Parsed arguments to pass to WP_Query.
423
- * @param array $original_atts Original attributes passed to the shortcode.
424
- */
425
- global $dps_listing;
426
- $dps_listing = new WP_Query( apply_filters( 'display_posts_shortcode_args', $args, $original_atts ) );
427
- if ( ! $dps_listing->have_posts() ) {
428
- /**
429
- * Filter content to display if no posts match the current query.
430
- *
431
- * @since 1.8
432
- *
433
- * @param string $no_posts_message Content to display, returned via {@see wpautop()}.
434
- */
435
- return apply_filters( 'display_posts_shortcode_no_results', wpautop( $no_posts_message ) );
436
- }
437
-
438
- $inner = '';
439
- while ( $dps_listing->have_posts() ): $dps_listing->the_post(); global $post;
440
-
441
- $image = $date = $author = $excerpt = $content = '';
442
-
443
- if ( $include_title && $include_link ) {
444
- /** This filter is documented in wp-includes/link-template.php */
445
- $title = '<a class="title" href="' . apply_filters( 'the_permalink', get_permalink() ) . '">' . get_the_title() . '</a>';
446
-
447
- } elseif( $include_title ) {
448
- $title = '<span class="title">' . get_the_title() . '</span>';
449
-
450
- } else {
451
- $title = '';
452
- }
453
-
454
- if ( $image_size && has_post_thumbnail() && $include_link ) {
455
- $image = '<a class="image" href="' . get_permalink() . '">' . get_the_post_thumbnail( get_the_ID(), $image_size ) . '</a> ';
456
-
457
- } elseif( $image_size && has_post_thumbnail() ) {
458
- $image = '<span class="image">' . get_the_post_thumbnail( get_the_ID(), $image_size ) . '</span> ';
459
-
460
- }
461
-
462
- if ( $include_date ) {
463
- $date = 'relative' == $date_format ? be_dps_relative_date( get_the_date( 'U' ) ) : get_the_date( $date_format );
464
- } elseif ( $include_date_modified ) {
465
- $date = 'relative' == $date_format ? be_dps_relative_date( get_the_modified_time( 'U' ) ) : get_the_modified_date( $date_format );
466
- }
467
- if( !empty( $date ) ) {
468
- $date = ' <span class="date">' . $date . '</span>';
469
- }
470
-
471
- if( $include_author )
472
- /**
473
- * Filter the HTML markup to display author information for the current post.
474
- *
475
- * @since Unknown
476
- *
477
- * @param string $author_output HTML markup to display author information.
478
- */
479
- $author = apply_filters( 'display_posts_shortcode_author', ' <span class="author">by ' . get_the_author() . '</span>', $original_atts );
480
-
481
- if ( $include_excerpt ) {
482
-
483
- // Custom build excerpt based on shortcode parameters
484
- if( $excerpt_length || $excerpt_more || $excerpt_more_link ) {
485
-
486
- $length = $excerpt_length ? $excerpt_length : apply_filters( 'excerpt_length', 55 );
487
- $more = $excerpt_more ? $excerpt_more : apply_filters( 'excerpt_more', '' );
488
- $more = $excerpt_more_link ? ' <a class="excerpt-more" href="' . get_permalink() . '">' . $more . '</a>' : ' <span class="excerpt-more">' . $more . '</span>';
489
-
490
- if( has_excerpt() && apply_filters( 'display_posts_shortcode_full_manual_excerpt', false ) ) {
491
- $excerpt = $post->post_excerpt . $more;
492
- } elseif( has_excerpt() ) {
493
- $excerpt = wp_trim_words( strip_shortcodes( $post->post_excerpt ), $length ) . $more;
494
- } else {
495
- $excerpt = wp_trim_words( strip_shortcodes( $post->post_content ), $length ) . $more;
496
- }
497
-
498
-
499
- // Use default, can customize with WP filters
500
- } else {
501
- $excerpt = get_the_excerpt();
502
- }
503
-
504
- $excerpt = '<span class="excerpt">' . $excerpt . '</span>';
505
- if( $include_excerpt_dash ) {
506
- $excerpt = '<span class="excerpt-dash">-</span> ' . $excerpt;
507
- }
508
-
509
- }
510
-
511
- if( $include_content ) {
512
- add_filter( 'shortcode_atts_display-posts', 'be_display_posts_off', 10, 3 );
513
- /** This filter is documented in wp-includes/post-template.php */
514
- $content = '<div class="' . implode( ' ', $content_class ) . '">' . apply_filters( 'the_content', get_the_content() ) . '</div>';
515
- remove_filter( 'shortcode_atts_display-posts', 'be_display_posts_off', 10, 3 );
516
- }
517
-
518
- // Display categories the post is in
519
- $category_display_text = '';
520
- if( $category_display && is_object_in_taxonomy( get_post_type(), $category_display ) ) {
521
- $terms = get_the_terms( get_the_ID(), $category_display );
522
- $term_output = array();
523
- foreach( $terms as $term )
524
- $term_output[] = '<a href="' . get_term_link( $term, $category_display ) . '">' . $term->name . '</a>';
525
- $category_display_text = ' <span class="category-display"><span class="category-display-label">' . $category_label . '</span> ' . implode( ', ', $term_output ) . '</span>';
526
-
527
- /**
528
- * Filter the list of categories attached to the current post.
529
- *
530
- * @since 2.4.2
531
- *
532
- * @param string $category_display Current Category Display text
533
- */
534
- $category_display_text = apply_filters( 'display_posts_shortcode_category_display', $category_display_text, $terms, $category_display, $original_atts );
535
-
536
- }
537
-
538
- $class = array( 'listing-item' );
539
-
540
- /**
541
- * Filter the post classes for the inner wrapper element of the current post.
542
- *
543
- * @since 2.2
544
- *
545
- * @param array $class Post classes.
546
- * @param WP_Post $post Post object.
547
- * @param WP_Query $dps_listing WP_Query object for the posts listing.
548
- * @param array $original_atts Original attributes passed to the shortcode.
549
- */
550
- $class = array_map( 'sanitize_html_class', apply_filters( 'display_posts_shortcode_post_class', $class, $post, $dps_listing, $original_atts ) );
551
- $output = '<' . $inner_wrapper . ' class="' . implode( ' ', $class ) . '">' . $image . $title . $date . $author . $category_display_text . $excerpt . $content . '</' . $inner_wrapper . '>';
552
-
553
- /**
554
- * Filter the HTML markup for output via the shortcode.
555
- *
556
- * @since 0.1.5
557
- *
558
- * @param string $output The shortcode's HTML output.
559
- * @param array $original_atts Original attributes passed to the shortcode.
560
- * @param string $image HTML markup for the post's featured image element.
561
- * @param string $title HTML markup for the post's title element.
562
- * @param string $date HTML markup for the post's date element.
563
- * @param string $excerpt HTML markup for the post's excerpt element.
564
- * @param string $inner_wrapper Type of container to use for the post's inner wrapper element.
565
- * @param string $content The post's content.
566
- * @param string $class Space-separated list of post classes to supply to the $inner_wrapper element.
567
- * @param string $author HTML markup for the post's author.
568
- * @param string $category_display_text
569
- */
570
- $inner .= apply_filters( 'display_posts_shortcode_output', $output, $original_atts, $image, $title, $date, $excerpt, $inner_wrapper, $content, $class, $author, $category_display_text );
571
-
572
- endwhile; wp_reset_postdata();
573
-
574
- /**
575
- * Filter the shortcode output's opening outer wrapper element.
576
- *
577
- * @since 1.7
578
- *
579
- * @param string $wrapper_open HTML markup for the opening outer wrapper element.
580
- * @param array $original_atts Original attributes passed to the shortcode.
581
- * @param object $dps_listing, WP Query object
582
- */
583
- $open = apply_filters( 'display_posts_shortcode_wrapper_open', '<' . $wrapper . $wrapper_class . $wrapper_id . '>', $original_atts, $dps_listing );
584
-
585
- /**
586
- * Filter the shortcode output's closing outer wrapper element.
587
- *
588
- * @since 1.7
589
- *
590
- * @param string $wrapper_close HTML markup for the closing outer wrapper element.
591
- * @param array $original_atts Original attributes passed to the shortcode.
592
- * @param object $dps_listing, WP Query object
593
- */
594
- $close = apply_filters( 'display_posts_shortcode_wrapper_close', '</' . $wrapper . '>', $original_atts, $dps_listing );
595
-
596
- $return = '';
597
-
598
- if( $shortcode_title ) {
599
-
600
- /**
601
- * Filter the shortcode output title tag element.
602
- *
603
- * @since 2.3
604
- *
605
- * @param string $tag Type of element to use for the output title tag. Default 'h2'.
606
- * @param array $original_atts Original attributes passed to the shortcode.
607
- */
608
- $title_tag = apply_filters( 'display_posts_shortcode_title_tag', 'h2', $original_atts );
609
-
610
- $return .= '<' . $title_tag . ' class="display-posts-title">' . $shortcode_title . '</' . $title_tag . '>' . "\n";
611
- }
612
-
613
- $return .= $open . $inner . $close;
614
-
615
- return $return;
616
- }
617
-
618
- /**
619
- * Sanitize the segments of a given date or time for a date query.
620
- *
621
- * Accepts times entered in the 'HH:MM:SS' or 'HH:MM' formats, and dates
622
- * entered in the 'YYYY-MM-DD' format.
623
- *
624
- * @param string $date_time Date or time string to sanitize the parts of.
625
- * @param string $type Optional. Type of value to sanitize. Accepts
626
- * 'date' or 'time'. Default 'date'.
627
- * @param bool $accepts_string Optional. Whether the return value accepts a string.
628
- * Default false.
629
- * @return array|string Array of valid date or time segments, a timestamp, otherwise
630
- * an empty array.
631
- */
632
- function be_sanitize_date_time( $date_time, $type = 'date', $accepts_string = false ) {
633
- if ( empty( $date_time ) || ! in_array( $type, array( 'date', 'time' ) ) ) {
634
- return array();
635
- }
636
-
637
- $segments = array();
638
-
639
- /*
640
- * If $date_time is not a strictly-formatted date or time, attempt to salvage it with
641
- * as strototime()-ready string. This is supported by the 'date', 'date_query_before',
642
- * and 'date_query_after' attributes.
643
- */
644
- if (
645
- true === $accepts_string
646
- && ( false !== strpos( $date_time, ' ' ) || false === strpos( $date_time, '-' ) )
647
- ) {
648
- if ( false !== $timestamp = strtotime( $date_time ) ) {
649
- return $date_time;
650
- }
651
- }
652
-
653
- $parts = array_map( 'absint', explode( 'date' == $type ? '-' : ':', $date_time ) );
654
-
655
- // Date.
656
- if ( 'date' == $type ) {
657
- // Defaults to 2001 for years, January for months, and 1 for days.
658
- $year = $month = $day = 1;
659
-
660
- if ( count( $parts ) >= 3 ) {
661
- list( $year, $month, $day ) = $parts;
662
-
663
- $year = ( $year >= 1 && $year <= 9999 ) ? $year : 1;
664
- $month = ( $month >= 1 && $month <= 12 ) ? $month : 1;
665
- $day = ( $day >= 1 && $day <= 31 ) ? $day : 1;
666
- }
667
-
668
- $segments = array(
669
- 'year' => $year,
670
- 'month' => $month,
671
- 'day' => $day
672
- );
673
-
674
- // Time.
675
- } elseif ( 'time' == $type ) {
676
- // Defaults to 0 for all segments.
677
- $hour = $minute = $second = 0;
678
-
679
- switch( count( $parts ) ) {
680
- case 3 :
681
- list( $hour, $minute, $second ) = $parts;
682
- $hour = ( $hour >= 0 && $hour <= 23 ) ? $hour : 0;
683
- $minute = ( $minute >= 0 && $minute <= 60 ) ? $minute : 0;
684
- $second = ( $second >= 0 && $second <= 60 ) ? $second : 0;
685
- break;
686
- case 2 :
687
- list( $hour, $minute ) = $parts;
688
- $hour = ( $hour >= 0 && $hour <= 23 ) ? $hour : 0;
689
- $minute = ( $minute >= 0 && $minute <= 60 ) ? $minute : 0;
690
- break;
691
- default : break;
692
- }
693
-
694
- $segments = array(
695
- 'hour' => $hour,
696
- 'minute' => $minute,
697
- 'second' => $second
698
- );
699
- }
700
-
701
- /**
702
- * Filter the sanitized segments for the given date or time string.
703
- *
704
- * @since 2.5
705
- *
706
- * @param array $segments Array of sanitized date or time segments, e.g. hour, minute, second,
707
- * or year, month, day, depending on the value of the $type parameter.
708
- * @param string $date_time Date or time string. Dates are formatted 'YYYY-MM-DD', and times are
709
- * formatted 'HH:MM:SS' or 'HH:MM'.
710
- * @param string $type Type of string to sanitize. Can be either 'date' or 'time'.
711
- */
712
- return apply_filters( 'display_posts_shortcode_sanitized_segments', $segments, $date_time, $type );
713
- }
714
-
715
- /**
716
- * Turn off display posts shortcode
717
- * If display full post content, any uses of [display-posts] are disabled
718
- *
719
- * @param array $out, returned shortcode values
720
- * @param array $pairs, list of supported attributes and their defaults
721
- * @param array $atts, original shortcode attributes
722
- * @return array $out
723
- */
724
- function be_display_posts_off( $out, $pairs, $atts ) {
725
- /**
726
- * Filter whether to disable the display-posts shortcode.
727
- *
728
- * The function and filter were added for backward-compatibility with
729
- * 2.3 behavior in certain circumstances.
730
- *
731
- * @since 2.4
732
- *
733
- * @param bool $disable Whether to disable the display-posts shortcode. Default true.
734
- */
735
- $out['display_posts_off'] = apply_filters( 'display_posts_shortcode_inception_override', true );
736
- return $out;
737
- }
738
-
739
- /**
740
- * Explode list using "," and ", "
741
- *
742
- */
743
- function be_dps_explode( $string = '' ) {
744
-
745
- $string = str_replace( ', ', ',', $string );
746
- return explode( ',', $string );
747
- }
748
-
749
- /**
750
- * Relative date
751
- *
752
- */
753
- function be_dps_relative_date( $date ) {
754
- return human_time_diff( $date ) . ' ' . __( 'ago', 'display-posts' );
755
- }
756
-
757
- /**
758
- * Survey admin notice
759
- *
760
- */
761
- function be_dps_survey_admin_notice() {
762
-
763
- if( ! is_super_admin() )
764
- return;
765
-
766
-
767
- $survey = get_option( 'display_posts_survey' );
768
- $time = time();
769
- $load = false;
770
- if ( ! $survey ) {
771
- $survey = array(
772
- 'time' => $time,
773
- 'dismissed' => false,
774
- );
775
- update_option( 'display_posts_survey', $survey );
776
- } else {
777
- // Check if it has been dismissed or not.
778
- if ( ( isset( $survey['dismissed'] ) && ! $survey['dismissed'] ) ) {
779
- $load = true;
780
- }
781
- }
782
- // If we cannot load, return early.
783
- if ( ! $load ) {
784
- return;
785
- }
786
-
787
-
788
- ?>
789
- <div class="notice notice-info is-dismissible display-posts-survey-notice">
790
- <p><?php esc_html_e( 'Thank you so much for using Display Posts! Could you please do me a BIG favor and answer four quick questions on how I can improve the plugin for you?', 'display-posts' ); ?></p>
791
- <p><?php esc_html_e( 'In 2019 I\'ll be working on new features, including the possibility of a premium version. As a valued Display Posts user, your feedback is important and appreciated!', 'display-posts' ); ?></p>
792
- <p><strong><?php echo wp_kses( __( '~ Bill Erickson<br>Developer of Display Posts', 'display-posts' ), array( 'br' => array() ) ); ?></strong></p>
793
- <p>
794
- <a href="https://displayposts.com/user-survey?utm_source=displaypostsplugin&utm_medium=link&utm_campaign=survey_notice" class="display-posts-dismiss-survey-notice display-posts-survey-out" target="_blank" rel="noopener"><?php esc_html_e( 'Yes, I will!', 'display-posts' ); ?></a><br>
795
- <a href="#" class="display-posts-dismiss-survey-notice" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Nope, maybe later', 'display-posts' ); ?></a><br>
796
- <a href="#" class="display-posts-dismiss-survey-notice" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'I already did', 'display-posts' ); ?></a>
797
- </p>
798
- </div>
799
- <script type="text/javascript">
800
- jQuery( document ).ready( function ( $ ) {
801
- $( document ).on( 'click', '.display-posts-dismiss-survey-notice, .display-posts-survey-notice button', function ( event ) {
802
- if ( ! $( this ).hasClass( 'display-posts-survey-out' ) ) {
803
- event.preventDefault();
804
- }
805
- $.post( ajaxurl, {
806
- action: 'display_posts_survey_dismiss'
807
- } );
808
- $( '.display-posts-survey-notice' ).remove();
809
- } );
810
- } );
811
- </script>
812
- <?php
813
- }
814
- add_action( 'admin_notices', 'be_dps_survey_admin_notice' );
815
-
816
- /**
817
- * Dismiss the admin notice
818
- *
819
- */
820
- function be_dps_survey_dismiss() {
821
-
822
- $survey = get_option( 'display_posts_survey', array() );
823
- $survey['time'] = time();
824
- $survey['dismissed'] = true;
825
- update_option( 'display_posts_survey', $survey );
826
- }
827
- add_action( 'wp_ajax_display_posts_survey_dismiss', 'be_dps_survey_dismiss' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: Display Posts
4
+ * Plugin URI: https://displayposts.com
5
+ * Description: Display a listing of posts using the [display-posts] shortcode
6
+ * Version: 3.0.2
7
+ * Author: Bill Erickson
8
+ * Author URI: https://www.billerickson.net
9
+ *
10
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
11
+ * General Public License version 2, as published by the Free Software Foundation. You may NOT assume
12
+ * that you can use any other version of the GPL.
13
+ *
14
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
15
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
+ *
17
+ * @package Display Posts
18
+ * @version 3.0.2
19
+ * @author Bill Erickson <bill@billerickson.net>
20
+ * @copyright Copyright (c) 2011, Bill Erickson
21
+ * @link https://displayposts.com
22
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23
+ */
24
+
25
+ add_shortcode( 'display-posts', 'be_display_posts_shortcode' );
26
+ /**
27
+ * Callback for the display-posts shortcode.
28
+ *
29
+ * To customize, use the following filters: https://displayposts.com/docs/filters/
30
+ *
31
+ * @param array $atts Shortcode attributes.
32
+ * @return string
33
+ */
34
+ function be_display_posts_shortcode( $atts ) {
35
+
36
+ /**
37
+ * Short circuit filter.
38
+ *
39
+ * Use this filter to return from this function immediately, with the return of the filter callback.
40
+ *
41
+ * @since 3.0.2
42
+ *
43
+ * @param bool $short_circuit False to allow this function to continue, anything else to return that value.
44
+ * @param array $atts Shortcode attributes.
45
+ */
46
+ $output = apply_filters( 'pre_display_posts_shortcode_output', false, $atts );
47
+ if ( false !== $output ) {
48
+ return $output;
49
+ }
50
+
51
+ // Original attributes, for filters.
52
+ $original_atts = $atts;
53
+
54
+ // Pull in shortcode attributes and set defaults.
55
+ $atts = shortcode_atts(
56
+ array(
57
+ 'author' => '',
58
+ 'author_id' => '',
59
+ 'category' => '',
60
+ 'category_display' => '',
61
+ 'category_id' => false,
62
+ 'category_label' => 'Posted in: ',
63
+ 'content_class' => 'content',
64
+ 'date_format' => '(n/j/Y)',
65
+ 'date' => '',
66
+ 'date_column' => 'post_date',
67
+ 'date_compare' => '=',
68
+ 'date_query_before' => '',
69
+ 'date_query_after' => '',
70
+ 'date_query_column' => '',
71
+ 'date_query_compare' => '',
72
+ 'display_posts_off' => false,
73
+ 'excerpt_length' => false,
74
+ 'excerpt_more' => false,
75
+ 'excerpt_more_link' => false,
76
+ 'exclude' => false,
77
+ 'exclude_current' => false,
78
+ 'has_password' => null,
79
+ 'id' => false,
80
+ 'ignore_sticky_posts' => false,
81
+ 'image_size' => false,
82
+ 'include_author' => false,
83
+ 'include_content' => false,
84
+ 'include_date' => false,
85
+ 'include_date_modified' => false,
86
+ 'include_excerpt' => false,
87
+ 'include_excerpt_dash' => true,
88
+ 'include_link' => true,
89
+ 'include_title' => true,
90
+ 'meta_key' => '', // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key
91
+ 'meta_value' => '', // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value
92
+ 'no_posts_message' => '',
93
+ 'offset' => 0,
94
+ 'order' => 'DESC',
95
+ 'orderby' => 'date',
96
+ 'post_parent' => false,
97
+ 'post_parent__in' => false,
98
+ 'post_parent__not_in' => false,
99
+ 'post_status' => 'publish',
100
+ 'post_type' => 'post',
101
+ 'posts_per_page' => '10',
102
+ 's' => false,
103
+ 'tag' => '',
104
+ 'tax_operator' => 'IN',
105
+ 'tax_include_children' => true,
106
+ 'tax_term' => false,
107
+ 'taxonomy' => false,
108
+ 'time' => '',
109
+ 'title' => '',
110
+ 'wrapper' => 'ul',
111
+ 'wrapper_class' => 'display-posts-listing',
112
+ 'wrapper_id' => false,
113
+ ),
114
+ $atts,
115
+ 'display-posts'
116
+ );
117
+
118
+ // End early if shortcode should be turned off.
119
+ if ( $atts['display_posts_off'] ) {
120
+ return;
121
+ }
122
+
123
+ $author = sanitize_text_field( $atts['author'] );
124
+ $author_id = (int) $atts['author_id'];
125
+ $category = sanitize_text_field( $atts['category'] );
126
+ $category_display = 'true' === $atts['category_display'] ? 'category' : sanitize_text_field( $atts['category_display'] );
127
+ $category_id = (int) $atts['category_id'];
128
+ $category_label = sanitize_text_field( $atts['category_label'] );
129
+ $content_class = array_map( 'sanitize_html_class', explode( ' ', $atts['content_class'] ) );
130
+ $date_format = sanitize_text_field( $atts['date_format'] );
131
+ $date = sanitize_text_field( $atts['date'] );
132
+ $date_column = sanitize_text_field( $atts['date_column'] );
133
+ $date_compare = sanitize_text_field( $atts['date_compare'] );
134
+ $date_query_before = sanitize_text_field( $atts['date_query_before'] );
135
+ $date_query_after = sanitize_text_field( $atts['date_query_after'] );
136
+ $date_query_column = sanitize_text_field( $atts['date_query_column'] );
137
+ $date_query_compare = sanitize_text_field( $atts['date_query_compare'] );
138
+ $excerpt_length = (int) $atts['excerpt_length'];
139
+ $excerpt_more = sanitize_text_field( $atts['excerpt_more'] );
140
+ $excerpt_more_link = filter_var( $atts['excerpt_more_link'], FILTER_VALIDATE_BOOLEAN );
141
+ $exclude = $atts['exclude']; // Sanitized later as an array of integers.
142
+ $exclude_current = filter_var( $atts['exclude_current'], FILTER_VALIDATE_BOOLEAN );
143
+ $has_password = null !== $atts['has_password'] ? filter_var( $atts['has_password'], FILTER_VALIDATE_BOOLEAN ) : null;
144
+ $id = $atts['id']; // Sanitized later as an array of integers.
145
+ $ignore_sticky_posts = filter_var( $atts['ignore_sticky_posts'], FILTER_VALIDATE_BOOLEAN );
146
+ $image_size = sanitize_key( $atts['image_size'] );
147
+ $include_title = filter_var( $atts['include_title'], FILTER_VALIDATE_BOOLEAN );
148
+ $include_author = filter_var( $atts['include_author'], FILTER_VALIDATE_BOOLEAN );
149
+ $include_content = filter_var( $atts['include_content'], FILTER_VALIDATE_BOOLEAN );
150
+ $include_date = filter_var( $atts['include_date'], FILTER_VALIDATE_BOOLEAN );
151
+ $include_date_modified = filter_var( $atts['include_date_modified'], FILTER_VALIDATE_BOOLEAN );
152
+ $include_excerpt = filter_var( $atts['include_excerpt'], FILTER_VALIDATE_BOOLEAN );
153
+ $include_excerpt_dash = filter_var( $atts['include_excerpt_dash'], FILTER_VALIDATE_BOOLEAN );
154
+ $include_link = filter_var( $atts['include_link'], FILTER_VALIDATE_BOOLEAN );
155
+ $meta_key = sanitize_text_field( $atts['meta_key'] );
156
+ $meta_value = sanitize_text_field( $atts['meta_value'] );
157
+ $no_posts_message = sanitize_text_field( $atts['no_posts_message'] );
158
+ $offset = (int) $atts['offset'];
159
+ $order = sanitize_key( $atts['order'] );
160
+ $orderby = sanitize_key( $atts['orderby'] );
161
+ $post_parent = $atts['post_parent']; // Validated later, after check for 'current'.
162
+ $post_parent__in = $atts['post_parent__in'];
163
+ $post_parent__not_in = $atts['post_parent__not_in'];
164
+ $post_status = $atts['post_status']; // Validated later as one of a few values.
165
+ $post_type = sanitize_text_field( $atts['post_type'] );
166
+ $posts_per_page = (int) $atts['posts_per_page'];
167
+ $s = sanitize_text_field( $atts['s'] );
168
+ $tag = sanitize_text_field( $atts['tag'] );
169
+ $tax_operator = $atts['tax_operator']; // Validated later as one of a few values.
170
+ $tax_include_children = filter_var( $atts['tax_include_children'], FILTER_VALIDATE_BOOLEAN );
171
+ $tax_term = sanitize_text_field( $atts['tax_term'] );
172
+ $taxonomy = sanitize_key( $atts['taxonomy'] );
173
+ $time = sanitize_text_field( $atts['time'] );
174
+ $shortcode_title = sanitize_text_field( $atts['title'] );
175
+ $wrapper = sanitize_text_field( $atts['wrapper'] );
176
+ $wrapper_class = array_map( 'sanitize_html_class', explode( ' ', $atts['wrapper_class'] ) );
177
+
178
+ if ( ! empty( $wrapper_class ) ) {
179
+ $wrapper_class = ' class="' . implode( ' ', $wrapper_class ) . '"';
180
+ }
181
+ $wrapper_id = sanitize_html_class( $atts['wrapper_id'] );
182
+ if ( ! empty( $wrapper_id ) ) {
183
+ $wrapper_id = ' id="' . $wrapper_id . '"';
184
+ }
185
+
186
+ // Set up initial query for post.
187
+ $args = array(
188
+ 'perm' => 'readable',
189
+ );
190
+
191
+ // Add args if they aren't empty.
192
+ if ( ! empty( $category_id ) ) {
193
+ $args['cat'] = $category_id;
194
+ }
195
+ if ( ! empty( $category ) ) {
196
+ $args['category_name'] = $category;
197
+ }
198
+ if ( ! empty( $order ) ) {
199
+ $args['order'] = $order;
200
+ }
201
+ if ( ! empty( $orderby ) ) {
202
+ $args['orderby'] = $orderby;
203
+ }
204
+ if ( ! empty( $post_type ) ) {
205
+ $args['post_type'] = be_dps_explode( $post_type );
206
+ }
207
+ if ( ! empty( $posts_per_page ) ) {
208
+ $args['posts_per_page'] = $posts_per_page;
209
+ }
210
+ if ( ! empty( $s ) ) {
211
+ $args['s'] = $s;
212
+ }
213
+ if ( ! empty( $tag ) ) {
214
+ $args['tag'] = $tag;
215
+ }
216
+
217
+ // Date query.
218
+ if ( ! empty( $date ) || ! empty( $time ) || ! empty( $date_query_after ) || ! empty( $date_query_before ) ) {
219
+ $initial_date_query = array();
220
+ $date_query_top_lvl = array();
221
+
222
+ $valid_date_columns = array(
223
+ 'post_date',
224
+ 'post_date_gmt',
225
+ 'post_modified',
226
+ 'post_modified_gmt',
227
+ 'comment_date',
228
+ 'comment_date_gmt',
229
+ );
230
+
231
+ $valid_compare_ops = array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' );
232
+
233
+ // Sanitize and add date segments.
234
+ $dates = be_sanitize_date_time( $date );
235
+ if ( ! empty( $dates ) ) {
236
+ if ( is_string( $dates ) ) {
237
+ $timestamp = strtotime( $dates );
238
+ $dates = array(
239
+ 'year' => date( 'Y', $timestamp ),
240
+ 'month' => date( 'm', $timestamp ),
241
+ 'day' => date( 'd', $timestamp ),
242
+ );
243
+ }
244
+ foreach ( $dates as $arg => $segment ) {
245
+ $initial_date_query[ $arg ] = $segment;
246
+ }
247
+ }
248
+
249
+ // Sanitize and add time segments.
250
+ $times = be_sanitize_date_time( $time, 'time' );
251
+ if ( ! empty( $times ) ) {
252
+ foreach ( $times as $arg => $segment ) {
253
+ $initial_date_query[ $arg ] = $segment;
254
+ }
255
+ }
256
+
257
+ // Date query 'before' argument.
258
+ $before = be_sanitize_date_time( $date_query_before, 'date', true );
259
+ if ( ! empty( $before ) ) {
260
+ $initial_date_query['before'] = $before;
261
+ }
262
+
263
+ // Date query 'after' argument.
264
+ $after = be_sanitize_date_time( $date_query_after, 'date', true );
265
+ if ( ! empty( $after ) ) {
266
+ $initial_date_query['after'] = $after;
267
+ }
268
+
269
+ // Date query 'column' argument.
270
+ if ( ! empty( $date_query_column ) && in_array( $date_query_column, $valid_date_columns, true ) ) {
271
+ $initial_date_query['column'] = $date_query_column;
272
+ }
273
+
274
+ // Date query 'compare' argument.
275
+ if ( ! empty( $date_query_compare ) && in_array( $date_query_compare, $valid_compare_ops, true ) ) {
276
+ $initial_date_query['compare'] = $date_query_compare;
277
+ }
278
+
279
+ // Top-level date_query arguments. Only valid arguments will be added.
280
+ //
281
+ // 'column' argument.
282
+ if ( ! empty( $date_column ) && in_array( $date_column, $valid_date_columns, true ) ) {
283
+ $date_query_top_lvl['column'] = $date_column;
284
+ }
285
+
286
+ // 'compare' argument.
287
+ if ( ! empty( $date_compare ) && in_array( $date_compare, $valid_compare_ops, true ) ) {
288
+ $date_query_top_lvl['compare'] = $date_compare;
289
+ }
290
+
291
+ // Bring in the initial date query.
292
+ if ( ! empty( $initial_date_query ) ) {
293
+ $date_query_top_lvl[] = $initial_date_query;
294
+ }
295
+
296
+ // Date queries.
297
+ $args['date_query'] = $date_query_top_lvl;
298
+ }
299
+
300
+ // Ignore Sticky Posts.
301
+ if ( $ignore_sticky_posts ) {
302
+ $args['ignore_sticky_posts'] = true;
303
+ }
304
+
305
+ // Password protected content.
306
+ if ( null !== $has_password ) {
307
+ $args['has_password'] = $has_password;
308
+ }
309
+
310
+ // Meta key (for ordering).
311
+ if ( ! empty( $meta_key ) ) {
312
+ $args['meta_key'] = $meta_key; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key
313
+ }
314
+
315
+ // Meta value (for simple meta queries).
316
+ if ( ! empty( $meta_value ) ) {
317
+ $args['meta_value'] = $meta_value; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value
318
+ }
319
+
320
+ // If Post IDs.
321
+ if ( $id ) {
322
+ $posts_in = array_map( 'intval', be_dps_explode( $id ) );
323
+ $args['post__in'] = $posts_in;
324
+ }
325
+
326
+ // If Exclude.
327
+ $post__not_in = array();
328
+ if ( ! empty( $exclude ) ) {
329
+ $post__not_in = array_map( 'intval', be_dps_explode( $exclude ) );
330
+ }
331
+ if ( is_singular() && $exclude_current ) {
332
+ $post__not_in[] = get_the_ID();
333
+ }
334
+ if ( ! empty( $post__not_in ) ) {
335
+ $args['post__not_in'] = $post__not_in; // phpcs:ignore WordPressVIPMinimum.VIP.WPQueryParams.post__not_in
336
+ }
337
+
338
+ // Post Author.
339
+ if ( ! empty( $author ) ) {
340
+ if ( 'current' === $author && is_user_logged_in() ) {
341
+ $args['author_name'] = wp_get_current_user()->user_login;
342
+ } elseif ( 'current' === $author ) {
343
+ $args['meta_key'] = 'dps_no_results'; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key
344
+ } else {
345
+ $args['author_name'] = $author;
346
+ }
347
+ } elseif ( ! empty( $author_id ) ) {
348
+ $args['author'] = $author_id;
349
+ }
350
+
351
+ // Offset.
352
+ if ( ! empty( $offset ) ) {
353
+ $args['offset'] = $offset;
354
+ }
355
+
356
+ // Post Status.
357
+ $post_status = be_dps_explode( $post_status );
358
+ $validated = array();
359
+ $available = array( 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash', 'any' );
360
+ foreach ( $post_status as $unvalidated ) {
361
+ if ( in_array( $unvalidated, $available, true ) ) {
362
+ $validated[] = $unvalidated;
363
+ }
364
+ }
365
+ if ( ! empty( $validated ) ) {
366
+ $args['post_status'] = $validated;
367
+ }
368
+
369
+ // If taxonomy attributes, create a taxonomy query.
370
+ if ( ! empty( $taxonomy ) && ! empty( $tax_term ) ) {
371
+
372
+ if ( 'current' === $tax_term ) {
373
+ global $post;
374
+ $terms = wp_get_post_terms( get_the_ID(), $taxonomy );
375
+ $tax_term = array();
376
+ foreach ( $terms as $term ) {
377
+ $tax_term[] = $term->slug;
378
+ }
379
+ } else {
380
+ // Term string to array.
381
+ $tax_term = be_dps_explode( $tax_term );
382
+ }
383
+
384
+ // Validate operator.
385
+ if ( ! in_array( $tax_operator, array( 'IN', 'NOT IN', 'AND' ), true ) ) {
386
+ $tax_operator = 'IN';
387
+ }
388
+
389
+ $tax_args = array(
390
+ 'tax_query' => array( // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_tax_query
391
+ array(
392
+ 'taxonomy' => $taxonomy,
393
+ 'field' => 'slug',
394
+ 'terms' => $tax_term,
395
+ 'operator' => $tax_operator,
396
+ 'include_children' => $tax_include_children,
397
+ ),
398
+ ),
399
+ );
400
+
401
+ // Check for multiple taxonomy queries.
402
+ $count = 2;
403
+ $more_tax_queries = false;
404
+ while (
405
+ isset( $original_atts[ 'taxonomy_' . $count ] ) && ! empty( $original_atts[ 'taxonomy_' . $count ] ) &&
406
+ isset( $original_atts[ 'tax_' . $count . '_term' ] ) && ! empty( $original_atts[ 'tax_' . $count . '_term' ] )
407
+ ) :
408
+
409
+ // Sanitize values.
410
+ $more_tax_queries = true;
411
+ $taxonomy = sanitize_key( $original_atts[ 'taxonomy_' . $count ] );
412
+ $terms = be_dps_explode( sanitize_text_field( $original_atts[ 'tax_' . $count . '_term' ] ) );
413
+ $tax_operator = isset( $original_atts[ 'tax_' . $count . '_operator' ] ) ? $original_atts[ 'tax_' . $count . '_operator' ] : 'IN';
414
+ $tax_operator = in_array( $tax_operator, array( 'IN', 'NOT IN', 'AND' ), true ) ? $tax_operator : 'IN';
415
+ $tax_include_children = isset( $original_atts[ 'tax_' . $count . '_include_children' ] ) ? filter_var( $atts[ 'tax_' . $count . '_include_children' ], FILTER_VALIDATE_BOOLEAN ) : true;
416
+
417
+ $tax_args['tax_query'][] = array(
418
+ 'taxonomy' => $taxonomy,
419
+ 'field' => 'slug',
420
+ 'terms' => $terms,
421
+ 'operator' => $tax_operator,
422
+ 'include_children' => $tax_include_children,
423
+ );
424
+
425
+ $count++;
426
+
427
+ endwhile;
428
+
429
+ if ( $more_tax_queries ) :
430
+ $tax_relation = 'AND';
431
+ if ( isset( $original_atts['tax_relation'] ) && in_array( $original_atts['tax_relation'], array( 'AND', 'OR' ), true ) ) {
432
+ $tax_relation = $original_atts['tax_relation'];
433
+ }
434
+ $args['tax_query']['relation'] = $tax_relation;
435
+ endif;
436
+
437
+ $args = array_merge_recursive( $args, $tax_args );
438
+ }
439
+
440
+ // If post parent attribute, set up parent.
441
+ if ( false !== $post_parent ) {
442
+ if ( 'current' === $post_parent ) {
443
+ $post_parent = get_the_ID();
444
+ }
445
+ $args['post_parent'] = (int) $post_parent;
446
+ }
447
+
448
+ if ( false !== $post_parent__in ) {
449
+ $args['post_parent__in'] = array_map( 'intval', be_dps_explode( $atts['post_parent__in'] ) );
450
+ }
451
+ if ( false !== $post_parent__not_in ) {
452
+ $args['post_parent__not_in'] = array_map( 'intval', be_dps_explode( $atts['post_parent__in'] ) );
453
+ }
454
+
455
+ // Set up html elements used to wrap the posts.
456
+ // Default is ul/li, but can also be ol/li and div/div.
457
+ $wrapper_options = array( 'ul', 'ol', 'div' );
458
+ if ( ! in_array( $wrapper, $wrapper_options, true ) ) {
459
+ $wrapper = 'ul';
460
+ }
461
+ $inner_wrapper = 'div' === $wrapper ? 'div' : 'li';
462
+
463
+ /**
464
+ * Filter the arguments passed to WP_Query.
465
+ *
466
+ * @since 1.7
467
+ *
468
+ * @param array $args Parsed arguments to pass to WP_Query.
469
+ * @param array $original_atts Original attributes passed to the shortcode.
470
+ */
471
+ global $dps_listing;
472
+ $dps_listing = new WP_Query( apply_filters( 'display_posts_shortcode_args', $args, $original_atts ) );
473
+ if ( ! $dps_listing->have_posts() ) {
474
+ /**
475
+ * Filter content to display if no posts match the current query.
476
+ *
477
+ * @since 1.8
478
+ *
479
+ * @param string $no_posts_message Content to display, returned via {@see wpautop()}.
480
+ */
481
+ return apply_filters( 'display_posts_shortcode_no_results', wpautop( $no_posts_message ) );
482
+ }
483
+
484
+ $inner = '';
485
+ while ( $dps_listing->have_posts() ) :
486
+ $dps_listing->the_post();
487
+ global $post;
488
+
489
+ $image = '';
490
+ $date = '';
491
+ $author = '';
492
+ $excerpt = '';
493
+ $content = '';
494
+
495
+ if ( $include_title && $include_link ) {
496
+ /** This filter is documented in wp-includes/link-template.php */
497
+ $title = '<a class="title" href="' . apply_filters( 'the_permalink', get_permalink() ) . '">' . get_the_title() . '</a>';
498
+
499
+ } elseif ( $include_title ) {
500
+ $title = '<span class="title">' . get_the_title() . '</span>';
501
+
502
+ } else {
503
+ $title = '';
504
+ }
505
+
506
+ if ( $image_size && has_post_thumbnail() && $include_link ) {
507
+ $image = '<a class="image" href="' . get_permalink() . '">' . get_the_post_thumbnail( get_the_ID(), $image_size ) . '</a> ';
508
+
509
+ } elseif ( $image_size && has_post_thumbnail() ) {
510
+ $image = '<span class="image">' . get_the_post_thumbnail( get_the_ID(), $image_size ) . '</span> ';
511
+
512
+ }
513
+
514
+ if ( $include_date ) {
515
+ $date = 'relative' === $date_format ? be_dps_relative_date( get_the_date( 'U' ) ) : get_the_date( $date_format );
516
+ } elseif ( $include_date_modified ) {
517
+ $date = 'relative' === $date_format ? be_dps_relative_date( get_the_modified_time( 'U' ) ) : get_the_modified_date( $date_format );
518
+ }
519
+ if ( ! empty( $date ) ) {
520
+ $date = ' <span class="date">' . $date . '</span>';
521
+ }
522
+
523
+ if ( $include_author ) {
524
+ /**
525
+ * Filter the HTML markup to display author information for the current post.
526
+ *
527
+ * @since 2.4.0
528
+ *
529
+ * @param string $author_output HTML markup to display author information.
530
+ */
531
+ $author = apply_filters( 'display_posts_shortcode_author', ' <span class="author">by ' . get_the_author() . '</span>', $original_atts );
532
+ }
533
+
534
+ if ( $include_excerpt ) {
535
+
536
+ // Custom build excerpt based on shortcode parameters.
537
+ if ( $excerpt_length || $excerpt_more || $excerpt_more_link ) {
538
+
539
+ $length = $excerpt_length ? $excerpt_length : apply_filters( 'excerpt_length', 55 );
540
+ $more = $excerpt_more ? $excerpt_more : apply_filters( 'excerpt_more', '' );
541
+ $more = $excerpt_more_link ? ' <a class="excerpt-more" href="' . get_permalink() . '">' . $more . '</a>' : ' <span class="excerpt-more">' . $more . '</span>';
542
+
543
+ if ( has_excerpt() && apply_filters( 'display_posts_shortcode_full_manual_excerpt', false ) ) {
544
+ $excerpt = $post->post_excerpt . $more;
545
+ } elseif ( has_excerpt() ) {
546
+ $excerpt = wp_trim_words( strip_shortcodes( $post->post_excerpt ), $length ) . $more;
547
+ } else {
548
+ $excerpt = wp_trim_words( strip_shortcodes( $post->post_content ), $length ) . $more;
549
+ }
550
+
551
+ // Use default, can customize with WP filters.
552
+ } else {
553
+ $excerpt = get_the_excerpt();
554
+ }
555
+
556
+ $excerpt = ' <span class="excerpt">' . $excerpt . '</span>';
557
+ if ( $include_excerpt_dash ) {
558
+ $excerpt = ' <span class="excerpt-dash">-</span>' . $excerpt;
559
+ }
560
+ }
561
+
562
+ if ( $include_content ) {
563
+ add_filter( 'shortcode_atts_display-posts', 'be_display_posts_off', 10, 3 );
564
+ /** This filter is documented in wp-includes/post-template.php */
565
+ $content = '<div class="' . implode( ' ', $content_class ) . '">' . apply_filters( 'the_content', get_the_content() ) . '</div>';
566
+ remove_filter( 'shortcode_atts_display-posts', 'be_display_posts_off', 10, 3 );
567
+ }
568
+
569
+ // Display categories the post is in.
570
+ $category_display_text = '';
571
+
572
+ if ( $category_display && is_object_in_taxonomy( get_post_type(), $category_display ) ) {
573
+ $terms = get_the_terms( get_the_ID(), $category_display );
574
+ $term_output = array();
575
+
576
+ if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
577
+ foreach ( $terms as $term ) {
578
+ $term_output[] = '<a href="' . get_term_link( $term, $category_display ) . '">' . $term->name . '</a>';
579
+ }
580
+ $category_display_text = ' <span class="category-display"><span class="category-display-label">' . $category_label . '</span> ' . implode( ', ', $term_output ) . '</span>';
581
+ }
582
+
583
+ /**
584
+ * Filter the list of categories attached to the current post.
585
+ *
586
+ * @since 2.4.2
587
+ *
588
+ * @param string $category_display Current Category Display text
589
+ */
590
+ $category_display_text = apply_filters( 'display_posts_shortcode_category_display', $category_display_text, $terms, $category_display, $original_atts );
591
+
592
+ }
593
+
594
+ $class = array( 'listing-item' );
595
+
596
+ /**
597
+ * Filter the post classes for the inner wrapper element of the current post.
598
+ *
599
+ * @since 2.2
600
+ *
601
+ * @param array $class Post classes.
602
+ * @param WP_Post $post Post object.
603
+ * @param WP_Query $dps_listing WP_Query object for the posts listing.
604
+ * @param array $original_atts Original attributes passed to the shortcode.
605
+ */
606
+ $class = array_map( 'sanitize_html_class', apply_filters( 'display_posts_shortcode_post_class', $class, $post, $dps_listing, $original_atts ) );
607
+ $output = '<' . $inner_wrapper . ' class="' . implode( ' ', $class ) . '">' . $image . $title . $date . $author . $category_display_text . $excerpt . $content . '</' . $inner_wrapper . '>';
608
+
609
+ /**
610
+ * Filter the HTML markup for output via the shortcode.
611
+ *
612
+ * @since 0.1.5
613
+ *
614
+ * @param string $output The shortcode's HTML output.
615
+ * @param array $original_atts Original attributes passed to the shortcode.
616
+ * @param string $image HTML markup for the post's featured image element.
617
+ * @param string $title HTML markup for the post's title element.
618
+ * @param string $date HTML markup for the post's date element.
619
+ * @param string $excerpt HTML markup for the post's excerpt element.
620
+ * @param string $inner_wrapper Type of container to use for the post's inner wrapper element.
621
+ * @param string $content The post's content.
622
+ * @param string $class Space-separated list of post classes to supply to the $inner_wrapper element.
623
+ * @param string $author HTML markup for the post's author.
624
+ * @param string $category_display_text
625
+ */
626
+ $inner .= apply_filters( 'display_posts_shortcode_output', $output, $original_atts, $image, $title, $date, $excerpt, $inner_wrapper, $content, $class, $author, $category_display_text );
627
+
628
+ endwhile;
629
+ wp_reset_postdata();
630
+
631
+ /**
632
+ * Filter the shortcode output's opening outer wrapper element.
633
+ *
634
+ * @since 1.7
635
+ *
636
+ * @param string $wrapper_open HTML markup for the opening outer wrapper element.
637
+ * @param array $original_atts Original attributes passed to the shortcode.
638
+ * @param object $dps_listing, WP Query object
639
+ */
640
+ $open = apply_filters( 'display_posts_shortcode_wrapper_open', '<' . $wrapper . $wrapper_class . $wrapper_id . '>', $original_atts, $dps_listing );
641
+
642
+ /**
643
+ * Filter the shortcode output's closing outer wrapper element.
644
+ *
645
+ * @since 1.7
646
+ *
647
+ * @param string $wrapper_close HTML markup for the closing outer wrapper element.
648
+ * @param array $original_atts Original attributes passed to the shortcode.
649
+ * @param object $dps_listing, WP Query object
650
+ */
651
+ $close = apply_filters( 'display_posts_shortcode_wrapper_close', '</' . $wrapper . '>', $original_atts, $dps_listing );
652
+
653
+ $return = '';
654
+
655
+ if ( $shortcode_title ) {
656
+
657
+ /**
658
+ * Filter the shortcode output title tag element.
659
+ *
660
+ * @since 2.3
661
+ *
662
+ * @param string $tag Type of element to use for the output title tag. Default 'h2'.
663
+ * @param array $original_atts Original attributes passed to the shortcode.
664
+ */
665
+ $title_tag = apply_filters( 'display_posts_shortcode_title_tag', 'h2', $original_atts );
666
+
667
+ $return .= '<' . $title_tag . ' class="display-posts-title">' . $shortcode_title . '</' . $title_tag . '>' . "\n";
668
+ }
669
+
670
+ $return .= $open . $inner . $close;
671
+
672
+ return $return;
673
+ }
674
+
675
+ /**
676
+ * Sanitize the segments of a given date or time for a date query.
677
+ *
678
+ * Accepts times entered in the 'HH:MM:SS' or 'HH:MM' formats, and dates
679
+ * entered in the 'YYYY-MM-DD' format.
680
+ *
681
+ * @param string $date_time Date or time string to sanitize the parts of.
682
+ * @param string $type Optional. Type of value to sanitize. Accepts
683
+ * 'date' or 'time'. Default 'date'.
684
+ * @param bool $accepts_string Optional. Whether the return value accepts a string.
685
+ * Default false.
686
+ * @return array|string Array of valid date or time segments, a timestamp, otherwise
687
+ * an empty array.
688
+ */
689
+ function be_sanitize_date_time( $date_time, $type = 'date', $accepts_string = false ) {
690
+ if ( empty( $date_time ) || ! in_array( $type, array( 'date', 'time' ), true ) ) {
691
+ return array();
692
+ }
693
+
694
+ $segments = array();
695
+
696
+ /*
697
+ * If $date_time is not a strictly-formatted date or time, attempt to salvage it with
698
+ * as strototime()-ready string. This is supported by the 'date', 'date_query_before',
699
+ * and 'date_query_after' attributes.
700
+ */
701
+ if (
702
+ true === $accepts_string
703
+ && ( false !== strpos( $date_time, ' ' ) || false === strpos( $date_time, '-' ) )
704
+ ) {
705
+ $timestamp = strtotime( $date_time );
706
+ if ( false !== $timestamp ) {
707
+ return $date_time;
708
+ }
709
+ }
710
+
711
+ $parts = array_map( 'absint', explode( 'date' === $type ? '-' : ':', $date_time ) );
712
+
713
+ // Date.
714
+ if ( 'date' === $type ) {
715
+ // Defaults to 2001 for years, January for months, and 1 for days.
716
+ $year = 1;
717
+ $month = 1;
718
+ $day = 1;
719
+
720
+ if ( count( $parts ) >= 3 ) {
721
+ list( $year, $month, $day ) = $parts;
722
+
723
+ $year = ( $year >= 1 && $year <= 9999 ) ? $year : 1;
724
+ $month = ( $month >= 1 && $month <= 12 ) ? $month : 1;
725
+ $day = ( $day >= 1 && $day <= 31 ) ? $day : 1;
726
+ }
727
+
728
+ $segments = array(
729
+ 'year' => $year,
730
+ 'month' => $month,
731
+ 'day' => $day,
732
+ );
733
+
734
+ // Time.
735
+ } elseif ( 'time' === $type ) {
736
+ // Defaults to 0 for all segments.
737
+ $hour = 0;
738
+ $minute = 0;
739
+ $second = 0;
740
+
741
+ switch ( count( $parts ) ) {
742
+ case 3:
743
+ list( $hour, $minute, $second ) = $parts;
744
+ $hour = ( $hour >= 0 && $hour <= 23 ) ? $hour : 0;
745
+ $minute = ( $minute >= 0 && $minute <= 60 ) ? $minute : 0;
746
+ $second = ( $second >= 0 && $second <= 60 ) ? $second : 0;
747
+ break;
748
+ case 2:
749
+ list( $hour, $minute ) = $parts;
750
+ $hour = ( $hour >= 0 && $hour <= 23 ) ? $hour : 0;
751
+ $minute = ( $minute >= 0 && $minute <= 60 ) ? $minute : 0;
752
+ break;
753
+ default:
754
+ break;
755
+ }
756
+
757
+ $segments = array(
758
+ 'hour' => $hour,
759
+ 'minute' => $minute,
760
+ 'second' => $second,
761
+ );
762
+ }
763
+
764
+ /**
765
+ * Filter the sanitized segments for the given date or time string.
766
+ *
767
+ * @since 2.5
768
+ *
769
+ * @param array $segments Array of sanitized date or time segments, e.g. hour, minute, second,
770
+ * or year, month, day, depending on the value of the $type parameter.
771
+ * @param string $date_time Date or time string. Dates are formatted 'YYYY-MM-DD', and times are
772
+ * formatted 'HH:MM:SS' or 'HH:MM'.
773
+ * @param string $type Type of string to sanitize. Can be either 'date' or 'time'.
774
+ */
775
+ return apply_filters( 'display_posts_shortcode_sanitized_segments', $segments, $date_time, $type );
776
+ }
777
+
778
+ /**
779
+ * Turn off display posts shortcode.
780
+ *
781
+ * If display full post content, any uses of [display-posts] are disabled.
782
+ *
783
+ * @param array $out Returned shortcode values.
784
+ * @param array $pairs List of supported attributes and their defaults.
785
+ * @param array $atts Original shortcode attributes.
786
+ * @return array
787
+ */
788
+ function be_display_posts_off( $out, $pairs, $atts ) {
789
+ /**
790
+ * Filter whether to disable the display-posts shortcode.
791
+ *
792
+ * The function and filter were added for backward-compatibility with
793
+ * 2.3 behavior in certain circumstances.
794
+ *
795
+ * @since 2.4.0
796
+ *
797
+ * @param bool $disable Whether to disable the display-posts shortcode. Default true.
798
+ */
799
+ $out['display_posts_off'] = apply_filters( 'display_posts_shortcode_inception_override', true );
800
+ return $out;
801
+ }
802
+
803
+ /**
804
+ * Explode list using "," and ", ".
805
+ *
806
+ * @param string $string String to split up.
807
+ * @return array Array of string parts.
808
+ */
809
+ function be_dps_explode( $string = '' ) {
810
+ $string = str_replace( ', ', ',', $string );
811
+ return explode( ',', $string );
812
+ }
813
+
814
+ /**
815
+ * Get a relative data with "ago" suffix.
816
+ *
817
+ * @param int $date Unix timestamp.
818
+ * @return string Human readable time difference.
819
+ */
820
+ function be_dps_relative_date( $date ) {
821
+ return human_time_diff( $date ) . ' ' . __( 'ago', 'display-posts' );
822
+ }
823
+
824
+ /**
825
+ * Survey admin notice.
826
+ */
827
+ function be_dps_survey_admin_notice() {
828
+
829
+ if ( ! is_super_admin() ) {
830
+ return;
831
+ }
832
+
833
+ $survey = get_option( 'display_posts_survey' );
834
+ $time = time();
835
+ $load = false;
836
+
837
+ if ( ! $survey ) {
838
+ $survey = array(
839
+ 'time' => $time,
840
+ 'dismissed' => false,
841
+ );
842
+ update_option( 'display_posts_survey', $survey );
843
+ } else {
844
+ // Check if it has been dismissed or not.
845
+ if ( ( isset( $survey['dismissed'] ) && ! $survey['dismissed'] ) ) {
846
+ $load = true;
847
+ }
848
+ }
849
+
850
+ // If we cannot load, return early.
851
+ if ( ! $load ) {
852
+ return;
853
+ }
854
+
855
+ ?>
856
+ <div class="notice notice-info is-dismissible display-posts-survey-notice">
857
+ <p><?php esc_html_e( 'Thank you so much for using Display Posts! Could you please do me a BIG favor and answer four quick questions on how I can improve the plugin for you?', 'display-posts' ); ?></p>
858
+ <p><?php esc_html_e( 'In 2019 I\'ll be working on new features, including the possibility of a premium version. As a valued Display Posts user, your feedback is important and appreciated!', 'display-posts' ); ?></p>
859
+ <p><strong><?php echo wp_kses( __( '~ Bill Erickson<br>Developer of Display Posts', 'display-posts' ), array( 'br' => array() ) ); ?></strong></p>
860
+ <p>
861
+ <a href="https://displayposts.com/user-survey?utm_source=displaypostsplugin&utm_medium=link&utm_campaign=survey_notice" class="display-posts-dismiss-survey-notice display-posts-survey-out" target="_blank" rel="noopener"><?php esc_html_e( 'Yes, I will!', 'display-posts' ); ?></a><br>
862
+ <a href="#" class="display-posts-dismiss-survey-notice" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Nope, maybe later', 'display-posts' ); ?></a><br>
863
+ <a href="#" class="display-posts-dismiss-survey-notice" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'I already did', 'display-posts' ); ?></a>
864
+ </p>
865
+ </div>
866
+ <script type="text/javascript">
867
+ jQuery( document ).ready( function ( $ ) {
868
+ $( document ).on( 'click', '.display-posts-dismiss-survey-notice, .display-posts-survey-notice button', function ( event ) {
869
+ if ( ! $( this ).hasClass( 'display-posts-survey-out' ) ) {
870
+ event.preventDefault();
871
+ }
872
+ $.post( ajaxurl, {
873
+ action: 'display_posts_survey_dismiss'
874
+ } );
875
+ $( '.display-posts-survey-notice' ).remove();
876
+ } );
877
+ } );
878
+ </script>
879
+ <?php
880
+ }
881
+ //add_action( 'admin_notices', 'be_dps_survey_admin_notice' );
882
+
883
+ /**
884
+ * Dismiss the admin notice.
885
+ */
886
+ function be_dps_survey_dismiss() {
887
+
888
+ $survey = get_option( 'display_posts_survey', array() );
889
+ $survey['time'] = time();
890
+ $survey['dismissed'] = true;
891
+ update_option( 'display_posts_survey', $survey );
892
+ }
893
+ add_action( 'wp_ajax_display_posts_survey_dismiss', 'be_dps_survey_dismiss' );
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: billerickson
3
  Tags: shortcode, pages, posts, page, query, display, list
4
  Requires at least: 3.0
5
- Tested up to: 5.0
6
- Stable tag: 3.0.1
7
 
8
  Add a listing of content on your website using a simple shortcode. Filter the results by category, author, and more.
9
 
@@ -56,6 +56,18 @@ Here are [tutorials for popular event calendar plugins](https://displayposts.com
56
  * [Output filter](https://displayposts.com/docs/the-output-filter/) for complete control over how the listing looks on your site
57
  * [Filters](https://displayposts.com/docs/parameters/#display-parameters) for even more powerful customizations for developers
58
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  == Installation ==
61
 
@@ -66,10 +78,15 @@ Here are [tutorials for popular event calendar plugins](https://displayposts.com
66
 
67
  == Changelog ==
68
 
 
 
 
 
 
 
69
  **Version 3.0.1**
70
  * Prevent empty empty parameters from being added to the query, see [#207](https://github.com/billerickson/display-posts-shortcode/issues/207)
71
 
72
-
73
  **Version 3.0.0**
74
  * Added author_id parameter, see [#195](https://github.com/billerickson/display-posts-shortcode/issues/195)
75
  * Added has_password parameter
2
  Contributors: billerickson
3
  Tags: shortcode, pages, posts, page, query, display, list
4
  Requires at least: 3.0
5
+ Tested up to: 5.2
6
+ Stable tag: 3.0.2
7
 
8
  Add a listing of content on your website using a simple shortcode. Filter the results by category, author, and more.
9
 
56
  * [Output filter](https://displayposts.com/docs/the-output-filter/) for complete control over how the listing looks on your site
57
  * [Filters](https://displayposts.com/docs/parameters/#display-parameters) for even more powerful customizations for developers
58
 
59
+ **Extensions**
60
+
61
+ * [Display Posts – Pagination](https://github.com/billerickson/Display-Posts-Pagination) – Allow results of Display Posts to be paginated
62
+ * [Display Posts – Date View](https://wordpress.org/plugins/display-posts-date-view/) – Lets you break your content down by month or year.
63
+ * [Display Posts – Alpha View](https://github.com/billerickson/Display-Posts-Alpha-View) – Display an alphabetical listing of your content, broken down by letter
64
+ * [Display Posts – Transient Cache](https://github.com/billerickson/Display-Posts-Transient-Cache) – Cache the output using transients
65
+ * [Co-Authors Plus Addon](https://github.com/billerickson/dps-coauthor-addon) – multiple authors on posts
66
+ * [Columns Extension](https://github.com/billerickson/dps-columns-extension) – display posts in columns
67
+ * [DPS Exclude Sticky](https://github.com/billerickson/DPS-Exclude-Sticky) – exclude sticky posts unless specifically requested
68
+ * [DPS Pinch Zoomer](https://github.com/shazahm1/Display-Posts-Shortcode-Pinch-Zoomer) – adds support pinch zooming post images on mobile devices and mouse wheel zooming on desktops
69
+ * [Display Posts Shortcode Remote](https://github.com/shazahm1/Display-Posts-Shortcode-Remote) – display posts from a remote WordPress site utilizing the WP REST API.
70
+
71
 
72
  == Installation ==
73
 
78
 
79
  == Changelog ==
80
 
81
+ **Version 3.0.2**
82
+ * Added `pre_display_posts_shortcode_output` filter before shortcode runs, used for transient caching, see [#210](https://github.com/billerickson/display-posts-shortcode/issues/214)
83
+ * Updated plugin to pass coding standards, see [#214](https://github.com/billerickson/display-posts-shortcode/issues/214)
84
+ * Removed survey admin notice, see [#213](https://github.com/billerickson/display-posts-shortcode/issues/213)
85
+ * Don't display empty term list, see [#208](https://github.com/billerickson/display-posts-shortcode/issues/208)
86
+
87
  **Version 3.0.1**
88
  * Prevent empty empty parameters from being added to the query, see [#207](https://github.com/billerickson/display-posts-shortcode/issues/207)
89
 
 
90
  **Version 3.0.0**
91
  * Added author_id parameter, see [#195](https://github.com/billerickson/display-posts-shortcode/issues/195)
92
  * Added has_password parameter