Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

Image Studio: Add CIAB/Next Admin support via opt-in filter and next_admin_init hook.
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,26 @@
/**
* Check if Image Studio is enabled.
*
* Enabled when AI features are available and either the request is from an
* Automattician or the Big Sky plugin is active and enabled.
* Enabled by default when Big Sky is active or Jetpack AI features are
* available. The result is passed through the {@see 'jetpack_image_studio_available'}
* filter so consumers (e.g. CIAB / Next Admin) can opt in or out.
*
* @return bool
*/
function is_image_studio_enabled() {
if ( is_ciab_environment() || is_big_sky_enabled() ) {
return true;
}

if ( ! has_jetpack_ai_features() ) {
return false;
}

return true;
$enabled = is_big_sky_enabled() || has_jetpack_ai_features();

/**
* Filter whether Image Studio is available.
*
* Allows consumers (e.g. CIAB / Next Admin) to enable or disable
* Image Studio independently of the default environment checks.
*
* @since $$next-version$$
*
* @param bool $enabled Whether Image Studio is currently enabled.
*/
return (bool) apply_filters( 'jetpack_image_studio_available', $enabled );
}

/**
Expand All @@ -57,12 +62,21 @@ function is_big_sky_enabled() {
/**
* Check if current environment is CIAB (Commerce in a Box) / Next Admin.
*
* Uses the same detection method as Help Center and Agents Manager
* On CIAB sites the entire admin is the CIAB SPA — every request runs in
* CIAB context — so the presence of the CIAB plugin marker constant is a
* sufficient signal.
*
* Note: Help Center and Agents Manager use `did_action( 'next_admin_init' )`
* for the same check. We can't reuse that signal because we also need to
* detect CIAB during REST requests (the CIAB editor calls a REST endpoint
* that fires `enqueue_block_editor_assets` to collect editor assets), and
* `next_admin_init` only fires during the CIAB SPA page render — not during
* REST requests.
*
* @return bool True if CIAB/Next Admin environment.
*/
function is_ciab_environment() {
return (bool) did_action( 'next_admin_init' );
return defined( 'NEXT_ADMIN_PLUGIN_DIR' );
}

/**
Expand Down Expand Up @@ -263,6 +277,9 @@ function do_enqueue_assets() {
return;
}

// Signal to Big Sky that Jetpack is handling Image Studio, so it skips its own loading.
add_filter( 'jetpack_image_studio_enabled', '__return_true', 5 );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude flagged this

Nit (non-blocking): The Big Sky signal filter is added before get_asset_data() is checked:

add_filter( 'jetpack_image_studio_enabled', '__return_true', 5 );`

$asset_data = get_asset_data();
if ( ! $asset_data ) {
    return; // Big Sky already told to skip, but Jetpack won't load either
}

If the asset manifest fetch fails (network error, missing file), Big Sky is signaled to stand down, but Jetpack's Image Studio won't actually enqueue — leaving the user with neither implementation.

I realize signal_image_studio_active() on init already has the same behavior (signals without verifying assets), so this isn't a new issue. But since you're already touching this function, would it be worth moving the add_filter to after the get_asset_data() check succeeds? That way the signal in do_enqueue_assets at least only fires when assets actually load. The init hook signal would still have the pre-existing gap, but this would be a small step toward correctness.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I feel it is okay. The intention of setting this filter is to prevent the Big Sky version from loading. Being a legacy version of the Image Studio, I don't think the Big Sky version should act as a fallback. It should probably be removed from the Big Sky plugin once the Jetpack release has completed.


$asset_data = get_asset_data();
if ( ! $asset_data ) {
return;
Expand Down Expand Up @@ -294,8 +311,9 @@ function do_enqueue_assets() {
);

$image_studio_data = array(
'enabled' => true,
'version' => '1.0',
'enabled' => true,
'version' => '1.0',
'environment' => is_ciab_environment() ? 'ciab-admin' : 'wp-admin',
);

wp_add_inline_script(
Expand All @@ -312,13 +330,26 @@ function do_enqueue_assets() {
);
}

/*
* CIAB / Next Admin SPA context.
*
* In the CIAB SPA, assets enqueued during admin_enqueue_scripts are
* dequeued before the SPA renders. Assets are re-enqueued on
* next_admin_init (after the dequeue sweep). The CIAB consumer must
* opt in via the jetpack_image_studio_available filter before this
* priority fires.
*
* Follows the same pattern as Help Center and Agents Manager.
*/
add_action( 'next_admin_init', __NAMESPACE__ . '\do_enqueue_assets', 1000 );

/**
* Enqueue Image Studio assets in the block editor.
*
* @return void
*/
function enqueue_image_studio() {
if ( ! is_block_editor() ) {
if ( is_ciab_environment() || ! is_block_editor() ) {
return;
}

Expand All @@ -332,7 +363,7 @@ function enqueue_image_studio() {
* @return void
*/
function enqueue_image_studio_admin() {
if ( ! is_media_library() ) {
if ( is_ciab_environment() || ! is_media_library() ) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

use Automattic\Jetpack\Extensions\ImageStudio;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;

require_once JETPACK__PLUGIN_DIR . '/extensions/plugins/image-studio/image-studio.php';
require_once JETPACK__PLUGIN_DIR . '/extensions/plugins/ai-assistant-plugin/ai-assistant-plugin.php';
Expand Down Expand Up @@ -73,6 +75,7 @@ public function set_up() {
public function tear_down() {
delete_transient( ImageStudio\ASSET_TRANSIENT );
remove_all_filters( 'jetpack_image_studio_enabled' );
remove_all_filters( 'jetpack_image_studio_available' );
remove_all_filters( 'pre_http_request' );
remove_all_filters( 'locale' );
remove_all_filters( 'jetpack_ai_enabled' );
Expand Down Expand Up @@ -277,6 +280,64 @@ public function test_is_not_enabled_when_ai_features_disabled() {
$this->assertFalse( ImageStudio\is_image_studio_enabled() );
}

/**
* Enabled when Big Sky is active.
*/
public function test_is_enabled_via_big_sky() {
$this->enable_big_sky();
$this->assertTrue( ImageStudio\is_image_studio_enabled() );
}

/**
* Enabled when opted in via the jetpack_image_studio_available filter,
* even when AI features and Big Sky are unavailable.
*/
public function test_is_enabled_when_opted_in_via_filter() {
$this->disable_ai_features();
add_filter( 'jetpack_image_studio_available', '__return_true' );
$this->assertTrue( ImageStudio\is_image_studio_enabled() );
}

/**
* Not enabled when filter jetpack_image_studio_available returns false,
* even when other conditions (Big Sky) are met.
*/
public function test_is_not_enabled_when_filter_returns_false() {
$this->enable_big_sky();
add_filter( 'jetpack_image_studio_available', '__return_false' );
$this->assertFalse( ImageStudio\is_image_studio_enabled() );
}

// -------------------------------------------------------------------------
// is_ciab_environment() tests
// -------------------------------------------------------------------------

/**
* Default state: CIAB plugin marker constant is not defined.
*/
public function test_is_ciab_environment_false_when_constant_not_defined() {
$this->assertFalse( defined( 'NEXT_ADMIN_PLUGIN_DIR' ) );
$this->assertFalse( ImageStudio\is_ciab_environment() );
}

/**
* CIAB detected when the plugin marker constant is defined.
*
* Runs in a separate process so the `define()` does not leak into other
* tests — PHP constants persist for the rest of the process once set,
* which would otherwise force every subsequent test into CIAB mode.
*
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
#[RunInSeparateProcess]
#[PreserveGlobalState( false )]
public function test_is_ciab_environment_true_when_constant_defined() {
require_once JETPACK__PLUGIN_DIR . '/extensions/plugins/image-studio/image-studio.php';
define( 'NEXT_ADMIN_PLUGIN_DIR', '/fake/ciab/plugin' );
$this->assertTrue( ImageStudio\is_ciab_environment() );
}

// -------------------------------------------------------------------------
// signal_image_studio_active() tests
// -------------------------------------------------------------------------
Expand All @@ -299,6 +360,38 @@ public function test_signal_does_not_add_filter_when_disabled() {
$this->assertFalse( apply_filters( 'jetpack_image_studio_enabled', false ) );
}

/**
* Test do_enqueue_assets also sets the Big Sky signal filter.
*/
public function test_do_enqueue_assets_sets_big_sky_signal() {
$this->enable_big_sky();
set_transient(
ImageStudio\ASSET_TRANSIENT,
array(
'version' => '1.0.0',
'dependencies' => array(),
),
HOUR_IN_SECONDS
);

// Clear any signal from init hook.
remove_all_filters( 'jetpack_image_studio_enabled' );

ImageStudio\do_enqueue_assets();
$this->assertTrue( apply_filters( 'jetpack_image_studio_enabled', false ) );
}

/**
* Test do_enqueue_assets is hooked to next_admin_init at priority 1000.
*/
public function test_do_enqueue_assets_hooked_to_next_admin_init() {
$priority = has_action(
'next_admin_init',
'Automattic\Jetpack\Extensions\ImageStudio\do_enqueue_assets'
);
$this->assertSame( 1000, $priority );
}

// -------------------------------------------------------------------------
// is_block_editor() tests
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -504,6 +597,64 @@ public function test_inline_script_sets_image_studio_data() {
$this->assertTrue( $found, 'Inline script with imageStudioData not found.' );
}

/**
* Test inline script includes wp-admin environment by default.
*/
public function test_inline_script_includes_wp_admin_environment() {
$this->enable_and_enqueue_block_editor();

$inline = $GLOBALS['wp_scripts']->get_data( ImageStudio\FEATURE_NAME, 'before' );

$found = false;
foreach ( $inline as $line ) {
if ( is_string( $line ) && strpos( $line, 'imageStudioData' ) !== false ) {
$found = true;
$this->assertStringContainsString( '"environment":"wp-admin"', $line );
}
}
$this->assertTrue( $found, 'Inline script with imageStudioData not found.' );
}

/**
* Test inline script includes ciab-admin environment in CIAB context.
*
* Runs in a separate process so defining `NEXT_ADMIN_PLUGIN_DIR` does
* not leak into other tests (see is_ciab_environment tests above).
*
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
#[RunInSeparateProcess]
#[PreserveGlobalState( false )]
public function test_inline_script_includes_ciab_environment() {
require_once JETPACK__PLUGIN_DIR . '/extensions/plugins/image-studio/image-studio.php';
define( 'NEXT_ADMIN_PLUGIN_DIR', '/fake/ciab/plugin' );

$GLOBALS['wp_scripts'] = new WP_Scripts();
add_filter( 'jetpack_image_studio_available', '__return_true' );
set_transient(
ImageStudio\ASSET_TRANSIENT,
array(
'version' => '1.0.0',
'dependencies' => array(),
),
HOUR_IN_SECONDS
);

ImageStudio\do_enqueue_assets();

$inline = $GLOBALS['wp_scripts']->get_data( ImageStudio\FEATURE_NAME, 'before' );

$found = false;
foreach ( (array) $inline as $line ) {
if ( is_string( $line ) && strpos( $line, 'imageStudioData' ) !== false ) {
$found = true;
$this->assertStringContainsString( '"environment":"ciab-admin"', $line );
}
}
$this->assertTrue( $found, 'Inline script with imageStudioData not found.' );
}

/**
* Test style is enqueued with wp-components dependency.
*/
Expand Down
Loading