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
11 changes: 6 additions & 5 deletions includes/abilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,12 @@ function static_site_importer_register_abilities(): void {
'input_schema' => array(
'type' => 'object',
'properties' => array(
'plan' => array( 'type' => 'object' ),
'slug' => array( 'type' => 'string' ),
'activate' => array( 'type' => 'boolean' ),
'site_title' => array( 'type' => 'string' ),
'overwrite' => array( 'type' => 'boolean' ),
'plan' => array( 'type' => 'object' ),
'slug' => array( 'type' => 'string' ),
'activate' => array( 'type' => 'boolean' ),
'site_title' => array( 'type' => 'string' ),
'overwrite' => array( 'type' => 'boolean' ),
'disable_smilies' => array( 'type' => 'boolean' ),
),
'required' => array( 'plan', 'slug' ),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Static_Site_Importer_Website_Artifact_Import_Input {
),
'activate' => array( 'type' => 'boolean' ),
'overwrite' => array( 'type' => 'boolean' ),
'disable_smilies' => array( 'type' => 'boolean' ),
'fail_on_quality' => array( 'type' => 'boolean' ),
'allow_missing_woocommerce' => array( 'type' => 'boolean' ),
'allow_missing_jetpack' => array( 'type' => 'boolean' ),
Expand Down Expand Up @@ -61,6 +62,7 @@ public static function normalize( array $input, array $defaults = array() ): arr
'stale_page_action' => '',
'activate' => false,
'overwrite' => false,
'disable_smilies' => true,
'fail_on_quality' => false,
'allow_missing_woocommerce' => false,
'allow_missing_jetpack' => false,
Expand Down Expand Up @@ -89,7 +91,7 @@ public static function normalize( array $input, array $defaults = array() ): arr
foreach ( array( 'slug', 'name', 'site_title', 'stale_page_action', 'report', 'asset_materialization_policy' ) as $field ) {
$values[ $field ] = is_scalar( $values[ $field ] ) ? (string) $values[ $field ] : '';
}
foreach ( array( 'activate', 'overwrite', 'fail_on_quality', 'allow_missing_woocommerce', 'allow_missing_jetpack', 'materialize_dependencies', 'require_proven_dynamic_client_assets', 'seed_entities', 'write_theme_report_artifacts' ) as $field ) {
foreach ( array( 'activate', 'overwrite', 'disable_smilies', 'fail_on_quality', 'allow_missing_woocommerce', 'allow_missing_jetpack', 'materialize_dependencies', 'require_proven_dynamic_client_assets', 'seed_entities', 'write_theme_report_artifacts' ) as $field ) {
$values[ $field ] = (bool) $values[ $field ];
}
foreach ( array( 'products_manifest', 'commerce_context', 'asset_map', 'compiler_options', 'source_metadata', 'validation_artifacts' ) as $field ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,16 @@ public static function materialize_prepared( array $prepared ): array {
'kind' => 'activate_theme',
'theme_slug' => $state['theme']['slug'],
);
if ( ! isset( $args['disable_smilies'] ) || false !== (bool) $args['disable_smilies'] ) {
// update_option( 'use_smilies', false ) returns false both on failure and
// when the stored value is already false, so the existing value is the oracle.
if ( false !== get_option( 'use_smilies', false ) ) {
if ( false === update_option( 'use_smilies', false ) ) {
return self::failed_receipt( $state, 'disable_smilies_not_applied' );
}
}
$state['applied']['runtime_policy']['disable_smilies'] = true;
}
if ( '' !== trim( (string) ( $args['site_title'] ?? '' ) ) ) {
update_option( 'blogname', sanitize_text_field( (string) $args['site_title'] ) );
$state['applied']['operations'][] = array( 'kind' => 'site_title' );
Expand Down Expand Up @@ -1099,6 +1109,12 @@ private static function receipt( string $status, array $state ): array {
'status' => 'not_requested',
'files' => array(),
),
'runtime_policy' => array(
'disable_smilies' => array(
'requested' => isset( $state['args']['disable_smilies'] ) ? (bool) $state['args']['disable_smilies'] : true,
'applied' => isset( $state['applied']['runtime_policy']['disable_smilies'] ) && true === $state['applied']['runtime_policy']['disable_smilies'],
),
),
'materialized_pages' => $materialized_pages,
'block_provenance' => $block_provenance,
'block_provenance_count' => $block_provenance_count,
Expand Down
9 changes: 6 additions & 3 deletions static-site-importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,10 @@ static function ( array $args, array $assoc_args ): void {
}
$receipt = static_site_importer_ability_materialize_wordpress_site_plan(
array(
'plan' => $plan,
'slug' => (string) $assoc_args['slug'],
'overwrite' => isset( $assoc_args['overwrite'] ),
'plan' => $plan,
'slug' => (string) $assoc_args['slug'],
'overwrite' => isset( $assoc_args['overwrite'] ),
'disable_smilies' => ! isset( $assoc_args['no-disable-smilies'] ),
)
);
WP_CLI::line( (string) wp_json_encode( $receipt, JSON_UNESCAPED_SLASHES ) );
Expand Down Expand Up @@ -203,6 +204,7 @@ static function ( array $args, array $assoc_args ): void {
'name' => isset( $assoc_args['name'] ) ? (string) $assoc_args['name'] : '',
'activate' => isset( $assoc_args['activate'] ),
'overwrite' => isset( $assoc_args['overwrite'] ),
'disable_smilies' => ! isset( $assoc_args['no-disable-smilies'] ),
'fail_on_quality' => isset( $assoc_args['fail-on-quality'] ),
'allow_missing_woocommerce' => isset( $assoc_args['allow-missing-woocommerce'] ),
'materialize_dependencies' => ! isset( $assoc_args['skip-dependency-materialization'] ),
Expand Down Expand Up @@ -252,6 +254,7 @@ static function ( array $args, array $assoc_args ): void {
'site_title' => isset( $assoc_args['site-title'] ) ? (string) $assoc_args['site-title'] : '',
'activate' => isset( $assoc_args['activate'] ),
'overwrite' => isset( $assoc_args['overwrite'] ),
'disable_smilies' => ! isset( $assoc_args['no-disable-smilies'] ),
'fail_on_quality' => isset( $assoc_args['fail-on-quality'] ),
'allow_missing_woocommerce' => isset( $assoc_args['allow-missing-woocommerce'] ),
'report' => isset( $assoc_args['report'] ) ? (string) $assoc_args['report'] : '',
Expand Down
7 changes: 7 additions & 0 deletions tests/smoke-website-artifact-import-input.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public static function import_website_artifact( array $artifact, array $args = a
'stale_page_action' => 'draft',
'activate' => true,
'overwrite' => true,
'disable_smilies' => true,
'fail_on_quality' => true,
'allow_missing_woocommerce' => true,
'allow_missing_jetpack' => true,
Expand All @@ -124,6 +125,12 @@ public static function import_website_artifact( array $artifact, array $args = a
);
$direct = Static_Site_Importer_Website_Artifact_Import_Input::normalize( $input );

// disable_smilies (issue #780) defaults on so ordinary imports keep literal text.
$default_input = Static_Site_Importer_Website_Artifact_Import_Input::normalize( array( 'slug' => 'default-theme' ) );
$assert( true === $default_input['disable_smilies'], 'disable-smilies-defaults-true' );
$assert( true === Static_Site_Importer_Website_Artifact_Import_Input::normalize( array( 'disable_smilies' => '1' ) )['disable_smilies'], 'disable-smilies-coerces-true-string' );
$assert( false === Static_Site_Importer_Website_Artifact_Import_Input::normalize( array( 'disable_smilies' => '0' ) )['disable_smilies'], 'disable-smilies-coerces-false-string' );

static_site_importer_ability_import_website_artifact( array_merge( $input, array( 'artifact' => array( 'schema' => 'test/artifact/v1' ) ) ) );
$direct_entrypoint = Static_Site_Importer_Theme_Generator::$last_args;

Expand Down
44 changes: 41 additions & 3 deletions tests/smoke-wordpress-site-plan-materializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
$GLOBALS['ssi_plan_root'] = sys_get_temp_dir() . '/ssi-plan-' . bin2hex( random_bytes( 4 ) );
$GLOBALS['ssi_plan_posts'] = array();
$GLOBALS['ssi_plan_meta'] = array();
$GLOBALS['ssi_plan_options'] = array( 'show_on_front' => 'posts', 'page_on_front' => 0, 'blogname' => 'Before' );
$GLOBALS['ssi_plan_options'] = array( 'show_on_front' => 'posts', 'page_on_front' => 0, 'blogname' => 'Before', 'use_smilies' => true );
$GLOBALS['ssi_plan_fail_after'] = 0;
$GLOBALS['ssi_plan_font_requests'] = array();
mkdir( $GLOBALS['ssi_plan_root'], 0777, true );
Expand Down Expand Up @@ -64,8 +64,16 @@ function wp_safe_remote_get( string $url, array $args ) {
}
function wp_remote_retrieve_response_code( $response ): int { return (int) ( $response['response']['code'] ?? 0 ); }
function wp_remote_retrieve_body( $response ): string { return (string) ( $response['body'] ?? '' ); }
function update_option( string $key, $value ): void { $GLOBALS['ssi_plan_options'][ $key ] = $value; }
function get_option( string $key, mixed $default = false ): mixed { return $GLOBALS['ssi_plan_options'][ $key ] ?? $default; }
function update_option( string $key, $value ): bool {
if ( array_key_exists( $key, $GLOBALS['ssi_plan_options'] ) && $GLOBALS['ssi_plan_options'][ $key ] === $value ) {
return false; // Core semantics: unchanged value writes no row and returns false.
}
$GLOBALS['ssi_plan_options'][ $key ] = $value;
return true;
}
function switch_theme( string $slug ): void { $GLOBALS['ssi_plan_options']['stylesheet'] = $slug; }
function convert_smilies( string $content, string $which = 'content' ): string { return ( $GLOBALS['ssi_plan_options']['use_smilies'] ?? true ) ? 'smilied-' . $which : $content; }
function sanitize_text_field( string $value ): string { return $value; }
function update_post_meta( int $id, string $key, string $value ): void { $GLOBALS['ssi_plan_meta'][ $id ][ $key ] = $value; }
function get_post_meta( int $id, string $key, bool $single = true ): string { return (string) ( $GLOBALS['ssi_plan_meta'][ $id ][ $key ] ?? '' ); }
Expand Down Expand Up @@ -406,14 +414,44 @@ function wp_insert_post( array $post, bool $wp_error ) {
$assert( hash_file( 'sha256', $publication_file ) === ( $publication_report['actual_content_hash'] ?? '' ) && $publication_plan['runtime_declarations'][0]['expected_content_hash'] === ( $publication_report['expected_content_hash'] ?? '' ), 'publication receipt proves canonical and resolved content integrity' );
$assert( str_contains( file_get_contents( $publication_file ), 'https://example.test/wp-content/themes/publication-plan/assets/assets/font.woff2' ), 'font-bearing SVG resolves only its declared local font URL' );

$GLOBALS['ssi_plan_options'] = array( 'show_on_front' => 'posts', 'page_on_front' => 0, 'blogname' => 'Before' );
$GLOBALS['ssi_plan_options'] = array( 'show_on_front' => 'posts', 'page_on_front' => 0, 'blogname' => 'Before', 'use_smilies' => true );
$preview = Static_Site_Importer_WordPress_Site_Plan_Materializer::materialize( $plan, array( 'slug' => 'site-plan', 'overwrite' => true ) );
$assert( 'completed' === $preview['status'], 'preview materialization completes' );
$assert( array( 'canonical_validations' => 1, 'plan_resolutions' => 1, 'destination_preflights' => 2, 'immutable_projection_reused' => true ) === ( $preview['preparation'] ?? array() ), 'materialization reuses one immutable projection while repeating destination preflight' );
$assert( 'posts' === $GLOBALS['ssi_plan_options']['show_on_front'] && ! isset( $GLOBALS['ssi_plan_options']['stylesheet'] ), 'activate=false preserves runtime options' );
$activated = Static_Site_Importer_WordPress_Site_Plan_Materializer::materialize( $plan, array( 'slug' => 'site-plan', 'overwrite' => true, 'activate' => true, 'site_title' => 'Activated Plan' ) );
$assert( 'site-plan' === $GLOBALS['ssi_plan_options']['stylesheet'] && 'page' === $GLOBALS['ssi_plan_options']['show_on_front'] && 'Activated Plan' === $GLOBALS['ssi_plan_options']['blogname'], 'activate=true applies theme title and reading policy' );

// disable_smilies (issue #780): non-activating import must not touch the global option.
$GLOBALS['ssi_plan_options'] = array( 'show_on_front' => 'posts', 'page_on_front' => 0, 'blogname' => 'Before', 'use_smilies' => true );
$receipt_default = Static_Site_Importer_WordPress_Site_Plan_Materializer::materialize( $plan, array( 'slug' => 'site-plan', 'overwrite' => true ) );
$assert( true === ( $receipt_default['completed']['runtime_policy']['disable_smilies']['requested'] ?? null ), 'disable-smilies-defaults-requested-true' );
$assert( false === ( $receipt_default['completed']['runtime_policy']['disable_smilies']['applied'] ?? null ), 'disable-smilies-not-applied-without-activate' );
$assert( true === $GLOBALS['ssi_plan_options']['use_smilies'], 'non-activating-import-preserves-use-smilies' );

// Explicit opt-out, non-activating: requested and applied both false.
$receipt_off = Static_Site_Importer_WordPress_Site_Plan_Materializer::materialize( $plan, array( 'slug' => 'site-plan', 'overwrite' => true, 'disable_smilies' => false ) );
$assert( false === ( $receipt_off['completed']['runtime_policy']['disable_smilies']['requested'] ?? null ) && false === ( $receipt_off['completed']['runtime_policy']['disable_smilies']['applied'] ?? null ), 'disable-smilies-false-requested-and-applied-false' );

// Activating import with default policy flips the option so literal :) stays text.
$activated_smilies = Static_Site_Importer_WordPress_Site_Plan_Materializer::materialize( $plan, array( 'slug' => 'site-plan', 'overwrite' => true, 'activate' => true, 'site_title' => 'Activated Plan' ) );
$assert( false === $GLOBALS['ssi_plan_options']['use_smilies'], 'activating-import-sets-use-smilies-false' );
$assert( 'Hello :)' === convert_smilies( 'Hello :)' ), 'convert-smilies-output-unchanged-when-disabled' );
$assert( true === ( $activated_smilies['completed']['runtime_policy']['disable_smilies']['requested'] ?? null ) && true === ( $activated_smilies['completed']['runtime_policy']['disable_smilies']['applied'] ?? null ), 'activating-import-records-requested-and-applied' );

// Explicit opt-out, activating: option untouched, policy not applied.
$GLOBALS['ssi_plan_options'] = array( 'show_on_front' => 'posts', 'page_on_front' => 0, 'blogname' => 'Before', 'use_smilies' => true );
$activated_off = Static_Site_Importer_WordPress_Site_Plan_Materializer::materialize( $plan, array( 'slug' => 'site-plan', 'overwrite' => true, 'activate' => true, 'disable_smilies' => false, 'site_title' => 'Activated Off' ) );
$assert( true === $GLOBALS['ssi_plan_options']['use_smilies'], 'activate-with-disable-smilies-false-keeps-smilies' );
$assert( false === ( $activated_off['completed']['runtime_policy']['disable_smilies']['requested'] ?? null ) && false === ( $activated_off['completed']['runtime_policy']['disable_smilies']['applied'] ?? null ), 'disable-smilies-false-not-applied-on-activate' );

// Repeated activating import: use_smilies already false, update_option returns false (unchanged value).
$GLOBALS['ssi_plan_options']['use_smilies'] = false;
$receipt_repeat_policy = Static_Site_Importer_WordPress_Site_Plan_Materializer::materialize( $plan, array( 'slug' => 'site-plan', 'overwrite' => true, 'activate' => true, 'site_title' => 'Repeat Policy' ) );
$assert( 'completed' === $receipt_repeat_policy['status'], 'repeated-activating-import-completes' );
$assert( true === ( $receipt_repeat_policy['completed']['runtime_policy']['disable_smilies']['requested'] ?? null ) && true === ( $receipt_repeat_policy['completed']['runtime_policy']['disable_smilies']['applied'] ?? null ), 'repeated-activating-import-records-requested-and-applied' );
$assert( false === $GLOBALS['ssi_plan_options']['use_smilies'], 'repeated-activating-import-keeps-use-smilies-false' );

$repeat = Static_Site_Importer_WordPress_Site_Plan_Materializer::materialize( $plan, array( 'slug' => 'site-plan' ) );
$assert( 'completed' === $repeat['status'], 'reconciliation repeat completes' );
$assert( count( $GLOBALS['ssi_plan_posts'] ) === count( $plan['pages'] ), 'reconciliation preserves source page identity' );
Expand Down
Loading