get_alphabet(); $this->alphabet_chars = array_values( array_unique( $this->alphabet ) ); if ( is_string( $query ) && ! empty( $query ) ) { $type = 'terms'; } if ( 'terms' === $type ) { if ( AZLISTINGLOG ) { do_action( 'log', 'A-Z Listing: Setting taxonomy mode', $query ); } $this->type = 'terms'; $defaults = array( 'hide_empty' => false, ); if ( is_array( $query ) ) { $query = wp_parse_args( $query, $defaults ); } elseif ( is_string( $query ) ) { $taxonomies = explode( ',', $query ); $taxonomies = array_unique( array_filter( array_map( 'trim', $taxonomies ) ) ); $query = wp_parse_args( array( 'taxonomy' => (array) $taxonomies, ), $defaults ); } /** * Modify or replace the query * * @since 1.0.0 * @since 2.0.0 apply to taxonomy queries. Add type parameter indicating type of query. * @param array|Object|WP_Query $query The query object * @param string $type The type of the query. Either 'posts' or 'terms'. */ $query = apply_filters( 'a_z_listing_query', $query, 'terms' ); /** * Modify or replace the query * * @since 1.7.1 * @since 2.0.0 apply to taxonomy queries. Add type parameter indicating type of query. * @param array|Object|WP_Query $query The query object * @param string $type The type of the query. Either 'posts' or 'terms'. */ $query = apply_filters( 'a-z-listing-query', $query, 'terms' ); if ( is_object( $query ) ) { $query = (array) $query; } $this->taxonomy = $query['taxonomy']; if ( $this->check_cache( $query, $type, $use_cache ) ) { return $this; } $items = get_terms( $query ); $this->query = $query; if ( AZLISTINGLOG ) { do_action( 'log', 'A-Z Listing: Terms', '!ID', $items ); } } else { if ( AZLISTINGLOG ) { do_action( 'log', 'A-Z Listing: Setting posts mode', $query ); } $this->type = 'posts'; if ( ! $query ) { $query = array(); } /** * Modify or replace the query * * @since 1.0.0 * @since 2.0.0 apply to taxonomy queries. Add type parameter indicating type of query. * @param array|Object|WP_Query $query The query object */ $query = apply_filters( 'a_z_listing_query', $query ); /** * Modify or replace the query * * @since 1.7.1 * @since 2.0.0 apply to taxonomy queries. Add type parameter indicating type of query. * @param array|Object|WP_Query $query The query object */ $query = apply_filters( 'a-z-listing-query', $query ); if ( ! $query instanceof WP_Query ) { $query = (array) $query; if ( isset( $query['post_type'] ) ) { if ( is_array( $query['post_type'] ) && count( $query['post_type'] ) === 1 ) { $query['post_type'] = array_shift( $query['post_type'] ); } } if ( ! isset( $query['post_parent'] ) && ! isset( $query['child_of'] ) ) { if ( isset( $query['post_type'] ) && isset( $post ) ) { if ( 'page' === $query['post_type'] && 'page' === $post->post_type ) { $section = self::get_section(); if ( $section && is_a( $section, 'WP_Post' ) ) { $query['child_of'] = $section->ID; } } } } $query = wp_parse_args( $query, array( 'post_type' => 'page', 'numberposts' => -1, 'nopaging' => true, ) ); } if ( $this->check_cache( (array) $query, $type, $use_cache ) ) { return $this; } if ( $query instanceof WP_Query ) { $items = $query->posts; $this->query = $query; } else { if ( isset( $query['child_of'] ) ) { $items = get_pages( $query ); $this->query = $query; } else { $wq = new WP_Query( $query ); $items = $wq->posts; $this->query = $wq; } } if ( AZLISTINGLOG ) { do_action( 'log', 'A-Z Listing: Posts', '!ID', $items ); } } // End if ( type is terms ). /** * Filter items from the query results * * @param array $items The query results. * @param string $type The query type - terms or posts. * @param array $query The query as an array. */ $items = apply_filters( 'a-z-listing-filter-items', $items, $type, (array) $query ); $this->matched_item_indices = $this->get_all_indices( $items ); if ( $use_cache ) { do_action( 'a_z_listing_save_cache', $query, $type, $this->matched_item_indices ); } } /** * Check for cached queries * * @since 2.0.0 * @param array $query the query. * @param string $type the type of query. * @param boolean $use_cache whether to check the cache. * @return bool whether we found a cached query */ private function check_cache( $query, $type, $use_cache ) { if ( $use_cache ) { /** * Get the cached data * * @since 1.0.0 * @since 2.0.0 apply to taxonomy queries. Add type parameter indicating type of query. * @param array $items The items from previous cache modules. * @param array $query The query. * @param string $type The type of the query. Either 'posts' or 'terms'. */ $cached = apply_filters( 'a_z_listing_get_cached_query', array(), (array) $query, $type ); if ( count( $cached ) > 0 ) { $this->matched_item_indices = $cached; return true; } } return false; } /** * Split a multibyte string into an array. (see https://php.net/manual/en/function.mb-split.php#121330) * * @since 1.0.0 * @param string $string multi-byte string. * @return array individual multi-byte characters from the string */ public static function mb_string_to_array( $string ) { if ( extension_loaded( 'mbstring' ) ) { return array_map( function ( $i ) use ( $string ) { return mb_substr( $string, $i, 1 ); }, range( 0, mb_strlen( $string ) - 1 ) ); } return explode( '', $string ); } /** * Build a translated alphabet * * @since 0.1 */ protected function get_alphabet() { if ( ! empty( $this->alphabet ) ) { return; } /* translators: List the aphabet of your language in the order that your language prefers. list as groups of identical meanings but different characters together, e.g. in English we group A with a because they are both the same letter but different character-code. Each character group should be followed by a comma separating it from the next group. Any amount of characters per group are acceptible, and there is no requirement for all the groups to contain the same amount of characters as all the others. Be careful with the character you place first in each group as that will be used as the identifier for the group which gets displayed on the page, e.g. in English a group definition of "Aa" will indicate that we display all the posts in the group, i.e. whose titles begin with either "A" or "a", listed under a heading of "A" (the first character in the definition). */ $alphabet = __( 'AÁÀÄÂaáàäâ,Bb,CÇcç,Dd,EÉÈËÊeéèëê,Ff,Gg,Hh,IÍÌÏÎiíìïî,Jj,Kk,Ll,Mm,Nn,OÓÒÖÔoóòöô,Pp,Qq,Rr,Ssß,Tt,UÚÙÜÛuúùüû,Vv,Ww,Xx,Yy,Zz', 'a-z-listing' ); /* translators: This should be a single character to denote "all entries that didn't fit under one of the alphabet character groups defined". This is used in English to categorise posts whose title begins with a numeric (0 through to 9), or some other character that is not a standard English alphabet letter. */ $others = __( '#', 'a-z-listing' ); /** * Filters the alphabet. The string should contain groups of similar or identical characters separated by commas. The first character in each group is the one used for the group title. * * @param string $alphabet The $alphabet */ $alphabet = apply_filters( 'a_z_listing_alphabet', $alphabet ); /** * Filters the alphabet. The string should contain groups of similar or identical characters separated by commas. The first character in each group is the one used for the group title. * * @since 1.7.1 * @param string $alphabet The $alphabet. */ $alphabet = apply_filters( 'a-z-listing-alphabet', $alphabet ); /** * Specifies the character used for all non-alphabetic titles, such as numeric titles in the default setup for English. Defaults to '#' unless overridden by a language pack. * * @param string $non_alpha_char The character for non-alphabetic post titles. */ $others = apply_filters( 'a_z_listing_non_alpha_char', $others ); /** * Specifies the character used for all non-alphabetic titles, such as numeric titles in the default setup for English. Defaults to '#' unless overridden by a language pack. * * @since 1.7.1 * @param string $non_alpha_char The character for non-alphabetic post titles. */ $others = apply_filters( 'a-z-listing-non-alpha-char', $others ); $alphabet_groups = explode( ',', $alphabet ); $letters = array_reduce( $alphabet_groups, function( $return, $group ) { $group = A_Z_Listing::mb_string_to_array( $group ); $group_index_character = $group[0]; $group = array_reduce( $group, function( $group, $character ) use ( $group_index_character ) { $group[ $character ] = $group_index_character; return $group; } ); if ( ! is_array( $return ) ) { return $group; } return array_merge( $return, $group ); } ); $this->alphabet = $letters; $this->unknown_letters = $others; } /** * Find a post's parent post. Will return the original post if the post-type is not hierarchical or the post does not have a parent. * * @since 1.4.0 * @param WP_Post|int $page The post whose parent we want to find. * @return WP_Post|bool The parent post or the original post if no parents were found. Will be false if the function is called with incorrect arguments. */ public static function find_post_parent( $page ) { if ( ! $page ) { return false; } if ( ! $page instanceof WP_Post ) { $page = get_post( $page ); } if ( ! $page->post_parent ) { return $page; } return self::find_post_parent( $page->post_parent ); } /** * Calculate the top-level section of the requested page * * @since 0.1 * @param WP_Post|int $page Optional: The post object, or post-ID, of the page whose section we want to find. * @return WP_Post|null The post object of the current section's top-level page. */ protected static function get_section( $page = 0 ) { global $post; $pages = get_pages( array( 'parent' => 0, ) ); $sections = array_map( function( $item ) { return $item->post_name; }, $pages ); /** * Override the detected top-level sections for the site. Defaults to contain each page with no post-parent. * * @deprecated Use a_z_listing_sections * @see a_z_listing_sections */ $sections = apply_filters_deprecated( 'az_sections', array( $sections ), '1.0.0', 'a_z_listing_sections' ); /** * Override the detected top-level sections for the site. Defaults to contain each page with no post-parent. * * @param array $sections The sections for the site. */ $sections = apply_filters( 'a_z_listing_sections', $sections ); /** * Override the detected top-level sections for the site. Defaults to contain each page with no post-parent. * * @since 1.7.1 * @param array $sections The sections for the site. */ $sections = apply_filters( 'a-z-listing-sections', $sections ); if ( ! $page ) { $page = $post; } if ( is_int( $page ) ) { $page = get_post( $page ); } $section_object = self::find_post_parent( $page ); $section_name = null; if ( $section_object === $page ) { $section_object = null; } elseif ( null !== $section_object ) { if ( isset( $section_object->post_name ) ) { $section_name = $section_object->post_name; } else { $section_name = null; $section_object = null; } } if ( AZLISTINGLOG ) { do_action( 'log', 'A-Z Section selection', $section_name, $sections ); } if ( null !== $section_name && ! in_array( $section_name, $sections, true ) ) { $section_name = null; $section_object = null; } if ( AZLISTINGLOG ) { do_action( 'log', 'A-Z Section', $section_name ); } return $section_object; } /** * Fetch the query we are currently using * * @since 1.0.0 * @return WP_Query The query object */ public function get_the_query() { return $this->query; } /** * Reducer used by get_the_item_indices() to filter the indices for each post to unique array_values (see: https://secure.php.net/array_reduce) * * @param array $carry Holds the return value of the previous iteration. * @param array $value Holds the value of the current iteration. * @return array The previous iteration return value with the current iteration added after running through array_unique() */ public function index_reduce( $carry, $value ) { $v = array_unique( $value ); if ( ! empty( $v ) ) { $carry[] = $v; } return $carry; } /** * Sort the letters to be used as indices and return as an Array * * @since 0.1 * @param array $items The items to index. * @return array The index letters */ protected function get_all_indices( $items = null ) { $indexed_items = array(); if ( ! $items ) { $items = $this->items; } if ( is_array( $items ) && count( $items ) > 0 ) { foreach ( $items as $item ) { $item_indices = apply_filters( '_a-z-listing-extract-item-indices', array(), $item, $this->type ); if ( count( $item_indices ) < 1 ) { continue; } foreach ( $item_indices as $index => $index_entries ) { if ( count( $index_entries ) > 0 ) { if ( in_array( $index, array_keys( $this->alphabet ), true ) ) { $index = $this->alphabet[ $index ]; } else { $index = $this->unknown_letters; } if ( ! isset( $indexed_items[ $index ] ) || ! is_array( $indexed_items[ $index ] ) ) { $indexed_items[ $index ] = array(); } $indexed_items[ $index ] = array_merge_recursive( $indexed_items[ $index ], $index_entries ); } } } if ( array_key_exists( $this->unknown_letters, $indexed_items ) && ! empty( $indexed_items[ $this->unknown_letters ] ) ) { $this->alphabet_chars[] = $this->unknown_letters; $this->alphabet[ $this->unknown_letters ] = $this->unknown_letters; } foreach ( $this->alphabet_chars as $character ) { if ( ! empty( $indexed_items[ $character ] ) ) { usort( $indexed_items[ $character ], function ( $a, $b ) { return strcasecmp( $a['title'], $b['title'] ); } ); } } } return $indexed_items; } /** * Print the letter links HTML * * @since 1.0.0 * @param string $target The page to point links toward. * @param string $style CSS classes to apply to the output. */ public function the_letters( $target = '', $style = null ) { echo $this->get_the_letters( $target, $style ); // WPCS: XSS OK. } /** * Print the letter links HTML * * @since 0.1 * @see A_Z_Listing::get_the_letters() * @deprecated use A_Z_Listing::get_the_letters(). * @param string $target The page to point links toward. * @param string $style CSS classes to apply to the output. * @return string The letter links HTML */ public function get_letter_display( $target = '', $style = null ) { _deprecated_function( __METHOD__, '1.0.0', 'A_Z_Listing::get_the_letters' ); return $this->get_the_letters( $target, $style ); } /** * Retrieve the letter links HTML * * @since 1.0.0 * @param string $target The page to point links toward. * @param string $style CSS classes to apply to the output. * @return string The letter links HTML */ public function get_the_letters( $target = '', $style = null ) { $classes = array( 'az-links' ); if ( null !== $style ) { if ( is_array( $style ) ) { $classes = array_merge( $classes, $style ); } elseif ( is_string( $style ) ) { $c = explode( ' ', $style ); $classes = array_merge( $classes, $c ); } } $classes = array_unique( $classes ); $ret = '