Add merchant email notifications (https://github.com/woocommerce/woocommerce-admin/pull/5922)
* Added MerchantEmailNotifications class * Added new type and Events refactor # Conflicts: # src/Events.php * Added templates * Refactored MerchantEmailNotifications and NotificationEmail * Templates refactored * Added email opening tracking endpoint * Added templates handling * Moved folder `MerchantEmailNotifications` * Fixed template extensibility * Fixed note `heading` check * Added default type in `get_template_filename` * Added tests * Removed bypass * Modified URL * Added required noteTypes * Added flag for functionallity * Fixed plain link * Fixed comment * Turned email notifications on by default This commit adds the code to turn email notifications on by default * Fixed email styles * Fixed typo * Renamed method "possible_send" as "run" * Removed unnecessary control * Fixed another typo * Renamed method as "get_notification_email_addresses" * Refactored method "send_merchant_notification" * Renamed plain-merchant-notification * Fixed tests * Merchant email notifications - Action triggering (https://github.com/woocommerce/woocommerce-admin/pull/5976) * Added templates # Conflicts: # includes/emails/plain-mechant-notification.php # Conflicts: # includes/emails/html-merchant-notification.php * Added note action triggering This commit adds the note actions triggering # Conflicts: # includes/emails/html-merchant-notification.php * Fixed error handling * Refactored "trigger_note_action" method * Fixed actions url * Fixed note URL * Added external redirect * Added image and html handling for email * Fixed tests * Fixed buttons style Co-authored-by: Fernando Marichal <contacto@fernandomarichal.com> * Add your first product: email notification (https://github.com/woocommerce/woocommerce-admin/pull/6024) * Added AddFirstProduct note # Conflicts: # src/Events.php * Added "AddFirstProduct" email note This commit adds the email note "AddFirstProduct" * Fixed image This commit removes the image img-product-light.svg to use a png instead. Otherwise, the gmail proxy would break the image * Fixed readme.txt Co-authored-by: Fernando Marichal <contacto@fernandomarichal.com> * Added readme.txt Co-authored-by: Fernando Marichal <contacto@fernandomarichal.com>
This commit is contained in:
parent
0b3f4d8e92
commit
513173a9d9
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* Merchant notification email
|
||||
*
|
||||
* @package WooCommerce\Admin\Templates\Emails\HTML
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/*
|
||||
* @hooked WC_Emails::email_header() Output the email header
|
||||
*/
|
||||
do_action( 'woocommerce_email_header', $email_heading, $email );
|
||||
?>
|
||||
|
||||
<?php if ( isset( $email_image ) ) { ?>
|
||||
<div>
|
||||
<img src="<?php echo esc_url( $email_image ); ?>" style="display: block; margin-bottom: 24px;"/>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php
|
||||
echo wp_kses(
|
||||
$email_content,
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'title' => array(),
|
||||
),
|
||||
'br' => array(),
|
||||
'em' => array(),
|
||||
'strong' => array(),
|
||||
)
|
||||
);
|
||||
|
||||
$base_color = get_option( 'woocommerce_email_base_color' );
|
||||
$base_text = wc_light_or_dark( $base_color, '#202020', '#ffffff' );
|
||||
$container_styles = 'margin-top: 25px;';
|
||||
$buttons_styles = "
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
color: {$base_text};
|
||||
margin-right: 15px;
|
||||
text-decoration: none;
|
||||
background: {$base_color};
|
||||
border: 1px solid {$base_color};
|
||||
border-radius: 3px;
|
||||
padding: 5px;";
|
||||
?>
|
||||
<div style="<?php echo esc_attr( $container_styles ); ?>">
|
||||
<?php foreach ( $email_actions as $an_action ) : ?>
|
||||
<a href="<?php echo esc_url( $trigger_note_action_url . $an_action->id ); ?>" style="<?php echo esc_attr( $buttons_styles ); ?>">
|
||||
<?php
|
||||
echo esc_html( $an_action->label );
|
||||
?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div style="opacity: 0;">
|
||||
<img src="<?php echo esc_url( $opened_tracking_url ); ?>" />
|
||||
</div>
|
||||
<?php
|
||||
/*
|
||||
* @hooked WC_Emails::email_footer() Output the email footer
|
||||
*/
|
||||
do_action( 'woocommerce_email_footer', $email );
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
/**
|
||||
* Merchant notification email (plain text)
|
||||
*
|
||||
* @package WooCommerce\Admin\Templates\Emails\HTML
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||
echo esc_html( wp_strip_all_tags( $email_heading ) );
|
||||
echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
|
||||
|
||||
echo wp_kses_post( $email_content );
|
||||
|
||||
foreach ( $email_actions as $an_action ) {
|
||||
echo "\n";
|
||||
/* translators: %1$s: action label, %2$s: action URL */
|
||||
echo wp_kses_post( sprintf( __( '%1$s: %2$s', 'woocommerce-admin' ), $an_action->label, $trigger_note_action_url . $an_action->id ) );
|
||||
}
|
||||
echo "\n\n----------------------------------------\n\n";
|
||||
|
||||
echo wp_kses_post( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
|
|
@ -24,4 +24,5 @@ export const QUERY_DEFAULTS = {
|
|||
pageSize: 25,
|
||||
period: 'month',
|
||||
compare: 'previous_year',
|
||||
noteTypes: [ 'info', 'marketing', 'survey', 'warning' ],
|
||||
};
|
||||
|
|
|
@ -89,6 +89,8 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
|
|||
- Add: Welcome modal when coming from Calypso #6004
|
||||
- Enhancement: Add an a/b experiment for installing free business features #5786
|
||||
- Dev: Add `onChangeCallback` feature to the wc-admin <Form> component #5786
|
||||
- Dev: Add merchant email notifications #5922
|
||||
- Add: Email note to add first product. #6024
|
||||
- Add: Note for users coming from Calypso. #6030
|
||||
- Enhancement: Add an "unread" indicator to inbox messages. #6047
|
||||
|
||||
|
|
|
@ -65,16 +65,7 @@ class NoteActions extends Notes {
|
|||
);
|
||||
}
|
||||
|
||||
// Find note action by ID.
|
||||
$action_id = $request->get_param( 'action_id' );
|
||||
$actions = $note->get_actions( 'edit' );
|
||||
$triggered_action = false;
|
||||
|
||||
foreach ( $actions as $action ) {
|
||||
if ( $action->id === $action_id ) {
|
||||
$triggered_action = $action;
|
||||
}
|
||||
}
|
||||
$triggered_action = NotesFactory::get_action_by_id( $note, $request->get_param( 'action_id' ) );
|
||||
|
||||
if ( ! $triggered_action ) {
|
||||
return new \WP_Error(
|
||||
|
@ -84,81 +75,12 @@ class NoteActions extends Notes {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when an admin note action is taken.
|
||||
*
|
||||
* @param string $name The triggered action name.
|
||||
* @param Note $note The corresponding Note.
|
||||
*/
|
||||
do_action( 'woocommerce_note_action', $triggered_action->name, $note );
|
||||
$triggered_note = NotesFactory::trigger_note_action( $note, $triggered_action );
|
||||
|
||||
/**
|
||||
* Fires when an admin note action is taken.
|
||||
* For more specific targeting of note actions.
|
||||
*
|
||||
* @param Note $note The corresponding Note.
|
||||
*/
|
||||
do_action( 'woocommerce_note_action_' . $triggered_action->name, $note );
|
||||
|
||||
// Update the note with the status for this action.
|
||||
if ( ! empty( $triggered_action->status ) ) {
|
||||
$note->set_status( $triggered_action->status );
|
||||
}
|
||||
|
||||
$note->save();
|
||||
|
||||
if ( in_array( $note->get_type(), array( 'error', 'update' ) ) ) {
|
||||
$tracks_event = 'wcadmin_store_alert_action';
|
||||
} else {
|
||||
$tracks_event = 'wcadmin_inbox_action_click';
|
||||
}
|
||||
|
||||
wc_admin_record_tracks_event(
|
||||
$tracks_event,
|
||||
array(
|
||||
'note_name' => $note->get_name(),
|
||||
'note_type' => $note->get_type(),
|
||||
'note_title' => $note->get_title(),
|
||||
'note_content' => $note->get_content(),
|
||||
'action_name' => $triggered_action->name,
|
||||
'action_label' => $triggered_action->label,
|
||||
'screen' => $this->get_screen_name(),
|
||||
)
|
||||
);
|
||||
|
||||
$data = $note->get_data();
|
||||
$data = $triggered_note->get_data();
|
||||
$data = $this->prepare_item_for_response( $data, $request );
|
||||
$data = $this->prepare_response_for_collection( $data );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get screen name.
|
||||
*
|
||||
* @return string The screen name.
|
||||
*/
|
||||
public function get_screen_name() {
|
||||
$screen_name = '';
|
||||
|
||||
if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
|
||||
parse_str( wp_parse_url( $_SERVER['HTTP_REFERER'], PHP_URL_QUERY ), $queries ); // phpcs:ignore sanitization ok.
|
||||
}
|
||||
if ( isset( $queries ) ) {
|
||||
$page = isset( $queries['page'] ) ? $queries['page'] : null;
|
||||
$path = isset( $queries['path'] ) ? $queries['path'] : null;
|
||||
$post_type = isset( $queries['post_type'] ) ? $queries['post_type'] : null;
|
||||
$post = isset( $queries['post'] ) ? get_post_type( $queries['post'] ) : null;
|
||||
}
|
||||
|
||||
if ( isset( $page ) ) {
|
||||
$current_page = 'wc-admin' === $page ? 'home_screen' : $page;
|
||||
$screen_name = isset( $path ) ? substr( str_replace( '/', '_', $path ), 1 ) : $current_page;
|
||||
} elseif ( isset( $post_type ) ) {
|
||||
$screen_name = $post_type;
|
||||
} elseif ( isset( $post ) ) {
|
||||
$screen_name = $post;
|
||||
}
|
||||
return $screen_name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,19 @@ class Notes extends \WC_REST_CRUD_Controller {
|
|||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/tracker/(?P<note_id>[\d-]+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'track_opened_email' ),
|
||||
'permission_callback' => '__return_true',
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/update',
|
||||
|
@ -451,6 +464,20 @@ class Notes extends \WC_REST_CRUD_Controller {
|
|||
return apply_filters( 'woocommerce_rest_prepare_note', $response, $data, $request );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Track opened emails.
|
||||
*
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
public function track_opened_email( $request ) {
|
||||
$note = NotesRepository::get_note( $request->get_param( 'note_id' ) );
|
||||
if ( ! $note ) {
|
||||
return;
|
||||
}
|
||||
wc_admin_record_tracks_event( 'wcadmin_email_note_opened', array( 'note_name' => $note->get_name() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
|
|
|
@ -29,6 +29,7 @@ use \Automattic\WooCommerce\Admin\Notes\LaunchChecklist;
|
|||
use \Automattic\WooCommerce\Admin\Notes\RealTimeOrderAlerts;
|
||||
use \Automattic\WooCommerce\Admin\RemoteInboxNotifications\DataSourcePoller;
|
||||
use \Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificationsEngine;
|
||||
use \Automattic\WooCommerce\Admin\Notes\MerchantEmailNotifications\MerchantEmailNotifications;
|
||||
use \Automattic\WooCommerce\Admin\Loader;
|
||||
use \Automattic\WooCommerce\Admin\Notes\InsightFirstSale;
|
||||
use \Automattic\WooCommerce\Admin\Notes\NeedSomeInspiration;
|
||||
|
@ -43,6 +44,7 @@ use \Automattic\WooCommerce\Admin\Notes\ManageOrdersOnTheGo;
|
|||
use \Automattic\WooCommerce\Admin\Notes\NavigationFeedback;
|
||||
use \Automattic\WooCommerce\Admin\Notes\NavigationFeedbackFollowUp;
|
||||
use \Automattic\WooCommerce\Admin\Notes\FilterByProductVariationsInReports;
|
||||
use \Automattic\WooCommerce\Admin\Notes\AddFirstProduct;
|
||||
use \Automattic\WooCommerce\Admin\Notes\DrawAttention;
|
||||
|
||||
/**
|
||||
|
@ -88,6 +90,22 @@ class Events {
|
|||
* Note: Order_Milestones::other_milestones is hooked to this as well.
|
||||
*/
|
||||
public function do_wc_admin_daily() {
|
||||
$this->possibly_add_notes();
|
||||
|
||||
if ( $this->is_remote_inbox_notifications_enabled() ) {
|
||||
DataSourcePoller::read_specs_from_data_sources();
|
||||
RemoteInboxNotificationsEngine::run();
|
||||
}
|
||||
|
||||
if ( $this->is_merchant_email_notifications_enabled() ) {
|
||||
MerchantEmailNotifications::run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds notes that should be added.
|
||||
*/
|
||||
protected function possibly_add_notes() {
|
||||
NewSalesRecord::possibly_add_note();
|
||||
MobileApp::possibly_add_note();
|
||||
TrackingOptIn::possibly_add_note();
|
||||
|
@ -120,12 +138,8 @@ class Events {
|
|||
DrawAttention::possibly_add_note();
|
||||
ChoosingTheme::possibly_add_note();
|
||||
InsightFirstProductAndPayment::possibly_add_note();
|
||||
AddFirstProduct::possibly_add_note();
|
||||
AddingAndManangingProducts::possibly_add_note();
|
||||
|
||||
if ( $this->is_remote_inbox_notifications_enabled() ) {
|
||||
DataSourcePoller::read_specs_from_data_sources();
|
||||
RemoteInboxNotificationsEngine::run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,4 +161,19 @@ class Events {
|
|||
// All checks have passed.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if merchant email notifications are enabled.
|
||||
*
|
||||
* @return bool Whether merchant email notifications are enabled.
|
||||
*/
|
||||
protected function is_merchant_email_notifications_enabled() {
|
||||
// Check if the feature flag is disabled.
|
||||
if ( 'yes' !== get_option( 'woocommerce_merchant_email_notifications', 'yes' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All checks have passed.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ use \Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificati
|
|||
use \Automattic\WooCommerce\Admin\Notes\SetUpAdditionalPaymentTypes;
|
||||
use \Automattic\WooCommerce\Admin\Notes\TestCheckout;
|
||||
use \Automattic\WooCommerce\Admin\Notes\SellingOnlineCourses;
|
||||
use \Automattic\WooCommerce\Admin\Notes\MerchantEmailNotifications\MerchantEmailNotifications;
|
||||
use \Automattic\WooCommerce\Admin\Notes\WelcomeToWooCommerceForStoreUsers;
|
||||
|
||||
/**
|
||||
|
@ -196,6 +197,9 @@ class FeaturePlugin {
|
|||
|
||||
// Initialize RemoteInboxNotificationsEngine.
|
||||
RemoteInboxNotificationsEngine::init();
|
||||
|
||||
// Initialize MerchantEmailNotifications.
|
||||
MerchantEmailNotifications::init();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce Admin: Add First Product.
|
||||
*
|
||||
* Adds a note (type `email`) to bring the client back to the store setup flow.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Notes;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Add_First_Product.
|
||||
*/
|
||||
class AddFirstProduct {
|
||||
/**
|
||||
* Note traits.
|
||||
*/
|
||||
use NoteTraits;
|
||||
|
||||
/**
|
||||
* Name of the note for use in the database.
|
||||
*/
|
||||
const NOTE_NAME = 'wc-admin-add-first-product-note';
|
||||
|
||||
/**
|
||||
* Get the note.
|
||||
*
|
||||
* @return Note
|
||||
*/
|
||||
public static function get_note() {
|
||||
// We want to show the note after three days.
|
||||
if ( ! self::wc_admin_active_for( 3 * DAY_IN_SECONDS ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't show if there is a product.
|
||||
$query = new \WC_Product_Query(
|
||||
array(
|
||||
'limit' => 1,
|
||||
'return' => 'ids',
|
||||
'status' => array( 'publish' ),
|
||||
)
|
||||
);
|
||||
$products = $query->get_products();
|
||||
if ( 0 !== count( $products ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content_lines = array(
|
||||
__( 'Nice one, you’ve created a WooCommerce store! Now it’s time to add your first product.<br/><br/>', 'woocommerce-admin' ),
|
||||
__( 'There are three ways to add your products: you can <strong>create products manually, import them at once via CSV file</strong>, or <strong>migrate them from another service</strong>.<br/><br/>', 'woocommerce-admin' ),
|
||||
__( '<a href="https://docs.woocommerce.com/document/managing-products/?utm_source=help_panel">Explore our docs</a> for more information, or just get started!', 'woocommerce-admin' ),
|
||||
);
|
||||
|
||||
$additional_data = array(
|
||||
'role' => 'administrator',
|
||||
);
|
||||
|
||||
$note = new Note();
|
||||
$note->set_title( __( 'Store setup', 'woocommerce-admin' ) );
|
||||
$note->set_content( implode( '', $content_lines ) );
|
||||
$note->set_content_data( (object) $additional_data );
|
||||
$note->set_image(
|
||||
plugins_url(
|
||||
'/images/admin_notes/img-product-light.png',
|
||||
WC_ADMIN_PLUGIN_FILE
|
||||
)
|
||||
);
|
||||
$note->set_type( Note::E_WC_ADMIN_NOTE_EMAIL );
|
||||
$note->set_name( self::NOTE_NAME );
|
||||
$note->set_source( 'woocommerce-admin' );
|
||||
$note->add_action( 'add-first-product', __( 'Add a product', 'woocommerce-admin' ), admin_url( 'admin.php?page=wc-admin&task=products' ) );
|
||||
return $note;
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ class WC_Admin_Note extends DeprecatedClassFacade {
|
|||
const E_WC_ADMIN_NOTE_UNACTIONED = Note::E_WC_ADMIN_NOTE_UNACTIONED;
|
||||
const E_WC_ADMIN_NOTE_ACTIONED = Note::E_WC_ADMIN_NOTE_ACTIONED;
|
||||
const E_WC_ADMIN_NOTE_SNOOZED = Note::E_WC_ADMIN_NOTE_SNOOZED;
|
||||
const E_WC_ADMIN_NOTE_EMAIL = Note::E_WC_ADMIN_NOTE_EMAIL;
|
||||
|
||||
/**
|
||||
* The name of the non-deprecated class that this facade covers.
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles merchant email notifications
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Notes\MerchantEmailNotifications;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Notes\Note;
|
||||
use Automattic\WooCommerce\Admin\Notes\Notes;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Merchant email notifications.
|
||||
* This gets all non-sent notes type `email` and sends them.
|
||||
*/
|
||||
class MerchantEmailNotifications {
|
||||
/**
|
||||
* Initialize the merchant email notifications.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_init', array( __CLASS__, 'trigger_notification_action' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the note action.
|
||||
*/
|
||||
public static function trigger_notification_action() {
|
||||
/* phpcs:disable WordPress.Security.NonceVerification */
|
||||
if (
|
||||
! isset( $_GET['external_redirect'] ) ||
|
||||
1 !== intval( $_GET['external_redirect'] ) ||
|
||||
! isset( $_GET['note'] ) ||
|
||||
! isset( $_GET['action'] )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$note_id = intval( $_GET['note'] );
|
||||
$action_id = intval( $_GET['action'] );
|
||||
/* phpcs:enable */
|
||||
|
||||
$note = Notes::get_note( $note_id );
|
||||
|
||||
if ( ! $note ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$triggered_action = Notes::get_action_by_id( $note, $action_id );
|
||||
|
||||
if ( ! $triggered_action ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Notes::trigger_note_action( $note, $triggered_action );
|
||||
|
||||
$url = $triggered_action->query;
|
||||
|
||||
// We will use "wp_safe_redirect" when it's an internal redirect.
|
||||
if ( strpos( $url, 'http' ) === false ) {
|
||||
wp_safe_redirect( $url );
|
||||
} else {
|
||||
header( 'Location: ' . $url );
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all the notifications type `email`.
|
||||
*/
|
||||
public static function run() {
|
||||
$data_store = \WC_Data_Store::load( 'admin-note' );
|
||||
$notes = $data_store->get_notes(
|
||||
array(
|
||||
'type' => array( Note::E_WC_ADMIN_NOTE_EMAIL ),
|
||||
'status' => array( 'unactioned' ),
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $notes as $note ) {
|
||||
$note = Notes::get_note( $note->note_id );
|
||||
if ( $note ) {
|
||||
self::send_merchant_notification( $note );
|
||||
$note->set_status( 'sent' );
|
||||
$note->save();
|
||||
wc_admin_record_tracks_event( 'wcadmin_email_note_sent', array( 'note_name' => $note->get_name() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the notification to the merchant.
|
||||
*
|
||||
* @param object $note The note to send.
|
||||
*/
|
||||
public static function send_merchant_notification( $note ) {
|
||||
\WC_Emails::instance();
|
||||
$users_emails = self::get_notification_email_addresses( $note );
|
||||
$email = new NotificationEmail( $note );
|
||||
foreach ( $users_emails as $user_email ) {
|
||||
if ( is_email( $user_email ) ) {
|
||||
$email->trigger( $user_email );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email addresses by role to notify.
|
||||
*
|
||||
* @param object $note The note to send.
|
||||
* @return array Emails to notify
|
||||
*/
|
||||
public static function get_notification_email_addresses( $note ) {
|
||||
$content_data = $note->get_content_data();
|
||||
$role = 'administrator';
|
||||
if ( isset( $content_data->role ) ) {
|
||||
$role = $content_data->role;
|
||||
}
|
||||
$args = array( 'role' => $role );
|
||||
$users = get_users( $args );
|
||||
return array_column( $users, 'user_email' );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles emailing user notes.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Notes\MerchantEmailNotifications;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Notes;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include dependencies.
|
||||
*/
|
||||
if ( ! class_exists( 'WC_Email', false ) ) {
|
||||
include_once WC_ABSPATH . 'includes/emails/class-wc-email.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationEmail Class.
|
||||
*/
|
||||
class NotificationEmail extends \WC_Email {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Note $note The notification to send.
|
||||
*/
|
||||
public function __construct( $note ) {
|
||||
$this->note = $note;
|
||||
$this->id = 'merchant_notification';
|
||||
$this->template_base = WC_ADMIN_ABSPATH . 'includes/emails/';
|
||||
|
||||
// Call parent constructor.
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* This email has no user-facing settings.
|
||||
*/
|
||||
public function init_form_fields() {}
|
||||
|
||||
/**
|
||||
* This email has no user-facing settings.
|
||||
*/
|
||||
public function init_settings() {}
|
||||
|
||||
/**
|
||||
* Return template filename.
|
||||
*
|
||||
* @param string $type Type of email to send.
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_filename( $type = 'html' ) {
|
||||
if ( ! in_array( $type, array( 'html', 'plain' ), true ) ) {
|
||||
return;
|
||||
}
|
||||
$content_data = $this->note->get_content_data();
|
||||
$template_filename = "{$type}-merchant-notification.php";
|
||||
if ( isset( $content_data->{"template_{$type}"} ) && file_exists( $this->template_base . $content_data->{ "template_{$type}" } ) ) {
|
||||
$template_filename = $content_data[ "template_{$type}" ];
|
||||
}
|
||||
return $template_filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return email type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_email_type() {
|
||||
return class_exists( 'DOMDocument' ) ? 'html' : 'plain';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email heading.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_default_heading() {
|
||||
$content_data = $this->note->get_content_data();
|
||||
if ( isset( $content_data->heading ) ) {
|
||||
return $content_data->heading;
|
||||
}
|
||||
|
||||
return $this->note->get_title();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email subject.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_default_subject() {
|
||||
return $this->note->get_title();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get note content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_note_content() {
|
||||
return $this->note->get_content();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get note image.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_image() {
|
||||
return $this->note->get_image();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email action.
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function get_actions() {
|
||||
return $this->note->get_actions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content html.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content_html() {
|
||||
return wc_get_template_html(
|
||||
$this->get_template_filename( 'html' ),
|
||||
array(
|
||||
'email_actions' => $this->get_actions(),
|
||||
'email_content' => $this->get_note_content(),
|
||||
'email_heading' => $this->get_heading(),
|
||||
'email_image' => $this->get_image(),
|
||||
'sent_to_admin' => true,
|
||||
'plain_text' => false,
|
||||
'email' => $this,
|
||||
'opened_tracking_url' => $this->opened_tracking_url,
|
||||
'trigger_note_action_url' => $this->trigger_note_action_url,
|
||||
),
|
||||
'',
|
||||
$this->template_base
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content plain.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content_plain() {
|
||||
return wc_get_template_html(
|
||||
$this->get_template_filename( 'plain' ),
|
||||
array(
|
||||
'email_heading' => $this->get_heading(),
|
||||
'email_content' => $this->get_note_content(),
|
||||
'email_actions' => $this->get_actions(),
|
||||
'sent_to_admin' => true,
|
||||
'plain_text' => true,
|
||||
'email' => $this,
|
||||
'trigger_note_action_url' => $this->trigger_note_action_url,
|
||||
),
|
||||
'',
|
||||
$this->template_base
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the sending of this email.
|
||||
*
|
||||
* @param string $email Email to send the note.
|
||||
*/
|
||||
public function trigger( $email ) {
|
||||
$this->recipient = $email;
|
||||
$this->opened_tracking_url = sprintf(
|
||||
'%1$s/wp-json/wc-analytics/admin/notes/tracker/%2$d',
|
||||
site_url(),
|
||||
$this->note->get_id()
|
||||
);
|
||||
$this->trigger_note_action_url = sprintf(
|
||||
'%1$s&external_redirect=1¬e=%2$d&action=',
|
||||
wc_admin_url(),
|
||||
$this->note->get_id()
|
||||
);
|
||||
$this->send(
|
||||
$this->get_recipient(),
|
||||
$this->get_subject(),
|
||||
$this->get_content(),
|
||||
$this->get_headers(),
|
||||
$this->get_attachments()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -21,12 +21,14 @@ class Note extends \WC_Data {
|
|||
const E_WC_ADMIN_NOTE_INFORMATIONAL = 'info'; // used for presenting informational messages.
|
||||
const E_WC_ADMIN_NOTE_MARKETING = 'marketing'; // used for adding marketing messages.
|
||||
const E_WC_ADMIN_NOTE_SURVEY = 'survey'; // used for adding survey messages.
|
||||
const E_WC_ADMIN_NOTE_EMAIL = 'email'; // used for adding notes that will be sent by email.
|
||||
|
||||
// Note status codes.
|
||||
const E_WC_ADMIN_NOTE_PENDING = 'pending'; // the note is pending - hidden but not actioned.
|
||||
const E_WC_ADMIN_NOTE_UNACTIONED = 'unactioned'; // the note has not yet been actioned by a user.
|
||||
const E_WC_ADMIN_NOTE_ACTIONED = 'actioned'; // the note has had its action completed by a user.
|
||||
const E_WC_ADMIN_NOTE_SNOOZED = 'snoozed'; // the note has been snoozed by a user.
|
||||
const E_WC_ADMIN_NOTE_SENT = 'sent'; // the note has been sent by email to the user.
|
||||
|
||||
/**
|
||||
* This is the name of this object type.
|
||||
|
@ -127,6 +129,7 @@ class Note extends \WC_Data {
|
|||
self::E_WC_ADMIN_NOTE_INFORMATIONAL,
|
||||
self::E_WC_ADMIN_NOTE_MARKETING,
|
||||
self::E_WC_ADMIN_NOTE_SURVEY,
|
||||
self::E_WC_ADMIN_NOTE_EMAIL,
|
||||
);
|
||||
|
||||
return apply_filters( 'woocommerce_note_types', $allowed_types );
|
||||
|
@ -143,6 +146,7 @@ class Note extends \WC_Data {
|
|||
self::E_WC_ADMIN_NOTE_ACTIONED,
|
||||
self::E_WC_ADMIN_NOTE_UNACTIONED,
|
||||
self::E_WC_ADMIN_NOTE_SNOOZED,
|
||||
self::E_WC_ADMIN_NOTE_SENT,
|
||||
);
|
||||
|
||||
return apply_filters( 'woocommerce_note_statuses', $allowed_statuses );
|
||||
|
|
|
@ -301,4 +301,104 @@ class Notes {
|
|||
|
||||
return $note->get_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action by id.
|
||||
*
|
||||
* @param Note $note The note that has of the action.
|
||||
* @param int $action_id Action ID.
|
||||
* @return object|bool The found action.
|
||||
*/
|
||||
public static function get_action_by_id( $note, $action_id ) {
|
||||
$actions = $note->get_actions( 'edit' );
|
||||
$found_action = false;
|
||||
|
||||
foreach ( $actions as $action ) {
|
||||
if ( $action->id === $action_id ) {
|
||||
$found_action = $action;
|
||||
}
|
||||
}
|
||||
return $found_action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger note action.
|
||||
*
|
||||
* @param Note $note The note that has the triggered action.
|
||||
* @param object $triggered_action The triggered action.
|
||||
* @return Note|bool
|
||||
*/
|
||||
public static function trigger_note_action( $note, $triggered_action ) {
|
||||
/**
|
||||
* Fires when an admin note action is taken.
|
||||
*
|
||||
* @param string $name The triggered action name.
|
||||
* @param Note $note The corresponding Note.
|
||||
*/
|
||||
do_action( 'woocommerce_note_action', $triggered_action->name, $note );
|
||||
|
||||
/**
|
||||
* Fires when an admin note action is taken.
|
||||
* For more specific targeting of note actions.
|
||||
*
|
||||
* @param Note $note The corresponding Note.
|
||||
*/
|
||||
do_action( 'woocommerce_note_action_' . $triggered_action->name, $note );
|
||||
|
||||
// Update the note with the status for this action.
|
||||
if ( ! empty( $triggered_action->status ) ) {
|
||||
$note->set_status( $triggered_action->status );
|
||||
}
|
||||
|
||||
$note->save();
|
||||
|
||||
if ( in_array( $note->get_type(), array( 'error', 'update' ), true ) ) {
|
||||
$tracks_event = 'wcadmin_store_alert_action';
|
||||
} else {
|
||||
$tracks_event = 'wcadmin_inbox_action_click';
|
||||
}
|
||||
|
||||
wc_admin_record_tracks_event(
|
||||
$tracks_event,
|
||||
array(
|
||||
'note_name' => $note->get_name(),
|
||||
'note_type' => $note->get_type(),
|
||||
'note_title' => $note->get_title(),
|
||||
'note_content' => $note->get_content(),
|
||||
'action_name' => $triggered_action->name,
|
||||
'action_label' => $triggered_action->label,
|
||||
'screen' => self::get_screen_name(),
|
||||
)
|
||||
);
|
||||
return $note;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get screen name.
|
||||
*
|
||||
* @return string The screen name.
|
||||
*/
|
||||
public static function get_screen_name() {
|
||||
$screen_name = '';
|
||||
|
||||
if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
|
||||
parse_str( wp_parse_url( $_SERVER['HTTP_REFERER'], PHP_URL_QUERY ), $queries ); // phpcs:ignore sanitization ok.
|
||||
}
|
||||
if ( isset( $queries ) ) {
|
||||
$page = isset( $queries['page'] ) ? $queries['page'] : null;
|
||||
$path = isset( $queries['path'] ) ? $queries['path'] : null;
|
||||
$post_type = isset( $queries['post_type'] ) ? $queries['post_type'] : null;
|
||||
$post = isset( $queries['post'] ) ? get_post_type( $queries['post'] ) : null;
|
||||
}
|
||||
|
||||
if ( isset( $page ) ) {
|
||||
$current_page = 'wc-admin' === $page ? 'home_screen' : $page;
|
||||
$screen_name = isset( $path ) ? substr( str_replace( '/', '_', $path ), 1 ) : $current_page;
|
||||
} elseif ( isset( $post_type ) ) {
|
||||
$screen_name = $post_type;
|
||||
} elseif ( isset( $post ) ) {
|
||||
$screen_name = $post;
|
||||
}
|
||||
return $screen_name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class WC_Helper_Admin_Notes {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create two notes that we can use for notes REST API tests
|
||||
* Create four notes that we can use for notes REST API tests
|
||||
*/
|
||||
public static function add_notes_for_tests() {
|
||||
$data_store = WC_Data_Store::load( 'admin-note' );
|
||||
|
@ -102,4 +102,34 @@ class WC_Helper_Admin_Notes {
|
|||
$note_4->save();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a note that we can use for email notes tests
|
||||
*/
|
||||
public static function add_email_notes_for_test() {
|
||||
$data_store = WC_Data_Store::load( 'admin-note' );
|
||||
|
||||
$note_5 = new Note();
|
||||
$note_5->set_title( 'PHPUNIT_TEST_NOTE_5_TITLE' );
|
||||
$note_5->set_content( 'PHPUNIT_TEST_NOTE_5_CONTENT' );
|
||||
$additional_data = array(
|
||||
'heading' => 'PHPUNIT_TEST_EMAIL_HEADING',
|
||||
'role' => 'administrator',
|
||||
'template_html' => 'PHPUNIT_TEST_EMAIL_HTML_TEMPLATE',
|
||||
'template_plain' => 'PHPUNIT_TEST_EMAIL_HTML_PLAIN',
|
||||
);
|
||||
$note_5->set_content_data( (object) $additional_data );
|
||||
$note_5->set_type( Note::E_WC_ADMIN_NOTE_EMAIL );
|
||||
$note_5->set_name( 'PHPUNIT_TEST_NOTE_NAME' );
|
||||
$note_5->set_source( 'PHPUNIT_TEST' );
|
||||
$note_5->set_is_snoozable( false );
|
||||
$note_5->set_layout( 'plain' );
|
||||
$note_5->set_image( '' );
|
||||
$note_5->add_action(
|
||||
'PHPUNIT_TEST_NOTE_5_ACTION_SLUG',
|
||||
'PHPUNIT_TEST_NOTE_5_ACTION_LABEL',
|
||||
'?s=PHPUNIT_TEST_NOTE_5_ACTION_URL'
|
||||
);
|
||||
$note_5->save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
/**
|
||||
* Email notes tests
|
||||
*
|
||||
* @package WooCommerce\Admin\Tests\Notes
|
||||
*/
|
||||
|
||||
use \Automattic\WooCommerce\Admin\Notes\Notes;
|
||||
use \Automattic\WooCommerce\Admin\Notes\Note;
|
||||
use \Automattic\WooCommerce\Admin\Notes\MerchantEmailNotifications\MerchantEmailNotifications;
|
||||
use \Automattic\WooCommerce\Admin\Notes\MerchantEmailNotifications\NotificationEmail;
|
||||
|
||||
/**
|
||||
* Class WC_Tests_Email_Notes
|
||||
*/
|
||||
class WC_Tests_Email_Notes extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* Setup test admin notes data. Called before every test.
|
||||
*
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->user = $this->factory->user->create(
|
||||
array(
|
||||
'role' => 'administrator',
|
||||
)
|
||||
);
|
||||
|
||||
WC_Helper_Admin_Notes::reset_notes_dbs();
|
||||
WC_Helper_Admin_Notes::add_notes_for_tests();
|
||||
WC_Helper_Admin_Notes::add_email_notes_for_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests NotificationEmail default values.
|
||||
*/
|
||||
public function test_default_values_create_notification_email() {
|
||||
$note = new Note();
|
||||
$note->set_title( 'PHPUNIT_TEST_NOTE_EMAIL_TITLE' );
|
||||
$note->set_content( 'PHPUNIT_TEST_NOTE_EMAIL_CONTENT' );
|
||||
$note->set_type( Note::E_WC_ADMIN_NOTE_EMAIL );
|
||||
$note->set_name( 'PHPUNIT_TEST_NOTE_EMAIL_NAME' );
|
||||
$note->set_source( 'PHPUNIT_TEST' );
|
||||
$note->set_is_snoozable( false );
|
||||
$note->set_layout( 'plain' );
|
||||
$note->set_image( '' );
|
||||
$content_data = array(
|
||||
'role' => 'administrator',
|
||||
);
|
||||
$note->set_content_data( (object) $content_data );
|
||||
$note->add_action(
|
||||
'PHPUNIT_TEST_EMAIL_ACTION_SLUG',
|
||||
'PHPUNIT_TEST_EMAIL_ACTION_LABEL',
|
||||
'?s=PHPUNIT_TEST_EMAIL_ACTION_URL'
|
||||
);
|
||||
$note->set_is_deleted( false );
|
||||
$notification_email = new NotificationEmail( $note );
|
||||
|
||||
$this->assertEquals( $notification_email->id, 'merchant_notification' );
|
||||
$this->assertEquals( $notification_email->get_default_heading(), $note->get_title() );
|
||||
$this->assertEquals( $notification_email->get_template_filename(), 'html-merchant-notification.php' );
|
||||
$this->assertEquals( $notification_email->get_template_filename( 'html' ), 'html-merchant-notification.php' );
|
||||
$this->assertEquals( $notification_email->get_template_filename( 'plain' ), 'plain-merchant-notification.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests NotificationEmail is created correctly.
|
||||
*/
|
||||
public function test_create_notification_email() {
|
||||
$data_store = WC_Data_Store::load( 'admin-note' );
|
||||
$note_data = $data_store->get_notes(
|
||||
array(
|
||||
'type' => array( Note::E_WC_ADMIN_NOTE_EMAIL ),
|
||||
'status' => array( 'unactioned' ),
|
||||
)
|
||||
);
|
||||
$note = Notes::get_note( $note_data[0]->note_id );
|
||||
$content_data = array(
|
||||
'heading' => 'PHPUNIT_TEST_EMAIL_HEADING',
|
||||
'role' => 'administrator',
|
||||
);
|
||||
$note->set_content_data( (object) $content_data );
|
||||
$note->save();
|
||||
$notification_email = new NotificationEmail( $note );
|
||||
$notification_email->opened_tracking_url = 'PHPUNIT_TEST_NOTE_EMAIL_TRACKING_URL';
|
||||
$notification_email->trigger_note_action_url = 'PHPUNIT_TEST_NOTE_EMAIL_TRIGGER_ACTION_URL';
|
||||
$content_html = $notification_email->get_content_html();
|
||||
$content_plain = $notification_email->get_content_plain();
|
||||
|
||||
$this->assertEquals( $notification_email->get_default_heading(), $content_data['heading'] );
|
||||
$this->assertEquals( $notification_email->get_default_subject(), $note->get_title() );
|
||||
$this->assertEquals( $notification_email->get_note_content(), $note->get_content() );
|
||||
$this->assertEquals( $notification_email->get_note_content(), $note->get_content() );
|
||||
$this->assertTrue( strpos( $content_html, 'PHPUNIT_TEST_NOTE_5_ACTION_URL' ) >= 0 );
|
||||
$this->assertTrue( strpos( $content_html, 'PHPUNIT_TEST_NOTE_5_ACTION_LABEL' ) >= 0 );
|
||||
$this->assertTrue( strpos( $content_html, 'PHPUNIT_TEST_NOTE_5_CONTENT' ) >= 0 );
|
||||
$this->assertTrue( strpos( $content_html, 'PHPUNIT_TEST_NOTE_EMAIL_TRACKING_URL' ) >= 0 );
|
||||
$this->assertTrue( strpos( $content_html, 'PHPUNIT_TEST_NOTE_EMAIL_TRIGGER_ACTION_URL' ) >= 0 );
|
||||
$this->assertTrue( strpos( $content_plain, 'PHPUNIT_TEST_NOTE_5_ACTION_URL' ) >= 0 );
|
||||
$this->assertTrue( strpos( $content_plain, 'PHPUNIT_TEST_NOTE_5_ACTION_LABEL' ) >= 0 );
|
||||
$this->assertTrue( strpos( $content_plain, 'PHPUNIT_TEST_NOTE_5_CONTENT' ) >= 0 );
|
||||
$this->assertTrue( strpos( $content_plain, 'PHPUNIT_TEST_EMAIL_HEADING' ) >= 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests NotificationEmail validations.
|
||||
*/
|
||||
public function test_create_invalid_notification_email() {
|
||||
$data_store = WC_Data_Store::load( 'admin-note' );
|
||||
$note_data = $data_store->get_notes(
|
||||
array(
|
||||
'type' => array( Note::E_WC_ADMIN_NOTE_EMAIL ),
|
||||
'status' => array( 'unactioned' ),
|
||||
)
|
||||
);
|
||||
$note = Notes::get_note( $note_data[0]->note_id );
|
||||
$content_data = array(
|
||||
'role' => 'invalid_role',
|
||||
);
|
||||
$note->set_content_data( (object) $content_data );
|
||||
$notification_email = new NotificationEmail( $note );
|
||||
|
||||
$this->assertEmpty( MerchantEmailNotifications::get_notification_email_addresses( $note ) );
|
||||
$this->assertEmpty( $notification_email->get_template_filename( 'wrong_type' ) );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue