PluginProbe
Flex Posts – Responsive Posts Block / trunk
Flex Posts – Responsive Posts Block vtrunk
2.1.0 trunk 1.12.0 2.0.0
flex-posts / includes / render.php

render.php in Flex Posts – Responsive Posts Block trunk, at includes/render.php

462 lines 12.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Front-end rendering: style registration, query building, and output
4 *
5 * @package Flex Posts
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Register style sheet
14 */
15 function flex_posts_register_style() {
16 $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
17 wp_register_style(
18 'flex-posts',
19 FLEX_POSTS_URL . 'public/css/flex-posts' . $suffix . '.css',
20 array(),
21 FLEX_POSTS_VERSION
22 );
23 }
24 add_action( 'init', 'flex_posts_register_style' );
25
26 /**
27 * Get args for WP Query
28 *
29 * @param array $instance Attributes.
30 * @return array Args
31 */
32 function flex_posts_get_query_args( $instance ) {
33 $args['ignore_sticky_posts'] = true;
34 $args['post_status'] = 'publish';
35 $args['orderby'] = 'date';
36 $args['order'] = 'desc';
37
38 if ( ! empty( $instance['order_by'] ) ) {
39 switch ( $instance['order_by'] ) {
40 case 'oldest':
41 $args['order'] = 'asc';
42 break;
43 case 'title':
44 $args['orderby'] = 'title';
45 $args['order'] = 'asc';
46 break;
47 case 'comments':
48 $args['orderby'] = 'comment_count';
49 break;
50 case 'random':
51 $args['orderby'] = 'rand';
52 break;
53 case 'modified':
54 $args['orderby'] = 'modified';
55 break;
56 case 'menu_order':
57 $args['orderby'] = 'menu_order';
58 $args['order'] = 'asc';
59 break;
60 }
61 }
62
63 if ( ! empty( $instance['post_type'] ) ) {
64 $args['post_type'] = $instance['post_type'];
65 } else {
66 $args['post_type'] = 'post';
67 }
68
69 if ( ! empty( $instance['cat'] ) ) {
70 $args['cat'] = absint( $instance['cat'] );
71 }
72
73 if ( ! empty( $instance['tag'] ) ) {
74 $tags = explode( ',', sanitize_text_field( wp_unslash( $instance['tag'] ) ) );
75
76 $include_tag_id = array();
77 $exclude_tag_id = array();
78 foreach ( $tags as $tag ) {
79 $tag = trim( $tag );
80 if ( strpos( $tag, '-' ) === 0 ) {
81 $tag = substr( $tag, 1 );
82 $term = get_term_by( 'slug', $tag, 'post_tag' );
83 if ( ! empty( $term ) ) {
84 $exclude_tag_id[] = $term->term_id;
85 }
86 } else {
87 $term = get_term_by( 'slug', $tag, 'post_tag' );
88 if ( ! empty( $term ) ) {
89 $include_tag_id[] = $term->term_id;
90 }
91 }
92 }
93
94 if ( ! empty( $include_tag_id ) ) {
95 $args['tag__in'] = $include_tag_id;
96 }
97
98 if ( ! empty( $exclude_tag_id ) ) {
99 $args['tag__not_in'] = $exclude_tag_id;
100 }
101 }
102
103 if ( isset( $instance['number'] ) ) {
104 $args['posts_per_page'] = intval( $instance['number'] );
105 }
106
107 if ( ! empty( $instance['skip'] ) ) {
108 $args['offset'] = absint( $instance['skip'] );
109 if ( 'rand' === $args['orderby'] ) {
110 // Make offset and order by random working together.
111 $args2 = $args;
112 unset( $args2['offset'] );
113
114 $args2['posts_per_page'] = $args['offset'];
115 $args2['orderby'] = 'date';
116 $args2['order'] = 'desc';
117 $args2['fields'] = 'ids';
118
119 $query2 = new WP_Query( $args2 );
120
121 if ( ! empty( $query2->posts ) ) {
122 // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Exclusion set is bounded by the small "skip" count, not an unbounded list.
123 $args['post__not_in'] = $query2->posts;
124 unset( $args['offset'] );
125 }
126 }
127 }
128
129 if ( ! empty( $instance['exclude_current'] ) ) {
130 $args['post__not_in'][] = get_the_ID();
131 }
132
133 if ( empty( $instance['pagination'] ) ) {
134 $args['no_found_rows'] = true;
135 } else {
136 $args['paged'] = flex_posts_get_current_page();
137 if ( ! empty( $args['offset'] ) ) {
138 // Modify offset value if pagination is active.
139 $old_offset = $args['offset'];
140 $new_offset = $old_offset + ( ( $args['paged'] - 1 ) * $args['posts_per_page'] );
141 $args['offset'] = $new_offset;
142 $args['old_offset'] = $old_offset;
143 }
144 }
145
146 return $args;
147 }
148
149 /**
150 * Front-end display of widget.
151 *
152 * @param array $instance Attributes.
153 * @param string $type Widget type.
154 */
155 function flex_posts_display( $instance, $type = 'list' ) {
156 /**
157 * Filter: flex_posts_{type}_args
158 *
159 * Dynamic filter applied to the WP_Query args before querying posts for
160 * a specific type (widget/block). Replace {type} with the actual type
161 * used when adding a callback (e.g. 'list').
162 *
163 * @param array $args WP_Query args.
164 * @param array $instance Widget/block instance or attributes.
165 * @return array Modified WP_Query args.
166 *
167 * Example:
168 * add_filter( 'flex_posts_list_args', function( $args, $instance ) {
169 * $args['post_status'] = 'private';
170 * return $args;
171 * }, 10, 2 );
172 */
173 $args = apply_filters( 'flex_posts_' . $type . '_args', flex_posts_get_query_args( $instance ), $instance );
174
175 $query = new WP_Query( $args );
176
177 $layout = 1;
178 if ( ! empty( $instance['layout'] ) ) {
179 $layout = absint( $instance['layout'] );
180 }
181
182 /**
183 * Filter: flex_posts_thumbnail_size
184 *
185 * Filter the thumbnail image size used for small thumbnails.
186 *
187 * @param string $size Default image size slug.
188 * @param array $context Instance/attributes that requested the size.
189 * @return string Image size slug.
190 *
191 * Example:
192 * add_filter( 'flex_posts_thumbnail_size', function( $size, $context ) {
193 * return 'medium';
194 * }, 10, 2 );
195 */
196 $thumbnail_size = apply_filters( 'flex_posts_thumbnail_size', 'thumbnail', $instance );
197
198 /**
199 * Filter: flex_posts_medium_size
200 *
201 * Filter the medium image size used for larger thumbnails.
202 *
203 * @param string $size Default image size slug.
204 * @param array $context Instance/attributes that requested the size.
205 * @return string Image size slug.
206 */
207 $medium_size = apply_filters( 'flex_posts_medium_size', '400x250-crop', $instance );
208
209 if ( ! empty( $instance['image_size'] ) ) {
210 $thumbnail_size = $instance['image_size'];
211 }
212
213 if ( ! empty( $instance['image_size2'] ) ) {
214 $medium_size = $instance['image_size2'];
215 }
216
217 if ( ! isset( $instance['show_image'] ) ) {
218 $instance['show_image'] = 'all';
219 }
220
221 if ( ! isset( $instance['show_title'] ) ) {
222 $instance['show_title'] = true;
223 }
224
225 $excerpt_length = 15;
226 if ( ! empty( $instance['excerpt_length'] ) ) {
227 $excerpt_length = absint( $instance['excerpt_length'] );
228 }
229
230 $readmore_text = __( 'Read more', 'flex-posts' );
231 if ( ! empty( $instance['readmore_text'] ) ) {
232 $readmore_text = $instance['readmore_text'];
233 }
234
235 $file = "flex-posts-{$type}-{$layout}.php";
236
237 /**
238 * Filter: flex_posts_template_directory
239 *
240 * Allow a theme to provide a directory prefix to locate templates.
241 *
242 * @param string $directory Directory prefix (can be empty).
243 * @return string Directory prefix to prepend to template filename.
244 *
245 * Example:
246 * add_filter( 'flex_posts_template_directory', function() {
247 * return 'my-plugin-templates/';
248 * } );
249 */
250 $directory = apply_filters( 'flex_posts_template_directory', '' );
251 $template = locate_template( $directory . $file );
252 if ( empty( $template ) ) {
253 /**
254 * Filter: flex_posts_template
255 *
256 * Filter the full template path that will be included if a theme
257 * does not provide an override. Callback signature: function( $path, $type, $layout ).
258 *
259 * @param string $path Default template path.
260 * @param string $type The render type (e.g. 'list').
261 * @param int $layout Layout number.
262 * @return string Modified template path.
263 */
264 $template = apply_filters( 'flex_posts_template', FLEX_POSTS_DIR . 'public/' . $file, $type, $layout );
265 }
266
267 if ( ! file_exists( $template ) ) {
268 return;
269 }
270
271 if ( $query->have_posts() ) {
272 if ( ! empty( $instance['show_excerpt'] ) ) {
273 add_filter( 'excerpt_more', '__return_null' );
274 }
275
276 include $template;
277
278 /**
279 * Action: flex_posts_end
280 *
281 * Fired at the end of the widget/block render. Useful to append
282 * pagination or other trailing content. Callback signature:
283 * function( $instance, $args, $max_num_pages, $found_posts ).
284 *
285 * @param array $instance Widget/block instance settings.
286 * @param array $args WP_Query arguments used.
287 * @param int $max_num_pages Maximum number of pages from the query.
288 * @param int $found_posts Total posts found by the query.
289 *
290 * Example:
291 * add_action( 'flex_posts_end', function( $instance, $args, $max, $found ) {
292 * // custom pagination
293 * }, 10, 4 );
294 */
295 do_action( 'flex_posts_end', $instance, $args, $query->max_num_pages, $query->found_posts );
296 wp_reset_postdata();
297
298 if ( ! empty( $instance['show_excerpt'] ) ) {
299 remove_filter( 'excerpt_more', '__return_null' );
300 }
301 }
302 }
303
304 /**
305 * Get allowed html tags
306 *
307 * Shared escaping whitelist used when rendering the block title and the
308 * pagination markup.
309 *
310 * @return array
311 */
312 function flex_posts_get_allowed_html() {
313 $attr = array(
314 'class' => array(),
315 'title' => array(),
316 );
317
318 /**
319 * Filter: flex_posts_allowed_html
320 *
321 * Allow modification of allowed HTML tags used by the plugin when
322 * rendering pagination and other safe markup. Expected to return an
323 * array in the same shape as wp_kses_allowed_html.
324 *
325 * @param array $allowed_html Default allowed HTML tags/attributes.
326 * @return array Modified allowed HTML tags/attributes.
327 *
328 * Example:
329 * add_filter( 'flex_posts_allowed_html', function( $allowed ) {
330 * $allowed['img'] = array( 'src' => true, 'alt' => true );
331 * return $allowed;
332 * } );
333 */
334 $allowed_html = apply_filters(
335 'flex_posts_allowed_html',
336 array(
337 'a' => $attr + array( 'href' => array() ),
338 'ul' => $attr,
339 'li' => $attr,
340 'span' => $attr + array( 'aria-current' => array() ),
341 )
342 );
343 return $allowed_html;
344 }
345
346 /**
347 * Render block
348 *
349 * @param array $attributes Attributes.
350 * @param string $type Block type.
351 * @return string
352 */
353 function flex_posts_render( $attributes, $type = 'list' ) {
354 ob_start();
355
356 $class = 'widget widget_flex-posts-' . $type;
357 if ( ! empty( $attributes['align'] ) ) {
358 $class .= ' align' . $attributes['align'];
359 }
360
361 $extra_attr = array( 'class' => $class );
362
363 /**
364 * Filter: flex_posts_extra_attr
365 *
366 * Filter the block wrapper attributes. Signature:
367 * function( $extra_attr, $attributes ).
368 *
369 * @param array $extra_attr Extra html attributes.
370 * @param array $attributes Block attributes.
371 * @return array Modified extra html attributes.
372 */
373 $extra_attr = apply_filters( 'flex_posts_extra_attr', $extra_attr, $attributes );
374
375 $attr = get_block_wrapper_attributes( $extra_attr );
376
377 echo '<section ' . $attr . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
378
379 /**
380 * Action: flex_posts_block_title_before
381 *
382 * Triggered before rendering the block title. Receives block attributes.
383 *
384 * @param array $attributes Block attributes.
385 */
386 do_action( 'flex_posts_block_title_before', $attributes );
387
388 if ( ! empty( $attributes['title'] ) || ! empty( $attributes['title_cat'] ) ) {
389 /**
390 * Filter: flex_posts_block_title
391 *
392 * Filter the block title before it is rendered. Signature:
393 * function( $title, $attributes, $id ).
394 *
395 * @param string $title Title text.
396 * @param array $attributes Block attributes.
397 * @param string $id Block identifier.
398 * @return string Modified title.
399 */
400 $title = apply_filters( 'flex_posts_block_title', $attributes['title'], $attributes, 'flex-posts-' . $type );
401
402 $el = 'h2';
403 if ( ! empty( $attributes['block_title_el'] ) ) {
404 if ( in_array( $attributes['block_title_el'], flex_posts_get_title_elements(), true ) ) {
405 $el = $attributes['block_title_el'];
406 }
407 }
408
409 echo '<' . sanitize_key( $el ) . ' class="widget-title">';
410 echo wp_kses( $title, flex_posts_get_allowed_html() );
411 echo '</' . sanitize_key( $el ) . '>';
412 }
413
414 /**
415 * Action: flex_posts_block_title_after
416 *
417 * Triggered after rendering the block title. Receives block attributes.
418 *
419 * @param array $attributes Block attributes.
420 */
421 do_action( 'flex_posts_block_title_after', $attributes );
422
423 flex_posts_display( $attributes, $type );
424
425 echo '</section>';
426 $display = ob_get_clean();
427 return $display;
428 }
429
430 /**
431 * Add link to widget title
432 *
433 * @param string $title Title.
434 * @param array $instance Instance.
435 * @param string $id_base ID Base.
436 * @return string
437 */
438 function flex_posts_widget_title( $title, $instance = array(), $id_base = '' ) {
439 if ( is_string( $id_base ) && strpos( $id_base, 'flex-posts' ) === 0 ) {
440 if ( ! empty( $instance['title_cat'] ) ) {
441 $title = empty( $instance['cat'] ) ? '' : get_cat_name( $instance['cat'] );
442 }
443
444 if ( $title ) {
445 $title_url = '';
446 if ( ! empty( $instance['title_url_cat'] ) ) {
447 $category_id = (int) $instance['cat'];
448 $title_url = empty( $category_id ) ? '' : get_term_link( $category_id );
449 } elseif ( ! empty( $instance['title_url'] ) ) {
450 $title_url = $instance['title_url'];
451 }
452
453 if ( $title_url ) {
454 $title = '<a href="' . esc_url( $title_url ) . '">' . $title . '</a>';
455 }
456 }
457 }
458 return $title;
459 }
460 add_filter( 'widget_title', 'flex_posts_widget_title', 10, 3 );
461 add_filter( 'flex_posts_block_title', 'flex_posts_widget_title', 10, 3 );
462