Skip to content

Commit c4742a3

Browse files
fix: incorrect slashes in some json_encoded values for CustomPostTypeStorage (#54)
1 parent 6497dde commit c4742a3

6 files changed

Lines changed: 320 additions & 26 deletions

File tree

src/DataObject/Storage/CustomPostTypeStorage.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ public function insert( array $data ): int {
2121
$post_id = wp_insert_post( [
2222
'post_type' => $this->slug,
2323
'post_status' => 'publish',
24-
'post_title' => $data['title'] ?? '',
24+
'post_title' => $this->prepare_data( $data['title'] ?? '' ),
2525
], true );
2626

2727
if ( is_wp_error( $post_id ) ) {
2828
return 0;
2929
}
3030

31-
update_post_meta( $post_id, self::META_KEY, wp_json_encode( $data ) );
31+
update_post_meta( $post_id, self::META_KEY, $this->prepare_data( $data ) );
3232

3333
return $post_id;
3434
}
@@ -42,11 +42,11 @@ public function update( int $id, array $data ): void {
4242
if ( isset( $data['title'] ) ) {
4343
wp_update_post( [
4444
'ID' => $id,
45-
'post_title' => $data['title'],
45+
'post_title' => $this->prepare_data( $data['title'] ),
4646
] );
4747
}
4848

49-
update_post_meta( $id, self::META_KEY, wp_json_encode( $data ) );
49+
update_post_meta( $id, self::META_KEY, $this->prepare_data( $data ) );
5050
}
5151

5252
public function delete( int $id ): void {
@@ -89,4 +89,19 @@ public function all(): array {
8989

9090
return $results;
9191
}
92+
93+
/**
94+
* We expect $value to be unslashed, as it will either come
95+
* from Request::get_body_params() or be manually set
96+
*
97+
* Post and meta functions both expect slashed data:
98+
* @see https://developer.wordpress.org/reference/hooks/wp_insert_post_data/
99+
* @see https://developer.wordpress.org/reference/functions/update_post_meta/
100+
*/
101+
protected function prepare_data( string|array $value ): string {
102+
if ( is_array( $value ) ) {
103+
$value = wp_json_encode( $value );
104+
}
105+
return wp_slash( $value );
106+
}
92107
}

src/DataView/FieldTypeRegistry.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public function sanitize_boolean( mixed $value ): bool {
209209
public function sanitize_repeater( mixed $value ): string {
210210
// Handle JSON string input (from Tangible Fields hidden input).
211211
if ( is_string( $value ) ) {
212-
$decoded = json_decode( stripslashes( $value ), true );
212+
$decoded = json_decode( $value, true );
213213
if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
214214
return wp_json_encode( $this->sanitize_repeater_rows( $decoded ) );
215215
}

src/DataView/Request.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,11 @@ public function get_nonce( ?string $name = '_wpnonce' ): string {
8282
public function is_post(): bool {
8383
return $this->rest_request->is_method( 'POST' );
8484
}
85+
86+
/**
87+
* Get the POST body parameters (unslashed)
88+
*/
89+
public function get_body_params(): array {
90+
return $this->rest_request->get_body_params();
91+
}
8592
}

src/DataView/RequestRouter.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
use Tangible\Renderer\HtmlRenderer;
1111
use Tangible\RequestHandler\PluralHandler;
1212
use Tangible\RequestHandler\SingularHandler;
13-
use Tangible\RequestHandler\Result;
1413

1514
/**
1615
* Handles request routing and rendering for DataView admin pages.
@@ -441,15 +440,14 @@ protected function handle_settings_submit(): void {
441440
protected function extract_post_data(): array {
442441
$data = [];
443442

444-
// phpcs:ignore WordPress.Security.NonceVerification.Missing
445-
$nested = $_POST[ $this->config->slug ] ?? [];
443+
$params = $this->request->get_body_params();
444+
$nested = $params[ $this->config->slug ] ?? [];
446445

447446
foreach ( $this->config->field_configs as $name => $config ) {
448447
$type = $config['type'];
449448

450449
// Check nested array first (singular/settings mode), then flat POST
451-
// phpcs:ignore WordPress.Security.NonceVerification.Missing
452-
$has_value = isset( $nested[ $name ] ) || isset( $_POST[ $name ] );
450+
$has_value = isset( $nested[ $name ] ) || isset( $params[ $name ] );
453451

454452
if ( ! $has_value ) {
455453
// Handle missing boolean fields (unchecked checkboxes).
@@ -464,8 +462,7 @@ protected function extract_post_data(): array {
464462
}
465463

466464
$sanitizer = $this->registry->get_sanitizer( $type );
467-
// phpcs:ignore WordPress.Security.NonceVerification.Missing
468-
$raw_value = $nested[ $name ] ?? $_POST[ $name ];
465+
$raw_value = $nested[ $name ] ?? $params[ $name ];
469466
$data[ $name ] = $sanitizer( $raw_value );
470467
}
471468

tests/phpunit/data-view.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -487,20 +487,6 @@ public function test_repeater_sanitizer_handles_json_string(): void {
487487
$this->assertEquals( 5, $decoded[0]['count'] );
488488
}
489489

490-
public function test_repeater_sanitizer_handles_escaped_json(): void {
491-
$registry = new FieldTypeRegistry();
492-
$sanitizer = $registry->get_sanitizer( 'repeater' );
493-
494-
// Simulates WordPress POST data with escaped quotes.
495-
$input = addslashes( '[{"key":"abc","name":"Test"}]' );
496-
$result = $sanitizer( $input );
497-
$decoded = json_decode( $result, true );
498-
499-
$this->assertIsArray( $decoded );
500-
$this->assertCount( 1, $decoded );
501-
$this->assertEquals( 'Test', $decoded[0]['name'] );
502-
}
503-
504490
public function test_repeater_sanitizer_handles_array_input(): void {
505491
$registry = new FieldTypeRegistry();
506492
$sanitizer = $registry->get_sanitizer( 'repeater' );

0 commit comments

Comments
 (0)