Plugin Directory

Changeset 3179979


Ignore:
Timestamp:
11/01/2024 01:56:37 PM (17 months ago)
Author:
ambercouch
Message:

Update to version 1.5.1. Added support for grouping and refactored code

Location:
ac-custom-loop-shortcode/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • ac-custom-loop-shortcode/trunk/ac-wp-custom-loop-sc.php

    r2509295 r3179979  
    22/*
    33  Plugin Name: AC Custom Loop Shortcode
    4   Plugin URI: https://github.com/ambercouch/ac-wp-custom-loop-shortcode
     4  Plugin URI: https://ambercouch.co.uk
    55  Description: Shortcode  ( [ac_custom_loop] ) that allows you to easily list post, pages or custom posts with the WordPress content editor or in any widget that supports short code. A typical use would be to show your latest post on your homepage.
    66  Version: 1.5
     
    88  Author URI: http://ambercouch.co.uk
    99  Author Email: richard@ambercouch.co.uk
    10   Text Domain: ac-wp-custom-loop-shortcode
    11   Domain Path: /lang/
    12   License:
    13   Copyright 2018 AmberCouch
    14   This program is free software; you can redistribute it and/or modify
    15   it under the terms of the GNU General Public License as published by
    16   the Free Software Foundation; either version 2 of the License, or
    17   (at your option) any later version.
    18 
    19   This program is distributed in the hope that it will be useful,
    20   but WITHOUT ANY WARRANTY; without even the implied warranty of
    21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    22   GNU General Public License for more details.
    23 
    24   You should have received a copy of the GNU General Public License
    25   along with this program; if not, write to the Free Software
    26   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     10  Text Domain: ac-custom-loop-shortcode
     11  License: GPL-2.0+
     12  License URI: http://www.gnu.org/licenses/gpl-2.0.txt
    2713 */
    2814
    29 use Timber\PostQuery;
    30 
    3115defined('ABSPATH') or die('You do not have the required permissions');
    3216
    33 if (!function_exists('ac_wp_custom_loop_short_code'))
    34 {
    35 
    36     function ac_wp_custom_loop_short_code($atts)
     17function acclsc_get_template($timber, $template_path, $template_type , $template){
     18    $theme_directory = $template_path;
     19
     20    $twig_template_folder = false;
     21    if ($timber != false){
     22        $twig_template_folder = $theme_directory . 'templates/';
     23        $template = (substr($template, -5) === '.twig') ? substr_replace($template ,"",-5) :  $template;
     24        $theme_template = $template . '.twig';
     25        $theme_template_type = $template . '-' . $template_type . '.twig';
     26    }else{
     27
     28        //$theme_extention = (substr($template, -4) === '.php' || substr($template, -5) === '.twig' ) ? '' : '.php';
     29        $template = (substr($template, -4) === '.php') ? substr_replace($template ,"",-4) :  $template;
     30        $theme_template = $theme_directory . $template . '.php';
     31        $theme_template_type = $theme_directory . $template . '-' . $template_type . '.php';
     32    }
     33    if($timber != false){
     34
     35        if (file_exists($twig_template_folder.$theme_template_type))
     36        {
     37            $template = $theme_template_type;
     38
     39        }elseif (file_exists($twig_template_folder.$theme_template ))
     40        {
     41            $template = $theme_template;
     42        }else{
     43            $template = "loop-template.twig";
     44        }
     45    }else{
     46
     47        if (file_exists($theme_template_type))
     48        {
     49            $template = $theme_template_type;
     50
     51        }elseif (file_exists( $theme_template ))
     52        {
     53            $template = $theme_template;
     54        }else{
     55            $template = "loop-template.php";
     56        }
     57    }
     58    return $template;
     59}
     60
     61function acclsc_get_orderby($ids, $type){
     62
     63    if($ids){
     64        $orderby = 'post__in';
     65    }
     66    elseif ($type == 'post')
    3767    {
    38 
    39 
     68        $orderby = 'date';
     69    }
     70    else
     71    {
     72        $orderby = 'menu_order';
     73    }
     74
     75    return $orderby;
     76
     77}
     78
     79// Function to validate the post type
     80function acclsc_valid_post_type($type) {
     81    $post_types = get_post_types(array('public' => true), 'names');
     82    return in_array($type, $post_types) || $type == 'any';
     83}
     84
     85// Function to return an error message for invalid post types
     86function acclsc_invalid_post_type_message($type) {
     87    $post_types = get_post_types(array('public' => true), 'names');
     88    $output = '<p><strong>' . $type . '</strong> ' . __('is not a public post type on this website.') . '</p>';
     89    $output .= '<ul>';
     90    foreach ($post_types as $cpt) {
     91        $output .= '<li>' . $cpt . '</li>';
     92    }
     93    $output .= '</ul>';
     94    $output .= '<p>';
     95    $output .= __('Please edit the short code to use one of the available post types.', 'ac-wp-custom-loop-shortcode');
     96    $output .= '</p>';
     97    $output .= '<code>[ ac_custom_loop type="post" show="4"]</code>';
     98    return $output;
     99}
     100
     101// Function to enqueue CSS
     102function acclsc_enqueue_styles() {
     103    $handle = 'ac_wp_custom_loop_styles';
     104    if (!wp_script_is($handle, 'enqueued')) {
     105        wp_register_style('ac_wp_custom_loop_styles', plugin_dir_url(__FILE__) . 'assets/css/ac_wp_custom_loop_styles.css', array(), '20181016');
     106        wp_enqueue_style('ac_wp_custom_loop_styles');
     107    }
     108}
     109
     110// Function to build WP_Query arguments with support for multiple terms and exclusion terms
     111function acclsc_build_query_args($type, $show, $orderby, $order, $ignore_sticky_posts, $tax, $term, $exclude, $ids) {
     112    $args = array(
     113        'post_type' => $type,
     114        'posts_per_page' => $show,
     115        'orderby' => $orderby,
     116        'order' => $order,
     117        'ignore_sticky_posts' => $ignore_sticky_posts
     118    );
     119
     120    // Initialize the tax_query array
     121    $args['tax_query'] = array('relation' => 'AND');
     122
     123    // Add included terms if `tax` and `term` are provided
     124    if (!empty($tax) && !empty($term)) {
     125        $terms = explode(',', $term); // Split terms by comma
     126        $args['tax_query'][] = array(
     127            'taxonomy' => $tax,
     128            'field' => 'slug',
     129            'terms' => $terms,
     130            'operator' => 'AND' // Ensures posts match all terms in the array
     131        );
     132    }
     133
     134    // Add excluded terms if `exclude` is provided
     135    if (!empty($tax) && !empty($exclude)) {
     136        $exclude_terms = explode(',', $exclude); // Split exclude terms by comma
     137        $args['tax_query'][] = array(
     138            'taxonomy' => $tax,
     139            'field' => 'slug',
     140            'terms' => $exclude_terms,
     141            'operator' => 'NOT IN' // Excludes posts with any of these terms
     142        );
     143    }
     144
     145    // Include specific post IDs if provided
     146    if (!empty($ids)) {
     147        $args['post__in'] = explode(',', $ids);
     148    }
     149
     150    return $args;
     151}
     152
     153// Function to render PHP template
     154function acclsc_render_php_template($query, $template) {
     155    $output = '';
     156    while ($query->have_posts()) {
     157        $query->the_post();
     158        ob_start();
     159        include($template);
     160        $output .= ob_get_clean();
     161    }
     162    wp_reset_postdata();
     163    return $output;
     164}
     165
     166// Function to render Timber template
     167function acclsc_render_timber_template($query, $template) {
     168    $context = Timber::get_context();
     169    $context['posts'] = new Timber\PostQuery($query);
     170    ob_start();
     171    Timber::render($template, $context);
     172    return ob_get_clean();
     173}
     174
     175
     176// Function to handle queries with one or more subtax terms and group by term combinations
     177function acclsc_handle_subtax_query($query_args, $subtaxes, $timber, $template, $wrapper, $class) {
     178    $output = '';
     179    $subtaxonomies = explode(',', $subtaxes); // Split subtaxonomies by comma
     180    $grouped_posts = []; // Initialize grouped posts array
     181
     182    // Get terms for each subtaxonomy
     183    $terms_by_taxonomy = [];
     184    foreach ($subtaxonomies as $subtax) {
     185        $terms = get_terms(array(
     186            'taxonomy' => $subtax,
     187            'hide_empty' => true
     188        ));
     189        if (!empty($terms) && !is_wp_error($terms)) {
     190            $terms_by_taxonomy[$subtax] = $terms;
     191        }
     192    }
     193
     194    // Handle single subtax case
     195    if (count($subtaxonomies) == 1) {
     196        foreach ($terms_by_taxonomy[$subtaxonomies[0]] as $term) {
     197            $subtax_query_args = $query_args;
     198            $subtax_query_args['tax_query'][] = array(
     199                'taxonomy' => $subtaxonomies[0],
     200                'field' => 'slug',
     201                'terms' => $term->slug
     202            );
     203
     204            $query = new WP_Query($subtax_query_args);
     205
     206            if ($query->have_posts()) {
     207                $grouped_posts[$term->name] = [];
     208                while ($query->have_posts()) {
     209                    $query->the_post();
     210                    $grouped_posts[$term->name][] = get_post(get_the_ID());
     211                }
     212            }
     213            wp_reset_postdata();
     214        }
     215
     216    } else {
     217        // Multiple subtaxonomies case with nested grouping
     218        foreach ($terms_by_taxonomy[$subtaxonomies[0]] as $term_1) {
     219            foreach ($terms_by_taxonomy[$subtaxonomies[1]] as $term_2) {
     220                $subtax_query_args = $query_args;
     221                $subtax_query_args['tax_query'] = array('relation' => 'AND',
     222                                                        array(
     223                                                            'taxonomy' => $subtaxonomies[0],
     224                                                            'field' => 'slug',
     225                                                            'terms' => $term_1->slug
     226                                                        ),
     227                                                        array(
     228                                                            'taxonomy' => $subtaxonomies[1],
     229                                                            'field' => 'slug',
     230                                                            'terms' => $term_2->slug
     231                                                        ),
     232                                                        array(
     233                                                            'taxonomy' => $query_args['tax_query'][0]['taxonomy'],
     234                                                            'field' => 'slug',
     235                                                            'terms' => $query_args['tax_query'][0]['terms']
     236                                                        )
     237                );
     238
     239                $query = new WP_Query($subtax_query_args);
     240
     241                if ($query->have_posts()) {
     242                    // Nest posts under [fooTerm][barTerm] structure
     243                    if (!isset($grouped_posts[$term_1->name])) {
     244                        $grouped_posts[$term_1->name] = [];
     245                    }
     246                    $grouped_posts[$term_1->name][$term_2->name] = [];
     247
     248                    while ($query->have_posts()) {
     249                        $query->the_post();
     250                        $grouped_posts[$term_1->name][$term_2->name][] = get_post(get_the_ID());
     251                    }
     252                }
     253                wp_reset_postdata();
     254            }
     255        }
     256    }
     257
     258    // Render grouped posts using either Timber or PHP templates
     259    if ($wrapper == 'true') {
     260        $output .= '<div class="' . esc_attr($class) . '">';
     261    }
     262
     263    if ($timber && class_exists('Timber')) {
     264        $output .= acclsc_render_grouped_timber_template($grouped_posts, $template);
     265    } else {
     266        $output .= acclsc_render_grouped_php_template($grouped_posts, $template);
     267    }
     268
     269    if ($wrapper == 'true') {
     270        $output .= '</div>';
     271    }
     272
     273    return $output;
     274}
     275
     276// Function to render grouped posts using PHP template
     277function acclsc_render_grouped_php_template($grouped_posts, $template) {
     278
     279    error_log(print_r('acclsc_render_grouped_php_template', true));
     280    error_log(print_r('grouped_post', true));
     281    error_log(print_r($grouped_posts, true));
     282    $output = '';
     283    ob_start();
     284    include($template);
     285    $output .= ob_get_clean();
     286    wp_reset_postdata();
     287    return $output;
     288}
     289
     290// Function to render grouped posts using Timber template
     291function acclsc_render_grouped_timber_template($grouped_posts, $template) {
     292    $context = Timber::get_context();
     293    $context['grouped_posts'] = $grouped_posts;
     294    ob_start();
     295    Timber::render($template, $context);
     296    return ob_get_clean();
     297}
     298
     299if (!function_exists('acclsc_sc')) {
     300
     301    function acclsc_sc($atts) {
    40302        extract(shortcode_atts(array(
    41303            'type' => 'post',
    42             'show' => 4,
     304            'show' => '-1',
    43305            'template_path' => get_stylesheet_directory() . '/',
    44306            'template' => 'loop-template',
     
    51313            'tax' => '',
    52314            'term' => '',
     315            'subtax' => '', // New subtax parameter
    53316            'timber' => false,
     317            'exclude' => '',
    54318            'ids' => ''
    55 
    56319        ), $atts));
    57320
     321        // Validate post type
     322        if (!acclsc_valid_post_type($type)) {
     323            return acclsc_invalid_post_type_message($type);
     324        }
     325
     326        $output = '';
    58327        $template_type = $type;
    59328
    60         //default orderby
    61         if ($type == 'post' && $orderby == '')
    62         {
    63             $orderby = 'date';
    64         }
    65         elseif($orderby == '')
    66         {
    67             $orderby = 'menu_order';
    68         }
    69 
    70         if($ids != ''){
     329        // Handle IDs
     330        if ($ids != '') {
    71331            $ids = explode(',', $ids);
    72332            $type = 'any';
    73             $orderby = 'post__in';
    74 
    75         }
    76 
    77         $args = [
    78             'public' => true
    79         ];
    80         $output = '';
    81         $post_types = get_post_types($args, 'names');
    82 
    83         $theme_directory = $template_path;
    84 
    85 
    86         if ($timber != false){
    87             $twig_template_folder = $theme_directory . 'templates/';
    88             $template = (substr($template, -5) === '.twig') ? substr_replace($template ,"",-5) :  $template;
    89             $theme_template = $template . '.twig';
    90             $theme_template_type = $template . '-' . $template_type . '.twig';
    91         }else{
    92 
    93             //$theme_extention = (substr($template, -4) === '.php' || substr($template, -5) === '.twig' ) ? '' : '.php';
    94             $template = (substr($template, -4) === '.php') ? substr_replace($template ,"",-4) :  $template;
    95             $theme_template = $theme_directory . $template . '.php';
    96             $theme_template_type = $theme_directory . $template . '-' . $template_type . '.php';
    97         }
    98 
    99         $wrapperOpen = ($wrapper == 'true') ? '<div class="'.$class.'" >' : '';
    100         $wrapperClose = ($wrapper == 'true') ? '</div>' : '';
    101 
    102         if ($css == 'true')
    103         {
    104             $handle = 'ac_wp_custom_loop_styles';
    105             $list = 'enqueued';
    106 
    107             if (!wp_script_is($handle, $list))
    108             {
    109                 wp_register_style('ac_wp_custom_loop_styles', plugin_dir_url(__FILE__) . 'assets/css/ac_wp_custom_loop_styles.css', array(), '20181016');
    110                 wp_enqueue_style('ac_wp_custom_loop_styles');
     333        }
     334
     335        // Get the template path
     336        $template = acclsc_get_template($timber, $template_path, $template_type, $template);
     337
     338        // Check if the template exists
     339        if (!file_exists($template)) {
     340            return '<p>Template not found: ' . $template . '</p>';
     341        }
     342
     343        // Get the correct orderby
     344        $orderby = acclsc_get_orderby($ids, $type);
     345
     346        // Enqueue CSS if required
     347        if ($css == 'true') {
     348            acclsc_enqueue_styles();
     349        }
     350
     351        // Main Query Arguments
     352        $query_args = acclsc_build_query_args($type, $show, $orderby, $order, $ignore_sticky_posts, $tax, $term, $exclude, $ids);
     353
     354        // If no subtax is provided, use the default query and rendering behavior
     355        if (empty($subtax)) {
     356            // Execute the query
     357            $query = new WP_Query($query_args);
     358
     359            // Check if there are posts and render accordingly
     360            if ($query->have_posts()) {
     361                if ($wrapper == 'true') {
     362                    $output .= '<div class="' . esc_attr($class) . '">';
     363                }
     364
     365                // Use Timber or PHP template rendering
     366                if ($timber && class_exists('Timber')) {
     367                    $output .= acclsc_render_timber_template($query, $template);
     368                } else {
     369                    $output .= acclsc_render_php_template($query, $template);
     370                }
     371
     372                if ($wrapper == 'true') {
     373                    $output .= '</div>';
     374                }
    111375            }
    112         }
    113 
    114 if($timber != false){
    115 
    116     if (file_exists($twig_template_folder.$theme_template_type))
    117     {
    118         $template = $theme_template_type;
    119 
    120     }elseif (file_exists($twig_template_folder.$theme_template ))
    121     {
    122         $template = $theme_template;
    123     }else{
    124         $template = "loop-template.twig";
    125     }
    126 }else{
    127 
    128     if (file_exists($theme_template_type))
    129     {
    130         $template = $theme_template_type;
    131 
    132     }elseif (file_exists( $theme_template ))
    133     {
    134         $template = $theme_template;
    135     }else{
    136         $template = "loop-template.php";
    137     }
    138 }
    139 
    140         if (!in_array($type, $post_types) && $type != 'any')
    141         {
    142             $output .= '<p>';
    143             $output .= '<strong>' . $type . '</strong> ';
    144             $output .= __('is not a public post type on this website. The following post type are available: -', 'ac-wp-custom-loop-shortcode');
    145             $output .= '</p>';
    146             $output .= '<ul>';
    147 
    148             foreach ($post_types as $key => $cpt)
    149             {
    150                 $output .= '<li>' . $cpt . '</li>';
    151             }
    152             $output .= '</ul>';
    153             $output .= '<p>';
    154             $output .= __('Please edit the short code to use one of the available post types.', 'ac-wp-custom-loop-shortcode');
    155             $output .= '</p>';
    156             $output .= '<code>[ ac_custom_loop type="post" show="4"]</code>';
    157 
    158             return $output;
    159         }
    160 
    161         global $wp_query;
    162         $temp_q = $wp_query;
    163         $wp_query = null;
    164         $wp_query = new WP_Query();
    165         $wp_query->query(array(
    166             'post_type' => $type,
    167             'showposts' => $show,
    168             'orderby' => $orderby,
    169             'order' => $order,
    170             'ignore_sticky_posts' => $ignore_sticky_posts,
    171             'taxonomy' => $tax,
    172             'term' => $term,
    173             'post__in' =>  $ids
    174         ));
    175 
    176 
    177         if (have_posts()) :
    178             $output .= $wrapperOpen;
    179             if($timber === false){
    180 
    181                 while (have_posts()):
    182                     the_post();
    183                     ob_start();
    184                     ?>
    185                     <?php include("$template"); ?>
    186                     <?php
    187                     $output .= ob_get_contents();
    188                     ob_end_clean();
    189                 endwhile;
    190 
    191             }else{
    192                 if(class_exists('Timber')){
    193 
    194                     $context = Timber::get_context();
    195                     $context['posts'] = new Timber\PostQuery();
    196                     $templates = array( $template);
    197                     ob_start();
    198                     Timber::render( $templates, $context );
    199                     $output .= ob_get_contents();
    200                     ob_end_clean();
    201                 }else{
    202                     ob_start();
    203                     ?>
    204                         <?php echo "<p>The Timber plugin is not active.<br> Activate Timber or set <code>timber='false'</code> in the short code</p>" ?>
    205                     <?php
    206                     $output .= ob_get_contents();
    207                     ob_end_clean();
    208                 }
    209 
    210             }
    211             $output .= $wrapperClose;
    212         endif;
    213 
    214         if (have_posts()) :
    215 
    216         endif;
    217 
    218         $wp_query = $temp_q;
     376
     377        } else {
     378            // If subtax is provided, query the terms and group the results by subtax term
     379            $output .= acclsc_handle_subtax_query($query_args, $subtax, $timber, $template, $wrapper, $class);
     380        }
    219381
    220382        return $output;
    221 
    222     }
    223 
    224     add_shortcode('ac_custom_loop', 'ac_wp_custom_loop_short_code');
    225 
    226 }
     383    }
     384
     385    add_shortcode('ac_custom_loop', 'acclsc_sc');
     386}
  • ac-custom-loop-shortcode/trunk/readme.txt

    r2509295 r3179979  
    33Donate link: http://ambercouch.co.uk/
    44Tags: shortcode, list post, list custom posts, timber, twig, custom post type
    5 Requires at least: 4.6
    6 Tested up to: 5.7
    7 Stable tag: 1.5
     5Requires at least: 5.2
     6Tested up to: 6.6
     7Stable tag: 1.5.1
    88Requires PHP: 5.2.4
    99License: GPLv2 or later
     
    2828== Frequently Asked Questions ==
    2929
     30= How do I display a certain post type using the short code
     31
     32[ac_custom_loop type="foo"]
     33this displays foo custom post types
     34
     35= How do I display a post that have a certain tag =
     36
     37[ac_custom_loop type="post" tax="tag" term="foo" ]
     38this displays posts that have been tagged foo
     39
     40= How do I group post in to tags =
     41
     42[ac_custom_loop type="post" subtax="category,tag"]
     43this displays posts grouped into category and sub-grouped in to tags
     44
     45Cat Foo
     46  Tag bar
     47    post1, post2
     48  tag baz
     49    post3, post4
     50Cat Foo2
     51  Tag bar2
     52    post5, post6
     53
    3054= Can I use my own template to display the looped posts? =
    3155
     
    3963
    4064== Upgrade Notice ==
     65
     66= 1.5.1 =
     67Added support for grouping posts by taxonomies.
    4168
    4269= 1.5 =
     
    6289
    6390== Changelog ==
     91
     92= 1.5.1 (2024-11-01) =
     93* Added support for grouping posts by taxonomies.
     94* Refactored all the code
    6495
    6596= 1.5 (2021-04-04) =
Note: See TracChangeset for help on using the changeset viewer.