PRC WP Admin Dataview
Shared WP DataViews admin list shells for core and shared post types. A modern way to manage content in WordPress.
by Seth Rubenstein · github.com/pewresearch/prc-wp-admin-dataview · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/pewresearch/prc-wp-admin-dataview/archive/refs/heads/trunk.zipReadme
PRC WP Admin Dataview
Shared DataViews admin list shells for core and shared post types, plus a field-provider contract for domain plugins.
What it does
- Replaces the classic
edit.phplist forpostandpagewith a DataViews screen by default. - Lets editors switch among table, grid, and list layouts. The last layout persists per browser and post type (localStorage).
- Mounts that screen through Gutenberg's
@wordpress/bootinitSinglePageruntime when the Boot script module is available (wp-admin chrome stays; Boot owns the stage layout, router?p=, and snackbars). Do not npm-install or webpack-bundle@wordpress/boot; consume Gutenberg's registered script module only. If Boot is missing, the classic bundle stillcreateRoots#prc-wp-admin-dataview. - Keeps
?classic=1so bulk actions, screen options, Empty Trash, and Quick Edit stay reachable. On DataViews list screens, open the WordPress command palette (Ctrl/Cmd+K) and run Switch to classic {type} table (for example Switch to classic post table). - Renders the Status column as stop-light badges (Gutenberg PostStatus icons or a colored dot, plus matching label) so editors can scan Published vs Draft at a glance.
- Shows active editor avatars and can filter to posts that are currently being edited when the Presence API supports the post type.
- Ships a Parent Post column + parent/child filter on the posts list.
- Lets other plugins enrich rows, query args, localized filter metadata, fields, and actions (SEO, attachments report, social packages).
- Lets editors bulk-edit selected rows through a shell-owned modal. Writes go one field at a time through
POST /prc-api/v3/wp-admin-dataview/field.
Bulk editing
A field is editable only when all of these hold:
readOnlyis nottrue.- It declares
type, orEditis'textarea'/{ control: 'textarea' }. - The inferred control is one of
text,textarea,integer, orselect. Editis not a custom function (parent filter button, media, entity search).
Inference:
- Non-empty
elementsplustype→select boolean→selectof Yes / Notext/email/url/telephone→textEdit: 'textarea'or{ control: 'textarea' }→textareainteger/number→integerdatetime,date,media,color,array,password, customEdit→ omit
type is the opt-in. Fields that only have elements for filters (taxonomies, chart type) stay display/filter until a provider adds type and a PHP update_field handler. Display-only typed fields must set readOnly: true.
Bulk edit is a selection action. Cells stay display-only and the title stays a row link. The modal cannot be dismissed while a job runs (isDismissible / click-outside / Escape are locked). Writes are sequential (await each POST .../field; never Promise.all). Item errors are recorded and the job continues.
Do not add domain “edit fields” row-action modals for simple typed fields. Keep PHP update_field handlers; the shell bulk UI covers the rest.
The shell writes title and status (status allowlist excludes trash and future). Domain plugins still win on their field ids when they return non-null first.
Provider contract
PHP filters
| Filter | Signature | Purpose |
|---|---|---|
prc_wp_admin_dataview_shape_row |
(array $row, WP_Post $post, string $post_type): array |
Add fields to each list row |
prc_wp_admin_dataview_query_args |
(array $args, WP_REST_Request $request, string $post_type): array |
Map DataViews filters to WP_Query |
prc_wp_admin_dataview_localize |
(array $data, string $post_type): array |
Pass filter elements / flags to JS |
prc_wp_admin_dataview_update_field |
(true\|WP_Error\|null $result, int $post_id, string $field, mixed $value, string $post_type) |
Handle a field write; first non-null wins |
prc_wp_admin_dataview_duplicate_args |
(array $args, string $post_type, WP_Post\|null $source): array |
Change include/exclude lists, suffix, or enabled after list config |
prc_wp_admin_dataview_taxonomy_fields |
(array $registry): array |
Add {taxonomy, fieldId, label, defaultVisible, isPrimaryFilter} rows. The shell lazy-loads term options, filters, and shapes those taxonomies when the current post type uses them. |
Constants live on PRC\Platform\Wp_Admin_Dataview\Provider_Registry.
JS hooks
| Hook | Signature | Purpose |
|---|---|---|
prcWpAdminDataview.fields |
(fields, { postType, config }) => fields |
Add / edit DataViews field defs |
prcWpAdminDataview.actions |
(actions, { postType, config, onRefresh }) => actions |
Add or remap row actions. Do not replace the array. |
prcWpAdminDataview.defaultVisibleFields |
(fields, boot) => fields |
Set the initial visible field IDs |
prcWpAdminDataview.pageDescription |
(description, boot) => ReactNode |
Add content below the page title |
prcWpAdminDataview.restQuery |
(args, { view, postType, restPath }) => args |
Map domain field IDs and sorting to REST query arguments |
prcWpAdminDataview.actions adds or remaps incoming shell actions (quiz remaps labels; email drops View on transactional lists). The shell already ships Duplicate after Edit. Do not replace the array. Replacing discards shell Edit (real link), Duplicate, View, Trash (bulk + notices), and later shared actions.
Row navigation uses DataViews renderItemLink with item.edit_url. Prefer that over onClickItem on list screens. Keep onClickItem only for pickers and modals that select a value instead of navigating.
JS SlotFills
The shell provides two named slots. Boot's RootSinglePage wraps SlotFillProvider (the classic fallback does not add a second provider):
prcWpAdminDataview.HeaderActionsrenders next to the default Add New button.prcWpAdminDataview.PageExtrasrenders after the list body and is intended for domain modals.
The shell entry exports HeaderActionsFill, PageExtrasFill, emitPageExtra, and subscribePageExtra. A domain bundle can also call createSlotFill() with the exact names above. Provider scripts must depend on the prc-wp-admin-dataview script handle.
REST
GET /prc-api/v3/wp-admin-dataview/list— paginated rows. A non-emptysearchparam is applied afterprc_wp_admin_dataview_query_args(s,ep_integrate, numericpost__in). Trash-only status lists, chart lists, and anymeta_queryfilter (Working on / watchers, dataset ZIP, quiz type, form action, email status) stay on MySQL. Chart search matches title anddesign_slugonly (not blockpost_content). Default date sort becomes relevance so title hits rank first. When Elasticsearch is down, ElasticPress falls back to MySQL. Anauthorparam of user IDs maps toWP_Queryauthor/author__in.GET /prc-api/v3/wp-admin-dataview/terms?taxonomy=&post_type=—{ value: slug, label }pairs for a registry taxonomy. DataViews filter chips call this on demand. Format and Research Team are primary chips (small lists). Other taxonomies stay under Add filter.POST /prc-api/v3/wp-admin-dataview/fieldwith{ postId, field, value, postType }POST /prc-api/v3/wp-admin-dataview/duplicatewith{ postId, postType }→{ id, edit_url }- Saved filters (per user, per post type):
GET /prc-api/v3/wp-admin-dataview/saved-filters?post_type=POST /prc-api/v3/wp-admin-dataview/saved-filterswith{ post_type, name, filters }PUT /prc-api/v3/wp-admin-dataview/saved-filters/{id}?post_type=with{ name? , filters? }DELETE /prc-api/v3/wp-admin-dataview/saved-filters/{id}?post_type=
Saved filters popover
Each list screen has a filter icon to the left of the page title. It opens a popover of personal named filter presets for that post type only (overlay, so the DataViews table width does not shift). Editors can save the current DataViews filters, apply a preset (replaces filters), update the active preset, rename, or delete. Sets are stored in user meta (prc_wp_admin_dataview_saved_filters) and bootstrapped via window.prcWpAdminDataview.savedFilters.
Status badges
The shell Status field uses StatusBadge (src/fields.jsx) with tones from getStatusBadgeTone() (src/utils/status-badge.js). Badges are stop-lights via @prc/components StatusDotBadge: Gutenberg PostStatus icons (@wordpress/icons) tinted to match the label, or an 8px colored dot when there is no Gutenberg icon (trash):
| Status | Tone class | Icon | Color |
|---|---|---|---|
publish |
--publish |
published |
Success green (#1a8a1a) |
draft |
--draft |
drafts |
Neutral gray (#757575) |
future |
--future |
scheduled |
Warning amber (#c07800) |
private |
--private |
notAllowed |
Charcoal (#1d2327) |
pending |
--pending |
pending |
Warning amber (#c07800) |
trash |
--trash |
Dot | Error red (#cc1818) |
Unknown statuses fall back to the draft tone. Bulk edit still writes raw status strings through POST .../field; only list rendering uses stop-light badges.
Appearance persistence
DataViews appearance settings persist in browser localStorage per post type (prcWpAdminDataview.appearance.${postType}). The sparse document stores only settings that differ from current provider defaults, including last-used filters and the last table, grid, or list layout. Search and pagination stay URL or session state. Explicit dvf_* / status= URL params still win for that visit and are not written to the document unless the user then changes filters.
When a browser has no stored document, the list seeds once from the existing user-meta appearance (localized as boot.appearance) and then writes that seed to localStorage. After the first write, this browser stops reading stale user meta.
Narrow viewports default to list layout (WordPress medium, max-width: 782px) until the editor picks a layout. Stored mode still wins. Named saved-filter presets remain user meta.