Files
hub-insurance/wp-content/plugins/advanced-custom-fields-pro/src/Pro/Fields/FlexibleContent/Layout.php
T
2026-07-02 15:54:39 -06:00

326 lines
8.5 KiB
PHP

<?php
/**
* @package ACF
* @author WP Engine
*
* © 2026 Advanced Custom Fields (ACF®). All rights reserved.
* "ACF" is a trademark of WP Engine.
* Licensed under the GNU General Public License v2 or later.
* https://www.gnu.org/licenses/gpl-2.0.html
*/
namespace ACF\Pro\Fields\FlexibleContent;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
class Layout {
/**
* The Flexible Content field the layout belongs to.
*
* @var array
*/
private $field;
/**
* The layout being rendered.
*
* @var array
*/
private $layout;
/**
* The order of the layout.
*
* @var integer|string
*/
private $order;
/**
* The value of the layout.
*
* @var mixed
*/
private $value;
/**
* The input prefix.
*
* @var string
*/
private $prefix;
/**
* If the layout is disabled.
*
* @var boolean
*/
private $disabled;
/**
* If the layout has been renamed, the new name of the layout.
*
* @var string
*/
private $renamed;
/**
* Constructs the class.
*
* @since 6.5
*
* @param array $field The Flexible Content field the layout belongs to.
* @param array $layout The layout to render.
* @param integer|string $order The order of the layout.
* @param mixed $value The value of the layout.
* @param boolean $disabled If the layout is disabled.
* @param string $renamed If the layout has been renamed, the new name of the layout.
*/
public function __construct( $field, $layout, $order, $value, $disabled = false, $renamed = '' ) {
$this->field = $field;
$this->layout = $layout;
$this->order = $order;
$this->value = $value;
$this->disabled = $disabled;
$this->renamed = $renamed;
}
/**
* Renders the layout.
*
* @since 6.5
*
* @return void
*/
public function render() {
$id = 'row-' . $this->order;
$class = 'layout';
if ( 'acfcloneindex' === $this->order ) {
$id = 'acfcloneindex';
$class .= ' acf-clone';
}
$this->prefix = $this->field['name'] . '[' . $id . ']';
$div_attrs = array(
'class' => $class,
'data-id' => $id,
'data-layout' => $this->layout['name'],
'data-label' => $this->layout['label'],
'data-min' => $this->layout['min'],
'data-max' => $this->layout['max'],
'data-enabled' => $this->disabled ? 0 : 1,
'data-renamed' => empty( $this->renamed ) ? 0 : 1,
);
echo '<div ' . acf_esc_attrs( $div_attrs ) . '>'; // Layout wrapper div.
acf_hidden_input(
array(
'name' => $this->prefix . '[acf_fc_layout]',
'value' => $this->layout['name'],
)
);
acf_hidden_input(
array(
'class' => 'acf-fc-layout-disabled',
'name' => $this->prefix . '[acf_fc_layout_disabled]',
'value' => $this->disabled ? 1 : 0,
)
);
acf_hidden_input(
array(
'class' => 'acf-fc-layout-custom-label',
'name' => $this->prefix . '[acf_fc_layout_custom_label]',
'value' => $this->renamed,
)
);
$this->action_buttons();
if ( ! empty( $this->layout['sub_fields'] ) ) {
if ( 'table' === $this->layout['display'] ) {
$this->render_as_table();
} else {
$this->render_as_div();
}
}
echo '</div>'; // End layout wrapper div.
}
/**
* Renders a layout as a table.
*
* @since 6.5
*
* @return void
*/
private function render_as_table() {
$sub_fields = $this->layout['sub_fields'];
?>
<table class="acf-table">
<thead>
<tr>
<?php
foreach ( $sub_fields as $sub_field ) {
// Set prefix to generate correct "for" attribute on <label>.
$sub_field['prefix'] = $this->prefix;
// Prepare field (allow sub fields to be removed).
$sub_field = acf_prepare_field( $sub_field );
if ( ! $sub_field ) {
continue;
}
$th_attrs = array(
'class' => 'acf-th',
'data-name' => $sub_field['_name'],
'data-type' => $sub_field['type'],
'data-key' => $sub_field['key'],
);
if ( $sub_field['wrapper']['width'] ) {
$th_attrs['data-width'] = $sub_field['wrapper']['width'];
$th_attrs['style'] = 'width: ' . $sub_field['wrapper']['width'] . '%;';
}
echo '<th ' . acf_esc_attrs( $th_attrs ) . '>';
acf_render_field_label( $sub_field );
acf_render_field_instructions( $sub_field );
echo '</th>';
}
?>
</tr>
</thead>
<tbody>
<tr><?php $this->sub_fields(); ?></tr>
</tbody>
</table>
<?php
}
/**
* Renders a layout as a div.
*
* @since 6.5
*
* @return void
*/
private function render_as_div() {
$class = 'acf-fields';
if ( 'row' === $this->layout['display'] ) {
$class .= ' -left';
}
echo '<div class="' . esc_attr( $class ) . '">';
$this->sub_fields();
echo '</div>';
}
/**
* Renders the layout actions (Add, Duplicate, Rename).
*
* @since 6.5
*
* @return void
*/
private function action_buttons() {
$title = $this->get_title();
$order = is_numeric( $this->order ) ? $this->order + 1 : 0;
?>
<div class="acf-fc-layout-actions-wrap">
<div class="acf-fc-layout-handle" title="<?php esc_attr_e( 'Drag to reorder', 'acf' ); ?>" data-name="collapse-layout">
<span class="acf-fc-layout-order"><?php echo (int) $order; ?></span>
<span class="acf-fc-layout-draggable-icon"></span>
<span class="acf-fc-layout-title">
<?php echo ! empty( $this->renamed ) ? esc_html( $this->renamed ) : $title; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped earlier in function. ?>
</span>
<span class="acf-fc-layout-original-title">
(<?php echo $title; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped earlier in function. ?>)
</span>
<span class="acf-layout-disabled"><?php esc_html_e( 'Disabled', 'acf' ); ?></span>
</div>
<div class="acf-fc-layout-controls">
<a class="acf-js-tooltip" href="#" data-name="add-layout" data-context="layout" title="<?php echo esc_attr( $this->field['button_label'] ); ?>"><span class="acf-icon -plus-alt "></span></a>
<a class="acf-js-tooltip" href="#" data-name="duplicate-layout" title="<?php esc_attr_e( 'Duplicate', 'acf' ); ?>"><span class="acf-icon -duplicate-alt"></span></a>
<a class="acf-js-tooltip" href="#" data-name="remove-layout" title="<?php esc_attr_e( 'Delete', 'acf' ); ?>"><span class="acf-icon -trash-alt"></span></a>
<a class="acf-js-tooltip" aria-haspopup="menu" href="#" data-name="more-layout-actions" title="<?php esc_attr_e( 'More layout actions...', 'acf' ); ?>"><span class="acf-icon -more-actions"></span></a>
<div class="acf-layout-collapse">
<a class="acf-icon -collapse -clear" href="#" data-name="collapse-layout" aria-label="<?php esc_attr_e( 'Toggle layout', 'acf' ); ?>"></a>
</div>
</div>
</div>
<?php
}
/**
* Renders the subfields for a layout.
*
* @since 6.5
* @return void
*/
private function sub_fields() {
foreach ( $this->layout['sub_fields'] as $sub_field ) {
// add value
if ( isset( $this->value[ $sub_field['key'] ] ) ) {
// this is a normal value
$sub_field['value'] = $this->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'] = $this->prefix;
// Render the input.
$el = 'table' === $this->layout['display'] ? 'td' : 'div';
acf_render_field_wrap( $sub_field, $el );
}
}
/**
* Returns the filtered layout title.
*
* @since 6.5
*
* @return string
*/
public function get_title() {
$rows = array();
$rows[ $this->order ] = $this->value;
acf_add_loop(
array(
'selector' => $this->field['name'],
'name' => $this->field['name'],
'value' => $rows,
'field' => $this->field,
'i' => $this->order,
'post_id' => 0,
)
);
// Make the title filterable.
$title = esc_html( $this->layout['label'] );
$title = apply_filters( 'acf/fields/flexible_content/layout_title', $title, $this->field, $this->layout, $this->order );
$title = apply_filters( 'acf/fields/flexible_content/layout_title/name=' . $this->field['_name'], $title, $this->field, $this->layout, $this->order );
$title = apply_filters( 'acf/fields/flexible_content/layout_title/key=' . $this->field['key'], $title, $this->field, $this->layout, $this->order );
$title = acf_esc_html( $title );
acf_remove_loop();
reset_rows(); // TODO: Make sure this is actually where this should go if needed at all.
return $title;
}
}