'POST', 'callback' => array( __CLASS__, 'rest_cell' ), 'permission_callback' => array( __CLASS__, 'rest_can' ), ) ); register_rest_route( 'aieo/v1', self::REST_GRANT, array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'rest_grant' ), 'permission_callback' => array( __CLASS__, 'rest_can' ), ) ); register_rest_route( 'aieo/v1', self::REST_COLUMNS . '/(?P[a-z0-9_-]+)', array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'rest_columns' ), 'permission_callback' => array( __CLASS__, 'rest_can' ), ) ); } private static function load_reports() { $base = AIEO_DIR . 'includes/dmm/reports/'; foreach ( array( 'class-aieo-report-registry.php', 'class-aieo-report-roles.php', 'class-aieo-report-column-settings.php' ) as $file ) { if ( is_readable( $base . $file ) ) { require_once $base . $file; } } } public static function rest_grant( $request ) { self::load_reports(); if ( ! current_user_can( 'manage_options' ) ) { return new WP_Error( 'aieo_dt_grant', __( 'Only an administrator can change report permissions.', 'ai-eshop-optimizer' ), array( 'status' => 403 ) ); } $role = sanitize_key( (string) $request->get_param( 'row' ) ); $cap = sanitize_key( (string) $request->get_param( 'key' ) ); $grant = '1' === (string) $request->get_param( 'value' ); if ( ! class_exists( 'AIEO_Report_Roles' ) || ! AIEO_Report_Roles::set_grant( $role, $cap, $grant ) ) { return new WP_Error( 'aieo_dt_grant', 'administrator' === $role ? __( 'An administrator cannot be locked out of reports.', 'ai-eshop-optimizer' ) : __( 'That role or capability does not exist.', 'ai-eshop-optimizer' ), array( 'status' => 400 ) ); } return rest_ensure_response( array( 'value' => $grant ? '1' : '0', 'message' => $grant ? __( 'Granted.', 'ai-eshop-optimizer' ) : __( 'Revoked. It will stay revoked.', 'ai-eshop-optimizer' ), ) ); } public static function rest_columns( $request ) { self::load_reports(); if ( ! class_exists( 'AIEO_Report_Column_Settings' ) ) { return new WP_Error( 'aieo_dt_columns', __( 'The reports are not loaded.', 'ai-eshop-optimizer' ), array( 'status' => 409 ) ); } $value = '1' === (string) $request->get_param( 'value' ); $res = AIEO_Report_Column_Settings::set( sanitize_key( (string) $request->get_param( 'report' ) ), (string) preg_replace( '/[^a-zA-Z0-9_]/', '', (string) $request->get_param( 'row' ) ), sanitize_key( (string) $request->get_param( 'key' ) ), $value ); if ( is_wp_error( $res ) ) { return new WP_Error( 'aieo_dt_columns', $res->get_error_message(), array( 'status' => 400 ) ); } return rest_ensure_response( array( 'value' => $value ? '1' : '0', 'message' => __( 'Saved.', 'ai-eshop-optimizer' ), ) ); } public static function rest_can( $request ) { return current_user_can( AIEO_CAPABILITY ) && false !== wp_verify_nonce( (string) $request->get_param( 'nonce' ), AIEO_DMM::NONCE_ACTION ); } public static function rest_cell( $request ) { $table = sanitize_text_field( (string) $request->get_param( 'row' ) ); $field = sanitize_key( (string) $request->get_param( 'key' ) ); $value = sanitize_text_field( (string) $request->get_param( 'value' ) ); $options = self::sort_options(); $choice = ( 'sort' === $field && isset( $options[ $value ] ) ) || ( in_array( $field, AIEO_Table_Registry::FLAGS, true ) && in_array( $value, array( '0', '1' ), true ) ); if ( 'sort' !== $field && ! in_array( $field, AIEO_Table_Registry::FLAGS, true ) ) { return new WP_Error( 'aieo_dt_field', __( 'That field cannot be overridden.', 'ai-eshop-optimizer' ), array( 'status' => 400 ) ); } if ( ! $choice ) { return new WP_Error( 'aieo_dt_value', __( 'That is not one of the choices.', 'ai-eshop-optimizer' ), array( 'status' => 400 ) ); } $fresh = self::save_cell( $table, $field, $value ); if ( is_wp_error( $fresh ) ) { return $fresh; } $stored = 'sort' === $field ? (string) $fresh['caps']['sort'] : ( ! empty( $fresh['caps'][ $field ] ) ? '1' : '0' ); $answer = array( 'value' => $stored, 'message' => __( 'Saved.', 'ai-eshop-optimizer' ), ); if ( 'sort' === $field ) { $answer['html'] = esc_html( isset( $options[ $stored ] ) ? $options[ $stored ] : $stored ); $answer['s'] = array( 'sort' => $stored ); } return rest_ensure_response( $answer ); } public static function sort_options() { return array( 'none' => __( 'Off', 'ai-eshop-optimizer' ), 'client' => __( 'In the browser', 'ai-eshop-optimizer' ), 'server' => __( 'On the server', 'ai-eshop-optimizer' ), ); } public function render_subtab() { $this->render_template( 'data-tables.php', array( 'rows' => self::registry_rows(), 'counts' => AIEO_Table_Registry::counts(), 'problems' => AIEO_Table_Registry::problems(), 'defaults' => AIEO_Table_Registry::site_defaults(), 'perms' => self::permission_rows(), 'storefront' => self::storefront_rows(), 'column_grids' => self::column_grids(), ) ); } public static function storefront_rows() { if ( ! class_exists( 'AIEO_Public_Tables' ) || ! is_callable( array( 'AIEO_Public_Tables', 'allowed' ) ) ) { return array(); } global $wpdb; $posts = $wpdb->get_results( "SELECT ID, post_title, post_content FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type IN ('page','post') AND post_content LIKE '%/', (string) $p['post_content'], $m ) ) { continue; } foreach ( $m[1] as $json ) { $attrs = '' === $json ? array() : json_decode( $json, true ); $table = isset( $attrs['table'] ) ? (string) $attrs['table'] : $default; if ( $table === $id ) { $placed[] = array( 'title' => (string) $p['post_title'], 'href' => (string) get_permalink( (int) $p['ID'] ) ); break; } } } $shown = array(); $filters = array(); if ( $def ) { foreach ( (array) $def->columns() as $c ) { $l = isset( $c['label'] ) && '' !== (string) $c['label'] ? (string) $c['label'] : (string) $c['key']; if ( empty( $c['hidden'] ) ) { $shown[] = $l; } if ( ! empty( $c['filter'] ) ) { $filters[] = $l; } } } $out[] = array( 'id' => $id, 'label' => $def ? (string) $def->get( 'label', $id ) : $id, 'page' => $placed ? $placed[0]['title'] . ( count( $placed ) > 1 ? ' +' . ( count( $placed ) - 1 ) : '' ) : '', 'page_href' => $placed ? $placed[0]['href'] : '', 'shown' => $shown, 'filters' => $filters, 'state' => $def ? ( $placed ? 'placed' : 'unplaced' ) : 'invalid', ); } return $out; } public static function storefront_table_html( $rows ) { return AIEO_Table::get_html( array( 'id' => 'aieo-dt-storefront', 'page_id' => 'data_tables', 'row_key' => 'id', 'density' => 'compact', 'rows' => (array) $rows, 'columns' => array( array( 'key' => 'id', 'label' => __( 'Table', 'ai-eshop-optimizer' ), 'type' => 'code', 'subline' => 'label' ), array( 'key' => 'page', 'label' => __( 'Placed on', 'ai-eshop-optimizer' ), 'type' => 'link', 'format' => array( 'href' => 'page_href', 'external' => true ), 'empty' => __( 'Not on any published page yet', 'ai-eshop-optimizer' ), ), array( 'key' => 'shown', 'label' => __( 'Columns shown', 'ai-eshop-optimizer' ), 'type' => 'list' ), array( 'key' => 'filters', 'label' => __( 'Filters', 'ai-eshop-optimizer' ), 'type' => 'list' ), array( 'key' => 'state', 'label' => __( 'State', 'ai-eshop-optimizer' ), 'type' => 'badge', 'format' => array( 'labels' => array( 'placed' => __( 'Live', 'ai-eshop-optimizer' ), 'unplaced' => __( 'Not placed', 'ai-eshop-optimizer' ), 'invalid' => __( 'Definition invalid', 'ai-eshop-optimizer' ), ), 'map' => array( 'placed' => 'success', 'unplaced' => 'warning', 'invalid' => 'destructive' ), ), ), ), 'empty' => __( 'No table is public. A table becomes public by being added to the allowlist in AIEO_Public_Tables, which is a code change and a review.', 'ai-eshop-optimizer' ), ) ); } public static function column_grids() { $out = array(); if ( ! class_exists( 'AIEO_Public_Tables' ) || ! class_exists( 'AIEO_Report_Registry' ) || ! class_exists( 'AIEO_Report_Column_Settings' ) ) { return $out; } foreach ( AIEO_Public_Tables::allowed() as $id ) { $def = AIEO_Report_Registry::get( $id ); if ( ! $def ) { continue; } $out[ $id ] = array( 'label' => (string) $def->get( 'label', $id ), 'html' => AIEO_Table::get_html( array( 'id' => 'aieo-dt-columns-' . sanitize_key( $id ), 'page_id' => 'data_tables', 'edit' => array( 'route' => 'aieo/v1' . self::REST_COLUMNS . '/' . sanitize_key( $id ), 'nonce' => wp_create_nonce( AIEO_DMM::NONCE_ACTION ), ), 'row_key' => 'key', 'density' => 'compact', 'rows' => AIEO_Report_Column_Settings::matrix( $def ), 'columns' => array( array( 'key' => 'key', 'label' => __( 'Column', 'ai-eshop-optimizer' ), 'type' => 'code', 'subline' => 'label' ), array( 'key' => 'type', 'label' => __( 'Type', 'ai-eshop-optimizer' ), 'type' => 'badge' ), self::flag_column( 'show', __( 'Shown', 'ai-eshop-optimizer' ) ), self::flag_column( 'filter', __( 'Filter', 'ai-eshop-optimizer' ) ), array( 'key' => 'kind', 'label' => __( 'Filter kind', 'ai-eshop-optimizer' ), 'type' => 'code', 'empty' => '' ), ), ) ), ); } return $out; } public function ajax_columns_save() { AIEO_DMM::verify_ajax_request(); if ( ! class_exists( 'AIEO_Report_Column_Settings' ) ) { wp_send_json_error( array( 'message' => __( 'The reports are not loaded.', 'ai-eshop-optimizer' ) ) ); } $report = isset( $_POST['report'] ) ? sanitize_key( wp_unslash( $_POST['report'] ) ) : ''; $column = isset( $_POST['column'] ) ? preg_replace( '/[^a-zA-Z0-9_]/', '', (string) wp_unslash( $_POST['column'] ) ) : ''; $field = isset( $_POST['field'] ) ? sanitize_key( wp_unslash( $_POST['field'] ) ) : ''; $value = isset( $_POST['value'] ) ? ( '1' === (string) wp_unslash( $_POST['value'] ) ) : false; $res = AIEO_Report_Column_Settings::set( $report, $column, $field, $value ); if ( is_wp_error( $res ) ) { wp_send_json_error( array( 'message' => $res->get_error_message() ) ); } wp_send_json_success( array( 'message' => __( 'Saved.', 'ai-eshop-optimizer' ), 'settings' => $res ) ); } public static function registry_rows() { $problems = AIEO_Table_Registry::problems(); $out = array(); foreach ( AIEO_Table_Registry::all() as $id => $row ) { $flat = array( 'id' => $id, 'label' => $row['label'], 'page' => $row['page'], 'page_url' => self::page_url( $row ), 'kind' => $row['kind'], 'tier' => AIEO_Table_Registry::tier( $id ), 'scope' => $row['scope'], 'source' => $row['source'], 'rows_est' => $row['rows_est'], 'defects' => $row['defects'], 'defect_count' => count( $row['defects'] ), 'defect_text' => AIEO_Table_Registry::defect_titles( $row['defects'] ), 'note' => $row['note'], 'adopted' => $row['adopted'], 'overridden' => ! empty( $row['overridden'] ), 'problem' => isset( $problems[ $id ] ) ? implode( ' ', $problems[ $id ] ) : '', 'sort' => $row['caps']['sort'], ); foreach ( AIEO_Table_Registry::FLAGS as $flag ) { $flat[ $flag ] = ! empty( $row['caps'][ $flag ] ); } $out[] = $flat; } return $out; } private static function page_url( $row ) { if ( '' === (string) $row['page'] ) { return ''; } $args = array_merge( array( 'page' => AIEO_DMM::MENU_SLUG, 'dmm_tab' => $row['page'] ), (array) $row['query'] ); $url = add_query_arg( $args, admin_url( 'admin.php' ) ); return $row['anchor'] ? $url . '#' . $row['anchor'] : $url; } public static function table_html( $rows ) { return AIEO_Table::get_html( array( 'per_page' => 50, 'caption' => __( 'Registered data tables', 'ai-eshop-optimizer' ), 'sticky_first' => true, 'edit' => array( 'route' => 'aieo/v1' . self::REST_CELL, 'nonce' => wp_create_nonce( AIEO_DMM::NONCE_ACTION ), 'open' => 'click', ), 'id' => 'aieo-dt-registry', 'page_id' => 'data_tables', 'caps' => array( 'search' => true ), 'row_key' => 'id', 'row_type' => 'kind', 'rows' => $rows, 'density' => 'compact', 'columns' => self::columns(), 'actions' => array( array( 'id' => 'reset', 'label' => __( 'Reset', 'ai-eshop-optimizer' ), 'capability' => 'manage_woocommerce', 'variant' => 'secondary', 'hook' => 'aieo-dt-reset', 'confirm' => __( 'Drop this table\'s overrides and follow the shipped defaults again?', 'ai-eshop-optimizer' ), 'visible' => static function ( $row ) { return ! empty( $row['overridden'] ); }, ), ), 'empty' => __( 'No data tables are registered yet.', 'ai-eshop-optimizer' ), ) ); } private static function columns() { $editable = static function ( $row ) { return 'bespoke' !== ( is_array( $row ) ? ( $row['kind'] ?? '' ) : '' ); }; $flag = static function ( $key, $label ) use ( $editable ) { $col = self::flag_column( $key, $label ); $col['format']['editable_if'] = $editable; return $col; }; return array( array( 'key' => 'id', 'label' => __( 'Table', 'ai-eshop-optimizer' ), 'type' => 'code', 'subline' => 'label', 'tooltip' => 'note', 'width' => '20%', ), array( 'key' => 'page', 'label' => __( 'Page', 'ai-eshop-optimizer' ), 'type' => 'link', 'format' => array( 'href' => 'page_url' ), 'width' => 'sm', ), array( 'key' => 'kind', 'label' => __( 'Kind', 'ai-eshop-optimizer' ), 'type' => 'badge', 'format' => array( 'map' => array( 'grid' => 'info', 'form' => 'warning', 'bespoke' => 'muted' ), 'labels' => array( 'grid' => __( 'Grid', 'ai-eshop-optimizer' ), 'form' => __( 'Form', 'ai-eshop-optimizer' ), 'bespoke' => __( 'Bespoke', 'ai-eshop-optimizer' ), ), ), ), array( 'key' => 'tier', 'label' => __( 'Tier', 'ai-eshop-optimizer' ), 'type' => 'badge', 'format' => array( 'map' => array( 'free' => 'neutral', 'pro' => 'success' ), 'labels' => array( 'free' => __( 'Free', 'ai-eshop-optimizer' ), 'pro' => __( 'Pro', 'ai-eshop-optimizer' ), ), ), ), array( 'key' => 'scope', 'label' => __( 'Scope', 'ai-eshop-optimizer' ), 'type' => 'badge', 'format' => array( 'map' => array( 'local' => 'muted', 'saas' => 'info', 'both' => 'success' ), 'labels' => array( 'local' => __( 'Local only', 'ai-eshop-optimizer' ), 'saas' => __( 'Service only', 'ai-eshop-optimizer' ), 'both' => __( 'Both', 'ai-eshop-optimizer' ), ), ), ), array( 'key' => 'source', 'label' => __( 'Source', 'ai-eshop-optimizer' ), 'type' => 'badge', 'format' => array( 'map' => array( 'rows' => 'muted', 'query' => 'neutral', 'sql' => 'warning' ), 'labels' => array( 'rows' => __( 'Callable', 'ai-eshop-optimizer' ), 'query' => __( 'Query', 'ai-eshop-optimizer' ), 'sql' => __( 'Raw SQL', 'ai-eshop-optimizer' ), ), ), ), array( 'key' => 'rows_est', 'label' => __( 'Rows', 'ai-eshop-optimizer' ), 'type' => 'integer', 'empty' => '?', ), array( 'key' => 'adopted', 'label' => __( 'On the contract', 'ai-eshop-optimizer' ), 'type' => 'boolean', 'align' => 'center', ), array( 'key' => 'sort', 'label' => __( 'Sort', 'ai-eshop-optimizer' ), 'type' => 'editable', 'width' => 'sm', 'format' => array( 'as' => 'select', 'options' => self::sort_options(), 'editable_if' => $editable, ), ), $flag( 'paginate', __( 'Pages', 'ai-eshop-optimizer' ) ), $flag( 'search', __( 'Search', 'ai-eshop-optimizer' ) ), $flag( 'filters', __( 'Filters', 'ai-eshop-optimizer' ) ), $flag( 'export', __( 'Export', 'ai-eshop-optimizer' ) ), $flag( 'bulk', __( 'Bulk', 'ai-eshop-optimizer' ) ), $flag( 'editable', __( 'Editing', 'ai-eshop-optimizer' ) ), array( 'key' => 'defects', 'label' => __( 'Known defects', 'ai-eshop-optimizer' ), 'type' => 'list', 'sort_key' => 'defect_count', 'empty' => '', 'format' => array( 'titles' => 'defect_text' ), ), ); } public static function permission_rows() { if ( ! class_exists( 'AIEO_Report_Roles' ) ) { return array(); } $caps = array_keys( AIEO_Report_Roles::capabilities() ); $rooster = AIEO_Report_Roles::roles(); $out = array(); foreach ( wp_roles()->roles as $slug => $role ) { $held = isset( $role['capabilities'] ) ? (array) $role['capabilities'] : array(); $row = array( 'role' => $slug, 'label' => isset( $role['name'] ) ? translate_user_role( $role['name'] ) : $slug, 'rooster' => isset( $rooster[ $slug ] ), 'users' => count( get_users( array( 'role' => $slug, 'fields' => 'ID', 'number' => -1 ) ) ), 'granted' => 0, ); foreach ( $caps as $cap ) { $row[ $cap ] = ! empty( $held[ $cap ] ); if ( $row[ $cap ] ) { $row['granted']++; } } $out[] = $row; } usort( $out, static function ( $a, $b ) { if ( $a['granted'] !== $b['granted'] ) { return $b['granted'] - $a['granted']; } return strcmp( (string) $a['label'], (string) $b['label'] ); } ); return $out; } public static function permissions_table_html( $rows ) { $columns = array( array( 'key' => 'label', 'label' => __( 'Role', 'ai-eshop-optimizer' ), 'subline' => 'role', 'width' => '26%', ), array( 'key' => 'users', 'label' => __( 'Accounts', 'ai-eshop-optimizer' ), 'type' => 'integer', ), ); foreach ( AIEO_Report_Roles::labels() as $cap => $label ) { $columns[] = array( 'key' => $cap, 'label' => $label, 'type' => 'toggle', 'align' => 'center', 'sortable' => false, ); } return AIEO_Table::get_html( array( 'id' => 'aieo-dt-perms', 'page_id' => 'data_tables', 'edit' => array( 'route' => 'aieo/v1' . self::REST_GRANT, 'nonce' => wp_create_nonce( AIEO_DMM::NONCE_ACTION ), 'capability' => 'manage_options', ), 'row_key' => 'role', 'row_type' => 'rooster', 'rows' => $rows, 'density' => 'compact', 'columns' => $columns, 'empty' => __( 'No roles are defined on this site.', 'ai-eshop-optimizer' ), ) ); } public function ajax_grant() { AIEO_DMM::verify_ajax_request(); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Only an administrator can change report permissions.', 'ai-eshop-optimizer' ) ), 403 ); } $role = isset( $_POST['role'] ) ? sanitize_key( wp_unslash( $_POST['role'] ) ) : ''; $cap = isset( $_POST['cap'] ) ? sanitize_key( wp_unslash( $_POST['cap'] ) ) : ''; $grant = ! empty( $_POST['granted'] ) && 'false' !== (string) $_POST['granted']; if ( ! AIEO_Report_Roles::set_grant( $role, $cap, $grant ) ) { wp_send_json_error( array( 'message' => 'administrator' === $role ? __( 'An administrator cannot be locked out of reports.', 'ai-eshop-optimizer' ) : __( 'That role or capability does not exist.', 'ai-eshop-optimizer' ), ) ); } wp_send_json_success( array( 'message' => $grant ? __( 'Granted.', 'ai-eshop-optimizer' ) : __( 'Revoked. It will stay revoked.', 'ai-eshop-optimizer' ), ) ); } private static function flag_column( $key, $label ) { return array( 'key' => $key, 'label' => $label, 'type' => 'toggle', 'align' => 'center', 'sortable' => false, ); } public function ajax_save() { AIEO_DMM::verify_ajax_request(); $table = isset( $_POST['table'] ) ? sanitize_text_field( wp_unslash( $_POST['table'] ) ) : ''; $field = isset( $_POST['field'] ) ? sanitize_key( wp_unslash( $_POST['field'] ) ) : ''; $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : ''; if ( '' === $field ) { wp_send_json_error( array( 'message' => __( 'No field given.', 'ai-eshop-optimizer' ) ) ); } if ( '' === $table ) { if ( 'per_page' !== $field ) { wp_send_json_error( array( 'message' => __( 'That is not a site-wide setting.', 'ai-eshop-optimizer' ) ) ); } AIEO_Table_Registry::set_site_defaults( array( 'per_page' => $value ) ); $defaults = AIEO_Table_Registry::site_defaults(); wp_send_json_success( array( 'per_page' => $defaults['per_page'], 'message' => __( 'Saved.', 'ai-eshop-optimizer' ), ) ); } $fresh = self::save_cell( $table, $field, $value ); if ( is_wp_error( $fresh ) ) { wp_send_json_error( array( 'message' => $fresh->get_error_message() ) ); } wp_send_json_success( array( 'table' => $table, 'caps' => $fresh['caps'], 'per_page' => AIEO_Table_Registry::per_page( $table ), 'message' => __( 'Saved.', 'ai-eshop-optimizer' ), ) ); } public static function save_cell( $table, $field, $value ) { $row = AIEO_Table_Registry::get( $table ); if ( ! $row ) { return new WP_Error( 'aieo_dt_table', __( 'No such table.', 'ai-eshop-optimizer' ), array( 'status' => 404 ) ); } if ( 'bespoke' === $row['kind'] ) { return new WP_Error( 'aieo_dt_bespoke', __( 'This screen is hand written, so its capability set is not read by anything.', 'ai-eshop-optimizer' ), array( 'status' => 409 ) ); } if ( 'per_page' === $field ) { $saved = AIEO_Table_Registry::set_override( $table, array( 'per_page' => $value ) ); } elseif ( 'sort' === $field || in_array( $field, AIEO_Table_Registry::FLAGS, true ) ) { $caps = array( 'sort' => $row['caps']['sort'] ); foreach ( AIEO_Table_Registry::FLAGS as $flag ) { $caps[ $flag ] = ! empty( $row['caps'][ $flag ] ); } $caps[ $field ] = ( 'sort' === $field ) ? $value : ( '1' === $value || 'true' === $value ); $existing = AIEO_Table_Registry::overrides(); $per_page = isset( $existing['tables'][ $table ]['per_page'] ) ? $existing['tables'][ $table ]['per_page'] : null; $saved = AIEO_Table_Registry::set_override( $table, null === $per_page ? array( 'caps' => $caps ) : array( 'caps' => $caps, 'per_page' => $per_page ) ); } else { return new WP_Error( 'aieo_dt_field', __( 'That field cannot be overridden.', 'ai-eshop-optimizer' ), array( 'status' => 400 ) ); } if ( ! $saved ) { return new WP_Error( 'aieo_dt_failed', __( 'Save failed.', 'ai-eshop-optimizer' ), array( 'status' => 500 ) ); } return AIEO_Table_Registry::get( $table ); } public function ajax_reset() { AIEO_DMM::verify_ajax_request(); $table = isset( $_POST['table'] ) ? sanitize_text_field( wp_unslash( $_POST['table'] ) ) : ''; if ( '' === $table || ! AIEO_Table_Registry::get( $table ) ) { wp_send_json_error( array( 'message' => __( 'No such table.', 'ai-eshop-optimizer' ) ) ); } AIEO_Table_Registry::clear_override( $table ); $fresh = AIEO_Table_Registry::get( $table ); wp_send_json_success( array( 'table' => $table, 'caps' => $fresh['caps'], 'message' => __( 'Back to the shipped defaults.', 'ai-eshop-optimizer' ), ) ); } }