Skip to content

Commit

Permalink
Add Tracks integration (#21)
Browse files Browse the repository at this point in the history
Add mu-plugins Tracks integrations for VIP sites
  • Loading branch information
hanifn authored Oct 14, 2024
1 parent 16d6ea1 commit ac24ea3
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 65 deletions.
88 changes: 58 additions & 30 deletions modules/custom-status/custom-status.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ class CustomStatus {
const SETTINGS_SLUG = 'vw-custom-status';

// The metadata keys for the custom status term
const METADATA_POSITION_KEY = 'position';
const METADATA_POSITION_KEY = 'position';
const METADATA_REQ_EDITORIAL_IDS_KEY = 'required_metadata_ids';
const METADATA_REQ_EDITORIALS_KEY = 'required_metadatas';
const METADATA_REQ_USER_IDS_KEY = 'required_user_ids';
const METADATA_REQ_USERS_KEY = 'required_users';
const METADATA_REQ_EDITORIALS_KEY = 'required_metadatas';
const METADATA_REQ_USER_IDS_KEY = 'required_user_ids';
const METADATA_REQ_USERS_KEY = 'required_users';

public static function init(): void {
// Register the taxonomy we use with WordPress core, and ensure it's registered after editorial metadata
Expand Down Expand Up @@ -161,10 +161,10 @@ public static function setup_install(): void {
'position' => 4,
],
[
'name' => __( 'Pending Review' ),
'slug' => 'pending',
'description' => __( 'Post needs to be reviewed by an editor.', 'vip-workflow' ),
'position' => 5,
'name' => __( 'Pending Review' ),
'slug' => 'pending',
'description' => __( 'Post needs to be reviewed by an editor.', 'vip-workflow' ),
'position' => 5,
],
];

Expand Down Expand Up @@ -208,10 +208,10 @@ public static function action_admin_enqueue_scripts(): void {
wp_enqueue_style( 'vip-workflow-custom-status-styles', VIP_WORKFLOW_URL . 'dist/modules/custom-status/custom-status-configure.css', [ 'wp-components' ], $asset_file['version'] );

wp_localize_script( 'vip-workflow-custom-status-configure', 'VW_CUSTOM_STATUS_CONFIGURE', [
'custom_statuses' => self::get_custom_statuses(),
'custom_statuses' => self::get_custom_statuses(),
'editorial_metadatas' => EditorialMetadata::get_editorial_metadata_terms(),
'url_edit_status' => CustomStatusEndpoint::get_crud_url(),
'url_reorder_status' => CustomStatusEndpoint::get_reorder_url(),
'url_edit_status' => CustomStatusEndpoint::get_crud_url(),
'url_reorder_status' => CustomStatusEndpoint::get_reorder_url(),
] );
}

Expand Down Expand Up @@ -271,7 +271,7 @@ private static function modify_custom_statuses_with_editorial_metadata(): array
// Add the required editorial metadata to the custom statuses for UI purposes
foreach ( $custom_statuses as $status ) {
$required_metadata_ids = $status->meta[ self::METADATA_REQ_EDITORIAL_IDS_KEY ] ?? [];
$required_metadatas = [];
$required_metadatas = [];
foreach ( $required_metadata_ids as $metadata_id ) {
$required_metadatas[] = $editorial_metadatas[ $metadata_id ];
}
Expand Down Expand Up @@ -333,7 +333,7 @@ public static function post_admin_header(): void {
$custom_statuses = self::get_custom_statuses();

// $selected can be empty, but must be set because it's used as a JS variable
$selected = '';
$selected = '';

if ( ! empty( $post ) ) {
// Get the status of the current post
Expand Down Expand Up @@ -547,9 +547,9 @@ public static function add_custom_status( array $args ): WP_Term|WP_Error {

$term_id = $inserted_term['term_id'];

$position = $args[ self::METADATA_POSITION_KEY ];
$position = $args[ self::METADATA_POSITION_KEY ];
$required_metadata_ids = $args[ self::METADATA_REQ_EDITORIAL_IDS_KEY ] ?? [];
$required_user_ids = $args[ self::METADATA_REQ_USER_IDS_KEY ] ?? [];
$required_user_ids = $args[ self::METADATA_REQ_USER_IDS_KEY ] ?? [];

// In case of failure, data cleanup happens which includes the term and the meta keys.

Expand All @@ -570,6 +570,15 @@ public static function add_custom_status( array $args ): WP_Term|WP_Error {

$term_result = self::get_custom_status_by( 'id', $term_id );

if ( false !== $term_result ) {
/**
* Fires after a custom status is added to the database.
*
* @param WP_Term $term The custom status term object.
*/
do_action( 'vw_add_custom_status', $term_result );
}

return $term_result;
}

Expand All @@ -584,7 +593,7 @@ public static function update_custom_status( int $status_id, array $args = [] ):
$old_status = self::get_custom_status_by( 'id', $status_id );
if ( is_wp_error( $old_status ) ) {
return $old_status;
} else if ( ! $old_status ) {
} elseif ( ! $old_status ) {
return new WP_Error( 'invalid', __( "Custom status doesn't exist.", 'vip-workflow' ), array( 'status' => 400 ) );
}

Expand Down Expand Up @@ -614,8 +623,8 @@ public static function update_custom_status( int $status_id, array $args = [] ):
}

$term_fields_to_update = [
'name' => isset( $args['name'] ) ? $args['name'] : $old_status->name,
'slug' => isset( $args['slug'] ) ? $args['slug'] : $old_status->slug,
'name' => isset( $args['name'] ) ? $args['name'] : $old_status->name,
'slug' => isset( $args['slug'] ) ? $args['slug'] : $old_status->slug,
'description' => isset( $args['description'] ) ? $args['description'] : $old_status->description,
];

Expand Down Expand Up @@ -648,9 +657,19 @@ public static function update_custom_status( int $status_id, array $args = [] ):
return $updated_term;
}

$status_result = self::get_custom_status_by( 'id', $status_id );
$updated_status = self::get_custom_status_by( 'id', $status_id );

return $status_result;
if ( $updated_status ) {
/**
* Fires after a custom status is updated in the database.
*
* @param WP_Term $updated_status The updated status WP_Term object.
* @param array $update_args The arguments used to update the status.
*/
do_action( 'vw_update_custom_status', $updated_status, $args );
}

return $updated_status;
}

/**
Expand All @@ -664,7 +683,7 @@ public static function delete_custom_status( int $status_id ): bool|WP_Error {
$old_status = self::get_custom_status_by( 'id', $status_id );
if ( is_wp_error( $old_status ) ) {
return $old_status;
} else if ( ! $old_status ) {
} elseif ( ! $old_status ) {
return new WP_Error( 'invalid', __( "Custom status doesn't exist.", 'vip-workflow' ), array( 'status' => 400 ) );
}

Expand Down Expand Up @@ -698,6 +717,15 @@ public static function delete_custom_status( int $status_id ): bool|WP_Error {
return new WP_Error( 'invalid', __( 'Unable to delete custom status.', 'vip-workflow' ) );
}

/**
* Fires after a custom status is deleted from the database.
*
* @param int $term_id The ID of the status being deleted
* @param string $term_name The name of the status being deleted
* @param string $old_status_slug The slug of the status being deleted
*/
do_action( 'vw_delete_custom_status', $status_id, $old_status->name, $old_status_slug );

// Re-order the positions after deletion
$custom_statuses = self::get_custom_statuses();

Expand Down Expand Up @@ -727,9 +755,9 @@ public static function get_custom_statuses(): array {
$statuses = get_terms( [
'taxonomy' => self::TAXONOMY_KEY,
'hide_empty' => false,
'orderby' => 'meta_value_num',
'order' => 'ASC',
'meta_key' => self::METADATA_POSITION_KEY,
'orderby' => 'meta_value_num',
'order' => 'ASC',
'meta_key' => self::METADATA_POSITION_KEY,
]);

if ( is_wp_error( $statuses ) || empty( $statuses ) ) {
Expand All @@ -738,7 +766,7 @@ public static function get_custom_statuses(): array {

// Add metadata to each term
$statuses = array_map( function ( $status ) {
$term_meta = apply_filters( 'vw_register_custom_status_meta', [], $status );
$term_meta = apply_filters( 'vw_register_custom_status_meta', [], $status );
$status->meta = $term_meta;

return $status;
Expand Down Expand Up @@ -771,8 +799,8 @@ public static function get_custom_status_by( string $field, int|string $value, $

if ( is_wp_error( $custom_status ) || ! $custom_status ) {
$custom_status = false;
} else if ( $include_metadata ) {
$term_meta = apply_filters( 'vw_register_custom_status_meta', [], $custom_status );
} elseif ( $include_metadata ) {
$term_meta = apply_filters( 'vw_register_custom_status_meta', [], $custom_status );
$custom_status->meta = $term_meta;
}

Expand All @@ -794,9 +822,9 @@ public static function get_core_statuses(): array {
'description' => __( 'Post is a draft; not ready for review or publication.', 'vip-workflow' ),
],
[
'name' => __( 'Pending Review' ),
'slug' => 'pending',
'description' => __( 'Post needs to be reviewed by an editor.', 'vip-workflow' ),
'name' => __( 'Pending Review' ),
'slug' => 'pending',
'description' => __( 'Post needs to be reviewed by an editor.', 'vip-workflow' ),
],
];

Expand Down
73 changes: 45 additions & 28 deletions modules/notifications/notifications.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public static function notification_status_change( string $new_status, string $o

$body = '';

$post_id = $post->ID;
$post_title = vw_draft_or_post_title( $post_id );
$post_type = $post->post_type;
$post_id = $post->ID;
$post_title = vw_draft_or_post_title( $post_id );
$post_type = $post->post_type;
$subject_post_type = ucfirst( $post_type );

if ( 0 != $current_user->ID ) {
Expand Down Expand Up @@ -147,13 +147,36 @@ public static function notification_status_change( string $new_status, string $o

$action = 'status-change';

self::schedule_emails( $action, $post, $subject, $body );
$notification_email = OptionsUtilities::get_options_by_key( 'email_address' );
$is_email_scheduled = '' !== $notification_email;

/* translators: 1: user name, 2: post type, 3: post id, 4: edit link, 5: post title, 6: old status, 7: new status */
$webhook_format = __( '*%1$s* changed the status of *%2$s #%3$s - <%4$s|%5$s>* from *%6$s* to *%7$s*', 'vip-workflow' );
$webhook_message = sprintf( $webhook_format, $current_user_display_name, $post_type, $post_id, $edit_link, $post_title, $old_status, $new_status );
if ( $is_email_scheduled ) {
$recipients = [ $notification_email ];
self::schedule_emails( $recipients, $action, $post, $subject, $body );
}

$webhook_url = OptionsUtilities::get_options_by_key( 'webhook_url' );
$is_webhook_scheduled = '' !== $webhook_url;

if ( $is_webhook_scheduled ) {
/* translators: 1: user name, 2: post type, 3: post id, 4: edit link, 5: post title, 6: old status, 7: new status */
$webhook_format = __( '*%1$s* changed the status of *%2$s #%3$s - <%4$s|%5$s>* from *%6$s* to *%7$s*', 'vip-workflow' );
$webhook_message = sprintf( $webhook_format, $current_user_display_name, $post_type, $post_id, $edit_link, $post_title, $old_status, $new_status );

self::schedule_webhook_notification( $webhook_message, $action, $post->post_modified_gmt );
}

self::schedule_webhook_notification( $webhook_message, $action, $post->post_modified_gmt );
// Fire the notification status change action if any notifications were scheduled
if ( $is_email_scheduled || $is_webhook_scheduled ) {
/**
* Fires after a notification is sent
*
* @param int $post_id The post ID of the post that was updated.
* @param bool $is_email_scheduled True if an email was scheduled as part of the notification, false otherwise.
* @param bool $is_webhook_scheduled True if a webhook was scheduled as part of the notification, false otherwise.
*/
do_action( 'vw_notification_status_change', $post->ID, $is_email_scheduled, $is_webhook_scheduled );
}
}
}

Expand All @@ -174,27 +197,21 @@ public static function get_notification_footer(): string {
/**
* Send email notifications
*
* @param array $recipients An array of string email addresses to send to
* @param string $action (status-change)
* @param string $subject Subject of the email
* @param string $message Body of the email
* @param string $message_headers. (optional) Message headers
*/
public static function schedule_emails( string $action, WP_Post $post, string $subject, string $message, string $message_headers = '' ): void {
// Ensure the email address is set from settings.
if ( empty( OptionsUtilities::get_options_by_key( 'email_address' ) ) ) {
return;
}

$email_recipients = [ OptionsUtilities::get_options_by_key( 'email_address' ) ];

public static function schedule_emails( array $recipients, string $action, WP_Post $post, string $subject, string $message, string $message_headers = '' ): void {
/**
* Filter the email recipients
*
* @param array $email_recipients Array of email recipients
* @param array $recipients Array of email recipients
* @param string $action Action being taken, eg. status-change
* @param WP_Post $post Post object
*/
$email_recipients = apply_filters( 'vw_notification_email_recipients', $email_recipients, $action, $post );
$recipients = apply_filters( 'vw_notification_email_recipients', $recipients, $action, $post );

/**
* Filter the email subject
Expand All @@ -203,7 +220,7 @@ public static function schedule_emails( string $action, WP_Post $post, string $s
* @param string $action Action being taken, eg. status-change
*
*/
$subject = apply_filters( 'vw_notification_email_subject', $subject, $action, $post );
$subject = apply_filters( 'vw_notification_email_subject', $subject, $action, $post );

/**
* Filter the email message
Expand All @@ -212,7 +229,7 @@ public static function schedule_emails( string $action, WP_Post $post, string $s
* @param string $action Action being taken, eg. status-change
* @param WP_Post $post Post object
*/
$message = apply_filters( 'vw_notification_email_message', $message, $action, $post );
$message = apply_filters( 'vw_notification_email_message', $message, $action, $post );

/**
* Filter the email headers
Expand All @@ -223,8 +240,8 @@ public static function schedule_emails( string $action, WP_Post $post, string $s
*/
$message_headers = apply_filters( 'vw_notification_email_headers', $message_headers, $action, $post );

if ( ! empty( $email_recipients ) ) {
wp_schedule_single_event( time(), 'vw_send_scheduled_emails', [ $email_recipients, $subject, $message, $message_headers ] );
if ( [] !== $recipients ) {
wp_schedule_single_event( time(), 'vw_send_scheduled_emails', [ $recipients, $subject, $message, $message_headers ] );
}
}

Expand All @@ -247,16 +264,11 @@ public static function send_emails( array $recipients, string $subject, string $
/**
* Schedule a webhook notification
*
* @param string $webhook_message Message to be sent to webhook
* @param string $webhook_url URL to be send the webhook to
* @param string $action Action being taken, eg. status-change
* @param string $timestamp Timestamp of the message, eg. the time at which the post was updated
*/
public static function schedule_webhook_notification( string $webhook_message, string $action, string $timestamp ): void {
// Ensure the webhook URL is set from settings.
if ( empty( OptionsUtilities::get_options_by_key( 'webhook_url' ) ) ) {
return;
}

$message_type = 'plugin:vip-workflow:' . $action;

wp_schedule_single_event( time(), 'vw_send_scheduled_webhook', [ $webhook_message, $message_type, $timestamp ] );
Expand All @@ -273,6 +285,11 @@ public static function schedule_webhook_notification( string $webhook_message, s
public static function send_to_webhook( string $message, string $message_type, string $timestamp ): bool {
$webhook_url = OptionsUtilities::get_options_by_key( 'webhook_url' );

if ( ! is_string( $webhook_url ) || strlen( $webhook_url ) === 0 ) {
// This can happen if the webhook URL was cleared after scheduling this notification
return false;
}

// Set up the payload
$payload = [
'type' => $message_type,
Expand Down
Loading

0 comments on commit ac24ea3

Please sign in to comment.