/**
  * Get the data being exported.
  *
  * @access public
  * @since  8.5.5
  *
  * @return array $data Data for Export
  */
 public function getData()
 {
     /** @var wpdb $wpdb */
     //global $wpdb;
     $data = array();
     $offset = $this->limit * ($this->step - 1);
     $results = cnTerm::getTaxonomyTerms($this->type, array('hide_empty' => FALSE, 'hierarchical' => FALSE, 'offset' => $offset, 'number' => $this->limit));
     $count = cnTerm::getTaxonomyTerms($this->type, array('hide_empty' => FALSE, 'fields' => 'count'));
     $this->setCount($count);
     $i = 0;
     $terms = $this->buildHierarchy($results);
     foreach ($terms as $term) {
         $data[$i]['id'] = $term->term_id;
         $data[$i]['name'] = $term->name;
         $data[$i]['desc'] = $term->description;
         $data[$i]['slug'] = $term->slug;
         $data[$i]['hierarchy'] = $term->hierarchy;
         $i++;
     }
     $data = apply_filters('cn_export_get_data', $data);
     $data = apply_filters('cn_export_get_data_' . $this->type, $data);
     return $data;
 }
 /**
  * Render an checklist of terms.
  *
  * This is the Connections equivalent of @see wp_terms_checklist() in WordPress core ..wp-admin/wp-includes/template.php
  *
  * @access public
  * @since  8.2
  * @static
  *
  * @param array $atts {
  *     Optional. An array of arguments.
  *     NOTE: Additionally, all valid options as supported in @see cnTerm::getTaxonomyTerms().
  *
  * @type bool   $show_count        Whether or not to display the category count.
  *                                 Default: FALSE
  * @type string $name              The select name attribute.
  *                                 Default: 'cat'
  * @type int    $depth             Controls how many levels in the hierarchy of categories are to be included in the list.
  *                                 Default: 0
  *                                 Accepts: 0  - All categories and child categories.
  *                                          -1 - All Categories displayed  flat, not showing the parent/child relationships.
  *                                          1  - Show only top level/root parent categories.
  *                                          n  - Value of n (int) specifies the depth (or level) to descend in displaying the categories.
  * @type string $taxonomy          The taxonomy tree to display.
  *                                 Default: 'category'
  *                                 Accepts: Any registered taxonomy.
  * @type mixed  $selected          The selected term ID(s) the term ID or array of term ID/s that are selected.
  *                                 Default: 0
  * @type bool   $return            Whether or not to return or echo the resulting HTML.
  *                                 Default: FALSE
  * }
  *
  * @return string
  */
 public static function render($atts = array())
 {
     $out = '';
     $defaults = array('orderby' => 'name', 'order' => 'ASC', 'show_count' => FALSE, 'hide_empty' => FALSE, 'name' => 'entry_category', 'depth' => 0, 'taxonomy' => 'category', 'selected' => 0, 'return' => FALSE);
     $atts = wp_parse_args($atts, $defaults);
     if (!is_array($atts['selected'])) {
         $atts['selected'] = array(absint($atts['selected']));
     } else {
         array_walk($atts['selected'], 'absint');
     }
     $walker = new self();
     $walker->tree_type = $atts['taxonomy'];
     $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], $atts);
     if (!empty($terms)) {
         $out .= '<ul id="' . esc_attr($atts['taxonomy']) . 'checklist" class="' . esc_attr($walker->tree_type) . 'checklist form-no-clear">';
         $out .= $walker->walk($terms, $atts['depth'], $atts);
         $out .= '</ul>';
     }
     if ($atts['return']) {
         return $out;
     }
     echo $out;
 }
 /**
  * Prepares the list of items for displaying.
  *
  * @access public
  * @since  8.2
  *
  * @uses   WP_List_Table::get_items_per_page()
  * @uses   WP_List_Table::get_pagenum()
  * @uses   WP_List_Table::set_pagination_args()
  * @uses   apply_filters()
  * @uses   wp_unslash()
  * @uses   cnTerm::getTaxonomyTerms()
  * @uses   CN_Term_Admin_List_Table::get_columns()
  * @uses   CN_Term_Admin_List_Table::get_hidden_columns()
  * @uses   CN_Term_Admin_List_Table::get_sortable_columns()
  */
 public function prepare_items()
 {
     // @todo this should be a screen option.
     $per_page = $this->get_items_per_page("cn_edit_{$this->taxonomy}_per_page", 100);
     /**
      * Filter the number of terms displayed per page for the terms list table.
      *
      * @since 8.2
      *
      * @param int $per_page Number of terms to be displayed.
      */
     $per_page = apply_filters("cn_edit_{$this->taxonomy}_per_page", $per_page);
     /**
      * NOTE:
      * Several of the $args vars are required in other parts of the class
      * which is why they are also assigned to class vars as well as the local
      * $args array var.
      */
     $this->search = !empty($_REQUEST['s']) ? trim(wp_unslash($_REQUEST['s'])) : '';
     $args = array('page' => $this->get_pagenum(), 'number' => $per_page, 'hide_empty' => FALSE, 'search' => $this->search);
     if (!empty($_REQUEST['orderby'])) {
         $args['orderby'] = $this->orderby = trim(wp_unslash($_REQUEST['orderby']));
     }
     if (!empty($_REQUEST['order'])) {
         $args['order'] = trim(wp_unslash($_REQUEST['order']));
     }
     // Set variable because $args['number'] can be subsequently overridden if doing an orderby term query.
     $this->number = $args['number'];
     $args['offset'] = $this->offset = ($args['page'] - 1) * $args['number'];
     // Query the all of terms.
     if (is_null($this->orderby)) {
         $args['number'] = $args['offset'] = 0;
     }
     $this->items = cnTerm::getTaxonomyTerms($this->taxonomy, $args);
     $this->set_pagination_args(array('total_items' => cnTerm::getTaxonomyTerms($this->taxonomy, array('hide_empty' => FALSE, 'search' => $this->search, 'fields' => 'count')), 'per_page' => $per_page));
     /**
      * NOTE: If these methods are overridden @see WP_List_Table::get_column_info(),
      * then the column filter in @see get_column_headers() is not run.
      *
      * As a workaround filters are added to the following methods. The downside
      * is that the screen options to hide columns are not added. The only way for that
      * to happen seems to be to init the table class on the `load-{page-hook}` action
      * and set it as a global var so it can be accessed in the callback function that
      * renders the plugin's admin page.
      */
     $columns = $this->get_columns();
     $hidden = $this->get_hidden_columns();
     $sortable = $this->get_sortable_columns();
     $this->_column_headers = array($columns, $hidden, $sortable);
 }
 /**
  * Display or retrieve the HTML select list of terms.
  *
  * This is the Connections equivalent of @see wp_dropdown_categories() in WordPress core ../wp-includes/category-template.php
  *
  * @access public
  * @since  8.2.4
  * @static
  *
  * @uses   wp_parse_args()
  * @uses   cnTerm::getTaxonomyTerms()
  * @uses   esc_attr()
  * @uses   sanitize_html_class()
  * @uses   apply_filters()
  * @uses   Walker::walk()
  * @uses   selected()
  *
  * @param array $atts {
  *     Optional. An array of arguments.
  *     NOTE: Additionally, all valid options as supported in @see cnTerm::getTaxonomyTerms().
  *
  * @type string $taxonomy          The taxonomy tree to display.
  *                                 Default: 'category'
  * @type bool   $hierarchical      Whether to include terms that have non-empty descendants, even if 'hide_empty' is set to TRUE.
  *                                 Default: TRUE
  * @type string $type              The output type of the categories.
  *                                 Default: select
  *                                 Accepts: select || multiselect
  * @type bool   $group             Whether or not to create option groups using the root parent as the group label.
  *                                 Default: FALSE
  * @type bool   $hide_if_empty     Whether or not to show the select if no terms are returned by term query.
  *                                 Default: FALSE
  * @type string $name              The select name attribute.
  *                                 Default: 'cn-cat'
  * @type string $id                The select id attribute.
  *                                 Default: ''
  * @type array  $class             An array if classes to applied to the select.
  *                                 Default: array('cn-category-select')
  * @type array  $style             An array of style to applied inline where the key is the style attribute and the value is the style attribute value.
  *                                 Default: array()
  * @type bool   $enhanced          Whether of not apply the required attributes for the Chosen jQuery plugin.
  *                                 Default: TRUE
  * @type string $on_change         An inline JavaScript on_change event.
  *                                 Default: ''
  *                                 Accepts: Any valid inline JavaScript.
  * @type int    $tab_index         The tab index of the select.
  *                                 Default: 0
  * @type bool   $show_select_all   Whether or not to render the $show_option_all option.
  *                                 Default: TRUE
  * @type string $show_option_all   A non-blank value causes the display of a link to the directory home page.
  *                                 Default: ''. The default is not to display a link.
  *                                 Accepts: Any valid string.
  * @type string $show_option_none  Set the text to show when no categories are listed.
  *                                 Default: 'No Categories'
  *                                 Accepts: Any valid string.
  * @type string $option_none_value Value to use when no term is selected.
  *                                 Default: -1
  *                                 Accepts: Any valid int/string for an option value attribute.
  * @type string $default           The default string to show as the first item in the list.
  *                                 Default: 'Select Category'
  * @type bool   $show_count        Whether or not to display the category count.
  *                                 Default: FALSE
  * @type bool   $hide_empty        Whether or not to display empty terms.
  *                                 Default: FALSE
  * @type int    $depth             Controls how many levels in the hierarchy of categories are to be included in the list.
  *                                 Default: 0
  *                                 Accepts: 0  - All categories and child categories.
  *                                          -1 - All Categories displayed  flat, not showing the parent/child relationships.
  *                                          1  - Show only top level/root parent categories.
  *                                          n  - Value of n (int) specifies the depth (or level) to descend in displaying the categories.
  * @type array  $parent_id
  * @type array  $selected          The selected term IDs.
  *                                 Default: 0
  * @type string $label             The label to render with the select.
  *                                 Default: ''
  * @type string $before            Content to be render before the label and select.
  *                                 Default: ''
  * @type string $after             Content to be render after the label and select.
  *                                 Default: ''
  * @type string $layout            Tokens which can be sued to control the order of the label and select.
  *                                 Default: '%label%%field%'
  *                                 Accepts: %label% %field%
  * @type bool   $return            Whether or not to return or echo the resulting HTML.
  *                                 Default: FALSE
  * }
  *
  * @return string
  */
 public static function render($atts = array())
 {
     $select = '';
     $out = '';
     $defaults = array('taxonomy' => 'category', 'hierarchical' => TRUE, 'type' => 'select', 'group' => FALSE, 'hide_if_empty' => FALSE, 'name' => 'cn-cat', 'id' => '', 'class' => array('cn-category-select'), 'style' => array(), 'enhanced' => TRUE, 'on_change' => '', 'tab_index' => 0, 'show_select_all' => TRUE, 'show_option_all' => '', 'show_option_none' => '', 'option_none_value' => -1, 'default' => __('Select Category', 'connections'), 'show_count' => FALSE, 'hide_empty' => FALSE, 'depth' => 0, 'parent_id' => array(), 'selected' => 0, 'label' => '', 'before' => '', 'after' => '', 'layout' => '%label%%field%', 'return' => FALSE);
     $atts = wp_parse_args($atts, $defaults);
     if (wp_is_mobile()) {
         $atts['enhanced'] = FALSE;
     }
     // The field parts to be searched for in $atts['layout'].
     $search = array('%label%', '%field%');
     // An array to store the replacement strings for the label and field.
     $replace = array();
     $walker = new self();
     $walker->tree_type = $atts['taxonomy'];
     if (!isset($atts['pad_counts']) && $atts['show_count'] && $atts['hierarchical']) {
         // Padding the counts is ideal, but really, really, bloats the memory required.
         $atts['pad_counts'] = FALSE;
     }
     if (empty($atts['parent_id'])) {
         $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], $atts);
     } else {
         $atts['parent_id'] = wp_parse_id_list($atts['parent_id']);
         $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('include' => $atts['parent_id'], 'child_of' => 0)));
         // If any of the `parent_id` is not a root parent (where $term->parent = 0) set it parent ID to `0`
         // so the term tree will be properly constructed.
         foreach ($terms as $term) {
             if (0 !== $term->parent) {
                 $term->parent = 0;
             }
         }
         foreach ($atts['parent_id'] as $termID) {
             $children = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('child_of' => $termID)));
             if (!is_wp_error($children)) {
                 $terms = array_merge($terms, $children);
             }
         }
     }
     if (!$atts['hide_if_empty'] || !empty($terms)) {
         //$out .= PHP_EOL . "<select name='$name' id='$id' class='$class' $tab_index_attribute>" . PHP_EOL;
         // Add the 'cn-enhanced-select' class for the jQuery Chosen Plugin will enhance the drop down.
         if ($atts['enhanced']) {
             $atts['class'] = array_merge((array) $atts['class'], array('cn-enhanced-select'));
         }
         // Create the field label, if supplied.
         $replace[] = !empty($atts['label']) ? cnHTML::label(array('for' => $atts['id'], 'label' => $atts['label'], 'return' => TRUE)) : '';
         $select .= sprintf('<select %1$s %2$s name="%3$s"%4$s%5$sdata-placeholder="%6$s"%7$s%8$s>' . PHP_EOL, empty($atts['class']) ? '' : cnHTML::attribute('class', $atts['class']), empty($atts['id']) ? '' : cnHTML::attribute('id', $atts['id']), $atts['type'] == 'multiselect' ? esc_attr($atts['name']) . '[]' : esc_attr($atts['name']), empty($atts['style']) ? '' : cnHTML::attribute('style', $atts['style']), $atts['type'] == 'multiselect' ? '' : (empty($atts['on_change']) ? '' : sprintf(' onchange="%s" ', esc_js($atts['on_change']))), esc_attr($atts['default']), $atts['type'] == 'multiselect' ? ' MULTIPLE' : '', (int) $atts['tab_index'] > 0 ? " tabindex=\"{$atts['tab_index']}\"" : '');
     } else {
         $select .= '';
     }
     if (empty($terms) && !$atts['hide_if_empty'] && !empty($atts['show_option_none'])) {
         /** This filter is documented in includes/template/class.template-walker-term-select.php */
         $show_option_none = apply_filters('cn_list_cats', $atts['show_option_none']);
         $select .= "\t<option value='" . esc_attr($atts['option_none_value']) . "' selected='selected'>{$show_option_none}</option>" . PHP_EOL;
     }
     if (!empty($terms)) {
         if ($atts['enhanced']) {
             $select .= "\t" . '<option value=""></option>';
         }
         if ($atts['show_select_all'] && $atts['show_option_all']) {
             /** This filter is documented in includes/template/class.template-walker-term-select.php */
             $show_option_all = apply_filters('cn_list_cats', $atts['show_option_all']);
             $selected = !$atts['enhanced'] && is_numeric($atts['selected']) && '0' === strval($atts['selected']) ? " selected='selected'" : '';
             $select .= "\t<option value='0'{$selected}>{$show_option_all}</option>" . PHP_EOL;
         }
         if ($atts['show_option_none']) {
             /** This filter is documented in includes/template/class.template-walker-term-select.php */
             $show_option_none = apply_filters('cn_list_cats', $atts['show_option_none']);
             $selected = selected($atts['option_none_value'], $atts['selected'], FALSE);
             $select .= "\t<option value='" . esc_attr($atts['option_none_value']) . "'{$selected}>{$show_option_none}</option>" . PHP_EOL;
         }
         if ($atts['hierarchical']) {
             $depth = $atts['depth'];
             // Walk the full depth.
         } else {
             $depth = -1;
             // Flat.
         }
         $select .= $walker->walk($terms, $depth, $atts);
     }
     if (!$atts['hide_if_empty'] || !empty($terms)) {
         // If an option group was left open, ensure it is closed before closing the select.
         if ($walker->close_group) {
             $select .= "\t" . '</optgroup>' . PHP_EOL;
             $walker->close_group = FALSE;
         }
         $select .= "</select>" . PHP_EOL;
         $replace[] = $select;
         $out = str_ireplace($search, $replace, $atts['layout']);
         $out = (empty($atts['before']) ? '' : $atts['before']) . $out . (empty($atts['after']) ? '' : $atts['after']);
     }
     /**
      * Filter the taxonomy drop-down output.
      *
      * @since 8.2.4
      *
      * @param string $out  HTML output.
      * @param array  $atts Arguments used to build the drop-down.
      */
     $out = apply_filters('cn_dropdown_cats', $out, $atts);
     if ($atts['return']) {
         return $out;
     }
     echo $out;
 }
 /**
  * Start the element output.
  *
  * @see   Walker::start_el()
  *
  * @since 8.1.6
  *
  * @uses   esc_attr()
  * @uses   number_format_i18n()
  * @uses   cnTerm::get()
  *
  * @param string $output Passed by reference. Used to append additional content.
  * @param object $term   Term object.
  * @param int    $depth  Depth of category in reference to parents. Default 0.
  * @param array  $args   An array of arguments. @see CN_Walker_Term_List::render()
  * @param int    $id     ID of the current term.
  */
 public function start_el(&$output, $term, $depth = 0, $args = array(), $id = 0)
 {
     $indent = str_repeat("\t", $depth);
     $count = $args['show_count'] ? '<span class="cn-cat-count">&nbsp;(' . esc_html(number_format_i18n($term->count)) . ')</span>' : '';
     $url = cnTerm::permalink($term, 'category', $args);
     $html = sprintf('<a href="%1$s" title="%2$s">%3$s</a>', $url, esc_attr($term->name), esc_html($term->name) . $count);
     /**
      * Allows extensions to alter the HTML of term list item.
      *
      * @since 8.5.18
      *
      * @param string        $html  The HTML.
      * @param cnTerm_Object $term  The current term.
      * @param int           $depth Depth of category. Used for tab indentation.
      * @param array         $args  The method attributes.
      */
     $html = apply_filters('cn_term_list_item', $html, $term, $depth, $args);
     $class = array('cat-item', 'cat-item-' . $term->term_id, 'cn-cat-parent');
     $termChildren = cnTerm::getTaxonomyTerms($term->taxonomy, array('parent' => $term->term_id, 'hide_empty' => FALSE, 'fields' => 'count'));
     if (!empty($termChildren)) {
         $class[] = 'cn-cat-has-children';
     }
     if (!empty($args['current_category'])) {
         if (is_numeric($args['current_category'])) {
             $_current_category = cnTerm::get($args['current_category'], $term->taxonomy);
             // cnTerm::get() can return NULL || an instance of WP_Error, so, lets check for that.
             if (is_null($_current_category) || is_wp_error($_current_category)) {
                 $_current_category = new stdClass();
                 $_current_category->parent = 0;
             }
         } else {
             $_current_category = new stdClass();
             $_current_category->parent = 0;
         }
         if ($term->slug == $args['current_category']) {
             $class[] = ' current-cat';
         } elseif ($term->term_id == $args['current_category']) {
             $class[] = ' current-cat';
         } elseif ($term->term_id == $_current_category->parent) {
             $class[] = ' current-cat-parent';
         }
     }
     /**
      * Allows extensions to add/remove class names to the current term list item.
      *
      * @since 8.5.18
      *
      * @param array         $class The array of class names.
      * @param cnTerm_Object $term  The current term.
      * @param int           $depth Depth of category. Used for tab indentation.
      * @param array         $args  The method attributes.
      */
     $class = apply_filters('cn_term_list_item_class', $class, $term, $depth, $args);
     $class = cnSanitize::htmlClass($class);
     $output .= "{$indent}<li" . ' class="' . cnFunction::escAttributeDeep($class) . '"' . ">{$html}";
     // Do not add EOL here, it'll add unwanted whitespace if terms are inline.
 }
 /**
  * Get a collection of items
  *
  * @since 8.5.26
  *
  * @param WP_REST_Request $request Full data about the request.
  *
  * @return WP_Error|WP_REST_Response
  */
 public function get_items($request)
 {
     $prepared_args = array('exclude' => $request['exclude'], 'include' => $request['include'], 'order' => $request['order'], 'orderby' => $request['orderby'], 'post' => $request['post'], 'hide_empty' => $request['hide_empty'], 'number' => $request['per_page'], 'search' => $request['search'], 'slug' => $request['slug']);
     if (!empty($request['offset'])) {
         $prepared_args['offset'] = $request['offset'];
     } else {
         $prepared_args['offset'] = ($request['page'] - 1) * $prepared_args['number'];
     }
     //$taxonomy_obj = get_taxonomy( $this->taxonomy );
     //if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) {
     if (0 === $request['parent']) {
         // Only query top-level terms.
         $prepared_args['parent'] = 0;
     } else {
         if ($request['parent']) {
             $prepared_args['parent'] = $request['parent'];
         }
     }
     //}
     /**
      * Filter the query arguments, before passing them to `cnTerm::getTaxonomyTerms()`.
      *
      * Enables adding extra arguments or setting defaults for a terms
      * collection request.
      *
      * @since 8.5.26
      *
      * @param array           $prepared_args Array of arguments to be
      *                                       passed to get_terms.
      * @param WP_REST_Request $request       The current request.
      */
     $prepared_args = apply_filters("cn_rest_{$this->taxonomy}_query", $prepared_args, $request);
     if (!empty($prepared_args['post'])) {
         $query_result = $this->get_terms_for_post($prepared_args);
         $total_terms = $this->total_terms;
     } else {
         $query_result = cnTerm::getTaxonomyTerms($this->taxonomy, $prepared_args);
         $count_args = $prepared_args;
         unset($count_args['number']);
         unset($count_args['offset']);
         $count_args['hide_empty'] = FALSE;
         $count_args['fields'] = 'count';
         $total_terms = cnTerm::getTaxonomyTerms($this->taxonomy, $count_args);
         // Ensure we don't return results when offset is out of bounds
         // see https://core.trac.wordpress.org/ticket/35935
         if ($prepared_args['offset'] >= $total_terms) {
             $query_result = array();
         }
         // $total_terms can be a falsy value when the term has no children
         if (!$total_terms) {
             $total_terms = 0;
         }
     }
     $response = array();
     foreach ($query_result as $term) {
         $data = $this->prepare_item_for_response($term, $request);
         $response[] = $this->prepare_response_for_collection($data);
     }
     $response = rest_ensure_response($response);
     // Store pagination values for headers then unset for count query.
     $per_page = (int) $prepared_args['number'];
     $page = ceil((int) $prepared_args['offset'] / $per_page + 1);
     $response->header('X-WP-Total', (int) $total_terms);
     $max_pages = ceil($total_terms / $per_page);
     $response->header('X-WP-TotalPages', (int) $max_pages);
     $base = add_query_arg($request->get_query_params(), rest_url($this->namespace . '/' . $this->rest_base));
     if ($page > 1) {
         $prev_page = $page - 1;
         if ($prev_page > $max_pages) {
             $prev_page = $max_pages;
         }
         $prev_link = add_query_arg('page', $prev_page, $base);
         $response->link_header('prev', $prev_link);
     }
     if ($max_pages > $page) {
         $next_page = $page + 1;
         $next_link = add_query_arg('page', $next_page, $base);
         $response->link_header('next', $next_link);
     }
     return $response;
 }
 /**
  * Display or retrieve the HTML select list of terms.
  *
  * This is the Connections equivalent of @see wp_dropdown_categories() in WordPress core ../wp-includes/category-template.php
  *
  * @access public
  * @since  8.2
  * @static
  *
  * @uses   wp_parse_args()
  * @uses   cnTerm::getTaxonomyTerms()
  * @uses   esc_attr()
  * @uses   sanitize_html_class()
  * @uses   apply_filters()
  * @uses   Walker::walk()
  * @uses   selected()
  *
  * @param array $atts {
  *     Optional. An array of arguments.
  *     NOTE: Additionally, all valid options as supported in @see cnTerm::getTaxonomyTerms().
  *
  * @type string $show_option_all   A non-blank value causes the display of a link to the directory home page.
  *                                 Default: ''. The default is not to display a link.
  *                                 Accepts: Any valid string.
  * @type string $show_option_none  Set the text to show when no categories are listed.
  *                                 Default: 'No Categories'
  *                                 Accepts: Any valid string.
  * @type bool   $show_count        Whether or not to display the category count.
  *                                 Default: FALSE
  * @type string $name              The select name attribute.
  *                                 Default: 'cat'
  * @type string $id                The select id attribute.
  *                                 Default: ''
  * @type string $class             The select class attribute.
  *                                 Default: 'postform'
  * @type int    $depth             Controls how many levels in the hierarchy of categories are to be included in the list.
  *                                 Default: 0
  *                                 Accepts: 0  - All categories and child categories.
  *                                          -1 - All Categories displayed  flat, not showing the parent/child relationships.
  *                                          1  - Show only top level/root parent categories.
  *                                          n  - Value of n (int) specifies the depth (or level) to descend in displaying the categories.
  * @type int    $tab_index         The select tab index.
  *                                 Default: 0
  * @type string $taxonomy          The taxonomy tree to display.
  *                                 Default: 'category'
  *                                 Accepts: Any registered taxonomy.
  * @type bool   $hide_if_empty     Whether or not to show the select if no terms are returned by term query.
  *                                 Default: FALSE
  * @type string $option_none_value Value to use when no term is selected.
  *                                 Default: -1
  *                                 Accepts: Any valid int/string for an option value attribute.
  * @type int    $selected          The selected term ID.
  * @type bool   $return            Whether or not to return or echo the resulting HTML.
  *                                 Default: FALSE
  * }
  *
  * @return string
  */
 public static function render($atts = array())
 {
     $out = '';
     $defaults = array('show_option_all' => '', 'show_option_none' => '', 'orderby' => 'name', 'order' => 'ASC', 'show_count' => FALSE, 'hide_empty' => FALSE, 'name' => 'cat', 'id' => '', 'class' => 'postform', 'depth' => 0, 'tab_index' => 0, 'taxonomy' => 'category', 'hide_if_empty' => FALSE, 'option_none_value' => -1, 'selected' => 0, 'return' => FALSE);
     $atts = wp_parse_args($atts, $defaults);
     $walker = new self();
     $walker->tree_type = $atts['taxonomy'];
     if (!isset($atts['pad_counts']) && $atts['show_count'] && $atts['hierarchical']) {
         $atts['pad_counts'] = TRUE;
     }
     $tab_index_attribute = (int) $atts['tab_index'] > 0 ? " tabindex=\"{$atts['tab_index']}\"" : '';
     $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], $atts);
     $name = esc_attr($atts['name']);
     $class = sanitize_html_class($atts['class']);
     $id = $atts['id'] ? esc_attr($atts['id']) : $name;
     if (!$atts['hide_if_empty'] || !empty($terms)) {
         $out .= PHP_EOL . "<select name='{$name}' id='{$id}' class='{$class}' {$tab_index_attribute}>" . PHP_EOL;
     } else {
         $out .= '';
     }
     if (empty($terms) && !$atts['hide_if_empty'] && !empty($atts['show_option_none'])) {
         /**
          * Filter a taxonomy drop-down display element.
          *
          * @since 8.2
          *
          * @param string $element Taxonomy term name.
          */
         $show_option_none = apply_filters('cn_list_cats', $atts['show_option_none']);
         $out .= "\t<option value='" . esc_attr($atts['option_none_value']) . "' selected='selected'>{$show_option_none}</option>\n";
     }
     if (!empty($terms)) {
         if ($atts['show_option_all']) {
             /** This filter is documented in includes/template/class.template-walker-term-select.php */
             $show_option_all = apply_filters('cn_list_cats', $atts['show_option_all']);
             $selected = '0' === strval($atts['selected']) ? " selected='selected'" : '';
             $out .= "\t<option value='0'{$selected}>{$show_option_all}</option>\n";
         }
         if ($atts['show_option_none']) {
             /** This filter is documented in includes/template/class.template-walker-term-select.php */
             $show_option_none = apply_filters('cn_list_cats', $atts['show_option_none']);
             $selected = selected($atts['option_none_value'], $atts['selected'], FALSE);
             $out .= "\t<option value='" . esc_attr($atts['option_none_value']) . "'{$selected}>{$show_option_none}</option>\n";
         }
         if ($atts['hierarchical']) {
             $depth = $atts['depth'];
             // Walk the full depth.
         } else {
             $depth = -1;
             // Flat.
         }
         $out .= $walker->walk($terms, $depth, $atts);
     }
     if (!$atts['hide_if_empty'] || !empty($terms)) {
         $out .= "</select>" . PHP_EOL;
     }
     /**
      * Filter the taxonomy drop-down output.
      *
      * @since 8.2
      *
      * @param string $out HTML output.
      * @param array  $atts      Arguments used to build the drop-down.
      */
     $out = apply_filters('cn_dropdown_cats', $out, $atts);
     if ($atts['return']) {
         return $out;
     }
     echo $out;
 }
Exemplo n.º 8
0
 /**
  * Retrieve the terms in a given taxonomy or list of taxonomies.
  *
  * NOTE: This is the Connections equivalent of @see get_terms() in WordPress core ../wp-includes/taxonomy.php
  *
  * Filters:
  *    cn_get_terms_atts - The method variables.
  *        Passes: (array) $atts, (array) $taxonomies
  *        Return: $atts
  *
  *    cn_get_terms_fields - The fields for the SELECT query clause.
  *        Passes: (array) $select, (array) $atts, (array) $taxonomies
  *        Return: $select
  *
  *    cn_term_inclusions - Query clause which includes terms.
  *        Passes: (string) $inclusions, (array) $atts, (array) $taxonomies
  *        Return: $inclusions
  *
  *    cn_term_exclusions - Query clause which excludes terms.
  *        Passes: (string) $exclusions, (array) $atts, (array) $taxonomies
  *        Return: $exclusions
  *
  *    cn_term_orderby - The ORDER BY query clause.
  *        Passes: (string) $orderBy, (array) $atts, (array) $taxonomies
  *        Return: $orderBy
  *
  *    cn_terms_clauses - An array containing the the query clause segments.
  *        Passes: (array) $pieces, (array) $taxonomies, (array) $atts
  *        Return: $pieces
  *
  * Accepted option for the $atts property are:
  *
  *    get ( string )
  *        Default: ''
  *        Valid:   all
  *        If set to 'all' instead of its default empty string,
  *        returns terms regardless of ancestry or whether the terms are empty.
  *
  *    fields ( string )
  *        Default: 'all'
  *        Valid:   all | ids | id=>parent | names | count | id=>name | id=>slug
  *        Default is 'all', which returns an array of term objects.
  *        If 'fields' is 'ids' or 'names', returns an array of integers or strings, respectively.
  *
  *    include ( string | array )
  *        Default: array()
  *        Valid:   An indexed array, comma- or space-delimited string of term_id.
  *
  *    exclude_tree ( string | array )
  *        Default: array()
  *        Valid:   An indexed array, comma- or space-delimited string of term_id.
  *        If 'include' is non-empty, 'exclude_tree' is ignored.
  *
  *    exclude ( string | array )
  *        Default: array()
  *        Valid:   An indexed array, comma- or space-delimited string of term_id.
  *        If 'include' is non-empty, 'exclude' is ignored.
  *
  *    slug ( string | array  )
  *        Default: ''
  *        Slug or array of slugs to return term(s) for.
  *
  *    hide_empty ( bool )
  *        Default: TRUE
  *        Will not return empty terms, which means terms whose count is 0.
  *
  *    hierarchical ( bool )
  *        Default: TRUE
  *        Whether to include terms that have non-empty descendants, even if 'hide_empty' is set to TRUE.
  *
  *    orderby ( string | array )
  *        Default: name
  *        Valid:   term_id | name | slug | term_group | parent | count | include
  *
  *    order ( string | array )
  *        Default: ASC
  *        Valid:   ASC | DESC
  *
  *    number ( int )
  *        Default: 0
  *        The maximum number of terms to return. Default is to return them all.
  *
  *    offset ( int )
  *        Default: 0
  *        The number by which to offset the terms query.
  *
  *    search ( string )
  *        Default: ''
  *        Returned terms' names will contain the value of 'search', case-insensitive.
  *
  *    name__like ( string )
  *        Default: ''
  *        Return terms' names will contain the value of 'name__like', case-insensitive.
  *
  *    description__like ( string )
  *        Default: ''
  *        Return terms' descriptions will contain the value of 'description__like', case-insensitive.
  *
  *    child_of ( int )
  *        Default: 0
  *        The 'child_of' argument, when used, should be set to the integer of a term ID.
  *        If set to a non-zero value, all returned terms will be descendants
  *        of that term according to the given taxonomy.
  *        Hence 'child_of' is set to 0 if more than one taxonomy is passed in $taxonomies,
  *        because multiple taxonomies make term ancestry ambiguous.
  *
  *    parent ( string | int )
  *        Default: ''
  *        The integer of a term ID.
  *        If set to an integer value, all returned terms will have as an immediate
  *        ancestor the term whose ID is specified by that integer according to the given taxonomy.
  *        The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
  *        of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
  *
  *    pad_counts ( bool )
  *        Default: FALSE
  *        If set to true, include the quantity of a term's children
  *        in the quantity of each term's 'count' property.
  *
  * @access public
  * @since  8.1
  * @static
  *
  * @global $wpdb
  *
  * @param  string|array $taxonomies Taxonomy name or array of taxonomy names.
  * @param  array        $atts
  *
  * @uses   apply_filters()
  * @uses   wp_parse_args()
  * @uses   wp_parse_id_list()
  * @uses   sanitize_title()
  * @uses   wpdb::prepare()
  * @uses   $wpdb::esc_like()
  * @uses   absint()
  * @uses   wpdb::get_results()
  * @uses   cnTerm::filter()
  * @uses   cnTerm::descendants()
  * @uses   cnTerm::childrenIDs()
  * @uses   cnTerm::padCounts()
  * @uses   cnTerm::children()
  *
  * @return array|WP_Error Indexed array of term objects. Will return WP_Error, if any of $taxonomies do not exist.*
  */
 public static function getTaxonomyTerms($taxonomies = array('category'), $atts = array())
 {
     /** @var $wpdb wpdb */
     global $wpdb;
     $select = array();
     $where = array();
     $orderBy = array();
     $orderByClause = '';
     /*
      * @TODO $taxonomies need to be checked against registered taxonomies.
      * Presently $taxonomies only support a string rather than array.
      * Additionally, category is the only supported taxonomy.
      */
     $single_taxonomy = !is_array($taxonomies) || 1 === count($taxonomies);
     if (!is_array($taxonomies)) {
         $taxonomies = array($taxonomies);
     }
     $defaults = array('get' => '', 'orderby' => 'name', 'order' => 'ASC', 'hide_empty' => TRUE, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => TRUE, 'child_of' => 0, 'name__like' => '', 'meta_query' => array(), 'pad_counts' => FALSE, 'offset' => 0, 'number' => 0, 'search' => '');
     /**
      * Filter the terms query arguments.
      *
      * @since 8.1
      *
      * @param array        $atts       An array of arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $atts = apply_filters('cn_get_terms_args', $atts, $taxonomies);
     $atts = wp_parse_args($atts, $defaults);
     // @TODO Implement is_taxonomy_hierarchical().
     if (!$single_taxonomy || '' !== $atts['parent'] && 0 !== $atts['parent']) {
         $atts['child_of'] = 0;
         $atts['hierarchical'] = FALSE;
         $atts['pad_counts'] = FALSE;
     }
     if ('all' == $atts['get']) {
         $atts['child_of'] = 0;
         $atts['hide_empty'] = 0;
         $atts['hierarchical'] = FALSE;
         $atts['pad_counts'] = FALSE;
     }
     if ($atts['child_of']) {
         $hierarchy = self::childrenIDs(reset($taxonomies));
         if (!isset($hierarchy[$atts['child_of']])) {
             return array();
         }
     }
     if ($atts['parent']) {
         $hierarchy = self::childrenIDs(reset($taxonomies));
         if (!isset($hierarchy[$atts['parent']])) {
             return array();
         }
     }
     // $args can be whatever, only use the args defined in defaults to compute the key
     $filter_key = has_filter('cn_term_exclusions') ? serialize($GLOBALS['wp_filter']['cn_term_exclusions']) : '';
     $key = md5(serialize(wp_array_slice_assoc($atts, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
     $last_changed = wp_cache_get('last_changed', 'cn_terms');
     if (!$last_changed) {
         $last_changed = microtime();
         wp_cache_set('last_changed', $last_changed, 'cn_terms');
     }
     $cache_key = "cn_get_terms:{$key}:{$last_changed}";
     $cache = wp_cache_get($cache_key, 'cn_terms');
     if (FALSE !== $cache) {
         /**
          * Filter the given taxonomy's terms cache.
          *
          * @since 8.1.6
          *
          * @param array        $cache      Cached array of terms for the given taxonomy.
          * @param string|array $taxonomies A taxonomy or array of taxonomies.
          * @param array        $args       An array of arguments to get terms.
          */
         $cache = apply_filters('cn_terms', $cache, $taxonomies, $atts);
         return $cache;
     }
     /*
      * Construct the ORDER By query clause.
      */
     if (is_array($atts['orderby'])) {
         foreach ($atts['orderby'] as $i => $value) {
             if (!isset($order)) {
                 $order = 'ASC';
             }
             switch ($value) {
                 case 'id':
                 case 'term_id':
                     $orderField = 't.term_id';
                     break;
                 case 'slug':
                     $orderField = 't.slug';
                     break;
                 case 'include':
                     $include = implode(',', wp_parse_id_list($atts['include']));
                     $orderField = "FIELD( t.term_id, {$include} )";
                     break;
                 case 'term_group':
                     $orderField = 't.term_group';
                     break;
                 case 'none':
                     $orderField = '';
                     // If an `none` order field was supplied, break out of both the switch and foreach statements.
                     break 2;
                 case 'parent':
                     $orderField = 'tt.parent';
                     break;
                 case 'count':
                     $orderField = 'tt.count';
                     break;
                 default:
                     $orderField = 't.name';
                     break;
             }
             // Set the $order to align with $atts['orderby'].
             if (is_array($atts['order']) && isset($atts['order'][$i])) {
                 $order = $atts['order'][$i];
                 // If an aligned $atts['order'] does not exist use the last $order set otherwise use $atts['order'].
             } else {
                 $order = is_array($atts['order']) ? $order : $atts['order'];
             }
             $order = strtoupper($order);
             $order = in_array($order, array('ASC', 'DESC')) ? $order : 'ASC';
             $orderBy[] = sprintf('%s %s', $orderField, $order);
         }
         // The @var $value will be set to the last value from the $atts['orderby'] foreach loop.
         // If a `none` $atts['orderby'] was found in the supplied array, no order by clause will be set.
         if (!empty($orderBy) && $value != 'none') {
             $orderByClause = 'ORDER BY ' . implode(', ', $orderBy);
         }
     } else {
         switch ($atts['orderby']) {
             case 'id':
             case 'term_id':
                 $atts['orderby'] = 't.term_id';
                 break;
             case 'slug':
                 $atts['orderby'] = 't.slug';
                 break;
             case 'include':
                 $include = implode(',', wp_parse_id_list($atts['include']));
                 $atts['orderby'] = "FIELD( t.term_id, {$include} )";
                 break;
             case 'term_group':
                 $atts['orderby'] = 't.term_group';
                 break;
             case 'none':
                 $atts['orderby'] = '';
                 break;
             case 'parent':
                 $atts['orderby'] = 'tt.parent';
                 break;
             case 'count':
                 $atts['orderby'] = 'tt.count';
                 break;
             default:
                 $atts['orderby'] = 't.name';
                 break;
         }
         if (is_array($atts['order'])) {
             // $atts['orderby'] was a string but an array was passed for $atts['order'], assume the 0 index.
             $order = $atts['order'][0];
         } else {
             $order = $atts['order'];
         }
         if (!empty($atts['orderby'])) {
             $order = strtoupper($order);
             $order = in_array($order, array('ASC', 'DESC')) ? $order : 'ASC';
             $orderByClause = 'ORDER BY ' . sprintf('%s %s', $atts['orderby'], $order);
         }
     }
     /*
      * Filter the ORDER BY clause of the terms query.
      *
      * @since 8.1
      *
      * @param string       $orderBy    ORDER BY clause of the terms query.
      * @param array        $atts       An array of terms query arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $orderBy = apply_filters('cn_terms_orderby', $orderByClause, $atts, $taxonomies);
     /*
      * Start construct the WHERE query clause.
      */
     $where[] = 'tt.taxonomy IN (\'' . implode('\', \'', $taxonomies) . '\')';
     /*
      * Define the included terms.
      */
     $inclusions = '';
     if (!empty($atts['include'])) {
         $atts['exclude'] = '';
         $atts['exclude_tree'] = '';
         $inclusions = implode(',', wp_parse_id_list($atts['include']));
     }
     if (!empty($inclusions)) {
         $inclusions = 'AND t.term_id IN ( ' . $inclusions . ' )';
     }
     /**
      * Filter the terms to be included in the terms query.
      *
      * @since 8.1
      *
      * @param string       $inclusions IN clause of the terms query.
      * @param array        $atts       An array of terms query arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $inclusions = apply_filters('cn_term_inclusions', $inclusions, $atts, $taxonomies);
     if (!empty($inclusions)) {
         $where[] = $inclusions;
     }
     /*
      * Define the excluded terms.
      */
     $exclusions = '';
     if (!empty($atts['exclude_tree'])) {
         $atts['exclude_tree'] = wp_parse_id_list($atts['exclude_tree']);
         $excluded_children = $atts['exclude_tree'];
         foreach ($atts['exclude_tree'] as $extrunk) {
             $excluded_children = array_merge($excluded_children, (array) cnTerm::getTaxonomyTerms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids', 'hide_empty' => 0)));
         }
         $exclusions = implode(',', array_map('intval', $excluded_children));
     }
     if (!empty($atts['exclude'])) {
         $exterms = wp_parse_id_list($atts['exclude']);
         if (empty($exclusions)) {
             $exclusions = implode(',', $exterms);
         } else {
             $exclusions .= ', ' . implode(',', $exterms);
         }
     }
     if (!empty($exclusions)) {
         $exclusions = 'AND t.term_id NOT IN (' . $exclusions . ')';
     }
     /**
      * Filter the terms to exclude from the terms query.
      *
      * @since 8.1
      *
      * @param string       $exclusions NOT IN clause of the terms query.
      * @param array        $atts       An array of terms query arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $exclusions = apply_filters('cn_term_exclusions', $exclusions, $atts, $taxonomies);
     if (!empty($exclusions)) {
         $where[] = $exclusions;
     }
     if (!empty($atts['slug'])) {
         if (is_array($atts['slug'])) {
             $slug = array_map('sanitize_title', $atts['slug']);
             $where[] = " AND t.slug IN ('" . implode("', '", $slug) . "')";
         } else {
             $slug = sanitize_title($atts['slug']);
             $where[] = " AND t.slug = '{$slug}'";
         }
     }
     if (!empty($atts['name__like'])) {
         //$atts['name__like'] = like_escape( $atts['name__like'] );
         $where[] = $wpdb->prepare(" AND t.name LIKE %s", '%' . $wpdb->esc_like($atts['name__like']) . '%');
         //$where[]            = $wpdb->prepare( 'AND t.name LIKE %s', '%' . $atts['name__like'] . '%' );
     }
     if (!empty($atts['description__like'])) {
         //$atts['description__like'] = like_escape( $atts['description__like'] );
         $where[] = $wpdb->prepare(" AND tt.description LIKE %s", '%' . $wpdb->esc_like($atts['description__like']) . '%');
         //$where[]                   = $wpdb->prepare( 'AND tt.description LIKE %s', '%' . $atts['description__like'] . '%' );
     }
     if ('' !== $atts['parent']) {
         $where[] = $wpdb->prepare('AND tt.parent = %d', $atts['parent']);
     }
     if ('count' == $atts['fields']) {
         $atts['hierarchical'] = FALSE;
     }
     if ($atts['hide_empty'] && !$atts['hierarchical']) {
         $where[] = 'AND tt.count > 0';
     }
     // Do not limit the query results when we have to descend the family tree.
     if ($atts['number'] && !$atts['hierarchical'] && !$atts['child_of'] && '' === $atts['parent']) {
         $atts['number'] = absint($atts['number']);
         $atts['offset'] = absint($atts['offset']);
         if ($atts['offset']) {
             $limit = $wpdb->prepare('LIMIT %d,%d', $atts['offset'], $atts['number']);
         } else {
             $limit = $wpdb->prepare('LIMIT %d', $atts['number']);
         }
     } else {
         $limit = '';
     }
     if (!empty($atts['search'])) {
         //$atts['search'] = like_escape( $atts['search'] );
         $atts['search'] = $wpdb->esc_like($atts['search']);
         $where[] = $wpdb->prepare('AND ( (t.name LIKE %s) OR (t.slug LIKE %s) )', '%' . $atts['search'] . '%', '%' . $atts['search'] . '%');
     }
     switch ($atts['fields']) {
         case 'all':
             $select = array('t.*', 'tt.*');
             break;
         case 'ids':
         case 'id=>parent':
             $select = array('t.term_id', 'tt.parent', 'tt.count');
             break;
         case 'names':
             $select = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
             break;
         case 'count':
             $orderBy = '';
             //$order   = '';
             $select = array('COUNT(*)');
             break;
         case 'id=>name':
             $select = array('t.term_id', 't.name');
             break;
         case 'id=>slug':
             $select = array('t.term_id', 't.slug');
             break;
     }
     /**
      * Filter the fields to select in the terms query.
      *
      * @since 8.1
      *
      * @param array        $select     An array of fields to select for the terms query.
      * @param array        $atts       An array of term query arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $fields = implode(', ', apply_filters('cn_get_terms_fields', $select, $atts, $taxonomies));
     $join = 'INNER JOIN ' . CN_TERM_TAXONOMY_TABLE . ' AS tt ON t.term_id = tt.term_id';
     $pieces = array('fields', 'join', 'where', 'orderBy', 'limit');
     /**
      * Filter the terms query SQL clauses.
      *
      * @since 8.1
      *
      * @param array        $pieces     Terms query SQL clauses.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      * @param array        $atts       An array of terms query arguments.
      */
     $clauses = apply_filters('cn_terms_clauses', compact($pieces), $taxonomies, $atts);
     foreach ($pieces as $piece) {
         ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
     }
     $sql = sprintf('SELECT %1$s FROM %2$s AS t %3$s WHERE %4$s %5$s%6$s', $fields, CN_TERMS_TABLE, $join, implode(' ', $where), $orderBy, empty($limit) ? '' : ' ' . $limit);
     if ('count' == $atts['fields']) {
         $term_count = $wpdb->get_var($sql);
         return $term_count;
     }
     $terms = $wpdb->get_results($sql);
     if ('all' == $atts['fields']) {
         foreach ($taxonomies as $taxonomy) {
             update_term_cache($terms, 'cn_' . $taxonomy);
         }
     }
     if (empty($terms)) {
         wp_cache_add($cache_key, array(), 'cn_terms', DAY_IN_SECONDS);
         $terms = apply_filters('cn_terms', array(), $taxonomies, $atts);
         return $terms;
     }
     if ($atts['child_of']) {
         $children = self::childrenIDs(reset($taxonomies));
         if (!empty($children)) {
             $terms = self::descendants($atts['child_of'], $terms, reset($taxonomies));
         }
     }
     /*
      * @todo Add method to adjust counts based on user visibility permissions.
      */
     // Update term counts to include children.
     if ($atts['pad_counts'] && 'all' == $atts['fields']) {
         self::padCounts($terms, reset($taxonomies));
     }
     // Make sure we show empty categories that have children.
     if ($atts['hierarchical'] && $atts['hide_empty'] && is_array($terms)) {
         foreach ($terms as $k => $term) {
             if (!$term->count) {
                 $children = self::children($term->term_id, reset($taxonomies));
                 if (is_array($children)) {
                     foreach ($children as $child_id) {
                         $child = self::filter($child_id, reset($taxonomies));
                         if ($child->count) {
                             continue 2;
                         }
                     }
                 }
                 // It really is empty
                 unset($terms[$k]);
             }
         }
     }
     reset($terms);
     $_terms = array();
     if ('id=>parent' == $atts['fields']) {
         while ($term = array_shift($terms)) {
             $_terms[$term->term_id] = $term->parent;
         }
     } elseif ('ids' == $atts['fields']) {
         while ($term = array_shift($terms)) {
             $_terms[] = $term->term_id;
         }
     } elseif ('names' == $atts['fields']) {
         while ($term = array_shift($terms)) {
             $_terms[] = $term->name;
         }
     } elseif ('id=>name' == $atts['fields']) {
         while ($term = array_shift($terms)) {
             $_terms[$term->term_id] = $term->name;
         }
     } elseif ('id=>slug' == $atts['fields']) {
         while ($term = array_shift($terms)) {
             $_terms[$term->term_id] = $term->slug;
         }
     }
     if (!empty($_terms)) {
         $terms = $_terms;
     }
     if ($atts['number'] && is_array($terms) && count($terms) > $atts['number']) {
         $terms = array_slice($terms, $atts['offset'], $atts['number']);
     }
     wp_cache_add($cache_key, $terms, 'cn_terms', DAY_IN_SECONDS);
     $terms = apply_filters('cn_terms', $terms, $taxonomies, $atts);
     return $terms;
 }
 /**
  * Render an unordered list of categories.
  *
  * This is the Connections equivalent of @see wp_list_categories() in WordPress core ../wp-includes/category-template.php
  *
  * @access public
  * @since  8.1.6
  * @static
  *
  * @uses   wp_parse_args()
  * @uses   cnTerm::getTaxonomyTerms()
  * @uses   cnURL::permalink()
  * @uses   Walker::walk()
  *
  * @param array $atts {
  *     Optional. An array of arguments.
  *     NOTE: Additionally, all valid options as supported in @see cnTerm::getTaxonomyTerms().
  *
  * @type string $show_option_all  A non-blank value causes the display of a link to the directory home page.
  *                                Default: ''. The default is not to display a link.
  *                                Accepts: Any valid string.
  * @type string $show_option_none Set the text to show when no categories are listed.
  *                                Default: 'No Categories'
  *                                Accepts: Any valid string.
  * @type bool   $show_count       Whether or not to display the category count.
  *                                Default: FALSE
  * @type int    $depth            Controls how many levels in the hierarchy of categories are to be included in the list.
  *                                Default: 0
  *                                Accepts: 0  - All categories and child categories.
  *                                         -1 - All Categories displayed  flat, not showing the parent/child relationships.
  *                                         1  - Show only top level/root parent categories.
  *                                         n  - Value of n (int) specifies the depth (or level) to descend in displaying the categories.
  * @type string $taxonomy         The taxonomy tree to display.
  *                                Default: 'category'
  *                                Accepts: Any registered taxonomy.
  * @type bool   $return           Whether or not to return or echo the resulting HTML.
  *                                Default: FALSE
  * }
  *
  * @return string
  */
 public static function render($atts = array())
 {
     $out = '';
     $defaults = array('show_option_all' => '', 'show_option_none' => __('No categories', 'connections'), 'orderby' => 'name', 'order' => 'ASC', 'show_count' => FALSE, 'hide_empty' => FALSE, 'child_of' => 0, 'exclude' => array(), 'hierarchical' => TRUE, 'depth' => 0, 'parent_id' => array(), 'taxonomy' => 'category', 'return' => FALSE);
     $atts = wp_parse_args($atts, $defaults);
     $atts['parent_id'] = wp_parse_id_list($atts['parent_id']);
     $walker = new self();
     if (empty($atts['parent_id'])) {
         $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], $atts);
     } else {
         $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('include' => $atts['parent_id'], 'child_of' => 0)));
         // If any of the `parent_id` is not a root parent (where $term->parent = 0) set it parent ID to `0`
         // so the term tree will be properly constructed.
         foreach ($terms as $term) {
             if (0 !== $term->parent) {
                 $term->parent = 0;
             }
         }
         foreach ($atts['parent_id'] as $termID) {
             $children = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('child_of' => $termID)));
             if (!is_wp_error($children)) {
                 $terms = array_merge($terms, $children);
             }
         }
     }
     $out .= '<ul class="cn-cat-tree">' . PHP_EOL;
     if (empty($terms)) {
         $out .= '<li class="cat-item-none">' . $atts['show_option_none'] . '</li>';
     } else {
         if (get_query_var('cn-cat-slug')) {
             $slug = explode('/', get_query_var('cn-cat-slug'));
             // If the category slug is a descendant, use the last slug from the URL for the query.
             $atts['current_category'] = end($slug);
         } elseif ($catIDs = get_query_var('cn-cat')) {
             if (is_array($catIDs)) {
                 // If value is a string, strip the white space and covert to an array.
                 $catIDs = wp_parse_id_list($catIDs);
                 // Use the first element
                 $atts['current_category'] = reset($catIDs);
             } else {
                 $atts['current_category'] = $catIDs;
             }
         } else {
             $atts['current_category'] = 0;
         }
         if (!empty($atts['show_option_all'])) {
             $out .= '<li class="cat-item-all"><a href="' . cnURL::permalink(array('type' => 'home', 'data' => 'url', 'return' => TRUE)) . '">' . $atts['show_option_all'] . '</a></li>';
         }
         $out .= $walker->walk($terms, $atts['depth'], $atts);
     }
     $out .= '</ul>' . PHP_EOL;
     if ($atts['return']) {
         return $out;
     }
     echo $out;
 }
 /**
  * Render an checklist of terms.
  *
  * This is the Connections equivalent of @see wp_terms_checklist() in WordPress core ..wp-admin/wp-includes/template.php
  *
  * @access public
  * @since  8.2.4
  * @static
  *
  * @uses   wp_parse_args()
  * @uses   cnTerm::getTaxonomyTerms()
  * @uses   wp_parse_id_list()
  * @uses   is_wp_error()
  * @uses   esc_attr()
  * @uses   apply_filters
  * @uses   checked()
  * @uses   esc_html()
  * @uses   Walker::walk()
  *
  * @param array $atts {
  *     Optional. An array of arguments.
  *     NOTE: Additionally, all valid options as supported in @see cnTerm::getTaxonomyTerms().
  *
  * @type string $taxonomy        The taxonomy tree to display.
  *                               Default: 'category'
  * @type bool   $hierarchical    Whether to include terms that have non-empty descendants, even if 'hide_empty' is set to TRUE.
  *                               Default: TRUE
  * @type string $name            The select name attribute.
  *                               Default: 'cn-cat'
  * @type bool   $show_select_all Whether or not to render the $show_option_all option.
  *                               Default: TRUE
  * @type string $show_option_all A non-blank value causes the display of a link to the directory home page.
  *                               Default: ''. The default is not to display a link.
  *                               Accepts: Any valid string.
  * @type bool   $show_count      Whether or not to display the category count.
  *                               Default: FALSE
  * @type bool   $hide_empty      Whether or not to display empty terms.
  *                               Default: FALSE
  * @type int    $depth           Controls how many levels in the hierarchy of categories are to be included in the list.
  *                               Default: 0
  *                               Accepts: 0  - All categories and child categories.
  *                                        -1 - All Categories displayed  flat, not showing the parent/child relationships.
  *                                        1  - Show only top level/root parent categories.
  *                                        n  - Value of n (int) specifies the depth (or level) to descend in displaying the categories.
  * @type array  $parent_id
  * @type array  $selected        The selected term IDs.
  *                               Default: 0
  * @type string $before          Content to be render before the label and select.
  *                               Default: ''
  * @type string $after           Content to be render after the label and select.
  *                               Default: ''
  * @type bool $return Whether or not to return or echo the resulting HTML.
  *                               Default: FALSE
  * }
  *
  * @return string
  */
 public static function render($atts = array())
 {
     $out = '';
     $defaults = array('taxonomy' => 'category', 'hierarchical' => TRUE, 'name' => 'cn-cat', 'show_select_all' => TRUE, 'show_option_all' => __('Select Category', 'connections'), 'show_count' => FALSE, 'hide_empty' => FALSE, 'depth' => 0, 'parent_id' => array(), 'selected' => 0, 'before' => '', 'after' => '', 'return' => FALSE);
     $atts = wp_parse_args($atts, $defaults);
     $walker = new self();
     $walker->tree_type = $atts['taxonomy'];
     if (empty($atts['parent_id'])) {
         $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], $atts);
     } else {
         $atts['parent_id'] = wp_parse_id_list($atts['parent_id']);
         $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('include' => $atts['parent_id'], 'child_of' => 0)));
         // If any of the `parent_id` is not a root parent (where $term->parent = 0) set it parent ID to `0`
         // so the term tree will be properly constructed.
         foreach ($terms as $term) {
             if (0 !== $term->parent) {
                 $term->parent = 0;
             }
         }
         foreach ($atts['parent_id'] as $termID) {
             $children = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('child_of' => $termID)));
             if (!is_wp_error($children)) {
                 $terms = array_merge($terms, $children);
             }
         }
     }
     if (!empty($terms)) {
         $out .= '<ul class="cn-' . esc_attr($atts['taxonomy']) . '-radio-group">' . PHP_EOL;
         if ($atts['show_select_all'] && $atts['show_option_all']) {
             /** This filter is documented in includes/template/class.template-walker-term-select.php */
             $show_option_all = apply_filters('cn_list_cats', $atts['show_option_all']);
             $type = esc_attr($walker->tree_type);
             $out .= "<li id='cn-{$type}-0'>" . '<label><input value="0" type="radio" name="' . esc_attr($atts['name']) . '" id="cn-in-' . $type . '-0"' . checked(in_array(0, (array) $atts['selected']), TRUE, FALSE) . ' /> ' . esc_html($show_option_all) . '</label>';
             $out .= '</li>' . PHP_EOL;
         }
         $out .= $walker->walk($terms, $atts['depth'], $atts);
         $out .= '</ul>' . PHP_EOL;
     }
     $out = (empty($atts['before']) ? '' : $atts['before']) . $out . (empty($atts['after']) ? '' : $atts['after']);
     if ($atts['return']) {
         return $out;
     }
     echo $out;
 }