plugin updates

This commit is contained in:
Tony Volpe
2024-10-29 13:49:07 -04:00
parent 66268c4512
commit 9000316050
41 changed files with 916 additions and 570 deletions

View File

@@ -9,7 +9,7 @@
* Plugin Name: Advanced Custom Fields PRO
* Plugin URI: https://www.advancedcustomfields.com
* Description: Customize WordPress with powerful, professional and intuitive fields.
* Version: 6.3.9
* Version: 6.3.10
* Author: WP Engine
* Author URI: https://wpengine.com/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=plugin_directory&utm_content=advanced_custom_fields
* Update URI: false
@@ -36,7 +36,7 @@ if ( ! class_exists( 'ACF' ) ) {
*
* @var string
*/
public $version = '6.3.9';
public $version = '6.3.10';
/**
* The plugin settings array.
@@ -130,6 +130,7 @@ if ( ! class_exists( 'ACF' ) ) {
'enable_shortcode' => true,
'enable_bidirection' => true,
'enable_block_bindings' => true,
'enable_meta_box_cb_edit' => true,
);
// Include utility functions.
@@ -227,15 +228,13 @@ if ( ! class_exists( 'ACF' ) ) {
// Include legacy.
acf_include( 'includes/legacy/legacy-locations.php' );
// Include updater.
acf_include( 'includes/Updater/Updater.php' );
// Include PRO.
acf_include( 'pro/acf-pro.php' );
if ( is_admin() && function_exists( 'acf_is_pro' ) && ! acf_is_pro() ) {
// Include WPE update system.
acf_include( 'includes/class-PluginUpdater.php' );
acf_include( 'includes/acf-upgrades.php' );
acf_include( 'includes/admin/admin-options-pages-preview.php' );
}
@@ -396,12 +395,24 @@ if ( ! class_exists( 'ACF' ) ) {
*/
do_action( 'acf/include_taxonomies', ACF_MAJOR_VERSION );
// If we're on 6.5 or newer, load block bindings. This will move to an autoloader in 6.3.
// If we're on 6.5 or newer, load block bindings. This will move to an autoloader in 6.4.
if ( version_compare( get_bloginfo( 'version' ), '6.5-beta1', '>=' ) ) {
acf_include( 'includes/Blocks/Bindings.php' );
new ACF\Blocks\Bindings();
}
// If we're ACF free, register the updater.
if ( function_exists( 'acf_is_pro' ) && ! acf_is_pro() ) {
acf_register_plugin_update(
array(
'id' => 'acf',
'slug' => acf_get_setting( 'slug' ),
'basename' => acf_get_setting( 'basename' ),
'version' => acf_get_setting( 'version' ),
)
);
}
/**
* Fires after ACF is completely "initialized".
*
@@ -788,6 +799,66 @@ if ( ! class_exists( 'ACF' ) ) {
}
}
/**
* The main function responsible for returning the acf_updates singleton.
* Use this function like you would a global variable, except without needing to declare the global.
*
* Example: <?php $acf_updates = acf_updates(); ?>
*
* @since 5.5.12
*
* @return ACF\Updater The singleton instance of Updater.
*/
function acf_updates() {
global $acf_updates;
if ( ! isset( $acf_updates ) ) {
$acf_updates = new ACF\Updater();
}
return $acf_updates;
}
/**
* Alias of acf_updates()->add_plugin().
*
* @since 5.5.10
*
* @param array $plugin Plugin data array.
*/
function acf_register_plugin_update( $plugin ) {
acf_updates()->add_plugin( $plugin );
}
/**
* An ACF specific getter to replace `home_url` in our license checks to ensure we can avoid third party filters.
*
* @since 6.0.1
* @since 6.2.8 - Renamed to acf_pro_get_home_url to match pro exclusive function naming.
* @since 6.3.10 - Renamed to acf_get_home_url now updater logic applies to free.
*
* @return string $home_url The output from home_url, sans known third party filters which cause license activation issues.
*/
function acf_get_home_url() {
// Disable WPML and TranslatePress's home url overrides for our license check.
add_filter( 'wpml_get_home_url', 'acf_pro_license_ml_intercept', 99, 2 );
add_filter( 'trp_home_url', 'acf_pro_license_ml_intercept', 99, 2 );
if ( acf_is_pro() ) {
if ( acf_pro_is_legacy_multisite() && acf_is_multisite_sub_site() ) {
$home_url = get_home_url( get_main_site_id() );
} else {
$home_url = home_url();
}
} else {
$home_url = home_url();
}
// Re-enable WPML and TranslatePress's home url overrides.
remove_filter( 'wpml_get_home_url', 'acf_pro_license_ml_intercept', 99 );
remove_filter( 'trp_home_url', 'acf_pro_license_ml_intercept', 99 );
return $home_url;
}
/**
* The main function responsible for returning the one true acf Instance to functions everywhere.
* Use this function like you would a global variable, except without needing to declare the global.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,24 +5,28 @@
* @package ACF
*/
namespace ACF;
use WP_Error;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'ACF_Updates' ) ) {
if ( ! class_exists( 'Updater' ) ) {
/**
* class for handling API services.
*/
class ACF_Updates {
class Updater {
/**
* The ACF_Updates version
* The Updater version
*
* @var string
*/
public $version = '2.4';
public $version = '3.0';
/**
* The array of registered plugins
@@ -45,8 +49,8 @@ if ( ! class_exists( 'ACF_Updates' ) ) {
*/
public function __construct() {
// disable showing updates if show updates is hidden.
if ( ! acf_pro_is_updates_page_visible() ) {
// disable showing PRO updates if show updates is hidden.
if ( acf_is_pro() && ! acf_pro_is_updates_page_visible() ) {
return;
}
@@ -120,7 +124,16 @@ if ( ! class_exists( 'ACF_Updates' ) ) {
*/
public function request( $endpoint = '', $body = null ) {
// Determine URL.
$site_url = acf_get_home_url();
if ( empty( $site_url ) || ! is_string( $site_url ) ) {
$site_url = '';
}
$headers = array(
'X-ACF-Version' => ACF_VERSION,
'X-ACF-URL' => $site_url,
);
$url = "https://connect.advancedcustomfields.com/$endpoint";
// Staging environment.
@@ -129,27 +142,25 @@ if ( ! class_exists( 'ACF_Updates' ) ) {
acf_log( $url, $body );
}
$license_key = acf_pro_get_license_key();
if ( ! $license_key ) {
$license_key = '';
}
$site_url = acf_pro_get_home_url();
if ( ! $site_url ) {
$site_url = '';
// Determine URL.
if ( acf_is_pro() ) {
$license_key = acf_pro_get_license_key();
if ( empty( $license_key ) || ! is_string( $license_key ) ) {
$license_key = '';
}
$headers['X-ACF-License'] = $license_key;
$headers['X-ACF-Plugin'] = 'pro';
} else {
$headers['X-ACF-Plugin'] = 'acf';
}
// Make request.
$raw_response = wp_remote_post(
$url,
array(
'timeout' => 28,
'timeout' => 20,
'body' => $body,
'headers' => array(
'X-ACF-Version' => ACF_VERSION,
'X-ACF-License' => $license_key,
'X-ACF-URL' => $site_url,
),
'headers' => $headers,
)
);
@@ -298,7 +309,7 @@ if ( ! class_exists( 'ACF_Updates' ) ) {
'wp' => wp_json_encode(
array(
'wp_name' => get_bloginfo( 'name' ),
'wp_url' => acf_pro_get_home_url(),
'wp_url' => acf_get_home_url(),
'wp_version' => get_bloginfo( 'version' ),
'wp_language' => get_bloginfo( 'language' ),
'wp_timezone' => get_option( 'timezone_string' ),
@@ -310,7 +321,7 @@ if ( ! class_exists( 'ACF_Updates' ) ) {
array(
'acf_version' => get_option( 'acf_version' ),
'acf_pro' => acf_is_pro(),
'block_count' => acf_pro_get_registered_block_count(),
'block_count' => function_exists( 'acf_pro_get_registered_block_count' ) ? acf_pro_get_registered_block_count() : 0,
)
),
);
@@ -481,34 +492,4 @@ if ( ! class_exists( 'ACF_Updates' ) ) {
}
}
/**
* The main function responsible for returning the acf_updates singleton.
* Use this function like you would a global variable, except without needing to declare the global.
*
* Example: <?php $acf_updates = acf_updates(); ?>
*
* @since 5.5.12
*
* @return ACF_Updates The singleton instance of ACF_Updates.
*/
function acf_updates() {
global $acf_updates;
if ( ! isset( $acf_updates ) ) {
$acf_updates = new ACF_Updates();
}
return $acf_updates;
}
/**
* Alias of acf_updates()->add_plugin().
*
* @since 5.5.10
*
* @param array $plugin Plugin data array.
*/
function acf_register_plugin_update( $plugin ) {
acf_updates()->add_plugin( $plugin );
}
}

View File

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

View File

@@ -1,17 +0,0 @@
<?php
namespace ACF\Upgrades;
/**
* Initialize the checking for plugin updates for ACF non-PRO.
*/
function check_for_acf_upgrades() {
$properties = array(
// This must match the key in "https://wpe-plugin-updates.wpengine.com/plugins.json".
'plugin_slug' => 'advanced-custom-fields',
'plugin_basename' => ACF_BASENAME,
);
new \ACF\Upgrades\PluginUpdater( $properties );
}
add_action( 'admin_init', __NAMESPACE__ . '\check_for_acf_upgrades' );

View File

@@ -312,6 +312,15 @@ if ( ! class_exists( 'ACF_Admin_Post_Type' ) ) :
$_POST['acf_post_type']['ID'] = $post_id;
$_POST['acf_post_type']['title'] = isset( $_POST['acf_post_type']['labels']['name'] ) ? $_POST['acf_post_type']['labels']['name'] : '';
if ( ! acf_get_setting( 'enable_meta_box_cb_edit' ) ) {
$_POST['acf_post_type']['register_meta_box_cb'] = '';
$existing_post = acf_maybe_unserialize( $post->post_content );
if ( ! empty( $existing_post['register_meta_box_cb'] ) ) {
$_POST['acf_post_type']['register_meta_box_cb'] = $existing_post['register_meta_box_cb'];
}
}
// Save the post type.
acf_update_internal_post_type( $_POST['acf_post_type'], $this->post_type ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Validated in verify_save_post
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

View File

@@ -314,6 +314,29 @@ if ( ! class_exists( 'ACF_Admin_Taxonomy' ) ) :
$_POST['acf_taxonomy']['ID'] = $post_id;
$_POST['acf_taxonomy']['title'] = isset( $_POST['acf_taxonomy']['labels']['name'] ) ? $_POST['acf_taxonomy']['labels']['name'] : '';
if ( ! acf_get_setting( 'enable_meta_box_cb_edit' ) ) {
$_POST['acf_taxonomy']['meta_box_cb'] = '';
$_POST['acf_taxonomy']['meta_box_sanitize_cb'] = '';
if ( ! empty( $_POST['acf_taxonomy']['meta_box'] ) && 'custom' === $_POST['acf_taxonomy']['meta_box'] ) {
$_POST['acf_taxonomy']['meta_box'] = 'default';
}
$existing_post = acf_maybe_unserialize( $post->post_content );
if ( ! empty( $existing_post['meta_box'] ) ) {
$_POST['acf_taxonomy']['meta_box'] = $existing_post['meta_box'];
}
if ( ! empty( $existing_post['meta_box_cb'] ) ) {
$_POST['acf_taxonomy']['meta_box_cb'] = $existing_post['meta_box_cb'];
}
if ( ! empty( $existing_post['meta_box_sanitize_cb'] ) ) {
$_POST['acf_taxonomy']['meta_box_sanitize_cb'] = $existing_post['meta_box_sanitize_cb'];
}
}
// Save the taxonomy.
acf_update_internal_post_type( $_POST['acf_taxonomy'], $this->post_type ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Validated in verify_save_post
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

View File

@@ -319,6 +319,7 @@ if ( isset( $field['conditional_logic'] ) && is_array( $field['conditional_logic
?>
<div class="acf-field-settings-footer">
<a class="button close-field edit-field" title="<?php esc_attr_e( 'Close Field', 'acf' ); ?>" href="#"><?php esc_html_e( 'Close Field', 'acf' ); ?></a>
<a class="acf-btn acf-btn-secondary close-add-field" title="<?php esc_attr_e( 'Close and Add Field', 'acf' ); ?>" href="#"><?php esc_html_e( 'Close and Add Field', 'acf' ); ?></a>
</div>
</div>
</div>

View File

@@ -98,7 +98,7 @@ if ( $is_subfield ) {
<ul class="acf-hl acf-tfoot">
<li class="acf-fr">
<a href="#" class="acf-btn acf-btn-secondary add-field"><i class="acf-icon acf-icon-plus"></i><?php esc_html_e( 'Add Field', 'acf' ); ?></a>
<a href="#" class="acf-btn acf-btn-sm add-field"><i class="acf-icon acf-icon-plus"></i><?php esc_html_e( 'Add Field', 'acf' ); ?></a>
</li>
</ul>

View File

@@ -838,24 +838,39 @@ foreach ( acf_get_combined_post_type_settings_tabs() as $tab_key => $tab_label )
'field'
);
acf_render_field_wrap(
array(
'type' => 'text',
'name' => 'register_meta_box_cb',
'key' => 'register_meta_box_cb',
'prefix' => 'acf_post_type',
'value' => $acf_post_type['register_meta_box_cb'],
'label' => __( 'Custom Meta Box Callback', 'acf' ),
'instructions' => __( 'A PHP function name to be called when setting up the meta boxes for the edit screen. For security, this callback will be executed in a special context without access to any superglobals like $_POST or $_GET.', 'acf' ),
'conditions' => array(
'field' => 'show_ui',
'operator' => '==',
'value' => '1',
$acf_enable_meta_box_cb_edit = acf_get_setting( 'enable_meta_box_cb_edit' );
$acf_meta_box_cb_instructions = __( 'A PHP function name to be called when setting up the meta boxes for the edit screen. For security, this callback will be executed in a special context without access to any superglobals like $_POST or $_GET.', 'acf' );
// Only show if user is allowed to update, or if it already has a value.
if ( $acf_enable_meta_box_cb_edit || ! empty( $acf_post_type['register_meta_box_cb'] ) ) {
if ( ! $acf_enable_meta_box_cb_edit ) {
if ( is_multisite() ) {
$acf_meta_box_cb_instructions .= ' ' . __( 'By default only super admin users can edit this setting.', 'acf' );
} else {
$acf_meta_box_cb_instructions .= ' ' . __( 'By default only admin users can edit this setting.', 'acf' );
}
}
acf_render_field_wrap(
array(
'type' => 'text',
'name' => 'register_meta_box_cb',
'key' => 'register_meta_box_cb',
'prefix' => 'acf_post_type',
'value' => $acf_post_type['register_meta_box_cb'],
'label' => __( 'Custom Meta Box Callback', 'acf' ),
'instructions' => $acf_meta_box_cb_instructions,
'readonly' => ! $acf_enable_meta_box_cb_edit,
'conditions' => array(
'field' => 'show_ui',
'operator' => '==',
'value' => '1',
),
),
),
'div',
'field'
);
'div',
'field'
);
}
acf_render_field_wrap(
array(

View File

@@ -745,6 +745,16 @@ foreach ( acf_get_combined_taxonomy_settings_tabs() as $tab_key => $tab_label )
$acf_tags_meta_box_text = __( 'Tags Meta Box', 'acf' );
$acf_categories_meta_box_text = __( 'Categories Meta Box', 'acf' );
$acf_default_meta_box_text = empty( $acf_taxonomy['hierarchical'] ) ? $acf_tags_meta_box_text : $acf_categories_meta_box_text;
$acf_enable_meta_box_cb_edit = acf_get_setting( 'enable_meta_box_cb_edit' );
$acf_meta_box_choices = array(
'default' => $acf_default_meta_box_text,
'custom' => __( 'Custom Meta Box', 'acf' ),
'disabled' => __( 'No Meta Box', 'acf' ),
);
if ( ! $acf_enable_meta_box_cb_edit && 'custom' !== $acf_taxonomy['meta_box'] ) {
unset( $acf_meta_box_choices['custom'] );
}
acf_render_field_wrap(
array(
@@ -757,11 +767,7 @@ foreach ( acf_get_combined_taxonomy_settings_tabs() as $tab_key => $tab_label )
'label' => __( 'Meta Box', 'acf' ),
'instructions' => __( 'Controls the meta box on the content editor screen. By default, the Categories meta box is shown for hierarchical taxonomies, and the Tags meta box is shown for non-hierarchical taxonomies.', 'acf' ),
'hide_search' => true,
'choices' => array(
'default' => $acf_default_meta_box_text,
'custom' => __( 'Custom Meta Box', 'acf' ),
'disabled' => __( 'No Meta Box', 'acf' ),
),
'choices' => $acf_meta_box_choices,
'data' => array(
'tags_meta_box' => __( 'Tags Meta Box', 'acf' ),
'categories_meta_box' => __( 'Categories Meta Box', 'acf' ),
@@ -794,54 +800,68 @@ foreach ( acf_get_combined_taxonomy_settings_tabs() as $tab_key => $tab_label )
)
);
acf_render_field_wrap(
array(
'type' => 'text',
'name' => 'meta_box_cb',
'key' => 'meta_box_cb',
'prefix' => 'acf_taxonomy',
'value' => $acf_taxonomy['meta_box_cb'],
'label' => __( 'Register Meta Box Callback', 'acf' ),
'instructions' => __( 'A PHP function name to be called to handle the content of a meta box on your taxonomy. For security, this callback will be executed in a special context without access to any superglobals like $_POST or $_GET.', 'acf' ),
'conditions' => array(
'field' => 'meta_box',
'operator' => '==',
'value' => 'custom',
),
),
'div',
'field'
);
if ( $acf_enable_meta_box_cb_edit || 'custom' === $acf_taxonomy['meta_box'] ) {
$acf_meta_box_cb_instructions = __( 'A PHP function name to be called to handle the content of a meta box on your taxonomy. For security, this callback will be executed in a special context without access to any superglobals like $_POST or $_GET.', 'acf' );
acf_render_field_wrap(
array(
'type' => 'text',
'name' => 'meta_box_sanitize_cb',
'key' => 'meta_box_sanitize_cb',
'prefix' => 'acf_taxonomy',
'value' => $acf_taxonomy['meta_box_sanitize_cb'],
'label' => __( 'Meta Box Sanitization Callback', 'acf' ),
'instructions' => __( 'A PHP function name to be called for sanitizing taxonomy data saved from a meta box.', 'acf' ),
'conditions' => array(
'field' => 'meta_box',
'operator' => '==',
'value' => 'custom',
),
),
'div',
'field'
);
if ( ! $acf_enable_meta_box_cb_edit ) {
if ( is_multisite() ) {
$acf_meta_box_cb_instructions .= ' ' . __( 'By default only super admin users can edit this setting.', 'acf' );
} else {
$acf_meta_box_cb_instructions .= ' ' . __( 'By default only admin users can edit this setting.', 'acf' );
}
}
acf_render_field_wrap(
array(
'type' => 'seperator',
'conditions' => array(
'field' => 'meta_box',
'operator' => '==',
'value' => 'custom',
acf_render_field_wrap(
array(
'type' => 'text',
'name' => 'meta_box_cb',
'key' => 'meta_box_cb',
'prefix' => 'acf_taxonomy',
'value' => $acf_taxonomy['meta_box_cb'],
'label' => __( 'Register Meta Box Callback', 'acf' ),
'instructions' => $acf_meta_box_cb_instructions,
'readonly' => ! $acf_enable_meta_box_cb_edit,
'conditions' => array(
'field' => 'meta_box',
'operator' => '==',
'value' => 'custom',
),
),
)
);
'div',
'field'
);
acf_render_field_wrap(
array(
'type' => 'text',
'name' => 'meta_box_sanitize_cb',
'key' => 'meta_box_sanitize_cb',
'prefix' => 'acf_taxonomy',
'value' => $acf_taxonomy['meta_box_sanitize_cb'],
'label' => __( 'Meta Box Sanitization Callback', 'acf' ),
'instructions' => __( 'A PHP function name to be called for sanitizing taxonomy data saved from a meta box.', 'acf' ),
'readonly' => ! $acf_enable_meta_box_cb_edit,
'conditions' => array(
'field' => 'meta_box',
'operator' => '==',
'value' => 'custom',
),
),
'div',
'field'
);
acf_render_field_wrap(
array(
'type' => 'seperator',
'conditions' => array(
'field' => 'meta_box',
'operator' => '==',
'value' => 'custom',
),
)
);
}
acf_render_field_wrap(
array(

View File

@@ -24,8 +24,9 @@ if ( ! class_exists( 'ACF_Ajax_Query_Users' ) ) :
return new WP_Error( 'acf_invalid_args', __( 'Invalid request args.', 'acf' ), array( 'status' => 404 ) );
}
$nonce = $request['nonce'];
$action = $request['field_key'];
$nonce = $request['nonce'];
$action = $request['field_key'];
$field_action = true;
if ( isset( $request['conditional_logic'] ) && true === (bool) $request['conditional_logic'] ) {
if ( ! acf_current_user_can_admin() ) {
@@ -33,11 +34,12 @@ if ( ! class_exists( 'ACF_Ajax_Query_Users' ) ) :
}
// Use the standard ACF admin nonce.
$nonce = '';
$action = '';
$nonce = '';
$action = '';
$field_action = false;
}
if ( ! acf_verify_ajax( $nonce, $action ) ) {
if ( ! acf_verify_ajax( $nonce, $action, $field_action ) ) {
return new WP_Error( 'acf_invalid_nonce', __( 'Invalid nonce.', 'acf' ), array( 'status' => 404 ) );
}

View File

@@ -687,16 +687,32 @@ function acf_verify_nonce( $value ) {
*
* @since 5.2.3
*
* @param string $nonce The nonce to check.
* @param string $action The action of the nonce.
* @param string $nonce The nonce to check.
* @param string $action The action of the nonce.
* @param boolean $action_is_field If the action is a field, modify the action to match validate the field type.
* @return boolean
*/
function acf_verify_ajax( $nonce = '', $action = '' ) {
function acf_verify_ajax( $nonce = '', $action = '', $action_is_field = false ) {
// Bail early if we don't have a nonce to check.
if ( empty( $nonce ) && empty( $_REQUEST['nonce'] ) ) {
return false;
}
// Build the action if we're trying to validate a specific field nonce.
if ( $action_is_field ) {
if ( ! acf_is_field_key( $action ) ) {
return false;
}
$field = acf_get_field( $action );
if ( empty( $field['type'] ) ) {
return false;
}
$action = 'acf_field_' . $field['type'] . '_' . $action;
}
$nonce_to_check = ! empty( $nonce ) ? $nonce : $_REQUEST['nonce']; // phpcs:ignore WordPress.Security -- We're verifying a nonce here.
$nonce_action = ! empty( $action ) ? $action : 'acf_nonce';
@@ -3974,3 +3990,20 @@ function acf_is_multisite_main_site() {
}
return false;
}
/**
* Allow filterable permissions metabox callbacks.
*
* @since 6.3.10
*
* @param boolean $enable_meta_box_cb_edit Can the current user edit metabox callbacks.
* @return boolean
*/
function acf_settings_enable_meta_box_cb_edit( $enable_meta_box_cb_edit ): bool {
if ( ! is_super_admin() ) {
return false;
}
return (bool) $enable_meta_box_cb_edit;
}
add_filter( 'acf/settings/enable_meta_box_cb_edit', 'acf_settings_enable_meta_box_cb_edit', 1 );

View File

@@ -1,251 +0,0 @@
<?php
/**
* The PluginUpdater class which can be used to pull plugin updates from a new location.
* @package advanced-custom-fields
*/
namespace ACF\Upgrades;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use stdClass;
/**
* The PluginUpdater class which can be used to pull plugin updates from a new location.
*/
class PluginUpdater {
/**
* The URL where the api is located.
* @var ApiUrl
*/
private $api_url;
/**
* The amount of time to wait before checking for new updates.
* @var CacheTime
*/
private $cache_time;
/**
* These properties are passed in when instantiating to identify the plugin and it's update location.
* @var Properties
*/
private $properties;
/**
* Get the class constructed.
*
* @param Properties $properties These properties are passed in when instantiating to identify the plugin and it's update location.
*/
public function __construct( $properties ) {
if (
// This must match the key in "https://wpe-plugin-updates.wpengine.com/plugins.json".
empty( $properties['plugin_slug'] ) ||
// This must be the result of calling plugin_basename( __FILE__ ); in the main plugin root file.
empty( $properties['plugin_basename'] )
) {
// If any of the values we require were not passed, throw a fatal.
error_log( 'WPE Secure Plugin Updater received a malformed request.' );
return;
}
$this->api_url = 'https://wpe-plugin-updates.wpengine.com/';
$this->cache_time = time() + HOUR_IN_SECONDS * 5;
$this->properties = $this->get_full_plugin_properties( $properties, $this->api_url );
if ( ! $this->properties ) {
return;
}
$this->register();
}
/**
* Get the full plugin properties, including the directory name, version, basename, and add a transient name.
*
* @param Properties $properties These properties are passed in when instantiating to identify the plugin and it's update location.
* @param ApiUrl $api_url The URL where the api is located.
*/
public function get_full_plugin_properties( $properties, $api_url ) {
$plugins = \get_plugins();
// Scan through all plugins installed and find the one which matches this one in question.
foreach ( $plugins as $plugin_basename => $plugin_data ) {
// Match using the passed-in plugin's basename.
if ( $plugin_basename === $properties['plugin_basename'] ) {
// Add the values we need to the properties.
$properties['plugin_dirname'] = dirname( $plugin_basename );
$properties['plugin_version'] = $plugin_data['Version'];
$properties['plugin_update_transient_name'] = 'wpesu-plugin-' . sanitize_title( $properties['plugin_dirname'] );
$properties['plugin_update_transient_exp_name'] = 'wpesu-plugin-' . sanitize_title( $properties['plugin_dirname'] ) . '-expiry';
$properties['plugin_manifest_url'] = trailingslashit( $api_url ) . trailingslashit( $properties['plugin_slug'] ) . 'info.json';
return $properties;
}
}
// No matching plugin was found installed.
return null;
}
/**
* Register hooks.
*
* @return void
*/
public function register() {
add_filter( 'plugins_api', array( $this, 'filter_plugin_update_info' ), 20, 3 );
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'filter_plugin_update_transient' ) );
}
/**
* Filter the plugin update transient to take over update notifications.
*
* @param object $transient The site_transient_update_plugins transient.
*
* @handles site_transient_update_plugins
* @return object
*/
public function filter_plugin_update_transient( $transient ) {
// No update object exists. Return early.
if ( empty( $transient ) ) {
return $transient;
}
$result = $this->fetch_plugin_info();
if ( false === $result ) {
return $transient;
}
$res = $this->parse_plugin_info( $result );
if ( version_compare( $this->properties['plugin_version'], $result->version, '<' ) ) {
$transient->response[ $res->plugin ] = $res;
$transient->checked[ $res->plugin ] = $result->version;
} else {
$transient->no_update[ $res->plugin ] = $res;
}
return $transient;
}
/**
* Filters the plugin update information.
*
* @param object $res The response to be modified for the plugin in question.
* @param string $action The action in question.
* @param object $args The arguments for the plugin in question.
*
* @handles plugins_api
* @return object
*/
public function filter_plugin_update_info( $res, $action, $args ) {
// Do nothing if this is not about getting plugin information.
if ( 'plugin_information' !== $action ) {
return $res;
}
// Do nothing if it is not our plugin.
if ( $this->properties['plugin_dirname'] !== $args->slug ) {
return $res;
}
$result = $this->fetch_plugin_info();
// Do nothing if we don't get the correct response from the server.
if ( false === $result ) {
return $res;
}
return $this->parse_plugin_info( $result );
}
/**
* Fetches the plugin update object from the WP Product Info API.
*
* @return object|false
*/
private function fetch_plugin_info() {
// Fetch cache first.
$expiry = get_option( $this->properties['plugin_update_transient_exp_name'], 0 );
$response = get_option( $this->properties['plugin_update_transient_name'] );
if ( empty( $expiry ) || time() > $expiry || empty( $response ) ) {
$response = wp_remote_get(
$this->properties['plugin_manifest_url'],
array(
'timeout' => 10,
'headers' => array(
'Accept' => 'application/json',
),
)
);
if (
is_wp_error( $response ) ||
200 !== wp_remote_retrieve_response_code( $response ) ||
empty( wp_remote_retrieve_body( $response ) )
) {
return false;
}
$response = wp_remote_retrieve_body( $response );
// Cache the response.
update_option( $this->properties['plugin_update_transient_exp_name'], $this->cache_time, false );
update_option( $this->properties['plugin_update_transient_name'], $response, false );
}
$decoded_response = json_decode( $response );
if ( json_last_error() !== JSON_ERROR_NONE ) {
return false;
}
return $decoded_response;
}
/**
* Parses the product info response into an object that WordPress would be able to understand.
*
* @param object $response The response object.
*
* @return stdClass
*/
private function parse_plugin_info( $response ) {
global $wp_version;
$res = new stdClass();
$res->name = $response->name;
$res->slug = $response->slug;
$res->version = $response->version;
$res->requires = $response->requires;
$res->download_link = $response->download_link;
$res->trunk = $response->download_link;
$res->new_version = $response->version;
$res->plugin = $this->properties['plugin_basename'];
$res->package = $response->download_link;
// Plugin information modal and core update table use a strict version comparison, which is weird.
// If we're genuinely not compatible with the point release, use our WP tested up to version.
// otherwise use exact same version as WP to avoid false positive.
$res->tested = 1 === version_compare( substr( $wp_version, 0, 3 ), $response->tested )
? $response->tested
: $wp_version;
$res->sections = array(
'description' => $response->sections->description,
'changelog' => $response->sections->changelog,
);
return $res;
}
}

View File

@@ -110,7 +110,7 @@ if ( ! class_exists( 'acf_field_oembed' ) ) :
)
);
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'] ) ) {
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true ) ) {
die();
}
@@ -169,7 +169,7 @@ if ( ! class_exists( 'acf_field_oembed' ) ) :
public function render_field( $field ) {
$atts = array(
'class' => 'acf-oembed',
'data-nonce' => wp_create_nonce( $field['key'] ),
'data-nonce' => wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] ),
);
if ( $field['value'] ) {

View File

@@ -81,7 +81,7 @@ if ( ! class_exists( 'acf_field_page_link' ) ) :
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key ) ) {
if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic ) ) {
die();
}
@@ -392,7 +392,7 @@ if ( ! class_exists( 'acf_field_page_link' ) ) :
$field['ui'] = 1;
$field['ajax'] = 1;
$field['choices'] = array();
$field['nonce'] = wp_create_nonce( $field['key'] );
$field['nonce'] = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
// populate choices if value exists
if ( ! empty( $field['value'] ) ) {

View File

@@ -76,7 +76,7 @@ if ( ! class_exists( 'acf_field_post_object' ) ) :
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key ) ) {
if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic ) ) {
die();
}
@@ -314,7 +314,7 @@ if ( ! class_exists( 'acf_field_post_object' ) ) :
$field['type'] = 'select';
$field['ui'] = 1;
$field['ajax'] = 1;
$field['nonce'] = wp_create_nonce( $field['key'] );
$field['nonce'] = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
$field['choices'] = array();
// load posts

View File

@@ -102,7 +102,7 @@ if ( ! class_exists( 'acf_field_relationship' ) ) :
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key ) ) {
if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic ) ) {
die();
}
@@ -417,7 +417,7 @@ if ( ! class_exists( 'acf_field_relationship' ) ) :
'data-paged' => 1,
'data-post_type' => '',
'data-taxonomy' => '',
'data-nonce' => wp_create_nonce( $field['key'] ),
'data-nonce' => wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] ),
);
?>

View File

@@ -115,13 +115,19 @@ if ( ! class_exists( 'acf_field_select' ) ) :
$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 ( ! acf_is_field_key( $key ) ) {
if ( ! $is_field_key ) {
if ( ! acf_current_user_can_admin() ) {
die();
}
$nonce = '';
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key ) ) {
if ( ! acf_verify_ajax( $nonce, $key, $is_field_key ) ) {
die();
}
@@ -286,7 +292,7 @@ if ( ! class_exists( 'acf_field_select' ) ) :
$select['data-nonce'] = $field['nonce'];
}
if ( $field['ajax'] && empty( $field['nonce'] ) && acf_is_field_key( $field['key'] ) ) {
$select['data-nonce'] = wp_create_nonce( $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';

View File

@@ -70,7 +70,7 @@ if ( ! class_exists( 'acf_field_taxonomy' ) ) :
$key = '';
}
if ( ! acf_verify_ajax( $nonce, $key ) ) {
if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic ) ) {
die();
}
@@ -470,6 +470,8 @@ if ( ! class_exists( 'acf_field_taxonomy' ) ) :
// 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',
@@ -477,7 +479,7 @@ if ( ! class_exists( 'acf_field_taxonomy' ) ) :
'data-ftype' => $field['field_type'],
'data-taxonomy' => $field['taxonomy'],
'data-allow_null' => $field['allow_null'],
'data-nonce' => wp_create_nonce( $field['key'] ),
'data-nonce' => $nonce,
);
// get taxonomy
$taxonomy = get_taxonomy( $field['taxonomy'] );
@@ -499,11 +501,11 @@ if ( ! class_exists( 'acf_field_taxonomy' ) ) :
if ( $field['field_type'] == 'select' ) {
$field['multiple'] = 0;
$this->render_field_select( $field );
$this->render_field_select( $field, $nonce );
} elseif ( $field['field_type'] == 'multi_select' ) {
$field['multiple'] = 1;
$this->render_field_select( $field );
$this->render_field_select( $field, $nonce );
} elseif ( $field['field_type'] == 'radio' ) {
$this->render_field_checkbox( $field );
} elseif ( $field['field_type'] == 'checkbox' ) {
@@ -524,12 +526,13 @@ if ( ! class_exists( 'acf_field_taxonomy' ) ) :
*
* @param $field - an array holding all the field's data
*/
function render_field_select( $field ) {
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
@@ -766,7 +769,7 @@ if ( ! class_exists( 'acf_field_taxonomy' ) ) :
)
);
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'] ) ) {
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true ) ) {
die();
}

View File

@@ -164,7 +164,7 @@ if ( ! class_exists( 'ACF_Field_User' ) ) :
$field['ui'] = 1;
$field['ajax'] = 1;
$field['choices'] = array();
$field['nonce'] = wp_create_nonce( $field['key'] );
$field['nonce'] = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
// Populate choices.
if ( $field['value'] ) {
@@ -404,7 +404,7 @@ if ( ! class_exists( 'ACF_Field_User' ) ) :
$nonce = acf_request_arg( 'nonce', '' );
$key = acf_request_arg( 'field_key', '' );
if ( ! acf_verify_ajax( $nonce, $key ) ) {
if ( ! acf_verify_ajax( $nonce, $key, true ) ) {
$query->send( new WP_Error( 'acf_invalid_request', __( 'Invalid request.', 'acf' ), array( 'status' => 404 ) ) );
}
}

View File

@@ -691,6 +691,12 @@ if ( ! class_exists( 'ACF_Post_Type' ) ) {
// Validate and prepare the post for export.
$post = $this->validate_post( $post );
$args = $this->get_post_type_args( $post, false );
// Restore original metabox callback.
if ( ! empty( $args['register_meta_box_cb'] ) && ! empty( $post['register_meta_box_cb'] ) ) {
$args['register_meta_box_cb'] = (string) $post['register_meta_box_cb'];
}
$code = var_export( $args, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions -- Used for PHP export.
if ( ! $code ) {
@@ -767,6 +773,30 @@ if ( ! class_exists( 'ACF_Post_Type' ) ) {
return $post;
}
/**
* Prepares an ACF post type for import.
*
* @since 6.3.10
*
* @param array $post The ACF post array.
* @return array
*/
public function prepare_post_for_import( $post ) {
if ( ! acf_get_setting( 'enable_meta_box_cb_edit' ) && ! empty( $post['register_meta_box_cb'] ) ) {
$post['register_meta_box_cb'] = '';
if ( ! empty( $post['ID'] ) ) {
$existing_post = $this->get_post( $post['ID'] );
if ( is_array( $existing_post ) ) {
$post['register_meta_box_cb'] = ! empty( $existing_post['register_meta_box_cb'] ) ? (string) $existing_post['register_meta_box_cb'] : '';
}
}
}
return parent::prepare_post_for_import( $post );
}
/**
* Imports a post type from CPTUI.
*

View File

@@ -577,7 +577,13 @@ if ( ! class_exists( 'ACF_Taxonomy' ) ) {
$objects = (array) $post['object_type'];
$objects = var_export( $objects, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions -- Used for PHP export.
$args = $this->get_taxonomy_args( $post, false );
$args = var_export( $args, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions -- Used for PHP export.
// Restore original metabox callback.
if ( ! empty( $args['meta_box_cb'] ) && ! empty( $post['meta_box_cb'] ) ) {
$args['meta_box_cb'] = $post['meta_box_cb'];
}
$args = var_export( $args, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions -- Used for PHP export.
if ( ! $args ) {
return $return;
@@ -654,6 +660,37 @@ if ( ! class_exists( 'ACF_Taxonomy' ) ) {
return $post;
}
/**
* Prepares an ACF taxonomy for import.
*
* @since 6.3.10
*
* @param array $post The ACF post array.
* @return array
*/
public function prepare_post_for_import( $post ) {
if ( ! acf_get_setting( 'enable_meta_box_cb_edit' ) && ( ! empty( $post['meta_box_cb'] ) || ! empty( $post['meta_box_sanitize_cb'] ) ) ) {
$post['meta_box_cb'] = '';
$post['meta_box_sanitize_cb'] = '';
if ( ! empty( $post['meta_box'] ) && 'custom' === $post['meta_box'] ) {
$post['meta_box'] = 'default';
}
if ( ! empty( $post['ID'] ) ) {
$existing_post = $this->get_post( $post['ID'] );
if ( is_array( $existing_post ) ) {
$post['meta_box'] = ! empty( $existing_post['meta_box'] ) ? (string) $existing_post['meta_box'] : '';
$post['meta_box_cb'] = ! empty( $existing_post['meta_box_cb'] ) ? (string) $existing_post['meta_box_cb'] : '';
$post['meta_box_sanitize_cb'] = ! empty( $existing_post['meta_box_sanitize_cb'] ) ? (string) $existing_post['meta_box_sanitize_cb'] : '';
}
}
}
return parent::prepare_post_for_import( $post );
}
/**
* Imports a taxonomy from CPTUI.
*

View File

@@ -24,7 +24,6 @@ if ( ! class_exists( 'acf_pro' ) ) :
acf_include( 'pro/blocks.php' );
acf_include( 'pro/options-page.php' );
acf_include( 'pro/acf-ui-options-page-functions.php' );
acf_include( 'pro/class-acf-updates.php' );
acf_include( 'pro/updates.php' );
if ( is_admin() ) {

View File

@@ -93,7 +93,7 @@ if ( ! class_exists( 'acf_field_gallery' ) ) :
);
// Validate request.
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'] ) ) {
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true ) ) {
die();
}
@@ -131,7 +131,7 @@ if ( ! class_exists( 'acf_field_gallery' ) ) :
)
);
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'] ) ) {
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true ) ) {
wp_send_json_error();
}
@@ -209,7 +209,7 @@ if ( ! class_exists( 'acf_field_gallery' ) ) :
)
);
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'] ) ) {
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true ) ) {
wp_send_json_error();
}
@@ -387,7 +387,7 @@ if ( ! class_exists( 'acf_field_gallery' ) ) :
'data-mime_types' => $field['mime_types'],
'data-insert' => $field['insert'],
'data-columns' => 4,
'data-nonce' => wp_create_nonce( $field['key'] ),
'data-nonce' => wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] ),
);
// Set gallery height with deafult of 400px and minimum of 200px.

View File

@@ -1046,7 +1046,7 @@ if ( ! class_exists( 'acf_field_repeater' ) ) :
)
);
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'] ) ) {
if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true ) ) {
$error = array( 'error' => __( 'Invalid nonce.', 'acf' ) );
wp_send_json_error( $error, 401 );
}

View File

@@ -164,7 +164,7 @@ class ACF_Repeater_Table {
$div['data-per_page'] = $this->field['rows_per_page'];
$div['data-total_rows'] = $this->field['total_rows'];
$div['data-orig_name'] = $this->field['orig_name'];
$div['data-nonce'] = wp_create_nonce( $this->field['key'] );
$div['data-nonce'] = wp_create_nonce( 'acf_field_' . $this->field['type'] . '_' . $this->field['key'] );
}
if ( empty( $this->value ) ) {

View File

@@ -374,32 +374,6 @@ function acf_pro_get_license() {
return $license;
}
/**
* An ACF specific getter to replace `home_url` in our license checks to ensure we can avoid third party filters.
*
* @since 6.0.1
* @since 6.2.8 - Renamed to acf_pro_get_home_url to match pro exclusive function naming.
*
* @return string $home_url The output from home_url, sans known third party filters which cause license activation issues.
*/
function acf_pro_get_home_url() {
// Disable WPML and TranslatePress's home url overrides for our license check.
add_filter( 'wpml_get_home_url', 'acf_pro_license_ml_intercept', 99, 2 );
add_filter( 'trp_home_url', 'acf_pro_license_ml_intercept', 99, 2 );
if ( acf_pro_is_legacy_multisite() && acf_is_multisite_sub_site() ) {
$home_url = get_home_url( get_main_site_id() );
} else {
$home_url = home_url();
}
// Re-enable WPML and TranslatePress's home url overrides.
remove_filter( 'wpml_get_home_url', 'acf_pro_license_ml_intercept', 99 );
remove_filter( 'trp_home_url', 'acf_pro_license_ml_intercept', 99 );
return $home_url;
}
/**
* Return the original home url inside ACF's home url getter.
*
@@ -477,7 +451,7 @@ function acf_pro_update_license( $key = '' ) {
// vars
$data = array(
'key' => $key,
'url' => acf_pro_get_home_url(),
'url' => acf_get_home_url(),
);
// encode
@@ -526,7 +500,7 @@ function acf_pro_activate_license( $license_key, $silent = false, $automatic = f
'acf_license' => trim( $license_key ),
'acf_version' => acf_get_setting( 'version' ),
'wp_name' => get_bloginfo( 'name' ),
'wp_url' => acf_pro_get_home_url(),
'wp_url' => acf_get_home_url(),
'wp_version' => get_bloginfo( 'version' ),
'wp_language' => get_bloginfo( 'language' ),
'wp_timezone' => get_option( 'timezone_string' ),
@@ -624,7 +598,7 @@ function acf_pro_deactivate_license( $silent = false ) {
// Connect to API.
$post = array(
'acf_license' => $license,
'wp_url' => acf_pro_get_home_url(),
'wp_url' => acf_get_home_url(),
);
$response = acf_updates()->request( 'v2/plugins/deactivate?p=pro', $post );
@@ -729,7 +703,7 @@ function acf_pro_get_license_status( $force_check = false ) {
$post = array(
'acf_license' => $license,
'wp_url' => acf_pro_get_home_url(),
'wp_url' => acf_get_home_url(),
);
$response = acf_updates()->request( 'v2/plugins/validate?p=pro', $post );
@@ -894,7 +868,7 @@ function acf_pro_was_license_refunded( $status = array() ) {
*/
function acf_pro_has_license_url_changed( $license = array(), $url = '' ) {
$license = ! empty( $license ) ? $license : acf_pro_get_license();
$home_url = ! empty( $url ) ? $url : acf_pro_get_home_url();
$home_url = ! empty( $url ) ? $url : acf_get_home_url();
// We can't know without a license, so let's assume not.
if ( ! is_array( $license ) || empty( $license['url'] ) ) {

View File

@@ -4,7 +4,6 @@ Tags: acf, fields, custom fields, meta, repeater
Requires at least: 6.0
Tested up to: 6.6
Requires PHP: 7.4
Stable tag: 6.3.8
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -94,6 +93,15 @@ From your WordPress dashboard
== Changelog ==
= 6.3.10 =
*Release Date 29th October 2024*
* Security - Setting a metabox callback for custom post types and taxonomies now requires being an admin, or super admin for multisite installs
* Security - Field specific ACF nonces are now prefixed, resolving an issue where third party nonces could be treated as valid for AJAX calls
* Enhancement - A new “Close and Add Field” option is now available when editing a field group, inserting a new field inline after the field being edited
* Enhancement - ACF and ACF PRO now share the same plugin updater for improved reliability and performance
* Fix - Exporting post types and taxonomies containing metabox callbacks now correctly exports the user defined callback
= 6.3.9 =
*Release Date 15th October 2024*