Skip to content
Merged
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
85 changes: 80 additions & 5 deletions classes/Admin/Shortcode/UI.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ public static function init_editor() {
add_filter( 'mce_buttons', [ __CLASS__, 'mce_buttons' ] );
add_filter( 'mce_external_plugins', [ __CLASS__, 'mce_external_plugins' ] );

// Add core site styles for form previews.
add_editor_style( Popup_Maker::$URL . 'dist/assets/site.css' );
// Load plugin editor styles without registering remote theme styles.
add_filter( 'mce_css', [ __CLASS__, 'mce_css' ] );
add_filter( 'block_editor_settings_all', [ __CLASS__, 'block_editor_settings' ], 10, 2 );

// Process live previews.
add_action( 'wp_ajax_pum_do_shortcode', [ __CLASS__, 'do_shortcode' ] );
Expand Down Expand Up @@ -99,9 +100,6 @@ public static function mce_buttons( $buttons ) {
* Enqueues needed assets.
*/
public static function enqueue_scripts() {
// Register editor styles.
add_editor_style( PUM_Admin_Assets::$css_url . 'admin-editor-styles.css' );

wp_enqueue_style( 'pum-admin-shortcode-ui' );
wp_enqueue_script( 'pum-admin-shortcode-ui' );
wp_localize_script(
Expand All @@ -123,6 +121,83 @@ public static function enqueue_scripts() {
);
}

/**
* Add Popup Maker styles to TinyMCE and Classic blocks.
*
* @param string $stylesheets Comma-separated stylesheet URLs.
* @return string
*/
public static function mce_css( $stylesheets = '' ) {
$stylesheets = is_string( $stylesheets ) ? array_filter( array_map( 'trim', explode( ',', $stylesheets ) ) ) : [];

foreach ( self::editor_stylesheets() as $stylesheet ) {
$stylesheets[] = $stylesheet['url'];
}

return implode( ',', array_unique( $stylesheets ) );
}

/**
* Add Popup Maker styles to the block editor from local files.
*
* WordPress fetches absolute URLs registered through add_editor_style()
* over HTTP while rendering the editor. Reading plugin-owned files directly
* avoids that request while retaining the same CSS in editor settings.
*
* @param array $settings Block editor settings.
* @param mixed $context Block editor context.
* @return array
*/
public static function block_editor_settings( $settings, $context = null ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
if ( ! is_array( $settings ) ) {
return $settings;
}

if ( ! isset( $settings['styles'] ) || ! is_array( $settings['styles'] ) ) {
$settings['styles'] = [];
}

foreach ( self::editor_stylesheets() as $stylesheet ) {
if ( ! is_readable( $stylesheet['path'] ) ) {
continue;
}

$css = file_get_contents( $stylesheet['path'] );

if ( ! is_string( $css ) || '' === $css ) {
continue;
}

$settings['styles'][] = [
'css' => $css,
'__unstableType' => 'theme',
'isGlobalStyles' => false,
];
}

return $settings;
}

/**
* Get Popup Maker editor stylesheet paths and URLs.
*
* @return array<int,array{path:string,url:string}>
*/
private static function editor_stylesheets() {
$suffix = is_rtl() ? '-rtl' : '';

return [
[
'path' => Popup_Maker::$DIR . "dist/assets/site{$suffix}.css",
'url' => Popup_Maker::$URL . "dist/assets/site{$suffix}.css",
],
[
'path' => Popup_Maker::$DIR . "dist/assets/admin-editor-styles{$suffix}.css",
'url' => Popup_Maker::$URL . "dist/assets/admin-editor-styles{$suffix}.css",
],
];
}

/**
* Generates a json object variable to pass to the Shortcode UI front end.
*
Expand Down
21 changes: 20 additions & 1 deletion classes/Controllers/Assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ public function get_packages() {
'vars' => function () {
return [
'cta_types' => $this->container->get( 'cta_types' )->get_as_array(),
'popups' => pum_get_all_popups(),
// Preserve the original models for extensions that filter these variables.
'popups' => false !== has_filter( 'popup_maker/block-editor_localized_vars' ) ? \pum_get_all_popups() : $this->get_block_editor_popup_choices(),
'homeUrl' => home_url(),
'previewNonce' => wp_create_nonce( 'popup-preview' ),
'popupTriggerExcludedBlocks' => apply_filters(
Expand Down Expand Up @@ -280,6 +281,24 @@ public function get_packages() {
return $packages;
}

/**
* Adapt the shared popup title map for block editor select controls.
*
* @return array<int,array{ID:int,post_title:string}>
*/
private function get_block_editor_popup_choices() {
$choices = [];

foreach ( \PUM_Helpers::popup_selectlist( [ 'post_status' => [ 'publish', 'private' ] ] ) as $popup_id => $post_title ) {
$choices[] = [
'ID' => (int) $popup_id,
'post_title' => (string) $post_title,
];
}

return $choices;
}

/**
* Register all package scripts & styles.
*/
Expand Down
113 changes: 113 additions & 0 deletions tests/php/tests/Block_Editor_Assets_Controller_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php
/**
* Tests for block editor popup asset data.
*
* @package Popup_Maker
*/

/**
* Verify block editor popup choices stay lightweight and compatible.
*/
class PUM_Block_Editor_Assets_Controller_Test extends WP_UnitTestCase {

/**
* Existing block editor variable filters retain the original popup models.
*
* @return void
*/
public function test_block_editor_preserves_popup_models_for_existing_filters() {
self::factory()->post->create(
[
'post_type' => 'popup',
'post_status' => 'publish',
]
);

$expected = pum_get_all_popups();
$filter = function ( $vars ) {
return $vars;
};

add_filter( 'popup_maker/block-editor_localized_vars', $filter, 0 );

$assets = \PopupMaker\plugin()->get_controller( 'Assets' );
$packages = $assets->get_packages();
$vars = call_user_func( $packages['block-editor']['vars'] );

remove_filter( 'popup_maker/block-editor_localized_vars', $filter, 0 );

$this->assertSame( $expected, $vars['popups'] );
}

/**
* Authorized editors retain private popups in the viewer title data.
*
* @return void
*/
public function test_block_editor_preserves_private_popup_choices_for_authorized_editors() {
$previous_user_id = get_current_user_id();
$admin_user_id = self::factory()->user->create( [ 'role' => 'administrator' ] );
$private_id = self::factory()->post->create(
[
'post_type' => 'popup',
'post_status' => 'private',
'post_title' => 'Private popup',
]
);

wp_set_current_user( $admin_user_id );

try {
$assets = \PopupMaker\plugin()->get_controller( 'Assets' );
$packages = $assets->get_packages();
$vars = call_user_func( $packages['block-editor']['vars'] );
} finally {
wp_set_current_user( $previous_user_id );
}

$this->assertContains(
[
'ID' => $private_id,
'post_title' => 'Private popup',
],
$vars['popups']
);
}

/**
* Shared title-choice filters are retained in lightweight popup choices.
*
* @return void
*/
public function test_block_editor_preserves_filtered_popup_titles() {
$popup_id = self::factory()->post->create(
[
'post_type' => 'popup',
'post_status' => 'publish',
'post_title' => 'Stored popup title',
]
);

$filter = function ( $titles ) use ( $popup_id ) {
$titles[ $popup_id ] = 'Filtered popup title';

return $titles;
};

add_filter( 'popup_maker/popup_title_choices', $filter );

$assets = \PopupMaker\plugin()->get_controller( 'Assets' );
$packages = $assets->get_packages();
$vars = call_user_func( $packages['block-editor']['vars'] );

remove_filter( 'popup_maker/popup_title_choices', $filter );

$this->assertContains(
[
'ID' => $popup_id,
'post_title' => 'Filtered popup title',
],
$vars['popups']
);
}
}
102 changes: 102 additions & 0 deletions tests/php/tests/PUM_Admin_Shortcode_UI_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
Comment thread
coderabbitai[bot] marked this conversation as resolved.
/**
* Tests for Popup Maker editor stylesheet loading.
*
* @package Popup_Maker
*/

/**
* Verify editor styles do not require HTTP requests.
*/
class PUM_Admin_Shortcode_UI_Test extends WP_UnitTestCase {

/**
* Prepare an editor-capable user.
*
* @return void
*/
public function set_up() {
parent::set_up();

$user_id = self::factory()->user->create( [ 'role' => 'administrator' ] );

wp_set_current_user( $user_id );
update_user_option( $user_id, 'rich_editing', 'true' );
}

/**
* TinyMCE retains both Popup Maker editor stylesheets.
*
* @return void
*/
public function test_mce_css_includes_popup_maker_styles() {
PUM_Admin_Shortcode_UI::init_editor();

$stylesheets = apply_filters( 'mce_css', 'https://example.com/theme.css' );

$this->assertStringContainsString( 'https://example.com/theme.css', $stylesheets );
$this->assertStringContainsString( Popup_Maker::$URL . 'dist/assets/site.css', $stylesheets );
$this->assertStringContainsString( Popup_Maker::$URL . 'dist/assets/admin-editor-styles.css', $stylesheets );
}

/**
* RTL editors load only the RTL Popup Maker stylesheets.
*
* @return void
*/
public function test_mce_css_selects_rtl_popup_maker_styles() {
global $wp_locale;

$original_direction = $wp_locale->text_direction;
$wp_locale->text_direction = 'rtl';

try {
PUM_Admin_Shortcode_UI::init_editor();

$stylesheets = apply_filters( 'mce_css', '' );
} finally {
$wp_locale->text_direction = $original_direction;
}

$this->assertStringContainsString( Popup_Maker::$URL . 'dist/assets/site-rtl.css', $stylesheets );
$this->assertStringContainsString( Popup_Maker::$URL . 'dist/assets/admin-editor-styles-rtl.css', $stylesheets );
$this->assertStringNotContainsString( Popup_Maker::$URL . 'dist/assets/site.css', $stylesheets );
$this->assertStringNotContainsString( Popup_Maker::$URL . 'dist/assets/admin-editor-styles.css', $stylesheets );
}

/**
* Block editor settings receive local CSS without an HTTP request.
*
* @return void
*/
public function test_block_editor_settings_include_local_styles() {
$site_styles_path = Popup_Maker::$DIR . 'dist/assets/site.css';
$admin_styles_path = Popup_Maker::$DIR . 'dist/assets/admin-editor-styles.css';

if ( ! is_readable( $site_styles_path ) || ! is_readable( $admin_styles_path ) ) {
$this->markTestSkipped( 'Dist assets not built in test environment.' );
}

$http_requests = 0;
$filter = function ( $response ) use ( &$http_requests ) {
++$http_requests;

return $response;
};

add_filter( 'pre_http_request', $filter );

PUM_Admin_Shortcode_UI::init_editor();

$settings = apply_filters( 'block_editor_settings_all', [ 'styles' => [] ], null );

remove_filter( 'pre_http_request', $filter );

$this->assertSame( 0, $http_requests );

$styles = array_column( $settings['styles'], 'css' );

$this->assertContains( file_get_contents( $site_styles_path ), $styles );
$this->assertContains( file_get_contents( $admin_styles_path ), $styles );
}
}
Loading