diff --git a/plugins/woocommerce-admin/images/admin_notes/img-product-light.png b/plugins/woocommerce-admin/images/admin_notes/img-product-light.png new file mode 100644 index 00000000000..3ecc1c2341e Binary files /dev/null and b/plugins/woocommerce-admin/images/admin_notes/img-product-light.png differ diff --git a/plugins/woocommerce-admin/includes/emails/html-merchant-notification.php b/plugins/woocommerce-admin/includes/emails/html-merchant-notification.php new file mode 100644 index 00000000000..9509cbcc901 --- /dev/null +++ b/plugins/woocommerce-admin/includes/emails/html-merchant-notification.php @@ -0,0 +1,69 @@ + + + +
+ +
+ + + 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;"; +?> +
+ + + label ); + ?> + + +
+
+ +
+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' ) ) ); diff --git a/plugins/woocommerce-admin/packages/data/src/constants.js b/plugins/woocommerce-admin/packages/data/src/constants.js index 4839bc0dd56..82764e56bac 100644 --- a/plugins/woocommerce-admin/packages/data/src/constants.js +++ b/plugins/woocommerce-admin/packages/data/src/constants.js @@ -24,4 +24,5 @@ export const QUERY_DEFAULTS = { pageSize: 25, period: 'month', compare: 'previous_year', + noteTypes: [ 'info', 'marketing', 'survey', 'warning' ], }; diff --git a/plugins/woocommerce-admin/readme.txt b/plugins/woocommerce-admin/readme.txt index e24c035aec4..395cdf1b4c5 100644 --- a/plugins/woocommerce-admin/readme.txt +++ b/plugins/woocommerce-admin/readme.txt @@ -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
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 diff --git a/plugins/woocommerce-admin/src/API/NoteActions.php b/plugins/woocommerce-admin/src/API/NoteActions.php index 5851d2f40fa..9f0f6b7ae97 100644 --- a/plugins/woocommerce-admin/src/API/NoteActions.php +++ b/plugins/woocommerce-admin/src/API/NoteActions.php @@ -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; - } } diff --git a/plugins/woocommerce-admin/src/API/Notes.php b/plugins/woocommerce-admin/src/API/Notes.php index 35a865b247c..52b278ae3f1 100644 --- a/plugins/woocommerce-admin/src/API/Notes.php +++ b/plugins/woocommerce-admin/src/API/Notes.php @@ -101,6 +101,19 @@ class Notes extends \WC_REST_CRUD_Controller { ) ); + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/tracker/(?P[\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. * diff --git a/plugins/woocommerce-admin/src/Events.php b/plugins/woocommerce-admin/src/Events.php index ae0887c84c1..5a671b0326a 100644 --- a/plugins/woocommerce-admin/src/Events.php +++ b/plugins/woocommerce-admin/src/Events.php @@ -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; + } } diff --git a/plugins/woocommerce-admin/src/FeaturePlugin.php b/plugins/woocommerce-admin/src/FeaturePlugin.php index 02eb6b64639..7ef996e19db 100644 --- a/plugins/woocommerce-admin/src/FeaturePlugin.php +++ b/plugins/woocommerce-admin/src/FeaturePlugin.php @@ -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(); } /** diff --git a/plugins/woocommerce-admin/src/Notes/AddFirstProduct.php b/plugins/woocommerce-admin/src/Notes/AddFirstProduct.php new file mode 100644 index 00000000000..18db173769f --- /dev/null +++ b/plugins/woocommerce-admin/src/Notes/AddFirstProduct.php @@ -0,0 +1,76 @@ + 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.

', 'woocommerce-admin' ), + __( 'There are three ways to add your products: you can create products manually, import them at once via CSV file, or migrate them from another service.

', 'woocommerce-admin' ), + __( 'Explore our docs 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; + } +} diff --git a/plugins/woocommerce-admin/src/Notes/DeprecatedNotes.php b/plugins/woocommerce-admin/src/Notes/DeprecatedNotes.php index 9979ffe9ee7..d3290a357cc 100644 --- a/plugins/woocommerce-admin/src/Notes/DeprecatedNotes.php +++ b/plugins/woocommerce-admin/src/Notes/DeprecatedNotes.php @@ -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. diff --git a/plugins/woocommerce-admin/src/Notes/MerchantEmailNotifications/MerchantEmailNotifications.php b/plugins/woocommerce-admin/src/Notes/MerchantEmailNotifications/MerchantEmailNotifications.php new file mode 100644 index 00000000000..5c5a043f1f7 --- /dev/null +++ b/plugins/woocommerce-admin/src/Notes/MerchantEmailNotifications/MerchantEmailNotifications.php @@ -0,0 +1,122 @@ +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' ); + } +} diff --git a/plugins/woocommerce-admin/src/Notes/MerchantEmailNotifications/NotificationEmail.php b/plugins/woocommerce-admin/src/Notes/MerchantEmailNotifications/NotificationEmail.php new file mode 100644 index 00000000000..502a78fcece --- /dev/null +++ b/plugins/woocommerce-admin/src/Notes/MerchantEmailNotifications/NotificationEmail.php @@ -0,0 +1,197 @@ +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() + ); + } +} diff --git a/plugins/woocommerce-admin/src/Notes/Note.php b/plugins/woocommerce-admin/src/Notes/Note.php index f0ef9075564..6e375144b84 100644 --- a/plugins/woocommerce-admin/src/Notes/Note.php +++ b/plugins/woocommerce-admin/src/Notes/Note.php @@ -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 ); diff --git a/plugins/woocommerce-admin/src/Notes/Notes.php b/plugins/woocommerce-admin/src/Notes/Notes.php index 68d9246ab5b..b0b889becbf 100644 --- a/plugins/woocommerce-admin/src/Notes/Notes.php +++ b/plugins/woocommerce-admin/src/Notes/Notes.php @@ -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; + } } diff --git a/plugins/woocommerce-admin/tests/framework/helpers/class-wc-helper-admin-notes.php b/plugins/woocommerce-admin/tests/framework/helpers/class-wc-helper-admin-notes.php index 43a56d80adc..bce9e3d750f 100644 --- a/plugins/woocommerce-admin/tests/framework/helpers/class-wc-helper-admin-notes.php +++ b/plugins/woocommerce-admin/tests/framework/helpers/class-wc-helper-admin-notes.php @@ -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(); + } } diff --git a/plugins/woocommerce-admin/tests/notes/class-wc-tests-email-notes.php b/plugins/woocommerce-admin/tests/notes/class-wc-tests-email-notes.php new file mode 100644 index 00000000000..25e971aca3d --- /dev/null +++ b/plugins/woocommerce-admin/tests/notes/class-wc-tests-email-notes.php @@ -0,0 +1,129 @@ +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' ) ); + } +}