first commit

This commit is contained in:
Roman Pyrih
2025-07-11 12:34:24 +02:00
commit 296b13244b
10181 changed files with 3916595 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
<?php
if ( ! class_exists( 'acf_field__accordion' ) ) :
class acf_field__accordion extends acf_field {
public $show_in_rest = false;
/**
* initialize
*
* This function will setup the field type data
*
* @date 30/10/17
* @since 5.6.3
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'accordion';
$this->label = __( 'Accordion', 'acf' );
$this->category = 'layout';
$this->description = __( 'Allows you to group and organize custom fields into collapsable panels that are shown while editing content. Useful for keeping large datasets tidy.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-accordion.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/accordion/', 'docs', 'field-type-selection' );
$this->supports = array(
'required' => false,
'bindings' => false,
);
$this->defaults = array(
'open' => 0,
'multi_expand' => 0,
'endpoint' => 0,
);
}
/**
* render_field
*
* Create the HTML interface for your field
*
* @date 30/10/17
* @since 5.6.3
*
* @param array $field
* @return n/a
*/
function render_field( $field ) {
// vars
$atts = array(
'class' => 'acf-fields',
'data-open' => $field['open'],
'data-multi_expand' => $field['multi_expand'],
'data-endpoint' => $field['endpoint'],
);
?>
<div <?php echo acf_esc_attrs( $atts ); ?>></div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Open', 'acf' ),
'instructions' => __( 'Display this accordion as open on page load.', 'acf' ),
'name' => 'open',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Multi-Expand', 'acf' ),
'instructions' => __( 'Allow this accordion to open without closing others.', 'acf' ),
'name' => 'multi_expand',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Endpoint', 'acf' ),
'instructions' => __( 'Define an endpoint for the previous accordion to stop. This accordion will not be visible.', 'acf' ),
'name' => 'endpoint',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* This filter is appied to the $field after it is loaded from the database
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $field - the field array holding all the field options
*
* @return $field - the field array holding all the field options
*/
function load_field( $field ) {
// remove name to avoid caching issue
$field['name'] = '';
// remove required to avoid JS issues
$field['required'] = 0;
// set value other than 'null' to avoid ACF loading / caching issue
$field['value'] = false;
// return
return $field;
}
}
// initialize
acf_register_field_type( 'acf_field__accordion' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,308 @@
<?php
if ( ! class_exists( 'acf_field_button_group' ) ) :
class acf_field_button_group extends acf_field {
/**
* initialize()
*
* This function will setup the field type data
*
* @date 18/9/17
* @since 5.6.3
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'button_group';
$this->label = __( 'Button Group', 'acf' );
$this->category = 'choice';
$this->description = __( 'A group of buttons with values that you specify, users can choose one option from the values provided.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-button-group.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/button-group/', 'docs', 'field-type-selection' );
$this->defaults = array(
'choices' => array(),
'default_value' => '',
'allow_null' => 0,
'return_format' => 'value',
'layout' => 'horizontal',
);
}
/**
* Creates the field's input HTML
*
* @since 5.6.3
*
* @param array $field The field settings array
*/
public function render_field( $field ) {
// vars
$html = '';
$selected = null;
$buttons = array();
$value = esc_attr( $field['value'] );
// bail ealrly if no choices
if ( empty( $field['choices'] ) ) {
return;
}
// buttons
foreach ( $field['choices'] as $_value => $_label ) {
// checked
$checked = ( $value === esc_attr( $_value ) );
if ( $checked ) {
$selected = true;
}
// append
$buttons[] = array(
'name' => $field['name'],
'value' => $_value,
'label' => $_label,
'checked' => $checked,
);
}
// maybe select initial value
if ( ! $field['allow_null'] && $selected === null ) {
$buttons[0]['checked'] = true;
}
// div
$div = array( 'class' => 'acf-button-group' );
if ( $field['layout'] == 'vertical' ) {
$div['class'] .= ' -vertical'; }
if ( $field['class'] ) {
$div['class'] .= ' ' . $field['class']; }
if ( $field['allow_null'] ) {
$div['data-allow_null'] = 1; }
// hdden input
$html .= acf_get_hidden_input( array( 'name' => $field['name'] ) );
// open
$html .= '<div ' . acf_esc_attrs( $div ) . '>';
// loop
foreach ( $buttons as $button ) {
// checked
if ( $button['checked'] ) {
$button['checked'] = 'checked';
} else {
unset( $button['checked'] );
}
// append
$html .= acf_get_radio_input( $button );
}
// close
$html .= '</div>';
// return
echo $html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- safe HTML, escaped by input building functions.
}
/**
* render_field_settings()
*
* Creates the field's settings HTML
*
* @date 18/9/17
* @since 5.6.3
*
* @param array $field The field settings array
* @return n/a
*/
function render_field_settings( $field ) {
// Encode choices (convert from array).
$field['choices'] = acf_encode_choices( $field['choices'] );
acf_render_field_setting(
$field,
array(
'label' => __( 'Choices', 'acf' ),
'instructions' => __( 'Enter each choice on a new line.', 'acf' ) . '<br />' . __( 'For more control, you may specify both a value and label like this:', 'acf' ) . '<br /><span class="acf-field-setting-example">' . __( 'red : Red', 'acf' ) . '</span>',
'type' => 'textarea',
'name' => 'choices',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'text',
'name' => 'default_value',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Value', 'acf' ),
'instructions' => __( 'Specify the returned value on front end', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'layout' => 'horizontal',
'choices' => array(
'value' => __( 'Value', 'acf' ),
'label' => __( 'Label', 'acf' ),
'array' => __( 'Both (Array)', 'acf' ),
),
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Null', 'acf' ),
'instructions' => '',
'name' => 'allow_null',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Layout', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'layout',
'layout' => 'horizontal',
'choices' => array(
'horizontal' => __( 'Horizontal', 'acf' ),
'vertical' => __( 'Vertical', 'acf' ),
),
)
);
}
/**
* This filter is appied to the $field before it is saved to the database
*
* @date 18/9/17
* @since 5.6.3
*
* @param array $field The field array holding all the field options
* @return $field
*/
function update_field( $field ) {
return acf_get_field_type( 'radio' )->update_field( $field );
}
/**
* This filter is appied to the $value after it is loaded from the db
*
* @date 18/9/17
* @since 5.6.3
*
* @param mixed $value The value found in the database
* @param mixed $post_id The post ID from which the value was loaded from
* @param array $field The field array holding all the field options
* @return $value
*/
function load_value( $value, $post_id, $field ) {
return acf_get_field_type( 'radio' )->load_value( $value, $post_id, $field );
}
/**
* This function will translate field settings
*
* @date 18/9/17
* @since 5.6.3
*
* @param array $field The field array holding all the field options
* @return $field
*/
function translate_field( $field ) {
return acf_get_field_type( 'radio' )->translate_field( $field );
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @date 18/9/17
* @since 5.6.3
*
* @param mixed $value The value found in the database
* @param mixed $post_id The post ID from which the value was loaded from
* @param array $field The field array holding all the field options
* @return $value
*/
function format_value( $value, $post_id, $field ) {
return acf_get_field_type( 'radio' )->format_value( $value, $post_id, $field );
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
function get_rest_schema( array $field ) {
$schema = parent::get_rest_schema( $field );
if ( isset( $field['default_value'] ) && '' !== $field['default_value'] ) {
$schema['default'] = $field['default_value'];
}
$schema['enum'] = acf_get_field_type( 'select' )->format_rest_choices( $field['choices'] );
$schema['enum'][] = null;
// Allow null via UI will value to empty string.
if ( ! empty( $field['allow_null'] ) ) {
$schema['enum'][] = '';
}
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_button_group' );
endif; // class_exists check

View File

@@ -0,0 +1,592 @@
<?php
if ( ! class_exists( 'acf_field_checkbox' ) ) :
class acf_field_checkbox extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'checkbox';
$this->label = __( 'Checkbox', 'acf' );
$this->category = 'choice';
$this->description = __( 'A group of checkbox inputs that allow the user to select one, or multiple values that you specify.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-checkbox.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/checkbox/', 'docs', 'field-type-selection' );
$this->defaults = array(
'layout' => 'vertical',
'choices' => array(),
'default_value' => '',
'allow_custom' => 0,
'save_custom' => 0,
'toggle' => 0,
'return_format' => 'value',
'custom_choice_button_text' => __( 'Add new choice', 'acf' ),
);
}
/**
* Create the HTML interface for your field
*
* @param $field (array) the $field being rendered
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field (array) the $field being edited
* @return n/a
*/
function render_field( $field ) {
// reset vars
$this->_values = array();
$this->_all_checked = true;
// ensure array
$field['value'] = acf_get_array( $field['value'] );
$field['choices'] = acf_get_array( $field['choices'] );
// hiden input
acf_hidden_input( array( 'name' => $field['name'] ) );
// vars
$li = '';
$ul = array(
'class' => 'acf-checkbox-list',
);
// append to class
$ul['class'] .= ' ' . ( $field['layout'] == 'horizontal' ? 'acf-hl' : 'acf-bl' );
$ul['class'] .= ' ' . $field['class'];
// checkbox saves an array
$field['name'] .= '[]';
// choices
if ( ! empty( $field['choices'] ) ) {
// choices
$li .= $this->render_field_choices( $field );
// toggle
if ( $field['toggle'] ) {
$li = $this->render_field_toggle( $field ) . $li;
}
}
// custom
if ( $field['allow_custom'] ) {
$li .= $this->render_field_custom( $field );
}
// return
echo '<ul ' . acf_esc_attrs( $ul ) . '>' . "\n" . $li . '</ul>' . "\n"; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped by specific render methods above.
}
/**
* description
*
* @type function
* @date 15/7/17
* @since 5.6.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function render_field_choices( $field ) {
// walk
return $this->walk( $field['choices'], $field );
}
/**
* Validates values for the checkbox field
*
* @since 6.0.0
*
* @param boolean $valid If the field is valid.
* @param mixed $value The value to validate.
* @param array $field The main field array.
* @param string $input The input element's name attribute.
* @return boolean
*/
public function validate_value( $valid, $value, $field, $input ) {
if ( ! is_array( $value ) || empty( $field['allow_custom'] ) ) {
return $valid;
}
foreach ( $value as $value ) {
if ( empty( $value ) && $value !== '0' ) {
return __( 'Checkbox custom values cannot be empty. Uncheck any empty values.', 'acf' );
}
}
return $valid;
}
/**
* description
*
* @type function
* @date 15/7/17
* @since 5.6.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function render_field_toggle( $field ) {
// vars
$atts = array(
'type' => 'checkbox',
'class' => 'acf-checkbox-toggle',
'label' => __( 'Toggle All', 'acf' ),
);
// custom label
if ( is_string( $field['toggle'] ) ) {
$atts['label'] = $field['toggle'];
}
// checked
if ( $this->_all_checked ) {
$atts['checked'] = 'checked';
}
// return
return '<li>' . acf_get_checkbox_input( $atts ) . '</li>' . "\n";
}
/**
* description
*
* @type function
* @date 15/7/17
* @since 5.6.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function render_field_custom( $field ) {
// vars
$html = '';
// loop
foreach ( $field['value'] as $value ) {
// ignore if already eixsts
if ( isset( $field['choices'][ $value ] ) ) {
continue;
}
// vars
$esc_value = esc_attr( $value );
$text_input = array(
'name' => $field['name'],
'value' => $value,
);
// bail early if choice already exists
if ( in_array( $esc_value, $this->_values ) ) {
continue;
}
// append
$html .= '<li><input class="acf-checkbox-custom" type="checkbox" checked="checked" />' . acf_get_text_input( $text_input ) . '</li>' . "\n";
}
// append button
$html .= '<li><a href="#" class="button acf-add-checkbox">' . esc_attr( $field['custom_choice_button_text'] ) . '</a></li>' . "\n";
// return
return $html;
}
function walk( $choices = array(), $args = array(), $depth = 0 ) {
// bail early if no choices
if ( empty( $choices ) ) {
return '';
}
// defaults
$args = wp_parse_args(
$args,
array(
'id' => '',
'type' => 'checkbox',
'name' => '',
'value' => array(),
'disabled' => array(),
)
);
// vars
$html = '';
// sanitize values for 'selected' matching
if ( $depth == 0 ) {
$args['value'] = array_map( 'esc_attr', $args['value'] );
$args['disabled'] = array_map( 'esc_attr', $args['disabled'] );
}
// loop
foreach ( $choices as $value => $label ) {
// open
$html .= '<li>';
// optgroup
if ( is_array( $label ) ) {
$html .= '<ul>' . "\n";
$html .= $this->walk( $label, $args, $depth + 1 );
$html .= '</ul>';
// option
} else {
// vars
$esc_value = esc_attr( $value );
$atts = array(
'id' => $args['id'] . '-' . str_replace( ' ', '-', $value ),
'type' => $args['type'],
'name' => $args['name'],
'value' => $value,
'label' => $label,
);
// selected
if ( in_array( $esc_value, $args['value'] ) ) {
$atts['checked'] = 'checked';
} else {
$this->_all_checked = false;
}
// disabled
if ( in_array( $esc_value, $args['disabled'] ) ) {
$atts['disabled'] = 'disabled';
}
// store value added
$this->_values[] = $esc_value;
// append
$html .= acf_get_checkbox_input( $atts );
}
// close
$html .= '</li>' . "\n";
}
// return
return $html;
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
// Encode choices (convert from array).
$field['choices'] = acf_encode_choices( $field['choices'] );
$field['default_value'] = acf_encode_choices( $field['default_value'], false );
acf_render_field_setting(
$field,
array(
'label' => __( 'Choices', 'acf' ),
'instructions' => __( 'Enter each choice on a new line.', 'acf' ) . '<br />' . __( 'For more control, you may specify both a value and label like this:', 'acf' ) . '<br /><span class="acf-field-setting-example">' . __( 'red : Red', 'acf' ) . '</span>',
'type' => 'textarea',
'name' => 'choices',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Enter each default value on a new line', 'acf' ),
'type' => 'textarea',
'name' => 'default_value',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Value', 'acf' ),
'instructions' => __( 'Specify the returned value on front end', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'layout' => 'horizontal',
'choices' => array(
'value' => __( 'Value', 'acf' ),
'label' => __( 'Label', 'acf' ),
'array' => __( 'Both (Array)', 'acf' ),
),
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Custom Values', 'acf' ),
'name' => 'allow_custom',
'type' => 'true_false',
'ui' => 1,
'instructions' => __( "Allow 'custom' values to be added", 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Save Custom Values', 'acf' ),
'name' => 'save_custom',
'type' => 'true_false',
'ui' => 1,
'instructions' => __( "Save 'custom' values to the field's choices", 'acf' ),
'conditions' => array(
'field' => 'allow_custom',
'operator' => '==',
'value' => 1,
),
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Layout', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'layout',
'layout' => 'horizontal',
'choices' => array(
'vertical' => __( 'Vertical', 'acf' ),
'horizontal' => __( 'Horizontal', 'acf' ),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Add Toggle All', 'acf' ),
'instructions' => __( 'Prepend an extra checkbox to toggle all choices', 'acf' ),
'name' => 'toggle',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* This filter is appied to the $field before it is saved to the database
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $field - the field array holding all the field options
* @param $post_id - the field group ID (post_type = acf)
*
* @return $field - the modified field
*/
function update_field( $field ) {
// Decode choices (convert to array).
$field['choices'] = acf_decode_choices( $field['choices'] );
$field['default_value'] = acf_decode_choices( $field['default_value'], true );
return $field;
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value which will be saved in the database
* @param $post_id - the post_id of which the value will be saved
* @param $field - the field array holding all the field options
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
// bail early if is empty
if ( empty( $value ) ) {
return $value;
}
// select -> update_value()
$value = acf_get_field_type( 'select' )->update_value( $value, $post_id, $field );
// save_other_choice
if ( $field['save_custom'] ) {
// get raw $field (may have been changed via repeater field)
// if field is local, it won't have an ID
$selector = $field['ID'] ? $field['ID'] : $field['key'];
$field = acf_get_field( $selector );
if ( ! $field ) {
return false;
}
// bail early if no ID (JSON only)
if ( ! $field['ID'] ) {
return $value;
}
// loop
foreach ( $value as $v ) {
// ignore if already eixsts
if ( isset( $field['choices'][ $v ] ) ) {
continue;
}
// unslash (fixes serialize single quote issue)
$v = wp_unslash( $v );
// sanitize (remove tags)
$v = sanitize_text_field( $v );
// append
$field['choices'][ $v ] = $v;
}
// save
acf_update_field( $field );
}
// return
return $value;
}
/**
* This function will translate field settings
*
* @type function
* @date 8/03/2016
* @since 5.3.2
*
* @param $field (array)
* @return $field
*/
function translate_field( $field ) {
return acf_get_field_type( 'select' )->translate_field( $field );
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// Bail early if is empty.
if ( acf_is_empty( $value ) ) {
return array();
}
// Always convert to array of items.
$value = acf_array( $value );
// Return.
return acf_get_field_type( 'select' )->format_value( $value, $post_id, $field );
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'integer', 'string', 'array', 'null' ),
'required' => isset( $field['required'] ) && $field['required'],
'items' => array(
'type' => array( 'string', 'integer' ),
),
);
if ( isset( $field['default_value'] ) && '' !== $field['default_value'] ) {
$schema['default'] = $field['default_value'];
}
// If we allow custom values, nothing else to do here.
if ( ! empty( $field['allow_custom'] ) ) {
return $schema;
}
$schema['items']['enum'] = acf_get_field_type( 'select' )->format_rest_choices( $field['choices'] );
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_checkbox' );
endif; // class_exists check

View File

@@ -0,0 +1,279 @@
<?php
if ( ! class_exists( 'acf_field_color_picker' ) ) :
class acf_field_color_picker extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'color_picker';
$this->label = __( 'Color Picker', 'acf' );
$this->category = 'advanced';
$this->description = __( 'An interactive UI for selecting a color, or specifying a Hex value.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-color-picker.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/color-picker/', 'docs', 'field-type-selection' );
$this->defaults = array(
'default_value' => '',
'enable_opacity' => false,
'return_format' => 'string', // 'string'|'array'
);
}
/**
* description
*
* @type function
* @date 16/12/2015
* @since 5.3.2
*
* @param $post_id (int)
* @return $post_id (int)
*/
function input_admin_enqueue_scripts() {
// Register scripts for non-admin.
// Applies logic from wp_default_scripts() function defined in "wp-includes/script-loader.php".
if ( ! is_admin() ) {
$suffix = defined( 'ACF_DEVELOPMENT_MODE' ) && ACF_DEVELOPMENT_MODE ? '' : '.min';
$scripts = wp_scripts();
$scripts->add( 'iris', '/wp-admin/js/iris.min.js', array( 'jquery-ui-draggable', 'jquery-ui-slider', 'jquery-touch-punch' ), '1.0.7', 1 );
$scripts->add( 'wp-color-picker', "/wp-admin/js/color-picker$suffix.js", array( 'iris' ), false, 1 );
// Handle localisation across multiple WP versions.
// WP 5.0+
if ( method_exists( $scripts, 'set_translations' ) ) {
$scripts->set_translations( 'wp-color-picker' );
// WP 4.9
} else {
$scripts->localize(
'wp-color-picker',
'wpColorPickerL10n',
array(
'clear' => __( 'Clear', 'acf' ),
'clearAriaLabel' => __( 'Clear color', 'acf' ),
'defaultString' => __( 'Default', 'acf' ),
'defaultAriaLabel' => __( 'Select default color', 'acf' ),
'pick' => __( 'Select Color', 'acf' ),
'defaultLabel' => __( 'Color value', 'acf' ),
)
);
}
}
// Enqueue alpha color picker assets.
wp_enqueue_script(
'acf-color-picker-alpha',
acf_get_url( 'assets/inc/color-picker-alpha/wp-color-picker-alpha.js' ),
array( 'jquery', 'wp-color-picker' ),
'3.0.0'
);
// Enqueue.
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
acf_localize_data(
array(
'colorPickerL10n' => array(
'hex_string' => __( 'Hex String', 'acf' ),
'rgba_string' => __( 'RGBA String', 'acf' ),
),
)
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
$text_input = acf_get_sub_array( $field, array( 'id', 'class', 'name', 'value' ) );
$hidden_input = acf_get_sub_array( $field, array( 'name', 'value' ) );
$text_input['data-alpha-skip-debounce'] = true;
// Color picker alpha library requires a specific data attribute to exist.
if ( $field['enable_opacity'] ) {
$text_input['data-alpha-enabled'] = true;
}
// html
?>
<div class="acf-color-picker">
<?php acf_hidden_input( $hidden_input ); ?>
<?php acf_text_input( $text_input ); ?>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
// display_format
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => '',
'type' => 'text',
'name' => 'default_value',
'placeholder' => '#FFFFFF',
)
);
// Toggle opacity control.
acf_render_field_setting(
$field,
array(
'label' => __( 'Enable Transparency', 'acf' ),
'instructions' => '',
'type' => 'true_false',
'name' => 'enable_opacity',
'ui' => 1,
)
);
// Return format control.
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'return_format',
'layout' => 'horizontal',
'choices' => array(
'string' => __( 'Hex String', 'acf' ),
'array' => __( 'RGBA Array', 'acf' ),
),
)
);
}
/**
* Format the value for use in templates. At this stage, the value has been loaded from the
* database and is being returned by an API function such as get_field(), the_field(), etc.
*
* @since 5.10
*
* @param mixed $value The field value
* @param integer $post_id The post ID
* @param array $field The field array
* @return string|array
*/
public function format_value( $value, $post_id, $field ) {
if ( isset( $field['return_format'] ) && $field['return_format'] === 'array' ) {
$value = $this->string_to_array( $value );
}
return $value;
}
/**
* Convert either a Hexadecimal or RGBA string to an RGBA array.
*
* @since 5.10
* @date 15/12/20
*
* @param string $value
* @return array
*/
private function string_to_array( $value ) {
$value = is_string( $value ) ? trim( $value ) : '';
// Match and collect r,g,b values from 6 digit hex code. If there are 4
// match-results, we have the values we need to build an r,g,b,a array.
preg_match( '/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i', $value, $matches );
if ( count( $matches ) === 4 ) {
return array(
'red' => hexdec( $matches[1] ),
'green' => hexdec( $matches[2] ),
'blue' => hexdec( $matches[3] ),
'alpha' => (float) 1,
);
}
// Match and collect r,g,b values from 3 digit hex code. If there are 4
// match-results, we have the values we need to build an r,g,b,a array.
// We have to duplicate the matched hex digit for 3 digit hex codes.
preg_match( '/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i', $value, $matches );
if ( count( $matches ) === 4 ) {
return array(
'red' => hexdec( $matches[1] . $matches[1] ),
'green' => hexdec( $matches[2] . $matches[2] ),
'blue' => hexdec( $matches[3] . $matches[3] ),
'alpha' => (float) 1,
);
}
// Attempt to match an rgba(…) or rgb(…) string (case-insensitive), capturing the decimals
// as a string. If there are two match results, we have the RGBA decimal values as a
// comma-separated string. Break it apart and, depending on the number of values, return
// our formatted r,g,b,a array.
preg_match( '/^rgba?\(([0-9,.]+)\)/i', $value, $matches );
if ( count( $matches ) === 2 ) {
$decimals = explode( ',', $matches[1] );
// Handle rgba() format.
if ( count( $decimals ) === 4 ) {
return array(
'red' => (int) $decimals[0],
'green' => (int) $decimals[1],
'blue' => (int) $decimals[2],
'alpha' => (float) $decimals[3],
);
}
// Handle rgb() format.
if ( count( $decimals ) === 3 ) {
return array(
'red' => (int) $decimals[0],
'green' => (int) $decimals[1],
'blue' => (int) $decimals[2],
'alpha' => (float) 1,
);
}
}
return array(
'red' => 0,
'green' => 0,
'blue' => 0,
'alpha' => (float) 0,
);
}
}
// initialize
acf_register_field_type( 'acf_field_color_picker' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,314 @@
<?php
if ( ! class_exists( 'acf_field_date_picker' ) ) :
class acf_field_date_picker extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'date_picker';
$this->label = __( 'Date Picker', 'acf' );
$this->category = 'advanced';
$this->description = __( 'An interactive UI for picking a date. The date return format can be customized using the field settings.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-date-picker.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/date-picker/', 'docs', 'field-type-selection' );
$this->defaults = array(
'display_format' => 'd/m/Y',
'return_format' => 'd/m/Y',
'first_day' => 1,
);
}
/**
* description
*
* @type function
* @date 16/12/2015
* @since 5.3.2
*
* @param $post_id (int)
* @return $post_id (int)
*/
function input_admin_enqueue_scripts() {
// bail early if no enqueue
if ( ! acf_get_setting( 'enqueue_datepicker' ) ) {
return;
}
// localize
global $wp_locale;
acf_localize_data(
array(
'datePickerL10n' => array(
'closeText' => _x( 'Done', 'Date Picker JS closeText', 'acf' ),
'currentText' => _x( 'Today', 'Date Picker JS currentText', 'acf' ),
'nextText' => _x( 'Next', 'Date Picker JS nextText', 'acf' ),
'prevText' => _x( 'Prev', 'Date Picker JS prevText', 'acf' ),
'weekHeader' => _x( 'Wk', 'Date Picker JS weekHeader', 'acf' ),
'monthNames' => array_values( $wp_locale->month ),
'monthNamesShort' => array_values( $wp_locale->month_abbrev ),
'dayNames' => array_values( $wp_locale->weekday ),
'dayNamesMin' => array_values( $wp_locale->weekday_initial ),
'dayNamesShort' => array_values( $wp_locale->weekday_abbrev ),
),
)
);
// script
wp_enqueue_script( 'jquery-ui-datepicker' );
// style
wp_enqueue_style( 'acf-datepicker', acf_get_url( 'assets/inc/datepicker/jquery-ui.min.css' ), array(), '1.11.4' );
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$hidden_value = '';
$display_value = '';
// format value
if ( $field['value'] ) {
$hidden_value = acf_format_date( $field['value'], 'Ymd' );
$display_value = acf_format_date( $field['value'], $field['display_format'] );
}
// elements
$div = array(
'class' => 'acf-date-picker acf-input-wrap',
'data-date_format' => acf_convert_date_to_js( $field['display_format'] ),
'data-first_day' => $field['first_day'],
);
$hidden_input = array(
'id' => $field['id'],
'name' => $field['name'],
'value' => $hidden_value,
);
$text_input = array(
'class' => $field['class'] . ' input',
'value' => $display_value,
);
// special attributes
foreach ( array( 'readonly', 'disabled' ) as $k ) {
if ( ! empty( $field[ $k ] ) ) {
$hidden_input[ $k ] = $k;
$text_input[ $k ] = $k;
}
}
// save_format - compatibility with ACF < 5.0.0
if ( ! empty( $field['save_format'] ) ) {
// add custom JS save format
$div['data-save_format'] = $field['save_format'];
// revert hidden input value to raw DB value
$hidden_input['value'] = $field['value'];
// remove formatted value (will do this via JS)
$text_input['value'] = '';
}
// html
?>
<div <?php echo acf_esc_attrs( $div ); ?>>
<?php acf_hidden_input( $hidden_input ); ?>
<?php acf_text_input( $text_input ); ?>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
global $wp_locale;
$d_m_Y = date_i18n( 'd/m/Y' );
$m_d_Y = date_i18n( 'm/d/Y' );
$F_j_Y = date_i18n( 'F j, Y' );
$Ymd = date_i18n( 'Ymd' );
echo '<div class="acf-field-settings-split">';
acf_render_field_setting(
$field,
array(
'label' => __( 'Display Format', 'acf' ),
'hint' => __( 'The format displayed when editing a post', 'acf' ),
'type' => 'radio',
'name' => 'display_format',
'other_choice' => 1,
'choices' => array(
'd/m/Y' => '<span>' . $d_m_Y . '</span><code>d/m/Y</code>',
'm/d/Y' => '<span>' . $m_d_Y . '</span><code>m/d/Y</code>',
'F j, Y' => '<span>' . $F_j_Y . '</span><code>F j, Y</code>',
'other' => '<span>' . __( 'Custom:', 'acf' ) . '</span>',
),
)
);
// save_format - compatibility with ACF < 5.0.0
if ( ! empty( $field['save_format'] ) ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Save Format', 'acf' ),
'hint' => __( 'The format used when saving a value', 'acf' ),
'type' => 'text',
'name' => 'save_format',
// 'readonly' => 1 // this setting was not readonly in v4
)
);
} else {
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'hint' => __( 'The format returned via template functions', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'other_choice' => 1,
'choices' => array(
'd/m/Y' => '<span>' . $d_m_Y . '</span><code>d/m/Y</code>',
'm/d/Y' => '<span>' . $m_d_Y . '</span><code>m/d/Y</code>',
'F j, Y' => '<span>' . $F_j_Y . '</span><code>F j, Y</code>',
'Ymd' => '<span>' . $Ymd . '</span><code>Ymd</code>',
'other' => '<span>' . __( 'Custom:', 'acf' ) . '</span>',
),
)
);
}
echo '</div>';
acf_render_field_setting(
$field,
array(
'label' => __( 'Week Starts On', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'first_day',
'choices' => array_values( $wp_locale->weekday ),
)
);
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// save_format - compatibility with ACF < 5.0.0
if ( ! empty( $field['save_format'] ) ) {
return $value;
}
// return
return acf_format_date( $value, $field['return_format'] );
}
/**
* This filter is applied to the $field after it is loaded from the database
* and ensures the return and display values are set.
*
* @type filter
* @since 5.11.0
* @date 28/09/21
*
* @param array $field The field array holding all the field options.
* @return array
*/
public function load_field( $field ) {
if ( empty( $field['display_format'] ) ) {
$field['display_format'] = $this->defaults['display_format'];
}
if ( empty( $field['return_format'] ) ) {
$field['return_format'] = $this->defaults['return_format'];
}
return $field;
}
/**
* Return the schema array for the REST API.
*
* @param array $field The field array
* @return array
*/
public function get_rest_schema( array $field ) {
return array(
'type' => array( 'string', 'null' ),
'description' => 'A `Ymd` formatted date string.',
'required' => ! empty( $field['required'] ),
);
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
if ( ! $value ) {
return null;
}
return (string) $value;
}
}
// initialize
acf_register_field_type( 'acf_field_date_picker' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,275 @@
<?php
if ( ! class_exists( 'acf_field_date_and_time_picker' ) ) :
class acf_field_date_and_time_picker extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'date_time_picker';
$this->label = __( 'Date Time Picker', 'acf' );
$this->category = 'advanced';
$this->description = __( 'An interactive UI for picking a date and time. The date return format can be customized using the field settings.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-date-time.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/date-time-picker/', 'docs', 'field-type-selection' );
$this->defaults = array(
'display_format' => 'd/m/Y g:i a',
'return_format' => 'd/m/Y g:i a',
'first_day' => 1,
);
}
/**
* description
*
* @type function
* @date 16/12/2015
* @since 5.3.2
*
* @param $post_id (int)
* @return $post_id (int)
*/
function input_admin_enqueue_scripts() {
// bail early if no enqueue
if ( ! acf_get_setting( 'enqueue_datetimepicker' ) ) {
return;
}
// vars
$version = '1.6.1';
// script
wp_enqueue_script( 'acf-timepicker', acf_get_url( 'assets/inc/timepicker/jquery-ui-timepicker-addon.min.js' ), array( 'jquery-ui-datepicker' ), $version );
// style
wp_enqueue_style( 'acf-timepicker', acf_get_url( 'assets/inc/timepicker/jquery-ui-timepicker-addon.min.css' ), '', $version );
// localize
acf_localize_data(
array(
'dateTimePickerL10n' => array(
'timeOnlyTitle' => _x( 'Choose Time', 'Date Time Picker JS timeOnlyTitle', 'acf' ),
'timeText' => _x( 'Time', 'Date Time Picker JS timeText', 'acf' ),
'hourText' => _x( 'Hour', 'Date Time Picker JS hourText', 'acf' ),
'minuteText' => _x( 'Minute', 'Date Time Picker JS minuteText', 'acf' ),
'secondText' => _x( 'Second', 'Date Time Picker JS secondText', 'acf' ),
'millisecText' => _x( 'Millisecond', 'Date Time Picker JS millisecText', 'acf' ),
'microsecText' => _x( 'Microsecond', 'Date Time Picker JS microsecText', 'acf' ),
'timezoneText' => _x( 'Time Zone', 'Date Time Picker JS timezoneText', 'acf' ),
'currentText' => _x( 'Now', 'Date Time Picker JS currentText', 'acf' ),
'closeText' => _x( 'Done', 'Date Time Picker JS closeText', 'acf' ),
'selectText' => _x( 'Select', 'Date Time Picker JS selectText', 'acf' ),
'amNames' => array(
_x( 'AM', 'Date Time Picker JS amText', 'acf' ),
_x( 'A', 'Date Time Picker JS amTextShort', 'acf' ),
),
'pmNames' => array(
_x( 'PM', 'Date Time Picker JS pmText', 'acf' ),
_x( 'P', 'Date Time Picker JS pmTextShort', 'acf' ),
),
),
)
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// Set value.
$hidden_value = '';
$display_value = '';
if ( $field['value'] ) {
$hidden_value = acf_format_date( $field['value'], 'Y-m-d H:i:s' );
$display_value = acf_format_date( $field['value'], $field['display_format'] );
}
// Convert "display_format" setting to individual date and time formats.
$formats = acf_split_date_time( $field['display_format'] );
// Elements.
$div = array(
'class' => 'acf-date-time-picker acf-input-wrap',
'data-date_format' => acf_convert_date_to_js( $formats['date'] ),
'data-time_format' => acf_convert_time_to_js( $formats['time'] ),
'data-first_day' => $field['first_day'],
);
$hidden_input = array(
'id' => $field['id'],
'class' => 'input-alt',
'name' => $field['name'],
'value' => $hidden_value,
);
$text_input = array(
'class' => $field['class'] . ' input',
'value' => $display_value,
);
foreach ( array( 'readonly', 'disabled' ) as $k ) {
if ( ! empty( $field[ $k ] ) ) {
$hidden_input[ $k ] = $k;
$text_input[ $k ] = $k;
}
}
// Output.
?>
<div <?php echo acf_esc_attrs( $div ); ?>>
<?php acf_hidden_input( $hidden_input ); ?>
<?php acf_text_input( $text_input ); ?>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
global $wp_locale;
$d_m_Y = date_i18n( 'd/m/Y g:i a' );
$m_d_Y = date_i18n( 'm/d/Y g:i a' );
$F_j_Y = date_i18n( 'F j, Y g:i a' );
$Ymd = date_i18n( 'Y-m-d H:i:s' );
echo '<div class="acf-field-settings-split">';
acf_render_field_setting(
$field,
array(
'label' => __( 'Display Format', 'acf' ),
'hint' => __( 'The format displayed when editing a post', 'acf' ),
'type' => 'radio',
'name' => 'display_format',
'other_choice' => 1,
'choices' => array(
'd/m/Y g:i a' => '<span>' . $d_m_Y . '</span><code>d/m/Y g:i a</code>',
'm/d/Y g:i a' => '<span>' . $m_d_Y . '</span><code>m/d/Y g:i a</code>',
'F j, Y g:i a' => '<span>' . $F_j_Y . '</span><code>F j, Y g:i a</code>',
'Y-m-d H:i:s' => '<span>' . $Ymd . '</span><code>Y-m-d H:i:s</code>',
'other' => '<span>' . __( 'Custom:', 'acf' ) . '</span>',
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'hint' => __( 'The format returned via template functions', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'other_choice' => 1,
'choices' => array(
'd/m/Y g:i a' => '<span>' . $d_m_Y . '</span><code>d/m/Y g:i a</code>',
'm/d/Y g:i a' => '<span>' . $m_d_Y . '</span><code>m/d/Y g:i a</code>',
'F j, Y g:i a' => '<span>' . $F_j_Y . '</span><code>F j, Y g:i a</code>',
'Y-m-d H:i:s' => '<span>' . $Ymd . '</span><code>Y-m-d H:i:s</code>',
'other' => '<span>' . __( 'Custom:', 'acf' ) . '</span>',
),
)
);
echo '</div>';
acf_render_field_setting(
$field,
array(
'label' => __( 'Week Starts On', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'first_day',
'choices' => array_values( $wp_locale->weekday ),
)
);
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
*
* @param mixed $value The value which was loaded from the database
* @param mixed $post_id The post_id from which the value was loaded
* @param array $field The field array holding all the field options
* @return mixed $value The modified value
*/
public function format_value( $value, $post_id, $field ) {
return acf_format_date( $value, $field['return_format'] );
}
/**
* This filter is applied to the $field after it is loaded from the database
* and ensures the return and display values are set.
*
* @type filter
* @since 5.11.0
*
* @param array $field The field array holding all the field options.
* @return array
*/
public function load_field( $field ) {
if ( empty( $field['display_format'] ) ) {
$field['display_format'] = $this->defaults['display_format'];
}
if ( empty( $field['return_format'] ) ) {
$field['return_format'] = $this->defaults['return_format'];
}
return $field;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
return array(
'type' => array( 'string', 'null' ),
'description' => 'A `Y-m-d H:i:s` formatted date string.',
'required' => ! empty( $field['required'] ),
);
}
}
// initialize
acf_register_field_type( 'acf_field_date_and_time_picker' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,189 @@
<?php
if ( ! class_exists( 'acf_field_email' ) ) :
class acf_field_email extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'email';
$this->label = __( 'Email', 'acf' );
$this->description = __( 'A text input specifically designed for storing email addresses.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-email.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/email/', 'docs', 'field-type-selection' );
$this->defaults = array(
'default_value' => '',
'placeholder' => '',
'prepend' => '',
'append' => '',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$atts = array();
$keys = array( 'type', 'id', 'class', 'name', 'value', 'placeholder', 'pattern' );
$keys2 = array( 'readonly', 'disabled', 'required', 'multiple' );
$html = '';
// prepend
if ( $field['prepend'] !== '' ) {
$field['class'] .= ' acf-is-prepended';
$html .= '<div class="acf-input-prepend">' . acf_esc_html( $field['prepend'] ) . '</div>';
}
// append
if ( $field['append'] !== '' ) {
$field['class'] .= ' acf-is-appended';
$html .= '<div class="acf-input-append">' . acf_esc_html( $field['append'] ) . '</div>';
}
// atts (value="123")
foreach ( $keys as $k ) {
if ( isset( $field[ $k ] ) ) {
$atts[ $k ] = $field[ $k ];
}
}
// atts2 (disabled="disabled")
foreach ( $keys2 as $k ) {
if ( ! empty( $field[ $k ] ) ) {
$atts[ $k ] = $k;
}
}
// remove empty atts
$atts = acf_clean_atts( $atts );
// render
$html .= '<div class="acf-input-wrap">' . acf_get_text_input( $atts ) . '</div>';
// return
echo $html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- safe HTML escaped by acf_get_text_input
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'text',
'name' => 'default_value',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Placeholder Text', 'acf' ),
'instructions' => __( 'Appears within the input', 'acf' ),
'type' => 'text',
'name' => 'placeholder',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Prepend', 'acf' ),
'instructions' => __( 'Appears before the input', 'acf' ),
'type' => 'text',
'name' => 'prepend',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Append', 'acf' ),
'instructions' => __( 'Appears after the input', 'acf' ),
'type' => 'text',
'name' => 'append',
)
);
}
/**
* Validate the email value. If this method returns TRUE, the input value is valid. If
* FALSE or a string is returned, the input value is invalid and the user is shown a
* notice. If a string is returned, the string is show as the message text.
*
* @param boolean $valid Whether the value is valid.
* @param mixed $value The field value.
* @param array $field The field array.
* @param string $input The request variable name for the inbound field.
* @return boolean|string
*/
public function validate_value( $valid, $value, $field, $input ) {
$flags = defined( 'FILTER_FLAG_EMAIL_UNICODE' ) ? FILTER_FLAG_EMAIL_UNICODE : 0;
if ( $value && filter_var( wp_unslash( $value ), FILTER_VALIDATE_EMAIL, $flags ) === false ) {
return sprintf( __( "'%s' is not a valid email address", 'acf' ), esc_html( $value ) );
}
return $valid;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = parent::get_rest_schema( $field );
$schema['format'] = 'email';
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_email' );
endif; // class_exists check

View File

@@ -0,0 +1,522 @@
<?php
if ( ! class_exists( 'acf_field_file' ) ) :
class acf_field_file extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'file';
$this->label = __( 'File', 'acf' );
$this->category = 'content';
$this->description = __( 'Uses the native WordPress media picker to upload, or choose files.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-file.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/file/', 'docs', 'field-type-selection' );
$this->defaults = array(
'return_format' => 'array',
'library' => 'all',
'min_size' => 0,
'max_size' => 0,
'mime_types' => '',
);
// filters
add_filter( 'get_media_item_args', array( $this, 'get_media_item_args' ) );
}
/**
* description
*
* @type function
* @date 16/12/2015
* @since 5.3.2
*
* @param $post_id (int)
* @return $post_id (int)
*/
function input_admin_enqueue_scripts() {
// localize
acf_localize_text(
array(
'Select File' => __( 'Select File', 'acf' ),
'Edit File' => __( 'Edit File', 'acf' ),
'Update File' => __( 'Update File', 'acf' ),
)
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$uploader = acf_get_setting( 'uploader' );
// allow custom uploader
$uploader = acf_maybe_get( $field, 'uploader', $uploader );
// enqueue
if ( $uploader == 'wp' ) {
acf_enqueue_uploader();
}
// vars
$o = array(
'icon' => '',
'title' => '',
'url' => '',
'filename' => '',
'filesize' => '',
);
$div = array(
'class' => 'acf-file-uploader',
'data-library' => $field['library'],
'data-mime_types' => $field['mime_types'],
'data-uploader' => $uploader,
);
// has value?
if ( $field['value'] ) {
$attachment = acf_get_attachment( $field['value'] );
if ( $attachment ) {
// has value
$div['class'] .= ' has-value';
// update
$o['icon'] = $attachment['icon'];
$o['title'] = $attachment['title'];
$o['url'] = $attachment['url'];
$o['filename'] = $attachment['filename'];
if ( $attachment['filesize'] ) {
$o['filesize'] = size_format( $attachment['filesize'] );
}
}
}
?>
<div <?php echo acf_esc_attrs( $div ); ?>>
<?php
acf_hidden_input(
array(
'name' => $field['name'],
'value' => $field['value'],
'data-name' => 'id',
)
);
?>
<div class="show-if-value file-wrap">
<div class="file-icon">
<img data-name="icon" src="<?php echo esc_url( $o['icon'] ); ?>" alt=""/>
</div>
<div class="file-info">
<p>
<strong data-name="title"><?php echo esc_html( $o['title'] ); ?></strong>
</p>
<p>
<strong><?php esc_html_e( 'File name', 'acf' ); ?>:</strong>
<a data-name="filename" href="<?php echo esc_url( $o['url'] ); ?>" target="_blank"><?php echo esc_html( $o['filename'] ); ?></a>
</p>
<p>
<strong><?php esc_html_e( 'File size', 'acf' ); ?>:</strong>
<span data-name="filesize"><?php echo esc_html( $o['filesize'] ); ?></span>
</p>
</div>
<div class="acf-actions -hover">
<?php if ( $uploader != 'basic' ) : ?>
<a class="acf-icon -pencil dark" data-name="edit" href="#" title="<?php esc_attr_e( 'Edit', 'acf' ); ?>"></a>
<?php endif; ?>
<a class="acf-icon -cancel dark" data-name="remove" href="#" title="<?php esc_attr_e( 'Remove', 'acf' ); ?>"></a>
</div>
</div>
<div class="hide-if-value">
<?php if ( $uploader == 'basic' ) : ?>
<?php if ( $field['value'] && ! is_numeric( $field['value'] ) ) : ?>
<div class="acf-error-message"><p><?php echo acf_esc_html( $field['value'] ); ?></p></div>
<?php endif; ?>
<label class="acf-basic-uploader">
<?php
acf_file_input(
array(
'name' => $field['name'],
'id' => $field['id'],
'key' => $field['key'],
)
);
?>
</label>
<?php else : ?>
<p><?php esc_html_e( 'No file selected', 'acf' ); ?> <a data-name="add" class="acf-button button" href="#"><?php esc_html_e( 'Add File', 'acf' ); ?></a></p>
<?php endif; ?>
</div>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Value', 'acf' ),
'instructions' => __( 'Specify the returned value on front end', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'layout' => 'horizontal',
'choices' => array(
'array' => __( 'File Array', 'acf' ),
'url' => __( 'File URL', 'acf' ),
'id' => __( 'File ID', 'acf' ),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Library', 'acf' ),
'instructions' => __( 'Limit the media library choice', 'acf' ),
'type' => 'radio',
'name' => 'library',
'layout' => 'horizontal',
'choices' => array(
'all' => __( 'All', 'acf' ),
'uploadedTo' => __( 'Uploaded to post', 'acf' ),
),
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
// Clear numeric settings.
$clear = array(
'min_size',
'max_size',
);
foreach ( $clear as $k ) {
if ( empty( $field[ $k ] ) ) {
$field[ $k ] = '';
}
}
acf_render_field_setting(
$field,
array(
'label' => __( 'Minimum', 'acf' ),
'instructions' => __( 'Restrict which files can be uploaded', 'acf' ),
'type' => 'text',
'name' => 'min_size',
'prepend' => __( 'File size', 'acf' ),
'append' => 'MB',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Maximum', 'acf' ),
'instructions' => __( 'Restrict which files can be uploaded', 'acf' ),
'type' => 'text',
'name' => 'max_size',
'prepend' => __( 'File size', 'acf' ),
'append' => 'MB',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Allowed File Types', 'acf' ),
'hint' => __( 'Comma separated list. Leave blank for all types', 'acf' ),
'type' => 'text',
'name' => 'mime_types',
)
);
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// bail early if no value
if ( empty( $value ) ) {
return false;
}
// bail early if not numeric (error message)
if ( ! is_numeric( $value ) ) {
return false;
}
// convert to int
$value = intval( $value );
// format
if ( $field['return_format'] == 'url' ) {
return wp_get_attachment_url( $value );
} elseif ( $field['return_format'] == 'array' ) {
return acf_get_attachment( $value );
}
// return
return $value;
}
/**
* description
*
* @type function
* @date 27/01/13
* @since 3.6.0
*
* @param $vars (array)
* @return $vars
*/
function get_media_item_args( $vars ) {
$vars['send'] = true;
return( $vars );
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value which will be saved in the database
* @param $post_id - the post_id of which the value will be saved
* @param $field - the field array holding all the field options
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
// Bail early if no value.
if ( empty( $value ) ) {
return $value;
}
// Parse value for id.
$attachment_id = acf_idval( $value );
// Connect attacment to post.
acf_connect_attachment_to_post( $attachment_id, $post_id );
// Return id.
return $attachment_id;
}
/**
* validate_value
*
* This function will validate a basic file input
*
* @type function
* @date 11/02/2014
* @since 5.0.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function validate_value( $valid, $value, $field, $input ) {
// bail early if empty
if ( empty( $value ) ) {
return $valid;
}
// bail early if is numeric
if ( is_numeric( $value ) ) {
return $valid;
}
// bail early if not basic string
if ( ! is_string( $value ) ) {
return $valid;
}
// decode value
$file = null;
parse_str( $value, $file );
// bail early if no attachment
if ( empty( $file ) ) {
return $valid;
}
// get errors
$errors = acf_validate_attachment( $file, $field, 'basic_upload' );
// append error
if ( ! empty( $errors ) ) {
$valid = implode( "\n", $errors );
}
// return
return $valid;
}
/**
* Validates file fields updated via the REST API.
*
* @param boolean $valid The current validity booleean
* @param integer $value The value of the field
* @param array $field The field array
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
if ( is_null( $value ) && empty( $field['required'] ) ) {
return $valid;
}
/**
* A bit of a hack, but we use `wp_prepare_attachment_for_js()` here
* since it returns all the data we need to validate the file, and we use this anyways
* to validate fields updated via UI.
*/
$attachment = wp_prepare_attachment_for_js( $value );
$param = sprintf( '%s[%s]', $field['prefix'], $field['name'] );
$data = array(
'param' => $param,
'value' => (int) $value,
);
if ( ! $attachment ) {
$error = sprintf( __( '%s requires a valid attachment ID.', 'acf' ), $param );
return new WP_Error( 'rest_invalid_param', $error, $data );
}
$errors = acf_validate_attachment( $attachment, $field, 'prepare' );
if ( ! empty( $errors ) ) {
$error = $param . ' - ' . implode( ' ', $errors );
return new WP_Error( 'rest_invalid_param', $error, $data );
}
return $valid;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'integer', 'null' ),
'required' => isset( $field['required'] ) && $field['required'],
);
if ( ! empty( $field['min_width'] ) ) {
$schema['minWidth'] = (int) $field['min_width'];
}
if ( ! empty( $field['min_height'] ) ) {
$schema['minHeight'] = (int) $field['min_height'];
}
if ( ! empty( $field['min_size'] ) ) {
$schema['minSize'] = $field['min_size'];
}
if ( ! empty( $field['max_width'] ) ) {
$schema['maxWidth'] = (int) $field['max_width'];
}
if ( ! empty( $field['max_height'] ) ) {
$schema['maxHeight'] = (int) $field['max_height'];
}
if ( ! empty( $field['max_size'] ) ) {
$schema['maxSize'] = $field['max_size'];
}
if ( ! empty( $field['mime_types'] ) ) {
$schema['mimeTypes'] = $field['mime_types'];
}
return $schema;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'acf_field_file' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,374 @@
<?php
if ( ! class_exists( 'acf_field_google_map' ) ) :
#[AllowDynamicProperties]
class acf_field_google_map extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'google_map';
$this->label = __( 'Google Map', 'acf' );
$this->category = 'advanced';
$this->description = __( 'An interactive UI for selecting a location using Google Maps. Requires a Google Maps API key and additional configuration to display correctly.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-google-map.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/google-map/', 'docs', 'field-type-selection' );
$this->defaults = array(
'height' => '',
'center_lat' => '',
'center_lng' => '',
'zoom' => '',
);
$this->default_values = array(
'height' => '400',
'center_lat' => '-37.81411',
'center_lng' => '144.96328',
'zoom' => '14',
);
}
/**
* description
*
* @type function
* @date 16/12/2015
* @since 5.3.2
*
* @param $post_id (int)
* @return $post_id (int)
*/
function input_admin_enqueue_scripts() {
// localize
acf_localize_text(
array(
'Sorry, this browser does not support geolocation' => __( 'Sorry, this browser does not support geolocation', 'acf' ),
)
);
// bail early if no enqueue
if ( ! acf_get_setting( 'enqueue_google_maps' ) ) {
return;
}
// vars
$api = array(
'key' => acf_get_setting( 'google_api_key' ),
'client' => acf_get_setting( 'google_api_client' ),
'libraries' => 'places',
'ver' => 3,
'callback' => 'Function.prototype',
'language' => acf_get_locale(),
);
// filter
$api = apply_filters( 'acf/fields/google_map/api', $api );
// remove empty
if ( empty( $api['key'] ) ) {
unset( $api['key'] );
}
if ( empty( $api['client'] ) ) {
unset( $api['client'] );
}
// construct url
$url = add_query_arg( $api, 'https://maps.googleapis.com/maps/api/js' );
// localize
acf_localize_data(
array(
'google_map_api' => $url,
)
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// Apply defaults.
foreach ( $this->default_values as $k => $v ) {
if ( ! $field[ $k ] ) {
$field[ $k ] = $v;
}
}
// Attrs.
$attrs = array(
'id' => $field['id'],
'class' => "acf-google-map {$field['class']}",
'data-lat' => $field['center_lat'],
'data-lng' => $field['center_lng'],
'data-zoom' => $field['zoom'],
);
$search = '';
if ( $field['value'] ) {
$attrs['class'] .= ' -value';
$search = $field['value']['address'];
} else {
$field['value'] = '';
}
?>
<div <?php echo acf_esc_attrs( $attrs ); ?>>
<?php
acf_hidden_input(
array(
'name' => $field['name'],
'value' => $field['value'],
)
);
?>
<div class="title">
<div class="acf-actions -hover">
<a href="#" data-name="search" class="acf-icon -search grey" title="<?php esc_attr_e( 'Search', 'acf' ); ?>"></a>
<a href="#" data-name="clear" class="acf-icon -cancel grey" title="<?php esc_attr_e( 'Clear location', 'acf' ); ?>"></a>
<a href="#" data-name="locate" class="acf-icon -location grey" title="<?php esc_attr_e( 'Find current location', 'acf' ); ?>"></a>
</div>
<input class="search" type="text" placeholder="<?php esc_attr_e( 'Search for address...', 'acf' ); ?>" value="<?php echo esc_attr( $search ); ?>" />
<i class="acf-loading"></i>
</div>
<div class="canvas" style="<?php echo esc_attr( 'height: ' . $field['height'] . 'px' ); ?>"></div>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
// center_lat
acf_render_field_setting(
$field,
array(
'label' => __( 'Center', 'acf' ),
'hint' => __( 'Center the initial map', 'acf' ),
'type' => 'text',
'name' => 'center_lat',
'prepend' => 'lat',
'placeholder' => $this->default_values['center_lat'],
)
);
// center_lng
acf_render_field_setting(
$field,
array(
'label' => __( 'Center', 'acf' ),
'hint' => __( 'Center the initial map', 'acf' ),
'type' => 'text',
'name' => 'center_lng',
'prepend' => 'lng',
'placeholder' => $this->default_values['center_lng'],
'_append' => 'center_lat',
)
);
// zoom
acf_render_field_setting(
$field,
array(
'label' => __( 'Zoom', 'acf' ),
'instructions' => __( 'Set the initial zoom level', 'acf' ),
'type' => 'text',
'name' => 'zoom',
'placeholder' => $this->default_values['zoom'],
)
);
// allow_null
acf_render_field_setting(
$field,
array(
'label' => __( 'Height', 'acf' ),
'instructions' => __( 'Customize the map height', 'acf' ),
'type' => 'text',
'name' => 'height',
'append' => 'px',
'placeholder' => $this->default_values['height'],
)
);
}
/**
* load_value
*
* Filters the value loaded from the database.
*
* @date 16/10/19
* @since 5.8.1
*
* @param mixed $value The value loaded from the database.
* @param mixed $post_id The post ID where the value is saved.
* @param array $field The field settings array.
* @return (array|false)
*/
function load_value( $value, $post_id, $field ) {
// Ensure value is an array.
if ( $value ) {
return wp_parse_args(
$value,
array(
'address' => '',
'lat' => 0,
'lng' => 0,
)
);
}
// Return default.
return false;
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value which will be saved in the database
* @param $post_id - the post_id of which the value will be saved
* @param $field - the field array holding all the field options
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
// decode JSON string.
if ( is_string( $value ) ) {
$value = json_decode( wp_unslash( $value ), true );
}
// Ensure value is an array.
if ( $value ) {
return (array) $value;
}
// Return default.
return false;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
return array(
'type' => array( 'object', 'null' ),
'required' => ! empty( $field['required'] ),
'properties' => array(
'address' => array(
'type' => 'string',
),
'lat' => array(
'type' => array( 'string', 'float' ),
),
'lng' => array(
'type' => array( 'string', 'float' ),
),
'zoom' => array(
'type' => array( 'string', 'int' ),
),
'place_id' => array(
'type' => 'string',
),
'name' => array(
'type' => 'string',
),
'street_number' => array(
'type' => array( 'string', 'int' ),
),
'street_name' => array(
'type' => 'string',
),
'street_name_short' => array(
'type' => 'string',
),
'city' => array(
'type' => 'string',
),
'state' => array(
'type' => 'string',
),
'state_short' => array(
'type' => 'string',
),
'post_code' => array(
'type' => array( 'string', 'int' ),
),
'country' => array(
'type' => 'string',
),
'country_short' => array(
'type' => 'string',
),
),
);
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
if ( ! $value ) {
return null;
}
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'acf_field_google_map' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,649 @@
<?php
if ( ! class_exists( 'acf_field__group' ) ) :
#[AllowDynamicProperties]
class acf_field__group extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'group';
$this->label = __( 'Group', 'acf' );
$this->category = 'layout';
$this->description = __( 'Provides a way to structure fields into groups to better organize the data and the edit screen.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-group.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/group/', 'docs', 'field-type-selection' );
$this->supports = array(
'bindings' => false,
);
$this->defaults = array(
'sub_fields' => array(),
'layout' => 'block',
);
$this->have_rows = 'single';
// field filters
$this->add_field_filter( 'acf/prepare_field_for_export', array( $this, 'prepare_field_for_export' ) );
$this->add_field_filter( 'acf/prepare_field_for_import', array( $this, 'prepare_field_for_import' ) );
}
/**
* This filter is appied to the $field after it is loaded from the database
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $field - the field array holding all the field options
*
* @return $field - the field array holding all the field options
*/
function load_field( $field ) {
// vars
$sub_fields = acf_get_fields( $field );
// append
if ( $sub_fields ) {
$field['sub_fields'] = $sub_fields;
}
// return
return $field;
}
/**
* This filter is applied to the $value after it is loaded from the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value found in the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
* @return $value
*/
function load_value( $value, $post_id, $field ) {
// bail early if no sub fields
if ( empty( $field['sub_fields'] ) ) {
return $value;
}
// modify names
$field = $this->prepare_field_for_db( $field );
// load sub fields
$value = array();
// loop
foreach ( $field['sub_fields'] as $sub_field ) {
// load
$value[ $sub_field['key'] ] = acf_get_value( $post_id, $sub_field );
}
// return
return $value;
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
*
* @param mixed $value The value which was loaded from the database.
* @param mixed $post_id The $post_id from which the value was loaded.
* @param array $field The field array holding all the field options.
* @param boolean $escape_html Should the field return a HTML safe formatted value.
* @return mixed the modified value
*/
public function format_value( $value, $post_id, $field, $escape_html = false ) {
// bail early if no value
if ( empty( $value ) ) {
return false;
}
// modify names
$field = $this->prepare_field_for_db( $field );
// loop
foreach ( $field['sub_fields'] as $sub_field ) {
// extract value
$sub_value = acf_extract_var( $value, $sub_field['key'] );
// format value
$sub_value = acf_format_value( $sub_value, $post_id, $sub_field, $escape_html );
// append to $row
$value[ $sub_field['_name'] ] = $sub_value;
}
// return
return $value;
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value which will be saved in the database
* @param $field - the field array holding all the field options
* @param $post_id - the post_id of which the value will be saved
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
// bail early if no value
if ( ! acf_is_array( $value ) ) {
return null;
}
// bail early if no sub fields
if ( empty( $field['sub_fields'] ) ) {
return null;
}
// modify names
$field = $this->prepare_field_for_db( $field );
// loop
foreach ( $field['sub_fields'] as $sub_field ) {
// vars
$v = false;
// key (backend)
if ( isset( $value[ $sub_field['key'] ] ) ) {
$v = $value[ $sub_field['key'] ];
// name (frontend)
} elseif ( isset( $value[ $sub_field['_name'] ] ) ) {
$v = $value[ $sub_field['_name'] ];
// empty
} else {
// input is not set (hidden by conditioanl logic)
continue;
}
// update value
acf_update_value( $v, $post_id, $sub_field );
}
// return
return '';
}
/**
* This function will modify sub fields ready for update / load
*
* @type function
* @date 4/11/16
* @since 5.5.0
*
* @param $field (array)
* @return $field
*/
function prepare_field_for_db( $field ) {
// bail early if no sub fields
if ( empty( $field['sub_fields'] ) ) {
return $field;
}
// loop
foreach ( $field['sub_fields'] as &$sub_field ) {
// prefix name
$sub_field['name'] = $field['name'] . '_' . $sub_field['_name'];
}
// return
return $field;
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// bail early if no sub fields
if ( empty( $field['sub_fields'] ) ) {
return;
}
// load values
foreach ( $field['sub_fields'] as &$sub_field ) {
// add value
if ( isset( $field['value'][ $sub_field['key'] ] ) ) {
// this is a normal value
$sub_field['value'] = $field['value'][ $sub_field['key'] ];
} elseif ( isset( $sub_field['default_value'] ) ) {
// no value, but this sub field has a default value
$sub_field['value'] = $sub_field['default_value'];
}
// update prefix to allow for nested values
$sub_field['prefix'] = $field['name'];
// restore required
if ( $field['required'] ) {
$sub_field['required'] = 0;
}
}
// render
if ( $field['layout'] == 'table' ) {
$this->render_field_table( $field );
} else {
$this->render_field_block( $field );
}
}
/**
* description
*
* @type function
* @date 12/07/2016
* @since 5.4.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function render_field_block( $field ) {
// vars
$label_placement = ( $field['layout'] == 'block' ) ? 'top' : 'left';
// html
echo '<div class="acf-fields -' . esc_attr( $label_placement ) . ' -border">';
foreach ( $field['sub_fields'] as $sub_field ) {
acf_render_field_wrap( $sub_field );
}
echo '</div>';
}
/**
* description
*
* @type function
* @date 12/07/2016
* @since 5.4.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function render_field_table( $field ) {
?>
<table class="acf-table">
<thead>
<tr>
<?php
foreach ( $field['sub_fields'] as $sub_field ) :
// prepare field (allow sub fields to be removed)
$sub_field = acf_prepare_field( $sub_field );
// bail early if no field
if ( ! $sub_field ) {
continue;
}
// vars
$atts = array();
$atts['class'] = 'acf-th';
$atts['data-name'] = $sub_field['_name'];
$atts['data-type'] = $sub_field['type'];
$atts['data-key'] = $sub_field['key'];
// Add custom width
if ( $sub_field['wrapper']['width'] ) {
$atts['data-width'] = $sub_field['wrapper']['width'];
$atts['style'] = 'width: ' . $sub_field['wrapper']['width'] . '%;';
}
?>
<th <?php echo acf_esc_attrs( $atts ); ?>>
<?php acf_render_field_label( $sub_field ); ?>
<?php acf_render_field_instructions( $sub_field ); ?>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<tr class="acf-row">
<?php
foreach ( $field['sub_fields'] as $sub_field ) {
acf_render_field_wrap( $sub_field, 'td' );
}
?>
</tr>
</tbody>
</table>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
// vars
$args = array(
'fields' => $field['sub_fields'],
'parent' => $field['ID'],
'is_subfield' => true,
);
?>
<div class="acf-field acf-field-setting-sub_fields" data-setting="group" data-name="sub_fields">
<div class="acf-label">
<label><?php esc_html_e( 'Sub Fields', 'acf' ); ?></label>
</div>
<div class="acf-input acf-input-sub">
<?php
acf_get_view( 'acf-field-group/fields', $args );
?>
</div>
</div>
<?php
// layout
acf_render_field_setting(
$field,
array(
'label' => __( 'Layout', 'acf' ),
'instructions' => __( 'Specify the style used to render the selected fields', 'acf' ),
'type' => 'radio',
'name' => 'layout',
'layout' => 'horizontal',
'choices' => array(
'block' => __( 'Block', 'acf' ),
'table' => __( 'Table', 'acf' ),
'row' => __( 'Row', 'acf' ),
),
)
);
}
/**
* description
*
* @type function
* @date 11/02/2014
* @since 5.0.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function validate_value( $valid, $value, $field, $input ) {
// bail early if no $value
if ( empty( $value ) ) {
return $valid;
}
// bail early if no sub fields
if ( empty( $field['sub_fields'] ) ) {
return $valid;
}
// loop
foreach ( $field['sub_fields'] as $sub_field ) {
// get sub field
$k = $sub_field['key'];
// bail early if value not set (conditional logic?)
if ( ! isset( $value[ $k ] ) ) {
continue;
}
// required
if ( $field['required'] ) {
$sub_field['required'] = 1;
}
// validate
acf_validate_value( $value[ $k ], $sub_field, "{$input}[{$k}]" );
}
// return
return $valid;
}
/**
* This filter is appied to the $field before it is duplicated and saved to the database
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $field - the field array holding all the field options
*
* @return $field - the modified field
*/
function duplicate_field( $field ) {
// get sub fields
$sub_fields = acf_extract_var( $field, 'sub_fields' );
// save field to get ID
$field = acf_update_field( $field );
// duplicate sub fields
acf_duplicate_fields( $sub_fields, $field['ID'] );
// return
return $field;
}
/**
* prepare_field_for_export
*
* Prepares the field for export.
*
* @date 11/03/2014
* @since 5.0.0
*
* @param array $field The field settings.
* @return array
*/
function prepare_field_for_export( $field ) {
// Check for sub fields.
if ( ! empty( $field['sub_fields'] ) ) {
$field['sub_fields'] = acf_prepare_fields_for_export( $field['sub_fields'] );
}
return $field;
}
/**
* prepare_field_for_import
*
* Returns a flat array of fields containing all sub fields ready for import.
*
* @date 11/03/2014
* @since 5.0.0
*
* @param array $field The field settings.
* @return array
*/
function prepare_field_for_import( $field ) {
// Check for sub fields.
if ( ! empty( $field['sub_fields'] ) ) {
$sub_fields = acf_extract_var( $field, 'sub_fields' );
// Modify sub fields.
foreach ( $sub_fields as $i => $sub_field ) {
$sub_fields[ $i ]['parent'] = $field['key'];
$sub_fields[ $i ]['menu_order'] = $i;
}
// Return array of [field, sub_1, sub_2, ...].
return array_merge( array( $field ), $sub_fields );
}
return $field;
}
/**
* Called when deleting this field's value.
*
* @date 1/07/2015
* @since 5.2.3
*
* @param mixed $post_id The post ID being saved
* @param string $meta_key The field name as seen by the DB
* @param array $field The field settings
* @return void
*/
function delete_value( $post_id, $meta_key, $field ) {
// bail early if no sub fields
if ( empty( $field['sub_fields'] ) ) {
return null;
}
// modify names
$field = $this->prepare_field_for_db( $field );
// loop
foreach ( $field['sub_fields'] as $sub_field ) {
acf_delete_value( $post_id, $sub_field );
}
}
/**
* delete_field
*
* Called when deleting a field of this type.
*
* @date 8/11/18
* @since 5.8.0
*
* @param arra $field The field settings.
* @return void
*/
function delete_field( $field ) {
// loop over sub fields and delete them
if ( $field['sub_fields'] ) {
foreach ( $field['sub_fields'] as $sub_field ) {
acf_delete_field( $sub_field['ID'] );
}
}
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'object', 'null' ),
'properties' => array(),
'required' => ! empty( $field['required'] ),
);
foreach ( $field['sub_fields'] as $sub_field ) {
if ( $sub_field_schema = acf_get_field_rest_schema( $sub_field ) ) {
$schema['properties'][ $sub_field['name'] ] = $sub_field_schema;
}
}
return $schema;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param integer|string $post_id
* @param array $field
* @return array|mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
if ( empty( $value ) || ! is_array( $value ) || empty( $field['sub_fields'] ) ) {
return $value;
}
// Loop through each row and within that, each sub field to process sub fields individually.
foreach ( $field['sub_fields'] as $sub_field ) {
// Extract the sub field 'field_key'=>'value' pair from the $value and format it.
$sub_value = acf_extract_var( $value, $sub_field['key'] );
$sub_value = acf_format_value_for_rest( $sub_value, $post_id, $sub_field );
// Add the sub field value back to the $value but mapped to the field name instead
// of the key reference.
$value[ $sub_field['name'] ] = $sub_value;
}
return $value;
}
}
// initialize
acf_register_field_type( 'acf_field__group' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,823 @@
<?php
/**
* This is a PHP file containing the code for the acf_field_icon_picker class.
*
* @package Advanced_Custom_Fields_Pro
*/
if ( ! class_exists( 'acf_field_icon_picker' ) ) :
/**
* Class acf_field_icon_picker.
*/
class acf_field_icon_picker extends acf_field {
/**
* Initialize icon picker field
*
* @since 6.3
*
* @return void
*/
public function initialize() {
$this->name = 'icon_picker';
$this->label = __( 'Icon Picker', 'acf' );
$this->public = true;
$this->category = 'advanced';
$this->description = __( 'An interactive UI for selecting an icon. Select from Dashicons, the media library, or a standalone URL input.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-icon-picker.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/icon-picker/', 'docs', 'field-type-selection' );
$this->defaults = array(
'library' => 'all',
'tabs' => array_keys( $this->get_tabs() ),
'return_format' => 'string',
'default_value' => array(
'type' => null,
'value' => null,
),
);
}
/**
* Gets the available tabs for the icon picker field.
*
* @since 6.3
*
* @return array
*/
public function get_tabs() {
$tabs = array(
'dashicons' => esc_html__( 'Dashicons', 'acf' ),
);
if ( current_user_can( 'upload_files' ) ) {
$tabs['media_library'] = esc_html__( 'Media Library', 'acf' );
}
$tabs['url'] = esc_html__( 'URL', 'acf' );
/**
* Allows filtering the tabs used by the icon picker.
*
* @since 6.3
*
* @param array $tabs An associative array of tabs in key => label format.
* @return array
*/
return apply_filters( 'acf/fields/icon_picker/tabs', $tabs );
}
/**
* Renders an icon list tab (i.e. dashicons, custom icons).
*
* @since 6.4
*
* @param string $tab_name The name of the tab being rendered.
* @return void
*/
public function render_icon_list_tab( $tab_name ) {
?>
<div class="acf-icon-list-search-wrap">
<?php
acf_text_input(
array(
'class' => 'acf-icon-list-search-input',
'placeholder' => esc_html__( 'Search icons...', 'acf' ),
'type' => 'search',
)
);
?>
</div>
<div class="acf-icon-list" role="radiogroup" data-parent-tab="<?php echo esc_attr( $tab_name ); ?>"></div>
<div class="acf-icon-list-empty">
<img src="<?php echo esc_url( acf_get_url( 'assets/images/face-sad.svg' ) ); ?>" />
<p class="acf-no-results-text">
<?php
printf(
/* translators: %s: The invalid search term */
esc_html__( "No search results for '%s'", 'acf' ),
'<span class="acf-invalid-icon-list-search-term"></span>'
);
?>
</p>
</div>
<?php
}
/**
* Renders icon picker field
*
* @since 6.3
*
* @param object $field The ACF Field
* @return void
*/
public function render_field( $field ) {
$uploader = acf_get_setting( 'uploader' );
// Enqueue uploader scripts
if ( $uploader === 'wp' ) {
acf_enqueue_uploader();
}
$div = array(
'id' => $field['id'],
'class' => $field['class'] . ' acf-icon-picker',
);
echo '<div ' . acf_esc_attrs( $div ) . '>';
acf_hidden_input(
array(
'name' => $field['name'] . '[type]',
'value' => $field['value']['type'],
'data-hidden-type' => 'type',
)
);
acf_hidden_input(
array(
'name' => $field['name'] . '[value]',
'value' => $field['value']['value'],
'data-hidden-type' => 'value',
)
);
if ( ! is_array( $field['tabs'] ) ) {
$field['tabs'] = array();
}
$tabs = $this->get_tabs();
$shown = array_filter(
$tabs,
function ( $tab ) use ( $field ) {
return in_array( $tab, $field['tabs'], true );
},
ARRAY_FILTER_USE_KEY
);
foreach ( $shown as $name => $label ) {
if ( count( $shown ) > 1 ) {
acf_render_field_wrap(
array(
'type' => 'tab',
'label' => $label,
'key' => 'acf_icon_picker_tabs',
'selected' => $name === $field['value']['type'],
'unique_tab_key' => $name,
)
);
}
$wrapper_class = str_replace( '_', '-', $name );
echo '<div class="acf-icon-picker-tabs acf-icon-picker-' . esc_attr( $wrapper_class ) . '-tabs" data-tab="' . esc_attr( $name ) . '">';
switch ( $name ) {
case 'dashicons':
$this->render_icon_list_tab( $name );
break;
case 'media_library':
?>
<div class="acf-icon-picker-tab" data-category="<?php echo esc_attr( $name ); ?>">
<div class="acf-icon-picker-media-library">
<?php
$button_style = 'display: none;';
if ( in_array( $field['value']['type'], array( 'media_library', 'dashicons' ), true ) && ! empty( $field['value']['value'] ) ) {
$button_style = '';
}
?>
<button
aria-label="<?php esc_attr_e( 'Click to change the icon in the Media Library', 'acf' ); ?>"
class="acf-icon-picker-media-library-preview"
style="<?php echo esc_attr( $button_style ); ?>"
>
<div class="acf-icon-picker-media-library-preview-img" style="<?php echo esc_attr( 'media_library' !== $field['value']['type'] ? 'display: none;' : '' ); ?>">
<?php
$img_url = wp_get_attachment_image_url( $field['value']['value'], 'thumbnail' );
// If the type is media_library, then we need to show the media library preview.
?>
<img src="<?php echo esc_url( $img_url ); ?>" alt="<?php esc_attr_e( 'The currently selected image preview', 'acf' ); ?>" />
</div>
<div class="acf-icon-picker-media-library-preview-dashicon" style="<?php echo esc_attr( 'dashicons' !== $field['value']['type'] ? 'display: none;' : '' ); ?>">
<div class="dashicons <?php echo esc_attr( $field['value']['value'] ); ?>">
</div>
</div>
</button>
<button class="acf-icon-picker-media-library-button">
<div class="acf-icon-picker-media-library-button-icon dashicons dashicons-admin-media"></div>
<span><?php esc_html_e( 'Browse Media Library', 'acf' ); ?></span>
</button>
</div>
</div>
<?php
break;
case 'url':
echo '<div class="acf-icon-picker-url">';
acf_text_input(
array(
'class' => 'acf-icon_url',
'value' => $field['value']['type'] === 'url' ? $field['value']['value'] : '',
)
);
// Helper Text
?>
<p class="description"><?php esc_html_e( 'The URL to the icon you\'d like to use, or svg as Data URI', 'acf' ); ?></p>
<?php
echo '</div>';
break;
default:
do_action( 'acf/fields/icon_picker/tab/' . $name, $field );
$custom_icons = apply_filters( 'acf/fields/icon_picker/' . $name . '/icons', array(), $field );
if ( is_array( $custom_icons ) && ! empty( $custom_icons ) ) {
$this->render_icon_list_tab( $name, $custom_icons );
acf_localize_data(
array(
'iconPickerIcons_' . $name => $custom_icons,
)
);
}
}
echo '</div>';
}
echo '</div>';
}
/**
* Renders field settings for the icon picker field.
*
* @since 6.3
*
* @param array $field The icon picker field object.
* @return void
*/
public function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Tabs', 'acf' ),
'instructions' => __( 'Select where content editors can choose the icon from.', 'acf' ),
'type' => 'checkbox',
'name' => 'tabs',
'choices' => $this->get_tabs(),
)
);
$return_format_doc = sprintf(
'<a href="%s" target="_blank">%s</a>',
acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/icon-picker/', 'docs', 'icon-picker-return-format' ),
__( 'Learn More', 'acf' )
);
$return_format_instructions = sprintf(
/* translators: %s - link to documentation */
__( 'Specify the return format for the icon. %s', 'acf' ),
$return_format_doc
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'instructions' => $return_format_instructions,
'type' => 'radio',
'name' => 'return_format',
'choices' => array(
'string' => __( 'String', 'acf' ),
'array' => __( 'Array', 'acf' ),
),
'layout' => 'horizontal',
)
);
}
/**
* Localizes text for Icon Picker
*
* @since 6.3
*
* @return void
*/
public function input_admin_enqueue_scripts() {
acf_localize_data(
array(
'iconPickerA11yStrings' => array(
'noResultsForSearchTerm' => esc_html__( 'No results found for that search term', 'acf' ),
'newResultsFoundForSearchTerm' => esc_html__( 'The available icons matching your search query have been updated in the icon picker below.', 'acf' ),
),
'iconPickeri10n' => $this->get_dashicons(),
)
);
}
/**
* Validates the field value before it is saved into the database.
*
* @since 6.3
*
* @param integer $valid The current validation status.
* @param mixed $value The value of the field.
* @param array $field The field array holding all the field options.
* @param string $input The corresponding input name for $_POST value.
* @return boolean true If the value is valid, false if not.
*/
public function validate_value( $valid, $value, $field, $input ) {
// If the value is empty, return true. You're allowed to save nothing.
if ( empty( $value ) && empty( $field['required'] ) ) {
return true;
}
// If the value is not an array, return $valid status.
if ( ! is_array( $value ) ) {
return $valid;
}
// If the value is an array, but the type is not set, fail validation.
if ( ! isset( $value['type'] ) ) {
return __( 'Icon picker requires an icon type.', 'acf' );
}
// If the value is an array, but the value is not set, fail validation.
if ( ! isset( $value['value'] ) ) {
return __( 'Icon picker requires a value.', 'acf' );
}
return true;
}
/**
* format_value()
*
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @since 6.3
*
* @param mixed $value The value which was loaded from the database.
* @param integer $post_id The $post_id from which the value was loaded.
* @param array $field The field array holding all the field options.
* @return mixed $value The modified value.
*/
public function format_value( $value, $post_id, $field ) {
// Handle empty values.
if ( empty( $value ) ) {
// Return the default value if there is one.
if ( isset( $field['default_value'] ) ) {
return $field['default_value'];
} else {
// Otherwise return false.
return false;
}
}
// If media_library, behave the same as an image field.
if ( $value['type'] === 'media_library' ) {
// convert to int
$value['value'] = intval( $value['value'] );
// format
if ( $field['return_format'] === 'string' ) {
return wp_get_attachment_url( $value['value'] );
} elseif ( $field['return_format'] === 'array' ) {
$value['value'] = acf_get_attachment( $value['value'] );
return $value;
}
}
// If the desired return format is a string
if ( $field['return_format'] === 'string' ) {
return $value['value'];
}
// If nothing specific matched the return format, just return the value.
return $value;
}
/**
* get_dashicons()
*
* This function will return an array of dashicons.
*
* @since 6.3
*
* @return array $dashicons an array of dashicons.
*/
public function get_dashicons() {
$dashicons = array(
'dashicons-admin-appearance' => esc_html__( 'Appearance Icon', 'acf' ),
'dashicons-admin-collapse' => esc_html__( 'Collapse Icon', 'acf' ),
'dashicons-admin-comments' => esc_html__( 'Comments Icon', 'acf' ),
'dashicons-admin-customizer' => esc_html__( 'Customizer Icon', 'acf' ),
'dashicons-admin-generic' => esc_html__( 'Generic Icon', 'acf' ),
'dashicons-admin-home' => esc_html__( 'Home Icon', 'acf' ),
'dashicons-admin-links' => esc_html__( 'Links Icon', 'acf' ),
'dashicons-admin-media' => esc_html__( 'Media Icon', 'acf' ),
'dashicons-admin-multisite' => esc_html__( 'Multisite Icon', 'acf' ),
'dashicons-admin-network' => esc_html__( 'Network Icon', 'acf' ),
'dashicons-admin-page' => esc_html__( 'Page Icon', 'acf' ),
'dashicons-admin-plugins' => esc_html__( 'Plugins Icon', 'acf' ),
'dashicons-admin-post' => esc_html__( 'Post Icon', 'acf' ),
'dashicons-admin-settings' => esc_html__( 'Settings Icon', 'acf' ),
'dashicons-admin-site' => esc_html__( 'Site Icon', 'acf' ),
'dashicons-admin-site-alt' => esc_html__( 'Site (alt) Icon', 'acf' ),
'dashicons-admin-site-alt2' => esc_html__( 'Site (alt2) Icon', 'acf' ),
'dashicons-admin-site-alt3' => esc_html__( 'Site (alt3) Icon', 'acf' ),
'dashicons-admin-tools' => esc_html__( 'Tools Icon', 'acf' ),
'dashicons-admin-users' => esc_html__( 'Users Icon', 'acf' ),
'dashicons-airplane' => esc_html__( 'Airplane Icon', 'acf' ),
'dashicons-album' => esc_html__( 'Album Icon', 'acf' ),
'dashicons-align-center' => esc_html__( 'Align Center Icon', 'acf' ),
'dashicons-align-full-width' => esc_html__( 'Align Full Width Icon', 'acf' ),
'dashicons-align-left' => esc_html__( 'Align Left Icon', 'acf' ),
'dashicons-align-none' => esc_html__( 'Align None Icon', 'acf' ),
'dashicons-align-pull-left' => esc_html__( 'Align Pull Left Icon', 'acf' ),
'dashicons-align-pull-right' => esc_html__( 'Align Pull Right Icon', 'acf' ),
'dashicons-align-right' => esc_html__( 'Align Right Icon', 'acf' ),
'dashicons-align-wide' => esc_html__( 'Align Wide Icon', 'acf' ),
'dashicons-amazon' => esc_html__( 'Amazon Icon', 'acf' ),
'dashicons-analytics' => esc_html__( 'Analytics Icon', 'acf' ),
'dashicons-archive' => esc_html__( 'Archive Icon', 'acf' ),
'dashicons-arrow-down' => esc_html__( 'Arrow Down Icon', 'acf' ),
'dashicons-arrow-down-alt' => esc_html__( 'Arrow Down (alt) Icon', 'acf' ),
'dashicons-arrow-down-alt2' => esc_html__( 'Arrow Down (alt2) Icon', 'acf' ),
'dashicons-arrow-left' => esc_html__( 'Arrow Left Icon', 'acf' ),
'dashicons-arrow-left-alt' => esc_html__( 'Arrow Left (alt) Icon', 'acf' ),
'dashicons-arrow-left-alt2' => esc_html__( 'Arrow Left (alt2) Icon', 'acf' ),
'dashicons-arrow-right' => esc_html__( 'Arrow Right Icon', 'acf' ),
'dashicons-arrow-right-alt' => esc_html__( 'Arrow Right (alt) Icon', 'acf' ),
'dashicons-arrow-right-alt2' => esc_html__( 'Arrow Right (alt2) Icon', 'acf' ),
'dashicons-arrow-up' => esc_html__( 'Arrow Up Icon', 'acf' ),
'dashicons-arrow-up-alt' => esc_html__( 'Arrow Up (alt) Icon', 'acf' ),
'dashicons-arrow-up-alt2' => esc_html__( 'Arrow Up (alt2) Icon', 'acf' ),
'dashicons-art' => esc_html__( 'Art Icon', 'acf' ),
'dashicons-awards' => esc_html__( 'Awards Icon', 'acf' ),
'dashicons-backup' => esc_html__( 'Backup Icon', 'acf' ),
'dashicons-bank' => esc_html__( 'Bank Icon', 'acf' ),
'dashicons-beer' => esc_html__( 'Beer Icon', 'acf' ),
'dashicons-bell' => esc_html__( 'Bell Icon', 'acf' ),
'dashicons-block-default' => esc_html__( 'Block Default Icon', 'acf' ),
'dashicons-book' => esc_html__( 'Book Icon', 'acf' ),
'dashicons-book-alt' => esc_html__( 'Book (alt) Icon', 'acf' ),
'dashicons-buddicons-activity' => esc_html__( 'Activity Icon', 'acf' ),
'dashicons-buddicons-bbpress-logo' => esc_html__( 'bbPress Icon', 'acf' ),
'dashicons-buddicons-buddypress-logo' => esc_html__( 'BuddyPress Icon', 'acf' ),
'dashicons-buddicons-community' => esc_html__( 'Community Icon', 'acf' ),
'dashicons-buddicons-forums' => esc_html__( 'Forums Icon', 'acf' ),
'dashicons-buddicons-friends' => esc_html__( 'Friends Icon', 'acf' ),
'dashicons-buddicons-groups' => esc_html__( 'Groups Icon', 'acf' ),
'dashicons-buddicons-pm' => esc_html__( 'PM Icon', 'acf' ),
'dashicons-buddicons-replies' => esc_html__( 'Replies Icon', 'acf' ),
'dashicons-buddicons-topics' => esc_html__( 'Topics Icon', 'acf' ),
'dashicons-buddicons-tracking' => esc_html__( 'Tracking Icon', 'acf' ),
'dashicons-building' => esc_html__( 'Building Icon', 'acf' ),
'dashicons-businessman' => esc_html__( 'Businessman Icon', 'acf' ),
'dashicons-businessperson' => esc_html__( 'Businessperson Icon', 'acf' ),
'dashicons-businesswoman' => esc_html__( 'Businesswoman Icon', 'acf' ),
'dashicons-button' => esc_html__( 'Button Icon', 'acf' ),
'dashicons-calculator' => esc_html__( 'Calculator Icon', 'acf' ),
'dashicons-calendar' => esc_html__( 'Calendar Icon', 'acf' ),
'dashicons-calendar-alt' => esc_html__( 'Calendar (alt) Icon', 'acf' ),
'dashicons-camera' => esc_html__( 'Camera Icon', 'acf' ),
'dashicons-camera-alt' => esc_html__( 'Camera (alt) Icon', 'acf' ),
'dashicons-car' => esc_html__( 'Car Icon', 'acf' ),
'dashicons-carrot' => esc_html__( 'Carrot Icon', 'acf' ),
'dashicons-cart' => esc_html__( 'Cart Icon', 'acf' ),
'dashicons-category' => esc_html__( 'Category Icon', 'acf' ),
'dashicons-chart-area' => esc_html__( 'Chart Area Icon', 'acf' ),
'dashicons-chart-bar' => esc_html__( 'Chart Bar Icon', 'acf' ),
'dashicons-chart-line' => esc_html__( 'Chart Line Icon', 'acf' ),
'dashicons-chart-pie' => esc_html__( 'Chart Pie Icon', 'acf' ),
'dashicons-clipboard' => esc_html__( 'Clipboard Icon', 'acf' ),
'dashicons-clock' => esc_html__( 'Clock Icon', 'acf' ),
'dashicons-cloud' => esc_html__( 'Cloud Icon', 'acf' ),
'dashicons-cloud-saved' => esc_html__( 'Cloud Saved Icon', 'acf' ),
'dashicons-cloud-upload' => esc_html__( 'Cloud Upload Icon', 'acf' ),
'dashicons-code-standards' => esc_html__( 'Code Standards Icon', 'acf' ),
'dashicons-coffee' => esc_html__( 'Coffee Icon', 'acf' ),
'dashicons-color-picker' => esc_html__( 'Color Picker Icon', 'acf' ),
'dashicons-columns' => esc_html__( 'Columns Icon', 'acf' ),
'dashicons-controls-back' => esc_html__( 'Back Icon', 'acf' ),
'dashicons-controls-forward' => esc_html__( 'Forward Icon', 'acf' ),
'dashicons-controls-pause' => esc_html__( 'Pause Icon', 'acf' ),
'dashicons-controls-play' => esc_html__( 'Play Icon', 'acf' ),
'dashicons-controls-repeat' => esc_html__( 'Repeat Icon', 'acf' ),
'dashicons-controls-skipback' => esc_html__( 'Skip Back Icon', 'acf' ),
'dashicons-controls-skipforward' => esc_html__( 'Skip Forward Icon', 'acf' ),
'dashicons-controls-volumeoff' => esc_html__( 'Volume Off Icon', 'acf' ),
'dashicons-controls-volumeon' => esc_html__( 'Volume On Icon', 'acf' ),
'dashicons-cover-image' => esc_html__( 'Cover Image Icon', 'acf' ),
'dashicons-dashboard' => esc_html__( 'Dashboard Icon', 'acf' ),
'dashicons-database' => esc_html__( 'Database Icon', 'acf' ),
'dashicons-database-add' => esc_html__( 'Database Add Icon', 'acf' ),
'dashicons-database-export' => esc_html__( 'Database Export Icon', 'acf' ),
'dashicons-database-import' => esc_html__( 'Database Import Icon', 'acf' ),
'dashicons-database-remove' => esc_html__( 'Database Remove Icon', 'acf' ),
'dashicons-database-view' => esc_html__( 'Database View Icon', 'acf' ),
'dashicons-desktop' => esc_html__( 'Desktop Icon', 'acf' ),
'dashicons-dismiss' => esc_html__( 'Dismiss Icon', 'acf' ),
'dashicons-download' => esc_html__( 'Download Icon', 'acf' ),
'dashicons-drumstick' => esc_html__( 'Drumstick Icon', 'acf' ),
'dashicons-edit' => esc_html__( 'Edit Icon', 'acf' ),
'dashicons-edit-large' => esc_html__( 'Edit Large Icon', 'acf' ),
'dashicons-edit-page' => esc_html__( 'Edit Page Icon', 'acf' ),
'dashicons-editor-aligncenter' => esc_html__( 'Align Center Icon', 'acf' ),
'dashicons-editor-alignleft' => esc_html__( 'Align Left Icon', 'acf' ),
'dashicons-editor-alignright' => esc_html__( 'Align Right Icon', 'acf' ),
'dashicons-editor-bold' => esc_html__( 'Bold Icon', 'acf' ),
'dashicons-editor-break' => esc_html__( 'Break Icon', 'acf' ),
'dashicons-editor-code' => esc_html__( 'Code Icon', 'acf' ),
'dashicons-editor-contract' => esc_html__( 'Contract Icon', 'acf' ),
'dashicons-editor-customchar' => esc_html__( 'Custom Character Icon', 'acf' ),
'dashicons-editor-expand' => esc_html__( 'Expand Icon', 'acf' ),
'dashicons-editor-help' => esc_html__( 'Help Icon', 'acf' ),
'dashicons-editor-indent' => esc_html__( 'Indent Icon', 'acf' ),
'dashicons-editor-insertmore' => esc_html__( 'Insert More Icon', 'acf' ),
'dashicons-editor-italic' => esc_html__( 'Italic Icon', 'acf' ),
'dashicons-editor-justify' => esc_html__( 'Justify Icon', 'acf' ),
'dashicons-editor-kitchensink' => esc_html__( 'Kitchen Sink Icon', 'acf' ),
'dashicons-editor-ltr' => esc_html__( 'LTR Icon', 'acf' ),
'dashicons-editor-ol' => esc_html__( 'Ordered List Icon', 'acf' ),
'dashicons-editor-ol-rtl' => esc_html__( 'Ordered List RTL Icon', 'acf' ),
'dashicons-editor-outdent' => esc_html__( 'Outdent Icon', 'acf' ),
'dashicons-editor-paragraph' => esc_html__( 'Paragraph Icon', 'acf' ),
'dashicons-editor-paste-text' => esc_html__( 'Paste Text Icon', 'acf' ),
'dashicons-editor-paste-word' => esc_html__( 'Paste Word Icon', 'acf' ),
'dashicons-editor-quote' => esc_html__( 'Quote Icon', 'acf' ),
'dashicons-editor-removeformatting' => esc_html__( 'Remove Formatting Icon', 'acf' ),
'dashicons-editor-rtl' => esc_html__( 'RTL Icon', 'acf' ),
'dashicons-editor-spellcheck' => esc_html__( 'Spellcheck Icon', 'acf' ),
'dashicons-editor-strikethrough' => esc_html__( 'Strikethrough Icon', 'acf' ),
'dashicons-editor-table' => esc_html__( 'Table Icon', 'acf' ),
'dashicons-editor-textcolor' => esc_html__( 'Text Color Icon', 'acf' ),
'dashicons-editor-ul' => esc_html__( 'Unordered List Icon', 'acf' ),
'dashicons-editor-underline' => esc_html__( 'Underline Icon', 'acf' ),
'dashicons-editor-unlink' => esc_html__( 'Unlink Icon', 'acf' ),
'dashicons-editor-video' => esc_html__( 'Video Icon', 'acf' ),
'dashicons-ellipsis' => esc_html__( 'Ellipsis Icon', 'acf' ),
'dashicons-email' => esc_html__( 'Email Icon', 'acf' ),
'dashicons-email-alt' => esc_html__( 'Email (alt) Icon', 'acf' ),
'dashicons-email-alt2' => esc_html__( 'Email (alt2) Icon', 'acf' ),
'dashicons-embed-audio' => esc_html__( 'Embed Audio Icon', 'acf' ),
'dashicons-embed-generic' => esc_html__( 'Embed Generic Icon', 'acf' ),
'dashicons-embed-photo' => esc_html__( 'Embed Photo Icon', 'acf' ),
'dashicons-embed-post' => esc_html__( 'Embed Post Icon', 'acf' ),
'dashicons-embed-video' => esc_html__( 'Embed Video Icon', 'acf' ),
'dashicons-excerpt-view' => esc_html__( 'Excerpt View Icon', 'acf' ),
'dashicons-exit' => esc_html__( 'Exit Icon', 'acf' ),
'dashicons-external' => esc_html__( 'External Icon', 'acf' ),
'dashicons-facebook' => esc_html__( 'Facebook Icon', 'acf' ),
'dashicons-facebook-alt' => esc_html__( 'Facebook (alt) Icon', 'acf' ),
'dashicons-feedback' => esc_html__( 'Feedback Icon', 'acf' ),
'dashicons-filter' => esc_html__( 'Filter Icon', 'acf' ),
'dashicons-flag' => esc_html__( 'Flag Icon', 'acf' ),
'dashicons-food' => esc_html__( 'Food Icon', 'acf' ),
'dashicons-format-aside' => esc_html__( 'Aside Icon', 'acf' ),
'dashicons-format-audio' => esc_html__( 'Audio Icon', 'acf' ),
'dashicons-format-chat' => esc_html__( 'Chat Icon', 'acf' ),
'dashicons-format-gallery' => esc_html__( 'Gallery Icon', 'acf' ),
'dashicons-format-image' => esc_html__( 'Image Icon', 'acf' ),
'dashicons-format-quote' => esc_html__( 'Quote Icon', 'acf' ),
'dashicons-format-status' => esc_html__( 'Status Icon', 'acf' ),
'dashicons-format-video' => esc_html__( 'Video Icon', 'acf' ),
'dashicons-forms' => esc_html__( 'Forms Icon', 'acf' ),
'dashicons-fullscreen-alt' => esc_html__( 'Fullscreen (alt) Icon', 'acf' ),
'dashicons-fullscreen-exit-alt' => esc_html__( 'Fullscreen Exit (alt) Icon', 'acf' ),
'dashicons-games' => esc_html__( 'Games Icon', 'acf' ),
'dashicons-google' => esc_html__( 'Google Icon', 'acf' ),
'dashicons-grid-view' => esc_html__( 'Grid View Icon', 'acf' ),
'dashicons-groups' => esc_html__( 'Groups Icon', 'acf' ),
'dashicons-hammer' => esc_html__( 'Hammer Icon', 'acf' ),
'dashicons-heading' => esc_html__( 'Heading Icon', 'acf' ),
'dashicons-heart' => esc_html__( 'Heart Icon', 'acf' ),
'dashicons-hidden' => esc_html__( 'Hidden Icon', 'acf' ),
'dashicons-hourglass' => esc_html__( 'Hourglass Icon', 'acf' ),
'dashicons-html' => esc_html__( 'HTML Icon', 'acf' ),
'dashicons-id' => esc_html__( 'ID Icon', 'acf' ),
'dashicons-id-alt' => esc_html__( 'ID (alt) Icon', 'acf' ),
'dashicons-image-crop' => esc_html__( 'Crop Icon', 'acf' ),
'dashicons-image-filter' => esc_html__( 'Filter Icon', 'acf' ),
'dashicons-image-flip-horizontal' => esc_html__( 'Flip Horizontal Icon', 'acf' ),
'dashicons-image-flip-vertical' => esc_html__( 'Flip Vertical Icon', 'acf' ),
'dashicons-image-rotate' => esc_html__( 'Rotate Icon', 'acf' ),
'dashicons-image-rotate-left' => esc_html__( 'Rotate Left Icon', 'acf' ),
'dashicons-image-rotate-right' => esc_html__( 'Rotate Right Icon', 'acf' ),
'dashicons-images-alt' => esc_html__( 'Images (alt) Icon', 'acf' ),
'dashicons-images-alt2' => esc_html__( 'Images (alt2) Icon', 'acf' ),
'dashicons-index-card' => esc_html__( 'Index Card Icon', 'acf' ),
'dashicons-info' => esc_html__( 'Info Icon', 'acf' ),
'dashicons-info-outline' => esc_html__( 'Info Outline Icon', 'acf' ),
'dashicons-insert' => esc_html__( 'Insert Icon', 'acf' ),
'dashicons-insert-after' => esc_html__( 'Insert After Icon', 'acf' ),
'dashicons-insert-before' => esc_html__( 'Insert Before Icon', 'acf' ),
'dashicons-instagram' => esc_html__( 'Instagram Icon', 'acf' ),
'dashicons-laptop' => esc_html__( 'Laptop Icon', 'acf' ),
'dashicons-layout' => esc_html__( 'Layout Icon', 'acf' ),
'dashicons-leftright' => esc_html__( 'Left Right Icon', 'acf' ),
'dashicons-lightbulb' => esc_html__( 'Lightbulb Icon', 'acf' ),
'dashicons-linkedin' => esc_html__( 'LinkedIn Icon', 'acf' ),
'dashicons-list-view' => esc_html__( 'List View Icon', 'acf' ),
'dashicons-location' => esc_html__( 'Location Icon', 'acf' ),
'dashicons-location-alt' => esc_html__( 'Location (alt) Icon', 'acf' ),
'dashicons-lock' => esc_html__( 'Lock Icon', 'acf' ),
'dashicons-marker' => esc_html__( 'Marker Icon', 'acf' ),
'dashicons-media-archive' => esc_html__( 'Archive Icon', 'acf' ),
'dashicons-media-audio' => esc_html__( 'Audio Icon', 'acf' ),
'dashicons-media-code' => esc_html__( 'Code Icon', 'acf' ),
'dashicons-media-default' => esc_html__( 'Default Icon', 'acf' ),
'dashicons-media-document' => esc_html__( 'Document Icon', 'acf' ),
'dashicons-media-interactive' => esc_html__( 'Interactive Icon', 'acf' ),
'dashicons-media-spreadsheet' => esc_html__( 'Spreadsheet Icon', 'acf' ),
'dashicons-media-text' => esc_html__( 'Text Icon', 'acf' ),
'dashicons-media-video' => esc_html__( 'Video Icon', 'acf' ),
'dashicons-megaphone' => esc_html__( 'Megaphone Icon', 'acf' ),
'dashicons-menu' => esc_html__( 'Menu Icon', 'acf' ),
'dashicons-menu-alt' => esc_html__( 'Menu (alt) Icon', 'acf' ),
'dashicons-menu-alt2' => esc_html__( 'Menu (alt2) Icon', 'acf' ),
'dashicons-menu-alt3' => esc_html__( 'Menu (alt3) Icon', 'acf' ),
'dashicons-microphone' => esc_html__( 'Microphone Icon', 'acf' ),
'dashicons-migrate' => esc_html__( 'Migrate Icon', 'acf' ),
'dashicons-minus' => esc_html__( 'Minus Icon', 'acf' ),
'dashicons-money' => esc_html__( 'Money Icon', 'acf' ),
'dashicons-money-alt' => esc_html__( 'Money (alt) Icon', 'acf' ),
'dashicons-move' => esc_html__( 'Move Icon', 'acf' ),
'dashicons-nametag' => esc_html__( 'Nametag Icon', 'acf' ),
'dashicons-networking' => esc_html__( 'Networking Icon', 'acf' ),
'dashicons-no' => esc_html__( 'No Icon', 'acf' ),
'dashicons-no-alt' => esc_html__( 'No (alt) Icon', 'acf' ),
'dashicons-open-folder' => esc_html__( 'Open Folder Icon', 'acf' ),
'dashicons-palmtree' => esc_html__( 'Palm Tree Icon', 'acf' ),
'dashicons-paperclip' => esc_html__( 'Paperclip Icon', 'acf' ),
'dashicons-pdf' => esc_html__( 'PDF Icon', 'acf' ),
'dashicons-performance' => esc_html__( 'Performance Icon', 'acf' ),
'dashicons-pets' => esc_html__( 'Pets Icon', 'acf' ),
'dashicons-phone' => esc_html__( 'Phone Icon', 'acf' ),
'dashicons-pinterest' => esc_html__( 'Pinterest Icon', 'acf' ),
'dashicons-playlist-audio' => esc_html__( 'Playlist Audio Icon', 'acf' ),
'dashicons-playlist-video' => esc_html__( 'Playlist Video Icon', 'acf' ),
'dashicons-plugins-checked' => esc_html__( 'Plugins Checked Icon', 'acf' ),
'dashicons-plus' => esc_html__( 'Plus Icon', 'acf' ),
'dashicons-plus-alt' => esc_html__( 'Plus (alt) Icon', 'acf' ),
'dashicons-plus-alt2' => esc_html__( 'Plus (alt2) Icon', 'acf' ),
'dashicons-podio' => esc_html__( 'Podio Icon', 'acf' ),
'dashicons-portfolio' => esc_html__( 'Portfolio Icon', 'acf' ),
'dashicons-post-status' => esc_html__( 'Post Status Icon', 'acf' ),
'dashicons-pressthis' => esc_html__( 'Pressthis Icon', 'acf' ),
'dashicons-printer' => esc_html__( 'Printer Icon', 'acf' ),
'dashicons-privacy' => esc_html__( 'Privacy Icon', 'acf' ),
'dashicons-products' => esc_html__( 'Products Icon', 'acf' ),
'dashicons-randomize' => esc_html__( 'Randomize Icon', 'acf' ),
'dashicons-reddit' => esc_html__( 'Reddit Icon', 'acf' ),
'dashicons-redo' => esc_html__( 'Redo Icon', 'acf' ),
'dashicons-remove' => esc_html__( 'Remove Icon', 'acf' ),
'dashicons-rest-api' => esc_html__( 'REST API Icon', 'acf' ),
'dashicons-rss' => esc_html__( 'RSS Icon', 'acf' ),
'dashicons-saved' => esc_html__( 'Saved Icon', 'acf' ),
'dashicons-schedule' => esc_html__( 'Schedule Icon', 'acf' ),
'dashicons-screenoptions' => esc_html__( 'Screen Options Icon', 'acf' ),
'dashicons-search' => esc_html__( 'Search Icon', 'acf' ),
'dashicons-share' => esc_html__( 'Share Icon', 'acf' ),
'dashicons-share-alt' => esc_html__( 'Share (alt) Icon', 'acf' ),
'dashicons-share-alt2' => esc_html__( 'Share (alt2) Icon', 'acf' ),
'dashicons-shield' => esc_html__( 'Shield Icon', 'acf' ),
'dashicons-shield-alt' => esc_html__( 'Shield (alt) Icon', 'acf' ),
'dashicons-shortcode' => esc_html__( 'Shortcode Icon', 'acf' ),
'dashicons-slides' => esc_html__( 'Slides Icon', 'acf' ),
'dashicons-smartphone' => esc_html__( 'Smartphone Icon', 'acf' ),
'dashicons-smiley' => esc_html__( 'Smiley Icon', 'acf' ),
'dashicons-sort' => esc_html__( 'Sort Icon', 'acf' ),
'dashicons-sos' => esc_html__( 'Sos Icon', 'acf' ),
'dashicons-spotify' => esc_html__( 'Spotify Icon', 'acf' ),
'dashicons-star-empty' => esc_html__( 'Star Empty Icon', 'acf' ),
'dashicons-star-filled' => esc_html__( 'Star Filled Icon', 'acf' ),
'dashicons-star-half' => esc_html__( 'Star Half Icon', 'acf' ),
'dashicons-sticky' => esc_html__( 'Sticky Icon', 'acf' ),
'dashicons-store' => esc_html__( 'Store Icon', 'acf' ),
'dashicons-superhero' => esc_html__( 'Superhero Icon', 'acf' ),
'dashicons-superhero-alt' => esc_html__( 'Superhero (alt) Icon', 'acf' ),
'dashicons-table-col-after' => esc_html__( 'Table Col After Icon', 'acf' ),
'dashicons-table-col-before' => esc_html__( 'Table Col Before Icon', 'acf' ),
'dashicons-table-col-delete' => esc_html__( 'Table Col Delete Icon', 'acf' ),
'dashicons-table-row-after' => esc_html__( 'Table Row After Icon', 'acf' ),
'dashicons-table-row-before' => esc_html__( 'Table Row Before Icon', 'acf' ),
'dashicons-table-row-delete' => esc_html__( 'Table Row Delete Icon', 'acf' ),
'dashicons-tablet' => esc_html__( 'Tablet Icon', 'acf' ),
'dashicons-tag' => esc_html__( 'Tag Icon', 'acf' ),
'dashicons-tagcloud' => esc_html__( 'Tagcloud Icon', 'acf' ),
'dashicons-testimonial' => esc_html__( 'Testimonial Icon', 'acf' ),
'dashicons-text' => esc_html__( 'Text Icon', 'acf' ),
'dashicons-text-page' => esc_html__( 'Text Page Icon', 'acf' ),
'dashicons-thumbs-down' => esc_html__( 'Thumbs Down Icon', 'acf' ),
'dashicons-thumbs-up' => esc_html__( 'Thumbs Up Icon', 'acf' ),
'dashicons-tickets' => esc_html__( 'Tickets Icon', 'acf' ),
'dashicons-tickets-alt' => esc_html__( 'Tickets (alt) Icon', 'acf' ),
'dashicons-tide' => esc_html__( 'Tide Icon', 'acf' ),
'dashicons-translation' => esc_html__( 'Translation Icon', 'acf' ),
'dashicons-trash' => esc_html__( 'Trash Icon', 'acf' ),
'dashicons-twitch' => esc_html__( 'Twitch Icon', 'acf' ),
'dashicons-twitter' => esc_html__( 'Twitter Icon', 'acf' ),
'dashicons-twitter-alt' => esc_html__( 'Twitter (alt) Icon', 'acf' ),
'dashicons-undo' => esc_html__( 'Undo Icon', 'acf' ),
'dashicons-universal-access' => esc_html__( 'Universal Access Icon', 'acf' ),
'dashicons-universal-access-alt' => esc_html__( 'Universal Access (alt) Icon', 'acf' ),
'dashicons-unlock' => esc_html__( 'Unlock Icon', 'acf' ),
'dashicons-update' => esc_html__( 'Update Icon', 'acf' ),
'dashicons-update-alt' => esc_html__( 'Update (alt) Icon', 'acf' ),
'dashicons-upload' => esc_html__( 'Upload Icon', 'acf' ),
'dashicons-vault' => esc_html__( 'Vault Icon', 'acf' ),
'dashicons-video-alt' => esc_html__( 'Video (alt) Icon', 'acf' ),
'dashicons-video-alt2' => esc_html__( 'Video (alt2) Icon', 'acf' ),
'dashicons-video-alt3' => esc_html__( 'Video (alt3) Icon', 'acf' ),
'dashicons-visibility' => esc_html__( 'Visibility Icon', 'acf' ),
'dashicons-warning' => esc_html__( 'Warning Icon', 'acf' ),
'dashicons-welcome-add-page' => esc_html__( 'Add Page Icon', 'acf' ),
'dashicons-welcome-comments' => esc_html__( 'Comments Icon', 'acf' ),
'dashicons-welcome-learn-more' => esc_html__( 'Learn More Icon', 'acf' ),
'dashicons-welcome-view-site' => esc_html__( 'View Site Icon', 'acf' ),
'dashicons-welcome-widgets-menus' => esc_html__( 'Widgets Menus Icon', 'acf' ),
'dashicons-welcome-write-blog' => esc_html__( 'Write Blog Icon', 'acf' ),
'dashicons-whatsapp' => esc_html__( 'WhatsApp Icon', 'acf' ),
'dashicons-wordpress' => esc_html__( 'WordPress Icon', 'acf' ),
'dashicons-wordpress-alt' => esc_html__( 'WordPress (alt) Icon', 'acf' ),
'dashicons-xing' => esc_html__( 'Xing Icon', 'acf' ),
'dashicons-yes' => esc_html__( 'Yes Icon', 'acf' ),
'dashicons-yes-alt' => esc_html__( 'Yes (alt) Icon', 'acf' ),
'dashicons-youtube' => esc_html__( 'YouTube Icon', 'acf' ),
);
return apply_filters( 'acf/fields/icon_picker/dashicons', $dashicons );
}
/**
* Returns the schema used by the REST API.
*
* @since 6.3
*
* @param array $field The main field array.
* @return array
*/
public function get_rest_schema( array $field ): array {
return array(
'type' => array( 'object', 'null' ),
'required' => ! empty( $field['required'] ),
'properties' => array(
'type' => array(
'description' => esc_html__( 'The type of icon to save.', 'acf' ),
'type' => array( 'string' ),
'required' => true,
'enum' => array_keys( $this->get_tabs() ),
),
'value' => array(
'description' => esc_html__( 'The value of icon to save.', 'acf' ),
'type' => array( 'string', 'int' ),
'required' => true,
),
),
);
}
/**
* Validates a value sent via the REST API.
*
* @since 6.3
*
* @param boolean $valid The current validity boolean.
* @param array|null $value The value of the field.
* @param array $field The main field array.
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
if ( is_null( $value ) ) {
if ( ! empty( $field['required'] ) ) {
return new WP_Error(
'rest_property_required',
/* translators: %s - field name */
sprintf( __( '%s is a required property of acf.', 'acf' ), $field['name'] )
);
} else {
return $valid;
}
}
if ( ! empty( $value['type'] ) && 'media_library' === $value['type'] ) {
$param = sprintf( '%s[%s][value]', $field['prefix'], $field['name'] );
$data = array(
'param' => $param,
'value' => (int) $value['value'],
);
if ( ! is_int( $value['value'] ) || 'attachment' !== get_post_type( $value['value'] ) ) {
/* translators: %s - field/param name */
$error = sprintf( __( '%s requires a valid attachment ID when type is set to media_library.', 'acf' ), $param );
return new WP_Error( 'rest_invalid_param', $error, $data );
}
}
return $valid;
}
}
acf_register_field_type( 'acf_field_icon_picker' );
endif;

View File

@@ -0,0 +1,467 @@
<?php
if ( ! class_exists( 'acf_field_image' ) ) :
class acf_field_image extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'image';
$this->label = __( 'Image', 'acf' );
$this->category = 'content';
$this->description = __( 'Uses the native WordPress media picker to upload, or choose images.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-image.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/image/', 'docs', 'field-type-selection' );
$this->defaults = array(
'return_format' => 'array',
'preview_size' => 'medium',
'library' => 'all',
'min_width' => 0,
'min_height' => 0,
'min_size' => 0,
'max_width' => 0,
'max_height' => 0,
'max_size' => 0,
'mime_types' => '',
);
// filters
add_filter( 'get_media_item_args', array( $this, 'get_media_item_args' ) );
}
/**
* description
*
* @type function
* @date 16/12/2015
* @since 5.3.2
*
* @param $post_id (int)
* @return $post_id (int)
*/
function input_admin_enqueue_scripts() {
// localize
acf_localize_text(
array(
'Select Image' => __( 'Select Image', 'acf' ),
'Edit Image' => __( 'Edit Image', 'acf' ),
'Update Image' => __( 'Update Image', 'acf' ),
'All images' => __( 'All images', 'acf' ),
)
);
}
/**
* Renders the field HTML.
*
* @date 23/01/13
* @since 3.6.0
*
* @param array $field The field settings.
* @return void
*/
function render_field( $field ) {
$uploader = acf_get_setting( 'uploader' );
// Enqueue uploader scripts
if ( $uploader === 'wp' ) {
acf_enqueue_uploader();
}
// Elements and attributes.
$value = '';
$div_attrs = array(
'class' => 'acf-image-uploader',
'data-preview_size' => $field['preview_size'],
'data-library' => $field['library'],
'data-mime_types' => $field['mime_types'],
'data-uploader' => $uploader,
);
$img_attrs = array(
'src' => '',
'alt' => '',
'data-name' => 'image',
);
// Detect value.
if ( $field['value'] && is_numeric( $field['value'] ) ) {
$image = wp_get_attachment_image_src( $field['value'], $field['preview_size'] );
if ( $image ) {
$value = $field['value'];
$img_attrs['src'] = $image[0];
$img_attrs['alt'] = get_post_meta( $field['value'], '_wp_attachment_image_alt', true );
$div_attrs['class'] .= ' has-value';
}
}
// Add "preview size" max width and height style.
// Apply max-width to wrap, and max-height to img for max compatibility with field widths.
$size = acf_get_image_size( $field['preview_size'] );
$size_w = $size['width'] ? $size['width'] . 'px' : '100%';
$size_h = $size['height'] ? $size['height'] . 'px' : '100%';
$img_attrs['style'] = sprintf( 'max-height: %s;', $size_h );
// Render HTML.
?>
<div <?php echo acf_esc_attrs( $div_attrs ); ?>>
<?php
acf_hidden_input(
array(
'name' => $field['name'],
'value' => $value,
)
);
?>
<div class="show-if-value image-wrap" style="max-width: <?php echo esc_attr( $size_w ); ?>">
<img <?php echo acf_esc_attrs( $img_attrs ); ?> />
<div class="acf-actions -hover">
<?php if ( $uploader !== 'basic' ) : ?>
<a class="acf-icon -pencil dark" data-name="edit" href="#" title="<?php esc_attr_e( 'Edit', 'acf' ); ?>"></a>
<?php endif; ?>
<a class="acf-icon -cancel dark" data-name="remove" href="#" title="<?php esc_attr_e( 'Remove', 'acf' ); ?>"></a>
</div>
</div>
<div class="hide-if-value">
<?php if ( $uploader === 'basic' ) : ?>
<?php if ( $field['value'] && ! is_numeric( $field['value'] ) ) : ?>
<div class="acf-error-message"><p><?php echo acf_esc_html( $field['value'] ); ?></p></div>
<?php endif; ?>
<label class="acf-basic-uploader">
<?php
acf_file_input(
array(
'name' => $field['name'],
'id' => $field['id'],
'key' => $field['key'],
)
);
?>
</label>
<?php else : ?>
<p><?php esc_html_e( 'No image selected', 'acf' ); ?> <a data-name="add" class="acf-button button" href="#"><?php esc_html_e( 'Add Image', 'acf' ); ?></a></p>
<?php endif; ?>
</div>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'return_format',
'layout' => 'horizontal',
'choices' => array(
'array' => __( 'Image Array', 'acf' ),
'url' => __( 'Image URL', 'acf' ),
'id' => __( 'Image ID', 'acf' ),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Library', 'acf' ),
'instructions' => __( 'Limit the media library choice', 'acf' ),
'type' => 'radio',
'name' => 'library',
'layout' => 'horizontal',
'choices' => array(
'all' => __( 'All', 'acf' ),
'uploadedTo' => __( 'Uploaded to post', 'acf' ),
),
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
// Clear numeric settings.
$clear = array(
'min_width',
'min_height',
'min_size',
'max_width',
'max_height',
'max_size',
);
foreach ( $clear as $k ) {
if ( empty( $field[ $k ] ) ) {
$field[ $k ] = '';
}
}
acf_render_field_setting(
$field,
array(
'label' => __( 'Minimum', 'acf' ),
'hint' => __( 'Restrict which images can be uploaded', 'acf' ),
'type' => 'text',
'name' => 'min_width',
'prepend' => __( 'Width', 'acf' ),
'append' => 'px',
)
);
acf_render_field_setting(
$field,
array(
'label' => '',
'type' => 'text',
'name' => 'min_height',
'prepend' => __( 'Height', 'acf' ),
'append' => 'px',
'_append' => 'min_width',
)
);
acf_render_field_setting(
$field,
array(
'label' => '',
'type' => 'text',
'name' => 'min_size',
'prepend' => __( 'File size', 'acf' ),
'append' => 'MB',
'_append' => 'min_width',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Maximum', 'acf' ),
'hint' => __( 'Restrict which images can be uploaded', 'acf' ),
'type' => 'text',
'name' => 'max_width',
'prepend' => __( 'Width', 'acf' ),
'append' => 'px',
)
);
acf_render_field_setting(
$field,
array(
'label' => '',
'type' => 'text',
'name' => 'max_height',
'prepend' => __( 'Height', 'acf' ),
'append' => 'px',
'_append' => 'max_width',
)
);
acf_render_field_setting(
$field,
array(
'label' => '',
'type' => 'text',
'name' => 'max_size',
'prepend' => __( 'File size', 'acf' ),
'append' => 'MB',
'_append' => 'max_width',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Allowed File Types', 'acf' ),
'instructions' => __( 'Comma separated list. Leave blank for all types', 'acf' ),
'type' => 'text',
'name' => 'mime_types',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Preview Size', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'preview_size',
'choices' => acf_get_image_sizes(),
)
);
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// bail early if no value
if ( empty( $value ) ) {
return false;
}
// bail early if not numeric (error message)
if ( ! is_numeric( $value ) ) {
return false;
}
// convert to int
$value = intval( $value );
// format
if ( $field['return_format'] == 'url' ) {
return wp_get_attachment_url( $value );
} elseif ( $field['return_format'] == 'array' ) {
return acf_get_attachment( $value );
}
// return
return $value;
}
/**
* description
*
* @type function
* @date 27/01/13
* @since 3.6.0
*
* @param $vars (array)
* @return $vars
*/
function get_media_item_args( $vars ) {
$vars['send'] = true;
return( $vars );
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value which will be saved in the database
* @param $post_id - the post_id of which the value will be saved
* @param $field - the field array holding all the field options
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
return acf_get_field_type( 'file' )->update_value( $value, $post_id, $field );
}
/**
* This function will validate a basic file input
*
* @type function
* @since 5.0.0
*
* @param boolean $valid The current validity status.
* @param mixed $value The field value.
* @param array $field The field array.
* @param string $input The name of the input in the POST object.
* @return boolean The validity status.
*/
public function validate_value( $valid, $value, $field, $input ) {
return acf_get_field_type( 'file' )->validate_value( $valid, $value, $field, $input );
}
/**
* Additional validation for the image field when submitted via REST.
*
* @param boolean $valid The current validity booleean
* @param integer $value The value of the field
* @param array $field The field array
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
return acf_get_field_type( 'file' )->validate_rest_value( $valid, $value, $field );
}
/**
* Return the schema array for the REST API.
*
* @param array $field The field array
* @return array
*/
public function get_rest_schema( array $field ) {
return acf_get_field_type( 'file' )->get_rest_schema( $field );
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value The field value
* @param string|integer $post_id The post ID
* @param array $field The field array
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'acf_field_image' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,275 @@
<?php
if ( ! class_exists( 'acf_field_link' ) ) :
class acf_field_link extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'link';
$this->label = __( 'Link', 'acf' );
$this->category = 'relational';
$this->description = __( 'Allows you to specify a link and its properties such as title and target using the WordPress native link picker.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-link.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/link/', 'docs', 'field-type-selection' );
$this->defaults = array(
'return_format' => 'array',
);
}
/**
* description
*
* @type function
* @date 16/5/17
* @since 5.5.13
*
* @param $post_id (int)
* @return $post_id (int)
*/
function get_link( $value = '' ) {
// vars
$link = array(
'title' => '',
'url' => '',
'target' => '',
);
// array (ACF 5.6.0)
if ( is_array( $value ) ) {
$link = array_merge( $link, $value );
// post id (ACF < 5.6.0)
} elseif ( is_numeric( $value ) ) {
$link['title'] = get_the_title( $value );
$link['url'] = get_permalink( $value );
// string (ACF < 5.6.0)
} elseif ( is_string( $value ) ) {
$link['url'] = $value;
}
// return
return $link;
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
*/
public function render_field( $field ) {
// vars
$div = array(
'id' => $field['id'],
'class' => $field['class'] . ' acf-link',
);
// render scripts/styles
acf_enqueue_uploader();
// get link
$link = $this->get_link( $field['value'] );
// classes
if ( $link['url'] ) {
$div['class'] .= ' -value';
}
if ( $link['target'] === '_blank' ) {
$div['class'] .= ' -external';
}
?>
<div <?php echo acf_esc_attrs( $div ); ?>>
<div class="acf-hidden">
<a class="link-node" href="<?php echo esc_url( $link['url'] ); ?>" target="<?php echo esc_attr( $link['target'] ); ?>"><?php echo esc_html( $link['title'] ); ?></a>
<?php foreach ( $link as $k => $v ) : ?>
<?php
acf_hidden_input(
array(
'class' => "input-$k",
'name' => $field['name'] . "[$k]",
'value' => $v,
)
);
?>
<?php endforeach; ?>
</div>
<a href="#" class="button" data-name="add" target=""><?php esc_html_e( 'Select Link', 'acf' ); ?></a>
<div class="link-wrap">
<span class="link-title"><?php echo esc_html( $link['title'] ); ?></span>
<a class="link-url" href="<?php echo esc_url( $link['url'] ); ?>" target="_blank"><?php echo esc_html( $link['url'] ); ?></a>
<i class="acf-icon -link-ext acf-js-tooltip" title="<?php esc_attr_e( 'Opens in a new window/tab', 'acf' ); ?>"></i><a class="acf-icon -pencil -clear acf-js-tooltip" data-name="edit" href="#" title="<?php esc_attr_e( 'Edit', 'acf' ); ?>"></a><a class="acf-icon -cancel -clear acf-js-tooltip" data-name="remove" href="#" title="<?php esc_attr_e( 'Remove', 'acf' ); ?>"></a>
</div>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Value', 'acf' ),
'instructions' => __( 'Specify the returned value on front end', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'layout' => 'horizontal',
'choices' => array(
'array' => __( 'Link Array', 'acf' ),
'url' => __( 'Link URL', 'acf' ),
),
)
);
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// bail early if no value
if ( empty( $value ) ) {
return $value;
}
// get link
$link = $this->get_link( $value );
// format value
if ( $field['return_format'] == 'url' ) {
return $link['url'];
}
// return link
return $link;
}
/**
* description
*
* @type function
* @date 11/02/2014
* @since 5.0.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function validate_value( $valid, $value, $field, $input ) {
// bail early if not required
if ( ! $field['required'] ) {
return $valid;
}
// URL is required
if ( empty( $value ) || empty( $value['url'] ) ) {
return false;
}
// return
return $valid;
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value which will be saved in the database
* @param $post_id - the post_id of which the value will be saved
* @param $field - the field array holding all the field options
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
// Check if value is an empty array and convert to empty string.
if ( empty( $value ) || empty( $value['url'] ) ) {
$value = '';
}
// return
return $value;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
return array(
'type' => array( 'object', 'null' ),
'required' => ! empty( $field['required'] ),
'properties' => array(
'title' => array(
'type' => 'string',
),
'url' => array(
'type' => 'string',
'required' => true,
'format' => 'uri',
),
'target' => array(
'type' => 'string',
),
),
);
}
}
// initialize
acf_register_field_type( 'acf_field_link' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,174 @@
<?php
if ( ! class_exists( 'acf_field_message' ) ) :
class acf_field_message extends acf_field {
public $show_in_rest = false;
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'message';
$this->label = __( 'Message', 'acf' );
$this->category = 'layout';
$this->description = __( 'Used to display a message to editors alongside other fields. Useful for providing additional context or instructions around your fields.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-message.png';
$this->supports = array(
'required' => false,
'bindings' => false,
);
$this->defaults = array(
'message' => '',
'esc_html' => 0,
'new_lines' => 'wpautop',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$m = $field['message'];
// wptexturize (improves "quotes")
$m = wptexturize( $m );
// esc_html
if ( $field['esc_html'] ) {
$m = esc_html( $m );
}
// new lines
if ( $field['new_lines'] == 'wpautop' ) {
$m = wpautop( $m );
} elseif ( $field['new_lines'] == 'br' ) {
$m = nl2br( $m );
}
// return
echo acf_esc_html( $m );
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Message', 'acf' ),
'instructions' => '',
'type' => 'textarea',
'name' => 'message',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'New Lines', 'acf' ),
'instructions' => __( 'Controls how new lines are rendered', 'acf' ),
'type' => 'select',
'name' => 'new_lines',
'choices' => array(
'wpautop' => __( 'Automatically add paragraphs', 'acf' ),
'br' => __( 'Automatically add &lt;br&gt;', 'acf' ),
'' => __( 'No Formatting', 'acf' ),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Escape HTML', 'acf' ),
'instructions' => __( 'Allow HTML markup to display as visible text instead of rendering', 'acf' ),
'name' => 'esc_html',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* This function will translate field settings
*
* @type function
* @date 8/03/2016
* @since 5.3.2
*
* @param $field (array)
* @return $field
*/
function translate_field( $field ) {
// translate
$field['message'] = acf_translate( $field['message'] );
// return
return $field;
}
/**
* This filter is appied to the $field after it is loaded from the database
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $field - the field array holding all the field options
*
* @return $field - the field array holding all the field options
*/
function load_field( $field ) {
// remove name to avoid caching issue
$field['name'] = '';
// remove instructions
$field['instructions'] = '';
// remove required to avoid JS issues
$field['required'] = 0;
// set value other than 'null' to avoid ACF loading / caching issue
$field['value'] = false;
// return
return $field;
}
}
// initialize
acf_register_field_type( 'acf_field_message' );
endif; // class_exists check

View File

@@ -0,0 +1,320 @@
<?php
if ( ! class_exists( 'acf_field_number' ) ) :
class acf_field_number extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'number';
$this->label = __( 'Number', 'acf' );
$this->description = __( 'An input limited to numerical values.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-number.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/number/', 'docs', 'field-type-selection' );
$this->defaults = array(
'default_value' => '',
'min' => '',
'max' => '',
'step' => '',
'placeholder' => '',
'prepend' => '',
'append' => '',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$atts = array();
$keys = array( 'type', 'id', 'class', 'name', 'value', 'min', 'max', 'step', 'placeholder', 'pattern' );
$keys2 = array( 'readonly', 'disabled', 'required' );
$html = '';
// step
if ( ! $field['step'] ) {
$field['step'] = 'any';
}
// prepend
if ( $field['prepend'] !== '' ) {
$field['class'] .= ' acf-is-prepended';
$html .= '<div class="acf-input-prepend">' . acf_esc_html( $field['prepend'] ) . '</div>';
}
// append
if ( $field['append'] !== '' ) {
$field['class'] .= ' acf-is-appended';
$html .= '<div class="acf-input-append">' . acf_esc_html( $field['append'] ) . '</div>';
}
// atts (value="123")
foreach ( $keys as $k ) {
if ( isset( $field[ $k ] ) ) {
$atts[ $k ] = $field[ $k ];
}
}
// atts2 (disabled="disabled")
foreach ( $keys2 as $k ) {
if ( ! empty( $field[ $k ] ) ) {
$atts[ $k ] = $k;
}
}
// remove empty atts
$atts = acf_clean_atts( $atts );
// render
$html .= '<div class="acf-input-wrap">' . acf_get_text_input( $atts ) . '</div>';
// return
echo $html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped by individual html functions above.
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
// default_value
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'text',
'name' => 'default_value',
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Minimum Value', 'acf' ),
'instructions' => '',
'type' => 'number',
'name' => 'min',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Maximum Value', 'acf' ),
'instructions' => '',
'type' => 'number',
'name' => 'max',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Placeholder Text', 'acf' ),
'instructions' => __( 'Appears within the input', 'acf' ),
'type' => 'text',
'name' => 'placeholder',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Step Size', 'acf' ),
'instructions' => '',
'type' => 'number',
'name' => 'step',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Prepend', 'acf' ),
'instructions' => __( 'Appears before the input', 'acf' ),
'type' => 'text',
'name' => 'prepend',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Append', 'acf' ),
'instructions' => __( 'Appears after the input', 'acf' ),
'type' => 'text',
'name' => 'append',
)
);
}
/**
* description
*
* @type function
* @date 11/02/2014
* @since 5.0.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function validate_value( $valid, $value, $field, $input ) {
// remove ','
if ( acf_str_exists( ',', $value ) ) {
$value = str_replace( ',', '', $value );
}
// if value is not numeric...
if ( ! is_numeric( $value ) ) {
// allow blank to be saved
if ( ! empty( $value ) ) {
$valid = __( 'Value must be a number', 'acf' );
}
// return early
return $valid;
}
// convert
$value = floatval( $value );
// min
if ( is_numeric( $field['min'] ) && $value < floatval( $field['min'] ) ) {
$valid = sprintf( __( 'Value must be equal to or higher than %d', 'acf' ), $field['min'] );
}
// max
if ( is_numeric( $field['max'] ) && $value > floatval( $field['max'] ) ) {
$valid = sprintf( __( 'Value must be equal to or lower than %d', 'acf' ), $field['max'] );
}
// return
return $valid;
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value which will be saved in the database
* @param $field - the field array holding all the field options
* @param $post_id - the post_id of which the value will be saved
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
// no formatting needed for empty value
if ( empty( $value ) ) {
return $value;
}
// remove ','
if ( acf_str_exists( ',', $value ) ) {
$value = str_replace( ',', '', $value );
}
// return
return $value;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'number', 'null' ),
'required' => ! empty( $field['required'] ),
);
if ( ! empty( $field['min'] ) ) {
$schema['minimum'] = (float) $field['min'];
}
if ( ! empty( $field['max'] ) ) {
$schema['maximum'] = (float) $field['max'];
}
if ( isset( $field['default_value'] ) && is_numeric( $field['default_value'] ) ) {
$schema['default'] = (float) $field['default_value'];
}
return $schema;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'acf_field_number' );
endif; // class_exists check

View File

@@ -0,0 +1,307 @@
<?php
if ( ! class_exists( 'acf_field_oembed' ) ) :
#[AllowDynamicProperties]
class acf_field_oembed extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'oembed';
$this->label = __( 'oEmbed', 'acf' );
$this->category = 'content';
$this->description = __( 'An interactive component for embedding videos, images, tweets, audio and other content by making use of the native WordPress oEmbed functionality.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-oembed.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/oembed/', 'docs', 'field-type-selection' );
$this->defaults = array(
'width' => '',
'height' => '',
);
$this->width = 640;
$this->height = 390;
$this->supports = array(
'escaping_html' => true, // The OEmbed field only produces html safe content from format_value.
);
// extra
add_action( 'wp_ajax_acf/fields/oembed/search', array( $this, 'ajax_query' ) );
add_action( 'wp_ajax_nopriv_acf/fields/oembed/search', array( $this, 'ajax_query' ) );
}
/**
* This function will prepare the field for input
*
* @type function
* @date 14/2/17
* @since 5.5.8
*
* @param $field (array)
* @return (int)
*/
function prepare_field( $field ) {
// defaults
if ( ! $field['width'] ) {
$field['width'] = $this->width;
}
if ( ! $field['height'] ) {
$field['height'] = $this->height;
}
// return
return $field;
}
/**
* Attempts to fetch the HTML for the provided URL using oEmbed.
*
* @date 24/01/2014
* @since 5.0.0
*
* @param string $url The URL that should be embedded.
* @param integer|string $width Optional maxwidth value passed to the provider URL.
* @param integer|string $height Optional maxheight value passed to the provider URL.
* @return string|false The embedded HTML on success, false on failure.
*/
function wp_oembed_get( $url = '', $width = 0, $height = 0 ) {
$embed = false;
$res = array(
'width' => $width,
'height' => $height,
);
if ( function_exists( 'wp_oembed_get' ) ) {
$embed = wp_oembed_get( $url, $res );
}
// try shortcode
if ( ! $embed ) {
global $wp_embed;
$embed = $wp_embed->shortcode( $res, $url );
}
return $embed;
}
/**
* Returns AJAX results for the oEmbed field.
*
* @since 5.0.0
*
* @return void
*/
public function ajax_query() {
$args = acf_request_args(
array(
'nonce' => '',
'field_key' => '',
)
);
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true ) ) {
die();
}
wp_send_json( $this->get_ajax_query( $_POST ) );
}
/**
* This function will return an array of data formatted for use in a select2 AJAX response
*
* @type function
* @date 15/10/2014
* @since 5.0.9
*
* @param $options (array)
* @return (array)
*/
function get_ajax_query( $args = array() ) {
// defaults
$args = acf_parse_args(
$args,
array(
's' => '',
'field_key' => '',
)
);
// load field
$field = acf_get_field( $args['field_key'] );
if ( ! $field ) {
return false;
}
// prepare field to correct width and height
$field = $this->prepare_field( $field );
// vars
$response = array(
'url' => $args['s'],
'html' => $this->wp_oembed_get( $args['s'], $field['width'], $field['height'] ),
);
// return
return $response;
}
/**
* Renders the oEmbed field.
*
* @since 3.6
*
* @param array $field The field settings array.
* @return void
*/
public function render_field( $field ) {
$atts = array(
'class' => 'acf-oembed',
'data-nonce' => wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] ),
);
if ( $field['value'] ) {
$atts['class'] .= ' has-value';
}
?>
<div <?php echo acf_esc_attrs( $atts ); ?>>
<?php
acf_hidden_input(
array(
'class' => 'input-value',
'name' => $field['name'],
'value' => $field['value'],
)
);
?>
<div class="title">
<?php
acf_text_input(
array(
'class' => 'input-search',
'value' => $field['value'],
'placeholder' => __( 'Enter URL', 'acf' ),
'autocomplete' => 'off',
)
);
?>
<div class="acf-actions -hover">
<a data-name="clear-button" href="#" class="acf-icon -cancel grey"></a>
</div>
</div>
<div class="canvas">
<div class="canvas-media">
<?php
if ( $field['value'] ) {
echo $this->wp_oembed_get( $field['value'], $field['width'], $field['height'] ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_ombed_get generates HTML safe output.
}
?>
</div>
<i class="acf-icon -picture hide-if-value"></i>
</div>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Embed Size', 'acf' ),
'type' => 'text',
'name' => 'width',
'prepend' => __( 'Width', 'acf' ),
'append' => 'px',
'placeholder' => $this->width,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Embed Size', 'acf' ),
'type' => 'text',
'name' => 'height',
'prepend' => __( 'Height', 'acf' ),
'append' => 'px',
'placeholder' => $this->height,
'_append' => 'width',
)
);
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template.
*
* @type filter
* @since 3.6
*
* @param mixed $value The value which was loaded from the database.
* @param mixed $post_id The $post_id from which the value was loaded.
* @param array $field The field array holding all the field options.
* @return mixed the modified value
*/
public function format_value( $value, $post_id, $field ) {
// bail early if no value
if ( empty( $value ) ) {
return $value;
}
// prepare field to correct width and height
$field = $this->prepare_field( $field );
// get oembed
$value = $this->wp_oembed_get( $value, $field['width'], $field['height'] );
// return
return $value;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = parent::get_rest_schema( $field );
$schema['format'] = 'uri';
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_oembed' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,45 @@
<?php
if ( ! class_exists( 'acf_field_output' ) ) :
/**
* This class and field type has been deprecated since ACF 6.3.2 and will not output anything.
*/
class acf_field_output extends acf_field {
/**
* This function will setup the field type data
*
* @since 5.0.0
*/
public function initialize() {
// vars
$this->name = 'output';
$this->label = 'output';
$this->public = false;
$this->defaults = array(
'html' => false,
);
}
/**
* The render field call. Deprecated since ACF 6.3.2.
*
* @param array $field The $field being edited
* @return false
*/
public function render_field( $field ) {
// Deprecated since 6.3.2 and will be removed in a future release.
_deprecated_function( __FUNCTION__, '6.3.2' );
return false;
}
}
// initialize
acf_register_field_type( 'acf_field_output' );
endif; // class_exists check

View File

@@ -0,0 +1,705 @@
<?php
if ( ! class_exists( 'acf_field_page_link' ) ) :
class acf_field_page_link extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'page_link';
$this->label = __( 'Page Link', 'acf' );
$this->category = 'relational';
$this->description = __( 'An interactive dropdown to select one or more posts, pages, custom post type items or archive URLs, with the option to search.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-page-link.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/page-link/', 'docs', 'field-type-selection' );
$this->defaults = array(
'post_type' => array(),
'taxonomy' => array(),
'allow_null' => 0,
'multiple' => 0,
'allow_archives' => 1,
);
// extra
add_action( 'wp_ajax_acf/fields/page_link/query', array( $this, 'ajax_query' ) );
add_action( 'wp_ajax_nopriv_acf/fields/page_link/query', array( $this, 'ajax_query' ) );
add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_page_link_conditional_choices' ), 10, 3 );
}
/**
* Filters choices in page link conditions.
*
* @since 6.3
*
* @param array $choices The selected choice.
* @param array $conditional_field The conditional field settings object.
* @param string $rule_value The rule value.
* @return array
*/
public function render_field_page_link_conditional_choices( $choices, $conditional_field, $rule_value ) {
if ( ! is_array( $conditional_field ) || $conditional_field['type'] !== 'page_link' ) {
return $choices;
}
if ( ! empty( $rule_value ) ) {
$post_title = get_the_title( $rule_value );
$choices = array( $rule_value => $post_title );
}
return $choices;
}
/**
* Returns AJAX results for the Page Link field.
*
* @since 5.0.0
*
* @return void
*/
public function ajax_query() {
$nonce = acf_request_arg( 'nonce', '' );
$key = acf_request_arg( 'field_key', '' );
$conditional_logic = (bool) acf_request_arg( 'conditional_logic', false );
if ( $conditional_logic ) {
if ( ! acf_current_user_can_admin() ) {
die();
}
// Use the standard ACF admin nonce.
$nonce = '';
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic ) ) {
die();
}
// defaults
$options = acf_parse_args(
$_POST,
array(
'post_id' => 0,
's' => '',
'field_key' => '',
'paged' => 1,
'include' => '',
)
);
// vars
$results = array();
$args = array();
$s = false;
$is_search = false;
// paged
$args['posts_per_page'] = 20;
$args['paged'] = $options['paged'];
// search
if ( $options['s'] !== '' ) {
// strip slashes (search may be integer)
$s = wp_unslash( strval( $options['s'] ) );
// update vars
$args['s'] = $s;
$is_search = true;
}
// load field
$field = acf_get_field( $options['field_key'] );
if ( ! $field ) {
die();
}
// update $args
if ( ! empty( $field['post_type'] ) ) {
$args['post_type'] = acf_get_array( $field['post_type'] );
} else {
$args['post_type'] = acf_get_post_types();
}
// post status
if ( ! empty( $options['post_status'] ) ) {
$args['post_status'] = acf_get_array( $options['post_status'] );
} elseif ( ! empty( $field['post_status'] ) ) {
$args['post_status'] = acf_get_array( $field['post_status'] );
}
// create tax queries
if ( ! empty( $field['taxonomy'] ) ) {
// append to $args
$args['tax_query'] = array();
// decode terms
$taxonomies = acf_decode_taxonomy_terms( $field['taxonomy'] );
// now create the tax queries
foreach ( $taxonomies as $taxonomy => $terms ) {
$args['tax_query'][] = array(
'taxonomy' => $taxonomy,
'field' => 'slug',
'terms' => $terms,
);
}
}
if ( ! empty( $options['include'] ) ) {
$args['include'] = $options['include'];
}
// filters
$args = apply_filters( 'acf/fields/page_link/query', $args, $field, $options['post_id'] );
$args = apply_filters( 'acf/fields/page_link/query/name=' . $field['name'], $args, $field, $options['post_id'] );
$args = apply_filters( 'acf/fields/page_link/query/key=' . $field['key'], $args, $field, $options['post_id'] );
// add archives to $results
if ( $field['allow_archives'] && $args['paged'] == 1 ) {
// Generate unique list of URLs.
$links = array();
$links[] = home_url();
foreach ( $args['post_type'] as $post_type ) {
$links[] = get_post_type_archive_link( $post_type );
}
$links = array_filter( $links );
$links = array_unique( $links );
// Convert list into choices.
$children = array();
foreach ( $links as $link ) {
// Ignore if search does not match.
if ( $is_search && stripos( $link, $s ) === false ) {
continue;
}
$children[] = array(
'id' => $link,
'text' => $link,
);
}
if ( $children ) {
$results[] = array(
'text' => __( 'Archives', 'acf' ),
'children' => $children,
);
}
}
// If there is an include set, we will unset search to avoid attempting to further filter by the search term.
if ( isset( $args['include'] ) ) {
unset( $args['s'] );
}
// get posts grouped by post type
$groups = acf_get_grouped_posts( $args );
// loop
if ( ! empty( $groups ) ) {
foreach ( array_keys( $groups ) as $group_title ) {
// vars
$posts = acf_extract_var( $groups, $group_title );
// data
$data = array(
'text' => $group_title,
'children' => array(),
);
// convert post objects to post titles
foreach ( array_keys( $posts ) as $post_id ) {
$posts[ $post_id ] = $this->get_post_title( $posts[ $post_id ], $field, $options['post_id'], $is_search );
}
// order posts by search
if ( $is_search && empty( $args['orderby'] ) && isset( $args['s'] ) ) {
$posts = acf_order_by_search( $posts, $args['s'] );
}
// append to $data
foreach ( array_keys( $posts ) as $post_id ) {
$data['children'][] = $this->get_post_result( $post_id, $posts[ $post_id ] );
}
// append to $results
$results[] = $data;
}
}
// return
acf_send_ajax_results(
array(
'results' => $results,
'limit' => $args['posts_per_page'],
)
);
}
/**
* This function will return an array containing id, text and maybe description data
*
* @type function
* @date 7/07/2016
* @since 5.4.0
*
* @param $id (mixed)
* @param $text (string)
* @return (array)
*/
function get_post_result( $id, $text ) {
// vars
$result = array(
'id' => $id,
'text' => $text,
);
// look for parent
$search = '| ' . __( 'Parent', 'acf' ) . ':';
$pos = strpos( $text, $search );
if ( $pos !== false ) {
$result['description'] = substr( $text, $pos + 2 );
$result['text'] = substr( $text, 0, $pos );
}
// return
return $result;
}
/**
* This function returns the HTML for a result
*
* @type function
* @date 1/11/2013
* @since 5.0.0
*
* @param $post (object)
* @param $field (array)
* @param $post_id (int) the post_id to which this value is saved to
* @return (string)
*/
function get_post_title( $post, $field, $post_id = 0, $is_search = 0 ) {
// get post_id
if ( ! $post_id ) {
$post_id = acf_get_form_data( 'post_id' );
}
// vars
$title = acf_get_post_title( $post, $is_search );
// filters
$title = apply_filters( 'acf/fields/page_link/result', $title, $post, $field, $post_id );
$title = apply_filters( 'acf/fields/page_link/result/name=' . $field['_name'], $title, $post, $field, $post_id );
$title = apply_filters( 'acf/fields/page_link/result/key=' . $field['key'], $title, $post, $field, $post_id );
// return
return $title;
}
/**
* This function will return an array of posts for a given field value
*
* @type function
* @date 13/06/2014
* @since 5.0.0
*
* @param $value (array)
* @return $value
*/
function get_posts( $value, $field ) {
// force value to array
$value = acf_get_array( $value );
// get selected post ID's
$post__in = array();
foreach ( $value as $k => $v ) {
if ( is_numeric( $v ) ) {
// append to $post__in
$post__in[] = (int) $v;
}
}
// bail early if no posts
if ( empty( $post__in ) ) {
return $value;
}
// get posts
$posts = acf_get_posts(
array(
'post__in' => $post__in,
'post_type' => $field['post_type'],
)
);
// override value with post
$return = array();
// append to $return
foreach ( $value as $k => $v ) {
if ( is_numeric( $v ) ) {
// extract first post
$post = array_shift( $posts );
// append
if ( $post ) {
$return[] = $post;
}
} else {
$return[] = $v;
}
}
// return
return $return;
}
/**
* Renders the Page Link field.
*
* @since 3.6
*
* @param array $field The field settings array.
* @return void
*/
public function render_field( $field ) {
// Change Field into a select
$field['type'] = 'select';
$field['ui'] = 1;
$field['ajax'] = 1;
$field['choices'] = array();
$field['nonce'] = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
// populate choices if value exists
if ( ! empty( $field['value'] ) ) {
// get posts
$posts = $this->get_posts( $field['value'], $field );
// set choices
if ( ! empty( $posts ) ) {
foreach ( array_keys( $posts ) as $i ) {
// vars
$post = acf_extract_var( $posts, $i );
if ( is_object( $post ) ) {
// append to choices
$field['choices'][ $post->ID ] = $this->get_post_title( $post, $field );
} else {
// append to choices
$field['choices'][ $post ] = $post;
}
}
}
}
// render
acf_render_field( $field );
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Post Type', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'post_type',
'choices' => acf_get_pretty_post_types(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'All post types', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Post Status', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'post_status',
'choices' => acf_get_pretty_post_statuses(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'Any post status', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Taxonomy', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'taxonomy',
'choices' => acf_get_taxonomy_terms(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'All taxonomies', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Archives URLs', 'acf' ),
'instructions' => '',
'name' => 'allow_archives',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Select Multiple', 'acf' ),
'instructions' => 'Allow content editors to select multiple values',
'name' => 'multiple',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Null', 'acf' ),
'instructions' => '',
'name' => 'allow_null',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// ACF4 null
if ( $value === 'null' ) {
return false;
}
// bail early if no value
if ( empty( $value ) ) {
return $value;
}
// get posts
$value = $this->get_posts( $value, $field );
// set choices
foreach ( array_keys( $value ) as $i ) {
// vars
$post = acf_extract_var( $value, $i );
// convert $post to permalink
if ( is_object( $post ) ) {
$post = get_permalink( $post );
}
// append back to $value
$value[ $i ] = $post;
}
// convert back from array if neccessary
if ( ! $field['multiple'] ) {
$value = array_shift( $value );
}
// return value
return $value;
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value which will be saved in the database
* @param $post_id - the post_id of which the value will be saved
* @param $field - the field array holding all the field options
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
// Bail early if no value.
if ( empty( $value ) ) {
return $value;
}
// Format array of values.
// - ensure each value is an id.
// - Parse each id as string for SQL LIKE queries.
if ( acf_is_sequential_array( $value ) ) {
$value = array_map( 'acf_maybe_idval', $value );
$value = array_map( 'strval', $value );
// Parse single value for id.
} else {
$value = acf_maybe_idval( $value );
}
// Return value.
return $value;
}
/**
* Validates page link fields updated via the REST API.
*
* @param boolean $valid The current validity booleean
* @param integer $value The value of the field
* @param array $field The field array
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
return acf_get_field_type( 'post_object' )->validate_rest_value( $valid, $value, $field );
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'integer', 'array', 'null' ),
'required' => ! empty( $field['required'] ),
'items' => array(
'type' => array( 'integer' ),
),
);
if ( empty( $field['allow_null'] ) ) {
$schema['minItems'] = 1;
}
if ( ! empty( $field['allow_archives'] ) ) {
$schema['type'][] = 'string';
$schema['items']['type'][] = 'string';
}
if ( empty( $field['multiple'] ) ) {
$schema['maxItems'] = 1;
}
return $schema;
}
/**
* @see \acf_field::get_rest_links()
* @param mixed $value The raw (unformatted) field value.
* @param integer|string $post_id
* @param array $field
* @return array
*/
public function get_rest_links( $value, $post_id, array $field ) {
$links = array();
if ( empty( $value ) ) {
return $links;
}
foreach ( (array) $value as $object_id ) {
if ( ! $post_type = get_post_type( $object_id ) or ! $post_type = get_post_type_object( $post_type ) ) {
continue;
}
$rest_base = acf_get_object_type_rest_base( $post_type );
$links[] = array(
'rel' => $post_type->name === 'attachment' ? 'acf:attachment' : 'acf:post',
'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
'embeddable' => true,
);
}
return $links;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'acf_field_page_link' );
endif; // class_exists check

View File

@@ -0,0 +1,106 @@
<?php
if ( ! class_exists( 'acf_field_password' ) ) :
class acf_field_password extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'password';
$this->label = __( 'Password', 'acf' );
$this->description = __( 'An input for providing a password using a masked field.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-password.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/password/', 'docs', 'field-type-selection' );
$this->defaults = array(
'placeholder' => '',
'prepend' => '',
'append' => '',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
acf_get_field_type( 'text' )->render_field( $field );
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
// TODO: Delete this method?
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Placeholder Text', 'acf' ),
'instructions' => __( 'Appears within the input', 'acf' ),
'type' => 'text',
'name' => 'placeholder',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Prepend', 'acf' ),
'instructions' => __( 'Appears before the input', 'acf' ),
'type' => 'text',
'name' => 'prepend',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Append', 'acf' ),
'instructions' => __( 'Appears after the input', 'acf' ),
'type' => 'text',
'name' => 'append',
)
);
}
}
// initialize
acf_register_field_type( 'acf_field_password' );
endif; // class_exists check

View File

@@ -0,0 +1,761 @@
<?php
if ( ! class_exists( 'acf_field_post_object' ) ) :
class acf_field_post_object extends acf_field {
/**
* This function will setup the field type data
*
* @since 5.0.0
*/
public function initialize() {
$this->name = 'post_object';
$this->label = __( 'Post Object', 'acf' );
$this->category = 'relational';
$this->description = __( 'An interactive and customizable UI for picking one or many posts, pages or post type items with the option to search. ', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-post-object.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/post-object/', 'docs', 'field-type-selection' );
$this->defaults = array(
'post_type' => array(),
'taxonomy' => array(),
'allow_null' => 0,
'multiple' => 0,
'return_format' => 'object',
'ui' => 1,
'bidirectional_target' => array(),
);
// extra
add_action( 'wp_ajax_acf/fields/post_object/query', array( $this, 'ajax_query' ) );
add_action( 'wp_ajax_nopriv_acf/fields/post_object/query', array( $this, 'ajax_query' ) );
add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_post_object_conditional_choices' ), 10, 3 );
}
/**
* Filters choices in post object conditions.
*
* @since 6.3
*
* @param array $choices The selected choice.
* @param array $conditional_field The conditional field settings object.
* @param string $rule_value The rule value.
* @return array
*/
public function render_field_post_object_conditional_choices( $choices, $conditional_field, $rule_value ) {
if ( ! is_array( $conditional_field ) || $conditional_field['type'] !== 'post_object' ) {
return $choices;
}
if ( ! empty( $rule_value ) ) {
$post_title = get_the_title( $rule_value );
$choices = array( $rule_value => $post_title );
}
return $choices;
}
/**
* AJAX query handler for post object fields.
*
* @since 5.0.0
*
* @return void
*/
public function ajax_query() {
$nonce = acf_request_arg( 'nonce', '' );
$key = acf_request_arg( 'field_key', '' );
$conditional_logic = (bool) acf_request_arg( 'conditional_logic', false );
if ( $conditional_logic ) {
if ( ! acf_current_user_can_admin() ) {
die();
}
// Use the standard ACF admin nonce.
$nonce = '';
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic ) ) {
die();
}
acf_send_ajax_results( $this->get_ajax_query( $_POST ) );
}
/**
* This function will return an array of data formatted for use in a select2 AJAX response
*
* @since 5.0.9
*
* @param array $options The options being queried for the ajax request.
* @return array|boolean The AJAX response array, or false on failure.
*/
public function get_ajax_query( $options = array() ) {
// defaults
$options = acf_parse_args(
$options,
array(
'post_id' => 0,
's' => '',
'field_key' => '',
'paged' => 1,
'include' => '',
)
);
// load field
$field = acf_get_field( $options['field_key'] );
if ( ! $field ) {
return false;
}
// vars
$results = array();
$args = array();
$s = false;
$is_search = false;
// paged
$args['posts_per_page'] = 20;
$args['paged'] = $options['paged'];
// search
if ( $options['s'] !== '' ) {
// strip slashes (search may be integer)
$s = wp_unslash( strval( $options['s'] ) );
// update vars
$args['s'] = $s;
$is_search = true;
}
if ( ! empty( $options['include'] ) ) {
$args['include'] = $options['include'];
}
// post_type
if ( ! empty( $field['post_type'] ) ) {
$args['post_type'] = acf_get_array( $field['post_type'] );
} else {
$args['post_type'] = acf_get_post_types();
}
// post status
if ( ! empty( $options['post_status'] ) ) {
$args['post_status'] = acf_get_array( $options['post_status'] );
} elseif ( ! empty( $field['post_status'] ) ) {
$args['post_status'] = acf_get_array( $field['post_status'] );
}
// If there is an include set, we will unset search to avoid attempting to further filter by the search term.
if ( isset( $args['include'] ) ) {
unset( $args['s'] );
}
// taxonomy
if ( ! empty( $field['taxonomy'] ) ) {
// vars
$terms = acf_decode_taxonomy_terms( $field['taxonomy'] );
// append to $args
$args['tax_query'] = array();
// now create the tax queries
foreach ( $terms as $k => $v ) {
$args['tax_query'][] = array(
'taxonomy' => $k,
'field' => 'slug',
'terms' => $v,
);
}
}
// filters
$args = apply_filters( 'acf/fields/post_object/query', $args, $field, $options['post_id'] );
$args = apply_filters( 'acf/fields/post_object/query/name=' . $field['name'], $args, $field, $options['post_id'] );
$args = apply_filters( 'acf/fields/post_object/query/key=' . $field['key'], $args, $field, $options['post_id'] );
// get posts grouped by post type
$groups = acf_get_grouped_posts( $args );
// bail early if no posts
if ( empty( $groups ) ) {
return false;
}
// loop
foreach ( array_keys( $groups ) as $group_title ) {
// vars
$posts = acf_extract_var( $groups, $group_title );
// data
$data = array(
'text' => $group_title,
'children' => array(),
);
// convert post objects to post titles
foreach ( array_keys( $posts ) as $post_id ) {
$posts[ $post_id ] = $this->get_post_title( $posts[ $post_id ], $field, $options['post_id'], $is_search, true );
}
// order posts by search
if ( $is_search && empty( $args['orderby'] ) && isset( $args['s'] ) ) {
$posts = acf_order_by_search( $posts, $args['s'] );
}
// append to $data
foreach ( array_keys( $posts ) as $post_id ) {
$data['children'][] = $this->get_post_result( $post_id, $posts[ $post_id ] );
}
// append to $results
$results[] = $data;
}
// optgroup or single
$post_type = acf_get_array( $args['post_type'] );
if ( count( $post_type ) == 1 ) {
$results = $results[0]['children'];
}
// vars
$response = array(
'results' => $results,
'limit' => $args['posts_per_page'],
);
// return
return $response;
}
/**
* This function will return an array containing id, text and maybe description data
*
* @since 5.4.0
*
* @param mixed $id The ID of the post result.
* @param string $text The text for the response item.
* @return array The combined result array.
*/
public function get_post_result( $id, $text ) {
// vars
$result = array(
'id' => $id,
'text' => $text,
);
// look for parent
$search = '| ' . __( 'Parent', 'acf' ) . ':';
$pos = strpos( $text, $search );
if ( $pos !== false ) {
$result['description'] = substr( $text, $pos + 2 );
$result['text'] = substr( $text, 0, $pos );
}
// return
return $result;
}
/**
* This function post object's filtered output post title
*
* @since 5.0.0
*
* @param WP_Post $post The WordPress post.
* @param array $field The field being output.
* @param integer $post_id The post_id to which this value is saved to.
* @param integer $is_search An int-as-boolean value for whether we're performing a search.
* @param boolean $unescape Should we return an unescaped post title.
* @return string A potentially user filtered post title for the post, which may contain unsafe HTML.
*/
public function get_post_title( $post, $field, $post_id = 0, $is_search = 0, $unescape = false ) {
// get post_id
if ( ! $post_id ) {
$post_id = acf_get_form_data( 'post_id' );
}
// vars
$title = acf_get_post_title( $post, $is_search );
// unescape for select2 output which handles the escaping.
if ( $unescape ) {
$title = html_entity_decode( $title );
}
// filters
$title = apply_filters( 'acf/fields/post_object/result', $title, $post, $field, $post_id );
$title = apply_filters( 'acf/fields/post_object/result/name=' . $field['_name'], $title, $post, $field, $post_id );
$title = apply_filters( 'acf/fields/post_object/result/key=' . $field['key'], $title, $post, $field, $post_id );
// return untrusted output.
return $title;
}
/**
* Create the HTML interface for the post object field.
*
* @since 3.6
*
* @param array $field An array holding all the field's data.
* @return void
*/
public function render_field( $field ) {
// Change Field into a select
$field['type'] = 'select';
$field['ui'] = 1;
$field['ajax'] = 1;
$field['nonce'] = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
$field['choices'] = array();
// load posts
$posts = $this->get_posts( $field['value'], $field );
if ( $posts ) {
foreach ( array_keys( $posts ) as $i ) {
// vars
$post = acf_extract_var( $posts, $i );
// append to choices
$field['choices'][ $post->ID ] = $this->get_post_title( $post, $field );
}
}
// render
acf_render_field( $field );
}
/**
* Create extra options for post object field. This is rendered when editing.
* The value of $field['name'] can be used (like below) to save extra data to the $field.
*
* @since 3.6
*
* @param array $field An array holding all the field's data.
*/
public function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Post Type', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'post_type',
'choices' => acf_get_pretty_post_types(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'All post types', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Post Status', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'post_status',
'choices' => acf_get_pretty_post_statuses(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'Any post status', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Taxonomy', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'taxonomy',
'choices' => acf_get_taxonomy_terms(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'All taxonomies', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'return_format',
'choices' => array(
'object' => __( 'Post Object', 'acf' ),
'id' => __( 'Post ID', 'acf' ),
),
'layout' => 'horizontal',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Select Multiple', 'acf' ),
'instructions' => 'Allow content editors to select multiple values',
'name' => 'multiple',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
public function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Null', 'acf' ),
'instructions' => '',
'name' => 'allow_null',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* Renders the field settings used in the "Advanced" tab.
*
* @since 6.2
*
* @param array $field The field settings array.
* @return void
*/
public function render_field_advanced_settings( $field ) {
acf_render_bidirectional_field_settings( $field );
}
/**
* This filter is applied to the $value after it is loaded from the db
*
* @since 3.6
*
* @param mixed $value The value found in the database
* @param mixed $post_id The post_id from which the value was loaded
* @param array $field The field array holding all the field options
* @return mixed $value
*/
public function load_value( $value, $post_id, $field ) {
// ACF4 null
if ( $value === 'null' ) {
return false;
}
// return
return $value;
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @since 3.6
*
* @param mixed $value The value found in the database
* @param mixed $post_id The post_id from which the value was loaded
* @param array $field The field array holding all the field options
* @return mixed $value
*/
public function format_value( $value, $post_id, $field ) {
$value = acf_get_numeric( $value );
// bail early if no value
if ( empty( $value ) ) {
return false;
}
// load posts if needed
if ( $field['return_format'] == 'object' ) {
$value = $this->get_posts( $value, $field );
}
// convert back from array if neccessary
if ( ! $field['multiple'] && is_array( $value ) ) {
$value = current( $value );
}
// return value
return $value;
}
/**
* Filters the field value before it is saved into the database.
*
* @since 3.6
*
* @param mixed $value The value which will be saved in the database.
* @param integer $post_id The post_id of which the value will be saved.
* @param array $field The field array holding all the field options.
* @return mixed $value The modified value.
*/
public function update_value( $value, $post_id, $field ) {
// Bail early if no value.
if ( empty( $value ) ) {
acf_update_bidirectional_values( array(), $post_id, $field );
return $value;
}
// Format array of values.
// - ensure each value is an id.
// - Parse each id as string for SQL LIKE queries.
if ( acf_is_sequential_array( $value ) ) {
$value = array_map( 'acf_idval', $value );
$value = array_map( 'strval', $value );
// Parse single value for id.
} else {
$value = acf_idval( $value );
}
acf_update_bidirectional_values( acf_get_array( $value ), $post_id, $field );
return $value;
}
/**
* This function will return an array of posts for a given field value
*
* @since 5.0
*
* @param mixed $value The value of the field.
* @param array $field The field array holding all the field options.
* @return array $value An array of post objects.
*/
public function get_posts( $value, $field ) {
// numeric
$value = acf_get_numeric( $value );
// bail early if no value
if ( empty( $value ) ) {
return false;
}
// get posts
$posts = acf_get_posts(
array(
'post__in' => $value,
'post_type' => $field['post_type'],
)
);
// return
return $posts;
}
/**
* Validates post object fields updated via the REST API.
*
* @since 5.11
*
* @param boolean $valid The current validity booleean
* @param integer $value The value of the field
* @param array $field The field array
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
if ( is_null( $value ) ) {
return $valid;
}
$param = sprintf( '%s[%s]', $field['prefix'], $field['name'] );
$data = array( 'param' => $param );
$value = is_array( $value ) ? $value : array( $value );
$invalid_posts = array();
$post_type_errors = array();
$taxonomy_errors = array();
foreach ( $value as $post_id ) {
if ( is_string( $post_id ) ) {
continue;
}
$post_type = get_post_type( $post_id );
if ( ! $post_type ) {
$invalid_posts[] = $post_id;
continue;
}
if (
is_array( $field['post_type'] ) &&
! empty( $field['post_type'] ) &&
! in_array( $post_type, $field['post_type'] )
) {
$post_type_errors[] = $post_id;
}
if ( is_array( $field['taxonomy'] ) && ! empty( $field['taxonomy'] ) ) {
$found = false;
foreach ( $field['taxonomy'] as $taxonomy_term ) {
$decoded = acf_decode_taxonomy_term( $taxonomy_term );
if ( $decoded && is_object_in_term( $post_id, $decoded['taxonomy'], $decoded['term'] ) ) {
$found = true;
break;
}
}
if ( ! $found ) {
$taxonomy_errors[] = $post_id;
}
}
}
if ( count( $invalid_posts ) ) {
$error = sprintf(
__( '%1$s must have a valid post ID.', 'acf' ),
$param
);
$data['value'] = $invalid_posts;
return new WP_Error( 'rest_invalid_param', $error, $data );
}
if ( count( $post_type_errors ) ) {
$error = sprintf(
_n(
'%1$s must be of post type %2$s.',
'%1$s must be of one of the following post types: %2$s',
count( $field['post_type'] ),
'acf'
),
$param,
count( $field['post_type'] ) > 1 ? implode( ', ', $field['post_type'] ) : $field['post_type'][0]
);
$data['value'] = $post_type_errors;
return new WP_Error( 'rest_invalid_param', $error, $data );
}
if ( count( $taxonomy_errors ) ) {
$error = sprintf(
_n(
'%1$s must have term %2$s.',
'%1$s must have one of the following terms: %2$s',
count( $field['taxonomy'] ),
'acf'
),
$param,
count( $field['taxonomy'] ) > 1 ? implode( ', ', $field['taxonomy'] ) : $field['taxonomy'][0]
);
$data['value'] = $taxonomy_errors;
return new WP_Error( 'rest_invalid_param', $error, $data );
}
return $valid;
}
/**
* Return the schema array for the REST API.
*
* @since 5.11
*
* @param array $field The field array.
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'integer', 'array', 'null' ),
'required' => ! empty( $field['required'] ),
'items' => array(
'type' => 'integer',
),
);
if ( empty( $field['allow_null'] ) ) {
$schema['minItems'] = 1;
}
if ( empty( $field['multiple'] ) ) {
$schema['maxItems'] = 1;
}
return $schema;
}
/**
* REST link attributes generator for this field.
*
* @since 5.11
* @see \acf_field::get_rest_links()
*
* @param mixed $value The raw (unformatted) field value.
* @param integer|string $post_id The post ID being queried.
* @param array $field The field array.
* @return array
*/
public function get_rest_links( $value, $post_id, array $field ) {
$links = array();
if ( empty( $value ) ) {
return $links;
}
foreach ( (array) $value as $object_id ) {
if ( ! $post_type = get_post_type( $object_id ) ) {
continue;
}
if ( ! $post_type_object = get_post_type_object( $post_type ) ) {
continue;
}
$rest_base = acf_get_object_type_rest_base( $post_type_object );
$links[] = array(
'rel' => $post_type_object->name === 'attachment' ? 'acf:attachment' : 'acf:post',
'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
'embeddable' => true,
);
}
return $links;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @since 5.11
*
* @param mixed $value The raw (unformatted) field value.
* @param integer|string $post_id The post ID being queried.
* @param array $field The field array.
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'acf_field_post_object' );
endif; // class_exists check

View File

@@ -0,0 +1,457 @@
<?php
if ( ! class_exists( 'acf_field_radio' ) ) :
class acf_field_radio extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'radio';
$this->label = __( 'Radio Button', 'acf' );
$this->category = 'choice';
$this->description = __( 'A group of radio button inputs that allows the user to make a single selection from values that you specify.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-radio-button.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/radio-button/', 'docs', 'field-type-selection' );
$this->defaults = array(
'layout' => 'vertical',
'choices' => array(),
'default_value' => '',
'other_choice' => 0,
'save_other_choice' => 0,
'allow_null' => 0,
'return_format' => 'value',
);
}
/**
* Create the HTML interface for your field
*
* @param $field (array) the $field being rendered
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field (array) the $field being edited
* @return n/a
*/
function render_field( $field ) {
// vars
$e = '';
$ul = array(
'class' => 'acf-radio-list',
'data-allow_null' => $field['allow_null'],
'data-other_choice' => $field['other_choice'],
);
// append to class
$ul['class'] .= ' ' . ( $field['layout'] == 'horizontal' ? 'acf-hl' : 'acf-bl' );
$ul['class'] .= ' ' . $field['class'];
// Determine selected value.
$value = (string) $field['value'];
// 1. Selected choice.
if ( isset( $field['choices'][ $value ] ) ) {
$checked = (string) $value;
// 2. Custom choice.
} elseif ( $field['other_choice'] && $value !== '' ) {
$checked = 'other';
// 3. Empty choice.
} elseif ( $field['allow_null'] ) {
$checked = '';
// 4. Default to first choice.
} else {
$checked = (string) key( $field['choices'] );
}
// other choice
$other_input = false;
if ( $field['other_choice'] ) {
// Define other input attrs.
$other_input = array(
'type' => 'text',
'name' => $field['name'],
'value' => '',
'disabled' => 'disabled',
'class' => 'acf-disabled',
);
// Select other choice if value is not a valid choice.
if ( $checked === 'other' ) {
unset( $other_input['disabled'] );
$other_input['value'] = $field['value'];
}
// Ensure an 'other' choice is defined.
if ( ! isset( $field['choices']['other'] ) ) {
$field['choices']['other'] = '';
}
}
// Bail early if no choices.
if ( empty( $field['choices'] ) ) {
return;
}
// Hiden input.
$e .= acf_get_hidden_input( array( 'name' => $field['name'] ) );
// Open <ul>.
$e .= '<ul ' . acf_esc_attrs( $ul ) . '>';
// Loop through choices.
foreach ( $field['choices'] as $value => $label ) {
$is_selected = false;
// Ensure value is a string.
$value = (string) $value;
// Define input attrs.
$attrs = array(
'type' => 'radio',
'id' => sanitize_title( $field['id'] . '-' . $value ),
'name' => $field['name'],
'value' => $value,
);
// Check if selected.
if ( esc_attr( $value ) === esc_attr( $checked ) ) {
$attrs['checked'] = 'checked';
$is_selected = true;
}
// Check if is disabled.
if ( isset( $field['disabled'] ) && acf_in_array( $value, $field['disabled'] ) ) {
$attrs['disabled'] = 'disabled';
}
// Additional HTML (the "Other" input).
$additional_html = '';
if ( $value === 'other' && $other_input ) {
$additional_html = ' ' . acf_get_text_input( $other_input );
}
// append
$e .= '<li><label' . ( $is_selected ? ' class="selected"' : '' ) . '><input ' . acf_esc_attrs( $attrs ) . '/>' . acf_esc_html( $label ) . '</label>' . $additional_html . '</li>';
}
// Close <ul>.
$e .= '</ul>';
// Output HTML.
echo $e; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped per attribute above.
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
// Encode choices (convert from array).
$field['choices'] = acf_encode_choices( $field['choices'] );
acf_render_field_setting(
$field,
array(
'label' => __( 'Choices', 'acf' ),
'instructions' => __( 'Enter each choice on a new line.', 'acf' ) . '<br />' . __( 'For more control, you may specify both a value and label like this:', 'acf' ) . '<br /><span class="acf-field-setting-example">' . __( 'red : Red', 'acf' ) . '</span>',
'type' => 'textarea',
'name' => 'choices',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'text',
'name' => 'default_value',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Value', 'acf' ),
'instructions' => __( 'Specify the returned value on front end', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'layout' => 'horizontal',
'choices' => array(
'value' => __( 'Value', 'acf' ),
'label' => __( 'Label', 'acf' ),
'array' => __( 'Both (Array)', 'acf' ),
),
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Null', 'acf' ),
'instructions' => '',
'name' => 'allow_null',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Other Choice', 'acf' ),
'name' => 'other_choice',
'type' => 'true_false',
'ui' => 1,
'instructions' => __( "Add 'other' choice to allow for custom values", 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Save Other Choice', 'acf' ),
'name' => 'save_other_choice',
'type' => 'true_false',
'ui' => 1,
'instructions' => __( "Save 'other' values to the field's choices", 'acf' ),
'conditions' => array(
'field' => 'other_choice',
'operator' => '==',
'value' => 1,
),
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Layout', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'layout',
'layout' => 'horizontal',
'choices' => array(
'vertical' => __( 'Vertical', 'acf' ),
'horizontal' => __( 'Horizontal', 'acf' ),
),
)
);
}
/**
* This filter is appied to the $field before it is saved to the database
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $field - the field array holding all the field options
* @param $post_id - the field group ID (post_type = acf)
*
* @return $field - the modified field
*/
function update_field( $field ) {
// decode choices (convert to array)
$field['choices'] = acf_decode_choices( $field['choices'] );
// return
return $field;
}
/**
* This filter is appied to the $value before it is updated in the db
*
* @type filter
* @since 3.6
* @date 23/01/13
* @todo Fix bug where $field was found via json and has no ID
*
* @param $value - the value which will be saved in the database
* @param $post_id - the post_id of which the value will be saved
* @param $field - the field array holding all the field options
*
* @return $value - the modified value
*/
function update_value( $value, $post_id, $field ) {
// bail early if no value (allow 0 to be saved)
if ( ! $value && ! is_numeric( $value ) ) {
return $value;
}
// save_other_choice
if ( $field['save_other_choice'] ) {
// value isn't in choices yet
if ( ! isset( $field['choices'][ $value ] ) ) {
// get raw $field (may have been changed via repeater field)
// if field is local, it won't have an ID
$selector = $field['ID'] ? $field['ID'] : $field['key'];
$field = acf_get_field( $selector );
// bail early if no ID (JSON only)
if ( ! $field['ID'] ) {
return $value;
}
// unslash (fixes serialize single quote issue)
$value = wp_unslash( $value );
// sanitize (remove tags)
$value = sanitize_text_field( $value );
// update $field
$field['choices'][ $value ] = $value;
// save
acf_update_field( $field );
}
}
// return
return $value;
}
/**
* This filter is appied to the $value after it is loaded from the db
*
* @type filter
* @since 5.2.9
* @date 23/01/13
*
* @param $value - the value found in the database
* @param $post_id - the post_id from which the value was loaded from
* @param $field - the field array holding all the field options
*
* @return $value - the value to be saved in te database
*/
function load_value( $value, $post_id, $field ) {
// must be single value
if ( is_array( $value ) ) {
$value = array_pop( $value );
}
// return
return $value;
}
/**
* This function will translate field settings
*
* @type function
* @date 8/03/2016
* @since 5.3.2
*
* @param $field (array)
* @return $field
*/
function translate_field( $field ) {
return acf_get_field_type( 'select' )->translate_field( $field );
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
return acf_get_field_type( 'select' )->format_value( $value, $post_id, $field );
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
function get_rest_schema( array $field ) {
$schema = parent::get_rest_schema( $field );
if ( isset( $field['default_value'] ) && '' !== $field['default_value'] ) {
$schema['default'] = $field['default_value'];
}
// If other/custom choices are allowed, nothing else to do here.
if ( ! empty( $field['other_choice'] ) ) {
return $schema;
}
$schema['enum'] = acf_get_field_type( 'select' )->format_rest_choices( $field['choices'] );
if ( ! empty( $field['allow_null'] ) ) {
$schema['enum'][] = null;
}
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_radio' );
endif; // class_exists check

View File

@@ -0,0 +1,272 @@
<?php
if ( ! class_exists( 'acf_field_range' ) ) :
class acf_field_range extends acf_field_number {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'range';
$this->label = __( 'Range', 'acf' );
$this->description = __( 'An input for selecting a numerical value within a specified range using a range slider element.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-range.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/range/', 'docs', 'field-type-selection' );
$this->defaults = array(
'default_value' => '',
'min' => '',
'max' => '',
'step' => '',
'prepend' => '',
'append' => '',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$atts = array();
$keys = array( 'type', 'id', 'class', 'name', 'value', 'min', 'max', 'step' );
$keys2 = array( 'readonly', 'disabled', 'required' );
// step
if ( ! $field['step'] ) {
$field['step'] = 1;
}
// min / max
if ( ! $field['min'] ) {
$field['min'] = 0;
}
if ( ! $field['max'] ) {
$field['max'] = 100;
}
// allow for prev 'non numeric' value
if ( ! is_numeric( $field['value'] ) ) {
$field['value'] = 0;
}
// constrain within max and min
$field['value'] = max( $field['value'], $field['min'] );
$field['value'] = min( $field['value'], $field['max'] );
// atts (value="123")
foreach ( $keys as $k ) {
if ( isset( $field[ $k ] ) ) {
$atts[ $k ] = $field[ $k ];
}
}
// atts2 (disabled="disabled")
foreach ( $keys2 as $k ) {
if ( ! empty( $field[ $k ] ) ) {
$atts[ $k ] = $k;
}
}
// remove empty atts
$atts = acf_clean_atts( $atts );
// open
$html = '<div class="acf-range-wrap">';
// prepend
if ( $field['prepend'] !== '' ) {
$html .= '<div class="acf-prepend">' . acf_esc_html( $field['prepend'] ) . '</div>';
}
// range
$html .= acf_get_text_input( $atts );
// Calculate input width based on the largest possible input character length.
// Also take into account the step size for decimal steps minus - 1.5 chars for leading "0.".
$len = max(
strlen( strval( $field['min'] ) ),
strlen( strval( $field['max'] ) )
);
if ( floatval( $atts['step'] ) < 1 ) {
$len += strlen( strval( $field['step'] ) ) - 1.5;
}
// input
$html .= acf_get_text_input(
array(
'type' => 'number',
'id' => $atts['id'] . '-alt',
'value' => $atts['value'],
'step' => $atts['step'],
// 'min' => $atts['min'], // removed to avoid browser validation errors
// 'max' => $atts['max'],
'style' => 'width: ' . ( 1.8 + $len * 0.7 ) . 'em;',
)
);
// append
if ( $field['append'] !== '' ) {
$html .= '<div class="acf-append">' . acf_esc_html( $field['append'] ) . '</div>';
}
// close
$html .= '</div>';
// return
echo $html; //phpcs:ignore WordPress.Security.EscapeOutput -- Only populated with already escaped HTML.
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'number',
'name' => 'default_value',
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Minimum Value', 'acf' ),
'instructions' => '',
'type' => 'number',
'name' => 'min',
'placeholder' => '0',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Maximum Value', 'acf' ),
'instructions' => '',
'type' => 'number',
'name' => 'max',
'placeholder' => '100',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Step Size', 'acf' ),
'instructions' => '',
'type' => 'number',
'name' => 'step',
'placeholder' => '1',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Prepend', 'acf' ),
'instructions' => __( 'Appears before the input', 'acf' ),
'type' => 'text',
'name' => 'prepend',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Append', 'acf' ),
'instructions' => __( 'Appears after the input', 'acf' ),
'type' => 'text',
'name' => 'append',
)
);
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'number', 'null' ),
'required' => ! empty( $field['required'] ),
'minimum' => empty( $field['min'] ) ? 0 : (int) $field['min'],
'maximum' => empty( $field['max'] ) ? 100 : (int) $field['max'],
);
if ( isset( $field['default_value'] ) && is_numeric( $field['default_value'] ) ) {
$schema['default'] = (int) $field['default_value'];
}
return $schema;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'acf_field_range' );
endif; // class_exists check

View File

@@ -0,0 +1,888 @@
<?php
if ( ! class_exists( 'acf_field_relationship' ) ) :
class acf_field_relationship extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*/
public function initialize() {
$this->name = 'relationship';
$this->label = __( 'Relationship', 'acf' );
$this->category = 'relational';
$this->description = __( 'A dual-column interface to select one or more posts, pages, or custom post type items to create a relationship with the item that you\'re currently editing. Includes options to search and filter.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-relationship.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/relationship/', 'docs', 'field-type-selection' );
$this->defaults = array(
'post_type' => array(),
'taxonomy' => array(),
'min' => 0,
'max' => 0,
'filters' => array( 'search', 'post_type', 'taxonomy' ),
'elements' => array(),
'return_format' => 'object',
'bidirectional_target' => array(),
);
add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_relation_conditional_choices' ), 10, 3 );
// extra
add_action( 'wp_ajax_acf/fields/relationship/query', array( $this, 'ajax_query' ) );
add_action( 'wp_ajax_nopriv_acf/fields/relationship/query', array( $this, 'ajax_query' ) );
}
/**
* Filters choices in relation conditions.
*
* @since 6.3
*
* @param array $choices The selected choice.
* @param array $conditional_field The conditional field settings object.
* @param string $rule_value The rule value.
* @return array
*/
public function render_field_relation_conditional_choices( $choices, $conditional_field, $rule_value ) {
if ( ! is_array( $conditional_field ) || $conditional_field['type'] !== 'relationship' ) {
return $choices;
}
if ( ! empty( $rule_value ) ) {
$post_title = get_the_title( $rule_value );
$choices = array( $rule_value => $post_title );
}
return $choices;
}
/**
* description
*
* @type function
* @date 16/12/2015
* @since 5.3.2
*
* @param $post_id (int)
* @return $post_id (int)
*/
function input_admin_enqueue_scripts() {
// localize
acf_localize_text(
array(
// 'Minimum values reached ( {min} values )' => __('Minimum values reached ( {min} values )', 'acf'),
'Maximum values reached ( {max} values )' => __( 'Maximum values reached ( {max} values )', 'acf' ),
'Loading' => __( 'Loading', 'acf' ),
'No matches found' => __( 'No matches found', 'acf' ),
)
);
}
/**
* Returns AJAX results for the Relationship field.
*
* @since 5.0.0
*
* @return void
*/
public function ajax_query() {
$nonce = acf_request_arg( 'nonce', '' );
$key = acf_request_arg( 'field_key', '' );
$conditional_logic = (bool) acf_request_arg( 'conditional_logic', false );
if ( $conditional_logic ) {
if ( ! acf_current_user_can_admin() ) {
die();
}
// Use the standard ACF admin nonce.
$nonce = '';
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic ) ) {
die();
}
acf_send_ajax_results( $this->get_ajax_query( $_POST ) );
}
/**
* This function will return an array of data formatted for use in a select2 AJAX response
*
* @since 5.0.9
*
* @param array $options An array of options for the query.
* @return array
*/
public function get_ajax_query( $options = array() ) {
// defaults
$options = wp_parse_args(
$options,
array(
'post_id' => 0,
's' => '',
'field_key' => '',
'paged' => 1,
'post_type' => '',
'include' => '',
'taxonomy' => '',
)
);
// load field
$field = acf_get_field( $options['field_key'] );
if ( ! $field ) {
return false;
}
// vars
$results = array();
$args = array();
$s = false;
$is_search = false;
// paged
$args['posts_per_page'] = 20;
$args['paged'] = intval( $options['paged'] );
// search
if ( $options['s'] !== '' && empty( $options['include'] ) ) {
// strip slashes (search may be integer)
$s = wp_unslash( strval( $options['s'] ) );
// update vars
$args['s'] = $s;
$is_search = true;
}
// post_type
if ( ! empty( $options['post_type'] ) ) {
$args['post_type'] = acf_get_array( $options['post_type'] );
} elseif ( ! empty( $field['post_type'] ) ) {
$args['post_type'] = acf_get_array( $field['post_type'] );
} else {
$args['post_type'] = acf_get_post_types();
}
// post status
if ( ! empty( $options['post_status'] ) ) {
$args['post_status'] = acf_get_array( $options['post_status'] );
} elseif ( ! empty( $field['post_status'] ) ) {
$args['post_status'] = acf_get_array( $field['post_status'] );
}
// taxonomy
if ( ! empty( $options['taxonomy'] ) ) {
// vars
$term = acf_decode_taxonomy_term( $options['taxonomy'] );
// tax query
$args['tax_query'] = array();
// append
$args['tax_query'][] = array(
'taxonomy' => $term['taxonomy'],
'field' => 'slug',
'terms' => $term['term'],
);
} elseif ( ! empty( $field['taxonomy'] ) ) {
// vars
$terms = acf_decode_taxonomy_terms( $field['taxonomy'] );
// append to $args
$args['tax_query'] = array(
'relation' => 'OR',
);
// now create the tax queries
foreach ( $terms as $k => $v ) {
$args['tax_query'][] = array(
'taxonomy' => $k,
'field' => 'slug',
'terms' => $v,
);
}
}
if ( ! empty( $options['include'] ) ) {
// If we have an include, we need to return only the selected posts.
$args['post__in'] = array( $options['include'] );
}
// filters
$args = apply_filters( 'acf/fields/relationship/query', $args, $field, $options['post_id'] );
$args = apply_filters( 'acf/fields/relationship/query/name=' . $field['name'], $args, $field, $options['post_id'] );
$args = apply_filters( 'acf/fields/relationship/query/key=' . $field['key'], $args, $field, $options['post_id'] );
// get posts grouped by post type
$groups = acf_get_grouped_posts( $args );
// bail early if no posts
if ( empty( $groups ) ) {
return false;
}
// loop
foreach ( array_keys( $groups ) as $group_title ) {
// vars
$posts = acf_extract_var( $groups, $group_title );
// data
$data = array(
'text' => $group_title,
'children' => array(),
);
// convert post objects to post titles
foreach ( array_keys( $posts ) as $post_id ) {
$posts[ $post_id ] = $this->get_post_title( $posts[ $post_id ], $field, $options['post_id'] );
}
// order posts by search
if ( $is_search && empty( $args['orderby'] ) && isset( $args['s'] ) ) {
$posts = acf_order_by_search( $posts, $args['s'] );
}
// append to $data
foreach ( array_keys( $posts ) as $post_id ) {
$data['children'][] = $this->get_post_result( $post_id, $posts[ $post_id ] );
}
// append to $results
$results[] = $data;
}
// add as optgroup or results
if ( count( $args['post_type'] ) == 1 ) {
$results = $results[0]['children'];
}
// vars
$response = array(
'results' => $results,
'limit' => $args['posts_per_page'],
);
// return
return $response;
}
/**
* This function will return an array containing id, text and maybe description data
*
* @type function
* @date 7/07/2016
* @since 5.4.0
*
* @param $id (mixed)
* @param $text (string)
* @return (array)
*/
function get_post_result( $id, $text ) {
// vars
$result = array(
'id' => $id,
'text' => $text,
);
// return
return $result;
}
/**
* This function returns the HTML for a result
*
* @type function
* @date 1/11/2013
* @since 5.0.0
*
* @param $post (object)
* @param $field (array)
* @param $post_id (int) the post_id to which this value is saved to
* @return (string)
*/
function get_post_title( $post, $field, $post_id = 0, $is_search = 0 ) {
// get post_id
if ( ! $post_id ) {
$post_id = acf_get_form_data( 'post_id' );
}
// vars
$title = acf_get_post_title( $post, $is_search );
// featured_image
if ( acf_in_array( 'featured_image', $field['elements'] ) ) {
// vars
$class = 'thumbnail';
$thumbnail = acf_get_post_thumbnail( $post->ID, array( 17, 17 ) );
// icon
if ( $thumbnail['type'] == 'icon' ) {
$class .= ' -' . $thumbnail['type'];
}
// append
$title = '<div class="' . $class . '">' . $thumbnail['html'] . '</div>' . $title;
}
// filters
$title = apply_filters( 'acf/fields/relationship/result', $title, $post, $field, $post_id );
$title = apply_filters( 'acf/fields/relationship/result/name=' . $field['_name'], $title, $post, $field, $post_id );
$title = apply_filters( 'acf/fields/relationship/result/key=' . $field['key'], $title, $post, $field, $post_id );
// return
return $title;
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$post_type = acf_get_array( $field['post_type'] );
$taxonomy = acf_get_array( $field['taxonomy'] );
$filters = acf_get_array( $field['filters'] );
// filters
$filter_count = count( $filters );
$filter_post_type_choices = array();
$filter_taxonomy_choices = array();
// post_type filter
if ( in_array( 'post_type', $filters ) ) {
$filter_post_type_choices = array(
'' => __( 'Select post type', 'acf' ),
) + acf_get_pretty_post_types( $post_type );
}
// taxonomy filter
if ( in_array( 'taxonomy', $filters ) ) {
$term_choices = array();
$filter_taxonomy_choices = array(
'' => __( 'Select taxonomy', 'acf' ),
);
// check for specific taxonomy setting
if ( $taxonomy ) {
$terms = acf_get_encoded_terms( $taxonomy );
$term_choices = acf_get_choices_from_terms( $terms, 'slug' );
// if no terms were specified, find all terms
} else {
// restrict taxonomies by the post_type selected
$term_args = array();
if ( $post_type ) {
$term_args['taxonomy'] = acf_get_taxonomies(
array(
'post_type' => $post_type,
)
);
}
// get terms
$terms = acf_get_grouped_terms( $term_args );
$term_choices = acf_get_choices_from_grouped_terms( $terms, 'slug' );
}
// append term choices
$filter_taxonomy_choices = $filter_taxonomy_choices + $term_choices;
}
// div attributes
$atts = array(
'id' => $field['id'],
'class' => "acf-relationship {$field['class']}",
'data-min' => $field['min'],
'data-max' => $field['max'],
'data-s' => '',
'data-paged' => 1,
'data-post_type' => '',
'data-taxonomy' => '',
'data-nonce' => wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] ),
);
?>
<div <?php echo acf_esc_attrs( $atts ); ?>>
<?php
acf_hidden_input(
array(
'name' => $field['name'],
'value' => '',
)
);
?>
<?php
/* filters */
if ( $filter_count ) :
?>
<div class="filters -f<?php echo esc_attr( $filter_count ); ?>">
<?php
/* search */
if ( in_array( 'search', $filters ) ) :
?>
<div class="filter -search">
<?php
acf_text_input(
array(
'placeholder' => __( 'Search...', 'acf' ),
'data-filter' => 's',
)
);
?>
</div>
<?php
endif;
/* post_type */
if ( in_array( 'post_type', $filters ) ) :
?>
<div class="filter -post_type">
<?php
acf_select_input(
array(
'choices' => $filter_post_type_choices,
'data-filter' => 'post_type',
)
);
?>
</div>
<?php
endif;
/* post_type */
if ( in_array( 'taxonomy', $filters ) ) :
?>
<div class="filter -taxonomy">
<?php
acf_select_input(
array(
'choices' => $filter_taxonomy_choices,
'data-filter' => 'taxonomy',
)
);
?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="selection">
<div class="choices">
<ul class="acf-bl list choices-list"></ul>
</div>
<div class="values">
<ul class="acf-bl list values-list">
<?php
if ( ! empty( $field['value'] ) ) :
// get posts
$posts = acf_get_posts(
array(
'post__in' => $field['value'],
'post_type' => $field['post_type'],
)
);
// loop
foreach ( $posts as $post ) :
?>
<li>
<?php
acf_hidden_input(
array(
'name' => $field['name'] . '[]',
'value' => $post->ID,
)
);
?>
<span tabindex="0" data-id="<?php echo esc_attr( $post->ID ); ?>" class="acf-rel-item acf-rel-item-remove">
<?php echo acf_esc_html( $this->get_post_title( $post, $field ) ); ?>
<a href="#" class="acf-icon -minus small dark" data-name="remove_item"></a>
</span>
</li>
<?php endforeach; ?>
<?php endif; ?>
</ul>
</div>
</div>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Post Type', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'post_type',
'choices' => acf_get_pretty_post_types(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'All post types', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Post Status', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'post_status',
'choices' => acf_get_pretty_post_statuses(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'Any post status', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Taxonomy', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'taxonomy',
'choices' => acf_get_taxonomy_terms(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'All taxonomies', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Filters', 'acf' ),
'instructions' => '',
'type' => 'checkbox',
'name' => 'filters',
'choices' => array(
'search' => __( 'Search', 'acf' ),
'post_type' => __( 'Post Type', 'acf' ),
'taxonomy' => __( 'Taxonomy', 'acf' ),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'return_format',
'choices' => array(
'object' => __( 'Post Object', 'acf' ),
'id' => __( 'Post ID', 'acf' ),
),
'layout' => 'horizontal',
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
$field['min'] = empty( $field['min'] ) ? '' : $field['min'];
$field['max'] = empty( $field['max'] ) ? '' : $field['max'];
acf_render_field_setting(
$field,
array(
'label' => __( 'Minimum Posts', 'acf' ),
'instructions' => '',
'type' => 'number',
'name' => 'min',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Maximum Posts', 'acf' ),
'instructions' => '',
'type' => 'number',
'name' => 'max',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Elements', 'acf' ),
'instructions' => __( 'Selected elements will be displayed in each result', 'acf' ),
'type' => 'checkbox',
'name' => 'elements',
'choices' => array(
'featured_image' => __( 'Featured Image', 'acf' ),
),
)
);
}
/**
* Renders the field settings used in the "Advanced" tab.
*
* @since 6.2
*
* @param array $field The field settings array.
* @return void
*/
public function render_field_advanced_settings( $field ) {
acf_render_bidirectional_field_settings( $field );
}
/**
* This filter is applied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// bail early if no value
if ( empty( $value ) ) {
return $value;
}
// force value to array
$value = acf_get_array( $value );
// convert to int
$value = array_map( 'intval', $value );
// load posts if needed
if ( $field['return_format'] == 'object' ) {
// get posts
$value = acf_get_posts(
array(
'post__in' => $value,
'post_type' => $field['post_type'],
)
);
}
// return
return $value;
}
/**
* description
*
* @type function
* @date 11/02/2014
* @since 5.0.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function validate_value( $valid, $value, $field, $input ) {
// default
if ( empty( $value ) || ! is_array( $value ) ) {
$value = array();
}
// min
if ( count( $value ) < $field['min'] ) {
$valid = _n( '%1$s requires at least %2$s selection', '%1$s requires at least %2$s selections', $field['min'], 'acf' );
$valid = sprintf( $valid, $field['label'], $field['min'] );
}
// return
return $valid;
}
/**
* Filters the field value before it is saved into the database.
*
* @since 3.6
*
* @param mixed $value The value which will be saved in the database.
* @param integer $post_id The post_id of which the value will be saved.
* @param array $field The field array holding all the field options.
*
* @return mixed $value The modified value.
*/
public function update_value( $value, $post_id, $field ) {
// Bail early if no value.
if ( empty( $value ) ) {
acf_update_bidirectional_values( array(), $post_id, $field );
return $value;
}
// Format array of values.
// - ensure each value is an id.
// - Parse each id as string for SQL LIKE queries.
if ( acf_is_sequential_array( $value ) ) {
$value = array_map( 'acf_idval', $value );
$value = array_map( 'strval', $value );
// Parse single value for id.
} else {
$value = acf_idval( $value );
}
acf_update_bidirectional_values( acf_get_array( $value ), $post_id, $field );
// Return value.
return $value;
}
/**
* Validates relationship fields updated via the REST API.
*
* @param boolean $valid The current validity booleean
* @param integer $value The value of the field
* @param array $field The field array
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
return acf_get_field_type( 'post_object' )->validate_rest_value( $valid, $value, $field );
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'integer', 'array', 'null' ),
'required' => ! empty( $field['required'] ),
'items' => array(
'type' => 'integer',
),
);
if ( empty( $field['allow_null'] ) ) {
$schema['minItems'] = 1;
}
if ( ! empty( $field['min'] ) ) {
$schema['minItems'] = (int) $field['min'];
}
if ( ! empty( $field['max'] ) ) {
$schema['maxItems'] = (int) $field['max'];
}
return $schema;
}
/**
* @see \acf_field::get_rest_links()
* @param mixed $value The raw (unformatted) field value.
* @param integer|string $post_id
* @param array $field
* @return array
*/
public function get_rest_links( $value, $post_id, array $field ) {
$links = array();
if ( empty( $value ) ) {
return $links;
}
foreach ( (array) $value as $object_id ) {
if ( ! $post_type = get_post_type( $object_id ) or ! $post_type = get_post_type_object( $post_type ) ) {
continue;
}
$rest_base = acf_get_object_type_rest_base( $post_type );
$links[] = array(
'rel' => $post_type->name === 'attachment' ? 'acf:attachment' : 'acf:post',
'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
'embeddable' => true,
);
}
return $links;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'acf_field_relationship' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,769 @@
<?php
if ( ! class_exists( 'acf_field_select' ) ) :
class acf_field_select extends acf_field {
/**
* Sets up the field type data.
*
* @since 5.0.0
*
* @return void
*/
public function initialize() {
$this->name = 'select';
$this->label = _x( 'Select', 'noun', 'acf' );
$this->category = 'choice';
$this->description = __( 'A dropdown list with a selection of choices that you specify.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-select.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/select/', 'docs', 'field-type-selection' );
$this->defaults = array(
'multiple' => 0,
'allow_null' => 0,
'choices' => array(),
'default_value' => '',
'ui' => 0,
'ajax' => 0,
'placeholder' => '',
'return_format' => 'value',
'create_options' => 0,
'save_options' => 0,
);
add_action( 'wp_ajax_acf/fields/select/query', array( $this, 'ajax_query' ) );
add_action( 'wp_ajax_nopriv_acf/fields/select/query', array( $this, 'ajax_query' ) );
}
/**
* Enqueues admin scripts for the Select field.
*
* @since 5.3.2
*
* @return void
*/
public function input_admin_enqueue_scripts() {
// Bail early if not enqueuing select2.
if ( ! acf_get_setting( 'enqueue_select2' ) ) {
return;
}
global $wp_scripts;
$min = defined( 'ACF_DEVELOPMENT_MODE' ) && ACF_DEVELOPMENT_MODE ? '' : '.min';
$major = acf_get_setting( 'select2_version' );
// attempt to find 3rd party Select2 version
// - avoid including v3 CSS when v4 JS is already enqueued.
if ( isset( $wp_scripts->registered['select2'] ) ) {
$major = (int) $wp_scripts->registered['select2']->ver;
}
if ( $major === 3 ) {
// Use v3 if necessary.
$version = '3.5.2';
$script = acf_get_url( "assets/inc/select2/3/select2{$min}.js" );
$style = acf_get_url( 'assets/inc/select2/3/select2.css' );
} else {
// Default to v4.
$version = '4.0.13';
$script = acf_get_url( "assets/inc/select2/4/select2.full{$min}.js" );
$style = acf_get_url( "assets/inc/select2/4/select2{$min}.css" );
}
wp_enqueue_script( 'select2', $script, array( 'jquery' ), $version );
wp_enqueue_style( 'select2', $style, '', $version );
acf_localize_data(
array(
'select2L10n' => array(
'matches_1' => _x( 'One result is available, press enter to select it.', 'Select2 JS matches_1', 'acf' ),
/* translators: %d - number of results available in select field */
'matches_n' => _x( '%d results are available, use up and down arrow keys to navigate.', 'Select2 JS matches_n', 'acf' ),
'matches_0' => _x( 'No matches found', 'Select2 JS matches_0', 'acf' ),
'input_too_short_1' => _x( 'Please enter 1 or more characters', 'Select2 JS input_too_short_1', 'acf' ),
/* translators: %d - number of characters to enter into select field input */
'input_too_short_n' => _x( 'Please enter %d or more characters', 'Select2 JS input_too_short_n', 'acf' ),
'input_too_long_1' => _x( 'Please delete 1 character', 'Select2 JS input_too_long_1', 'acf' ),
/* translators: %d - number of characters that should be removed from select field */
'input_too_long_n' => _x( 'Please delete %d characters', 'Select2 JS input_too_long_n', 'acf' ),
'selection_too_long_1' => _x( 'You can only select 1 item', 'Select2 JS selection_too_long_1', 'acf' ),
/* translators: %d - maximum number of items that can be selected in the select field */
'selection_too_long_n' => _x( 'You can only select %d items', 'Select2 JS selection_too_long_n', 'acf' ),
'load_more' => _x( 'Loading more results&hellip;', 'Select2 JS load_more', 'acf' ),
'searching' => _x( 'Searching&hellip;', 'Select2 JS searching', 'acf' ),
'load_fail' => _x( 'Loading failed', 'Select2 JS load_fail', 'acf' ),
),
)
);
}
/**
* AJAX handler for getting Select field choices.
*
* @since 5.0.0
*
* @return void
*/
public function ajax_query() {
$nonce = acf_request_arg( 'nonce', '' );
$key = acf_request_arg( 'field_key', '' );
$is_field_key = acf_is_field_key( $key );
// Back-compat for field settings.
if ( ! $is_field_key ) {
if ( ! acf_current_user_can_admin() ) {
die();
}
$nonce = '';
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key, $is_field_key ) ) {
die();
}
acf_send_ajax_results( $this->get_ajax_query( $_POST ) );
}
/**
* This function will return an array of data formatted for use in a select2 AJAX response
*
* @since 5.0.9
*
* @param array $options An array of options.
* @return array A select2 compatible array of options.
*/
public function get_ajax_query( $options = array() ) {
$options = acf_parse_args(
$options,
array(
'post_id' => 0,
's' => '',
'field_key' => '',
'paged' => 1,
)
);
$shortcut = apply_filters( 'acf/fields/select/query', array(), $options );
$shortcut = apply_filters( 'acf/fields/select/query/key=' . $options['field_key'], $shortcut, $options );
if ( ! empty( $shortcut ) ) {
return $shortcut;
}
// load field.
$field = acf_get_field( $options['field_key'] );
if ( ! $field ) {
return false;
}
// get choices.
$choices = acf_get_array( $field['choices'] );
if ( empty( $field['choices'] ) ) {
return false;
}
$results = array();
$s = null;
// search.
if ( $options['s'] !== '' ) {
// strip slashes (search may be integer)
$s = strval( $options['s'] );
$s = wp_unslash( $s );
}
foreach ( $field['choices'] as $k => $v ) {
// ensure $v is a string.
$v = strval( $v );
// if searching, but doesn't exist.
if ( is_string( $s ) && stripos( $v, $s ) === false ) {
continue;
}
// append results.
$results[] = array(
'id' => $k,
'text' => $v,
);
}
$response = array(
'results' => $results,
);
return $response;
}
/**
* Creates the HTML interface for the field.
*
* @since 3.6
*
* @param array $field An array holding all the field's data.
* @return void
*/
public function render_field( $field ) {
$value = acf_get_array( $field['value'] );
$choices = acf_get_array( $field['choices'] );
if ( empty( $field['placeholder'] ) ) {
$field['placeholder'] = _x( 'Select', 'verb', 'acf' );
}
// Add empty value (allows '' to be selected).
if ( empty( $value ) ) {
$value = array( '' );
}
// prepend empty choice
// - only for single selects
// - have tried array_merge but this causes keys to re-index if is numeric (post ID's)
if ( $field['allow_null'] && ! $field['multiple'] ) {
$choices = array( '' => "- {$field['placeholder']} -" ) + $choices;
}
// clean up choices if using ajax
if ( $field['ui'] && $field['ajax'] ) {
$minimal = array();
foreach ( $value as $key ) {
if ( isset( $choices[ $key ] ) ) {
$minimal[ $key ] = $choices[ $key ];
}
}
$choices = $minimal;
}
$select = array(
'id' => $field['id'],
'class' => $field['class'],
'name' => $field['name'],
'data-ui' => $field['ui'],
'data-ajax' => $field['ajax'],
'data-multiple' => $field['multiple'],
'data-placeholder' => $field['placeholder'],
'data-allow_null' => $field['allow_null'],
);
if ( ! empty( $field['aria-label'] ) ) {
$select['aria-label'] = $field['aria-label'];
}
if ( $field['multiple'] ) {
$select['multiple'] = 'multiple';
$select['size'] = 5;
$select['name'] .= '[]';
// Reduce size to single line if UI.
if ( $field['ui'] ) {
$select['size'] = 1;
}
}
if ( ! empty( $field['create_options'] ) && $field['ui'] ) {
$select['data-create_options'] = true;
}
// special atts
if ( ! empty( $field['readonly'] ) ) {
$select['readonly'] = 'readonly';
}
if ( ! empty( $field['disabled'] ) ) {
$select['disabled'] = 'disabled';
}
if ( ! empty( $field['ajax_action'] ) ) {
$select['data-ajax_action'] = $field['ajax_action'];
}
if ( ! empty( $field['nonce'] ) ) {
$select['data-nonce'] = $field['nonce'];
}
if ( $field['ajax'] && empty( $field['nonce'] ) && acf_is_field_key( $field['key'] ) ) {
$select['data-nonce'] = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
}
if ( ! empty( $field['hide_search'] ) ) {
$select['data-minimum-results-for-search'] = '-1';
}
// Hidden input is needed to allow validation to see <select> element with no selected value.
if ( $field['multiple'] || $field['ui'] ) {
acf_hidden_input(
array(
'id' => $field['id'] . '-input',
'name' => $field['name'],
)
);
}
$select['value'] = $value;
$select['choices'] = $choices;
if ( ! empty( $field['create_options'] ) && $field['ui'] && is_array( $field['value'] ) ) {
foreach ( $field['value'] as $value ) {
// Already exists in choices.
if ( isset( $field['choices'][ $value ] ) ) {
continue;
}
$option = esc_attr( $value );
$select['choices'][ $option ] = $option;
}
}
acf_select_input( $select );
}
/**
* Renders the field settings used in the "General" tab.
*
* @since 3.6
*
* @param array $field An array holding all the field's data.
* @return void
*/
public function render_field_settings( $field ) {
// encode choices (convert from array)
$field['choices'] = acf_encode_choices( $field['choices'] );
$field['default_value'] = acf_encode_choices( $field['default_value'], false );
// choices
acf_render_field_setting(
$field,
array(
'label' => __( 'Choices', 'acf' ),
'instructions' => __( 'Enter each choice on a new line.', 'acf' ) . '<br />' . __( 'For more control, you may specify both a value and label like this:', 'acf' ) . '<br /><span class="acf-field-setting-example">' . __( 'red : Red', 'acf' ) . '</span>',
'name' => 'choices',
'type' => 'textarea',
)
);
// default_value
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Enter each default value on a new line', 'acf' ),
'name' => 'default_value',
'type' => 'textarea',
)
);
// return_format
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'instructions' => __( 'Specify the value returned', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'layout' => 'horizontal',
'choices' => array(
'value' => __( 'Value', 'acf' ),
'label' => __( 'Label', 'acf' ),
'array' => __( 'Both (Array)', 'acf' ),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Select Multiple', 'acf' ),
'instructions' => 'Allow content editors to select multiple values',
'name' => 'multiple',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
public function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Null', 'acf' ),
'instructions' => '',
'name' => 'allow_null',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
public function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Stylized UI', 'acf' ),
'instructions' => __( 'Use a stylized checkbox using select2', 'acf' ),
'name' => 'ui',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Use AJAX to lazy load choices?', 'acf' ),
'instructions' => '',
'name' => 'ajax',
'type' => 'true_false',
'ui' => 1,
'conditions' => array(
'field' => 'ui',
'operator' => '==',
'value' => 1,
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Create Options', 'acf' ),
'instructions' => __( 'Allow content editors to create new options by typing in the Select input. Multiple options can be created from a comma separated string.', 'acf' ),
'name' => 'create_options',
'type' => 'true_false',
'ui' => 1,
'conditions' => array(
array(
'field' => 'ui',
'operator' => '==',
'value' => 1,
),
array(
'field' => 'multiple',
'operator' => '==',
'value' => 1,
),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Save Options', 'acf' ),
'instructions' => __( 'Save created options back to the "Choices" setting in the field definition.', 'acf' ),
'name' => 'save_options',
'type' => 'true_false',
'ui' => 1,
'conditions' => array(
array(
'field' => 'ui',
'operator' => '==',
'value' => 1,
),
array(
'field' => 'multiple',
'operator' => '==',
'value' => 1,
),
array(
'field' => 'create_options',
'operator' => '==',
'value' => 1,
),
),
)
);
}
/**
* Filters the $value after it is loaded from the db.
*
* @since 3.6
*
* @param mixed $value The value found in the database.
* @param integer|string $post_id The post_id from which the value was loaded.
* @param array $field The field array holding all the field options.
* @return mixed
*/
public function load_value( $value, $post_id, $field ) {
// Return an array when field is set for multiple.
if ( $field['multiple'] ) {
if ( acf_is_empty( $value ) ) {
return array();
}
return acf_array( $value );
}
// Otherwise, return a single value.
return acf_unarray( $value );
}
/**
* Filters the $field before it is saved to the database.
*
* @since 3.6
*
* @param array $field The field array holding all the field options.
* @return array
*/
public function update_field( $field ) {
// Decode choices (convert to array).
$field['choices'] = acf_decode_choices( $field['choices'] );
$field['default_value'] = acf_decode_choices( $field['default_value'], true );
// Convert back to string for single selects.
if ( ! $field['multiple'] ) {
$field['default_value'] = acf_unarray( $field['default_value'] );
}
return $field;
}
/**
* Filters the $value before it is updated in the db.
*
* @since 3.6
*
* @param mixed $value The value which will be saved in the database.
* @param integer|string $post_id The post_id of which the value will be saved.
* @param array $field The field array holding all the field options.
*
* @return mixed
*/
public function update_value( $value, $post_id, $field ) {
// Bail early if no value.
if ( empty( $value ) ) {
return $value;
}
// Format array of values.
// - Parse each value as string for SQL LIKE queries.
if ( is_array( $value ) ) {
$value = array_map( 'strval', $value );
}
// Save custom options back to the field definition if configured.
if ( ! empty( $field['save_options'] ) && is_array( $value ) ) {
// Get the raw field, using the ID if present or the key otherwise (i.e. when using JSON).
$selector = $field['ID'] ? $field['ID'] : $field['key'];
$field = acf_get_field( $selector );
// Bail if we don't have a valid field or field ID (JSON only).
if ( empty( $field['ID'] ) ) {
return $value;
}
foreach ( $value as $v ) {
// Ignore if the option already exists.
if ( isset( $field['choices'][ $v ] ) ) {
continue;
}
// Unslash (fixes serialize single quote issue) and sanitize.
$v = wp_unslash( $v );
$v = sanitize_text_field( $v );
// Append to the field choices.
$field['choices'][ $v ] = $v;
}
acf_update_field( $field );
}
return $value;
}
/**
* Translates the field settings.
*
* @since 5.3.2
*
* @param array $field The main field array.
* @return array
*/
public function translate_field( $field ) {
$field['choices'] = acf_translate( $field['choices'] );
return $field;
}
/**
* Filters the $value after it is loaded from the db, and before it is returned to the template.
*
* @since 3.6
*
* @param mixed $value The value which was loaded from the database.
* @param integer|string $post_id The post_id from which the value was loaded.
* @param array $field The field array holding all the field options.
*
* @return mixed
*/
public function format_value( $value, $post_id, $field ) {
if ( is_array( $value ) ) {
foreach ( $value as $i => $val ) {
$value[ $i ] = $this->format_value_single( $val, $post_id, $field );
}
} else {
$value = $this->format_value_single( $value, $post_id, $field );
}
return $value;
}
/**
* Formats the value when the select is not a multi-select.
*
* @since 3.6
*
* @param mixed $value The value to format.
* @param integer|string $post_id The post_id from which the value was loaded.
* @param array $field The field array holding all the field options.
* @return mixed
*/
public function format_value_single( $value, $post_id, $field ) {
// Bail early if is empty.
if ( acf_is_empty( $value ) ) {
return $value;
}
$label = acf_maybe_get( $field['choices'], $value, $value );
if ( $field['return_format'] === 'label' ) {
$value = $label;
} elseif ( $field['return_format'] === 'array' ) {
$value = array(
'value' => $value,
'label' => $label,
);
}
return $value;
}
/**
* Validates select fields updated via the REST API.
*
* @param boolean $valid The current validity booleean
* @param integer $value The value of the field
* @param array $field The field array
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
// rest_validate_request_arg() handles the other types, we just worry about strings.
if ( is_null( $value ) || is_array( $value ) ) {
return $valid;
}
$option_keys = array_diff(
array_keys( $field['choices'] ),
array_values( $field['choices'] )
);
$allowed = empty( $option_keys ) ? $field['choices'] : $option_keys;
if ( ! in_array( $value, $allowed ) ) {
$param = sprintf( '%s[%s]', $field['prefix'], $field['name'] );
$data = array(
'param' => $param,
'value' => $value,
);
$error = sprintf(
__( '%1$s is not one of %2$s', 'acf' ),
$param,
implode( ', ', $allowed )
);
return new WP_Error( 'rest_invalid_param', $error, $data );
}
return $valid;
}
/**
* Formats the choices available for the REST API.
*
* @since 6.2
*
* @param array $choices The choices for the field.
* @return array
*/
public function format_rest_choices( $choices ) {
$keys = array_keys( $choices );
$values = array_values( $choices );
$int_choices = array();
if ( array_diff( $keys, $values ) ) {
// User has specified custom keys.
$choices = $keys;
} else {
// Default keys, same as value.
$choices = $values;
}
// Assume everything is a string by default.
$choices = array_map( 'strval', $choices );
// Also allow integers if is_numeric().
foreach ( $choices as $choice ) {
if ( is_numeric( $choice ) ) {
$int_choices[] = (int) $choice;
}
}
return array_merge( $choices, $int_choices );
}
/**
* Return the schema array for the REST API.
*
* @param array $field The main field array.
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'string', 'array', 'int', 'null' ),
'required' => ! empty( $field['required'] ),
'items' => array(
'type' => array( 'string', 'int' ),
'enum' => $this->format_rest_choices( $field['choices'] ),
),
);
if ( empty( $field['allow_null'] ) ) {
$schema['minItems'] = 1;
}
if ( empty( $field['multiple'] ) ) {
$schema['maxItems'] = 1;
}
if ( isset( $field['default_value'] ) && '' !== $field['default_value'] ) {
$schema['default'] = $field['default_value'];
}
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_select' );
endif; // class_exists check

View File

@@ -0,0 +1,74 @@
<?php
if ( ! class_exists( 'acf_field_separator' ) ) :
class acf_field_separator extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'separator';
$this->label = __( 'Separator', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-separator.png';
$this->supports = array( 'required' => false );
$this->category = 'layout';
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
/* do nothing */
}
/**
* This filter is appied to the $field after it is loaded from the database
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $field - the field array holding all the field options
*
* @return $field - the field array holding all the field options
*/
function load_field( $field ) {
// remove name to avoid caching issue
$field['name'] = '';
// remove required to avoid JS issues
$field['required'] = 0;
// set value other than 'null' to avoid ACF loading / caching issue
$field['value'] = false;
// return
return $field;
}
}
// initialize
acf_register_field_type( 'acf_field_separator' );
endif; // class_exists check

View File

@@ -0,0 +1,166 @@
<?php
if ( ! class_exists( 'acf_field_tab' ) ) :
class acf_field_tab extends acf_field {
public $show_in_rest = false;
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'tab';
$this->label = __( 'Tab', 'acf' );
$this->category = 'layout';
$this->description = __( 'Allows you to group fields into tabbed sections in the edit screen. Useful for keeping fields organized and structured.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-tabs.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/tab/', 'docs', 'field-type-selection' );
$this->supports = array(
'required' => false,
'bindings' => false,
);
$this->defaults = array(
'placement' => 'top',
'endpoint' => 0, // added in 5.2.8
'selected' => 0, // added in 6.3
);
}
/**
* Output the HTML required for a tab.
*
* @since 3.6
*
* @param array $field An array of the field data.
*/
public function render_field( $field ) {
$atts = array(
'href' => '',
'class' => 'acf-tab-button',
'data-placement' => $field['placement'],
'data-endpoint' => $field['endpoint'],
'data-key' => $field['key'],
'data-selected' => $field['selected'],
);
if ( isset( $field['unique_tab_key'] ) && ! empty( $field['unique_tab_key'] ) ) {
$atts['data-unique-tab-key'] = $field['unique_tab_key'];
}
if ( isset( $field['settings-type'] ) ) {
$atts['data-settings-type'] = acf_slugify( $field['settings-type'] );
$atts['class'] .= ' acf-settings-type-' . acf_slugify( $field['settings-type'] );
}
if ( isset( $field['class'] ) && ! empty( $field['class'] ) ) {
$atts['class'] .= ' ' . $field['class'];
}
?>
<a <?php echo acf_esc_attrs( $atts ); ?>><?php echo acf_esc_html( $field['label'] ); ?></a>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field_settings( $field ) {
/*
// message
$message = '';
$message .= '<p>' . __( 'Use "Tab Fields" to better organize your edit screen by grouping fields together.', 'acf') . '</p>';
$message .= '<p>' . __( 'All fields following this "tab field" (or until another "tab field" is defined) will be grouped together using this field\'s label as the tab heading.','acf') . '</p>';
// default_value
acf_render_field_setting( $field, array(
'label' => __('Instructions','acf'),
'instructions' => '',
'name' => 'notes',
'type' => 'message',
'message' => $message,
));
*/
// preview_size
acf_render_field_setting(
$field,
array(
'label' => __( 'Placement', 'acf' ),
'type' => 'select',
'name' => 'placement',
'choices' => array(
'top' => __( 'Top aligned', 'acf' ),
'left' => __( 'Left aligned', 'acf' ),
),
)
);
// endpoint
acf_render_field_setting(
$field,
array(
'label' => __( 'New Tab Group', 'acf' ),
'instructions' => __( 'Start a new group of tabs at this tab.', 'acf' ),
'name' => 'endpoint',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* This filter is appied to the $field after it is loaded from the database
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $field - the field array holding all the field options
*
* @return $field - the field array holding all the field options
*/
function load_field( $field ) {
// remove name to avoid caching issue
$field['name'] = '';
// remove instructions
$field['instructions'] = '';
// remove required to avoid JS issues
$field['required'] = 0;
// set value other than 'null' to avoid ACF loading / caching issue
$field['value'] = false;
// return
return $field;
}
}
// initialize
acf_register_field_type( 'acf_field_tab' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,960 @@
<?php
if ( ! class_exists( 'acf_field_taxonomy' ) ) :
class acf_field_taxonomy extends acf_field {
// vars
var $save_post_terms = array();
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*/
public function initialize() {
$this->name = 'taxonomy';
$this->label = __( 'Taxonomy', 'acf' );
$this->category = 'relational';
$this->description = __( 'Allows the selection of one or more taxonomy terms based on the criteria and options specified in the fields settings.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-taxonomy.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/taxonomy/', 'docs', 'field-type-selection' );
$this->defaults = array(
'taxonomy' => 'category',
'field_type' => 'checkbox',
'multiple' => 0,
'allow_null' => 0,
'return_format' => 'id',
'add_term' => 1, // 5.2.3
'load_terms' => 0, // 5.2.7
'save_terms' => 0, // 5.2.7
'bidirectional_target' => array(),
);
// Register filter variations.
acf_add_filter_variations( 'acf/fields/taxonomy/query', array( 'name', 'key' ), 1 );
acf_add_filter_variations( 'acf/fields/taxonomy/result', array( 'name', 'key' ), 2 );
// ajax
add_action( 'wp_ajax_acf/fields/taxonomy/query', array( $this, 'ajax_query' ) );
add_action( 'wp_ajax_nopriv_acf/fields/taxonomy/query', array( $this, 'ajax_query' ) );
add_action( 'wp_ajax_acf/fields/taxonomy/add_term', array( $this, 'ajax_add_term' ) );
add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_taxonomy_conditional_choices' ), 10, 3 );
// actions
add_action( 'acf/save_post', array( $this, 'save_post' ), 15, 1 );
}
/**
* Returns AJAX results for the Taxonomy field.
*
* @since 5.0.0
*
* @return void
*/
public function ajax_query() {
$nonce = acf_request_arg( 'nonce', '' );
$key = acf_request_arg( 'field_key', '' );
$conditional_logic = (bool) acf_request_arg( 'conditional_logic', false );
if ( $conditional_logic ) {
if ( ! acf_current_user_can_admin() ) {
die();
}
// Use the standard ACF admin nonce.
$nonce = '';
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic ) ) {
die();
}
acf_send_ajax_results( $this->get_ajax_query( $_POST ) );
}
/**
* This function will return an array of data formatted for use in a select2 AJAX response
*
* @type function
* @date 15/10/2014
* @since 5.0.9
*
* @param $options (array)
* @return (array)
*/
function get_ajax_query( $options = array() ) {
$options = acf_parse_args(
$options,
array(
'post_id' => 0,
's' => '',
'field_key' => '',
'term_id' => '',
'include' => '',
'paged' => 1,
)
);
$field = acf_get_field( $options['field_key'] );
if ( ! $field ) {
return false;
}
// if options include isset, then we are loading a specific term.
if ( ! empty( $options['include'] ) ) {
$options['term_id'] = $options['include'];
// paged should be 1.
$options['paged'] = 1;
}
// Bail early if taxonomy does not exist.
if ( ! taxonomy_exists( $field['taxonomy'] ) ) {
return false;
}
$results = array();
$is_hierarchical = is_taxonomy_hierarchical( $field['taxonomy'] );
$is_pagination = ( $options['paged'] > 0 );
$is_search = false;
$limit = 20;
$offset = 20 * ( $options['paged'] - 1 );
$args = array(
'taxonomy' => $field['taxonomy'],
'hide_empty' => false,
);
// Don't bother for hierarchial terms, we will need to load all terms anyway.
if ( $is_pagination && ! $is_hierarchical ) {
$args['number'] = $limit;
$args['offset'] = $offset;
}
// search
if ( $options['s'] !== '' ) {
// strip slashes (search may be integer)
$s = wp_unslash( strval( $options['s'] ) );
$args['search'] = isset( $options['term_id'] ) && $options['term_id'] ? '' : $s;
$is_search = true;
}
$args = apply_filters( 'acf/fields/taxonomy/query', $args, $field, $options['post_id'] );
if ( ! empty( $options['include'] ) ) {
// Limit search to a specific id if one is provided.
$args['include'] = $options['include'];
}
$terms = acf_get_terms( $args );
// Sort hierachial.
if ( $is_hierarchical ) {
$limit = acf_maybe_get( $args, 'number', $limit );
$offset = acf_maybe_get( $args, 'offset', $offset );
$parent = acf_maybe_get( $args, 'parent', 0 );
$parent = acf_maybe_get( $args, 'child_of', $parent );
// This will fail if a search has taken place because parents wont exist.
if ( ! $is_search ) {
$ordered_terms = _get_term_children( $parent, $terms, $field['taxonomy'] );
// Check for empty array. Possible if parent did not exist within original data.
if ( ! empty( $ordered_terms ) ) {
$terms = $ordered_terms;
}
}
// Fake pagination.
if ( $is_pagination && ! $options['include'] ) {
$terms = array_slice( $terms, $offset, $limit );
}
}
// Append to r.
foreach ( $terms as $term ) {
// Add to json.
$results[] = array(
'id' => $term->term_id,
'text' => $this->get_term_title( $term, $field, $options['post_id'], true ),
);
}
$response = array(
'results' => $results,
'limit' => $limit,
);
return $response;
}
/**
* Returns the Term's title displayed in the field UI.
*
* @since 5.0.0
*
* @param WP_Term $term The term object.
* @param array $field The field settings.
* @param mixed $post_id The post_id being edited.
* @param boolean $unescape Should we return an unescaped post title.
* @return string
*/
function get_term_title( $term, $field, $post_id = 0, $unescape = false ) {
$title = acf_get_term_title( $term );
// Default $post_id to current post being edited.
$post_id = $post_id ? $post_id : acf_get_form_data( 'post_id' );
// unescape for select2 output which handles the escaping.
if ( $unescape ) {
$title = html_entity_decode( $title );
}
/**
* Filters the term title.
*
* @date 1/11/2013
* @since 5.0.0
*
* @param string $title The term title.
* @param WP_Term $term The term object.
* @param array $field The field settings.
* @param (int|string) $post_id The post_id being edited.
*/
return apply_filters( 'acf/fields/taxonomy/result', $title, $term, $field, $post_id );
}
/**
* This function will return an array of terms for a given field value
*
* @type function
* @date 13/06/2014
* @since 5.0.0
*
* @param $value (array)
* @return $value
*/
function get_terms( $value, $taxonomy = 'category' ) {
// load terms in 1 query to save multiple DB calls from following code
if ( count( $value ) > 1 ) {
$terms = acf_get_terms(
array(
'taxonomy' => $taxonomy,
'include' => $value,
'hide_empty' => false,
)
);
}
// update value to include $post
foreach ( array_keys( $value ) as $i ) {
$value[ $i ] = get_term( $value[ $i ], $taxonomy );
}
// filter out null values
$value = array_filter( $value );
// return
return $value;
}
/**
* This filter is appied to the $value after it is loaded from the db
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value - the value found in the database
* @param $post_id - the post_id from which the value was loaded from
* @param $field - the field array holding all the field options
*
* @return $value - the value to be saved in te database
*/
function load_value( $value, $post_id, $field ) {
// get valid terms
$value = acf_get_valid_terms( $value, $field['taxonomy'] );
// load_terms
if ( $field['load_terms'] ) {
// Decode $post_id for $type and $id.
$decoded = acf_decode_post_id( $post_id );
$type = $decoded['type'];
$id = $decoded['id'];
if ( $type === 'block' ) {
// Get parent block...
}
// get terms
$term_ids = wp_get_object_terms(
$id,
$field['taxonomy'],
array(
'fields' => 'ids',
'orderby' => 'none',
)
);
// bail early if no terms
if ( empty( $term_ids ) || is_wp_error( $term_ids ) ) {
return false;
}
// sort
if ( ! empty( $value ) ) {
$order = array();
foreach ( $term_ids as $i => $v ) {
$order[ $i ] = array_search( $v, $value );
}
array_multisort( $order, $term_ids );
}
// update value
$value = $term_ids;
}
// convert back from array if neccessary
if ( $field['field_type'] == 'select' || $field['field_type'] == 'radio' ) {
$value = array_shift( $value );
}
// return
return $value;
}
/**
* Filters the field value before it is saved into the database.
*
* @since 3.6
*
* @param mixed $value The value which will be saved in the database.
* @param integer $post_id The post_id of which the value will be saved.
* @param array $field The field array holding all the field options.
* @return mixed $value The modified value.
*/
public function update_value( $value, $post_id, $field ) {
if ( is_array( $value ) ) {
$value = array_filter( $value );
}
acf_update_bidirectional_values( acf_get_array( $value ), $post_id, $field, 'term' );
// save_terms if enabled.
if ( $field['save_terms'] ) {
// vars
$taxonomy = $field['taxonomy'];
// force value to array.
$term_ids = acf_get_array( $value );
// convert to int.
$term_ids = array_map( 'intval', $term_ids );
// get existing term id's (from a previously saved field).
$old_term_ids = isset( $this->save_post_terms[ $taxonomy ] ) ? $this->save_post_terms[ $taxonomy ] : array();
// append
$this->save_post_terms[ $taxonomy ] = array_merge( $old_term_ids, $term_ids );
// if called directly from frontend update_field().
if ( ! did_action( 'acf/save_post' ) ) {
$this->save_post( $post_id );
return $value;
}
}
return $value;
}
/**
* This function will save any terms in the save_post_terms array
*
* @since 5.0.9
*
* @param mixed $post_id The ACF post ID to save to.
* @return void
*/
public function save_post( $post_id ) {
// Check for saved terms.
if ( ! empty( $this->save_post_terms ) ) {
/**
* Determine object ID allowing for non "post" $post_id (user, taxonomy, etc).
* Although not fully supported by WordPress, non "post" objects may use the term relationships table.
* Sharing taxonomies across object types is discouraged, but unique taxonomies work well.
* Note: Do not attempt to restrict to "post" only. This has been attempted in 5.8.9 and later reverted.
*/
$decoded = acf_decode_post_id( $post_id );
$type = $decoded['type'];
$id = $decoded['id'];
if ( $type === 'block' ) {
// Get parent block...
}
// Loop over taxonomies and save terms.
foreach ( $this->save_post_terms as $taxonomy => $term_ids ) {
wp_set_object_terms( $id, $term_ids, $taxonomy, false );
}
// Reset storage.
$this->save_post_terms = array();
}
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// bail early if no value
if ( empty( $value ) ) {
return false;
}
// force value to array
$value = acf_get_array( $value );
// load posts if needed
if ( $field['return_format'] == 'object' ) {
// get posts
$value = $this->get_terms( $value, $field['taxonomy'] );
}
// convert back from array if neccessary
if ( $field['field_type'] == 'select' || $field['field_type'] == 'radio' ) {
$value = array_shift( $value );
}
// return
return $value;
}
/**
* Renders the Taxonomy field.
*
* @since 3.6
*
* @param array $field The field settings array.
* @return void
*/
public function render_field( $field ) {
// force value to array
$field['value'] = acf_get_array( $field['value'] );
$nonce = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
// vars
$div = array(
'class' => 'acf-taxonomy-field',
'data-save' => $field['save_terms'],
'data-ftype' => $field['field_type'],
'data-taxonomy' => $field['taxonomy'],
'data-allow_null' => $field['allow_null'],
'data-nonce' => $nonce,
);
// get taxonomy
$taxonomy = get_taxonomy( $field['taxonomy'] );
// bail early if taxonomy does not exist
if ( ! $taxonomy ) {
return;
}
?>
<div <?php echo acf_esc_attrs( $div ); ?>>
<?php if ( $field['add_term'] && current_user_can( $taxonomy->cap->manage_terms ) ) : ?>
<div class="acf-actions -hover">
<a href="#" class="acf-icon -plus acf-js-tooltip small" data-name="add" title="<?php echo esc_attr( $taxonomy->labels->add_new_item ); ?>"></a>
</div>
<?php
endif;
if ( $field['field_type'] == 'select' ) {
$field['multiple'] = 0;
$this->render_field_select( $field, $nonce );
} elseif ( $field['field_type'] == 'multi_select' ) {
$field['multiple'] = 1;
$this->render_field_select( $field, $nonce );
} elseif ( $field['field_type'] == 'radio' ) {
$this->render_field_checkbox( $field );
} elseif ( $field['field_type'] == 'checkbox' ) {
$this->render_field_checkbox( $field );
}
?>
</div>
<?php
}
/**
* Create the HTML interface for your field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_select( $field, $nonce ) {
// Change Field into a select
$field['type'] = 'select';
$field['ui'] = 1;
$field['ajax'] = 1;
$field['nonce'] = $nonce;
$field['choices'] = array();
// value
if ( ! empty( $field['value'] ) ) {
// get terms
$terms = $this->get_terms( $field['value'], $field['taxonomy'] );
// set choices
if ( ! empty( $terms ) ) {
foreach ( array_keys( $terms ) as $i ) {
// vars
$term = acf_extract_var( $terms, $i );
// append to choices
$field['choices'][ $term->term_id ] = $this->get_term_title( $term, $field );
}
}
}
// render select
acf_render_field( $field );
}
/**
* Create the HTML interface for your field
*
* @since 3.6
*
* @param array $field an array holding all the field's data.
*/
public function render_field_checkbox( $field ) {
// hidden input.
acf_hidden_input(
array(
'type' => 'hidden',
'name' => $field['name'],
)
);
// checkbox saves an array.
if ( $field['field_type'] == 'checkbox' ) {
$field['name'] .= '[]';
}
// taxonomy.
$taxonomy_obj = get_taxonomy( $field['taxonomy'] );
// include walker.
acf_include( 'includes/walkers/class-acf-walker-taxonomy-field.php' );
// vars.
$args = array(
'taxonomy' => $field['taxonomy'],
'show_option_none' => sprintf( _x( 'No %s', 'No Terms', 'acf' ), $taxonomy_obj->labels->name ),
'hide_empty' => false,
'style' => 'none',
'walker' => new ACF_Taxonomy_Field_Walker( $field ),
);
// filter for 3rd party customization.
$args = apply_filters( 'acf/fields/taxonomy/wp_list_categories', $args, $field );
$args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/name=' . $field['_name'], $args, $field );
$args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/key=' . $field['key'], $args, $field );
?>
<div class="categorychecklist-holder">
<ul class="acf-checkbox-list acf-bl">
<?php wp_list_categories( $args ); ?>
</ul>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Taxonomy', 'acf' ),
'instructions' => __( 'Select the taxonomy to be displayed', 'acf' ),
'type' => 'select',
'name' => 'taxonomy',
'choices' => acf_get_taxonomy_labels(),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Create Terms', 'acf' ),
'instructions' => __( 'Allow new terms to be created whilst editing', 'acf' ),
'name' => 'add_term',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Save Terms', 'acf' ),
'instructions' => __( 'Connect selected terms to the post', 'acf' ),
'name' => 'save_terms',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Load Terms', 'acf' ),
'instructions' => __( 'Load value from posts terms', 'acf' ),
'name' => 'load_terms',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Value', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'return_format',
'choices' => array(
'object' => __( 'Term Object', 'acf' ),
'id' => __( 'Term ID', 'acf' ),
),
'layout' => 'horizontal',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Appearance', 'acf' ),
'instructions' => __( 'Select the appearance of this field', 'acf' ),
'type' => 'select',
'name' => 'field_type',
'optgroup' => true,
'choices' => array(
__( 'Multiple Values', 'acf' ) => array(
'checkbox' => __( 'Checkbox', 'acf' ),
'multi_select' => __( 'Multi Select', 'acf' ),
),
__( 'Single Value', 'acf' ) => array(
'radio' => __( 'Radio Buttons', 'acf' ),
'select' => _x( 'Select', 'noun', 'acf' ),
),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Null', 'acf' ),
'instructions' => '',
'name' => 'allow_null',
'type' => 'true_false',
'ui' => 1,
'conditions' => array(
'field' => 'field_type',
'operator' => '!=',
'value' => 'checkbox',
),
)
);
}
/**
* Renders the field settings used in the "Advanced" tab.
*
* @since 6.2
*
* @param array $field The field settings array.
* @return void
*/
public function render_field_advanced_settings( $field ) {
acf_render_bidirectional_field_settings( $field );
}
/**
* Filters choices in taxonomy conditions.
*
* @since 6.3
*
* @param array $choices The selected choice.
* @param array $conditional_field The conditional field settings object.
* @param string $rule_value The rule value.
* @return mixed
*/
public function render_field_taxonomy_conditional_choices( $choices, $conditional_field, $rule_value ) {
if ( is_array( $conditional_field ) && $conditional_field['type'] === 'taxonomy' ) {
if ( ! empty( $rule_value ) ) {
$term = get_term( $rule_value );
$choices = array( $rule_value => $term->name );
}
}
return $choices;
}
/**
* AJAX handler for adding Taxonomy field terms.
*
* @since 5.2.3
*
* @return void
*/
public function ajax_add_term() {
$args = acf_request_args(
array(
'nonce' => '',
'field_key' => '',
'term_name' => '',
'term_parent' => '',
)
);
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true ) ) {
die();
}
// load field
$field = acf_get_field( $args['field_key'] );
if ( ! $field ) {
die();
}
// vars
$taxonomy_obj = get_taxonomy( $field['taxonomy'] );
$taxonomy_label = $taxonomy_obj->labels->singular_name;
// validate cap
// note: this situation should never occur due to condition of the add new button
if ( ! current_user_can( $taxonomy_obj->cap->manage_terms ) ) {
wp_send_json_error(
array(
'error' => sprintf( __( 'User unable to add new %s', 'acf' ), $taxonomy_label ),
)
);
}
// save?
if ( $args['term_name'] ) {
// exists
if ( term_exists( $args['term_name'], $field['taxonomy'], $args['term_parent'] ) ) {
wp_send_json_error(
array(
'error' => sprintf( __( '%s already exists', 'acf' ), $taxonomy_label ),
)
);
}
// vars
$extra = array();
if ( $args['term_parent'] ) {
$extra['parent'] = (int) $args['term_parent'];
}
// insert
$data = wp_insert_term( $args['term_name'], $field['taxonomy'], $extra );
// error
if ( is_wp_error( $data ) ) {
wp_send_json_error(
array(
'error' => $data->get_error_message(),
)
);
}
// load term
$term = get_term( $data['term_id'] );
// prepend ancenstors count to term name
$prefix = '';
$ancestors = get_ancestors( $term->term_id, $term->taxonomy );
if ( ! empty( $ancestors ) ) {
$prefix = str_repeat( '- ', count( $ancestors ) );
}
// success
wp_send_json_success(
array(
'message' => sprintf( __( '%s added', 'acf' ), $taxonomy_label ),
'term_id' => $term->term_id,
'term_name' => $term->name,
'term_label' => $prefix . $term->name,
'term_parent' => $term->parent,
)
);
}
?>
<form method="post">
<?php
acf_render_field_wrap(
array(
'label' => __( 'Name', 'acf' ),
'name' => 'term_name',
'type' => 'text',
)
);
if ( is_taxonomy_hierarchical( $field['taxonomy'] ) ) {
$choices = array();
$response = $this->get_ajax_query( $args );
if ( $response ) {
foreach ( $response['results'] as $v ) {
$choices[ $v['id'] ] = $v['text'];
}
}
acf_render_field_wrap(
array(
'label' => __( 'Parent', 'acf' ),
'name' => 'term_parent',
'type' => 'select',
'allow_null' => 1,
'ui' => 0,
'choices' => $choices,
)
);
}
?>
<p class="acf-submit">
<button class="acf-submit-button button button-primary" type="submit"><?php esc_html_e( 'Add', 'acf' ); ?></button>
</p>
</form><?php
// die
die;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'integer', 'array', 'null' ),
'required' => ! empty( $field['required'] ),
'items' => array(
'type' => 'integer',
),
);
if ( empty( $field['allow_null'] ) ) {
$schema['minItems'] = 1;
}
if ( in_array( $field['field_type'], array( 'radio', 'select' ) ) ) {
$schema['maxItems'] = 1;
}
return $schema;
}
/**
* @see \acf_field::get_rest_links()
* @param mixed $value The raw (unformatted) field value.
* @param integer|string $post_id
* @param array $field
* @return array
*/
public function get_rest_links( $value, $post_id, array $field ) {
$links = array();
if ( empty( $value ) ) {
return $links;
}
foreach ( (array) $value as $object_id ) {
$term = get_term( $object_id );
if ( ! $term instanceof WP_Term ) {
continue;
}
$rest_base = acf_get_object_type_rest_base( get_taxonomy( $term->taxonomy ) );
if ( ! $rest_base ) {
continue;
}
$links[] = array(
'rel' => 'acf:term',
'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
'embeddable' => true,
'taxonomy' => $term->taxonomy,
);
}
return $links;
}
}
// initialize
acf_register_field_type( 'acf_field_taxonomy' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,208 @@
<?php
if ( ! class_exists( 'acf_field_text' ) ) :
class acf_field_text extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'text';
$this->label = __( 'Text', 'acf' );
$this->description = __( 'A basic text input, useful for storing single string values.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-text.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/text/', 'docs', 'field-type-selection' );
$this->defaults = array(
'default_value' => '',
'maxlength' => '',
'placeholder' => '',
'prepend' => '',
'append' => '',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
$html = '';
// Prepend text.
if ( $field['prepend'] !== '' ) {
$field['class'] .= ' acf-is-prepended';
$html .= '<div class="acf-input-prepend">' . acf_esc_html( $field['prepend'] ) . '</div>';
}
// Append text.
if ( $field['append'] !== '' ) {
$field['class'] .= ' acf-is-appended';
$html .= '<div class="acf-input-append">' . acf_esc_html( $field['append'] ) . '</div>';
}
// Input.
$input_attrs = array();
foreach ( array( 'type', 'id', 'class', 'name', 'value', 'placeholder', 'maxlength', 'pattern', 'readonly', 'disabled', 'required' ) as $k ) {
if ( isset( $field[ $k ] ) ) {
$input_attrs[ $k ] = $field[ $k ];
}
}
if ( isset( $field['input-data'] ) && is_array( $field['input-data'] ) ) {
foreach ( $field['input-data'] as $name => $attr ) {
$input_attrs[ 'data-' . $name ] = $attr;
}
}
$html .= '<div class="acf-input-wrap">' . acf_get_text_input( acf_filter_attrs( $input_attrs ) ) . '</div>';
// Display.
echo $html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- only safe HTML output generated and escaped by functions above.
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'text',
'name' => 'default_value',
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Character Limit', 'acf' ),
'instructions' => __( 'Leave blank for no limit', 'acf' ),
'type' => 'number',
'name' => 'maxlength',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Placeholder Text', 'acf' ),
'instructions' => __( 'Appears within the input', 'acf' ),
'type' => 'text',
'name' => 'placeholder',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Prepend', 'acf' ),
'instructions' => __( 'Appears before the input', 'acf' ),
'type' => 'text',
'name' => 'prepend',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Append', 'acf' ),
'instructions' => __( 'Appears after the input', 'acf' ),
'type' => 'text',
'name' => 'append',
)
);
}
/**
* validate_value
*
* Validates a field's value.
*
* @date 29/1/19
* @since 5.7.11
*
* @param (bool|string) Whether the value is vaid or not.
* @param mixed $value The field value.
* @param array $field The field array.
* @param string $input The HTML input name.
* @return (bool|string)
*/
function validate_value( $valid, $value, $field, $input ) {
// Check maxlength
if ( $field['maxlength'] && ( acf_strlen( $value ) > $field['maxlength'] ) ) {
return sprintf( __( 'Value must not exceed %d characters', 'acf' ), $field['maxlength'] );
}
// Return.
return $valid;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
function get_rest_schema( array $field ) {
$schema = parent::get_rest_schema( $field );
if ( ! empty( $field['maxlength'] ) ) {
$schema['maxLength'] = (int) $field['maxlength'];
}
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_text' );
endif; // class_exists check

View File

@@ -0,0 +1,244 @@
<?php
if ( ! class_exists( 'acf_field_textarea' ) ) :
class acf_field_textarea extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'textarea';
$this->label = __( 'Text Area', 'acf' );
$this->description = __( 'A basic textarea input for storing paragraphs of text.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-textarea.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/textarea/', 'docs', 'field-type-selection' );
$this->defaults = array(
'default_value' => '',
'new_lines' => '',
'maxlength' => '',
'placeholder' => '',
'rows' => '',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$atts = array();
$keys = array( 'id', 'class', 'name', 'value', 'placeholder', 'rows', 'maxlength' );
$keys2 = array( 'readonly', 'disabled', 'required' );
// rows
if ( ! $field['rows'] ) {
$field['rows'] = 8;
}
// atts (value="123")
foreach ( $keys as $k ) {
if ( isset( $field[ $k ] ) ) {
$atts[ $k ] = $field[ $k ];
}
}
// atts2 (disabled="disabled")
foreach ( $keys2 as $k ) {
if ( ! empty( $field[ $k ] ) ) {
$atts[ $k ] = $k;
}
}
// remove empty atts
$atts = acf_clean_atts( $atts );
// return
acf_textarea_input( $atts );
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'textarea',
'name' => 'default_value',
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Character Limit', 'acf' ),
'instructions' => __( 'Leave blank for no limit', 'acf' ),
'type' => 'number',
'name' => 'maxlength',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Rows', 'acf' ),
'instructions' => __( 'Sets the textarea height', 'acf' ),
'type' => 'number',
'name' => 'rows',
'placeholder' => 8,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Placeholder Text', 'acf' ),
'instructions' => __( 'Appears within the input', 'acf' ),
'type' => 'text',
'name' => 'placeholder',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'New Lines', 'acf' ),
'instructions' => __( 'Controls how new lines are rendered', 'acf' ),
'type' => 'select',
'name' => 'new_lines',
'choices' => array(
'wpautop' => __( 'Automatically add paragraphs', 'acf' ),
'br' => __( 'Automatically add &lt;br&gt;', 'acf' ),
'' => __( 'No Formatting', 'acf' ),
),
)
);
}
/**
* This filter is applied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
// bail early if no value or not for template
if ( empty( $value ) || ! is_string( $value ) ) {
return $value;
}
// new lines
if ( $field['new_lines'] == 'wpautop' ) {
$value = wpautop( $value );
} elseif ( $field['new_lines'] == 'br' ) {
$value = nl2br( $value );
}
// return
return $value;
}
/**
* validate_value
*
* Validates a field's value.
*
* @date 29/1/19
* @since 5.7.11
*
* @param (bool|string) Whether the value is vaid or not.
* @param mixed $value The field value.
* @param array $field The field array.
* @param string $input The HTML input name.
* @return (bool|string)
*/
function validate_value( $valid, $value, $field, $input ) {
// Check maxlength.
if ( $field['maxlength'] && ( acf_strlen( $value ) > $field['maxlength'] ) ) {
return sprintf( __( 'Value must not exceed %d characters', 'acf' ), $field['maxlength'] );
}
// Return.
return $valid;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
function get_rest_schema( array $field ) {
$schema = parent::get_rest_schema( $field );
if ( ! empty( $field['maxlength'] ) ) {
$schema['maxLength'] = (int) $field['maxlength'];
}
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_textarea' );
endif; // class_exists check

View File

@@ -0,0 +1,195 @@
<?php
if ( ! class_exists( 'acf_field_time_picker' ) ) :
class acf_field_time_picker extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'time_picker';
$this->label = __( 'Time Picker', 'acf' );
$this->category = 'advanced';
$this->description = __( 'An interactive UI for picking a time. The time format can be customized using the field settings.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-time.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/time-picker/', 'docs', 'field-type-selection' );
$this->defaults = array(
'display_format' => 'g:i a',
'return_format' => 'g:i a',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// Set value.
$display_value = '';
if ( $field['value'] ) {
$display_value = acf_format_date( $field['value'], $field['display_format'] );
}
// Elements.
$div = array(
'class' => 'acf-time-picker acf-input-wrap',
'data-time_format' => acf_convert_time_to_js( $field['display_format'] ),
);
$hidden_input = array(
'id' => $field['id'],
'class' => 'input-alt',
'type' => 'hidden',
'name' => $field['name'],
'value' => $field['value'],
);
$text_input = array(
'class' => $field['class'] . ' input',
'type' => 'text',
'value' => $display_value,
);
foreach ( array( 'readonly', 'disabled' ) as $k ) {
if ( ! empty( $field[ $k ] ) ) {
$hidden_input[ $k ] = $k;
$text_input[ $k ] = $k;
}
}
// Output.
?>
<div <?php echo acf_esc_attrs( $div ); ?>>
<?php acf_hidden_input( $hidden_input ); ?>
<?php acf_text_input( $text_input ); ?>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
$g_i_a = date_i18n( 'g:i a' );
$H_i_s = date_i18n( 'H:i:s' );
echo '<div class="acf-field-settings-split">';
acf_render_field_setting(
$field,
array(
'label' => __( 'Display Format', 'acf' ),
'hint' => __( 'The format displayed when editing a post', 'acf' ),
'type' => 'radio',
'name' => 'display_format',
'other_choice' => 1,
'choices' => array(
'g:i a' => '<span>' . $g_i_a . '</span><code>g:i a</code>',
'H:i:s' => '<span>' . $H_i_s . '</span><code>H:i:s</code>',
'other' => '<span>' . __( 'Custom:', 'acf' ) . '</span>',
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'hint' => __( 'The format returned via template functions', 'acf' ),
'type' => 'radio',
'name' => 'return_format',
'other_choice' => 1,
'choices' => array(
'g:i a' => '<span>' . $g_i_a . '</span><code>g:i a</code>',
'H:i:s' => '<span>' . $H_i_s . '</span><code>H:i:s</code>',
'other' => '<span>' . __( 'Custom:', 'acf' ) . '</span>',
),
)
);
echo '</div>';
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
* @return $value (mixed) the modified value
*/
public function format_value( $value, $post_id, $field ) {
return acf_format_date( $value, $field['return_format'] );
}
/**
* This filter is applied to the $field after it is loaded from the database
* and ensures the return and display values are set.
*
* @type filter
* @since 5.11.0
*
* @param array $field The field array holding all the field options.
* @return array
*/
public function load_field( $field ) {
if ( empty( $field['display_format'] ) ) {
$field['display_format'] = $this->defaults['display_format'];
}
if ( empty( $field['return_format'] ) ) {
$field['return_format'] = $this->defaults['return_format'];
}
return $field;
}
/**
* Return the schema array for the REST API.
*
* @param array $field The field array.
* @return array
*/
public function get_rest_schema( array $field ) {
return array(
'type' => array( 'string', 'null' ),
'description' => 'A `H:i:s` formatted time string.',
'required' => ! empty( $field['required'] ),
);
}
}
// initialize
acf_register_field_type( 'acf_field_time_picker' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,301 @@
<?php
if ( ! class_exists( 'acf_field_true_false' ) ) :
class acf_field_true_false extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'true_false';
$this->label = __( 'True / False', 'acf' );
$this->category = 'choice';
$this->description = __( 'A toggle that allows you to pick a value of 1 or 0 (on or off, true or false, etc). Can be presented as a stylized switch or checkbox.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-true-false.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/true-false/', 'docs', 'field-type-selection' );
$this->defaults = array(
'default_value' => 0,
'message' => '',
'ui' => 0,
'ui_on_text' => '',
'ui_off_text' => '',
);
}
/**
* Create the HTML interface for your field
*
* @param $field - an array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// vars
$input = array(
'type' => 'checkbox',
'id' => $field['id'],
'name' => $field['name'],
'value' => '1',
'class' => $field['class'],
'autocomplete' => 'off',
);
$hidden = array(
'name' => $field['name'],
'value' => 0,
);
$active = $field['value'] ? true : false;
$switch = '';
// checked
if ( $active ) {
$input['checked'] = 'checked';
}
// ui
if ( $field['ui'] ) {
// vars
if ( $field['ui_on_text'] === '' ) {
$field['ui_on_text'] = __( 'Yes', 'acf' );
}
if ( $field['ui_off_text'] === '' ) {
$field['ui_off_text'] = __( 'No', 'acf' );
}
// update input
$input['class'] .= ' acf-switch-input';
// $input['style'] = 'display:none;';
$switch .= '<div class="acf-switch' . ( $active ? ' -on' : '' ) . '">';
$switch .= '<span class="acf-switch-on">' . $field['ui_on_text'] . '</span>';
$switch .= '<span class="acf-switch-off">' . $field['ui_off_text'] . '</span>';
$switch .= '<div class="acf-switch-slider"></div>';
$switch .= '</div>';
}
?>
<div class="acf-true-false">
<?php acf_hidden_input( $hidden ); ?>
<label>
<input <?php echo acf_esc_attrs( $input ); ?>/>
<?php
if ( $switch ) {
echo acf_esc_html( $switch );}
?>
<?php
if ( $field['message'] ) :
?>
<span class="message"><?php echo acf_esc_html( $field['message'] ); ?></span><?php endif; ?>
</label>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Message', 'acf' ),
'instructions' => __( 'Displays text alongside the checkbox', 'acf' ),
'type' => 'text',
'name' => 'message',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => '',
'type' => 'true_false',
'name' => 'default_value',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'On Text', 'acf' ),
'instructions' => __( 'Text shown when active', 'acf' ),
'type' => 'text',
'name' => 'ui_on_text',
'placeholder' => __( 'Yes', 'acf' ),
'conditions' => array(
'field' => 'ui',
'operator' => '==',
'value' => 1,
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Off Text', 'acf' ),
'instructions' => __( 'Text shown when inactive', 'acf' ),
'type' => 'text',
'name' => 'ui_off_text',
'placeholder' => __( 'No', 'acf' ),
'conditions' => array(
'field' => 'ui',
'operator' => '==',
'value' => 1,
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Stylized UI', 'acf' ),
'instructions' => __( 'Use a stylized checkbox using select2', 'acf' ),
'type' => 'true_false',
'name' => 'ui',
'ui' => 1,
'class' => 'acf-field-object-true-false-ui',
)
);
}
/**
* This filter is appied to the $value after it is loaded from the db and before it is returned to the template
*
* @type filter
* @since 3.6
* @date 23/01/13
*
* @param $value (mixed) the value which was loaded from the database
* @param $post_id (mixed) the post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value (mixed) the modified value
*/
function format_value( $value, $post_id, $field ) {
return empty( $value ) ? false : true;
}
/**
* description
*
* @type function
* @date 11/02/2014
* @since 5.0.0
*
* @param $post_id (int)
* @return $post_id (int)
*/
function validate_value( $valid, $value, $field, $input ) {
// bail early if not required
if ( ! $field['required'] ) {
return $valid;
}
// value may be '0'
if ( ! $value ) {
return false;
}
// return
return $valid;
}
/**
* This function will translate field settings
*
* @type function
* @date 8/03/2016
* @since 5.3.2
*
* @param $field (array)
* @return $field
*/
function translate_field( $field ) {
// translate
$field['message'] = acf_translate( $field['message'] );
$field['ui_on_text'] = acf_translate( $field['ui_on_text'] );
$field['ui_off_text'] = acf_translate( $field['ui_off_text'] );
// return
return $field;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'boolean', 'null' ),
'required' => ! empty( $field['required'] ),
);
if ( isset( $field['default_value'] ) && '' !== $field['default_value'] ) {
$schema['default'] = (bool) $field['default_value'];
}
return $schema;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return (bool) $value;
}
}
// initialize
acf_register_field_type( 'acf_field_true_false' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,180 @@
<?php
if ( ! class_exists( 'acf_field_url' ) ) :
/**
* The URL field class.
*/
class acf_field_url extends acf_field {
/**
* This function will setup the field type data
*
* @since 5.0.0
*/
public function initialize() {
// vars
$this->name = 'url';
$this->label = __( 'URL', 'acf' );
$this->description = __( 'A text input specifically designed for storing web addresses.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-url.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/url/', 'docs', 'field-type-selection' );
$this->defaults = array(
'default_value' => '',
'placeholder' => '',
);
$this->supports = array(
'escaping_html' => true,
);
}
/**
* Create the HTML interface for your field
*
* @since 3.6
*
* @param array $field An array holding all the field's data.
*/
public function render_field( $field ) {
$atts = array();
$keys = array( 'type', 'id', 'class', 'name', 'value', 'placeholder', 'pattern' );
$keys2 = array( 'readonly', 'disabled', 'required' );
// atts (value="123")
foreach ( $keys as $k ) {
if ( isset( $field[ $k ] ) ) {
$atts[ $k ] = $field[ $k ];
}
}
// atts2 (disabled="disabled")
foreach ( $keys2 as $k ) {
if ( ! empty( $field[ $k ] ) ) {
$atts[ $k ] = $k;
}
}
// remove empty atts
$atts = acf_clean_atts( $atts );
// render
$html = '<div class="acf-input-wrap acf-url">';
$html .= '<i class="acf-icon -globe -small"></i>' . acf_get_text_input( $atts );
$html .= '</div>';
// return
echo $html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- safe HTML, escaped by acf_get_text_input.
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @since 3.6
*
* @param array $field An array holding all the field's data.
*/
public function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'text',
'name' => 'default_value',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
public function render_field_presentation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Placeholder Text', 'acf' ),
'instructions' => __( 'Appears within the input', 'acf' ),
'type' => 'text',
'name' => 'placeholder',
)
);
}
/**
* Validate the fields value is correctly formatted as a URL
*
* @since 5.0.0
*
* @param mixed $valid The current validity of the field value. Boolean true if valid, a validation error message string if not.
* @param string $value The value of the field.
* @param array $field Field object array.
* @param string $input The form input name for this field.
* @return mixed Boolean true if valid, a validation error message string if not.
*/
public function validate_value( $valid, $value, $field, $input ) {
// bail early if empty
if ( empty( $value ) ) {
return $valid;
}
if ( strpos( $value, '://' ) !== false ) {
// url
} elseif ( strpos( $value, '//' ) === 0 ) {
// protocol relative url
} else {
$valid = __( 'Value must be a valid URL', 'acf' );
}
// return
return $valid;
}
/**
* This filter is applied to the $value after it is loaded from the db, and before it is returned to the template
*
* @since 6.2.6
*
* @param mixed $value The value which was loaded from the database.
* @param mixed $post_id The $post_id from which the value was loaded.
* @param array $field The field array holding all the field options.
* @param boolean $escape_html Should the field return a HTML safe formatted value.
* @return mixed $value The modified value
*/
public function format_value( $value, $post_id, $field, $escape_html ) {
if ( $escape_html ) {
return esc_url( $value );
}
return $value;
}
/**
* Return the schema array for the REST API.
*
* @param array $field The field object.
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = parent::get_rest_schema( $field );
$schema['format'] = 'uri';
return $schema;
}
}
// initialize
acf_register_field_type( 'acf_field_url' );
endif; // class_exists check

View File

@@ -0,0 +1,661 @@
<?php
if ( ! class_exists( 'ACF_Field_User' ) ) :
class ACF_Field_User extends ACF_Field {
/**
* Initializes the field type.
*
* @date 5/03/2014
* @since 5.0.0
*/
function initialize() {
$this->name = 'user';
$this->label = __( 'User', 'acf' );
$this->category = 'relational';
$this->description = __( 'Allows the selection of one or more users which can be used to create relationships between data objects.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-user.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/user/', 'docs', 'field-type-selection' );
$this->defaults = array(
'role' => '',
'multiple' => 0,
'allow_null' => 0,
'return_format' => 'array',
'bidirectional_target' => array(),
);
// Register filter variations.
acf_add_filter_variations( 'acf/fields/user/query', array( 'name', 'key' ), 1 );
acf_add_filter_variations( 'acf/fields/user/result', array( 'name', 'key' ), 2 );
acf_add_filter_variations( 'acf/fields/user/search_columns', array( 'name', 'key' ), 3 );
add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_user_conditional_choices' ), 10, 3 );
// Add AJAX query.
add_action( 'wp_ajax_acf/fields/user/query', array( $this, 'ajax_query' ) );
add_action( 'wp_ajax_nopriv_acf/fields/user/query', array( $this, 'ajax_query' ) );
}
/**
* Filters choices in user conditions.
*
* @since 6.3
*
* @param array $choices The selected choice.
* @param array $conditional_field The conditional field settings object.
* @param string $rule_value The rule value.
* @return array
*/
public function render_field_user_conditional_choices( $choices, $conditional_field, $rule_value ) {
if ( ! is_array( $conditional_field ) || $conditional_field['type'] !== 'user' ) {
return $choices;
}
if ( ! empty( $rule_value ) ) {
$user = acf_get_users(
array(
'include' => array( $rule_value ),
)
);
$user_result = acf_get_user_result( $user[0] );
$choices = array( $user_result['id'] => $user_result['text'] );
}
return $choices;
}
/**
* Renders the field settings HTML.
*
* @date 23/01/13
* @since 3.6.0
*
* @param array $field The ACF field.
* @return void
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Filter by Role', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'role',
'choices' => acf_get_user_role_labels(),
'multiple' => 1,
'ui' => 1,
'allow_null' => 1,
'placeholder' => __( 'All user roles', 'acf' ),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Return Format', 'acf' ),
'instructions' => '',
'type' => 'radio',
'name' => 'return_format',
'choices' => array(
'array' => __( 'User Array', 'acf' ),
'object' => __( 'User Object', 'acf' ),
'id' => __( 'User ID', 'acf' ),
),
'layout' => 'horizontal',
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Select Multiple', 'acf' ),
'instructions' => 'Allow content editors to select multiple values',
'name' => 'multiple',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* Renders the field settings used in the "Validation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_validation_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Null', 'acf' ),
'instructions' => '',
'name' => 'allow_null',
'type' => 'true_false',
'ui' => 1,
)
);
}
/**
* Renders the field settings used in the "Advanced" tab.
*
* @since 6.2
*
* @param array $field The field settings array.
* @return void
*/
public function render_field_advanced_settings( $field ) {
acf_render_bidirectional_field_settings( $field );
}
/**
* Renders the field input HTML.
*
* @since 3.6.0
*
* @param array $field The ACF field.
* @return void
*/
public function render_field( $field ) {
// Change Field into a select.
$field['type'] = 'select';
$field['ui'] = 1;
$field['ajax'] = 1;
$field['choices'] = array();
$field['nonce'] = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
// Populate choices.
if ( $field['value'] ) {
// Clean value into an array of IDs.
$user_ids = array_map( 'intval', acf_array( $field['value'] ) );
// Find users in database (ensures all results are real).
$users = acf_get_users(
array(
'include' => $user_ids,
)
);
// Append.
if ( $users ) {
foreach ( $users as $user ) {
$field['choices'][ $user->ID ] = $this->get_result( $user, $field );
}
}
}
// Render.
acf_render_field( $field );
}
/**
* Returns the result text for a given WP_User object.
*
* @date 1/11/2013
* @since 5.0.0
*
* @param WP_User $user The WP_User object.
* @param array $field The ACF field related to this query.
* @param (int|string) $post_id The post_id being edited.
* @return string
*/
function get_result( $user, $field, $post_id = 0 ) {
// Get user result item.
$item = acf_get_user_result( $user );
// Default $post_id to current post being edited.
$post_id = $post_id ? $post_id : acf_get_form_data( 'post_id' );
/**
* Filters the result text.
*
* @date 21/5/19
* @since 5.8.1
*
* @param array $args The query args.
* @param array $field The ACF field related to this query.
* @param (int|string) $post_id The post_id being edited.
*/
return apply_filters( 'acf/fields/user/result', $item['text'], $user, $field, $post_id );
}
/**
* Filters the field value after it is loaded from the database.
*
* @date 23/01/13
* @since 3.6.0
*
* @param mixed $value The field value.
* @param mixed $post_id The post ID where the value is saved.
* @param array $field The field array containing all settings.
* @return mixed
*/
function load_value( $value, $post_id, $field ) {
// Add compatibility for version 4.
if ( $value === 'null' ) {
return false;
}
return $value;
}
/**
* Filters the field value after it is loaded from the database but before it is returned to the front-end API.
*
* @date 23/01/13
* @since 3.6.0
*
* @param mixed $value The field value.
* @param mixed $post_id The post ID where the value is saved.
* @param array $field The field array containing all settings.
* @return mixed
*/
function format_value( $value, $post_id, $field ) {
// Bail early if no value.
if ( ! $value ) {
return false;
}
// Clean value into an array of IDs.
$user_ids = array_map( 'intval', acf_array( $value ) );
// Find users in database (ensures all results are real).
$users = acf_get_users(
array(
'include' => $user_ids,
)
);
// Bail early if no users found.
if ( ! $users ) {
return false;
}
// Format values using field settings.
$value = array();
foreach ( $users as $user ) {
// Return object.
if ( $field['return_format'] == 'object' ) {
$item = $user;
// Return array.
} elseif ( $field['return_format'] == 'array' ) {
$item = array(
'ID' => $user->ID,
'user_firstname' => $user->user_firstname,
'user_lastname' => $user->user_lastname,
'nickname' => $user->nickname,
'user_nicename' => $user->user_nicename,
'display_name' => $user->display_name,
'user_email' => $user->user_email,
'user_url' => $user->user_url,
'user_registered' => $user->user_registered,
'user_description' => $user->user_description,
'user_avatar' => get_avatar( $user->ID ),
);
// Return ID.
} else {
$item = $user->ID;
}
// Append item
$value[] = $item;
}
// Convert to single.
if ( ! $field['multiple'] ) {
$value = array_shift( $value );
}
// Return.
return $value;
}
/**
* Filters the field value before it is saved into the database.
*
* @since 3.6.0
*
* @param mixed $value The field value.
* @param mixed $post_id The post ID where the value is saved.
* @param array $field The field array containing all settings.
* @return mixed $value The modified value.
*/
public function update_value( $value, $post_id, $field ) {
// Bail early if no value.
if ( empty( $value ) ) {
acf_update_bidirectional_values( array(), $post_id, $field, 'user' );
return $value;
}
// Format array of values.
// - ensure each value is an id.
// - Parse each id as string for SQL LIKE queries.
if ( acf_is_sequential_array( $value ) ) {
$value = array_map( 'acf_idval', $value );
$value = array_map( 'strval', $value );
// Parse single value for id.
} else {
$value = acf_idval( $value );
}
acf_update_bidirectional_values( acf_get_array( $value ), $post_id, $field, 'user' );
// Return value.
return $value;
}
/**
* Callback for the AJAX query request.
*
* @date 24/10/13
* @since 5.0.0
*
* @param void
* @return void
*/
function ajax_query() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
// Modify Request args.
if ( isset( $_REQUEST['s'] ) ) {
$_REQUEST['search'] = sanitize_text_field( $_REQUEST['s'] );
}
if ( isset( $_REQUEST['paged'] ) ) {
$_REQUEST['page'] = absint( $_REQUEST['paged'] );
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
// Add query hooks.
add_action( 'acf/ajax/query_users/init', array( $this, 'ajax_query_init' ), 10, 2 );
add_filter( 'acf/ajax/query_users/args', array( $this, 'ajax_query_args' ), 10, 3 );
add_filter( 'acf/ajax/query_users/result', array( $this, 'ajax_query_result' ), 10, 3 );
add_filter( 'acf/ajax/query_users/search_columns', array( $this, 'ajax_query_search_columns' ), 10, 4 );
// Simulate AJAX request.
acf_get_instance( 'ACF_Ajax_Query_Users' )->request();
}
/**
* Runs during the AJAX query initialization.
*
* @date 9/3/20
* @since 5.8.8
*
* @param array $request The query request.
* @param ACF_Ajax_Query $query The query object.
* @return void
*/
function ajax_query_init( $request, $query ) {
// Require field and make sure it's a user field.
if ( ! $query->field || $query->field['type'] !== $this->name ) {
$query->send( new WP_Error( 'acf_missing_field', __( 'Error loading field.', 'acf' ), array( 'status' => 404 ) ) );
}
// Verify that this is a legitimate request using a separate nonce from the main AJAX nonce.
$nonce = acf_request_arg( 'nonce', '' );
$key = acf_request_arg( 'field_key', '' );
if ( ! acf_verify_ajax( $nonce, $key, true ) ) {
$query->send( new WP_Error( 'acf_invalid_request', __( 'Invalid request.', 'acf' ), array( 'status' => 404 ) ) );
}
}
/**
* Filters the AJAX query args.
*
* @date 9/3/20
* @since 5.8.8
*
* @param array $args The query args.
* @param array $request The query request.
* @param ACF_Ajax_Query $query The query object.
* @return array
*/
function ajax_query_args( $args, $request, $query ) {
// Add specific roles.
if ( $query->field['role'] ) {
$args['role__in'] = acf_array( $query->field['role'] );
}
/**
* Filters the query args.
*
* @date 21/5/19
* @since 5.8.1
*
* @param array $args The query args.
* @param array $field The ACF field related to this query.
* @param (int|string) $post_id The post_id being edited.
*/
return apply_filters( 'acf/fields/user/query', $args, $query->field, $query->post_id );
}
/**
* Filters the WP_User_Query search columns.
*
* @date 9/3/20
* @since 5.8.8
*
* @param array $columns An array of column names to be searched.
* @param string $search The search term.
* @param WP_User_Query $WP_User_Query The WP_User_Query instance.
* @return array
*/
function ajax_query_search_columns( $columns, $search, $WP_User_Query, $query ) {
/**
* Filters the column names to be searched.
*
* @date 21/5/19
* @since 5.8.1
*
* @param array $columns An array of column names to be searched.
* @param string $search The search term.
* @param WP_User_Query $WP_User_Query The WP_User_Query instance.
* @param array $field The ACF field related to this query.
*/
return apply_filters( 'acf/fields/user/search_columns', $columns, $search, $WP_User_Query, $query->field );
}
/**
* Filters the AJAX Query result.
*
* @date 9/3/20
* @since 5.8.8
*
* @param array $item The choice id and text.
* @param WP_User $user The user object.
* @param ACF_Ajax_Query $query The query object.
* @return array
*/
function ajax_query_result( $item, $user, $query ) {
/**
* Filters the result text.
*
* @date 21/5/19
* @since 5.8.1
*
* @param string The result text.
* @param WP_User $user The user object.
* @param array $field The ACF field related to this query.
* @param (int|string) $post_id The post_id being edited.
*/
$item['text'] = apply_filters( 'acf/fields/user/result', $item['text'], $user, $query->field, $query->post_id );
return $item;
}
/**
* Return an array of data formatted for use in a select2 AJAX response.
*
* @date 15/10/2014
* @since 5.0.9
* @deprecated 5.8.9
*
* @param array $args An array of query args.
* @return array
*/
function get_ajax_query( $options = array() ) {
_deprecated_function( __FUNCTION__, '5.8.9' );
return array();
}
/**
* Filters the WP_User_Query search columns.
*
* @date 15/10/2014
* @since 5.0.9
* @deprecated 5.8.9
*
* @param array $columns An array of column names to be searched.
* @param string $search The search term.
* @param WP_User_Query $WP_User_Query The WP_User_Query instance.
* @return array
*/
function user_search_columns( $columns, $search, $WP_User_Query ) {
_deprecated_function( __FUNCTION__, '5.8.9' );
return $columns;
}
/**
* Validates user fields updated via the REST API.
*
* @param boolean $valid The current validity booleean
* @param integer $value The value of the field
* @param array $field The field array
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
if ( is_null( $value ) ) {
return $valid;
}
$param = sprintf( '%s[%s]', $field['prefix'], $field['name'] );
$data = array( 'param' => $param );
$value = is_array( $value ) ? $value : array( $value );
$invalid_users = array();
$insufficient_roles = array();
foreach ( $value as $user_id ) {
$user_data = get_userdata( $user_id );
if ( ! $user_data ) {
$invalid_users[] = $user_id;
continue;
}
if ( empty( $field['role'] ) ) {
continue;
}
$has_roles = count( array_intersect( $field['role'], $user_data->roles ) );
if ( ! $has_roles ) {
$insufficient_roles[] = $user_id;
}
}
if ( count( $invalid_users ) ) {
$error = sprintf(
__( '%1$s must have a valid user ID.', 'acf' ),
$param
);
$data['value'] = $invalid_users;
return new WP_Error( 'rest_invalid_param', $error, $data );
}
if ( count( $insufficient_roles ) ) {
$error = sprintf(
_n(
'%1$s must have a user with the %2$s role.',
'%1$s must have a user with one of the following roles: %2$s',
count( $field['role'] ),
'acf'
),
$param,
count( $field['role'] ) > 1 ? implode( ', ', $field['role'] ) : $field['role'][0]
);
$data['value'] = $insufficient_roles;
return new WP_Error( 'rest_invalid_param', $error, $data );
}
return $valid;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'integer', 'array', 'null' ),
'required' => ! empty( $field['required'] ),
'items' => array(
'type' => 'integer',
),
);
if ( empty( $field['allow_null'] ) ) {
$schema['minItems'] = 1;
}
if ( empty( $field['multiple'] ) ) {
$schema['maxItems'] = 1;
}
return $schema;
}
/**
* @see \acf_field::get_rest_links()
* @param mixed $value The raw (unformatted) field value.
* @param integer|string $post_id
* @param array $field
* @return array
*/
public function get_rest_links( $value, $post_id, array $field ) {
$links = array();
if ( empty( $value ) ) {
return $links;
}
foreach ( (array) $value as $object_id ) {
$links[] = array(
'rel' => 'acf:user',
'href' => rest_url( '/wp/v2/users/' . $object_id ),
'embeddable' => true,
);
}
return $links;
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return acf_format_numerics( $value );
}
}
// initialize
acf_register_field_type( 'ACF_Field_User' );
endif; // class_exists check

View File

@@ -0,0 +1,417 @@
<?php
if ( ! class_exists( 'acf_field_wysiwyg' ) ) :
class acf_field_wysiwyg extends acf_field {
/**
* This function will setup the field type data
*
* @type function
* @date 5/03/2014
* @since 5.0.0
*
* @param n/a
* @return n/a
*/
function initialize() {
// vars
$this->name = 'wysiwyg';
$this->label = __( 'WYSIWYG Editor', 'acf' );
$this->category = 'content';
$this->description = __( 'Displays the WordPress WYSIWYG editor as seen in Posts and Pages allowing for a rich text-editing experience that also allows for multimedia content.', 'acf' ) . ' ' . __( 'We do not recommend using this field in ACF Blocks.', 'acf' );
$this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-wysiwyg.png';
$this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/wysiwyg-editor/', 'docs', 'field-type-selection' );
$this->defaults = array(
'tabs' => 'all',
'toolbar' => 'full',
'media_upload' => 1,
'default_value' => '',
'delay' => 0,
);
$this->supports = array(
'escaping_html' => true,
);
// add acf_the_content filters
$this->add_filters();
// actions
add_action( 'acf/enqueue_uploader', array( $this, 'acf_enqueue_uploader' ) );
}
/**
* This function will add filters to 'acf_the_content'
*
* @type function
* @date 20/09/2016
* @since 5.4.0
*
* @param n/a
* @return n/a
*/
function add_filters() {
// WordPress 5.5 introduced new function for applying image tags.
$wp_filter_content_tags = function_exists( 'wp_filter_content_tags' ) ? 'wp_filter_content_tags' : 'wp_make_content_images_responsive';
// Mimic filters added to "the_content" in "wp-includes/default-filters.php".
add_filter( 'acf_the_content', 'capital_P_dangit', 11 );
// add_filter( 'acf_the_content', 'do_blocks', 9 ); Not yet supported.
add_filter( 'acf_the_content', 'wptexturize' );
add_filter( 'acf_the_content', 'convert_smilies', 20 );
add_filter( 'acf_the_content', 'wpautop' );
add_filter( 'acf_the_content', 'shortcode_unautop' );
// add_filter( 'acf_the_content', 'prepend_attachment' ); Causes double image on attachment page.
add_filter( 'acf_the_content', $wp_filter_content_tags );
add_filter( 'acf_the_content', 'do_shortcode', 11 );
// Mimic filters added to "the_content" in "wp-includes/class-wp-embed.php"
if ( isset( $GLOBALS['wp_embed'] ) ) {
add_filter( 'acf_the_content', array( $GLOBALS['wp_embed'], 'run_shortcode' ), 8 );
add_filter( 'acf_the_content', array( $GLOBALS['wp_embed'], 'autoembed' ), 8 );
}
}
/**
* This function will return an array of toolbars for the WYSIWYG field
*
* @type function
* @date 18/04/2014
* @since 5.0.0
*
* @param n/a
* @return (array)
*/
function get_toolbars() {
// vars
$editor_id = 'acf_content';
$toolbars = array();
// mce buttons (Full)
$mce_buttons = array( 'formatselect', 'bold', 'italic', 'bullist', 'numlist', 'blockquote', 'alignleft', 'aligncenter', 'alignright', 'link', 'wp_more', 'spellchecker', 'fullscreen', 'wp_adv' );
$mce_buttons_2 = array( 'strikethrough', 'hr', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo', 'wp_help' );
// mce buttons (Basic)
$teeny_mce_buttons = array( 'bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'alignleft', 'aligncenter', 'alignright', 'undo', 'redo', 'link', 'fullscreen' );
// Full
$toolbars['Full'] = array(
1 => apply_filters( 'mce_buttons', $mce_buttons, $editor_id ),
2 => apply_filters( 'mce_buttons_2', $mce_buttons_2, $editor_id ),
3 => apply_filters( 'mce_buttons_3', array(), $editor_id ),
4 => apply_filters( 'mce_buttons_4', array(), $editor_id ),
);
// Basic
$toolbars['Basic'] = array(
1 => apply_filters( 'teeny_mce_buttons', $teeny_mce_buttons, $editor_id ),
);
// Filter for 3rd party
$toolbars = apply_filters( 'acf/fields/wysiwyg/toolbars', $toolbars );
// return
return $toolbars;
}
/**
* Registers toolbars data for the WYSIWYG field.
*
* @type function
* @date 16/12/2015
* @since 5.3.2
*
* @param void
* @return void
*/
function acf_enqueue_uploader() {
// vars
$data = array();
$toolbars = $this->get_toolbars();
// loop
if ( $toolbars ) {
foreach ( $toolbars as $label => $rows ) {
// vars
$key = $label;
$key = sanitize_title( $key );
$key = str_replace( '-', '_', $key );
// append
$data[ $key ] = array();
if ( $rows ) {
foreach ( $rows as $i => $row ) {
$data[ $key ][ $i ] = implode( ',', $row );
}
}
}
}
// localize
acf_localize_data(
array(
'toolbars' => $data,
)
);
}
/**
* Create the HTML interface for your field
*
* @param array $field An array holding all the field's data
*
* @type action
* @since 3.6
* @date 23/01/13
*/
function render_field( $field ) {
// enqueue
acf_enqueue_uploader();
// vars
$id = uniqid( 'acf-editor-' );
$default_editor = 'html';
$show_tabs = true;
// get height
$height = acf_get_user_setting( 'wysiwyg_height', 300 );
$height = max( $height, 300 ); // minimum height is 300
// detect mode
if ( ! user_can_richedit() ) {
$show_tabs = false;
} elseif ( $field['tabs'] == 'visual' ) {
// case: visual tab only
$default_editor = 'tinymce';
$show_tabs = false;
} elseif ( $field['tabs'] == 'text' ) {
// case: text tab only
$show_tabs = false;
} elseif ( wp_default_editor() == 'tinymce' ) {
// case: both tabs
$default_editor = 'tinymce';
}
// must be logged in to upload
if ( ! current_user_can( 'upload_files' ) ) {
$field['media_upload'] = 0;
}
// mode
$switch_class = ( $default_editor === 'html' ) ? 'html-active' : 'tmce-active';
// filter
add_filter( 'acf_the_editor_content', 'format_for_editor', 10, 2 );
$field['value'] = is_string( $field['value'] ) ? $field['value'] : '';
$field['value'] = apply_filters( 'acf_the_editor_content', $field['value'], $default_editor );
// attr
$wrap = array(
'id' => 'wp-' . $id . '-wrap',
'class' => 'acf-editor-wrap wp-core-ui wp-editor-wrap ' . $switch_class,
'data-toolbar' => $field['toolbar'],
);
// delay
if ( $field['delay'] ) {
$wrap['class'] .= ' delay';
}
// vars
$textarea = acf_get_textarea_input(
array(
'id' => $id,
'class' => 'wp-editor-area',
'name' => $field['name'],
'style' => $height ? "height:{$height}px;" : '',
'value' => '%s',
)
);
?>
<div <?php echo acf_esc_attrs( $wrap ); ?>>
<div id="wp-<?php echo esc_attr( $id ); ?>-editor-tools" class="wp-editor-tools hide-if-no-js">
<?php if ( $field['media_upload'] ) : ?>
<div id="wp-<?php echo esc_attr( $id ); ?>-media-buttons" class="wp-media-buttons">
<?php
if ( ! function_exists( 'media_buttons' ) ) {
require ABSPATH . 'wp-admin/includes/media.php';
}
do_action( 'media_buttons', $id );
?>
</div>
<?php endif; ?>
<?php if ( user_can_richedit() && $show_tabs ) : ?>
<div class="wp-editor-tabs">
<button id="<?php echo esc_attr( $id ); ?>-tmce" class="wp-switch-editor switch-tmce" data-wp-editor-id="<?php echo esc_attr( $id ); ?>" type="button"><?php esc_html_e( 'Visual', 'acf' ); ?></button>
<button id="<?php echo esc_attr( $id ); ?>-html" class="wp-switch-editor switch-html" data-wp-editor-id="<?php echo esc_attr( $id ); ?>" type="button"><?php echo esc_html_x( 'Text', 'Name for the Text editor tab (formerly HTML)', 'acf' ); ?></button>
</div>
<?php endif; ?>
</div>
<div id="wp-<?php echo esc_attr( $id ); ?>-editor-container" class="wp-editor-container">
<?php if ( $field['delay'] ) : ?>
<div class="acf-editor-toolbar"><?php esc_html_e( 'Click to initialize TinyMCE', 'acf' ); ?></div>
<?php endif; ?>
<?php printf( $textarea, $field['value'] ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped by format_for_editor(). ?>
</div>
</div>
<?php
}
/**
* Create extra options for your field. This is rendered when editing a field.
* The value of $field['name'] can be used (like bellow) to save extra data to the $field
*
* @type action
* @since 3.6
* @date 23/01/13
*
* @param $field - an array holding all the field's data
*/
function render_field_settings( $field ) {
acf_render_field_setting(
$field,
array(
'label' => __( 'Default Value', 'acf' ),
'instructions' => __( 'Appears when creating a new post', 'acf' ),
'type' => 'textarea',
'name' => 'default_value',
)
);
}
/**
* Renders the field settings used in the "Presentation" tab.
*
* @since 6.0
*
* @param array $field The field settings array.
* @return void
*/
function render_field_presentation_settings( $field ) {
$toolbars = $this->get_toolbars();
$choices = array();
if ( ! empty( $toolbars ) ) {
foreach ( $toolbars as $k => $v ) {
$label = $k;
$name = sanitize_title( $label );
$name = str_replace( '-', '_', $name );
$choices[ $name ] = $label;
}
}
acf_render_field_setting(
$field,
array(
'label' => __( 'Tabs', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'tabs',
'choices' => array(
'all' => __( 'Visual & Text', 'acf' ),
'visual' => __( 'Visual Only', 'acf' ),
'text' => __( 'Text Only', 'acf' ),
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Toolbar', 'acf' ),
'instructions' => '',
'type' => 'select',
'name' => 'toolbar',
'choices' => $choices,
'conditions' => array(
'field' => 'tabs',
'operator' => '!=',
'value' => 'text',
),
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Show Media Upload Buttons', 'acf' ),
'instructions' => '',
'name' => 'media_upload',
'type' => 'true_false',
'ui' => 1,
)
);
acf_render_field_setting(
$field,
array(
'label' => __( 'Delay Initialization', 'acf' ),
'instructions' => __( 'TinyMCE will not be initialized until field is clicked', 'acf' ),
'name' => 'delay',
'type' => 'true_false',
'ui' => 1,
'conditions' => array(
'field' => 'tabs',
'operator' => '!=',
'value' => 'text',
),
)
);
}
/**
* This filter is applied to the $value after it is loaded from the db, and before it is returned to the template
*
* @type filter
* @since 3.6
*
* @param mixed $value The value which was loaded from the database.
* @param mixed $post_id The $post_id from which the value was loaded.
* @param array $field The field array holding all the field options.
* @param boolean $escape_html Should the field return a HTML safe formatted value.
* @return mixed $value The modified value
*/
public function format_value( $value, $post_id, $field, $escape_html ) {
// Bail early if no value or not a string.
if ( empty( $value ) || ! is_string( $value ) ) {
return $value;
}
if ( $escape_html ) {
add_filter( 'acf_the_content', 'acf_esc_html', 1 );
}
$value = apply_filters( 'acf_the_content', $value );
if ( $escape_html ) {
remove_filter( 'acf_the_content', 'acf_esc_html', 1 );
}
// Follow the_content function in /wp-includes/post-template.php
return str_replace( ']]>', ']]&gt;', $value );
}
}
// initialize
acf_register_field_type( 'acf_field_wysiwyg' );
endif; // class_exists check
?>

View File

@@ -0,0 +1,371 @@
<?php
if ( ! class_exists( 'acf_field' ) ) :
#[AllowDynamicProperties]
class acf_field {
// field information properties.
public $name = '';
public $label = '';
public $category = 'basic';
public $description = '';
public $doc_url = false;
public $tutorial_url = false;
public $preview_image = false;
public $pro = false;
public $defaults = array();
public $l10n = array();
public $public = true;
public $show_in_rest = true;
public $supports = array(
'escaping_html' => false, // Set true when a field handles its own HTML escaping in format_value
'required' => true,
);
/**
* Initializes the `acf_field` class. To initialize a field type that is
* extending this class, use the `initialize()` method in the child class instead.
*
* @since 5.0.0
*/
public function __construct() {
// Initialize the field type.
$this->initialize();
// Register info about the field type.
acf_register_field_type_info(
array(
'label' => $this->label,
'name' => $this->name,
'category' => $this->category,
'description' => $this->description,
'doc_url' => $this->doc_url,
'tutorial_url' => $this->tutorial_url,
'preview_image' => $this->preview_image,
'pro' => $this->pro,
'public' => $this->public,
)
);
// value
$this->add_field_filter( 'acf/load_value', array( $this, 'load_value' ), 10, 3 );
$this->add_field_filter( 'acf/update_value', array( $this, 'update_value' ), 10, 3 );
$this->add_field_filter( 'acf/format_value', array( $this, 'format_value' ), 10, 4 );
$this->add_field_filter( 'acf/validate_value', array( $this, 'validate_value' ), 10, 4 );
$this->add_field_action( 'acf/delete_value', array( $this, 'delete_value' ), 10, 3 );
// field
$this->add_field_filter( 'acf/validate_rest_value', array( $this, 'validate_rest_value' ), 10, 3 );
$this->add_field_filter( 'acf/validate_field', array( $this, 'validate_field' ), 10, 1 );
$this->add_field_filter( 'acf/load_field', array( $this, 'load_field' ), 10, 1 );
$this->add_field_filter( 'acf/update_field', array( $this, 'update_field' ), 10, 1 );
$this->add_field_filter( 'acf/duplicate_field', array( $this, 'duplicate_field' ), 10, 1 );
$this->add_field_action( 'acf/delete_field', array( $this, 'delete_field' ), 10, 1 );
$this->add_field_action( 'acf/render_field', array( $this, 'render_field' ), 9, 1 );
$this->add_field_action( 'acf/render_field_settings', array( $this, 'render_field_settings' ), 9, 1 );
$this->add_field_filter( 'acf/prepare_field', array( $this, 'prepare_field' ), 10, 1 );
$this->add_field_filter( 'acf/translate_field', array( $this, 'translate_field' ), 10, 1 );
// input actions
$this->add_action( 'acf/input/admin_enqueue_scripts', array( $this, 'input_admin_enqueue_scripts' ), 10, 0 );
$this->add_action( 'acf/input/admin_head', array( $this, 'input_admin_head' ), 10, 0 );
$this->add_action( 'acf/input/form_data', array( $this, 'input_form_data' ), 10, 1 );
$this->add_filter( 'acf/input/admin_l10n', array( $this, 'input_admin_l10n' ), 10, 1 );
$this->add_action( 'acf/input/admin_footer', array( $this, 'input_admin_footer' ), 10, 1 );
// field group actions
$this->add_action( 'acf/field_group/admin_enqueue_scripts', array( $this, 'field_group_admin_enqueue_scripts' ), 10, 0 );
$this->add_action( 'acf/field_group/admin_head', array( $this, 'field_group_admin_head' ), 10, 0 );
$this->add_action( 'acf/field_group/admin_footer', array( $this, 'field_group_admin_footer' ), 10, 0 );
// Add field global settings configurable by supports on specific field types.
$this->add_field_action( 'acf/field_group/render_field_settings_tab/validation', array( $this, 'render_required_setting' ), 5 );
$this->add_field_action( 'acf/field_group/render_field_settings_tab/presentation', array( $this, 'render_bindings_setting' ), 5 );
foreach ( acf_get_combined_field_type_settings_tabs() as $tab_key => $tab_label ) {
$this->add_field_action( "acf/field_group/render_field_settings_tab/{$tab_key}", array( $this, "render_field_{$tab_key}_settings" ), 9, 1 );
}
}
/**
* Initializes the field type. Overridden in child classes.
*
* @since 5.6.0
*/
public function initialize() {
/* do nothing */
}
/**
* Checks a function `is_callable()` before adding the filter, since
* classes that extend `acf_field` might not implement all filters.
*
* @since 5.0.0
*
* @param string $tag The name of the filter to add the callback to.
* @param string $function_to_add The callback to be run when the filter is applied.
* @param integer $priority The priority to add the filter on.
* @param integer $accepted_args The number of args to pass to the function.
* @return void
*/
public function add_filter( $tag = '', $function_to_add = '', $priority = 10, $accepted_args = 1 ) {
// Bail early if not callable.
if ( ! is_callable( $function_to_add ) ) {
return;
}
add_filter( $tag, $function_to_add, $priority, $accepted_args );
}
/**
* Adds a filter specific to the current field type.
*
* @since 5.4.0
*
* @param string $tag The name of the filter to add the callback to.
* @param string $function_to_add The callback to be run when the filter is applied.
* @param integer $priority The priority to add the filter on.
* @param integer $accepted_args The number of args to pass to the function.
* @return void
*/
public function add_field_filter( $tag = '', $function_to_add = '', $priority = 10, $accepted_args = 1 ) {
// Append the field type name to the tag before adding the filter.
$tag .= '/type=' . $this->name;
$this->add_filter( $tag, $function_to_add, $priority, $accepted_args );
}
/**
* Checks a function `is_callable()` before adding the action, since
* classes that extend `acf_field` might not implement all actions.
*
* @since 5.0.0
*
* @param string $tag The name of the action to add the callback to.
* @param string $function_to_add The callback to be run when the action is ran.
* @param integer $priority The priority to add the action on.
* @param integer $accepted_args The number of args to pass to the function.
* @return void
*/
public function add_action( $tag = '', $function_to_add = '', $priority = 10, $accepted_args = 1 ) {
// Bail early if not callable
if ( ! is_callable( $function_to_add ) ) {
return;
}
add_action( $tag, $function_to_add, $priority, $accepted_args );
}
/**
* Adds an action specific to the current field type.
*
* @since 5.4.0
*
* @param string $tag The name of the action to add the callback to.
* @param string $function_to_add The callback to be run when the action is ran.
* @param integer $priority The priority to add the action on.
* @param integer $accepted_args The number of args to pass to the function.
* @return void
*/
public function add_field_action( $tag = '', $function_to_add = '', $priority = 10, $accepted_args = 1 ) {
// Append the field type name to the tag before adding the action.
$tag .= '/type=' . $this->name;
$this->add_action( $tag, $function_to_add, $priority, $accepted_args );
}
/**
* Appends default settings to a field.
* Runs on `acf/validate_field/type={$this->name}`.
*
* @since 3.6
*
* @param array $field The field array.
* @return array $field
*/
public function validate_field( $field ) {
// Bail early if no defaults.
if ( ! is_array( $this->defaults ) ) {
return $field;
}
// Merge in defaults but keep order of $field keys.
foreach ( $this->defaults as $k => $v ) {
if ( ! isset( $field[ $k ] ) ) {
$field[ $k ] = $v;
}
}
return $field;
}
/**
* Append l10n text translations to an array which is later passed to JS.
* Runs on `acf/input/admin_l10n`.
*
* @since 3.6
*
* @param array $l10n
* @return array $l10n
*/
public function input_admin_l10n( $l10n ) {
// Bail early if no defaults.
if ( empty( $this->l10n ) ) {
return $l10n;
}
// Append.
$l10n[ $this->name ] = $this->l10n;
return $l10n;
}
/**
* Add additional validation for fields being updated via the REST API.
*
* @param boolean $valid The current validity booleean
* @param integer $value The value of the field
* @param array $field The field array
* @return boolean|WP_Error
*/
public function validate_rest_value( $valid, $value, $field ) {
return $valid;
}
/**
* Return the schema array for the REST API.
*
* @param array $field
* @return array
*/
public function get_rest_schema( array $field ) {
$schema = array(
'type' => array( 'string', 'null' ),
'required' => ! empty( $field['required'] ),
);
if ( isset( $field['default_value'] ) && '' !== $field['default_value'] ) {
$schema['default'] = $field['default_value'];
}
return $schema;
}
/**
* Return an array of links for addition to the REST API response. Each link is an array and must have both `rel` and
* `href` keys. The `href` key must be a REST API resource URL. If a link is marked as `embeddable`, the `_embed` URL
* parameter will trigger WordPress to dispatch an internal sub request and load the object within the same request
* under the `_embedded` response property.
*
* e.g;
* [
* [
* 'rel' => 'acf:post',
* 'href' => 'https://example.com/wp-json/wp/v2/posts/497',
* 'embeddable' => true,
* ],
* [
* 'rel' => 'acf:user',
* 'href' => 'https://example.com/wp-json/wp/v2/users/2',
* 'embeddable' => true,
* ],
* ]
*
* @param mixed $value The raw (unformatted) field value.
* @param string|integer $post_id
* @param array $field
* @return array
*/
public function get_rest_links( $value, $post_id, array $field ) {
return array();
}
/**
* Apply basic formatting to prepare the value for default REST output.
*
* @param mixed $value
* @param string|integer $post_id
* @param array $field
* @return mixed
*/
public function format_value_for_rest( $value, $post_id, array $field ) {
return $value;
}
/**
* Renders the "Required" setting on the field type "Validation" settings tab.
*
* @since 6.2.5
*
* @param array $field The field type being rendered.
* @return void
*/
public function render_required_setting( $field ) {
$supports_required = acf_field_type_supports( $field['type'], 'required', true );
// Only prevent rendering if explicitly disabled.
if ( ! $supports_required ) {
return;
}
acf_render_field_setting(
$field,
array(
'label' => __( 'Required', 'acf' ),
'instructions' => '',
'type' => 'true_false',
'name' => 'required',
'ui' => 1,
'class' => 'field-required',
),
true
);
}
/**
* Renders the "Allow in Bindings" setting on the field type "Presentation" settings tab.
*
* @since 6.3.6
*
* @param array $field The field type being rendered.
* @return void
*/
public function render_bindings_setting( $field ) {
$supports_bindings = acf_field_type_supports( $field['type'], 'bindings', true );
// Only prevent rendering if explicitly disabled.
if ( ! $supports_bindings ) {
return;
}
/* translators: %s A "Learn More" link to documentation explaining the setting further. */
$binding_string = esc_html__( 'Allow content editors to access and display the field value in the editor UI using Block Bindings or the ACF Shortcode. %s', 'acf' );
$binding_url = '<a target="_blank" href="' . acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/bindings-security/', 'docs', 'field-settings' ) . '">' . esc_html__( 'Learn more.', 'acf' ) . '</a>';
$binding_instructions = sprintf(
$binding_string,
$binding_url
);
// This field setting has unique behavior. If the value isn't defined on the field object, it defaults to true, but for new fields or when changing field types, it defaults to off.
if ( ! isset( $field['allow_in_bindings'] ) ) {
if ( empty( $field['ID'] ) || doing_action( 'wp_ajax_acf/field_group/render_field_settings' ) ) {
$field['allow_in_bindings'] = false;
} else {
$field['allow_in_bindings'] = true;
}
}
acf_render_field_setting(
$field,
array(
'label' => __( 'Allow Access to Value in Editor UI', 'acf' ),
'instructions' => $binding_instructions,
'type' => 'true_false',
'name' => 'allow_in_bindings',
'ui' => 1,
'class' => 'field-show-in-bindings',
),
true
);
}
}
endif; // class_exists check

View File

@@ -0,0 +1,2 @@
<?php
// There are many ways to WordPress.