'lead', 'plural' => 'leads', 'ajax' => false, ) ); } /** * Current filters from the request. * * @return array */ public static function filters() { // phpcs:disable WordPress.Security.NonceVerification.Recommended $filters = array( 'status' => isset( $_REQUEST['status'] ) ? sanitize_key( wp_unslash( $_REQUEST['status'] ) ) : '', 'channel' => isset( $_REQUEST['channel'] ) && '' !== $_REQUEST['channel'] ? sanitize_key( wp_unslash( $_REQUEST['channel'] ) ) : null, 'search' => isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : '', 'no_value' => isset( $_REQUEST['value'] ) && 'missing' === $_REQUEST['value'], ); // phpcs:enable if ( 'none' === $filters['channel'] ) { $filters['channel'] = ''; } /** * Filters what the list and the board of leads show, e.g. only the leads of one person. * * @param array $filters status, channel, search, no_value, ids (lead IDs, or null for all) * and the period of the report. */ return (array) apply_filters( 'dealroot_lead_filters', array_merge( $filters, Report_State::query_args() ) ); } /** * Lead counts per email address on the current page. * * @var array */ private $repeat_counts = array(); /** * Columns. * * @return array */ public function get_columns() { return array( 'cb' => '', 'name' => __( 'Lead', 'dealroot' ), 'channel' => __( 'Channel', 'dealroot' ), 'campaign' => __( 'Campaign', 'dealroot' ), 'form_name' => __( 'Form', 'dealroot' ), 'status' => __( 'Status', 'dealroot' ), 'value' => __( 'Value', 'dealroot' ), 'created_at' => __( 'Date', 'dealroot' ), ); } /** * Sortable columns. * * @return array */ protected function get_sortable_columns() { return array( 'name' => array( 'name', false ), 'channel' => array( 'channel', false ), 'status' => array( 'status', false ), 'value' => array( 'value', true ), 'created_at' => array( 'created_at', true ), ); } /** * Bulk actions. * * @return array */ protected function get_bulk_actions() { $actions = array(); foreach ( Leads::status_labels() as $status => $label ) { /* translators: %s: lead status. */ $actions[ 'mark_' . $status ] = sprintf( __( 'Mark as: %s', 'dealroot' ), $label ); } $actions['delete'] = __( 'Delete permanently', 'dealroot' ); return $actions; } /** * Runs bulk actions before output and redirects back. */ public function process_bulk_action() { $action = $this->current_action(); if ( ! $action ) { return; } check_admin_referer( 'bulk-leads' ); if ( ! current_user_can( Admin::capability() ) ) { wp_die( esc_html__( 'You are not allowed to edit leads.', 'dealroot' ), 403 ); } $ids = isset( $_REQUEST['lead_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_REQUEST['lead_ids'] ) ) : array(); if ( $ids ) { if ( 'delete' === $action ) { Leads::delete( $ids ); } elseif ( 0 === strpos( $action, 'mark_' ) ) { $status = substr( $action, 5 ); foreach ( $ids as $id ) { Leads::update( $id, array( 'status' => $status ) ); } } } wp_safe_redirect( remove_query_arg( array( 'action', 'action2', 'lead_ids', '_wpnonce', '_wp_http_referer' ), wp_get_referer() ? wp_get_referer() : admin_url( 'admin.php?page=' . Admin::LEADS_PAGE ) ) ); exit; } /** * Removes the nonce of the bulk actions from the address after a search or a filter. */ public function clean_address() { if ( empty( $_GET['_wp_http_referer'] ) || $this->current_action() ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } $uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; wp_safe_redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce', 'action', 'action2', 'filter_action' ), $uri ) ); exit; } /** * Status filters, shown as chips. * * @return array */ protected function get_views() { $filters = self::filters(); $counts = Leads::status_counts( $filters ); $current = $filters['status']; /** This filter is documented in includes/admin/class-leads-page.php */ $extra = array_map( 'strval', (array) apply_filters( 'dealroot_lead_filter_args', array() ) ); $base = add_query_arg( array_merge( Report_State::url_args(), array_filter( array( 's' => rawurlencode( $filters['search'] ) ), 'strlen' ), $extra ), admin_url( 'admin.php' ) ); $all = array_sum( $counts ) - $counts['spam']; if ( null !== $filters['channel'] ) { $base = add_query_arg( 'channel', '' === $filters['channel'] ? 'none' : $filters['channel'], $base ); } $views = array( 'all' => sprintf( '%4$s %5$s', '' === $current ? ' is-current' : '', esc_url( $base ), '' === $current ? ' aria-current="page"' : '', esc_html__( 'All', 'dealroot' ), esc_html( number_format_i18n( $all ) ) ), ); foreach ( Leads::status_labels() as $status => $label ) { if ( 0 === $counts[ $status ] && $current !== $status ) { continue; } $views[ $status ] = sprintf( '%5$s %6$s', esc_attr( $status ), $current === $status ? ' is-current' : '', esc_url( add_query_arg( 'status', $status, $base ) ), $current === $status ? ' aria-current="page"' : '', esc_html( $label ), esc_html( number_format_i18n( $counts[ $status ] ) ) ); } return $views; } /** * Prints the status filters. */ public function views() { $views = $this->get_views(); echo ''; } /** * The channel filter is above the list, next to the search. * * @param string $which Top or bottom. */ protected function extra_tablenav( $which ) { } /** * Loads items. */ public function prepare_items() { $filters = self::filters(); $per_page = $this->get_items_per_page( 'dealroot_per_page', 20 ); // phpcs:disable WordPress.Security.NonceVerification.Recommended $orderby = isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : 'created_at'; $order = isset( $_GET['order'] ) ? sanitize_key( wp_unslash( $_GET['order'] ) ) : 'desc'; // phpcs:enable $result = Leads::query( array_merge( $filters, array( 'orderby' => $orderby, 'order' => $order, 'per_page' => $per_page, 'page' => $this->get_pagenum(), ) ) ); $this->items = $result['items']; $this->repeat_counts = Leads::counts_by_email( wp_list_pluck( $this->items, 'email' ) ); $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns(), 'name' ); $this->set_pagination_args( array( 'total_items' => $result['total'], 'per_page' => $per_page, ) ); } /** * Empty state. */ public function no_items() { $filters = self::filters(); if ( '' !== $filters['status'] || '' !== $filters['search'] || null !== $filters['channel'] || $filters['no_value'] || ! empty( $filters['column'] ) ) { esc_html_e( 'No leads match these filters.', 'dealroot' ); return; } esc_html_e( 'No leads yet. They appear here as soon as someone submits one of your forms.', 'dealroot' ); } /** * Checkbox column. * * @param object $item Lead. * @return string */ protected function column_cb( $item ) { return sprintf( '', (int) $item->id ); } /** * Name column with contact details. * * @param object $item Lead. * @return string */ protected function column_name( $item ) { $url = admin_url( 'admin.php?page=' . Admin::PAGE . '&lead=' . (int) $item->id ); $title = Lead_View::title( $item ); $meta = array_filter( array( $item->name ? $item->email : '', $item->phone ) ); $out = '' . esc_html( $title ) . ''; $repeats = (int) ( $this->repeat_counts[ $item->email ] ?? 0 ); if ( $repeats > 1 ) { /* translators: %d: number of enquiries from the same email address. */ $tooltip = sprintf( _n( '%d enquiry from this email address', '%d enquiries from this email address', $repeats, 'dealroot' ), $repeats ); $out .= '' . esc_html( number_format_i18n( $repeats ) ) . '×'; } if ( ! empty( $item->unread ) ) { $out .= ' ' . esc_html__( 'New message', 'dealroot' ) . ''; } $out .= Ai_View::marks( $item ) . self::badges( $item ); if ( $meta ) { $out .= ''; } if ( '' !== (string) ( $item->ai_summary ?? '' ) ) { $out .= '' . esc_html( $item->ai_summary ) . ''; } return '