* 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:
Fernando 2021-01-12 21:09:22 -03:00 committed by GitHub
parent 0b3f4d8e92
commit 513173a9d9
17 changed files with 823 additions and 87 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -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 );

View File

@ -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' ) ) );

View File

@ -24,4 +24,5 @@ export const QUERY_DEFAULTS = {
pageSize: 25, pageSize: 25,
period: 'month', period: 'month',
compare: 'previous_year', compare: 'previous_year',
noteTypes: [ 'info', 'marketing', 'survey', 'warning' ],
}; };

View File

@ -89,6 +89,8 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
- Add: Welcome modal when coming from Calypso #6004 - Add: Welcome modal when coming from Calypso #6004
- Enhancement: Add an a/b experiment for installing free business features #5786 - 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 `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 - Add: Note for users coming from Calypso. #6030
- Enhancement: Add an "unread" indicator to inbox messages. #6047 - Enhancement: Add an "unread" indicator to inbox messages. #6047

View File

@ -65,16 +65,7 @@ class NoteActions extends Notes {
); );
} }
// Find note action by ID. $triggered_action = NotesFactory::get_action_by_id( $note, $request->get_param( 'action_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;
}
}
if ( ! $triggered_action ) { if ( ! $triggered_action ) {
return new \WP_Error( return new \WP_Error(
@ -84,81 +75,12 @@ class NoteActions extends Notes {
); );
} }
/** $triggered_note = NotesFactory::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 );
/** $data = $triggered_note->get_data();
* 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 = $this->prepare_item_for_response( $data, $request ); $data = $this->prepare_item_for_response( $data, $request );
$data = $this->prepare_response_for_collection( $data ); $data = $this->prepare_response_for_collection( $data );
return rest_ensure_response( $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;
}
} }

View File

@ -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( register_rest_route(
$this->namespace, $this->namespace,
'/' . $this->rest_base . '/update', '/' . $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 ); 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. * Get the query params for collections.
* *

View File

@ -29,6 +29,7 @@ use \Automattic\WooCommerce\Admin\Notes\LaunchChecklist;
use \Automattic\WooCommerce\Admin\Notes\RealTimeOrderAlerts; use \Automattic\WooCommerce\Admin\Notes\RealTimeOrderAlerts;
use \Automattic\WooCommerce\Admin\RemoteInboxNotifications\DataSourcePoller; use \Automattic\WooCommerce\Admin\RemoteInboxNotifications\DataSourcePoller;
use \Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificationsEngine; use \Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificationsEngine;
use \Automattic\WooCommerce\Admin\Notes\MerchantEmailNotifications\MerchantEmailNotifications;
use \Automattic\WooCommerce\Admin\Loader; use \Automattic\WooCommerce\Admin\Loader;
use \Automattic\WooCommerce\Admin\Notes\InsightFirstSale; use \Automattic\WooCommerce\Admin\Notes\InsightFirstSale;
use \Automattic\WooCommerce\Admin\Notes\NeedSomeInspiration; 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\NavigationFeedback;
use \Automattic\WooCommerce\Admin\Notes\NavigationFeedbackFollowUp; use \Automattic\WooCommerce\Admin\Notes\NavigationFeedbackFollowUp;
use \Automattic\WooCommerce\Admin\Notes\FilterByProductVariationsInReports; use \Automattic\WooCommerce\Admin\Notes\FilterByProductVariationsInReports;
use \Automattic\WooCommerce\Admin\Notes\AddFirstProduct;
use \Automattic\WooCommerce\Admin\Notes\DrawAttention; use \Automattic\WooCommerce\Admin\Notes\DrawAttention;
/** /**
@ -88,6 +90,22 @@ class Events {
* Note: Order_Milestones::other_milestones is hooked to this as well. * Note: Order_Milestones::other_milestones is hooked to this as well.
*/ */
public function do_wc_admin_daily() { 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(); NewSalesRecord::possibly_add_note();
MobileApp::possibly_add_note(); MobileApp::possibly_add_note();
TrackingOptIn::possibly_add_note(); TrackingOptIn::possibly_add_note();
@ -120,12 +138,8 @@ class Events {
DrawAttention::possibly_add_note(); DrawAttention::possibly_add_note();
ChoosingTheme::possibly_add_note(); ChoosingTheme::possibly_add_note();
InsightFirstProductAndPayment::possibly_add_note(); InsightFirstProductAndPayment::possibly_add_note();
AddFirstProduct::possibly_add_note();
AddingAndManangingProducts::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. // All checks have passed.
return true; 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;
}
} }

View File

@ -21,6 +21,7 @@ use \Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificati
use \Automattic\WooCommerce\Admin\Notes\SetUpAdditionalPaymentTypes; use \Automattic\WooCommerce\Admin\Notes\SetUpAdditionalPaymentTypes;
use \Automattic\WooCommerce\Admin\Notes\TestCheckout; use \Automattic\WooCommerce\Admin\Notes\TestCheckout;
use \Automattic\WooCommerce\Admin\Notes\SellingOnlineCourses; use \Automattic\WooCommerce\Admin\Notes\SellingOnlineCourses;
use \Automattic\WooCommerce\Admin\Notes\MerchantEmailNotifications\MerchantEmailNotifications;
use \Automattic\WooCommerce\Admin\Notes\WelcomeToWooCommerceForStoreUsers; use \Automattic\WooCommerce\Admin\Notes\WelcomeToWooCommerceForStoreUsers;
/** /**
@ -196,6 +197,9 @@ class FeaturePlugin {
// Initialize RemoteInboxNotificationsEngine. // Initialize RemoteInboxNotificationsEngine.
RemoteInboxNotificationsEngine::init(); RemoteInboxNotificationsEngine::init();
// Initialize MerchantEmailNotifications.
MerchantEmailNotifications::init();
} }
/** /**

View File

@ -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, youve created a WooCommerce store! Now its 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;
}
}

View File

@ -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_UNACTIONED = Note::E_WC_ADMIN_NOTE_UNACTIONED;
const E_WC_ADMIN_NOTE_ACTIONED = Note::E_WC_ADMIN_NOTE_ACTIONED; 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_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. * The name of the non-deprecated class that this facade covers.

View File

@ -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' );
}
}

View File

@ -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&note=%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()
);
}
}

View File

@ -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_INFORMATIONAL = 'info'; // used for presenting informational messages.
const E_WC_ADMIN_NOTE_MARKETING = 'marketing'; // used for adding marketing 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_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. // Note status codes.
const E_WC_ADMIN_NOTE_PENDING = 'pending'; // the note is pending - hidden but not actioned. 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_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_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_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. * 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_INFORMATIONAL,
self::E_WC_ADMIN_NOTE_MARKETING, self::E_WC_ADMIN_NOTE_MARKETING,
self::E_WC_ADMIN_NOTE_SURVEY, self::E_WC_ADMIN_NOTE_SURVEY,
self::E_WC_ADMIN_NOTE_EMAIL,
); );
return apply_filters( 'woocommerce_note_types', $allowed_types ); 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_ACTIONED,
self::E_WC_ADMIN_NOTE_UNACTIONED, self::E_WC_ADMIN_NOTE_UNACTIONED,
self::E_WC_ADMIN_NOTE_SNOOZED, self::E_WC_ADMIN_NOTE_SNOOZED,
self::E_WC_ADMIN_NOTE_SENT,
); );
return apply_filters( 'woocommerce_note_statuses', $allowed_statuses ); return apply_filters( 'woocommerce_note_statuses', $allowed_statuses );

View File

@ -301,4 +301,104 @@ class Notes {
return $note->get_status(); 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;
}
} }

View File

@ -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() { public static function add_notes_for_tests() {
$data_store = WC_Data_Store::load( 'admin-note' ); $data_store = WC_Data_Store::load( 'admin-note' );
@ -102,4 +102,34 @@ class WC_Helper_Admin_Notes {
$note_4->save(); $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();
}
} }

View File

@ -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' ) );
}
}