diff --git a/eval/scenarios/admin-ui-list-table.json b/eval/scenarios/admin-ui-list-table.json new file mode 100644 index 0000000..5533fa0 --- /dev/null +++ b/eval/scenarios/admin-ui-list-table.json @@ -0,0 +1,26 @@ +{ + "name": "Add an admin list table for plugin data", + "skills": ["wordpress-router", "wp-project-triage", "wp-admin-ui"], + "query": "Add an admin page to my plugin that shows a searchable, sortable table of items stored in a custom DB table. Include bulk delete. Make sure it's secure.", + "expected_behavior": [ + "Step 1: Run wordpress-router to classify repo kind", + "Step 2: Run wp-project-triage script to detect plugin structure", + "Step 3: Route to wp-admin-ui based on admin interface task", + "Step 4: Register admin menu page with add_menu_page() hooked on admin_menu", + "Step 5: Gate render callback immediately with current_user_can()", + "Step 6: Create class extending WP_List_Table with get_columns(), get_sortable_columns(), get_bulk_actions(), prepare_items()", + "Step 7: Implement prepare_items() with sanitized orderby/order, pagination via set_pagination_args()", + "Step 8: Add nonce field in the form wrapping the table", + "Step 9: Implement process_bulk_action() with check_admin_referer() and current_user_can() before deleting", + "Step 10: Escape all output in column render methods with esc_html(), esc_attr(), esc_url()" + ], + "success_criteria": [ + "Admin page registered with correct capability", + "Render callback gates with current_user_can()", + "WP_List_Table subclass implements required methods", + "prepare_items() sets _column_headers and calls set_pagination_args()", + "Bulk delete verifies nonce and capability before writing to DB", + "All column output is escaped", + "orderby and order query params are sanitized before use in SQL" + ] +} diff --git a/skills/wp-admin-ui/SKILL.md b/skills/wp-admin-ui/SKILL.md new file mode 100644 index 0000000..1c05037 --- /dev/null +++ b/skills/wp-admin-ui/SKILL.md @@ -0,0 +1,123 @@ +--- +name: wp-admin-ui +description: "Use when building WordPress admin interfaces: menu and submenu pages, options pages via Settings API, data tables (WP_List_Table or @wordpress/dataviews), schema-driven forms (@wordpress/dataform), meta boxes, and dashboard widgets. Covers both classic PHP and modern React paradigms." +compatibility: "Targets WordPress 6.9+ (PHP 7.2.24+). Classic approach works on all versions; @wordpress/dataviews and @wordpress/dataform require WP 6.6+." +--- + +# WP Admin UI + +## When to use + +Use this skill when: + +- registering admin menu pages or submenu pages +- building options/settings pages +- displaying tabular data in the admin (list tables, data views) +- building admin forms (Settings API or @wordpress/dataform) +- adding meta boxes to post/page editors +- creating dashboard widgets + +## Inputs required + +- Repo root and target plugin (path to main plugin file). +- WordPress version (determines whether @wordpress/dataviews is available — 6.6+). +- Whether the page is top-level menu or submenu, and required capability. +- For list tables: data source (custom DB table, posts, options, REST endpoint). +- For React-based UI: whether @wordpress/scripts build tooling is present. + +## Procedure + +### 0) Triage and detect project + +1. Run triage: + - `node skills/wp-project-triage/scripts/detect_wp_project.mjs` +2. Detect plugin headers: + - `node skills/wp-plugin-development/scripts/detect_plugins.mjs` +3. Check WordPress version to decide classic vs modern path: + - WP < 6.6 → classic only (PHP, WP_List_Table) + - WP 6.6+ → modern path available (@wordpress/dataviews, @wordpress/dataform) + +### 1) Register admin pages + +- Use `add_menu_page()` for top-level entries; `add_submenu_page()` for children. +- Always pass the minimum required capability (`manage_options` for site-wide settings, narrower caps for scoped tools). +- Hook registration on `admin_menu`. +- Gate the callback body immediately with `current_user_can()` before rendering anything. + +See: +- `references/admin-menus.md` + +### 2a) Classic: Settings API + WP_List_Table + +For options pages: + +- Register each option with `register_setting()` and a `sanitize_callback`. +- Group with `add_settings_section()` and `add_settings_field()`. +- Render using `settings_fields()` + `do_settings_sections()` inside a `