feat: render admin settings workflow
This commit is contained in:
+143
-37
@@ -10,22 +10,33 @@
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$sync_pairs = $settings->syncPairs();
|
||||
|
||||
if ( array() === $sync_pairs ) {
|
||||
$sync_pairs = array(
|
||||
array(
|
||||
'name' => '',
|
||||
'source_url' => '',
|
||||
'destination_url' => '',
|
||||
'username' => '',
|
||||
'application_password' => '',
|
||||
'default_direction' => 'push',
|
||||
'content_types' => array( 'posts', 'terms', 'media', 'custom_post_types' ),
|
||||
'url_mappings' => array(
|
||||
array(
|
||||
'source' => '',
|
||||
'destination' => '',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html__( 'WP Content Sync', 'wp-content-sync' ); ?></h1>
|
||||
<p><?php echo esc_html__( 'Configure sync pairs, test connections, and run push or pull operations from this screen.', 'wp-content-sync' ); ?></p>
|
||||
|
||||
<div class="notice notice-info">
|
||||
<p>
|
||||
<?php
|
||||
echo esc_html__(
|
||||
'The foundation is installed. Sync pair management, URL mappings, file transport, and REST transport will be added in upcoming implementation phases.',
|
||||
'wp-content-sync'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<?php // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Displays redirect status only. ?>
|
||||
<?php if ( isset( $_GET['wpcs_import_error'] ) ) : ?>
|
||||
<div class="notice notice-error">
|
||||
@@ -40,35 +51,130 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2><?php echo esc_html__( 'Current Defaults', 'wp-content-sync' ); ?></h2>
|
||||
<table class="widefat striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'Configured Sync Pairs', 'wp-content-sync' ); ?></th>
|
||||
<td><?php echo esc_html( (string) count( $settings->syncPairs() ) ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'Logging Level', 'wp-content-sync' ); ?></th>
|
||||
<td><?php echo esc_html( $settings->loggingLevel() ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'URL Replacement', 'wp-content-sync' ); ?></th>
|
||||
<td>
|
||||
<h2><?php echo esc_html__( 'Configuration', 'wp-content-sync' ); ?></h2>
|
||||
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
|
||||
<input type="hidden" name="action" value="wpcs_save_settings" />
|
||||
<?php wp_nonce_field( 'wpcs_save_settings', 'wpcs_settings_nonce' ); ?>
|
||||
|
||||
<h3><?php echo esc_html__( 'Sync Pairs', 'wp-content-sync' ); ?></h3>
|
||||
<?php foreach ( $sync_pairs as $index => $pair ) : ?>
|
||||
<div class="card">
|
||||
<h4>
|
||||
<?php
|
||||
echo esc_html(
|
||||
$settings->automaticUrlReplacementEnabled()
|
||||
? __( 'Enabled', 'wp-content-sync' )
|
||||
: __( 'Disabled', 'wp-content-sync' )
|
||||
printf(
|
||||
/* translators: %d: sync pair number. */
|
||||
esc_html__( 'Sync Pair %d', 'wp-content-sync' ),
|
||||
(int) $index + 1
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'Conflict Strategy', 'wp-content-sync' ); ?></th>
|
||||
<td><?php echo esc_html( $settings->conflictStrategy() ); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</h4>
|
||||
<table class="form-table" role="presentation">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row"><label for="wpcs-pair-name-<?php echo esc_attr( (string) $index ); ?>"><?php echo esc_html__( 'Name', 'wp-content-sync' ); ?></label></th>
|
||||
<td><input class="regular-text" id="wpcs-pair-name-<?php echo esc_attr( (string) $index ); ?>" name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][name]" type="text" value="<?php echo esc_attr( $pair['name'] ); ?>" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="wpcs-source-url-<?php echo esc_attr( (string) $index ); ?>"><?php echo esc_html__( 'Source URL', 'wp-content-sync' ); ?></label></th>
|
||||
<td><input class="regular-text code" id="wpcs-source-url-<?php echo esc_attr( (string) $index ); ?>" name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][source_url]" type="url" value="<?php echo esc_attr( $pair['source_url'] ); ?>" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="wpcs-destination-url-<?php echo esc_attr( (string) $index ); ?>"><?php echo esc_html__( 'Destination URL', 'wp-content-sync' ); ?></label></th>
|
||||
<td><input class="regular-text code" id="wpcs-destination-url-<?php echo esc_attr( (string) $index ); ?>" name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][destination_url]" type="url" value="<?php echo esc_attr( $pair['destination_url'] ); ?>" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="wpcs-username-<?php echo esc_attr( (string) $index ); ?>"><?php echo esc_html__( 'REST Username', 'wp-content-sync' ); ?></label></th>
|
||||
<td><input class="regular-text" id="wpcs-username-<?php echo esc_attr( (string) $index ); ?>" name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][username]" type="text" value="<?php echo esc_attr( $pair['username'] ); ?>" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="wpcs-application-password-<?php echo esc_attr( (string) $index ); ?>"><?php echo esc_html__( 'Application Password', 'wp-content-sync' ); ?></label></th>
|
||||
<td>
|
||||
<input class="regular-text" id="wpcs-application-password-<?php echo esc_attr( (string) $index ); ?>" name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][application_password]" type="password" value="" autocomplete="new-password" placeholder="<?php echo esc_attr( __( 'Enter a new application password to replace the saved value', 'wp-content-sync' ) ); ?>" />
|
||||
<p class="description"><?php echo esc_html__( 'Saved application passwords are never rendered back to the browser.', 'wp-content-sync' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'Default Direction', 'wp-content-sync' ); ?></th>
|
||||
<td>
|
||||
<select name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][default_direction]">
|
||||
<option value="push" <?php echo 'push' === $pair['default_direction'] ? 'selected="selected"' : ''; ?>><?php echo esc_html__( 'Push', 'wp-content-sync' ); ?></option>
|
||||
<option value="pull" <?php echo 'pull' === $pair['default_direction'] ? 'selected="selected"' : ''; ?>><?php echo esc_html__( 'Pull', 'wp-content-sync' ); ?></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'Content Types', 'wp-content-sync' ); ?></th>
|
||||
<td>
|
||||
<?php foreach ( array( 'posts', 'terms', 'media', 'custom_post_types' ) as $content_type ) : ?>
|
||||
<label>
|
||||
<input name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][content_types][]" type="checkbox" value="<?php echo esc_attr( $content_type ); ?>" <?php echo in_array( $content_type, $pair['content_types'], true ) ? 'checked="checked"' : ''; ?> />
|
||||
<?php echo esc_html( ucwords( str_replace( '_', ' ', $content_type ) ) ); ?>
|
||||
</label><br />
|
||||
<?php endforeach; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'URL Mapping', 'wp-content-sync' ); ?></th>
|
||||
<td>
|
||||
<?php $mapping = $pair['url_mappings'][0] ?? array( 'source' => '', 'destination' => '' ); ?>
|
||||
<input class="regular-text code" name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][url_mappings][0][source]" type="url" value="<?php echo esc_attr( $mapping['source'] ); ?>" placeholder="<?php echo esc_attr( __( 'Source URL', 'wp-content-sync' ) ); ?>" />
|
||||
<input class="regular-text code" name="wpcs_settings[sync_pairs][<?php echo esc_attr( (string) $index ); ?>][url_mappings][0][destination]" type="url" value="<?php echo esc_attr( $mapping['destination'] ); ?>" placeholder="<?php echo esc_attr( __( 'Destination URL', 'wp-content-sync' ) ); ?>" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<h3><?php echo esc_html__( 'Defaults', 'wp-content-sync' ); ?></h3>
|
||||
<table class="form-table" role="presentation">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'Logging Level', 'wp-content-sync' ); ?></th>
|
||||
<td>
|
||||
<select name="wpcs_settings[logging_level]">
|
||||
<?php foreach ( array( 'error', 'warning', 'info', 'debug' ) as $level ) : ?>
|
||||
<option value="<?php echo esc_attr( $level ); ?>" <?php echo $level === $settings->loggingLevel() ? 'selected="selected"' : ''; ?>><?php echo esc_html( $level ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'Conflict Strategy', 'wp-content-sync' ); ?></th>
|
||||
<td>
|
||||
<select name="wpcs_settings[conflict_strategy]">
|
||||
<option value="last_write_wins" <?php echo 'last_write_wins' === $settings->conflictStrategy() ? 'selected="selected"' : ''; ?>><?php echo esc_html__( 'Last write wins', 'wp-content-sync' ); ?></option>
|
||||
<option value="manual_review" <?php echo 'manual_review' === $settings->conflictStrategy() ? 'selected="selected"' : ''; ?>><?php echo esc_html__( 'Manual review', 'wp-content-sync' ); ?></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'URL Replacement', 'wp-content-sync' ); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input name="wpcs_settings[automatic_url_replacement]" type="checkbox" value="1" <?php echo $settings->automaticUrlReplacementEnabled() ? 'checked="checked"' : ''; ?> />
|
||||
<?php echo esc_html__( 'Automatically replace mapped URLs during import.', 'wp-content-sync' ); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="wpcs-log-retention"><?php echo esc_html__( 'Log Retention', 'wp-content-sync' ); ?></label></th>
|
||||
<td><input id="wpcs-log-retention" name="wpcs_settings[log_retention]" type="number" min="10" max="1000" value="<?php echo esc_attr( (string) $settings->logRetention() ); ?>" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php echo esc_html__( 'Debug Logging', 'wp-content-sync' ); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input name="wpcs_settings[debug_logging]" type="checkbox" value="1" <?php echo $settings->debugLoggingEnabled() ? 'checked="checked"' : ''; ?> />
|
||||
<?php echo esc_html__( 'Enable verbose debug logs while troubleshooting.', 'wp-content-sync' ); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php submit_button( __( 'Save Settings', 'wp-content-sync' ) ); ?>
|
||||
</form>
|
||||
|
||||
<h2><?php echo esc_html__( 'File Package Import', 'wp-content-sync' ); ?></h2>
|
||||
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" enctype="multipart/form-data">
|
||||
|
||||
@@ -30,8 +30,33 @@ class DashboardTemplateTest extends TestCase {
|
||||
self::assertStringContainsString( 'The package JSON file was validated successfully.', $output );
|
||||
}
|
||||
|
||||
private function renderDashboard(): string {
|
||||
$settings = Settings::fromArray( array() );
|
||||
public function test_it_renders_settings_form_with_nonce_and_escaped_pair_values(): void {
|
||||
$settings = Settings::fromArray(
|
||||
array(
|
||||
'sync_pairs' => array(
|
||||
array(
|
||||
'name' => '<b>Staging</b>',
|
||||
'source_url' => 'https://example.test',
|
||||
'destination_url' => 'https://staging.example.test',
|
||||
'username' => 'codex',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$output = $this->renderDashboard( $settings );
|
||||
|
||||
self::assertStringContainsString( 'action="https://example.test/wp-admin/admin-post.php"', $output );
|
||||
self::assertStringContainsString( 'name="action" value="wpcs_save_settings"', $output );
|
||||
self::assertStringContainsString( 'name="wpcs_settings_nonce"', $output );
|
||||
self::assertStringContainsString( 'Staging', $output );
|
||||
self::assertStringNotContainsString( '<b>Staging</b>', $output );
|
||||
self::assertStringContainsString( 'name="wpcs_settings[sync_pairs][0][application_password]"', $output );
|
||||
self::assertStringContainsString( 'name="wpcs_settings[sync_pairs][0][url_mappings][0][source]"', $output );
|
||||
}
|
||||
|
||||
private function renderDashboard( ?Settings $settings = null ): string {
|
||||
$settings = $settings ?? Settings::fromArray( array() );
|
||||
|
||||
ob_start();
|
||||
include WPCS_PLUGIN_DIR . 'templates/admin/dashboard.php';
|
||||
|
||||
Reference in New Issue
Block a user