'display_posts',
'callback' => 'su_shortcode_display_posts',
'image' => su_get_plugin_url() . 'admin/images/shortcodes/display-posts.svg',
'icon' => 'th-list',
'name' => __( 'Posts', 'shortcodes-ultimate' ),
'type' => 'single',
'group' => 'other',
'article' => 'https://getshortcodes.com/docs/display-posts/',
'atts' => array(
'template' => array(
'default' => 'default',
'name' => __( 'Template', 'shortcodes-ultimate' ),
'desc' => sprintf(
'
%s.
%s:
- %s - %s
- %s - %s
- %s - %s
- %s - %s
- %s - %s
%s
',
__( 'Template name', 'shortcodes-ultimate' ),
__( 'Available templates', 'shortcodes-ultimate' ),
'default',
__( 'default template with thumbnail, title, and excerpt', 'shortcodes-ultimate' ),
'default-meta',
__( 'default template with various meta data', 'shortcodes-ultimate' ),
'list',
__( 'unordered list with post titles', 'shortcodes-ultimate' ),
'teasers',
__( 'small teasers containing post thumbnails and titles', 'shortcodes-ultimate' ),
'single',
__( 'single post template', 'shortcodes-ultimate' ),
'https://getshortcodes.com/docs/display-posts/',
__( 'How to create/edit a template', 'shortcodes-ultimate' )
),
),
'post_ids' => array(
'default' => '',
'name' => __( 'Post IDs', 'shortcodes-ultimate' ),
'desc' => __( 'Comma separated list of post IDs to include', 'shortcodes-ultimate' ),
),
'posts_per_page' => array(
'type' => 'number',
'min' => -1,
'max' => 100,
'step' => 1,
'default' => '10',
'name' => __( 'Posts per page', 'shortcodes-ultimate' ),
'desc' => __( 'Number of posts that will be shown. Use -1 to display all posts.', 'shortcodes-ultimate' ),
),
'post_type' => array(
'type' => 'post_type',
'multiple' => true,
'values' => array(),
'default' => 'post',
'name' => __( 'Post types', 'shortcodes-ultimate' ),
'desc' => __( 'Filter posts by post type', 'shortcodes-ultimate' ),
),
'taxonomy_1' => array(
'type' => 'taxonomy',
'values' => array(),
'default' => 'category',
'name' => __( 'Taxonomy', 'shortcodes-ultimate' ),
'desc' => __( 'Show posts associated with certain taxonomy', 'shortcodes-ultimate' ),
),
'tax_terms_1' => array(
'type' => 'term',
'multiple' => true,
'values' => array(),
'default' => '',
'name' => __( 'Terms', 'shortcodes-ultimate' ),
'desc' => __( 'Show posts associated with specified taxonomy terms.', 'shortcodes-ultimate' ),
),
'tax_operator_1' => array(
'type' => 'select',
'values' => array(
'IN' => __(
'IN - posts that have any of the selected terms',
'shortcodes-ultimate'
),
'NOT IN' => __(
'NOT IN - posts that do not have any of the selected terms',
'shortcodes-ultimate'
),
'AND' => __(
'AND - posts that have all of the selected terms',
'shortcodes-ultimate'
),
),
'default' => 'IN',
'name' => __( 'Taxonomy term operator', 'shortcodes-ultimate' ),
'desc' => __( 'Taxonomy terms operator', 'shortcodes-ultimate' ),
),
'author' => array(
'default' => '',
'name' => __( 'Authors', 'shortcodes-ultimate' ),
'desc' => __( 'Comma separated list of author IDs', 'shortcodes-ultimate' ),
),
'meta_key' => array(
'default' => '',
'name' => __( 'Meta key', 'shortcodes-ultimate' ),
'desc' => __( 'Show posts associated with a certain custom field', 'shortcodes-ultimate' ),
),
'offset' => array(
'type' => 'number',
'min' => 0,
'max' => 10000,
'step' => 1,
'default' => 0,
'name' => __( 'Offset', 'shortcodes-ultimate' ),
'desc' => __( 'Number of posts to displace or pass over. The offset parameter is ignored when posts_per_page=-1 (show all posts) is used.', 'shortcodes-ultimate' ),
),
'orderby' => array(
'type' => 'select',
'values' => array(
'none' => __( 'None', 'shortcodes-ultimate' ),
'id' => __( 'Post ID', 'shortcodes-ultimate' ),
'author' => __( 'Post author', 'shortcodes-ultimate' ),
'title' => __( 'Post title', 'shortcodes-ultimate' ),
'name' => __( 'Post slug', 'shortcodes-ultimate' ),
'date' => __( 'Date', 'shortcodes-ultimate' ),
'modified' => __( 'Last modified date', 'shortcodes-ultimate' ),
'parent' => __( 'Post parent', 'shortcodes-ultimate' ),
'rand' => __( 'Random', 'shortcodes-ultimate' ),
'comment_count' => __( 'Comments number', 'shortcodes-ultimate' ),
'menu_order' => __( 'Menu order', 'shortcodes-ultimate' ),
'meta_value' => __( 'Meta key values', 'shortcodes-ultimate' ),
'meta_value_num' => __( 'Meta key values (Numeric)', 'shortcodes-ultimate' ),
),
'default' => 'date',
'name' => __( 'Order by', 'shortcodes-ultimate' ),
'desc' => __( 'Sort retrieved posts by parameter', 'shortcodes-ultimate' ),
),
'order' => array(
'type' => 'select',
'values' => array(
'desc' => __( 'Descending', 'shortcodes-ultimate' ),
'asc' => __( 'Ascending', 'shortcodes-ultimate' ),
),
'default' => 'desc',
'name' => __( 'Order', 'shortcodes-ultimate' ),
'desc' => __( 'Designates the ascending or descending order of the orderby parameter', 'shortcodes-ultimate' ),
),
'post_parent' => array(
'default' => '',
'name' => __( 'Post parent', 'shortcodes-ultimate' ),
'desc' => sprintf(
// translators: %s will be replaced with clickable text "current"
__( 'Filter posts by post parent (use parent post ID). Use "%s" keyword to display childs of the current post.', 'shortcodes-ultimate' ),
'current'
),
),
'post_status' => array(
'type' => 'select',
'values' => array(
'publish' => __( 'Published', 'shortcodes-ultimate' ),
'pending' => __( 'Pending', 'shortcodes-ultimate' ),
'draft' => __( 'Draft', 'shortcodes-ultimate' ),
'auto-draft' => __( 'Auto-draft', 'shortcodes-ultimate' ),
'future' => __( 'Future post', 'shortcodes-ultimate' ),
'private' => __( 'Private post', 'shortcodes-ultimate' ),
'inherit' => __( 'Inherit', 'shortcodes-ultimate' ),
'trash' => __( 'Trashed', 'shortcodes-ultimate' ),
'any' => __( 'Any', 'shortcodes-ultimate' ),
),
'default' => 'publish',
'name' => __( 'Post status', 'shortcodes-ultimate' ),
'desc' => __( 'Filter posts by status', 'shortcodes-ultimate' ),
),
'ignore_sticky' => array(
'type' => 'bool',
'default' => 'no',
'name' => __( 'Ignore sticky', 'shortcodes-ultimate' ),
'desc' => __( 'Set this option to yes to ignore sticky posts', 'shortcodes-ultimate' ),
),
'exclude' => array(
'default' => '',
'name' => __( 'Exclude Posts', 'shortcodes-ultimate' ),
'desc' => sprintf(
// translators: %s will be replaced with clickable text "current"
__( 'Comma separated list of post IDs to exclude. Use "%s" keyword to exclude the current post.', 'shortcodes-ultimate' ),
'current'
),
),
'quality' => array(
'type' => 'select',
'values' => su_get_image_sizes(),
'default' => 'thubmnail',
'name' => __( 'Thumbnails quality (if applicable)', 'shortcodes-ultimate' ),
'desc' => __( 'This option controls the size of thumbnail images. This option only affects image quality, not the actual thumbnail size.', 'shortcodes-ultimate' ),
),
'pagination' => array(
'type' => 'select',
'values' => array(
'no' => __( 'Disabled', 'shortcodes-ultimate' ),
'prev-next' => __( 'Previous/Next', 'shortcodes-ultimate' ),
),
'default' => 'no',
'name' => __( 'Pagination', 'shortcodes-ultimate' ),
'desc' => __( 'This option controls pagination', 'shortcodes-ultimate' ),
),
'pagination_prev' => array(
'default' => __( 'Previous page', 'shortcodes-ultimate' ),
'name' => __( 'Previous page link label', 'shortcodes-ultimate' ),
'desc' => __( 'Use this option to set a custom label for the previous page link.', 'shortcodes-ultimate' ),
),
'pagination_next' => array(
'default' => __( 'Next page', 'shortcodes-ultimate' ),
'name' => __( 'Next page link label', 'shortcodes-ultimate' ),
'desc' => __( 'Use this option to set a custom label for the next page link.', 'shortcodes-ultimate' ),
),
'pagination_anchor' => array(
'type' => 'bool',
'default' => 'no',
'name' => __( 'Achors in Pagination', 'shortcodes-ultimate' ),
'desc' => __( 'Use this option to enable anchors in pagination links. As a result, after navigating to a new page, browser will scroll in the posts element.', 'shortcodes-ultimate' ),
),
'id' => array(
'name' => __( 'HTML Anchor (ID)', 'shortcodes-ultimate' ),
'desc' => __( 'Anchors lets you link directly to an element on a page', 'shortcodes-ultimate' ),
'default' => '',
),
'class' => array(
'type' => 'extra_css_class',
'name' => __( 'Extra CSS class', 'shortcodes-ultimate' ),
'desc' => __( 'Additional CSS class name(s) separated by space(s)', 'shortcodes-ultimate' ),
'default' => '',
),
),
'desc' => __( 'Custom posts query with customizable template', 'shortcodes-ultimate' ),
)
);
function su_shortcode_display_posts( $atts = null, $content = null ) {
$raw = (array) $atts;
$defaults = su_get_shortcode_defaults( 'display_posts' );
$atts = su_parse_shortcode_atts(
'display_posts',
$atts,
array( 'tax_relation' => 'AND' )
);
$atts['id'] = sanitize_html_class(
$atts['id'],
sprintf(
'su-display-posts-%s',
md5( wp_json_encode( $raw ) )
)
);
$atts['quality'] = sanitize_key( $atts['quality'] );
$atts['template'] = su_shortcode_display_posts_locate_template( $atts['template'] );
if ( ! $atts['template'] ) {
return su_error_message(
'Posts',
__( 'invalid template name', 'shortcodes-ultimate' )
);
}
$query = su_shortcode_display_posts_build_query( $raw, $atts, $defaults );
$su_query = new WP_Query( $query );
su_shortcode_display_posts_store( 'set', 'su_query', $su_query );
su_shortcode_display_posts_store( 'set', 'atts', $atts );
$output = su_shortcode_display_posts_include_template( $su_query, $atts );
wp_reset_postdata();
su_query_asset( 'css', 'su-shortcodes' );
return $output;
}
function su_shortcode_display_posts_locate_template( $template ) {
$template = su_set_file_extension( $template, 'php' );
$template = ltrim( $template, '\\/' );
$locations = array(
path_join( get_stylesheet_directory(), 'su-display-posts' ),
path_join( get_template_directory(), 'su-display-posts' ),
path_join( su_get_plugin_path(), 'includes/partials/shortcodes/display-posts' ),
);
foreach ( $locations as $location ) {
$path = path_join( $location, $template );
$path = realpath( $path );
if ( strpos( $path, $location ) === 0 && file_exists( $path ) ) {
return $path;
}
}
return false;
}
function su_shortcode_display_posts_include_template( $su_query, $atts ) {
ob_start();
include $atts['template'];
return ob_get_clean();
}
function su_shortcode_display_posts_build_query( $raw, $atts, $defaults ) {
$query = array();
$query['paged'] = su_shortcode_display_posts_get_current_page( $atts['id'] );
if ( $atts['author'] ) {
$query['author'] = sanitize_text_field( $atts['author'] );
}
$query['ignore_sticky_posts'] = 'yes' === $atts['ignore_sticky'];
if ( intval( $atts['offset'] ) ) {
$query['offset'] = intval( $atts['offset'] );
}
if ( $atts['meta_key'] ) {
$query['meta_key'] = sanitize_text_field( $atts['meta_key'] );
}
$query['order'] = sanitize_key( $atts['order'] );
$query['orderby'] = sanitize_key( $atts['orderby'] );
$query['post_status'] = sanitize_key( $atts['post_status'] );
$query['posts_per_page'] = intval( $atts['posts_per_page'] );
$atts['post_parent'] = str_replace(
'current',
get_the_ID(),
$atts['post_parent']
);
if ( is_numeric( $atts['post_parent'] ) ) {
$query['post_parent'] = intval( $atts['post_parent'] );
}
$atts['post_ids'] = array_map(
'intval',
array_filter( explode( ',', $atts['post_ids'] ), 'is_numeric' )
);
if ( ! empty( $atts['post_ids'] ) ) {
$query['post__in'] = $atts['post_ids'];
}
$atts['post_type'] = array_map(
'sanitize_text_field',
explode( ',', $atts['post_type'] )
);
$query['post_type'] = array_filter(
$atts['post_type'],
function( $item ) {
return ! empty( $item );
}
);
if ( $atts['exclude'] ) {
$atts['exclude'] = str_replace(
'current',
get_the_ID(),
$atts['exclude']
);
$query['post__not_in'] = array_map(
'intval',
explode( ',', $atts['exclude'] )
);
}
$query['tax_query'] = su_shortcode_display_posts_build_tax_query( $raw, $defaults );
if ( count( $query['tax_query'] ) > 1 ) {
$query['tax_query']['relation'] = strtoupper(
sanitize_key( $atts['tax_relation'] )
);
}
$query = apply_filters( 'su/shortcode/display_posts/query', $query, $atts, $raw );
return $query;
}
function su_shortcode_display_posts_build_tax_query( $raw, $defaults ) {
$tax_query = array();
for ( $i = 1; true; $i++ ) {
$raw[ "taxonomy_{$i}" ] = isset( $raw[ "taxonomy_{$i}" ] )
? sanitize_text_field( $raw[ "taxonomy_{$i}" ] )
: $defaults['taxonomy_1'];
if ( ! isset( $raw[ "tax_terms_{$i}" ] ) ) {
break;
}
$raw[ "tax_terms_{$i}" ] = array_map(
'sanitize_text_field',
explode( ',', $raw[ "tax_terms_{$i}" ] )
);
$raw[ "tax_terms_{$i}" ] = array_filter(
$raw[ "tax_terms_{$i}" ],
function( $item ) {
return ! empty( $item );
}
);
if ( ! isset( $raw[ "tax_operator_{$i}" ] ) ) {
$raw[ "tax_operator_{$i}" ] = $defaults['tax_operator_1'];
}
$raw[ "tax_operator_{$i}" ] = sanitize_text_field( $raw[ "tax_operator_{$i}" ] );
$raw[ "tax_operator_{$i}" ] = strtoupper( $raw[ "tax_operator_{$i}" ] );
if (
empty( $raw[ "taxonomy_{$i}" ] ) ||
empty( $raw[ "tax_terms_{$i}" ] ) ||
empty( $raw[ "tax_operator_{$i}" ] )
) {
break;
}
$tax_query[] = array(
'taxonomy' => $raw[ "taxonomy_{$i}" ],
'field' => is_numeric( $raw[ "tax_terms_{$i}" ][0] ) ? 'id' : 'slug',
'terms' => $raw[ "tax_terms_{$i}" ],
'operator' => $raw[ "tax_operator_{$i}" ],
);
}
return $tax_query;
}
function su_shortcode_display_posts_pagination() {
$atts = su_shortcode_display_posts_store( 'get', 'atts' );
if ( 'prev-next' === $atts['pagination'] ) {
return su_shortcode_display_posts_prevnext_pagination();
}
}
function su_shortcode_display_posts_prevnext_pagination() {
$prev = su_shortcode_display_posts_prevnext_pagination_link( 'prev' );
$next = su_shortcode_display_posts_prevnext_pagination_link( 'next' );
if ( ! $next && ! $prev ) {
return;
}
// phpcs:disable
printf( '', $prev, $next );
// phpcs:enable
}
function su_shortcode_display_posts_prevnext_pagination_link( $direction = 'next' ) {
$su_query = su_shortcode_display_posts_store( 'get', 'su_query' );
$atts = su_shortcode_display_posts_store( 'get', 'atts' );
$direction = sanitize_key( $direction );
$label = $atts[ 'pagination_' . $direction ];
$class = 'su-display-posts-pagination-' . $direction;
$key = su_shortcode_display_posts_get_pagination_key( $atts['id'] );
$current = su_shortcode_display_posts_get_current_page( $atts['id'] );
$total = $su_query->max_num_pages;
$template = apply_filters(
'su/shortcode/display_posts/prevnext_pagination_link_template',
'%3$s',
$atts,
$direction
);
if ( $current > $total ) {
return;
}
if ( 'next' === $direction && $current < $total ) {
$goto = $current + 1;
}
if ( 'prev' === $direction && $current > 1 ) {
$goto = $current - 1;
}
if ( ! isset( $goto ) ) {
return;
}
$url = 1 === $goto
? remove_query_arg( $key )
: add_query_arg( $key, $goto );
if ( 'yes' === $atts['pagination_anchor'] ) {
$url .= '#' . $atts['id'];
}
return sprintf(
$template,
esc_attr( $url ),
sanitize_html_class( $class ),
esc_html( $label )
);
}
function su_shortcode_display_posts_get_pagination_key( $id ) {
return sprintf(
apply_filters( 'su/shortcode/display_posts/pagination_key', '%s-page' ),
$id
);
}
function su_shortcode_display_posts_get_current_page( $id ) {
$key = su_shortcode_display_posts_get_pagination_key( $id );
// phpcs:disable
return isset( $_GET[ $key ] ) && is_numeric( $_GET[ $key ] ) && $_GET[ $key ] > 0
? intval( $_GET[ $key ] )
: 1;
// phpcs:enable
}
function su_shortcode_display_posts_store( $action = 'get', $key = '', $value = '' ) {
static $store;
if ( ! is_array( $store ) ) {
$store = array();
}
if ( 'get' === $action && ! empty( $key ) && isset( $store[ $key ] ) ) {
return $store[ $key ];
}
if ( 'set' === $action ) {
$store[ $key ] = $value;
}
}