Skip to content
Merged
2 changes: 1 addition & 1 deletion build/gutenberg/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('jquery', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-plugins', 'wp-rich-text', 'wp-token-list'), 'version' => 'baf9403ebed6986c7701');
<?php return array('dependencies' => array('jquery', 'lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-plugins', 'wp-rich-text', 'wp-token-list'), 'version' => '8118fa58a5693cf17c9c');
6 changes: 3 additions & 3 deletions build/gutenberg/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/gutenberg/plugins.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => 'e6dbd4b9206c2bdc5366');
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => '56573776e74ba2be617f');
2 changes: 1 addition & 1 deletion build/gutenberg/plugins.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/settings/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'c7aed1f811a0a2f68241');
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'a31191f122b90cdb04f3');
2 changes: 1 addition & 1 deletion build/settings/index.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions gutenberg/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ function ghostkit_register_blocks() {
require_once ghostkit()->plugin_path . 'gutenberg/blocks/form/fields/radio/block.php';
require_once ghostkit()->plugin_path . 'gutenberg/blocks/form/fields/hidden/block.php';

require_once ghostkit()->plugin_path . 'gutenberg/utils/rest-meta-auth/index.php';

require_once ghostkit()->plugin_path . 'gutenberg/plugins/color-palette/index.php';
require_once ghostkit()->plugin_path . 'gutenberg/plugins/customizer/index.php';
require_once ghostkit()->plugin_path . 'gutenberg/plugins/custom-code/index.php';
Expand Down
84 changes: 15 additions & 69 deletions gutenberg/plugins/custom-code/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,6 @@ public function __construct() {
/**
* Check if current user can edit custom JS.
*
* WP LIMITATION WORKAROUND:
* When saving posts in Gutenberg, WordPress sends ALL meta fields via REST API,
* including unchanged ones. The auth_callback is triggered for every meta field,
* and if ANY field fails permission check, the ENTIRE post save fails.
*
* This is a known WordPress core issue (Trac #48426, #57745) where protected
* meta fields prevent updates to unrelated meta fields during bulk operations.
*
* OUR SOLUTION:
* We allow the update if:
* 1. User has proper capability (unfiltered_html), OR
* 2. The meta value is not actually changing (preventing false permission errors)
*
* This maintains security while preventing post save failures.
*
* @param bool $allowed Whether the user can add the meta field.
* @param string $meta_key The meta key.
* @param int $object_id Object ID.
Expand All @@ -49,63 +34,23 @@ public function __construct() {
* @return bool Whether the current user has permission to edit custom JS.
*/
public function can_edit_custom_js_permission( $allowed, $meta_key, $object_id, $user_id, $cap, $caps ) {
// First check: If user has proper capability, always allow.
if ( $this->can_edit_custom_js_simple() ) {
return true;
}

// Second check: Allow if value is not actually changing
// This prevents WordPress bulk update failures when JS fields are
// included in POST data but haven't actually been modified.
$current_value = get_post_meta( $object_id, $meta_key, true );
$new_value = $this->get_meta_value_from_rest_request( $meta_key );

// If we can't determine the new value, be conservative and check capability.
if ( null === $new_value ) {
return false;
}

// Allow if values are identical (no real change being made).
if ( $current_value === $new_value ) {
return true;
}

// Block if user lacks capability and is trying to change the value.
return false;
return ghostkit_rest_meta_auth_allows_unchanged( $meta_key, $object_id, 'unfiltered_html' );
}

/**
* Helper method to extract meta value from current REST API request.
* Check if current user can edit custom CSS.
*
* TECHNICAL NOTE:
* WordPress auth_callback doesn't provide the new meta value directly.
* We need to access the current REST request to compare old vs new values.
* This prevents permission errors when meta fields are included in bulk
* updates but haven't actually changed.
* @param bool $allowed Whether the user can add the meta field.
* @param string $meta_key The meta key.
* @param int $object_id Object ID.
* @param int $user_id User ID.
* @param string $cap Capability name.
* @param array $caps User capabilities.
*
* @param string $meta_key The meta key to look for.
* @return string|null The new meta value from request, or null if not found.
* @return bool Whether the current user has permission to edit custom CSS.
*/
private function get_meta_value_from_rest_request( $meta_key ) {
// Check if we're in a REST API request context.
if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
return null;
}

// Try to get the current request.
global $wp;
if ( ! isset( $wp->query_vars['rest_route'] ) ) {
return null;
}

// Get request body.
$request_body = json_decode( file_get_contents( 'php://input' ), true );

if ( ! $request_body || ! isset( $request_body['meta'][ $meta_key ] ) ) {
return null;
}

return $request_body['meta'][ $meta_key ];
public function can_edit_custom_css_permission( $allowed, $meta_key, $object_id, $user_id, $cap, $caps ) {
return ghostkit_rest_meta_auth_allows_unchanged( $meta_key, $object_id, 'edit_post' );
}

/**
Expand Down Expand Up @@ -137,9 +82,10 @@ public function register_meta() {
'post',
'ghostkit_custom_css',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => array( $this, 'can_edit_custom_css_permission' ),
)
);
register_meta(
Expand Down
Loading
Loading