diff --git a/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php b/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php index f51c2aeeab..fcc149ccd7 100644 --- a/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php +++ b/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php @@ -333,7 +333,6 @@ public static function get_current_tab() { $tabs = array( 'drafts', 'overdue', - 'pending-approval', 'approved', 'pending-payment', diff --git a/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-list-table.php b/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-list-table.php index 561bbdfb2c..b56fca8326 100644 --- a/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-list-table.php +++ b/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-list-table.php @@ -71,6 +71,8 @@ public function prepare_items() { $where .= " AND `status` IN ( 'wcb-failed', 'wcb-cancelled' ) "; } elseif ( 'drafts' == $view ) { $where .= " AND `status` = 'draft' "; + } elseif ( 'needs-followup' == $view ) { + $where .= " AND `status` = 'wcb-needs-followup' "; } if ( ! empty( $_REQUEST['s'] ) ) { diff --git a/public_html/wp-content/plugins/wordcamp-payments/includes/reimbursement-request.php b/public_html/wp-content/plugins/wordcamp-payments/includes/reimbursement-request.php index 65eb9644f1..0097455f46 100644 --- a/public_html/wp-content/plugins/wordcamp-payments/includes/reimbursement-request.php +++ b/public_html/wp-content/plugins/wordcamp-payments/includes/reimbursement-request.php @@ -163,6 +163,15 @@ function init_meta_boxes() { 'high' ); + add_meta_box( + 'wcbrr_notes', + esc_html__( 'Notes', 'wordcamporg' ), + __NAMESPACE__ . '\render_notes_metabox', + POST_TYPE, + 'side', + 'high' + ); + add_meta_box( 'wcbrr_general_information', esc_html__( 'General Information', 'wordcamporg' ), @@ -308,6 +317,19 @@ function render_status_metabox( $post ) { require_once dirname( __DIR__ ) . '/views/reimbursement-request/metabox-status.php'; } +/** + * Render the Notes metabox + * + * @param WP_Post $post + */ +function render_notes_metabox( $post ) { + wp_nonce_field( 'notes', 'notes_nonce' ); + + $existing_notes = get_post_meta( $post->ID, '_wcbrr_notes', true ); + + require_once dirname( __DIR__ ) . '/views/reimbursement-request/metabox-notes.php'; +} + /** * Render General Information Metabox * @@ -500,6 +522,10 @@ function save_request( $post_id, $post ) { verify_metabox_nonces(); + // phpcs:ignore is added because verify_metabox_nonces(); already checks that. + // phpcs:ignore WordPress.Security.NonceVerification.Missing + validate_and_save_notes( $post, $_POST['wcbrr_new_note'] ); + /* * We need to determine if the user is allowed to modify the request -- in terms of this plugin's post_status * restrictions, not in terms of current_user_can( 'edit_post', N ) -- but at this point in the execution @@ -651,6 +677,7 @@ function render_log_metabox( $post ) { function verify_metabox_nonces() { $nonces = array( 'status_nonce', + 'notes_nonce', 'general_information_nonce', 'payment_details_nonce', 'expenses_nonce', @@ -720,6 +747,101 @@ function validate_and_save_expenses( $post_id, $expenses ) { update_post_meta( $post_id, '_wcbrr_expenses', $expenses ); } +/** + * Validate and save expense data + * + * @param WP_Post $post + * @param string $new_note_message + */ +function validate_and_save_notes( $post, $new_note_message ) { + + // Save incomplete message. + // phpcs:ignore is used because verify_metabox_nonces(); already checks that. + if ( isset( $_POST['wcp_mark_incomplete_notes'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + $safe_value = ''; + if ( 'wcb-incomplete' == $post->post_status ) { + $safe_value = wp_kses( $_POST['wcp_mark_incomplete_notes'], wp_kses_allowed_html( 'strip' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + } + + update_post_meta( $post->ID, '_wcp_incomplete_notes', $safe_value ); + } + + $new_note_message = sanitize_text_field( wp_unslash( $new_note_message ) ); + + if ( empty( $new_note_message ) ) { + return; + } + + $notes = get_post_meta( $post->ID, '_wcbrr_notes', true ); + if ( ! is_array( $notes ) ) { + $notes = array(); + } + + $new_note = array( + 'timestamp' => time(), + 'author_id' => get_current_user_id(), + 'message' => $new_note_message, + ); + + $notes[] = $new_note; + + update_post_meta( $post->ID, '_wcbrr_notes', $notes ); + notify_parties_of_new_note( $post, $new_note ); + + \WordCamp_Budgets::log( + $post->ID, + get_current_user_id(), + sprintf( 'Note: %s', $new_note_message ), + array( + 'action' => 'note-added', + ) + ); +} + +/** + * Notify WordCamp Central or the request author when new notes are added + * + * @param WP_Post $request + * @param array $note + */ +function notify_parties_of_new_note( $request, $note ) { + $note_author = get_user_by( 'id', $note['author_id'] ); + + if ( $note_author->has_cap( 'manage_network' ) ) { + $to = \WordCamp_Budgets::get_requester_formatted_email( $request->post_author ); + $subject_prefix = sprintf( '[%s] ', get_wordcamp_name() ); + } else { + $to = 'support@wordcamp.org'; + $subject_prefix = ''; + } + + if ( ! $to ) { + return; + } + + $subject = sprintf( '%sNew note on `%s`', $subject_prefix, sanitize_text_field( $request->post_title ) ); + $note_author_name = \WordCamp_Budgets::get_requester_name( $note['author_id'] ); + $request_url = admin_url( sprintf( 'post.php?post=%s&action=edit', $request->ID ) ); + $headers = array( 'Reply-To: support@wordcamp.org' ); + + $message = sprintf( ' + %s has added the following note on the reimbursement request for %s: + + %s + + You can view the request and respond to their note at: + + %s', + sanitize_text_field( $note_author_name ), + sanitize_text_field( $request->post_title ), + sanitize_text_field( $note['message'] ), + esc_url_raw( $request_url ) + ); + $message = str_replace( "\t", '', $message ); + + wp_mail( $to, $subject, $message, $headers ); +} + /** * Notify the organizer when the status of their reimbursement changes or when notes are added * diff --git a/public_html/wp-content/plugins/wordcamp-payments/includes/wordcamp-budgets.php b/public_html/wp-content/plugins/wordcamp-payments/includes/wordcamp-budgets.php index 56c9a3a772..f803e2eebc 100644 --- a/public_html/wp-content/plugins/wordcamp-payments/includes/wordcamp-budgets.php +++ b/public_html/wp-content/plugins/wordcamp-payments/includes/wordcamp-budgets.php @@ -19,8 +19,6 @@ public function __construct() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_common_assets' ), 11 ); add_filter( 'user_has_cap', array( __CLASS__, 'user_can_view_payment_details' ), 10, 4 ); add_filter( 'default_title', array( $this, 'set_default_payments_title' ), 10, 2 ); - add_action( 'add_meta_boxes', array( $this, 'init_meta_boxes' ) ); - add_action( 'save_post', array( $this, 'save_request' ), 10, 2 ); } /** @@ -29,93 +27,117 @@ public function __construct() { public static function register_post_statuses() { // Uses core's draft status too. - register_post_status( 'wcb-incomplete', array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'label' => esc_html_x( 'Incomplete', 'payment request', 'wordcamporg' ), - 'public' => false, - 'protected' => true, - 'label_count' => _nx_noop( - 'Incomplete (%s)', - 'Incomplete (%s)', - 'wordcamporg' - ), - ) ); + register_post_status( + 'wcb-incomplete', + array( + 'label' => esc_html_x( 'Incomplete', 'payment request', 'wordcamporg' ), + 'public' => false, + 'protected' => true, + 'label_count' => _nx_noop( + 'Incomplete (%s)', + 'Incomplete (%s)', + 'wordcamporg' + ), + ) + ); - register_post_status( 'wcb-pending-approval', array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'label' => esc_html_x( 'Pending Approval', 'payment request', 'wordcamporg' ), - 'public' => false, - 'protected' => true, - 'label_count' => _nx_noop( - 'Pending Approval (%s)', - 'Pending Approval (%s)', - 'wordcamporg' - ), - ) ); + register_post_status( + 'wcb-pending-approval', + array( + 'label' => esc_html_x( 'Pending Approval', 'payment request', 'wordcamporg' ), + 'public' => false, + 'protected' => true, + 'label_count' => _nx_noop( + 'Pending Approval (%s)', + 'Pending Approval (%s)', + 'wordcamporg' + ), + ) + ); - register_post_status( 'wcb-needs-followup', array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'label' => esc_html_x( 'Needs Follow-up', 'payment request', 'wordcamporg' ), - 'public' => false, - 'protected' => true, - 'label_count' => _nx_noop( - 'Needs Follow-up (%s)', - 'Needs Follow-up (%s)', - 'wordcamporg' - ), - ) ); + register_post_status( + 'wcb-needs-followup', + array( + 'label' => esc_html_x( 'Needs Follow-up', 'payment request', 'wordcamporg' ), + 'public' => false, + 'protected' => true, + 'label_count' => _nx_noop( + 'Needs Follow-up (%s)', + 'Needs Follow-up (%s)', + 'wordcamporg' + ), + ) + ); - register_post_status( 'wcb-approved', array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'label' => esc_html_x( 'Approved', 'payment request', 'wordcamporg' ), - 'public' => false, - 'protected' => true, - 'label_count' => _nx_noop( - 'Approved (%s)', - 'Approved (%s)', - 'wordcamporg' - ), - ) ); + register_post_status( + 'wcb-approved', + array( + 'label' => esc_html_x( 'Approved', 'payment request', 'wordcamporg' ), + 'public' => false, + 'protected' => true, + 'label_count' => _nx_noop( + 'Approved (%s)', + 'Approved (%s)', + 'wordcamporg' + ), + ) + ); - register_post_status( 'wcb-pending-payment', array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'label' => esc_html_x( 'Payment Sent', 'payment request', 'wordcamporg' ), - 'public' => false, - 'protected' => true, - 'label_count' => _nx_noop( - 'Payment Sent (%s)', - 'Payment Sent (%s)', - 'wordcamporg' - ), - ) ); + register_post_status( + 'wcb-pending-payment', + array( + 'label' => esc_html_x( 'Payment Sent', 'payment request', 'wordcamporg' ), + 'public' => false, + 'protected' => true, + 'label_count' => _nx_noop( + 'Payment Sent (%s)', + 'Payment Sent (%s)', + 'wordcamporg' + ), + ) + ); - register_post_status( 'wcb-paid', array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'label' => esc_html_x( 'Paid', 'payment request', 'wordcamporg' ), - 'public' => false, - 'protected' => true, - 'label_count' => _nx_noop( - 'Paid (%s)', - 'Paid (%s)', - 'wordcamporg' - ), - ) ); + register_post_status( + 'wcb-paid', + array( + 'label' => esc_html_x( 'Paid', 'payment request', 'wordcamporg' ), + 'public' => false, + 'protected' => true, + 'label_count' => _nx_noop( + 'Paid (%s)', + 'Paid (%s)', + 'wordcamporg' + ), + ) + ); - register_post_status( 'wcb-failed', array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'label' => esc_html_x( 'Failed', 'payment request', 'wordcamporg' ), - 'public' => false, - 'protected' => true, - 'label_count' => _nx_noop( - 'Failed (%s)', - 'Failed (%s)', - 'wordcamporg' - ), - ) ); + register_post_status( + 'wcb-failed', + array( + 'label' => esc_html_x( 'Failed', 'payment request', 'wordcamporg' ), + 'public' => false, + 'protected' => true, + 'label_count' => _nx_noop( + 'Failed (%s)', + 'Failed (%s)', + 'wordcamporg' + ), + ) + ); - register_post_status( 'wcb-cancelled', array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'label' => esc_html_x( 'Cancelled', 'payment request', 'wordcamporg' ), - 'public' => false, - 'protected' => true, - 'label_count' => _nx_noop( - 'Cancelled (%s)', - 'Cancelled (%s)', - 'wordcamporg' - ), - ) ); + register_post_status( + 'wcb-cancelled', + array( + 'label' => esc_html_x( 'Cancelled', 'payment request', 'wordcamporg' ), + 'public' => false, + 'protected' => true, + 'label_count' => _nx_noop( + 'Cancelled (%s)', + 'Cancelled (%s)', + 'wordcamporg' + ), + ) + ); } /** @@ -868,186 +890,4 @@ public static function log( $post_id, $user_id, $message, $data = array() ) { update_post_meta( $post_id, '_wcp_log', wp_slash( $log ) ); } - - /** - * Register meta boxes - */ - public function init_meta_boxes() { - add_meta_box( - 'wcbrr_notes', - esc_html__( 'Notes', 'wordcamporg' ), - array( $this, 'render_notes_metabox' ), - array( 'wcb_reimbursement', 'wcp_payment_request' ), - 'side', - 'default' - ); - - if ( current_user_can( 'manage_network' ) ) { - add_meta_box( - 'wcbrr_notes_private', - esc_html__( 'Private notes', 'wordcamporg' ), - array( $this, 'render_notes_private_metabox' ), - array( 'wcb_reimbursement', 'wcp_payment_request' ), - 'side', - 'default' - ); - } - } - - /** - * Render the Notes metabox - * - * @param WP_Post $post - */ - public function render_notes_metabox( $post ) { - wp_nonce_field( 'notes', 'notes_nonce' ); - - $existing_notes = get_post_meta( $post->ID, '_wcbrr_notes', true ); - - require_once dirname( __DIR__ ) . '/views/wordcamp-budgets/metabox-notes.php'; - } - - /** - * Render the Private notes metabox - * - * @param WP_Post $post - */ - public function render_notes_private_metabox( $post ) { - wp_nonce_field( 'notes_private', 'notes_private_nonce' ); - - $existing_notes = get_post_meta( $post->ID, '_wcbrr_notes_private', true ); - - require_once dirname( __DIR__ ) . '/views/wordcamp-budgets/metabox-notes-private.php'; - } - - /** - * Save the post's data - * - * @param int $post_id - * @param WP_Post $post - */ - public function save_request( $post_id, $post ) { - if ( empty( $_POST ) || ! empty( $_POST['wcpn-request-import'] ) ) { - return; - } - - check_admin_referer( str_replace( '_nonce', '', 'notes_nonce' ), 'notes_nonce' ); - $this::validate_and_save_notes( $post, $_POST['wcbrr_new_note'] ); - - if ( current_user_can( 'manage_network' ) ) { - check_admin_referer( str_replace( '_nonce', '', 'notes_private_nonce' ), 'notes_private_nonce' ); - $this::validate_and_save_notes_private( $post, $_POST['wcbrr_new_note_private'] ); - } - } - - /** - * Validate and save notes. - * - * @param WP_Post $post - * @param string $new_note_message - */ - public function validate_and_save_notes( $post, $new_note_message ) { - $new_note_message = sanitize_text_field( wp_unslash( $new_note_message ) ); - - if ( empty( $new_note_message ) ) { - return; - } - - $notes = get_post_meta( $post->ID, '_wcbrr_notes', true ); - if ( ! is_array( $notes ) ) { - $notes = array(); - } - - $new_note = array( - 'timestamp' => time(), - 'author_id' => get_current_user_id(), - 'message' => $new_note_message, - ); - - $notes[] = $new_note; - - update_post_meta( $post->ID, '_wcbrr_notes', $notes ); - $this::notify_parties_of_new_note( $post, $new_note ); - - $this::log( $post->ID, get_current_user_id(), sprintf( 'Note: %s', $new_note_message ), array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'action' => 'note-added', - ) ); - } - - /** - * Validate and save private notes. - * - * @param WP_Post $post - * @param string $new_note_message - */ - public function validate_and_save_notes_private( $post, $new_note_message ) { - $new_note_message = sanitize_text_field( wp_unslash( $new_note_message ) ); - - if ( empty( $new_note_message ) ) { - return; - } - - $notes = get_post_meta( $post->ID, '_wcbrr_notes_private', true ); - if ( ! is_array( $notes ) ) { - $notes = array(); - } - - $new_note = array( - 'timestamp' => time(), - 'author_id' => get_current_user_id(), - 'message' => $new_note_message, - ); - - $notes[] = $new_note; - - update_post_meta( $post->ID, '_wcbrr_notes_private', $notes ); - - $this::log( $post->ID, get_current_user_id(), __( 'Private note', 'wordcamporg' ), array( // phpcs:ignore PEAR.Functions.FunctionCallSignature.MultipleArguments - 'action' => 'note-added', - ) ); - } - - /** - * Notify WordCamp Central or the request author when new notes are added - * - * @param WP_Post $request - * @param array $note - */ - public function notify_parties_of_new_note( $request, $note ) { - $note_author = get_user_by( 'id', $note['author_id'] ); - - if ( $note_author->has_cap( 'manage_network' ) ) { - $to = $this::get_requester_formatted_email( $request->post_author ); - $subject_prefix = sprintf( '[%s] ', get_wordcamp_name() ); - } else { - $to = 'support@wordcamp.org'; - $subject_prefix = ''; - } - - if ( ! $to ) { - return; - } - - $subject = sprintf( '%sNew note on `%s`', $subject_prefix, sanitize_text_field( $request->post_title ) ); - $note_author_name = $this::get_requester_name( $note['author_id'] ); - $request_url = admin_url( sprintf( 'post.php?post=%s&action=edit', $request->ID ) ); - $headers = array( 'Reply-To: support@wordcamp.org' ); - - $message = sprintf( ' - %s has added the following note on the reimbursement request for %s: - - %s - - You can view the request and respond to their note at: - - %s', - sanitize_text_field( $note_author_name ), - sanitize_text_field( $request->post_title ), - sanitize_text_field( $note['message'] ), - esc_url_raw( $request_url ) - ); - $message = str_replace( "\t", '', $message ); - - wp_mail( $to, $subject, $message, $headers ); - } } diff --git a/public_html/wp-content/plugins/wordcamp-payments/views/wordcamp-budgets/metabox-notes-private.php b/public_html/wp-content/plugins/wordcamp-payments/views/reimbursement-request/metabox-notes.php similarity index 64% rename from public_html/wp-content/plugins/wordcamp-payments/views/wordcamp-budgets/metabox-notes-private.php rename to public_html/wp-content/plugins/wordcamp-payments/views/reimbursement-request/metabox-notes.php index d68aedd665..e014c65649 100644 --- a/public_html/wp-content/plugins/wordcamp-payments/views/wordcamp-budgets/metabox-notes-private.php +++ b/public_html/wp-content/plugins/wordcamp-payments/views/reimbursement-request/metabox-notes.php @@ -21,15 +21,19 @@

-

- + diff --git a/public_html/wp-content/plugins/wordcamp-payments/views/wordcamp-budgets/metabox-notes.php b/public_html/wp-content/plugins/wordcamp-payments/views/wordcamp-budgets/metabox-notes.php deleted file mode 100644 index 83294aac0f..0000000000 --- a/public_html/wp-content/plugins/wordcamp-payments/views/wordcamp-budgets/metabox-notes.php +++ /dev/null @@ -1,42 +0,0 @@ - -
- - - : - - - -
- ID ) ) : ?> -
-

- - - -

(visible to organizers)

- -

- - - - -
-