picandocodigo/List-Category-Posts

View on GitHub
include/lcp-catlist.php

Summary

Maintainability
F
3 days
Test Coverage
<?php
define( 'LCP_PATH', plugin_dir_path( __FILE__ ) );
require_once ( LCP_PATH . 'lcp-thumbnail.php' );
require_once ( LCP_PATH . 'lcp-parameters.php' );
require_once ( LCP_PATH . 'lcp-utils.php' );
require_once ( LCP_PATH . 'lcp-category.php' );
require_once ( LCP_PATH . 'lcp-paginator.php' );

/**
 * The CatList object gets the info for the CatListDisplayer to show.
 * Each time you use the shortcode, you get an instance of this class.
 * @author fernando@picandocodigo.net
 */
class CatList{
  private $params = array();
  private $lcp_category_id = 0;
  private $page = 1;
  private $posts_count = 0;
  private $instance = 0;
  private $utils;
  private $wrapper;

  /**
   * Constructor gets the shortcode attributes as parameter
   * @param array $atts
   */
  public function __construct($atts) {
    load_plugin_textdomain(
      'list-category-posts',
      false,
      dirname( plugin_basename( __FILE__ ) ) . '/languages/'
    );
    $this->params = $atts;
    $this->utils = new LcpUtils($this->params);
    $this->wrapper = LcpWrapper::get_instance();

    if ( $this->utils->lcp_not_empty('instance') ){
      $this->instance = $atts['instance'];
    }
  }

  /**
   * Determine the categories of posts and execute the WP_query
   */
  public function get_posts() {
     return $this->set_lcp_parameters();
  }

  /**
   * Save the existing wp_query
   */
  public function save_wp_query() {
    global $wp_query;
    if ( isset($wp_query) ) {
      $this->saved_wp_query = clone $wp_query;
      global $post;
      $this->saved_post = $post;
    }
  }

  /**
   * Restore the previous wp_query
   */
  public function restore_wp_query() {
    global $wp_query;
    if ( isset($this->saved_wp_query) ) {
      $wp_query = clone $this->saved_wp_query;
      global $post;
      $post = $this->saved_post;
    }
  }

  /**
   * Order the parameters and query the DB for posts
   */
  private function set_lcp_parameters(){
    $args = LcpCategory::get_instance()->get_lcp_category([
      'id'               => $this->params['id'],
      'name'             => $this->params['name'],
      'categorypage'     => $this->params['categorypage'],
      'child_categories' => $this->params['child_categories'],
      'main_cat_only'    => $this->params['main_cat_only'],
    ], $this->lcp_category_id);
    $processed_params = LcpParameters::get_instance()->get_query_params($this->params);
    $args = array_merge($args, $processed_params);
    $args = $this->check_pagination($args);

    // for WP_Query compatibility
    // http://core.trac.wordpress.org/browser/tags/3.7.1/src/wp-includes/post.php#L1686
    $args['posts_per_page'] = $args['numberposts'];

    do_action( 'lcp_pre_run_query', $args );

    if ('no' === $this->params['main_query']) {
      // Use a standard Loop with WP_Query.
      $lcp_query = new WP_Query($args);
    } else {
      // Run as a main query.
      query_posts($args);
      global $wp_query;
      $lcp_query = $wp_query;
    }
    $this->posts_count = $lcp_query->found_posts;

    if ( $this->params[ 'keep_orderby_filters' ] !== 'yes' ) {
      remove_all_filters('posts_orderby');
    }
    remove_filter('posts_where', array(LcpParameters::get_instance(), 'starting_with'));
    remove_filter('posts_results', [ LcpCategory::get_instance(), 'filter_by_main_category' ]);

    return $lcp_query;
  }

  /* Should I return posts or show that the tag/category or whatever
     posts combination that I called has no posts? By default I've
     always returned the latest posts because that's what the query
     does when the params are "wrong". But could make for a better user
     experience if I returned an empty list in certain cases.
     private function lcp_should_return_posts() */

  /** HELPER FUNCTIONS **/

  private function check_pagination($args) {
    if (LcpUtils::lcp_show_pagination($this->params['pagination'])) {
      if(array_key_exists('QUERY_STRING', $_SERVER) && null !== $_SERVER['QUERY_STRING'] ) {
        $query = $_SERVER['QUERY_STRING'];
        if ($query !== '' && preg_match('/lcp_page' . preg_quote($this->instance) .
                                        '=([0-9]+)/i', $query, $match)) {
          $this->page = $match[1];
          $offset = ($this->page - 1) * $this->params['numberposts'];
          $args = array_merge($args, array('offset' => $offset));
        }
      }
    }
    return $args;
  }

  public function get_category_id() {
    return $this->lcp_category_id;
  }

  /**
   * This is no longer used by templates. Keeping for
   * backward compatibility with old templates.
   */
  public function get_categories_posts(){
    global $wp_query;
    return $wp_query->get_posts();
  }

  /**
   * Load category name and link to the category:
   */
  public function get_category_link() {
    if(($this->params['catlink'] == 'yes' ||
        $this->params['catname'] == 'yes') &&
       $this->lcp_category_id != 0){
      // Check for one id or several:
      $ids = null;
      if (is_array($this->lcp_category_id)) {
        $ids = $this->lcp_category_id;
      } else {
        $ids = explode(",", $this->lcp_category_id);
      }

      $link = array();
      // Loop on several categories:
      foreach($ids as $lcp_id) {
        $cat_link = get_category_link($lcp_id);
        $cat_title = get_cat_name($lcp_id);

        // Use the category title or 'catlink_string' set by user:
        if ($this->utils->lcp_not_empty('catlink_string')){
          $cat_string = $this->params['catlink_string'];
        } else {
          $cat_string = $cat_title;
        }

        $cat_string = esc_html($cat_string);

        // Do we want the link or just the title?
        if ($this->params['catlink'] == 'yes') {
          $cat_string = $this->wrapper->to_html(
            'a',
            ['href' => esc_url($cat_link)],
            $cat_string . $this->get_category_count()
          );
        }

        array_push($link, $cat_string);
      }
      return implode(", ", $link);
    } else {
      return null;
    }
  }

  /**
   * Load morelink name and link to the category:
   */
  public function get_morelink() {
    if (!empty($this->params['morelink'])) {
      $props = ['href' => esc_url(get_category_link($this->lcp_category_id))];
      $readmore = ($this->params['morelink'] !== '' ? esc_html($this->params['morelink']) : 'More posts');
      return $this->wrapper->to_html('a', $props, $readmore);
     } else {
      return null;
    }
  }

  public function get_posts_morelink($single, $css_class) {
    if(!empty($this->params['posts_morelink'])) {
      $props = ['href' => esc_url(get_permalink($single->ID))];
      $class = $css_class ?: "";
      if ($class) {
        $props['class'] = esc_attr($class);
      }
      $readmore = esc_html($this->params['posts_morelink']);
      return $this->wrapper->to_html('a', $props, $readmore);
    }
  }

  public function get_category_count() {
    if($this->utils->lcp_not_empty('category_count') && $this->params['category_count'] == 'yes') {
      return ' ' . esc_html(get_category($this->lcp_category_id)->category_count);
    }
  }

  public function get_category_description() {
    if ($this->utils->lcp_not_empty('category_description') && $this->params['category_description'] == 'yes') {
      return esc_html(get_term_field('description', $this->lcp_category_id, '', 'raw'));
    }
  }

  public function get_conditional_title() {
    if($this->utils->lcp_not_empty('conditional_title') && $this->get_posts_count() > 0) {
      return esc_html(trim($this->params['conditional_title']));
    }
  }

  /**
   * Array of custom fields.
   * @see http://codex.wordpress.org/Function_Reference/get_post_custom
   * @param string $custom_key
   * @param int $post_id
   */
  public function get_custom_fields($custom_key, $post_id) {
    if ($this->utils->lcp_not_empty('customfield_display')) {
      $lcp_customs = array();

      //Doesn't work for many custom fields when having spaces:
      $custom_key = trim($custom_key);

      //Create array for many fields:
      $custom_array = explode(',', $custom_key);

      //Get post custom fields:
      $custom_fields = get_post_custom($post_id);

      //Loop on custom fields and if there's a value, add it:
      foreach ($custom_array as $user_customfield) {
        // Check that the custom field is wanted:
        if (isset( $custom_fields[$user_customfield])) {
          //Browse through the custom field values:
          foreach ($custom_fields[$user_customfield] as $key => $value) {
            if ($this->params['customfield_display_name'] != 'no' && $value !== '' ){
              $value = $user_customfield . $this->params['customfield_display_name_glue'] . $value;
            }
            if($value != '') {
              $lcp_customs[] = wp_kses_post($value);
            }
          }
        }
      }

      // Return a string instead of array if custom fields
      // are not displayed separately.
      if ($this->params['customfield_display_separately'] === 'no') {
        $lcp_customs = implode($this->params['customfield_display_glue'], $lcp_customs);
      }

      return $lcp_customs;
    } else {
      return null;
    }
  }

  public function get_comments_count($single) {
    if (isset($this->params['comments']) &&
        $this->params['comments'] == 'yes') {
      return esc_html(' (' . $single->comment_count . ')');
    } else {
      return null;
    }
  }

  /**
   * Parse posts_tags parameters
   */
  public function get_posts_terms($single, $tax) {
    // Check if terms should be displayed.
    if ('yes' !== $this->params["posts_{$tax}s"])  {
      return null;
    }
    // Get parsed parameters.
    $params = $this->get_pt_params($tax);

    // Get the post's terms IDs, returns false if post has none.
    $terms = get_the_terms($single->ID, $params['tax_slug']);

    // Construct output string based on $params.
    $output = '';
    if (is_array($terms)) {
      $output .= esc_html($params['prefix']);
      $parsed_terms = array();
      foreach($terms as $term) {
        $term_string = $term->name;
        $term_string = $this->pt_link_wrapper($params['link'], $term, $term_string);
        $term_string = $this->pt_inner_wrapper(
          $params['inner'],
          $tax,
          $term,
          $term_string
        );
        $parsed_terms[] = $term_string;
      }
      $output .= implode($params['glue'], $parsed_terms);
      return $output;
    } else {
      return null;
    }
  }

  private function get_pt_params($tax) {
    $taxonomies = ['cat' => 'category', 'tag' => 'post_tag'];
    $slug =  array_key_exists($tax, $taxonomies) ? $taxonomies[$tax] : '';
    return array(
      'tax_slug' => $slug,
      'link'     => 'yes' === $this->params["posts_{$tax}link"] ? true : false,
      'prefix'   => $this->params["posts_{$tax}s_prefix"] ?: null,
      'glue'     => $this->params["posts_{$tax}s_glue"] ?: '',
      'inner'    => $this->params["posts_{$tax}s_inner"] ?: null,
    );
  }

  private function pt_link_wrapper($link, $term, $term_string) {
    if ($link) {
      $term_string = $this->wrapper->to_html(
        'a',
        ['href' => esc_url(get_term_link($term->term_id))],
        esc_html($term_string)
      );
    }
    return $term_string;
  }

  private function pt_inner_wrapper($inner, $tax, $term, $term_string_safe) {
    if ($inner) {
      $term_string_safe = $this->wrapper->to_html(
        $inner,
        ['class' => esc_attr("{$tax}-{$term->slug}")],
        $term_string_safe
      );
    }
    return $term_string_safe;
  }

  public function get_author_to_show($single) {
    if ($this->params['author'] == 'yes') {
      $lcp_userdata = get_userdata($single->post_author);
      $author_name =  $lcp_userdata->display_name;
      if($this->utils->lcp_not_empty('author_posts_link') &&
         $this->params['author_posts_link'] == 'yes') {
        $link = get_author_posts_url($lcp_userdata->ID);
        return $this->wrapper->to_html(
          'a',
          ['href' => esc_url($link), 'title' => esc_attr($author_name)],
          $author_name
        );
      } else {
        return esc_html($author_name);
      }
    } else {
      return null;
    }
  }


  /** Pagination **/
  public function get_page() {
    return $this->page;
  }

  // Helper method for tests.
  public function update_page($page) {
    $this->page = $page;
  }

  public function get_posts_count() {
    return $this->posts_count;
  }

  public function get_number_posts() {
    return $this->params['numberposts'];
  }

  public function get_instance() {
    return $this->instance;
  }

  public function get_date_to_show($single) {
    if ($this->params['date'] == 'yes') {
      //by Verex, great idea!
      return ' ' . esc_html(get_the_time($this->params['dateformat'], $single));
    } else {
      return null;
    }
  }

  public function get_modified_date_to_show($single) {
    if ($this->params['date_modified'] == 'yes') {
      return " " . get_the_modified_time($this->params['dateformat'], $single);
    } else {
      return null;
    }
  }

  public function get_display_id($single) {
    if (!empty($this->params['display_id']) && $this->params['display_id'] == 'yes') {
      return esc_html($single->ID);
    }
  }

  public function get_no_posts_text() {
    if (($this->get_posts_count() == 0) &&
        ($this->params["no_posts_text"] != '')) {
      return esc_html($this->params["no_posts_text"]);
    }
  }

  public function get_content($single) {
    if (isset($this->params['content']) &&
        ($this->params['content'] =='yes' || $this->params['content'] == 'full') &&
        $single->post_content){
      // get_extended - get content split by <!--more-->
      $lcp_extended = get_extended($single->post_content);
      $lcp_content = $lcp_extended['main'];
      $lcp_content = apply_filters('the_content', $lcp_content);
      $lcp_content = str_replace(']]>', ']]&gt', $lcp_content);

      if ($this->params['content'] == 'full') {
        $lcp_extended_content = str_replace(
          ']]>',
          ']]&gt', apply_filters('the_content', $lcp_extended['extended'])
        );
        $lcp_content .= $lcp_extended_content;
      } else {
        if (empty($this->params['posts_morelink'])) {
          $lcp_more = __('Continue reading &rarr;', 'list-category-posts');
          $lcp_content .= $this->wrapper->to_html(
            'a',
            ['href' => esc_url(get_permalink($single->ID)), 'title' => esc_attr($lcp_more)],
            $lcp_more
          );
        }
      }
      return $lcp_content;
    } else {
        return null;
    }
  }

  public function get_excerpt($single) {
    if (!empty( $this->params['excerpt']) &&
         ($this->params['excerpt']=='yes' || $this->params['excerpt']=='full')) {

      if($single->post_excerpt == "" ||
         (!empty($this->params['excerpt_overwrite']) && $this->params['excerpt_overwrite'] == 'yes')) {
        // No explicit excerpt or excerpt_overwrite=yes, so generate from content:
        $lcp_content = $single->post_content;
        // <!--more--> tag?
        if($this->params['excerpt']=='full' &&
          preg_match('/[\S\s]+(<!--more(.*?)?-->)[\S\s]+/', $lcp_content, $matches)) {
          $lcp_excerpt = explode($matches[1], $lcp_content);
          $lcp_excerpt = $lcp_excerpt[0];
          $lcp_excerpt = apply_filters('the_excerpt', $lcp_excerpt);
        } else {
          $lcp_excerpt = $this->lcp_trim_excerpt($lcp_content);
        }
      } else {
        // Explicit excerpt and excerpt_overwrite=no:
        if($this->params['excerpt']=='full') {
          $lcp_excerpt = $single->post_excerpt;
        } else {
          $lcp_excerpt = $this->lcp_trim_excerpt($single->post_excerpt);
        }
      }
      if(strlen($lcp_excerpt) < 1 ) {
        $lcp_excerpt = $single->post_title;
      }
      return $lcp_excerpt;
    }
  }

  private function lcp_trim_excerpt($text = '') {
    $excerpt_length = intval($this->params['excerpt_size']);

    $text = strip_shortcodes($text);
    $text = apply_filters('the_excerpt', $text);
    $text = str_replace(']]>',']]&gt;', $text);

    if($this->utils->lcp_not_empty('excerpt_strip') &&
       $this->params['excerpt_strip'] == 'yes') {
      $text = strip_tags($text);
    }

    $words = explode(' ', $text, $excerpt_length + 1);
    if(count($words) > $excerpt_length) {
      array_pop($words);
      array_push($words, '...');
      $text = implode(' ', $words);
    }
    return $text;
  }

  public function get_thumbnail($single, $lcp_thumb_class = null) {
    if ($this->utils->lcp_not_empty('force_thumbnail')) {
      $force_thumbnail = $this->params['force_thumbnail'];
    } else {
      $force_thumbnail = 'no';
    }
    return LcpThumbnail::get_instance()->get_thumbnail(
      $single,
      $this->params['thumbnail'],
      $this->params['thumbnail_size'],
      $force_thumbnail,
      $lcp_thumb_class
    );
  }

  public function get_outer_tag($tag, $css_class) {
    $css_class = $this->params['class'] ?: $css_class;

    $props = [];
    if ($tag == 'ol' && !empty($this->params['ol_offset'])) {
      $props['start'] = esc_attr($this->params['ol_offset']);
    }

    // Follow the number of posts in an ordered list with pagination.
    if('ol' === $tag && $this->page > 1) {
      $start = $this->get_number_posts() * ($this->page - 1) + 1;
      $props['start'] = esc_attr($start);
    }
    //Give a class to wrapper tag
    $props['class'] = LcpUtils::sanitize_html_classes($css_class);

    //Give id to wrapper tag
    $props['id'] = esc_attr('lcp_instance_' . $this->instance);

    return $this->wrapper->to_html($tag, $props, null, false);
  }

  public function get_inner_tag($single, $parent, $tag, $css_class='') {
    $class = $css_class;
    $props = [];
    if (is_object( $parent) && is_object($single) &&
        $parent->ID === $single->ID) {
      $class .= 'current';
    }

    if ($this->params['tags_as_class'] === 'yes') {
      $post_tags = wp_get_post_Tags($single->ID);
      if (!empty($post_tags)) {
        foreach ($post_tags as $post_tag) {
          $class .= esc_attr(" $post_tag->slug ");
        }
      }
    }
    if (!empty($class)) {
      $props['class'] = esc_attr($class);
    }
    return $this->wrapper->to_html($tag, $props, null, false);
  }

  public function get_pagination() {
    $paginator_params = array(
          'bookmarks'   => $this->params['pagination_bookmarks'],
          'instance'    => $this->get_instance(),
          'next'        => $this->params['pagination_next'],
          'numberposts' => $this->get_number_posts(),
          'padding'     => $this->params['pagination_padding'],
          'page'        => $this->get_page(),
          'pagination'  => $this->params['pagination'],
          'posts_count' => $this->get_posts_count(),
          'previous'    => $this->params['pagination_prev'],
    );
    return LcpPaginator::get_instance()->get_pagination($paginator_params);
  }
}