diff --git a/assets/images/icons/icon-add-alt.svg b/assets/images/icons/icon-add-alt.svg
new file mode 100644
index 00000000..461e36bf
--- /dev/null
+++ b/assets/images/icons/icon-add-alt.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/icons/icon-duplicate.svg b/assets/images/icons/icon-duplicate.svg
new file mode 100644
index 00000000..2ee9555d
--- /dev/null
+++ b/assets/images/icons/icon-duplicate.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/icons/icon-edit.svg b/assets/images/icons/icon-edit.svg
new file mode 100644
index 00000000..e2ee7fc0
--- /dev/null
+++ b/assets/images/icons/icon-edit.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/icons/icon-hidden-alt.svg b/assets/images/icons/icon-hidden-alt.svg
new file mode 100644
index 00000000..4de62f9e
--- /dev/null
+++ b/assets/images/icons/icon-hidden-alt.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/icons/icon-more-vertical.svg b/assets/images/icons/icon-more-vertical.svg
new file mode 100644
index 00000000..19dea137
--- /dev/null
+++ b/assets/images/icons/icon-more-vertical.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/icons/icon-trash-alt.svg b/assets/images/icons/icon-trash-alt.svg
new file mode 100644
index 00000000..64d74fe8
--- /dev/null
+++ b/assets/images/icons/icon-trash-alt.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/icons/icon-visible.svg b/assets/images/icons/icon-visible.svg
new file mode 100644
index 00000000..5c117f45
--- /dev/null
+++ b/assets/images/icons/icon-visible.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/src/js/_acf-field-date-picker.js b/assets/src/js/_acf-field-date-picker.js
index b57de98b..0fe1952e 100644
--- a/assets/src/js/_acf-field-date-picker.js
+++ b/assets/src/js/_acf-field-date-picker.js
@@ -47,7 +47,32 @@
// add date picker
acf.newDatePicker( $inputText, args );
- // action
+ // Check if default to today is enabled and field is empty
+ if (
+ $inputText.data( 'default-to-today' ) === 1 &&
+ ! $input.val()
+ ) {
+ // Get current date
+ const currentDate = new Date();
+
+ // Format display date
+ const displayDate = $.datepicker.formatDate(
+ args.dateFormat,
+ currentDate
+ );
+
+ // Set the display input value (what user sees)
+ $inputText.val( `${ displayDate }` );
+
+ // Format hidden field date (for database storage)
+ const hiddenDate = $.datepicker.formatDate(
+ 'yymmdd',
+ currentDate
+ );
+
+ // Set the hidden input value (what gets saved)
+ $input.val( `${ hiddenDate }` );
+ }
acf.doAction( 'date_picker_init', $inputText, args, this );
},
diff --git a/assets/src/js/_acf-field-date-time-picker.js b/assets/src/js/_acf-field-date-time-picker.js
index d06de6c4..2fba2338 100644
--- a/assets/src/js/_acf-field-date-time-picker.js
+++ b/assets/src/js/_acf-field-date-time-picker.js
@@ -34,6 +34,43 @@
// add date time picker
acf.newDateTimePicker( $inputText, args );
+ // Check if default to today is enabled and field is empty
+ if (
+ $inputText.data( 'default-to-today' ) === 1 &&
+ ! $input.val()
+ ) {
+ // Get current date
+ const currentDate = new Date();
+
+ // Format display date and time
+ const displayDate = $.datepicker.formatDate(
+ args.dateFormat,
+ currentDate
+ );
+ const displayTime = $.datepicker.formatTime( args.timeFormat, {
+ hour: currentDate.getHours(),
+ minute: currentDate.getMinutes(),
+ second: currentDate.getSeconds(),
+ } );
+
+ // Set the display input value (what user sees)
+ $inputText.val( `${ displayDate } ${ displayTime }` );
+
+ // Format hidden field date and time (for database storage)
+ const hiddenDate = $.datepicker.formatDate(
+ 'yy-mm-dd',
+ currentDate
+ );
+ const hiddenTime = $.datepicker.formatTime( 'hh:mm:ss', {
+ hour: currentDate.getHours(),
+ minute: currentDate.getMinutes(),
+ second: currentDate.getSeconds(),
+ } );
+
+ // Set the hidden input value (what gets saved)
+ $input.val( `${ hiddenDate } ${ hiddenTime }` );
+ }
+
// action
acf.doAction( 'date_time_picker_init', $inputText, args, this );
},
diff --git a/assets/src/js/_acf-field-icon-picker.js b/assets/src/js/_acf-field-icon-picker.js
index ecf52aa9..9213efa6 100644
--- a/assets/src/js/_acf-field-icon-picker.js
+++ b/assets/src/js/_acf-field-icon-picker.js
@@ -58,7 +58,7 @@
// Initialize the state of the icon picker.
let typeAndValue = {
type: this.$typeInput().val(),
- value: this.$valueInput().val()
+ value: this.$valueInput().val(),
};
// Store the type and value object.
@@ -135,7 +135,7 @@
initializeIconLists( typeAndValue ) {
const self = this;
- this.$( '.acf-icon-list' ).each( function( i ) {
+ this.$( '.acf-icon-list' ).each( function ( i ) {
const tabName = $( this ).data( 'parent-tab' );
const icons = self.getIconsList( tabName ) || [];
self.set( tabName, icons );
@@ -143,7 +143,11 @@
if ( typeAndValue.type === tabName ) {
// Select the correct icon.
- self.selectIcon( $( this ), typeAndValue.value, false ).then( () => {
+ self.selectIcon(
+ $( this ),
+ typeAndValue.value,
+ false
+ ).then( () => {
// Scroll to the selected icon.
self.scrollToSelectedIcon();
} );
@@ -152,13 +156,9 @@
},
alignIconListTabsToCurrentValue( typeAndValue ) {
- const icons = this.$( '.acf-icon-list' ).filter(
- function () {
- return (
- $( this ).data( 'parent-tab' ) !== typeAndValue.type
- );
- }
- );
+ const icons = this.$( '.acf-icon-list' ).filter( function () {
+ return $( this ).data( 'parent-tab' ) !== typeAndValue.type;
+ } );
const self = this;
icons.each( function () {
self.unselectIcon( $( this ) );
@@ -179,12 +179,8 @@
icon.key
) } acf-icon-picker-list-icon" role="radio" data-icon="${ acf.strEscape(
icon.key
- ) }" style="${ style }" title="${ acf.strEscape(
- icon.label
- ) }">
-
+ ) }" style="${ style }" title="${ acf.strEscape( icon.label ) }">
+
' + this.get( 'text' ) + '
' );
+ this.html( '' + acf.strEscape( this.get( 'text' ) ) + '
' );
// close
if ( this.get( 'dismiss' ) ) {
- this.$el.append( '' );
+ this.$el.append(
+ ''
+ );
this.$el.addClass( '-dismiss' );
}
@@ -144,14 +146,29 @@
$( this ).remove();
} else {
$( this ).show();
- $( this ).on( 'click', '.notice-dismiss', function ( e ) {
- dismissed = acf.getPreference( 'dismissed-notices' );
- if ( ! dismissed || typeof dismissed != 'object' ) {
- dismissed = [];
+ $( this ).on(
+ 'click',
+ '.notice-dismiss',
+ function ( e ) {
+ dismissed =
+ acf.getPreference( 'dismissed-notices' );
+ if (
+ ! dismissed ||
+ typeof dismissed != 'object'
+ ) {
+ dismissed = [];
+ }
+ dismissed.push(
+ $( this )
+ .closest( '.acf-admin-notice' )
+ .data( 'persist-id' )
+ );
+ acf.setPreference(
+ 'dismissed-notices',
+ dismissed
+ );
}
- dismissed.push( $( this ).closest( '.acf-admin-notice' ).data( 'persist-id' ) );
- acf.setPreference( 'dismissed-notices', dismissed );
- } );
+ );
}
}
} );
diff --git a/assets/src/js/_acf-popup.js b/assets/src/js/_acf-popup.js
index 5fcf42d2..685cdd87 100644
--- a/assets/src/js/_acf-popup.js
+++ b/assets/src/js/_acf-popup.js
@@ -7,12 +7,13 @@
height: 0,
loading: false,
openedBy: null,
+ confirmRemove: false,
},
events: {
'click [data-event="close"]': 'onClickClose',
'click .acf-close-popup': 'onClickClose',
- 'keydown': 'onPressEscapeClose',
+ keydown: 'onPressEscapeClose',
},
setup: function ( props ) {
@@ -31,7 +32,9 @@
return [
'
- render();
}
/**
@@ -696,9 +530,8 @@ public function render_field_presentation_settings( $field ) {
/**
- * This filter is applied to the $value after it is loaded from the db
+ * Filters the $value after it is loaded from the database.
*
- * @type filter
* @since ACF 3.6
*
* @param mixed $value The value found in the database.
@@ -712,20 +545,20 @@ public function load_value( $value, $post_id, $field ) {
return $value;
}
- // value must be an array
- $value = acf_get_array( $value );
-
- // vars
- $rows = array();
-
- // sort layouts into names
- $layouts = array();
+ $value = acf_get_array( $value );
+ $disabled_layouts = $this->get_disabled_layouts( $post_id, $field );
+ $rows = array();
+ $layouts = array();
foreach ( $field['layouts'] as $k => $layout ) {
$layouts[ $layout['name'] ] = $layout['sub_fields'];
}
// loop through rows
foreach ( $value as $i => $l ) {
+ // If the layout is disabled, prevent it from showing up on the frontend.
+ if ( $this->should_disable_layout( $i, $disabled_layouts ) ) {
+ continue;
+ }
// append to $values
$rows[ $i ] = array();
@@ -764,6 +597,48 @@ public function load_value( $value, $post_id, $field ) {
return $rows;
}
+ /**
+ * Checks if a layout should be disabled based on the provided index and disabled layouts.
+ *
+ * @since ACF 6.5
+ *
+ * @param integer|string $layout_index The index of the layout to check.
+ * @param array $disabled_layouts The array of disabled layout indices.
+ * @return boolean
+ */
+ private function should_disable_layout( $layout_index, $disabled_layouts = array() ): bool {
+ // No disabled layouts provided, so no need to disable.
+ if ( ! is_array( $disabled_layouts ) || empty( $disabled_layouts ) ) {
+ return false;
+ }
+
+ // The layout is not in the disabled list, so no need to disable.
+ if ( ! in_array( $layout_index, $disabled_layouts, true ) ) {
+ return false;
+ }
+
+ if ( is_admin() ) {
+ $args = acf_request_args(
+ array(
+ 'action' => '',
+ 'query' => '',
+ )
+ );
+
+ // If this is a block preview, disable the layout.
+ if ( ( 'acf/ajax/fetch-block' === $args['action'] && ! empty( $args['query']['preview'] ) ) ||
+ acf_get_data( 'acf_doing_block_preview' ) ) {
+ return true;
+ }
+
+ // Editing a layout in the admin, so don't disable it.
+ return false;
+ }
+
+ // The layout has been disabled, and we're on the frontend.
+ return true;
+ }
+
/**
* This filter is applied to the $value after it is loaded from the db and before it is returned to the template
@@ -1003,6 +878,86 @@ public function get_layout( $name, $field ) {
return false;
}
+ /**
+ * Retrieves layout meta for the Flexible Content field saved to the provided post.
+ *
+ * @since ACF 6.5
+ *
+ * @param integer|string $post_id The ID of the post being edited.
+ * @param array $field The Flexible Content field array.
+ * @return array
+ */
+ public function get_layout_meta( $post_id, $field ) {
+ $field_name = $field['name'];
+
+ // Enables compatibility with nested Flexible Content fields during render.
+ if ( ! empty( $field['_prepare'] ) ) {
+ $field_name = acf_get_field_type( 'repeater' )->get_field_name_from_input_name( $field_name );
+ }
+
+ // Bail early if we don't have a field name to check.
+ if ( empty( $field_name ) ) {
+ return array();
+ }
+
+ // Return the cached meta if we have it.
+ if ( ! empty( $this->layout_meta[ $field_name ] ) ) {
+ return $this->layout_meta[ $field_name ];
+ }
+
+ $layout_meta = acf_get_metadata_by_field(
+ $post_id,
+ array(
+ 'name' => '_' . $field_name . '_layout_meta',
+ )
+ );
+
+ if ( empty( $layout_meta ) || ! is_array( $layout_meta ) ) {
+ return array();
+ }
+
+ $this->layout_meta[ $field_name ] = $layout_meta;
+
+ return $this->layout_meta[ $field_name ];
+ }
+
+ /**
+ * Returns an array of layouts that have been disabled for the current field.
+ *
+ * @since ACF 6.5
+ *
+ * @param integer|string $post_id The ID of the post being edited.
+ * @param array $field The Flexible Content field array.
+ * @return array
+ */
+ public function get_disabled_layouts( $post_id, $field ): array {
+ $layout_meta = $this->get_layout_meta( $post_id, $field );
+
+ if ( empty( $layout_meta['disabled'] ) || ! is_array( $layout_meta['disabled'] ) ) {
+ return array();
+ }
+
+ return $layout_meta['disabled'];
+ }
+
+ /**
+ * Returns an array of layouts that have been renamed for the current field.
+ *
+ * @since ACF 6.5
+ *
+ * @param integer|string $post_id The ID of the post being edited.
+ * @param array $field The Flexible Content field array.
+ * @return array
+ */
+ public function get_renamed_layouts( $post_id, $field ): array {
+ $layout_meta = $this->get_layout_meta( $post_id, $field );
+
+ if ( empty( $layout_meta['renamed'] ) || ! is_array( $layout_meta['renamed'] ) ) {
+ return array();
+ }
+
+ return $layout_meta['renamed'];
+ }
/**
* This function will delete a value row
@@ -1094,9 +1049,8 @@ public function update_row( $row, $i, $field, $post_id ) {
}
/**
- * This filter is applied to the $value before it is updated in the db
+ * Filters the $value before it is updated in the database.
*
- * @type filter
* @since ACF 3.6
*
* @param mixed $value The value which will be saved in the database.
@@ -1106,15 +1060,17 @@ public function update_row( $row, $i, $field, $post_id ) {
*/
public function update_value( $value, $post_id, $field ) {
- // bail early if no layouts
- if ( empty( $field['layouts'] ) ) {
+ // Bail early if no layouts or field name.
+ if ( empty( $field['layouts'] ) || empty( $field['name'] ) ) {
return $value;
}
// vars
- $new_value = array();
- $old_value = acf_get_metadata_by_field( $post_id, $field );
- $old_value = is_array( $old_value ) ? $old_value : array();
+ $new_value = array();
+ $disabled_layouts = array();
+ $renamed_layouts = array();
+ $old_value = acf_get_metadata_by_field( $post_id, $field );
+ $old_value = is_array( $old_value ) ? $old_value : array();
// update
if ( ! empty( $value ) ) {
@@ -1139,6 +1095,16 @@ public function update_value( $value, $post_id, $field ) {
$this->delete_row( $i, $field, $post_id );
}
+ if ( ! empty( $row['acf_fc_layout_disabled'] ) ) {
+ $disabled_layouts[] = $i;
+ }
+ unset( $row['acf_fc_layout_disabled'] );
+
+ if ( ! empty( $row['acf_fc_layout_custom_label'] ) ) {
+ $renamed_layouts[ $i ] = $row['acf_fc_layout_custom_label'];
+ }
+ unset( $row['acf_fc_layout_custom_label'] );
+
// update row
$this->update_row( $row, $i, $field, $post_id );
@@ -1151,6 +1117,18 @@ public function update_value( $value, $post_id, $field ) {
$old_count = empty( $old_value ) ? 0 : count( $old_value );
$new_count = empty( $new_value ) ? 0 : count( $new_value );
+ // Update layout meta.
+ acf_update_metadata_by_field(
+ $post_id,
+ array(
+ 'name' => '_' . $field['name'] . '_layout_meta',
+ ),
+ array(
+ 'disabled' => $disabled_layouts,
+ 'renamed' => $renamed_layouts,
+ )
+ );
+
// remove old rows
if ( $old_count > $new_count ) {
@@ -1346,39 +1324,8 @@ public function ajax_layout_title() {
* @return string The layout title, optionally filtered.
*/
public function get_layout_title( $field, $layout, $i, $value ) {
-
- // vars
- $rows = array();
- $rows[ $i ] = $value;
-
- // add loop
- acf_add_loop(
- array(
- 'selector' => $field['name'],
- 'name' => $field['name'],
- 'value' => $rows,
- 'field' => $field,
- 'i' => $i,
- 'post_id' => 0,
- )
- );
-
- // vars
- $title = $layout['label'];
-
- // filters
- $title = apply_filters( 'acf/fields/flexible_content/layout_title', $title, $field, $layout, $i );
- $title = apply_filters( 'acf/fields/flexible_content/layout_title/name=' . $field['_name'], $title, $field, $layout, $i );
- $title = apply_filters( 'acf/fields/flexible_content/layout_title/key=' . $field['key'], $title, $field, $layout, $i );
-
- // remove loop
- acf_remove_loop();
-
- // prepend order
- $order = is_numeric( $i ) ? $i + 1 : 0;
- $title = '' . $order . ' ' . acf_esc_html( $title );
-
- return $title;
+ $layout = new Layout( $field, $layout, $i, $value );
+ return $layout->get_title();
}
diff --git a/includes/fields/class-acf-field-google-map.php b/includes/fields/class-acf-field-google-map.php
index b0270f98..ea280835 100644
--- a/includes/fields/class-acf-field-google-map.php
+++ b/includes/fields/class-acf-field-google-map.php
@@ -1,7 +1,6 @@
-
+
+
+
+ >
@@ -173,7 +191,7 @@ function ( $tab ) use ( $field ) {
switch ( $name ) {
case 'dashicons':
- $this->render_icon_list_tab( $name );
+ $this->render_icon_list_tab( $name, $field );
break;
case 'media_library':
?>
@@ -228,18 +246,7 @@ class="acf-icon-picker-media-library-preview"
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 );
-
- acf_localize_data(
- array(
- 'iconPickerIcons_' . $name => $custom_icons,
- )
- );
- }
+ $this->render_icon_list_tab( $name, $field );
}
echo '
';
diff --git a/includes/fields/class-acf-field-oembed.php b/includes/fields/class-acf-field-oembed.php
index 7f5340a9..9df09be1 100644
--- a/includes/fields/class-acf-field-oembed.php
+++ b/includes/fields/class-acf-field-oembed.php
@@ -1,7 +1,6 @@
true,
);
+ /**
+ * Default values for the field.
+ *
+ * @var array
+ */
+ public $default_values = array();
+
+ /**
+ * Whether the field has rows.
+ *
+ * @var string
+ */
+ public $have_rows = '';
+
+ /**
+ * The width of the field.
+ *
+ * @var string
+ */
+ public $width = '';
+
+ /**
+ * The height of the field.
+ *
+ * @var string
+ */
+ public $height = '';
+
/**
* Initializes the `acf_field` class. To initialize a field type that is
* extending this class, use the `initialize()` method in the child class instead.
diff --git a/includes/forms/form-customizer.php b/includes/forms/form-customizer.php
index 370311b4..c87f8da3 100644
--- a/includes/forms/form-customizer.php
+++ b/includes/forms/form-customizer.php
@@ -23,14 +23,14 @@ class ACF_Form_Customizer {
*
* @var array
*/
- public $preview_values;
+ public $preview_values = array();
/**
* Fields to be used in the preview.
*
* @var array
*/
- public $preview_fields;
+ public $preview_fields = array();
/**
@@ -38,7 +38,7 @@ class ACF_Form_Customizer {
*
* @var array
*/
- public $preview_errors;
+ public $preview_errors = array();
/**
* This function will setup the class functionality
@@ -52,11 +52,6 @@ class ACF_Form_Customizer {
*/
public function __construct() {
- // vars
- $this->preview_values = array();
- $this->preview_fields = array();
- $this->preview_errors = array();
-
// actions
add_action( 'customize_controls_init', array( $this, 'customize_controls_init' ) );
add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ), 1, 1 );
@@ -381,75 +376,75 @@ public function admin_footer() {
?>
preview_values = array();
- $this->preview_reference = array();
- $this->preview_errors = array();
-
// actions
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
add_action( 'in_widget_form', array( $this, 'edit_widget' ), 10, 3 );
@@ -185,9 +179,9 @@ function edit_widget( $widget, $return, $instance ) {
if ( $widget->updated ) : ?>
loops = array();
- }
-
/**
* This function will return true if no loops exist
diff --git a/includes/revisions.php b/includes/revisions.php
index 90263a87..899cfeac 100644
--- a/includes/revisions.php
+++ b/includes/revisions.php
@@ -5,11 +5,14 @@
}
if ( ! class_exists( 'acf_revisions' ) ) :
- #[AllowDynamicProperties]
class acf_revisions {
- // vars
- var $cache = array();
+ /**
+ * An array to cache post IDs for revisions.
+ *
+ * @var array
+ */
+ public $cache = array();
/**
* Constructs the acf_revisions class.
diff --git a/includes/validation.php b/includes/validation.php
index 93e7b196..9a1440e4 100644
--- a/includes/validation.php
+++ b/includes/validation.php
@@ -5,7 +5,6 @@
}
if ( ! class_exists( 'acf_validation' ) ) :
- #[AllowDynamicProperties]
/**
* Validation Class
*/
@@ -28,9 +27,6 @@ class acf_validation {
*/
public function __construct() {
- // vars
- $this->errors = array();
-
// ajax
add_action( 'wp_ajax_acf/validate_save_post', array( $this, 'ajax_validate_save_post' ) );
add_action( 'wp_ajax_nopriv_acf/validate_save_post', array( $this, 'ajax_validate_save_post' ) );
diff --git a/secure-custom-fields.php b/secure-custom-fields.php
index b9c286c6..5113b176 100644
--- a/secure-custom-fields.php
+++ b/secure-custom-fields.php
@@ -56,6 +56,48 @@ class ACF {
*/
public $instances = array();
+ /**
+ * The loop instance.
+ *
+ * @var acf_loop
+ */
+ public $loop;
+
+ /**
+ * The revisions instance.
+ *
+ * @var acf_revisions
+ */
+ public $revisions;
+
+ /**
+ * The fields instance.
+ *
+ * @var acf_fields
+ */
+ public $fields;
+
+ /**
+ * The form front instance.
+ *
+ * @var acf_form_front
+ */
+ public $form_front;
+
+ /**
+ * The validation instance.
+ *
+ * @var acf_validation
+ */
+ public $validation;
+
+ /**
+ * The admin tools instance.
+ *
+ * @var acf_admin_tools
+ */
+ public $admin_tools;
+
/**
* A dummy constructor to ensure ACF is only setup once.
*