add_tag(array(
'name' => 'field',
'priority' => 5,
'condition' => function($args){
return !acf_is_empty($args);
},
'resolver' => function($args){
// extract args
$keys = explode(':', $args);
$name = array_shift($keys);
$format = array_shift($keys);
// validate
$format = $format && $format === 'false' ? false : true;
// vars
$setup_meta = !acfe_is_local_meta() && !empty($_POST['acf']);
if($setup_meta){
acfe_setup_meta(wp_unslash($_POST['acf']), 'acfe/tag/field', true);
}
// vars
$post_id = acf_get_valid_post_id();
$field = acf_maybe_get_field($name, $post_id, false);
if(!$field){
$field = acf_validate_field(array(
'name' => $name,
'key' => '',
'type' => '',
));
// prevent formatting
$format = false;
}
// field_key
// get_field('my_group_my_sub_field') can retrieve values from group subfield
// but get_field('field_abcdef123456') cannot, thus this implementation
// apply acf/load_value filter so payment/date range fields generate dynamic subfields
if(acf_is_field_key($name)){
$acf = acfe_get_fields(); // pass via acf_get_value() > acf/load_value
$value = acfe_get_value_from_acf_values_by_key($acf, $name);
// field name
}else{
$value = acf_get_value($post_id, $field);
}
if($setup_meta){
acfe_reset_meta();
}
// format via context
if($this->has_context('format')){
$format = $this->get_context('format');
}
// context = save
// never format wysiwyg in save context
// to avoid shortcodes to be saved as interpreted
if($this->get_context('context') === 'save'){
if($field['type'] === 'wysiwyg'){
$format = false;
}
}
// unformat = field_type
// never format specific fields in unformat context
if($this->get_context('unformat') && in_array($field['type'], acfe_as_array($this->get_context('unformat')))){
$format = false;
}
// format value
if($format){
$value = acfe_form_format_value($value, $field);
}
// context display
// value is already unslashed when using name, via acfe_setup_meta()
if($this->get_context('context') === 'display' && acf_is_field_key($name)){
$value = wp_unslash($value);
}
// return
return $value;
}
));
// {get_field:my_field}
// {get_field:my_field:145}
// {get_field:my_field:145:false}
// {get_field:field_5d5f3b2b3e3e4}
$this->add_tag(array(
'name' => 'get_field',
'condition' => function($args){
return !acf_is_empty($args);
},
'resolver' => function($args){
// extract args
$keys = explode(':', $args);
$name = array_shift($keys);
$post_id = array_shift($keys);
$format = array_shift($keys);
// validate
$post_id = $post_id && ($post_id === 'false' || $post_id === 'current') ? false : $post_id;
$format = $format && $format === 'false' ? false : true;
// return
return get_field($name, $post_id, $format);
}
));
// {fields}
$this->add_tag(array(
'name' => 'fields',
'resolver' => function($args){
$values = acfe_get_fields();
$tag = $this->get_tag('field');
if(!$values){
return false;
}
// html
$html = '';
// loop raw values
foreach($values as $field_key => $unformatted){
// get field array
$field = acf_get_field($field_key);
if($field){
// bypass
if($field['key'] === '_validate_email' || $field['type'] === 'acfe_recaptcha'){
continue;
}
// label
$label = !empty($field['label']) ? $field['label'] : $field['name'];
$formatted = call_user_func_array($tag['resolver'], array($field_key));
// html
$html .= "$label: $formatted
\n";
}
}
// get form context
$form = $this->get_context('form', array('name' => '')); // default for deprecated filter
// depreacted filters
$html = apply_filters_deprecated("acfe/form/template_tag/fields", array($html, array(), $form), '0.9');
$html = apply_filters_deprecated("acfe/form/template_tag/fields/form={$form['name']}", array($html, array(), $form), '0.9');
// return
return $html;
}
));
// {render:group_5d5f3b2b3e3e4}
// {render:field_5d5f3b2b3e3e4}
// {render:my_field}
// {render:fields}
// {render:submit}
$this->add_tag(array(
'name' => 'render',
'condition' => function($args){
return !acf_is_empty($args);
},
'resolver' => function($args){
// get context
$form = $this->get_context('form');
if(!$form){
return false;
}
// field key
if(acf_is_field_key($args)){
$field = acf_get_field($args);
if(!$field){
return false;
}
$fields = array($field);
// field group key
}elseif(acf_is_field_group_key($args)){
$field_group = acf_get_field_group($args);
if(!$field_group){
return false;
}
$fields = acf_get_fields($field_group);
if(!$fields){
return false;
}
// fields
}elseif($args === 'fields'){
$fields = acf_get_instance('acfe_module_form_front_render')->get_allowed_fields($form);
// submit
}elseif($args === 'submit'){
// form submit
if($form['attributes']['submit']){
ob_start();
do_action("acfe/form/render_submit", $form);
return ob_get_clean();
}
return false;
// field name
}else{
// try to get field key using field name from mapped field groups
// otheriwse, fallback to field name
$args = $this->get_field_key_from_field_groups($args);
// get field array
$field = acf_get_field($args);
// field name
if($field){
$fields = array($field);
// field group ID
}else{
$field_group = acf_get_field_group($args);
if(!$field_group){
return false;
}
$fields = acf_get_fields($field_group);
if(!$fields){
return false;
}
}
}
ob_start();
acf_render_fields($fields, $form['uniqid'], $form['attributes']['fields']['element'], $form['attributes']['fields']['instruction']);
return ob_get_clean();
}
));
// {action:my-post}
// {action:my-post:ID}
$this->add_tag(array(
'name' => 'action',
'condition' => function($args){
$keys = explode(':', $args);
$name = array_shift($keys);
$action = acfe_get_form_action($name);
return !acf_is_empty($name) && $action;
},
'resolver' => function($args){
// extract args
$keys = explode(':', $args);
$name = array_shift($keys);
// get action
$action = acfe_get_form_action($name);
if(!$action){
return false;
}
return $this->array_get($action, $keys);
}
));
// {get_option:my_option}
// {get_option:my_option:child}
$this->add_tag(array(
'name' => 'get_option',
'condition' => function($args){
return !acf_is_empty($args);
},
'resolver' => function($args){
// extract args
$keys = explode(':', $args);
$name = array_shift($keys);
// get option
$option = get_option($name);
// other arguments
if($keys){
return $this->array_get($option, $keys);
}
// return
return $option;
}
));
// {request:url_param}
// {request:url_param:child}
$this->add_tag(array(
'name' => 'request',
'condition' => function($args){
return !acf_is_empty($args);
},
'resolver' => function($args){
return $this->array_get($_REQUEST, $args);
}
));
// {query_var:my_var}
// {query_var:my_var:child}
$this->add_tag(array(
'name' => 'query_var',
'condition' => function($args){
return !acf_is_empty($args);
},
'resolver' => function($args){
// extract args
$keys = explode(':', $args);
$name = array_shift($keys);
// get query var
$query_var = get_query_var($name);
// other arguments
if($keys){
return $this->array_get($query_var, $keys);
}
return $query_var;
}
));
// {post}
// {post:ID}
$this->add_tag(array(
'name' => 'post',
'resolver' => function($args){
// extract
$keys = explode(':', $args);
$keys = array_merge(array('0'), $keys);
// merge back
$args = implode(':', $keys);
// fallback to {get_post}
$tag = $this->get_tag('get_post');
return call_user_func_array($tag['resolver'], array($args));
}
));
// {get_post}
// {get_post:145}
// {get_post:145:ID}
$this->add_tag(array(
'name' => 'get_post',
'resolver' => function($args){
// extract args
$keys = explode(':', $args);
$post_id = (int) array_shift($keys);
// current post
if(empty($post_id)){
// default
$post_id = null;
// check form context exists
$form = $this->get_context('form');
if($form){
// decode post_id
$decoded = acf_decode_post_id($form['post_id']);
// check if post
if($decoded['type'] === 'post'){
$post_id = $decoded['id'];
}
}
}
// merge back
$args = implode(':', $keys);
// default to ID
if(empty($args)){
$args = 'ID';
}
// allow id/post_id
if(in_array(strtolower($args), array('id', 'post_id'), true)){
$args = 'ID';
}
// get post
$post = get_post($post_id, ARRAY_A);
// validate post found
if(!$post){
// allow to return ID '0' if no post found
if($args === 'ID'){
return $post_id;
}
return false;
}
// additional fields
$post['permalink'] = get_permalink($post['ID']);
$post['admin_url'] = admin_url("post.php?post={$post['ID']}&action=edit");
$post['post_author_data'] = $this->get_user_array($post['post_author']);
// return
return $this->array_get($post, $args);
}
));
// {term}
// {term:term_id}
$this->add_tag(array(
'name' => 'term',
'resolver' => function($args){
// extract
$keys = explode(':', $args);
$keys = array_merge(array('0'), $keys);
// merge back
$args = implode(':', $keys);
// fallback to {get_term}
$tag = $this->get_tag('get_term');
return call_user_func_array($tag['resolver'], array($args));
}
));
// {get_term}
// {get_term:45}
// {get_term:45:term_id}
$this->add_tag(array(
'name' => 'get_term',
'resolver' => function($args){
// extract args
$keys = explode(':', $args);
$term_id = (int) array_shift($keys);
if(empty($term_id)){
$term_id = null; // current queried object
// check form context exists
$form = $this->get_context('form');
if($form){
// decode post_id
$decoded = acf_decode_post_id($form['post_id']);
// check if post
if($decoded['type'] === 'term'){
$term_id = $decoded['id'];
}
}
}
// merge back
$args = implode(':', $keys);
// default to ID
if(empty($args)){
$args = 'term_id';
}
// allow id/term_id
if(in_array(strtolower($args), array('id', 'term_id'), true)){
$args = 'term_id';
}
// get object
if($term_id === null){
$object = get_queried_object();
}else{
$object = get_term($term_id);
}
// validate term found
if(!$object || is_wp_error($object) || !is_a($object, 'WP_Term')){
// allow to return ID '0' if no term found
if($args === 'term_id'){
return $term_id;
}
return false;
}
// convert to array
$term = (array) $object;
// additional fields
$term['permalink'] = get_term_link($term['term_id']);
$term['admin_url'] = admin_url("term.php?taxonomy={$term['taxonomy']}&tag_ID={$term['term_id']}");
// return
return $this->array_get($term, $args);
}
));
// {user}
// {user:ID}
$this->add_tag(array(
'name' => 'user',
'resolver' => function($args){
// extract
$keys = explode(':', $args);
$keys = array_merge(array('0'), $keys);
// merge back
$args = implode(':', $keys);
// fallback to {get_user}
$tag = $this->get_tag('get_user');
return call_user_func_array($tag['resolver'], array($args));
}
));
// {get_user}
// {get_user:25}
// {get_user:25:ID}
$this->add_tag(array(
'name' => 'get_user',
'resolver' => function($args){
// extract args
$keys = explode(':', $args);
$user_id = (int) array_shift($keys);
if(empty($user_id)){
$user_id = get_current_user_id(); // current user
}
// merge back
$args = implode(':', $keys);
// default to ID
if(empty($args)){
$args = 'ID';
}
// allow id/user_id
if(in_array(strtolower($args), array('id', 'user_id'), true)){
$args = 'ID';
}
// allow to return ID '0' if not logged in
if($args === 'ID'){
return $user_id;
}
// get user array
$user = $this->get_user_array($user_id);
// validate
if(!$user){
return false;
}
// return
return $this->array_get($user, $args);
}
));
// {author}
// {author:ID}
$this->add_tag(array(
'name' => 'author',
'resolver' => function($args){
// default id
$user_id = 0;
// default to ID
if(empty($args)){
$args = 'ID';
}
// allow id/user_id
if(in_array(strtolower($args), array('id', 'user_id'), true)){
$args = 'ID';
}
// get post
$tag = $this->get_tag('post');
$post_id = call_user_func_array($tag['resolver'], array(''));
$post = get_post($post_id, ARRAY_A);
// validate post found
if(!$post){
// allow to return ID '0' if no post found
if($args === 'ID'){
return $user_id;
}
return false;
}
// get author id
$user_id = (int) $post['post_author'];
$user = $this->get_user_array($user_id);
// validate
if(!$user){
return false;
}
// return
return $this->array_get($user, $args);
}
));
// {form}
// {form:post_id}
$this->add_tag(array(
'name' => 'form',
'resolver' => function($args){
// get form
$form = $this->get_context('form');
if(!$form){
return false;
}
// default to ID
if(empty($args)){
$args = 'ID';
}
// allow id/form_id
if(in_array(strtolower($args), array('id', 'form_id'), true)){
$args = 'ID';
}
// allow name/form_name
if(in_array(strtolower($args), array('name', 'form_name'), true)){
$args = 'name';
}
// allow title/form_title
if(in_array(strtolower($args), array('title', 'form_title'), true)){
$args = 'title';
}
// return
return $this->array_get($form, $args);
}
));
// {current:form:post_id}
// deprecated
$this->add_tag(array(
'name' => 'current',
'condition' => function($args){
return !acf_is_empty($args);
},
'resolver' => function($args){
// tag: {form:post_id}
$keys = explode(':', $args);
$name = array_shift($keys); // form
$args_left = implode(':', $keys); // post_id
// deprecated warning
$deprecated_version = $name === 'form' ? '0.8.7.5' : '0.8.8';
_deprecated_function('ACF Extended: "{current:' . $args . '}" template tag', $deprecated_version, "the new {" . $args . "} Template Tag");
// get fallback tag (form, post, term etc...)
$tag = $this->get_tag($name);
// return tag resolver
if($tag && is_callable($tag['resolver'])){
return call_user_func_array($tag['resolver'], array($args_left));
}
return false;
}
));
// {generate_password}
$this->add_tag(array(
'name' => 'generate_password',
'resolver' => function($args){
return wp_generate_password(8, false);
}
));
// {generated_id}
$this->add_tag(array(
'name' => 'generated_id',
'condition' => function($args){
return (bool) $this->get_context('generated_id');
},
'resolver' => function($args){
return $this->get_context('generated_id');
}
));
// {date}
// {date:+2 days +1 year}
// {date:+2 days +1 year:d/m/Y}
$this->add_tag(array(
'name' => 'date',
'resolver' => function($args){
// escape
$args = str_replace('\:', '[sep]', $args);
// explode
$keys = explode(':', $args);
$date = array_shift($keys);
$format = array_shift($keys);
// unespace
$date = str_replace('[sep]', ':', $date);
$format = str_replace('[sep]', ':', $format);
$date = $date ? $date : 'now';
$format = $format ? $format : 'Y-m-d';
return date($format, strtotime($date));
}
));
}
/**
* parse
*
* @param $string
* @param $tmp_context
*
* @return mixed|string
*/
function parse($string, $tmp_context){
// array
if(is_array($string)){
foreach(array_keys($string) as $key){
$string[ $key ] = $this->parse($string[ $key ], $tmp_context);
}
}
// validate
if(!is_string($string) || empty($string)){
return $string;
}
// add temp context
if(is_array($tmp_context)){
$this->add_context($tmp_context);
}
// match
while($this->match($string)){}
// remove temp context
if(is_array($tmp_context)){
$this->delete_context($tmp_context);
}
// restore excluded tags
$string = str_replace('ACFE_ESCAPE_START', '{', $string);
$string = str_replace('ACFE_ESCAPE_END', '}', $string);
// return
return $string;
}
/**
* match
*
* @param $string
*
* @return bool
*/
function match(&$string){
// direct tags
// match field_65671b1d11f07 current_post etc...
$direct_tags = $this->get_tags(array('direct' => true));
// loop
foreach($direct_tags as $tag){
// default condition
$condition = $string === $tag['name'];
// tag condition
if(is_callable($tag['condition'])){
$condition = call_user_func_array($tag['condition'], array($string));
}
// check condition
if($condition && is_callable($tag['resolver'])){
// replace string
$string = call_user_func_array($tag['resolver'], array($string));
if($this->get_context('return') !== 'raw'){
// convert array into value
if(is_array($string)){
$string = acfe_array_first($string, function($val){
return is_string($val) || is_numeric($val) || is_bool($val);
}, false);
}
}
// stop loop
return false;
}
}
// tags
// match {name} {name:value} {name:value:value2} etc...
preg_match_all('/[{\w: +-\\\]*(?{(?[\w]+)(?!:})(?::(?.*?))?})}*/', $string, $matches);
// keys
$keys = current($matches);
// stop loop
if(empty($keys)){
return false;
}
// loop matches
foreach(array_keys($keys) as $i){
// vars
$replace = '';
$search = $matches['full'][ $i ]; // {field:my_field}
$name = trim($matches['name'][ $i ]); // field
$value = trim($matches['value'][ $i ]); // my_field
// get tags
$tags = $this->get_tags(array('direct' => false, 'name' => $name));
if($tags){
// var
$resolver_executed = false;
// loop tags
foreach($tags as $tag){
// default condition
$condition = true;
// check condition
if(is_callable($tag['condition'])){
$condition = call_user_func_array($tag['condition'], array($value));
}
// condition ok: call resolver
if($condition && is_callable($tag['resolver'])){
// execute resolver
$replace = call_user_func_array($tag['resolver'], array($value));
if($this->get_context('return') !== 'raw'){
// convert array into value
if(is_array($replace)){
$replace = acfe_array_first($replace, function($val){
return is_string($val) || is_numeric($val) || is_bool($val);
}, false);
}
}
// escape brackets
if(!empty($replace) && is_string($replace)){
$replace = str_replace('{', 'ACFE_ESCAPE_START', $replace);
$replace = str_replace('}', 'ACFE_ESCAPE_END', $replace);
}
// marker
$resolver_executed = true;
}
// resolver executed: stop tags loop
if($resolver_executed){
break;
}
}
// condition/resolver not executed: add as excluded tag
// this will prevent current tag from being parsed again by next loop call
if(!$resolver_executed){
$replace = $search;
$replace = str_replace('{', 'ACFE_ESCAPE_START', $replace);
$replace = str_replace('}', 'ACFE_ESCAPE_END', $replace);
}
}
// raw context + array
if($this->get_context('return') === 'raw' && is_array($replace)){
// return array
$string = $replace;
// stop loop
return false;
}
if($replace === null || $replace === false){
$replace = '';
}
// replace
$string = str_replace($search, $replace, $string);
}
// keep looping
return true;
}
/**
* clear
*
* @param $string
*
* @return array|mixed|string|string[]
*/
function clear($string){
// array
if(is_array($string)){
foreach(array_keys($string) as $key){
$string[ $key ] = $this->clear($string[ $key ]);
}
}
// validate
if(!is_string($string) || empty($string)){
return $string;
}
// match
while($this->match_clear($string)){}
// return
return $string;
}
/**
* match_clear
*
* @param $string
*
* @return bool
*/
function match_clear(&$string){
// tags
// match {name} {name:value} {name:value:value2} etc...
preg_match_all('/[{\w: +-\\\]*(?{(?[\w]+)(?!:})(?::(?.*?))?})}*/', $string, $matches);
// keys
$keys = current($matches);
// stop loop
if(empty($keys)){
return false;
}
// loop matches
foreach(array_keys($keys) as $i){
// remove tags
$string = str_replace($matches['full'][ $i ], '', $string);
}
// keep looping
return true;
}
/**
* array_get
*
* @param $array
* @param $key
* @param $default
*
* @return mixed|null
*/
function array_get($array, $key, $default = null){
$key = acfe_as_string($key, ':');
$key = str_replace(':', '.', $key);
return acfe_get($array, $key, $default);
}
/**
* get_user_array
*
* @param $user_id
*
* @return array|false
*/
function get_user_array($user_id){
// bail early if user id is 0
if(!$user_id){
return false;
}
// user object
$user = get_user_by('ID', $user_id);
// validate
if(!$user){
return false;
}
// cast as array
$user = (array) $user->data;
// user meta
$user_meta = get_user_meta($user_id);
foreach($user_meta as $k => $v){
if(isset($v[0])){
$user[ $k ] = $v[0];
}
}
// additional fields
$user['permalink'] = get_author_posts_url($user_id);
$user['admin_url'] = admin_url("user-edit.php?user_id=$user_id");
// return
return $user;
}
/**
* get_field_key_from_field_groups
*
* @param $name
*
* @return mixed
*/
function get_field_key_from_field_groups($name){
$mapped = $this->get_context('mapped_fields');
if(!empty($mapped) && isset($mapped[ $name ])){
return $mapped[ $name ];
}
return $name;
}
/**
* add_tag
*
* @param $args
*/
function add_tag($args){
$this->tags[] = $args;
}
/**
* get_tag
*
* @param $name
*
* @return array|false|mixed
*/
function get_tag($name){
return current($this->get_tags(array('name' => $name)));
}
/**
* get_tags
*
* @return array
*/
function get_tags($args = array(), $operator = 'AND'){
// vars
$tags = array();
// loop raw tags
foreach($this->tags as $tag){
// validate tag
$tag = $this->validate_tag($tag);
// append
if($tag){
$tags[] = $tag;
}
}
// found tags
if($tags){
// sort by priority
$priority = array_column($tags, 'priority');
array_multisort($priority, SORT_ASC, $tags);
// filter by args
if($args){
$tags = wp_list_filter($tags, $args, $operator);
$tags = array_values($tags);
}
return $tags;
}
// return
return $tags;
}
/**
* validate_tag
*
* @param $args
*
* @return array
*/
function validate_tag($args){
// default args
$args = wp_parse_args($args, array(
'name' => '',
'priority' => 10,
'direct' => false,
'condition' => false,
'resolver' => false,
));
// validate args
$args['direct'] = (bool) $args['direct'];
$args['priority'] = (int) $args['priority'];
// return
return $args;
}
/**
* get_context
*
* @param $name
*
* @return array|mixed|null
*/
function has_context($name){
return acfe_get($this->context, $name) !== null;
}
/**
* get_context
*
* @param $name
*
* @return array|mixed|null
*/
function get_context($name = false, $default = null){
if($name){
return acfe_get($this->context, $name, $default);
}
return $this->context;
}
/**
* add_context
*
* @param $name
* @param $value
*/
function add_context($name, $value = null){
// array
if(is_array($name) && $value === null){
foreach($name as $key => $val){
$this->context[ $key ] = $val;
}
// normal
}else{
$this->context[ $name ] = $value;
}
}
/**
* delete_context
*
* @param $name
*/
function delete_context($name){
// array
if(is_array($name)){
if(acf_is_associative_array($name)){
foreach(array_keys($name) as $key){
unset($this->context[ $key ]);
}
}else{
foreach($name as $key){
unset($this->context[ $key ]);
}
}
// normal
}else{
unset($this->context[ $name ]);
}
}
/**
* clear_context
*/
function clear_context(){
$this->context = array();
}
}
acf_new_instance('acfe_template_tags');
endif;
/**
* acfe_add_tag
*
* @param $args
*/
function acfe_add_tag($args){
acf_get_instance('acfe_template_tags')->add_tag($args);
}
/**
* acfe_get_tag
*
* @param $name
*
* @return mixed
*/
function acfe_get_tag($name){
return acf_get_instance('acfe_template_tags')->get_tag($name);
}
/**
* acfe_get_tags
*
* @return mixed
*/
function acfe_get_tags($args = array(), $operator = 'AND'){
return acf_get_instance('acfe_template_tags')->get_tags($args, $operator);
}
/**
* acfe_get_context
*
* @param $name
*
* @return mixed
*/
function acfe_get_context($name = false, $default = null){
return acf_get_instance('acfe_template_tags')->get_context($name, $default);
}
/**
* acfe_add_context
*
* @param $name
* @param $value
*/
function acfe_add_context($name, $value = null){
acf_get_instance('acfe_template_tags')->add_context($name, $value);
}
/**
* acfe_delete_context
*
* @param $name
*/
function acfe_delete_context($name){
acf_get_instance('acfe_template_tags')->delete_context($name);
}
/**
* acfe_clear_context
*/
function acfe_clear_context(){
acf_get_instance('acfe_template_tags')->clear_context();
}
/**
* acfe_parse_tags
*
* @param $text
* @param $tmp_context
*
* @return mixed
*/
function acfe_parse_tags($text, $tmp_context = null){
return acf_get_instance('acfe_template_tags')->parse($text, $tmp_context);
}
/**
* acfe_apply_tags
*
* @param $text
* @param $tmp_context
*
* @return mixed
*/
function acfe_apply_tags(&$text, $tmp_context = null){
$text = acfe_parse_tags($text, $tmp_context);
return $text;
}
/**
* acfe_clear_tags
*
* @param $text
*
* @return mixed
*/
function acfe_clear_tags($text){
return acf_get_instance('acfe_template_tags')->clear($text);
}
/**
* acfe_apply_clear_tags
*
* @param $text
*
* @return mixed
*/
function acfe_apply_clear_tags(&$text){
$text = acfe_clear_tags($text);
return $text;
}