PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.9.1
Secure Custom Fields v6.9.1
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / includes / fields / FlexibleContent / Layout.php
secure-custom-fields / includes / fields / FlexibleContent Last commit date
Layout.php 7 months ago Render.php 2 months ago
Layout.php
329 lines
1 <?php
2 /**
3 * A helper class for rendering an individual Flexible Content Layout.
4 *
5 * @package SCF
6 */
7
8 namespace SCF\Fields\FlexibleContent;
9
10 // Exit if accessed directly.
11 defined( 'ABSPATH' ) || exit;
12
13 /**
14 * Layout class for handling individual Flexible Content layouts.
15 *
16 * This class handles rendering and management of individual layouts within a Flexible Content field.
17 * It provides methods for constructing, rendering, and manipulating layouts.
18 *
19 * @since ACF 6.5
20 */
21 class Layout {
22 /**
23 * The Flexible Content field the layout belongs to.
24 *
25 * @var array
26 */
27 private $field;
28
29 /**
30 * The layout being rendered.
31 *
32 * @var array
33 */
34 private $layout;
35
36 /**
37 * The order of the layout.
38 *
39 * @var integer|string
40 */
41 private $order;
42
43 /**
44 * The value of the layout.
45 *
46 * @var mixed
47 */
48 private $value;
49
50 /**
51 * The input prefix.
52 *
53 * @var string
54 */
55 private $prefix;
56
57 /**
58 * If the layout is disabled.
59 *
60 * @var boolean
61 */
62 private $disabled;
63
64 /**
65 * If the layout has been renamed, the new name of the layout.
66 *
67 * @var string
68 */
69 private $renamed;
70
71 /**
72 * Constructs the class.
73 *
74 * @since 6.5
75 *
76 * @param array $field The Flexible Content field the layout belongs to.
77 * @param array $layout The layout to render.
78 * @param integer|string $order The order of the layout.
79 * @param mixed $value The value of the layout.
80 * @param boolean $disabled If the layout is disabled.
81 * @param string $renamed If the layout has been renamed, the new name of the layout.
82 */
83 public function __construct( $field, $layout, $order, $value, $disabled = false, $renamed = '' ) {
84 $this->field = $field;
85 $this->layout = $layout;
86 $this->order = $order;
87 $this->value = $value;
88 $this->disabled = $disabled;
89 $this->renamed = $renamed;
90 }
91
92 /**
93 * Renders the layout.
94 *
95 * @since 6.5
96 *
97 * @return void
98 */
99 public function render() {
100 $id = 'row-' . $this->order;
101 $class = 'layout';
102
103 if ( 'acfcloneindex' === $this->order ) {
104 $id = 'acfcloneindex';
105 $class .= ' acf-clone';
106 }
107
108 $this->prefix = $this->field['name'] . '[' . $id . ']';
109
110 $div_attrs = array(
111 'class' => $class,
112 'data-id' => $id,
113 'data-layout' => $this->layout['name'],
114 'data-label' => $this->layout['label'],
115 'data-min' => $this->layout['min'],
116 'data-max' => $this->layout['max'],
117 'data-enabled' => $this->disabled ? 0 : 1,
118 'data-renamed' => empty( $this->renamed ) ? 0 : 1,
119 );
120
121 echo '<div ' . acf_esc_attrs( $div_attrs ) . '>'; // Layout wrapper div.
122
123 acf_hidden_input(
124 array(
125 'name' => $this->prefix . '[acf_fc_layout]',
126 'value' => $this->layout['name'],
127 )
128 );
129
130 acf_hidden_input(
131 array(
132 'class' => 'acf-fc-layout-disabled',
133 'name' => $this->prefix . '[acf_fc_layout_disabled]',
134 'value' => $this->disabled ? 1 : 0,
135 )
136 );
137
138 acf_hidden_input(
139 array(
140 'class' => 'acf-fc-layout-custom-label',
141 'name' => $this->prefix . '[acf_fc_layout_custom_label]',
142 'value' => $this->renamed,
143 )
144 );
145
146 $this->action_buttons();
147
148 if ( ! empty( $this->layout['sub_fields'] ) ) {
149 if ( 'table' === $this->layout['display'] ) {
150 $this->render_as_table();
151 } else {
152 $this->render_as_div();
153 }
154 }
155
156 echo '</div>'; // End layout wrapper div.
157 }
158
159 /**
160 * Renders a layout as a table.
161 *
162 * @since 6.5
163 *
164 * @return void
165 */
166 private function render_as_table() {
167 $sub_fields = $this->layout['sub_fields'];
168 ?>
169 <table class="acf-table">
170 <thead>
171 <tr>
172 <?php
173 foreach ( $sub_fields as $sub_field ) {
174 // Set prefix to generate correct "for" attribute on <label>.
175 $sub_field['prefix'] = $this->prefix;
176
177 // Prepare field (allow sub fields to be removed).
178 $sub_field = acf_prepare_field( $sub_field );
179 if ( ! $sub_field ) {
180 continue;
181 }
182
183 $th_attrs = array(
184 'class' => 'acf-th',
185 'data-name' => $sub_field['_name'],
186 'data-type' => $sub_field['type'],
187 'data-key' => $sub_field['key'],
188 );
189
190 if ( $sub_field['wrapper']['width'] ) {
191 $th_attrs['data-width'] = $sub_field['wrapper']['width'];
192 $th_attrs['style'] = 'width: ' . $sub_field['wrapper']['width'] . '%;';
193 }
194
195 echo '<th ' . acf_esc_attrs( $th_attrs ) . '>';
196 acf_render_field_label( $sub_field );
197 acf_render_field_instructions( $sub_field );
198 echo '</th>';
199 }
200 ?>
201 </tr>
202 </thead>
203 <tbody>
204 <tr><?php $this->sub_fields(); ?></tr>
205 </tbody>
206 </table>
207 <?php
208 }
209
210 /**
211 * Renders a layout as a div.
212 *
213 * @since 6.5
214 *
215 * @return void
216 */
217 private function render_as_div() {
218 $class = 'acf-fields';
219
220 if ( 'row' === $this->layout['display'] ) {
221 $class .= ' -left';
222 }
223
224 echo '<div class="' . esc_attr( $class ) . '">';
225 $this->sub_fields();
226 echo '</div>';
227 }
228
229 /**
230 * Renders the layout actions (Add, Duplicate, Rename).
231 *
232 * @since 6.5
233 *
234 * @return void
235 */
236 private function action_buttons() {
237 $title = $this->get_title();
238 $order = is_numeric( $this->order ) ? $this->order + 1 : 0;
239 ?>
240 <div class="acf-fc-layout-actions-wrap">
241 <div class="acf-fc-layout-handle" title="<?php esc_attr_e( 'Drag to reorder', 'secure-custom-fields' ); ?>" data-name="collapse-layout">
242 <span class="acf-fc-layout-order"><?php echo (int) $order; ?></span>
243 <span class="acf-fc-layout-draggable-icon"></span>
244 <span class="acf-fc-layout-title">
245 <?php echo ! empty( $this->renamed ) ? esc_html( $this->renamed ) : $title; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped earlier in function. ?>
246 </span>
247 <span class="acf-fc-layout-original-title">
248 (<?php echo $title; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped earlier in function. ?>)
249 </span>
250 <span class="acf-layout-disabled"><?php esc_html_e( 'Disabled', 'secure-custom-fields' ); ?></span>
251 </div>
252 <div class="acf-fc-layout-controls">
253 <a class="acf-js-tooltip" href="#" data-name="add-layout" data-context="layout" title="<?php esc_attr( $this->field['button_label'] ); ?>"><span class="acf-icon -plus-alt "></span></a>
254 <a class="acf-js-tooltip" href="#" data-name="duplicate-layout" title="<?php esc_attr_e( 'Duplicate', 'secure-custom-fields' ); ?>"><span class="acf-icon -duplicate-alt"></span></a>
255 <a class="acf-js-tooltip" href="#" data-name="remove-layout" title="<?php esc_attr_e( 'Delete', 'secure-custom-fields' ); ?>"><span class="acf-icon -trash-alt"></span></a>
256 <a class="acf-js-tooltip" aria-haspopup="menu" href="#" data-name="more-layout-actions" title="<?php esc_attr_e( 'More layout actions...', 'secure-custom-fields' ); ?>"><span class="acf-icon -more-actions"></span></a>
257 <div class="acf-layout-collapse">
258 <a class="acf-icon -collapse -clear" href="#" data-name="collapse-layout" aria-label="<?php esc_attr_e( 'Toggle layout', 'secure-custom-fields' ); ?>"></a>
259 </div>
260 </div>
261 </div>
262 <?php
263 }
264
265 /**
266 * Renders the subfields for a layout.
267 *
268 * @since 6.5
269 * @return void
270 */
271 private function sub_fields() {
272 foreach ( $this->layout['sub_fields'] as $sub_field ) {
273
274 // add value
275 if ( isset( $this->value[ $sub_field['key'] ] ) ) {
276
277 // this is a normal value
278 $sub_field['value'] = $this->value[ $sub_field['key'] ];
279 } elseif ( isset( $sub_field['default_value'] ) ) {
280
281 // no value, but this sub field has a default value
282 $sub_field['value'] = $sub_field['default_value'];
283 }
284
285 // update prefix to allow for nested values
286 $sub_field['prefix'] = $this->prefix;
287
288 // Render the input.
289 $el = 'table' === $this->layout['display'] ? 'td' : 'div';
290 acf_render_field_wrap( $sub_field, $el );
291 }
292 }
293
294 /**
295 * Returns the filtered layout title.
296 *
297 * @since 6.5
298 *
299 * @return string
300 */
301 public function get_title() {
302 $rows = array();
303 $rows[ $this->order ] = $this->value;
304
305 acf_add_loop(
306 array(
307 'selector' => $this->field['name'],
308 'name' => $this->field['name'],
309 'value' => $rows,
310 'field' => $this->field,
311 'i' => $this->order,
312 'post_id' => 0,
313 )
314 );
315
316 // Make the title filterable.
317 $title = esc_html( $this->layout['label'] );
318 $title = apply_filters( 'acf/fields/flexible_content/layout_title', $title, $this->field, $this->layout, $this->order );
319 $title = apply_filters( 'acf/fields/flexible_content/layout_title/name=' . $this->field['_name'], $title, $this->field, $this->layout, $this->order );
320 $title = apply_filters( 'acf/fields/flexible_content/layout_title/key=' . $this->field['key'], $title, $this->field, $this->layout, $this->order );
321 $title = acf_esc_html( $title );
322
323 acf_remove_loop();
324 reset_rows(); // TODO: Make sure this is actually where this should go if needed at all.
325
326 return $title;
327 }
328 }
329