add file transport implementation #2
+104
-9
@@ -5,29 +5,39 @@ namespace WPContentSync\Settings;
|
||||
final class Settings {
|
||||
private const LOGGING_LEVELS = array( 'error', 'warning', 'info', 'debug' );
|
||||
private const CONFLICT_STRATEGIES = array( 'last_write_wins', 'manual_review' );
|
||||
private const DIRECTIONS = array( 'push', 'pull' );
|
||||
private const CONTENT_TYPES = array( 'posts', 'terms', 'media', 'custom_post_types' );
|
||||
private const MIN_LOGS = 10;
|
||||
private const MAX_LOGS = 1000;
|
||||
|
||||
/**
|
||||
* @var array<int, array{name: string, source_url: string, destination_url: string}>
|
||||
* @var array<int, array{name: string, source_url: string, destination_url: string, username: string, application_password: string, default_direction: string, content_types: array<int, string>, url_mappings: array<int, array{source: string, destination: string}>}>
|
||||
*/
|
||||
private array $sync_pairs;
|
||||
|
||||
private string $logging_level;
|
||||
private bool $automatic_url_replacement;
|
||||
private string $conflict_strategy;
|
||||
private int $log_retention;
|
||||
private bool $debug_logging;
|
||||
|
||||
/**
|
||||
* @param array<int, array{name: string, source_url: string, destination_url: string}> $sync_pairs Sync pairs.
|
||||
* @param array<int, array{name: string, source_url: string, destination_url: string, username: string, application_password: string, default_direction: string, content_types: array<int, string>, url_mappings: array<int, array{source: string, destination: string}>}> $sync_pairs Sync pairs.
|
||||
*/
|
||||
private function __construct(
|
||||
array $sync_pairs,
|
||||
string $logging_level,
|
||||
bool $automatic_url_replacement,
|
||||
string $conflict_strategy
|
||||
string $conflict_strategy,
|
||||
int $log_retention,
|
||||
bool $debug_logging
|
||||
) {
|
||||
$this->sync_pairs = $sync_pairs;
|
||||
$this->logging_level = $logging_level;
|
||||
$this->automatic_url_replacement = $automatic_url_replacement;
|
||||
$this->conflict_strategy = $conflict_strategy;
|
||||
$this->log_retention = $log_retention;
|
||||
$this->debug_logging = $debug_logging;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,12 +64,16 @@ final class Settings {
|
||||
self::sanitizeSyncPairs( $data['sync_pairs'] ?? array() ),
|
||||
$logging_level,
|
||||
$automatic_url_replacement,
|
||||
$conflict_strategy
|
||||
$conflict_strategy,
|
||||
self::sanitizeLogRetention( $data['log_retention'] ?? 200 ),
|
||||
array_key_exists( 'debug_logging', $data )
|
||||
? self::sanitizeBoolean( $data['debug_logging'] )
|
||||
: false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{name: string, source_url: string, destination_url: string}>
|
||||
* @return array<int, array{name: string, source_url: string, destination_url: string, username: string, application_password: string, default_direction: string, content_types: array<int, string>, url_mappings: array<int, array{source: string, destination: string}>}>
|
||||
*/
|
||||
public function syncPairs(): array {
|
||||
return $this->sync_pairs;
|
||||
@@ -77,6 +91,14 @@ final class Settings {
|
||||
return $this->conflict_strategy;
|
||||
}
|
||||
|
||||
public function logRetention(): int {
|
||||
return $this->log_retention;
|
||||
}
|
||||
|
||||
public function debugLoggingEnabled(): bool {
|
||||
return $this->debug_logging;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
@@ -86,6 +108,8 @@ final class Settings {
|
||||
'logging_level' => $this->logging_level,
|
||||
'automatic_url_replacement' => $this->automatic_url_replacement,
|
||||
'conflict_strategy' => $this->conflict_strategy,
|
||||
'log_retention' => $this->log_retention,
|
||||
'debug_logging' => $this->debug_logging,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -114,7 +138,7 @@ final class Settings {
|
||||
|
||||
/**
|
||||
* @param mixed $pairs Raw sync pairs.
|
||||
* @return array<int, array{name: string, source_url: string, destination_url: string}>
|
||||
* @return array<int, array{name: string, source_url: string, destination_url: string, username: string, application_password: string, default_direction: string, content_types: array<int, string>, url_mappings: array<int, array{source: string, destination: string}>}>
|
||||
*/
|
||||
private static function sanitizeSyncPairs( $pairs ): array {
|
||||
if ( ! is_array( $pairs ) ) {
|
||||
@@ -131,15 +155,86 @@ final class Settings {
|
||||
$name = sanitize_text_field( (string) ( $pair['name'] ?? '' ) );
|
||||
$source_url = esc_url_raw( (string) ( $pair['source_url'] ?? '' ) );
|
||||
$destination_url = esc_url_raw( (string) ( $pair['destination_url'] ?? '' ) );
|
||||
$username = sanitize_text_field( (string) ( $pair['username'] ?? '' ) );
|
||||
$password = sanitize_text_field( (string) ( $pair['application_password'] ?? '' ) );
|
||||
$direction = self::sanitizeChoice( $pair['default_direction'] ?? 'push', self::DIRECTIONS, 'push' );
|
||||
$content_types = self::sanitizeContentTypes( $pair['content_types'] ?? self::CONTENT_TYPES );
|
||||
$url_mappings = self::sanitizeUrlMappings( $pair['url_mappings'] ?? array() );
|
||||
|
||||
if ( '' === $name || '' === $source_url || '' === $destination_url ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sanitized[] = array(
|
||||
'name' => $name,
|
||||
'source_url' => $source_url,
|
||||
'destination_url' => $destination_url,
|
||||
'name' => $name,
|
||||
'source_url' => $source_url,
|
||||
'destination_url' => $destination_url,
|
||||
'username' => $username,
|
||||
'application_password' => $password,
|
||||
'default_direction' => $direction,
|
||||
'content_types' => $content_types,
|
||||
'url_mappings' => $url_mappings,
|
||||
);
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Raw log retention.
|
||||
*/
|
||||
private static function sanitizeLogRetention( $value ): int {
|
||||
return min( self::MAX_LOGS, max( self::MIN_LOGS, (int) $value ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $content_types Raw content type list.
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private static function sanitizeContentTypes( $content_types ): array {
|
||||
if ( ! is_array( $content_types ) ) {
|
||||
return self::CONTENT_TYPES;
|
||||
}
|
||||
|
||||
$sanitized = array();
|
||||
|
||||
foreach ( $content_types as $content_type ) {
|
||||
$content_type = sanitize_text_field( (string) $content_type );
|
||||
|
||||
if ( in_array( $content_type, self::CONTENT_TYPES, true ) && ! in_array( $content_type, $sanitized, true ) ) {
|
||||
$sanitized[] = $content_type;
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $mappings Raw URL mappings.
|
||||
* @return array<int, array{source: string, destination: string}>
|
||||
*/
|
||||
private static function sanitizeUrlMappings( $mappings ): array {
|
||||
if ( ! is_array( $mappings ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$sanitized = array();
|
||||
|
||||
foreach ( $mappings as $mapping ) {
|
||||
if ( ! is_array( $mapping ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$source = esc_url_raw( (string) ( $mapping['source'] ?? '' ) );
|
||||
$destination = esc_url_raw( (string) ( $mapping['destination'] ?? '' ) );
|
||||
|
||||
if ( '' === $source || '' === $destination ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sanitized[] = array(
|
||||
'source' => $source,
|
||||
'destination' => $destination,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,53 @@ class SettingsTest extends TestCase {
|
||||
self::assertTrue( $settings->automaticUrlReplacementEnabled() );
|
||||
}
|
||||
|
||||
public function test_it_sanitizes_full_admin_workflow_settings(): void {
|
||||
$settings = Settings::fromArray(
|
||||
array(
|
||||
'sync_pairs' => array(
|
||||
array(
|
||||
'name' => '<b>Production to Staging</b>',
|
||||
'source_url' => 'https://example.test/',
|
||||
'destination_url' => 'https://staging.example.test/',
|
||||
'username' => '<script>codex</script>',
|
||||
'application_password' => 'secret app password',
|
||||
'default_direction' => 'push',
|
||||
'content_types' => array( 'posts', 'terms', 'media', 'bad_type' ),
|
||||
'url_mappings' => array(
|
||||
array(
|
||||
'source' => 'https://example.test',
|
||||
'destination' => 'https://staging.example.test',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'log_retention' => '50',
|
||||
'debug_logging' => '1',
|
||||
)
|
||||
);
|
||||
|
||||
$pairs = $settings->syncPairs();
|
||||
|
||||
self::assertSame( 'Production to Staging', $pairs[0]['name'] );
|
||||
self::assertSame( 'https://example.test/', $pairs[0]['source_url'] );
|
||||
self::assertSame( 'https://staging.example.test/', $pairs[0]['destination_url'] );
|
||||
self::assertSame( 'codex', $pairs[0]['username'] );
|
||||
self::assertSame( 'secret app password', $pairs[0]['application_password'] );
|
||||
self::assertSame( 'push', $pairs[0]['default_direction'] );
|
||||
self::assertSame( array( 'posts', 'terms', 'media' ), $pairs[0]['content_types'] );
|
||||
self::assertSame(
|
||||
array(
|
||||
array(
|
||||
'source' => 'https://example.test',
|
||||
'destination' => 'https://staging.example.test',
|
||||
),
|
||||
),
|
||||
$pairs[0]['url_mappings']
|
||||
);
|
||||
self::assertSame( 50, $settings->logRetention() );
|
||||
self::assertTrue( $settings->debugLoggingEnabled() );
|
||||
}
|
||||
|
||||
public function test_it_serializes_to_array(): void {
|
||||
$settings = Settings::fromArray(
|
||||
array(
|
||||
@@ -74,14 +121,21 @@ class SettingsTest extends TestCase {
|
||||
array(
|
||||
'sync_pairs' => array(
|
||||
array(
|
||||
'name' => 'Staging',
|
||||
'source_url' => 'https://example.test',
|
||||
'destination_url' => 'https://staging.example.test',
|
||||
'name' => 'Staging',
|
||||
'source_url' => 'https://example.test',
|
||||
'destination_url' => 'https://staging.example.test',
|
||||
'username' => '',
|
||||
'application_password' => '',
|
||||
'default_direction' => 'push',
|
||||
'content_types' => array( 'posts', 'terms', 'media', 'custom_post_types' ),
|
||||
'url_mappings' => array(),
|
||||
),
|
||||
),
|
||||
'logging_level' => 'warning',
|
||||
'automatic_url_replacement' => true,
|
||||
'conflict_strategy' => 'last_write_wins',
|
||||
'log_retention' => 200,
|
||||
'debug_logging' => false,
|
||||
),
|
||||
$settings->toArray()
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user