array( 'name' => __( 'Leads', 'avi-support-assistant' ), 'singular_name' => __( 'Lead', 'avi-support-assistant' ), 'menu_name' => __( 'Leads', 'avi-support-assistant' ), 'all_items' => __( 'Leads', 'avi-support-assistant' ), 'edit_item' => __( 'Lead Details', 'avi-support-assistant' ), 'search_items' => __( 'Search Leads', 'avi-support-assistant' ), 'not_found' => __( 'No leads yet. Leads appear here when visitors leave their details in the chat.', 'avi-support-assistant' ), 'not_found_in_trash' => __( 'No leads in Trash.', 'avi-support-assistant' ), ), 'public' => false, 'show_ui' => true, 'show_in_menu' => AVCA_MENU_SLUG, 'show_in_rest' => false, 'supports' => array( 'title' ), 'map_meta_cap' => false, 'capabilities' => array( 'create_posts' => 'do_not_allow', 'edit_post' => $cap, 'read_post' => $cap, 'delete_post' => $cap, 'edit_posts' => $cap, 'edit_others_posts' => $cap, 'edit_published_posts' => $cap, 'edit_private_posts' => $cap, 'publish_posts' => $cap, 'read_private_posts' => $cap, 'delete_posts' => $cap, 'delete_others_posts' => $cap, 'delete_published_posts' => $cap, 'delete_private_posts' => $cap, ), 'rewrite' => false, 'query_var' => false, 'capability_type' => 'post', ) ); } add_action( 'init', 'avca_register_lead_post_type' ); /** * Lead meta fields. * * @return array Meta key => label. */ function avca_lead_fields() { return array( '_avca_email' => __( 'Email', 'avi-support-assistant' ), '_avca_phone' => __( 'Phone', 'avi-support-assistant' ), '_avca_preferred_time' => __( 'Preferred call time', 'avi-support-assistant' ), '_avca_question' => __( 'Question asked', 'avi-support-assistant' ), '_avca_message' => __( 'Message', 'avi-support-assistant' ), '_avca_page' => __( 'Page', 'avi-support-assistant' ), ); } /** * Save a lead and send the email notification. * * @param array $data Sanitized lead data: type, name, email, phone, preferred_time, question, message, page. * @return int|WP_Error Lead ID or error. */ function avca_create_lead( $data ) { $lead_id = wp_insert_post( array( 'post_type' => AVCA_LEAD_POST_TYPE, 'post_status' => 'publish', 'post_title' => $data['name'], ), true ); if ( is_wp_error( $lead_id ) ) { return $lead_id; } update_post_meta( $lead_id, '_avca_type', 'callback' === $data['type'] ? 'callback' : 'lead' ); foreach ( array( 'email', 'phone', 'preferred_time', 'question', 'message', 'page' ) as $key ) { if ( isset( $data[ $key ] ) && '' !== $data[ $key ] ) { update_post_meta( $lead_id, '_avca_' . $key, $data[ $key ] ); } } avca_send_lead_email( $lead_id, $data ); /** * Fires after a chat lead is saved. * * @param int $lead_id Lead post ID. * @param array $data Lead data. */ do_action( 'avca_lead_created', $lead_id, $data ); return $lead_id; } /** * Email the site owner about a new lead. * * @param int $lead_id Lead post ID. * @param array $data Lead data. */ function avca_send_lead_email( $lead_id, $data ) { $to = avca_get_setting( 'lead_email' ); $to = $to ? $to : get_option( 'admin_email' ); $site = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); if ( 'callback' === $data['type'] ) { /* translators: 1: Visitor name, 2: Site name. */ $subject = sprintf( __( 'Callback request from %1$s – %2$s', 'avi-support-assistant' ), $data['name'], $site ); $intro = __( 'A visitor asked for a callback in the chat assistant.', 'avi-support-assistant' ); } else { /* translators: 1: Visitor name, 2: Site name. */ $subject = sprintf( __( 'New chat lead from %1$s – %2$s', 'avi-support-assistant' ), $data['name'], $site ); $intro = __( 'A visitor left their details in the chat assistant.', 'avi-support-assistant' ); } $lines = array( $intro, '', __( 'Name', 'avi-support-assistant' ) . ': ' . $data['name'], ); foreach ( avca_lead_fields() as $meta_key => $label ) { $key = substr( $meta_key, 6 ); if ( ! empty( $data[ $key ] ) ) { $lines[] = $label . ': ' . $data[ $key ]; } } $lines[] = ''; $lines[] = __( 'View lead:', 'avi-support-assistant' ) . ' ' . admin_url( 'post.php?post=' . absint( $lead_id ) . '&action=edit' ); $headers = array(); if ( ! empty( $data['email'] ) && is_email( $data['email'] ) ) { $headers[] = 'Reply-To: ' . str_replace( array( "\r", "\n", '<', '>' ), '', $data['name'] ) . ' <' . $data['email'] . '>'; } wp_mail( $to, $subject, implode( "\n", $lines ), $headers ); } /** * Columns for the leads list. * * @param array $columns Default columns. * @return array */ function avca_lead_columns( $columns ) { return array( 'cb' => isset( $columns['cb'] ) ? $columns['cb'] : '', 'title' => __( 'Name', 'avi-support-assistant' ), 'avca_type' => __( 'Type', 'avi-support-assistant' ), 'avca_email' => __( 'Email', 'avi-support-assistant' ), 'avca_phone' => __( 'Phone', 'avi-support-assistant' ), 'avca_question' => __( 'Question asked', 'avi-support-assistant' ), 'date' => __( 'Date', 'avi-support-assistant' ), ); } add_filter( 'manage_' . AVCA_LEAD_POST_TYPE . '_posts_columns', 'avca_lead_columns' ); /** * Render custom lead columns. * * @param string $column Column key. * @param int $post_id Lead ID. */ function avca_lead_column_content( $column, $post_id ) { switch ( $column ) { case 'avca_type': echo 'callback' === get_post_meta( $post_id, '_avca_type', true ) ? esc_html__( 'Callback', 'avi-support-assistant' ) : esc_html__( 'Lead', 'avi-support-assistant' ); break; case 'avca_email': $email = get_post_meta( $post_id, '_avca_email', true ); if ( $email ) { echo '' . esc_html( $email ) . ''; } break; case 'avca_phone': $phone = get_post_meta( $post_id, '_avca_phone', true ); if ( $phone ) { echo '' . esc_html( $phone ) . ''; } break; case 'avca_question': echo esc_html( wp_trim_words( (string) get_post_meta( $post_id, '_avca_question', true ), 12 ) ); break; } } add_action( 'manage_' . AVCA_LEAD_POST_TYPE . '_posts_custom_column', 'avca_lead_column_content', 10, 2 ); /** * Remove "Edit" / "Quick Edit" row actions; keep View details and Trash. * * @param array $actions Row actions. * @param WP_Post $post Post. * @return array */ function avca_lead_row_actions( $actions, $post ) { if ( AVCA_LEAD_POST_TYPE !== $post->post_type ) { return $actions; } unset( $actions['inline hide-if-no-js'] ); if ( isset( $actions['edit'] ) ) { $actions['edit'] = '' . esc_html__( 'View details', 'avi-support-assistant' ) . ''; } return $actions; } add_filter( 'post_row_actions', 'avca_lead_row_actions', 10, 2 ); /** * Add the lead details box on the lead screen. */ function avca_lead_meta_boxes() { add_meta_box( 'avca_lead_details', __( 'Lead Details', 'avi-support-assistant' ), 'avca_render_lead_details', AVCA_LEAD_POST_TYPE, 'normal', 'high' ); } add_action( 'add_meta_boxes_' . AVCA_LEAD_POST_TYPE, 'avca_lead_meta_boxes' ); /** * Render the read-only lead details. * * @param WP_Post $post Lead post. */ function avca_render_lead_details( $post ) { ?> $label ) : $value = (string) get_post_meta( $post->ID, $meta_key, true ); if ( '' === $value ) { continue; } ?> AVCA_LEAD_POST_TYPE, 'post_status' => 'any', 'posts_per_page' => 50, 'paged' => max( 1, (int) $page ), 'fields' => 'ids', 'no_found_rows' => true, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Only runs from the admin privacy tools. 'meta_query' => array( array( 'key' => '_avca_email', 'value' => $email, ), ), ) ); } /** * Register the personal data exporter. * * @param array $exporters Exporters. * @return array */ function avca_register_exporter( $exporters ) { $exporters['avi-support-assistant'] = array( 'exporter_friendly_name' => __( 'Avi Support Assistant Leads', 'avi-support-assistant' ), 'callback' => 'avca_export_personal_data', ); return $exporters; } add_filter( 'wp_privacy_personal_data_exporters', 'avca_register_exporter' ); /** * Export leads for an email address. * * @param string $email Email address. * @param int $page Page. * @return array */ function avca_export_personal_data( $email, $page = 1 ) { $items = array(); $ids = avca_find_leads_by_email( $email, $page ); foreach ( $ids as $id ) { $data = array( array( 'name' => __( 'Name', 'avi-support-assistant' ), 'value' => get_the_title( $id ), ), ); foreach ( avca_lead_fields() as $meta_key => $label ) { $value = (string) get_post_meta( $id, $meta_key, true ); if ( '' !== $value ) { $data[] = array( 'name' => $label, 'value' => $value, ); } } $items[] = array( 'group_id' => 'avca-leads', 'group_label' => __( 'Chat Leads', 'avi-support-assistant' ), 'item_id' => 'avca-lead-' . $id, 'data' => $data, ); } return array( 'data' => $items, 'done' => count( $ids ) < 50, ); } /** * Register the personal data eraser. * * @param array $erasers Erasers. * @return array */ function avca_register_eraser( $erasers ) { $erasers['avi-support-assistant'] = array( 'eraser_friendly_name' => __( 'Avi Support Assistant Leads', 'avi-support-assistant' ), 'callback' => 'avca_erase_personal_data', ); return $erasers; } add_filter( 'wp_privacy_personal_data_erasers', 'avca_register_eraser' ); /** * Erase leads for an email address. * * @param string $email Email address. * @return array */ function avca_erase_personal_data( $email ) { $ids = avca_find_leads_by_email( $email, 1 ); $removed = false; foreach ( $ids as $id ) { if ( wp_delete_post( $id, true ) ) { $removed = true; } } return array( 'items_removed' => $removed, 'items_retained' => false, 'messages' => array(), 'done' => count( $ids ) < 50, ); } /** * Suggested privacy policy text. */ function avca_privacy_policy_content() { if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) { return; } $content = '

' . __( 'Our website has a chat assistant. When you leave your details in the chat, we store your name, email address, phone number (if given), your question and the page you were on, so our team can contact you. This information is stored on this website and emailed to our team. It is not shared with third parties.', 'avi-support-assistant' ) . '

' . '

' . __( 'Questions that the assistant cannot answer may be stored anonymously (without your name or contact details) so we can improve our answers.', 'avi-support-assistant' ) . '

'; wp_add_privacy_policy_content( __( 'Avi Support Assistant', 'avi-support-assistant' ), wp_kses_post( wpautop( $content, false ) ) ); } add_action( 'admin_init', 'avca_privacy_policy_content' );