Created
September 3, 2025 21:35
-
-
Save mlbd/8d8bc2561d67f00993719eb2f3f8aa93 to your computer and use it in GitHub Desktop.
Auto-reject after one, send an email after each assignment is completed, and add a ‘Last Signed Off’ placeholder.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| add_filter('owf_review_step_total_reviews', function ($total_reviews, $review_setting, $action_history_id, $step_id) { | |
| global $wpdb; | |
| /** | |
| * NEW: For steps requiring "everyone" to approve, if ANY reviewer has rejected (unable), | |
| * auto-reject all remaining "assignment" reviews immediately so the failure branch proceeds now. | |
| * We also persist these changes so inbox/reminders are cleaned up. | |
| */ | |
| if ( ! empty( $total_reviews ) ) { | |
| if ( $review_setting === 'everyone' ) { | |
| $action_table = OW_Utility::instance()->get_action_table_name(); | |
| $formated_total_reviews = $total_reviews; | |
| // 1) Find the first rejector (unable) row, if any. | |
| $reject_row = null; | |
| foreach ( $total_reviews as $r ) { | |
| if ( isset( $r->review_status ) && $r->review_status === 'unable' ) { | |
| $reject_row = $r; | |
| break; | |
| } | |
| } | |
| // 2) If we have a rejector and there are still pending assignments, flip them to unable. | |
| if ( $reject_row ) { | |
| $rejector_user = get_userdata( intval( $reject_row->actor_id ) ); | |
| $rejector_name = $rejector_user ? $rejector_user->display_name : __( 'another reviewer', 'oasisworkflow' ); | |
| $now_time = current_time( 'mysql' ); | |
| $ow_email_local = new OW_Email(); | |
| foreach ( $total_reviews as $id => $r ) { | |
| if ( isset( $r->review_status ) && $r->review_status === 'assignment' ) { | |
| // Build/append auto-comment (JSON shape you requested). | |
| $existing = array(); | |
| if ( ! empty( $r->comments ) ) { | |
| $decoded = json_decode( $r->comments, true ); | |
| if ( is_array( $decoded ) ) { | |
| $existing = $decoded; | |
| } | |
| } | |
| $existing[] = array( | |
| 'send_id' => intval( $r->actor_id ), // as per your example | |
| 'comment' => sprintf( __( 'Auto-rejected: another reviewer (%s) rejected this step.', 'oasisworkflow' ), $rejector_name ), | |
| 'comment_timestamp' => $now_time, | |
| ); | |
| $new_comments_json = wp_json_encode( $existing ); | |
| $formated_total_reviews[ $id ]->review_status = 'unable'; | |
| $formated_total_reviews[ $id ]->comments = $new_comments_json; | |
| $formated_total_reviews[ $id ]->step_id = $new_comments_json; | |
| // Clone critical fields from the rejector row to align failure path. | |
| $new_step_id = isset( $reject_row->step_id ) ? $reject_row->step_id : $r->step_id; | |
| $new_next_assign_json = isset( $reject_row->next_assign_actors ) ? $reject_row->next_assign_actors : $r->next_assign_actors; | |
| $new_due_date = isset( $reject_row->due_date ) ? $reject_row->due_date : $r->due_date; | |
| // Clone critical fields from the rejector row to align failure path. | |
| $formated_total_reviews[ $id ]->step_id = $new_step_id; | |
| $formated_total_reviews[ $id ]->next_assign_actors = $new_next_assign_json; | |
| $formated_total_reviews[ $id ]->due_date = $new_due_date; | |
| // Update the row. | |
| $wpdb->update( $action_table, (array) $formated_total_reviews[ $id ], array( | |
| "actor_id" => $formated_total_reviews[ $id ]->actor_id, | |
| "action_history_id" => $formated_total_reviews[ $id ]->action_history_id | |
| ) ); | |
| // Clear reminder emails for this user on this action. | |
| $ow_email_local->delete_step_email( intval( $r->action_history_id ), intval( $r->actor_id ) ); | |
| } | |
| } | |
| // No return here; let the normal tally/decision logic proceed now that no "assignment" remains. | |
| } | |
| $total_reviews = $formated_total_reviews; | |
| error_log( "total_reviews (adjusted): " . print_r( $total_reviews, true ) ); | |
| } | |
| } | |
| return $total_reviews; | |
| }, 10, 4); // priority 10, and 4 args | |
| // [NEW] Per-assignee approval email | |
| add_action( 'owf_individual_signoff_approved', 'send_individual_signoff_email', 10, 7 ); | |
| /** | |
| * [NEW] Send "assignment-style" email when an individual assignee approves a review step. | |
| * Reuses the Assignment Email template via get_step_mail_content(), without needing the next action_id. | |
| * | |
| * @param int $post_id | |
| * @param int $action_history_id Current step action_history_id | |
| * @param int $actor_id Approver user ID | |
| * @param array $review_data Includes 'next_assign_actors' (json) and 'step_id' (proposed next step) | |
| * @param int $current_step_id | |
| * @param int $next_step_id Proposed next step id | |
| */ | |
| function send_individual_signoff_email( $post_id, $action_history_id, $actor_id, $review_data, $current_step_id, $next_step_id, $sign_off_comments ) { | |
| $post_id = intval( $post_id ); | |
| $action_history_id = intval( $action_history_id ); | |
| $actor_id = intval( $actor_id ); | |
| $current_step_id = intval( $current_step_id ); | |
| $next_step_id = intval( $next_step_id ); | |
| $post = get_post( $post_id ); | |
| if ( ! $post ) { | |
| return; | |
| } | |
| // Respect Assignment Emails on/off toggle. | |
| $email_settings = get_option( 'oasiswf_email_settings' ); | |
| if ( ! is_array( $email_settings ) || ! isset( $email_settings['assignment_emails'] ) || $email_settings['assignment_emails'] !== 'yes' ) { | |
| return; | |
| } | |
| $current_user_id = get_current_user_id(); | |
| $ow_email_settings_helper = new OW_Email_Settings_Helper(); | |
| $ow_email = new OW_Email(); | |
| // Candidate next assignees for this approval (may be empty). | |
| $candidate_to_users = array(); | |
| if ( isset( $review_data['next_assign_actors'] ) && ! empty( $review_data['next_assign_actors'] ) ) { | |
| $decoded = json_decode( $review_data['next_assign_actors'], true ); | |
| if ( is_array( $decoded ) ) { | |
| $candidate_to_users = array_filter( array_map( 'intval', $decoded ) ); | |
| } | |
| } | |
| // Fallback: if we cannot determine next assignees yet, notify the post author. | |
| if ( empty( $candidate_to_users ) ) { | |
| $candidate_to_users = array( intval( $post->post_author ) ); | |
| } | |
| // Use proposed next step id if present, otherwise fall back to current step id. | |
| $step_for_template = $next_step_id > 0 ? $next_step_id : $current_step_id; | |
| $review_data = array( | |
| "comment" => $sign_off_comments, | |
| "task_actor_id" => $actor_id | |
| ); | |
| // Use the same comment accessor as assignment emails. | |
| $comment = $ow_email->get_step_comment_content( $action_history_id, $review_data ); | |
| $post_id_for_params = $post_id; // clarity | |
| foreach ( $candidate_to_users as $to_user_id ) { | |
| $to_user_id = intval( $to_user_id ); | |
| if ( $to_user_id === $current_user_id ) { | |
| continue; // mirror send_step_email: don't email the actor themselves | |
| } | |
| // Build the standard assignment email content using existing helper. | |
| $mails = $ow_email->get_step_mail_content( $action_history_id, $step_for_template, $to_user_id, $post_id_for_params ); | |
| if ( ! $mails || empty( $mails->assign_subject ) || empty( $mails->assign_content ) ) { | |
| continue; | |
| } | |
| $mail_content = $mails->assign_content . $comment; | |
| $mail_content = apply_filters( "oasiswf_custom_email_content", $mail_content, $post_id_for_params, $action_history_id ); | |
| $email_cc = isset( $mails->assign_cc ) ? json_decode( json_encode( $mails->assign_cc ), true ) : array(); | |
| $email_bcc = isset( $mails->assign_bcc ) ? json_decode( json_encode( $mails->assign_bcc ), true ) : array(); | |
| $email_recipient_params = array( | |
| "post_id" => $post_id_for_params, | |
| "email_cc" => $email_cc, | |
| "email_bcc" => $email_bcc | |
| ); | |
| $recipients = $ow_email_settings_helper->get_cc_recipients( $email_recipient_params ); | |
| $cc_users = apply_filters( 'ow_assignment_cc_users', $recipients["email_cc"] ); | |
| $bcc_users = apply_filters( 'ow_assignment_bcc_users', $recipients["email_bcc"] ); | |
| // Send the email. | |
| $ow_email->send_mail( $to_user_id, $mails->assign_subject, $mail_content, '', $cc_users, $bcc_users ); | |
| } | |
| } | |
| // [PATCH] Added custom placeholder "%last_signed_off_by%" for emails and its runtime resolver. | |
| if ( ! function_exists( 'owf_register_last_signed_off_by_placeholder' ) ) { | |
| function owf_register_last_signed_off_by_placeholder() { | |
| // Show the placeholder label in the Step Info email editor (step-info-content.php) | |
| add_filter( 'oasiswf_custom_placeholders', function( $placeholders ) { | |
| if ( ! is_array( $placeholders ) ) { | |
| $placeholders = array(); | |
| } | |
| // [PATCH] Added new placeholder label so it appears in the dropdown | |
| $placeholders['%last_signed_off_by%'] = esc_html__( 'Last Signed off by', 'oasisworkflow' ); | |
| return $placeholders; | |
| } ); | |
| /** | |
| * Inject the runtime value for assignment/reminder emails. | |
| * This filter is called in the email sending pipeline with full context. | |
| * | |
| * @param array $mapping Existing custom placeholder map. | |
| * @param int $action_id Current action history id (for the receiving assignment). | |
| * @param int $step_id Current step id. | |
| * @param int $to_user_id Recipient user id. | |
| * @param int $post_id Post id. | |
| * | |
| * @return array | |
| */ | |
| add_filter( 'oasiswf_custom_placeholders_list', function( $mapping, $action_id, $step_id, $to_user_id, $post_id ) { | |
| if ( ! is_array( $mapping ) ) { | |
| $mapping = array(); | |
| } | |
| // [PATCH] Compute "Last Signed off by" using the previous action (same logic as Inbox list). | |
| $name = owf_get_last_signed_off_by( $action_id, $post_id ); | |
| if ( $name ) { | |
| $mapping['%last_signed_off_by%'] = $name; | |
| } | |
| return $mapping; | |
| }, 10, 5 ); | |
| } | |
| owf_register_last_signed_off_by_placeholder(); | |
| } | |
| function owf_get_last_signed_off_by( $action_id, $post_id ) { | |
| $action_id = intval( $action_id ); | |
| $post_id = intval( $post_id ); | |
| if ( ! $action_id || ! $post_id ) { | |
| return ''; | |
| } | |
| $ow_history_service = new OW_History_Service(); | |
| $previous = $ow_history_service->get_action_history_by_id( $action_id ); // previous step/action | |
| if ( ! $previous ) { | |
| return ''; | |
| } | |
| // Mirror class-ow-inbox-service.php get_table_rows() logic. | |
| $comments = null; | |
| if ( intval( $previous->assign_actor_id ) === -1 ) { | |
| // Multi-assignee/review case: get review row with status "complete" for the previous action. | |
| $review_rows = $ow_history_service->get_review_action_by_status( 'complete', $previous->ID ); | |
| if ( $review_rows ) { | |
| $comments = json_decode( $review_rows[0]->comments ); | |
| } | |
| } | |
| if ( ! $comments ) { | |
| $comments = json_decode( $previous->comment ); | |
| } | |
| if ( is_array( $comments ) && ! empty( $comments[0]->send_id ) ) { | |
| return OW_Utility::instance()->get_user_name( intval( $comments[0]->send_id ) ); | |
| } | |
| return ''; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment