'field', 'plural' => 'fields', 'ajax' => false, ) ); $this->field_types = accua_forms_fields_get_types(); } function get_columns() { return array( 'cb' => '', 'name' => __( 'Label', 'contact-forms' ), 'slug' => __( 'Slug', 'contact-forms' ), 'type' => __( 'Type', 'contact-forms' ), 'forms' => __( 'Forms', 'contact-forms' ), 'description' => __( 'Description', 'contact-forms' ), ); } function get_sortable_columns() { return array( 'name' => array( 'name', false ), 'slug' => array( 'slug', false ), 'type' => array( 'type', false ), 'forms' => array( 'forms', false ), ); } function get_bulk_actions() { return array( 'delete' => __( 'Delete', 'contact-forms' ), ); } function column_cb( $item ) { return sprintf( '', esc_attr( $item['slug'] ) ); } function column_default( $item, $column_name ) { if ( isset( $item[ $column_name ] ) ) { return esc_html( $item[ $column_name ] ); } return ''; } function column_name( $item ) { $edit_url = admin_url( 'admin.php?page=accua_forms_fields&edit-fid=' . urlencode( $item['slug'] ) ); $delete_url = wp_nonce_url( admin_url( 'admin.php?page=accua_forms_fields&delete-fid=' . urlencode( $item['slug'] ) ), 'delete_form_field_' . $item['slug'] ); $actions = array( 'edit' => sprintf( '%s', esc_url( $edit_url ), esc_html__( 'Edit', 'contact-forms' ) ), 'delete' => sprintf( '%s', esc_url( $delete_url ), esc_js( __( 'Are you sure you want to delete this field?', 'contact-forms' ) ), esc_html__( 'Delete', 'contact-forms' ) ), ); return sprintf( '%s%s', esc_url( $edit_url ), esc_html( $item['name'] ), $this->row_actions( $actions ) ); } function column_type( $item ) { $type_key = $item['type']; if ( isset( $this->field_types[ $type_key ] ) ) { return esc_html( $this->field_types[ $type_key ] ); } return esc_html( $type_key ); } function column_forms( $item ) { $count = $item['forms']; if ( $count > 0 ) { return '' . (int) $count . ''; } return '0'; } /** * Whether a row matches the current search. * * Searches the text the table actually shows: label, slug, type and * description. The type is matched on its human-readable label as well as * its key, so "Casella di controllo" finds a checkbox field on an Italian * install just as "checkbox" does on an English one. */ private function matches_search( $item ) { $type_label = isset( $this->field_types[ $item['type'] ] ) ? $this->field_types[ $item['type'] ] : ''; $haystack = implode( ' ', array( $item['name'], $item['slug'], $item['type'], $type_label, $item['description'], ) ); // mb_stripos keeps the match case-insensitive for accented characters; // mbstring is not guaranteed to be installed, hence the fallback. if ( function_exists( 'mb_stripos' ) ) { return false !== mb_stripos( $haystack, $this->search ); } return false !== stripos( $haystack, $this->search ); } function no_items() { if ( '' !== $this->search ) { esc_html_e( 'No fields match your search.', 'contact-forms' ); return; } esc_html_e( 'No fields found.', 'contact-forms' ); } function prepare_items() { $columns = $this->get_columns(); $hidden = array(); $sortable = $this->get_sortable_columns(); $this->_column_headers = array( $columns, $hidden, $sortable ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only search parameter $this->search = isset( $_REQUEST['s'] ) ? trim( sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) ) : ''; $avail_fields = get_option( 'accua_forms_avail_fields', array() ); $saved_forms = get_option( 'accua_forms_saved_forms', array() ); // Count how many forms use each field $field_usage = array(); foreach ( $saved_forms as $form ) { if ( ! empty( $form['fields'] ) && is_array( $form['fields'] ) ) { foreach ( array_keys( $form['fields'] ) as $field_slug ) { if ( ! isset( $field_usage[ $field_slug ] ) ) { $field_usage[ $field_slug ] = 0; } $field_usage[ $field_slug ]++; } } } $data = array(); foreach ( $avail_fields as $id => $field ) { $data[] = array( 'slug' => $id, 'name' => $field['name'] ?? $field['label'] ?? $id, 'type' => $field['type'] ?? '', 'forms' => $field_usage[ $id ] ?? 0, 'description' => $field['description'] ?? '', ); } // Search first, so the count, the order and the paging are all those of // the result set rather than of the whole list. if ( '' !== $this->search ) { $data = array_values( array_filter( $data, array( $this, 'matches_search' ) ) ); } // Sorting // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only sort parameter $orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : 'slug'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only sort parameter $order = isset( $_GET['order'] ) ? sanitize_text_field( wp_unslash( $_GET['order'] ) ) : 'asc'; $valid_columns = array( 'name', 'slug', 'type', 'forms' ); if ( ! in_array( $orderby, $valid_columns, true ) ) { $orderby = 'slug'; } $order_asc = ( 'desc' !== strtolower( $order ) ); usort( $data, function ( $a, $b ) use ( $orderby, $order_asc ) { if ( $orderby === 'forms' ) { $result = $a['forms'] - $b['forms']; } else { $result = strcasecmp( $a[ $orderby ], $b[ $orderby ] ); } return $order_asc ? $result : -$result; } ); // Paging. Mostly this is what puts the "N items" count above the table, // which is the feedback a search needs; the list is short enough on a // normal install that the page links never appear. $per_page = $this->get_items_per_page( 'accua_forms_fields_per_page', 50 ); $total_items = count( $data ); $current_page = $this->get_pagenum(); $this->set_pagination_args( array( 'total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => (int) ceil( $total_items / max( 1, $per_page ) ), ) ); $this->items = array_slice( $data, ( $current_page - 1 ) * $per_page, $per_page ); } protected function get_default_primary_column_name() { return 'name'; } }