diff --git a/src/DataView/DataViewConfig.php b/src/DataView/DataViewConfig.php
index 678d2be..a4102d4 100644
--- a/src/DataView/DataViewConfig.php
+++ b/src/DataView/DataViewConfig.php
@@ -282,6 +282,31 @@ public function get_menu_page(): string {
return $this->ui['menu_page'];
}
+ /**
+ * Generate the nonce action name for a given action and optional ID.
+ *
+ * @param string $action Action name.
+ * @param int|null $id Entity ID.
+ * @return string Nonce action name.
+ */
+ public function get_nonce_action( string $action, ?int $id = null ): string {
+ $nonce = $this->get_menu_page() . '_' . $action;
+ if ( $id !== null ) {
+ $nonce .= '_' . $id;
+ }
+ return $nonce;
+ }
+
+ /**
+ * Generate the nonce request field name for a given action.
+ *
+ * @param string $action Action name.
+ * @return string Nonce field name.
+ */
+ public function get_nonce_name( string $action ): string {
+ return '_wpnonce_' . $action;
+ }
+
/**
* Get the admin menu label.
*
diff --git a/src/DataView/Request.php b/src/DataView/Request.php
index 15a56ed..e67537b 100644
--- a/src/DataView/Request.php
+++ b/src/DataView/Request.php
@@ -60,8 +60,8 @@ public function get_current_id(): ?int {
/**
* Get the WordPress nonce from the current request.
*/
- public function get_nonce(): string {
- return (string) $this->rest_request->get_param( '_wpnonce' );
+ public function get_nonce( ?string $name = '_wpnonce' ): string {
+ return (string) $this->rest_request->get_param( $name );
}
/**
diff --git a/src/DataView/RequestRouter.php b/src/DataView/RequestRouter.php
index 90adc5b..123767b 100644
--- a/src/DataView/RequestRouter.php
+++ b/src/DataView/RequestRouter.php
@@ -257,7 +257,7 @@ protected function render_create_form( array $errors = [], array $data = [] ): v
}
echo '
';
@@ -316,8 +316,10 @@ protected function render_edit_form( int $id, array $errors = [] ): void {
}
echo '';
@@ -388,7 +390,7 @@ protected function render_settings_form( array $errors = [] ): void {
}
echo '';
@@ -480,7 +482,9 @@ protected function build_default_layout( Layout $layout ): void {
} );
$layout->sidebar( function ( Sidebar $sidebar ) {
- $sidebar->actions( [ 'save', 'delete' ] );
+ $this->request->get_current_action() === 'create'
+ ? $sidebar->actions( [ 'create' ] )
+ : $sidebar->actions( [ 'save', 'delete' ] );
} );
}
@@ -519,7 +523,7 @@ protected function render_list_table( array $entities ): string {
$html .= '';
$html .= 'Edit';
$html .= ' | ';
- $html .= 'Delete';
+ $html .= 'Delete';
$html .= ' | ';
$html .= '';
@@ -529,6 +533,19 @@ protected function render_list_table( array $entities ): string {
return $html;
}
+ /**
+ * Output a nonce field for an action.
+ *
+ * @param string $action Action name.
+ * @param int|null $id Entity ID.
+ */
+ protected function nonce_field( string $action, ?int $id = null ): void {
+ wp_nonce_field(
+ $this->config->get_nonce_action( $action, $id ),
+ $this->config->get_nonce_name( $action )
+ );
+ }
+
/**
* Verify nonce for an action.
*
@@ -537,8 +554,8 @@ protected function render_list_table( array $entities ): string {
* @return bool True if nonce is valid.
*/
protected function verify_nonce( string $action, ?int $id = null ): bool {
- $nonce_action = $this->url_builder->get_nonce_action( $action, $id );
- $nonce = $this->request->get_nonce();
+ $nonce_action = $this->config->get_nonce_action( $action, $id );
+ $nonce = $this->request->get_nonce( $this->config->get_nonce_name( $action ) );
return wp_verify_nonce( $nonce, $nonce_action ) !== false;
}
diff --git a/src/DataView/UrlBuilder.php b/src/DataView/UrlBuilder.php
index aef632e..77ae2b1 100644
--- a/src/DataView/UrlBuilder.php
+++ b/src/DataView/UrlBuilder.php
@@ -47,30 +47,6 @@ public function url( string $action = 'list', ?int $id = null, array $extra = []
*/
public function url_with_nonce( string $action, ?int $id, string $nonce_action ): string {
$url = $this->url( $action, $id );
- return wp_nonce_url( $url, $nonce_action );
- }
-
- /**
- * Get the menu page slug.
- *
- * @return string Menu page slug.
- */
- public function get_menu_page(): string {
- return $this->menu_page;
- }
-
- /**
- * Generate the nonce action name for a given action and optional ID.
- *
- * @param string $action Action name.
- * @param int|null $id Entity ID.
- * @return string Nonce action name.
- */
- public function get_nonce_action( string $action, ?int $id = null ): string {
- $nonce = $this->menu_page . '_' . $action;
- if ( $id !== null ) {
- $nonce .= '_' . $id;
- }
- return $nonce;
+ return wp_nonce_url( $url, $nonce_action, '_wpnonce_' . $action );
}
}
diff --git a/src/Renderer/TangibleFieldsRenderer.php b/src/Renderer/TangibleFieldsRenderer.php
index 36556b8..f34f287 100644
--- a/src/Renderer/TangibleFieldsRenderer.php
+++ b/src/Renderer/TangibleFieldsRenderer.php
@@ -279,11 +279,19 @@ protected function render_sidebar( array $sidebar ): string {
*/
protected function render_action( string $action ): string {
$config = match ( $action ) {
+ 'create' => [
+ 'label' => __( 'Create', 'tangible-object' ),
+ 'type' => 'submit',
+ 'name' => 'action',
+ 'value' => 'create',
+ 'class' => 'button button-primary',
+ 'onclick' => '',
+ ],
'save' => [
'label' => __( 'Save', 'tangible-object' ),
'type' => 'submit',
'name' => 'action',
- 'value' => 'save',
+ 'value' => 'edit',
'class' => 'button button-primary',
'onclick' => '',
],
diff --git a/tests/phpunit/data-view.php b/tests/phpunit/data-view.php
index ff79edf..6eb9ef0 100644
--- a/tests/phpunit/data-view.php
+++ b/tests/phpunit/data-view.php
@@ -10,6 +10,9 @@
use Tangible\DataView\DataView;
use Tangible\DataView\DataViewConfig;
use Tangible\DataView\FieldTypeRegistry;
+use Tangible\DataView\Request;
+use Tangible\DataView\RequestRouter;
+use Tangible\EditorLayout\Layout;
use Tangible\DataView\LabelGenerator;
use Tangible\DataView\SchemaGenerator;
use Tangible\DataView\UrlBuilder;
@@ -700,14 +703,6 @@ public function test_url_builder_generates_edit_url_with_id(): void {
$this->assertStringContainsString( 'id=42', $url );
}
- public function test_url_builder_generates_nonce_action(): void {
- $builder = new UrlBuilder( 'my_page' );
-
- $this->assertEquals( 'my_page_create', $builder->get_nonce_action( 'create' ) );
- $this->assertEquals( 'my_page_edit_42', $builder->get_nonce_action( 'edit', 42 ) );
- $this->assertEquals( 'my_page_delete_5', $builder->get_nonce_action( 'delete', 5 ) );
- }
-
/**
* ==========================================================================
* DataView with CPT Storage Tests
@@ -1264,6 +1259,92 @@ public function test_dataview_database_storage_options_override_defaults(): void
$this->assertEquals( 'Test Item', $result->get_entity()->get( 'name' ) );
}
+ public function test_data_view_config_generates_nonce_action(): void {
+ $config = new DataViewConfig( [
+ 'slug' => 'test_view',
+ 'label' => 'Test',
+ 'fields' => [ 'name' => 'string' ],
+ ] );
+
+ $this->assertEquals( 'test_view_create', $config->get_nonce_action( 'create' ) );
+ $this->assertEquals( 'test_view_edit_42', $config->get_nonce_action( 'edit', 42 ) );
+ $this->assertEquals( 'test_view_delete_5', $config->get_nonce_action( 'delete', 5 ) );
+ }
+
+ /**
+ * ==========================================================================
+ * RequestRouter Default Layout Tests
+ * ==========================================================================
+ */
+
+ /**
+ * Get default layout structure according to action (create or edit)
+ */
+ private function build_default_layout_for_action( string $action ): array {
+ $request = new class( $action ) extends Request {
+ public function __construct( public string $current_action ) {}
+ public function get_current_action(): string {
+ return $this->current_action;
+ }
+ };
+
+ $config = new DataViewConfig( [
+ 'slug' => 'dv_layout_test',
+ 'label' => 'Book',
+ 'fields' => [
+ 'title' => 'string',
+ 'author' => 'string'
+ ],
+ ] );
+
+ $dataset = new DataSet();
+ $dataset->add_string( 'title' );
+ $dataset->add_string( 'author' );
+
+ $router = new RequestRouter(
+ $config,
+ $dataset,
+ new PluralHandler( new PluralObject( $config->slug ) ),
+ new FieldTypeRegistry(),
+ new UrlBuilder( $config->get_menu_page() ),
+ request: $request
+ );
+
+ $method = new \ReflectionMethod( $router, 'build_default_layout' );
+ $method->setAccessible( true );
+ $layout = new Layout( $dataset );
+ $method->invoke( $router, $layout );
+
+ return $layout->get_structure();
+ }
+
+ public function test_build_default_layout_uses_create_action_for_create(): void {
+ $this->assertEquals(
+ [ 'create' ],
+ $this->build_default_layout_for_action( 'create' )['sidebar']['actions']
+ );
+ }
+
+ public function test_build_default_layout_uses_save_and_delete_for_edit(): void {
+ $this->assertEquals(
+ [ 'save', 'delete' ],
+ $this->build_default_layout_for_action( 'edit' )['sidebar']['actions']
+ );
+ }
+
+ public function test_build_default_layout_section_contains_config_fields(): void {
+ $structure = $this->build_default_layout_for_action( 'edit' );
+
+ $section = $structure['items'][0];
+ $this->assertEquals( 1, count( $structure['items'] ) );
+ $this->assertEquals( 2, count( $section['fields'] ) );
+
+ $field_slugs = array_column( $section['fields'], 'slug' );
+
+ $this->assertEquals( 'section', $section['type'] );
+ $this->assertEquals( [ 'title', 'author' ], $field_slugs );
+ }
+
/**
* ==========================================================================
* TangibleFieldsRenderer Tests
@@ -1506,7 +1587,7 @@ public function test_renderer_action_buttons_output_html(): void {
$this->assertStringContainsString( '', $save_html );
// Test delete button.