array( 'name' => __( 'Quote Requests', 'boring-quote' ), 'singular_name' => __( 'Quote Request', 'boring-quote' ), 'edit_item' => __( 'Edit Quote Request', 'boring-quote' ), 'view_item' => __( 'View Quote Request', 'boring-quote' ), 'search_items' => __( 'Search Quote Requests', 'boring-quote' ), 'not_found' => __( 'No quote requests found.', 'boring-quote' ), 'not_found_in_trash' => __( 'No quote requests found in Trash.', 'boring-quote' ), ), 'public' => false, 'show_ui' => true, 'show_in_menu' => 'woocommerce', 'supports' => array( 'title' ), 'capability_type' => array( 'bbquote_quote', 'bbquote_quotes' ), 'capabilities' => array( 'create_posts' => 'do_not_allow' ), 'map_meta_cap' => true, 'exclude_from_search' => true, ) ); foreach ( self::statuses() as $status => $label ) { register_post_status( $status, array( 'label' => $label, 'public' => false, 'protected' => true, // Needed for the admin "All" view (WP_Query only lists public/protected/private). 'exclude_from_search' => true, 'show_in_admin_all_list' => true, 'show_in_admin_status_list' => true, // translators: %s: number of quotes. 'label_count' => array( 'singular' => $label . ' (%s)', 'plural' => $label . ' (%s)', 'context' => null, 'domain' => null, ), ) ); } } /** * Quote statuses. All of them can be set by hand in Lite; add-ons may also set them programmatically. * * @return array */ public static function statuses() { return apply_filters( 'bbquote_quote_statuses', array( 'bbquote-new' => __( 'New', 'boring-quote' ), 'bbquote-pending' => __( 'In progress', 'boring-quote' ), 'bbquote-sent' => __( 'Offer sent', 'boring-quote' ), 'bbquote-accepted' => __( 'Accepted', 'boring-quote' ), 'bbquote-rejected' => __( 'Rejected', 'boring-quote' ), 'bbquote-expired' => __( 'Expired', 'boring-quote' ), ) ); } /** * Statuses the admin can pick by hand in the status box. Every registered quote status * is selectable. The current status is always included, so saving never changes it by accident. * * @param string $current Current status of the quote. * @return array slug => label */ public static function selectable_statuses( $current = '' ) { $all = self::statuses(); $selectable = $all; /** * Filters the statuses the admin can pick by hand in the quote status box. * * @param array $selectable slug => label. * @param array $all All registered quote statuses (slug => label). */ $selectable = (array) apply_filters( 'bbquote_selectable_statuses', $selectable, $all ); if ( isset( $all[ $current ] ) && ! isset( $selectable[ $current ] ) ) { $selectable = array( $current => $all[ $current ] ) + $selectable; } return $selectable; } /** * Saving the edit screen may only set a quote status (never e.g. "publish"). Programmatic updates * (offer flow, trash) do not send `original_post_status` and are not affected. * * @param array $data Post data to be saved. * @param array $postarr Raw post array. * @return array */ public function guard_status_change( $data, $postarr ) { if ( self::POST_TYPE !== $data['post_type'] || empty( $postarr['original_post_status'] ) ) { return $data; } $original = sanitize_key( $postarr['original_post_status'] ); if ( ! isset( self::selectable_statuses( $original )[ $data['post_status'] ] ) && isset( self::statuses()[ $original ] ) ) { $data['post_status'] = $original; } return $data; } /** * Create a quote post from submitted data. * * @param array $customer Customer fields (name, email, phone, message). * @param array $items Quote list items. * @return int|\WP_Error Post id. */ public static function create_quote( $customer, $items ) { $quote_id = wp_insert_post( array( 'post_type' => self::POST_TYPE, 'post_status' => 'bbquote-new', 'post_title' => sprintf( /* translators: 1: customer name, 2: date */ __( 'Quote - %1$s - %2$s', 'boring-quote' ), sanitize_text_field( $customer['name'] ), wp_date( get_option( 'date_format' ) . ' H:i' ) ), ), true ); if ( is_wp_error( $quote_id ) ) { return $quote_id; } update_post_meta( $quote_id, '_bbquote_items', $items ); update_post_meta( $quote_id, '_bbquote_customer', array_map( 'sanitize_textarea_field', $customer ) ); // Separate, queryable copy for the privacy exporter/eraser. update_post_meta( $quote_id, '_bbquote_customer_email', sanitize_email( $customer['email'] ?? '' ) ); if ( is_user_logged_in() ) { update_post_meta( $quote_id, '_bbquote_customer_user', get_current_user_id() ); } do_action( 'bbquote_quote_created', $quote_id, $customer, $items ); return $quote_id; } /** * Admin list columns. * * @param array $columns Columns. * @return array */ public function columns( $columns ) { return array( 'cb' => $columns['cb'], 'title' => __( 'Quote', 'boring-quote' ), 'bbquote_customer' => __( 'Customer', 'boring-quote' ), 'bbquote_items' => __( 'Items', 'boring-quote' ), 'bbquote_status' => __( 'Status', 'boring-quote' ), 'date' => __( 'Date', 'boring-quote' ), ); } /** * Column content. * * @param string $column Column id. * @param int $post_id Post id. */ public function column_content( $column, $post_id ) { if ( 'bbquote_customer' === $column ) { $customer = (array) get_post_meta( $post_id, '_bbquote_customer', true ); $name = isset( $customer['name'] ) ? $customer['name'] : ''; $email = isset( $customer['email'] ) ? $customer['email'] : ''; printf( '%s
%s', esc_html( $name ), esc_attr( $email ), esc_html( $email ) ); } if ( 'bbquote_items' === $column ) { echo esc_html( (string) count( (array) get_post_meta( $post_id, '_bbquote_items', true ) ) ); } if ( 'bbquote_status' === $column ) { $statuses = self::statuses(); $status = get_post_status( $post_id ); printf( '%s', esc_attr( str_replace( 'bbquote-', '', (string) $status ) ), esc_html( isset( $statuses[ $status ] ) ? $statuses[ $status ] : $status ) ); } } /** * Read-only meta box with request content. */ public function meta_boxes() { // The default Publish box would save the quote as "publish". Replace it with a status box. remove_meta_box( 'submitdiv', self::POST_TYPE, 'side' ); add_meta_box( 'bbquote_quote_status', __( 'Status', 'boring-quote' ), array( $this, 'render_status_box' ), self::POST_TYPE, 'side', 'high' ); add_meta_box( 'bbquote_quote_content', __( 'Quote request', 'boring-quote' ), array( $this, 'render_meta_box' ), self::POST_TYPE, 'normal', 'high' ); } /** * Render the read-only quote view. * * @param \WP_Post $post Post. */ public function render_meta_box( $post ) { self::render_customer_details( $post->ID ); /** * Whether to render the default read-only product table. * Pro returns false and renders its editable quote editor via `bbquote_admin_quote_view`. * * @param bool $render Render default view. * @param \WP_Post $post Quote post. */ if ( ! apply_filters( 'bbquote_render_default_quote_view', true, $post ) ) { do_action( 'bbquote_admin_quote_view', $post ); return; } echo '

' . esc_html__( 'Products', 'boring-quote' ) . '

'; foreach ( self::get_items( $post->ID ) as $item ) { $product = QuoteList::item_product( $item ); if ( $product ) { printf( '', wp_kses_post( $product->get_image( array( 40, 40 ) ) ), esc_url( (string) get_edit_post_link( $product->get_parent_id() ? $product->get_parent_id() : $product->get_id() ) ), esc_html( $product->get_formatted_name() ), (int) $item['quantity'] ); } else { printf( '', esc_html__( '(deleted product)', 'boring-quote' ), (int) $item['quantity'] ); } } echo '
' . esc_html__( 'Product', 'boring-quote' ) . '' . esc_html__( 'Qty', 'boring-quote' ) . '
%s%s
%d
%s%d
'; } /** * Side box: status select + save. Pro adds its actions via `bbquote_admin_status_box_actions`. * * @param \WP_Post $post Quote post. */ public function render_status_box( $post ) { $current = get_post_status( $post ); ?>

' . esc_html__( 'Customer', 'boring-quote' ) . '

'; if ( ! empty( $customer['email'] ) && is_email( $customer['email'] ) ) { printf( '%s', esc_url( 'mailto:' . $customer['email'] . '?subject=' . rawurlencode( get_the_title( $quote_id ) ) ), esc_html__( 'Reply by email', 'boring-quote' ) ); } echo ''; } /** * Quote items. * * @param int $quote_id Quote id. * @return array */ public static function get_items( $quote_id ) { $items = get_post_meta( $quote_id, '_bbquote_items', true ); return is_array( $items ) ? $items : array(); } /** * Quote customer data. * * @param int $quote_id Quote id. * @return array */ public static function get_customer( $quote_id ) { $customer = get_post_meta( $quote_id, '_bbquote_customer', true ); return is_array( $customer ) ? $customer : array(); } }