File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/Notes.tar
AbstractNote.php 0000644 00000002201 15154307501 0007642 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface;
use WC_Data_Store;
defined( 'ABSPATH' ) || exit;
/**
* AbstractNote class.
*
* @since 1.7.0
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
abstract class AbstractNote implements Note, OptionsAwareInterface {
/**
* Remove the note from the datastore.
*
* @since 1.12.5
*/
public function delete() {
if ( class_exists( Notes::class ) ) {
Notes::delete_notes_with_name( $this->get_name() );
}
}
/**
* Get note data store.
*
* @see \Automattic\WooCommerce\Admin\Notes\DataStore for relavent data store.
*
* @return WC_Data_Store
*/
protected function get_data_store(): WC_Data_Store {
return WC_Data_Store::load( 'admin-note' );
}
/**
* Check if the note has already been added.
*
* @return bool
*/
protected function has_been_added(): bool {
$note_ids = $this->get_data_store()->get_notes_with_name( $this->get_name() );
return ! empty( $note_ids );
}
}
AbstractSetupCampaign.php 0000644 00000005714 15154307502 0011512 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
use Automattic\WooCommerce\GoogleListingsAndAds\Ads\AdsAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Ads\AdsAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\Utilities;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterService;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use Exception;
use stdClass;
defined( 'ABSPATH' ) || exit;
/**
* Abstract Class AbstractSetupCampaign
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
* @since 1.11.0
*/
abstract class AbstractSetupCampaign extends AbstractNote implements AdsAwareInterface {
use AdsAwareTrait;
use PluginHelper;
use Utilities;
/**
* @var MerchantCenterService
*/
protected $merchant_center_service;
/**
* AbstractSetupCampaign constructor.
*
* @param MerchantCenterService $merchant_center_service
*/
public function __construct( MerchantCenterService $merchant_center_service ) {
$this->merchant_center_service = $merchant_center_service;
}
/**
* Get the note entry.
*/
public function get_entry(): NoteEntry {
$note = new NoteEntry();
$this->set_title_and_content( $note );
$this->add_common_note_settings( $note );
return $note;
}
/**
* @param NoteEntry $note
*
* @return void
*/
protected function add_common_note_settings( NoteEntry $note ): void {
$note->set_content_data( new stdClass() );
$note->set_type( NoteEntry::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_layout( 'plain' );
$note->set_image( '' );
$note->set_name( $this->get_name() );
$note->set_source( $this->get_slug() );
}
/**
* Checks if a note can and should be added.
*
* Check if ads setup IS NOT complete
* Check if it is > $this->get_gla_setup_days() days ago from DATE OF SETUP COMPLETION
* Send notification
*
* @return bool
*/
public function should_be_added(): bool {
if ( $this->has_been_added() ) {
return false;
}
if ( $this->ads_service->is_setup_complete() ) {
return false;
}
if ( ! $this->gla_setup_for( $this->get_gla_setup_days() * DAY_IN_SECONDS ) ) {
return false;
}
// We don't need to process exceptions here, as we're just determining whether to add a note.
try {
if ( $this->merchant_center_service->has_account_issues() ) {
return false;
}
if ( ! $this->merchant_center_service->has_at_least_one_synced_product() ) {
return false;
}
} catch ( Exception $e ) {
return false;
}
return true;
}
/**
* Get the number of days after which to add the note.
*
* @since 1.11.0
*
* @return int
*/
abstract protected function get_gla_setup_days(): int;
/**
* Set the title and content of the Note.
*
* @since 1.11.0
*
* @param NoteEntry $note
*
* @return void
*/
abstract protected function set_title_and_content( NoteEntry $note ): void;
}
CompleteSetup.php 0000644 00000004066 15154307502 0010056 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\Utilities;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use stdClass;
defined( 'ABSPATH' ) || exit;
/**
* Class CompleteSetup
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
class CompleteSetup extends AbstractNote implements MerchantCenterAwareInterface {
use MerchantCenterAwareTrait;
use PluginHelper;
use Utilities;
/**
* Get the note's unique name.
*
* @return string
*/
public function get_name(): string {
return 'gla-complete-setup';
}
/**
* Get the note entry.
*/
public function get_entry(): NoteEntry {
$note = new NoteEntry();
$note->set_title( __( 'Reach more shoppers with free listings on Google', 'google-listings-and-ads' ) );
$note->set_content( __( 'Finish setting up Google for WooCommerce to list your products on Google for free and promote them with ads.', 'google-listings-and-ads' ) );
$note->set_content_data( new stdClass() );
$note->set_type( NoteEntry::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_layout( 'plain' );
$note->set_image( '' );
$note->set_name( $this->get_name() );
$note->set_source( $this->get_slug() );
$note->add_action(
'complete-setup',
__( 'Finish setup', 'google-listings-and-ads' ),
$this->get_start_url()
);
return $note;
}
/**
* Checks if a note can and should be added.
*
* Check if setup IS NOT complete
* Check if a stores done 5 sales
* Send notification
*
* @return bool
*/
public function should_be_added(): bool {
if ( $this->has_been_added() ) {
return false;
}
if ( $this->merchant_center->is_setup_complete() ) {
return false;
}
if ( ! $this->has_orders( 5 ) ) {
return false;
}
return true;
}
}
ContactInformation.php 0000644 00000004406 15154307502 0011064 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\Utilities;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
defined( 'ABSPATH' ) || exit;
/**
* Class ContactInformation
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*
* @since 1.4.0
*/
class ContactInformation extends AbstractNote implements MerchantCenterAwareInterface {
use MerchantCenterAwareTrait;
use PluginHelper;
use Utilities;
/**
* Get the note's unique name.
*
* @return string
*/
public function get_name(): string {
return 'gla-contact-information';
}
/**
* Get the note entry.
*/
public function get_entry(): NoteEntry {
$note = new NoteEntry();
$note->set_title( __( 'Please add your contact information', 'google-listings-and-ads' ) );
$note->set_content( __( 'Google requires the phone number and store address for all stores using Google Merchant Center. This is required to verify your store, and it will not be shown to customers. If you do not add your contact information, your listings may not appear on Google.', 'google-listings-and-ads' ) );
$note->set_content_data( (object) [] );
$note->set_type( NoteEntry::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_layout( 'plain' );
$note->set_image( '' );
$note->set_name( $this->get_name() );
$note->set_source( $this->get_slug() );
$note->add_action(
'contact-information',
__( 'Add contact information', 'google-listings-and-ads' ),
$this->get_settings_url()
);
return $note;
}
/**
* Checks if a note can and should be added.
*
* Checks if merchant center has been setup and contact information is valid.
* Send notification
*
* @return bool
*/
public function should_be_added(): bool {
if ( $this->has_been_added() ) {
return false;
}
if ( ! $this->merchant_center->is_connected() ) {
return false;
}
if ( $this->merchant_center->is_contact_information_setup() ) {
return false;
}
return true;
}
}
LeaveReviewActionTrait.php 0000644 00000001534 15154307502 0011642 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
/**
* Trait LeaveReviewActionTrait
*
* @since 1.7.0
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
trait LeaveReviewActionTrait {
/**
* Add the 'leave a review' action to a note.
*
* Randomly chooses whether to show the WP.org vs WC.com link.
*
* @param NoteEntry $note
*/
protected function add_leave_review_note_action( NoteEntry $note ) {
$wp_link = 'https://wordpress.org/support/plugin/google-listings-and-ads/reviews/#new-post';
$wc_link = 'https://woocommerce.com/products/google-listings-and-ads/#reviews';
$note->add_action(
'leave-review',
__( 'Leave a review', 'google-listings-and-ads' ),
wp_rand( 0, 1 ) ? $wp_link : $wc_link
);
}
}
Note.php 0000644 00000045753 15154307502 0006202 0 ustar 00 <?php
/**
* WooCommerce Admin (Dashboard) Notes.
*
* The WooCommerce admin notes class gets admin notes data from storage and checks validity.
*/
namespace Automattic\WooCommerce\Admin\Notes;
defined( 'ABSPATH' ) || exit;
/**
* Note class.
*/
class Note extends \WC_Data {
// Note types.
const E_WC_ADMIN_NOTE_ERROR = 'error'; // used for presenting error conditions.
const E_WC_ADMIN_NOTE_WARNING = 'warning'; // used for presenting warning conditions.
const E_WC_ADMIN_NOTE_UPDATE = 'update'; // i.e. used when a new version is available.
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.
*
* @var string
*/
protected $object_type = 'admin-note';
/**
* Cache group.
*
* @var string
*/
protected $cache_group = 'admin-note';
/**
* Note constructor. Loads note data.
*
* @param mixed $data Note data, object, or ID.
*/
public function __construct( $data = '' ) {
// Set default data here to allow `content_data` to be an object.
$this->data = array(
'name' => '-',
'type' => self::E_WC_ADMIN_NOTE_INFORMATIONAL,
'locale' => 'en_US',
'title' => '-',
'content' => '-',
'content_data' => new \stdClass(),
'status' => self::E_WC_ADMIN_NOTE_UNACTIONED,
'source' => 'woocommerce',
'date_created' => '0000-00-00 00:00:00',
'date_reminder' => '',
'is_snoozable' => false,
'actions' => array(),
'layout' => 'plain',
'image' => '',
'is_deleted' => false,
'is_read' => false,
);
parent::__construct( $data );
if ( $data instanceof Note ) {
$this->set_id( absint( $data->get_id() ) );
} elseif ( is_numeric( $data ) ) {
$this->set_id( $data );
} elseif ( is_object( $data ) && ! empty( $data->note_id ) ) {
$this->set_id( $data->note_id );
unset( $data->icon ); // Icons are deprecated.
$this->set_props( (array) $data );
$this->set_object_read( true );
} else {
$this->set_object_read( true );
}
$this->data_store = Notes::load_data_store();
if ( $this->get_id() > 0 ) {
$this->data_store->read( $this );
}
}
/**
* Merge changes with data and clear.
*
* @since 3.0.0
*/
public function apply_changes() {
$this->data = array_replace_recursive( $this->data, $this->changes ); // @codingStandardsIgnoreLine
// Note actions need to be replaced wholesale.
// Merging arrays doesn't allow for deleting note actions.
if ( isset( $this->changes['actions'] ) ) {
$this->data['actions'] = $this->changes['actions'];
}
$this->changes = array();
}
/*
|--------------------------------------------------------------------------
| Helpers
|--------------------------------------------------------------------------
|
| Methods for getting allowed types, statuses.
|
*/
/**
* Get allowed types.
*
* @return array
*/
public static function get_allowed_types() {
$allowed_types = array(
self::E_WC_ADMIN_NOTE_ERROR,
self::E_WC_ADMIN_NOTE_WARNING,
self::E_WC_ADMIN_NOTE_UPDATE,
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 );
}
/**
* Get allowed statuses.
*
* @return array
*/
public static function get_allowed_statuses() {
$allowed_statuses = array(
self::E_WC_ADMIN_NOTE_PENDING,
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 );
}
/*
|--------------------------------------------------------------------------
| Getters
|--------------------------------------------------------------------------
|
| Methods for getting data from the note object.
|
*/
/**
* Returns all data for this object.
*
* Override \WC_Data::get_data() to avoid errantly including meta data
* from ID collisions with the posts table.
*
* @return array
*/
public function get_data() {
return array_merge( array( 'id' => $this->get_id() ), $this->data );
}
/**
* Get note name.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return string
*/
public function get_name( $context = 'view' ) {
return $this->get_prop( 'name', $context );
}
/**
* Get note type.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return string
*/
public function get_type( $context = 'view' ) {
return $this->get_prop( 'type', $context );
}
/**
* Get note locale.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return string
*/
public function get_locale( $context = 'view' ) {
return $this->get_prop( 'locale', $context );
}
/**
* Get note title.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return string
*/
public function get_title( $context = 'view' ) {
return $this->get_prop( 'title', $context );
}
/**
* Get note content.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return string
*/
public function get_content( $context = 'view' ) {
return $this->get_prop( 'content', $context );
}
/**
* Get note content data (i.e. values that would be needed for re-localization)
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return object
*/
public function get_content_data( $context = 'view' ) {
return $this->get_prop( 'content_data', $context );
}
/**
* Get note status.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return string
*/
public function get_status( $context = 'view' ) {
return $this->get_prop( 'status', $context );
}
/**
* Get note source.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return string
*/
public function get_source( $context = 'view' ) {
return $this->get_prop( 'source', $context );
}
/**
* Get date note was created.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return WC_DateTime|NULL object if the date is set or null if there is no date.
*/
public function get_date_created( $context = 'view' ) {
return $this->get_prop( 'date_created', $context );
}
/**
* Get date on which user should be reminded of the note (if any).
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return WC_DateTime|NULL object if the date is set or null if there is no date.
*/
public function get_date_reminder( $context = 'view' ) {
return $this->get_prop( 'date_reminder', $context );
}
/**
* Get note snoozability.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return bool Whether or not the note can be snoozed.
*/
public function get_is_snoozable( $context = 'view' ) {
return $this->get_prop( 'is_snoozable', $context );
}
/**
* Get actions on the note (if any).
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return array
*/
public function get_actions( $context = 'view' ) {
return $this->get_prop( 'actions', $context );
}
/**
* Get action by action name on the note.
*
* @param string $action_name The action name.
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return object the action.
*/
public function get_action( $action_name, $context = 'view' ) {
$actions = $this->get_prop( 'actions', $context );
$matching_action = null;
foreach ( $actions as $i => $action ) {
if ( $action->name === $action_name ) {
$matching_action =& $actions[ $i ];
break;
}
}
return $matching_action;
}
/**
* Get note layout (the old notes won't have one).
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return array
*/
public function get_layout( $context = 'view' ) {
return $this->get_prop( 'layout', $context );
}
/**
* Get note image (if any).
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return array
*/
public function get_image( $context = 'view' ) {
return $this->get_prop( 'image', $context );
}
/**
* Get deleted status.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return array
*/
public function get_is_deleted( $context = 'view' ) {
return $this->get_prop( 'is_deleted', $context );
}
/**
* Get is_read status.
*
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
* @return array
*/
public function get_is_read( $context = 'view' ) {
return $this->get_prop( 'is_read', $context );
}
/*
|--------------------------------------------------------------------------
| Setters
|--------------------------------------------------------------------------
|
| Methods for setting note data. These should not update anything in the
| database itself and should only change what is stored in the class
| object.
|
*/
/**
* Set note name.
*
* @param string $name Note name.
*/
public function set_name( $name ) {
// Don't allow empty names.
if ( empty( $name ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note name prop cannot be empty.', 'woocommerce' ) );
}
$this->set_prop( 'name', $name );
}
/**
* Set note type.
*
* @param string $type Note type.
*/
public function set_type( $type ) {
if ( empty( $type ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note type prop cannot be empty.', 'woocommerce' ) );
}
if ( ! in_array( $type, self::get_allowed_types(), true ) ) {
$this->error(
'admin_note_invalid_data',
sprintf(
/* translators: %s: admin note type. */
__( 'The admin note type prop (%s) is not one of the supported types.', 'woocommerce' ),
$type
)
);
}
$this->set_prop( 'type', $type );
}
/**
* Set note locale.
*
* @param string $locale Note locale.
*/
public function set_locale( $locale ) {
if ( empty( $locale ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note locale prop cannot be empty.', 'woocommerce' ) );
}
$this->set_prop( 'locale', $locale );
}
/**
* Set note title.
*
* @param string $title Note title.
*/
public function set_title( $title ) {
if ( empty( $title ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note title prop cannot be empty.', 'woocommerce' ) );
}
$this->set_prop( 'title', $title );
}
/**
* Set note icon (Deprecated).
*
* @param string $icon Note icon.
*/
public function set_icon( $icon ) {
wc_deprecated_function( 'set_icon', '4.3' );
}
/**
* Set note content.
*
* @param string $content Note content.
*/
public function set_content( $content ) {
$allowed_html = array(
'br' => array(),
'em' => array(),
'strong' => array(),
'a' => array(
'href' => true,
'rel' => true,
'name' => true,
'target' => true,
'download' => array(
'valueless' => 'y',
),
),
'p' => array(),
);
$content = wp_kses( $content, $allowed_html );
if ( empty( $content ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note content prop cannot be empty.', 'woocommerce' ) );
}
$this->set_prop( 'content', $content );
}
/**
* Set note data for potential re-localization.
*
* @todo Set a default empty array? https://github.com/woocommerce/woocommerce-admin/pull/1763#pullrequestreview-212442921.
* @param object $content_data Note data.
*/
public function set_content_data( $content_data ) {
$allowed_type = false;
// Make sure $content_data is stdClass Object or an array.
if ( ! ( $content_data instanceof \stdClass ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note content_data prop must be an instance of stdClass.', 'woocommerce' ) );
}
$this->set_prop( 'content_data', $content_data );
}
/**
* Set note status.
*
* @param string $status Note status.
*/
public function set_status( $status ) {
if ( empty( $status ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note status prop cannot be empty.', 'woocommerce' ) );
}
if ( ! in_array( $status, self::get_allowed_statuses(), true ) ) {
$this->error(
'admin_note_invalid_data',
sprintf(
/* translators: %s: admin note status property. */
__( 'The admin note status prop (%s) is not one of the supported statuses.', 'woocommerce' ),
$status
)
);
}
$this->set_prop( 'status', $status );
}
/**
* Set note source.
*
* @param string $source Note source.
*/
public function set_source( $source ) {
if ( empty( $source ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note source prop cannot be empty.', 'woocommerce' ) );
}
$this->set_prop( 'source', $source );
}
/**
* Set date note was created. NULL is not allowed
*
* @param string|integer $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed.
*/
public function set_date_created( $date ) {
if ( empty( $date ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note date prop cannot be empty.', 'woocommerce' ) );
}
if ( is_string( $date ) ) {
$date = wc_string_to_timestamp( $date );
}
$this->set_date_prop( 'date_created', $date );
}
/**
* Set date admin should be reminded of note. NULL IS allowed
*
* @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if there is no date.
*/
public function set_date_reminder( $date ) {
if ( is_string( $date ) ) {
$date = wc_string_to_timestamp( $date );
}
$this->set_date_prop( 'date_reminder', $date );
}
/**
* Set note snoozability.
*
* @param bool $is_snoozable Whether or not the note can be snoozed.
*/
public function set_is_snoozable( $is_snoozable ) {
return $this->set_prop( 'is_snoozable', $is_snoozable );
}
/**
* Clear actions from a note.
*/
public function clear_actions() {
$this->set_prop( 'actions', array() );
}
/**
* Set note layout.
*
* @param string $layout Note layout.
*/
public function set_layout( $layout ) {
// If we don't receive a layout we will set it by default as "plain".
if ( empty( $layout ) ) {
$layout = 'plain';
}
$valid_layouts = array( 'banner', 'plain', 'thumbnail' );
if ( in_array( $layout, $valid_layouts, true ) ) {
$this->set_prop( 'layout', $layout );
} else {
$this->error( 'admin_note_invalid_data', __( 'The admin note layout has a wrong prop value.', 'woocommerce' ) );
}
}
/**
* Set note image.
*
* @param string $image Note image.
*/
public function set_image( $image ) {
$this->set_prop( 'image', $image );
}
/**
* Set note deleted status. NULL is not allowed
*
* @param bool $is_deleted Note deleted status.
*/
public function set_is_deleted( $is_deleted ) {
$this->set_prop( 'is_deleted', $is_deleted );
}
/**
* Set note is_read status. NULL is not allowed
*
* @param bool $is_read Note is_read status.
*/
public function set_is_read( $is_read ) {
$this->set_prop( 'is_read', $is_read );
}
/**
* Add an action to the note
*
* @param string $name Action name (not presented to user).
* @param string $label Action label (presented as button label).
* @param string $url Action URL, if navigation needed. Optional.
* @param string $status Status to transition parent Note to upon click. Defaults to 'actioned'.
* @param boolean $primary Deprecated since version 3.4.0.
* @param string $actioned_text The label to display after the note has been actioned but before it is dismissed in the UI.
*/
public function add_action(
$name,
$label,
$url = '',
$status = self::E_WC_ADMIN_NOTE_ACTIONED,
$primary = false,
$actioned_text = ''
) {
$name = wc_clean( $name );
$label = wc_clean( $label );
$query = esc_url_raw( $url );
$status = wc_clean( $status );
$actioned_text = wc_clean( $actioned_text );
if ( empty( $name ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note action name prop cannot be empty.', 'woocommerce' ) );
}
if ( empty( $label ) ) {
$this->error( 'admin_note_invalid_data', __( 'The admin note action label prop cannot be empty.', 'woocommerce' ) );
}
$action = array(
'name' => $name,
'label' => $label,
'query' => $query,
'status' => $status,
'actioned_text' => $actioned_text,
'nonce_name' => null,
'nonce_action' => null,
);
$note_actions = $this->get_prop( 'actions', 'edit' );
$note_actions[] = (object) $action;
$this->set_prop( 'actions', $note_actions );
}
/**
* Set actions on a note.
*
* @param array $actions Note actions.
*/
public function set_actions( $actions ) {
$this->set_prop( 'actions', $actions );
}
/**
* Add a nonce to an existing note action.
*
* @link https://codex.wordpress.org/WordPress_Nonces
*
* @param string $note_action_name Name of action to add a nonce to.
* @param string $nonce_action The nonce action.
* @param string $nonce_name The nonce Name. This is used as the paramater name in the resulting URL for the action.
* @return void
* @throws \Exception If note name cannot be found.
*/
public function add_nonce_to_action( string $note_action_name, string $nonce_action, string $nonce_name ) {
$actions = $this->get_prop( 'actions', 'edit' );
$matching_action = null;
foreach ( $actions as $i => $action ) {
if ( $action->name === $note_action_name ) {
$matching_action =& $actions[ $i ];
}
}
if ( empty( $matching_action ) ) {
throw new \Exception( sprintf( 'Could not find action %s in note %s', $note_action_name, $this->get_name() ) );
}
$matching_action->nonce_action = $nonce_action;
$matching_action->nonce_name = $nonce_name;
$this->set_actions( $actions );
}
}
NoteInitializer.php 0000644 00000006777 15154307502 0010411 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\GoogleListingsAndAds\ActionScheduler\ActionSchedulerException;
use Automattic\WooCommerce\GoogleListingsAndAds\ActionScheduler\ActionSchedulerInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Exception\ValidateInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Activateable;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Deactivateable;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Registerable;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;
use Automattic\WooCommerce\GoogleListingsAndAds\Internal\Interfaces\InstallableInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Internal\ContainerAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\Internal\Interfaces\ContainerAwareInterface;
use Exception;
defined( 'ABSPATH' ) || exit;
/**
* NoteInitializer class.
*
* ContainerAware used to access:
* - Note
*
* @since 1.7.0
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
class NoteInitializer implements Activateable, Deactivateable, InstallableInterface, Service, Registerable, ContainerAwareInterface {
use ValidateInterface;
use ContainerAwareTrait;
/**
* Hook name for daily cron.
*/
protected const CRON_HOOK = 'wc_gla_cron_daily_notes';
/**
* @var ActionSchedulerInterface
*/
protected $action_scheduler;
/**
* NoteInitializer constructor.
*
* @param ActionSchedulerInterface $action_scheduler
*/
public function __construct( ActionSchedulerInterface $action_scheduler ) {
$this->action_scheduler = $action_scheduler;
}
/**
* Register the service.
*/
public function register(): void {
add_action( self::CRON_HOOK, [ $this, 'add_notes' ] );
}
/**
* Loop through all notes to add any that should be added.
*/
public function add_notes(): void {
$notes = $this->container->get( Note::class );
foreach ( $notes as $note ) {
try {
if ( $note->should_be_added() ) {
$note->get_entry()->save();
}
} catch ( Exception $e ) {
do_action( 'woocommerce_gla_exception', $e, __METHOD__ );
}
}
}
/**
* Activate the service.
*
* @return void
*/
public function activate(): void {
$this->maybe_add_cron_job();
}
/**
* Run's when plugin is installed or updated.
*
* @param string $old_version Previous version before updating.
* @param string $new_version Current version after updating.
*/
public function install( string $old_version, string $new_version ): void {
$this->maybe_add_cron_job();
}
/**
* Add notes cron job if it doesn't already exist.
*/
protected function maybe_add_cron_job(): void {
if ( ! $this->action_scheduler->has_scheduled_action( self::CRON_HOOK ) ) {
$this->action_scheduler->schedule_recurring( time(), DAY_IN_SECONDS, self::CRON_HOOK );
}
}
/**
* Deactivate the service.
*
* Delete the notes cron job and all notes.
*/
public function deactivate(): void {
try {
$this->action_scheduler->cancel( self::CRON_HOOK );
} catch ( ActionSchedulerException $e ) {
do_action( 'woocommerce_gla_exception', $e, __METHOD__ );
}
// Ensure all note names are deleted
if ( class_exists( Notes::class ) ) {
$note_names = [];
$notes = $this->container->get( Note::class );
foreach ( $notes as $note ) {
$note_names[] = $note->get_name();
}
Notes::delete_notes_with_name( $note_names );
}
}
}
ReconnectWordPress.php 0000644 00000005716 15154307502 0011061 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\Connection;
use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\Utilities;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
defined( 'ABSPATH' ) || exit;
/**
* Class ReconnectWordPress
*
* @since 1.12.5
*
* Note for prompting to reconnect the WordPress.com account.
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
class ReconnectWordPress extends AbstractNote implements MerchantCenterAwareInterface {
use PluginHelper;
use Utilities;
use MerchantCenterAwareTrait;
/**
* @var Connection
*/
protected $connection;
/**
* ReconnectWordPress constructor.
*
* @param Connection $connection
*/
public function __construct( Connection $connection ) {
$this->connection = $connection;
}
/**
* Get the note's unique name.
*
* @return string
*/
public function get_name(): string {
return 'gla-reconnect-wordpress';
}
/**
* Get the note entry.
*/
public function get_entry(): NoteEntry {
$note = new NoteEntry();
$note->set_title(
__( 'Re-connect your store to Google for WooCommerce', 'google-listings-and-ads' )
);
$note->set_content(
__( 'Your WordPress.com account has been disconnected from Google for WooCommerce. Connect your WordPress.com account again to ensure your products stay listed on Google through the Google for WooCommerce extension.<br/><br/>If you do not re-connect, any existing listings may be removed from Google.', 'google-listings-and-ads' )
);
$note->set_content_data( (object) [] );
$note->set_type( NoteEntry::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( $this->get_name() );
$note->set_source( $this->get_slug() );
$note->add_action(
'reconnect-wordpress',
__( 'Go to Google for WooCommerce', 'google-listings-and-ads' ),
add_query_arg( 'subpath', '/reconnect-wpcom-account', $this->get_settings_url() )
);
return $note;
}
/**
* Checks if a note can and should be added.
*
* - Triggers a status check if not already disconnected.
* - Checks if Jetpack is disconnected.
*
* @return bool
*/
public function should_be_added(): bool {
if ( $this->has_been_added() || ! $this->merchant_center->is_setup_complete() ) {
return false;
}
$this->maybe_check_status();
return ! $this->is_jetpack_connected();
}
/**
* Trigger a status check if we are not already disconnected.
* A request to the server must be sent to detect a disconnect.
*/
protected function maybe_check_status() {
if ( ! $this->is_jetpack_connected() ) {
return;
}
try {
$this->connection->get_status();
} catch ( Exception $e ) {
return;
}
}
}
ReviewAfterClicks.php 0000644 00000006420 15154307502 0010635 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\MerchantMetrics;
use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\Utilities;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WP;
use Exception;
defined( 'ABSPATH' ) || exit;
/**
* Class ReviewAfterClicks
*
* Note for requesting a review after 10 clicks.
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
class ReviewAfterClicks extends AbstractNote implements MerchantCenterAwareInterface {
use LeaveReviewActionTrait;
use MerchantCenterAwareTrait;
use PluginHelper;
use Utilities;
/**
* @var MerchantMetrics
*/
protected $merchant_metrics;
/**
* @var WP
*/
protected $wp;
/**
* ReviewAfterClicks constructor.
*
* @param MerchantMetrics $merchant_metrics
* @param WP $wp
*/
public function __construct( MerchantMetrics $merchant_metrics, WP $wp ) {
$this->merchant_metrics = $merchant_metrics;
$this->wp = $wp;
}
/**
* Get the note's unique name.
*
* @return string
*/
public function get_name(): string {
return 'gla-review-after-clicks';
}
/**
* Get the note entry.
*
* @throws Exception When unable to get clicks data.
*/
public function get_entry(): NoteEntry {
$clicks_count = $this->get_free_listing_clicks_count();
// Round to nearest 10
$clicks_count_rounded = floor( $clicks_count / 10 ) * 10;
$note = new NoteEntry();
$note->set_title(
sprintf(
/* translators: %s number of clicks */
__( 'You’ve gotten %s+ clicks on your free listings! 🎉', 'google-listings-and-ads' ),
$this->wp->number_format_i18n( $clicks_count_rounded )
)
);
$note->set_content(
__( 'Congratulations! Tell us what you think about Google for WooCommerce by leaving a review. Your feedback will help us make WooCommerce even better for you.', 'google-listings-and-ads' )
);
$note->set_content_data( (object) [] );
$note->set_type( NoteEntry::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( $this->get_name() );
$note->set_source( $this->get_slug() );
$this->add_leave_review_note_action( $note );
return $note;
}
/**
* Checks if a note can and should be added.
*
* - checks there are more than 10 clicks
*
* @throws Exception When unable to get clicks data.
*
* @return bool
*/
public function should_be_added(): bool {
if ( $this->has_been_added() ) {
return false;
}
$clicks_count = $this->get_free_listing_clicks_count();
return $clicks_count > 10;
}
/**
* Get free listing clicks count.
*
* Will return 0 if account is not connected.
*
* @return int
*
* @throws Exception When unable to get data.
*/
protected function get_free_listing_clicks_count(): int {
if ( ! $this->merchant_center->is_connected() ) {
return 0;
}
$metrics = $this->merchant_metrics->get_cached_free_listing_metrics();
return empty( $metrics ) ? 0 : $metrics['clicks'];
}
}
ReviewAfterConversions.php 0000644 00000005626 15154307502 0011744 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
use Automattic\WooCommerce\GoogleListingsAndAds\Ads\AdsAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Ads\AdsAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\MerchantMetrics;
use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\Utilities;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WP;
use Exception;
defined( 'ABSPATH' ) || exit;
/**
* Class ReviewAfterConversions
*
* Note for requesting a review after one ad conversion.
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
class ReviewAfterConversions extends AbstractNote implements AdsAwareInterface {
use AdsAwareTrait;
use LeaveReviewActionTrait;
use PluginHelper;
use Utilities;
/**
* @var MerchantMetrics
*/
protected $merchant_metrics;
/**
* @var WP
*/
protected $wp;
/**
* ReviewAfterConversions constructor.
*
* @param MerchantMetrics $merchant_metrics
* @param WP $wp
*/
public function __construct( MerchantMetrics $merchant_metrics, WP $wp ) {
$this->merchant_metrics = $merchant_metrics;
$this->wp = $wp;
}
/**
* Get the note's unique name.
*
* @return string
*/
public function get_name(): string {
return 'gla-review-after-conversions';
}
/**
* Get ads conversions count.
*
* @return int
*
* @throws Exception When unable to get data.
*/
protected function get_ads_conversions_count(): int {
$metrics = $this->merchant_metrics->get_cached_ads_metrics();
return empty( $metrics ) ? 0 : (int) $metrics['conversions'];
}
/**
* Get the note entry.
*
* @throws Exception When unable to get data.
*/
public function get_entry(): NoteEntry {
$note = new NoteEntry();
$note->set_title( __( 'You got your first conversion on Google Ads! 🎉', 'google-listings-and-ads' ), );
$note->set_content(
__( 'Congratulations! Tell us what you think about Google for WooCommerce by leaving a review. Your feedback will help us make WooCommerce even better for you.', 'google-listings-and-ads' )
);
$note->set_content_data( (object) [] );
$note->set_type( NoteEntry::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( $this->get_name() );
$note->set_source( $this->get_slug() );
$this->add_leave_review_note_action( $note );
return $note;
}
/**
* Checks if a note can and should be added.
*
* - checks there is at least one ad conversion
*
* @throws Exception When unable to get data.
*
* @return bool
*/
public function should_be_added(): bool {
if ( $this->has_been_added() ) {
return false;
}
if ( ! $this->ads_service->is_connected() ) {
return false;
}
if ( $this->get_ads_conversions_count() < 1 ) {
return false;
}
return true;
}
}
SetupCampaign.php 0000644 00000004506 15154307502 0010024 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
defined( 'ABSPATH' ) || exit;
/**
* Class SetupCampaign
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
class SetupCampaign extends AbstractSetupCampaign {
/**
* Get the note's unique name.
*
* @return string
*/
public function get_name(): string {
return 'gla-setup-campaign';
}
/**
* Get the number of days after which to add the note.
*
* @since 1.11.0
*
* @return int
*/
protected function get_gla_setup_days(): int {
return 3;
}
/**
* Set the title and content of the Note.
*
* @since 1.11.0
*
* @param NoteEntry $note
*
* @return void
*/
protected function set_title_and_content( NoteEntry $note ): void {
if ( ! $this->ads_service->is_setup_started() ) {
$note->set_title( __( 'Launch ads to drive traffic and grow sales', 'google-listings-and-ads' ) );
$note->set_content(
__(
'Your products are ready for Google Ads! Get your products shown on Google exactly when shoppers are searching for the products you offer. For new Google Ads accounts, get $500 in ad credit when you spend $500 within your first 60 days. T&Cs apply.',
'google-listings-and-ads'
)
);
$note->add_action(
'setup-campaign',
__( 'Set up Google Ads', 'google-listings-and-ads' ),
$this->get_setup_ads_url(),
NoteEntry::E_WC_ADMIN_NOTE_ACTIONED,
true
);
} else {
$note->set_title( __( 'Finish connecting your Google Ads account', 'google-listings-and-ads' ) );
$note->set_content(
__(
'Your products are ready for Google Ads! Finish connecting your account, create your campaign, pick your budget, and easily measure the impact of your ads. Plus, Google will give you $500 USD in ad credit when you spend $500 for new accounts. T&Cs apply.',
'google-listings-and-ads'
)
);
$note->add_action(
'setup-campaign',
__( 'Complete Setup', 'google-listings-and-ads' ),
$this->get_setup_ads_url(),
NoteEntry::E_WC_ADMIN_NOTE_ACTIONED,
true
);
}
$note->add_action(
'setup-campaign-learn-more',
__( 'Learn more', 'google-listings-and-ads' ),
'https://woocommerce.com/document/google-for-woocommerce/get-started/google-performance-max-campaigns'
);
}
}
SetupCampaignTwoWeeks.php 0000644 00000004052 15154307502 0011511 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
defined( 'ABSPATH' ) || exit;
/**
* Class SetupCampaignTwoWeeks
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
class SetupCampaignTwoWeeks extends AbstractSetupCampaign {
/**
* Get the note's unique name.
*
* @return string
*/
public function get_name(): string {
return 'gla-setup-campaign-two-weeks';
}
/**
* Get the number of days after which to add the note.
*
* @since 1.11.0
*
* @return int
*/
protected function get_gla_setup_days(): int {
return 14;
}
/**
* Set the title and content of the Note.
*
* @since 1.11.0
*
* @param NoteEntry $note
*
* @return void
*/
protected function set_title_and_content( NoteEntry $note ): void {
if ( ! $this->ads_service->is_setup_started() ) {
$note->set_title( __( 'Reach more shoppers with Google Ads', 'google-listings-and-ads' ) );
$note->set_content(
__(
'Your products are ready for Google Ads! Connect with the right shoppers at the right moment when they’re searching for products like yours. Connect your Google Ads account to create your first campaign.',
'google-listings-and-ads'
)
);
$note->add_action(
'setup-campaign',
__( 'Set up Google Ads', 'google-listings-and-ads' ),
$this->get_setup_ads_url(),
NoteEntry::E_WC_ADMIN_NOTE_ACTIONED,
true
);
} else {
$note->set_title(
__( 'Finish setting up your ads campaign and boost your sales', 'google-listings-and-ads' )
);
$note->set_content(
__(
"You're just a few steps away from reaching new shoppers across Google. Finish connecting your account, create your campaign, pick your budget, and easily measure the impact of your ads.",
'google-listings-and-ads'
)
);
$note->add_action(
'setup-campaign',
__( 'Complete Setup', 'google-listings-and-ads' ),
$this->get_setup_ads_url(),
NoteEntry::E_WC_ADMIN_NOTE_ACTIONED,
true
);
}
}
}
SetupCouponSharing.php 0000644 00000007767 15154307502 0011100 0 ustar 00 <?php
declare( strict_types=1 );
namespace Automattic\WooCommerce\GoogleListingsAndAds\Notes;
use Automattic\WooCommerce\Admin\Notes\Note as NoteEntry;
use Automattic\WooCommerce\GoogleListingsAndAds\Ads\AdsAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\Utilities;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantStatuses;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use Automattic\WooCommerce\GoogleListingsAndAds\Ads\AdsAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Value\ChannelVisibility;
use Automattic\WooCommerce\GoogleListingsAndAds\Coupon\CouponMetaHandler;
use Exception;
defined( 'ABSPATH' ) || exit;
/**
* Class SetupCouponSharing
*
* @package Automattic\WooCommerce\GoogleListingsAndAds\Notes
*/
class SetupCouponSharing extends AbstractNote implements MerchantCenterAwareInterface, AdsAwareInterface {
use AdsAwareTrait;
use MerchantCenterAwareTrait;
use PluginHelper;
use Utilities;
/** @var MerchantStatuses */
protected $merchant_statuses;
/**
* SetupCouponSharing constructor.
*
* @param MerchantStatuses $merchant_statuses
*/
public function __construct( MerchantStatuses $merchant_statuses ) {
$this->merchant_statuses = $merchant_statuses;
}
/**
* Get the note's unique name.
*
* @return string
*/
public function get_name(): string {
return 'gla-coupon-optin';
}
/**
* Get the note entry.
*/
public function get_entry(): NoteEntry {
$note = new NoteEntry();
$note->set_title( __( 'Show your store coupons on your Google listings', 'google-listings-and-ads' ) );
$note->set_content(
__(
'Sync your store promotions and coupons directly with Google to showcase on your product listings across the Google Shopping tab. <br/><br/>When creating a coupon, you’ll see a Channel Visibility settings box on the right; select "Show coupon on Google" to enable.',
'google-listings-and-ads'
)
);
$note->set_content_data( (object) [] );
$note->set_type( NoteEntry::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_layout( 'plain' );
$note->set_image( '' );
$note->set_name( $this->get_name() );
$note->set_source( $this->get_slug() );
$note->add_action(
'coupon-views',
__( 'Go to coupons', 'google-listings-and-ads' ),
$this->get_coupons_url()
);
return $note;
}
/**
* Checks if a note can and should be added. We insert the notes only when all the conditions are satisfied:
* 1. Store is in promotion supported country
* 2. Store has at least one active product in Merchant Center
* 3. Store has coupons created, but no coupons synced with Merchant Center
* 4. Store has Ads account connected and has been setup for >3 days OR no Ads account and >17 days
*
* @return bool
*/
public function should_be_added(): bool {
if ( $this->has_been_added() ) {
return false;
}
if ( ! $this->merchant_center->is_promotion_supported_country() ) {
return false;
}
// Check if there are synced products.
try {
$statuses = $this->merchant_statuses->get_product_statistics();
if ( isset( $statuses['statistics']['active'] ) && $statuses['statistics']['active'] <= 0 ) {
return false;
}
} catch ( Exception $e ) {
return false;
}
// Check if merchants have created coupons.
$coupons = get_posts( [ 'post_type' => 'shop_coupon' ] );
$shared_coupons = get_posts(
[
'post_type' => 'shop_coupon',
'meta_key' => CouponMetaHandler::KEY_VISIBILITY,
'meta_value' => ChannelVisibility::SYNC_AND_SHOW,
]
);
if ( empty( $coupons ) || ! empty( $shared_coupons ) ) {
return false;
}
if ( $this->ads_service->is_setup_complete() ) {
if ( ! $this->gla_setup_for( 3 * DAY_IN_SECONDS ) ) {
return false;
}
} elseif ( ! $this->gla_setup_for( 17 * DAY_IN_SECONDS ) ) {
return false;
}
return true;
}
}
DataStore.php 0000644 00000042757 15154536651 0007175 0 ustar 00 <?php
/**
* WC Admin Note Data_Store class file.
*/
namespace Automattic\WooCommerce\Admin\Notes;
defined( 'ABSPATH' ) || exit;
/**
* WC Admin Note Data Store (Custom Tables)
*/
class DataStore extends \WC_Data_Store_WP implements \WC_Object_Data_Store_Interface {
// Extensions should define their own contexts and use them to avoid applying woocommerce_note_where_clauses when not needed.
const WC_ADMIN_NOTE_OPER_GLOBAL = 'global';
/**
* Method to create a new note in the database.
*
* @param Note $note Admin note.
*/
public function create( &$note ) {
$date_created = time();
$note->set_date_created( $date_created );
global $wpdb;
$note_to_be_inserted = array(
'name' => $note->get_name(),
'type' => $note->get_type(),
'locale' => $note->get_locale(),
'title' => $note->get_title(),
'content' => $note->get_content(),
'status' => $note->get_status(),
'source' => $note->get_source(),
'is_snoozable' => (int) $note->get_is_snoozable(),
'layout' => $note->get_layout(),
'image' => $note->get_image(),
'is_deleted' => (int) $note->get_is_deleted(),
);
$note_to_be_inserted['content_data'] = wp_json_encode( $note->get_content_data() );
$note_to_be_inserted['date_created'] = gmdate( 'Y-m-d H:i:s', $date_created );
$note_to_be_inserted['date_reminder'] = null;
$wpdb->insert( $wpdb->prefix . 'wc_admin_notes', $note_to_be_inserted );
$note_id = $wpdb->insert_id;
$note->set_id( $note_id );
$this->save_actions( $note );
$note->apply_changes();
/**
* Fires when an admin note is created.
*
* @param int $note_id Note ID.
*/
do_action( 'woocommerce_note_created', $note_id );
}
/**
* Method to read a note.
*
* @param Note $note Admin note.
* @throws \Exception Throws exception when invalid data is found.
*/
public function read( &$note ) {
global $wpdb;
$note->set_defaults();
$note_row = false;
$note_id = $note->get_id();
if ( 0 !== $note_id || '0' !== $note_id ) {
$note_row = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wc_admin_notes WHERE note_id = %d LIMIT 1",
$note->get_id()
)
);
}
if ( 0 === $note->get_id() || '0' === $note->get_id() ) {
$this->read_actions( $note );
$note->set_object_read( true );
/**
* Fires when an admin note is loaded.
*
* @param int $note_id Note ID.
*/
do_action( 'woocommerce_note_loaded', $note );
} elseif ( $note_row ) {
$note->set_name( $note_row->name );
$note->set_type( $note_row->type );
$note->set_locale( $note_row->locale );
$note->set_title( $note_row->title );
$note->set_content( $note_row->content );
// The default for 'content_value' used to be an array, so there might be rows with invalid data!
$content_data = json_decode( $note_row->content_data );
if ( ! $content_data ) {
$content_data = new \stdClass();
} elseif ( is_array( $content_data ) ) {
$content_data = (object) $content_data;
}
$note->set_content_data( $content_data );
$note->set_status( $note_row->status );
$note->set_source( $note_row->source );
$note->set_date_created( $note_row->date_created );
$note->set_date_reminder( $note_row->date_reminder );
$note->set_is_snoozable( $note_row->is_snoozable );
$note->set_is_deleted( (bool) $note_row->is_deleted );
isset( $note_row->is_read ) && $note->set_is_read( (bool) $note_row->is_read );
$note->set_layout( $note_row->layout );
$note->set_image( $note_row->image );
$this->read_actions( $note );
$note->set_object_read( true );
/**
* Fires when an admin note is loaded.
*
* @param int $note_id Note ID.
*/
do_action( 'woocommerce_note_loaded', $note );
} else {
throw new \Exception( __( 'Invalid admin note', 'woocommerce' ) );
}
}
/**
* Updates a note in the database.
*
* @param Note $note Admin note.
*/
public function update( &$note ) {
global $wpdb;
if ( $note->get_id() ) {
$date_created = $note->get_date_created();
$date_created_timestamp = $date_created->getTimestamp();
$date_created_to_db = gmdate( 'Y-m-d H:i:s', $date_created_timestamp );
$date_reminder = $note->get_date_reminder();
if ( is_null( $date_reminder ) ) {
$date_reminder_to_db = null;
} else {
$date_reminder_timestamp = $date_reminder->getTimestamp();
$date_reminder_to_db = gmdate( 'Y-m-d H:i:s', $date_reminder_timestamp );
}
$wpdb->update(
$wpdb->prefix . 'wc_admin_notes',
array(
'name' => $note->get_name(),
'type' => $note->get_type(),
'locale' => $note->get_locale(),
'title' => $note->get_title(),
'content' => $note->get_content(),
'content_data' => wp_json_encode( $note->get_content_data() ),
'status' => $note->get_status(),
'source' => $note->get_source(),
'date_created' => $date_created_to_db,
'date_reminder' => $date_reminder_to_db,
'is_snoozable' => $note->get_is_snoozable(),
'layout' => $note->get_layout(),
'image' => $note->get_image(),
'is_deleted' => $note->get_is_deleted(),
'is_read' => $note->get_is_read(),
),
array( 'note_id' => $note->get_id() )
);
}
$this->save_actions( $note );
$note->apply_changes();
/**
* Fires when an admin note is updated.
*
* @param int $note_id Note ID.
*/
do_action( 'woocommerce_note_updated', $note->get_id() );
}
/**
* Deletes a note from the database.
*
* @param Note $note Admin note.
* @param array $args Array of args to pass to the delete method (not used).
*/
public function delete( &$note, $args = array() ) {
$note_id = $note->get_id();
if ( $note_id ) {
global $wpdb;
$wpdb->delete( $wpdb->prefix . 'wc_admin_notes', array( 'note_id' => $note_id ) );
$wpdb->delete( $wpdb->prefix . 'wc_admin_note_actions', array( 'note_id' => $note_id ) );
$note->set_id( null );
}
/**
* Fires when an admin note is deleted.
*
* @param int $note_id Note ID.
*/
do_action( 'woocommerce_note_deleted', $note_id );
}
/**
* Read actions from the database.
*
* @param Note $note Admin note.
*/
private function read_actions( &$note ) {
global $wpdb;
$db_actions = $wpdb->get_results(
$wpdb->prepare(
"SELECT action_id, name, label, query, status, actioned_text, nonce_action, nonce_name
FROM {$wpdb->prefix}wc_admin_note_actions
WHERE note_id = %d",
$note->get_id()
)
);
$note_actions = array();
if ( $db_actions ) {
foreach ( $db_actions as $action ) {
$note_actions[] = (object) array(
'id' => (int) $action->action_id,
'name' => $action->name,
'label' => $action->label,
'query' => $action->query,
'status' => $action->status,
'actioned_text' => $action->actioned_text,
'nonce_action' => $action->nonce_action,
'nonce_name' => $action->nonce_name,
);
}
}
$note->set_actions( $note_actions );
}
/**
* Save actions to the database.
* This function clears old actions, then re-inserts new if any changes are found.
*
* @param Note $note Note object.
*
* @return bool|void
*/
private function save_actions( &$note ) {
global $wpdb;
$changed_props = array_keys( $note->get_changes() );
if ( ! in_array( 'actions', $changed_props, true ) ) {
return false;
}
// Process action removal. Actions are removed from
// the note if they aren't part of the changeset.
// See Note::add_action().
$changed_actions = $note->get_actions( 'edit' );
$actions_to_keep = array();
foreach ( $changed_actions as $action ) {
if ( ! empty( $action->id ) ) {
$actions_to_keep[] = (int) $action->id;
}
}
$clear_actions_query = $wpdb->prepare(
"DELETE FROM {$wpdb->prefix}wc_admin_note_actions WHERE note_id = %d",
$note->get_id()
);
if ( $actions_to_keep ) {
$clear_actions_query .= sprintf( ' AND action_id NOT IN (%s)', implode( ',', $actions_to_keep ) );
}
$wpdb->query( $clear_actions_query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
// Update/insert the actions in this changeset.
foreach ( $changed_actions as $action ) {
$action_data = array(
'note_id' => $note->get_id(),
'name' => $action->name,
'label' => $action->label,
'query' => $action->query,
'status' => $action->status,
'actioned_text' => $action->actioned_text,
'nonce_action' => $action->nonce_action,
'nonce_name' => $action->nonce_name,
);
$data_format = array(
'%d',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
);
if ( ! empty( $action->id ) ) {
$action_data['action_id'] = $action->id;
$data_format[] = '%d';
}
$wpdb->replace(
$wpdb->prefix . 'wc_admin_note_actions',
$action_data,
$data_format
);
}
// Update actions from DB (to grab new IDs).
$this->read_actions( $note );
}
/**
* Return an ordered list of notes.
*
* @param array $args Query arguments.
* @param string $context Optional argument that the woocommerce_note_where_clauses filter can use to determine whether to apply extra conditions. Extensions should define their own contexts and use them to avoid adding to notes where clauses when not needed.
* @return array An array of objects containing a note id.
*/
public function get_notes( $args = array(), $context = self::WC_ADMIN_NOTE_OPER_GLOBAL ) {
global $wpdb;
$defaults = array(
'per_page' => get_option( 'posts_per_page' ),
'page' => 1,
'order' => 'DESC',
'orderby' => 'date_created',
);
$args = wp_parse_args( $args, $defaults );
$offset = $args['per_page'] * ( $args['page'] - 1 );
$where_clauses = $this->get_notes_where_clauses( $args, $context );
// sanitize order and orderby.
$order_by = '`' . str_replace( '`', '', $args['orderby'] ) . '`';
$order_dir = 'asc' === strtolower( $args['order'] ) ? 'ASC' : 'DESC';
$query = $wpdb->prepare(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"SELECT * FROM {$wpdb->prefix}wc_admin_notes WHERE 1=1{$where_clauses} ORDER BY {$order_by} {$order_dir} LIMIT %d, %d",
$offset,
$args['per_page']
);
return $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Return an ordered list of notes, without paging or applying the 'woocommerce_note_where_clauses' filter.
* INTERNAL: This method is not intended to be used by external code, and may change without notice.
*
* @param array $args Query arguments.
* @return array An array of database records.
*/
public function lookup_notes( $args = array() ) {
global $wpdb;
$defaults = array(
'order' => 'DESC',
'orderby' => 'date_created',
);
$args = wp_parse_args( $args, $defaults );
$where_clauses = $this->args_to_where_clauses( $args );
// sanitize order and orderby.
$order_by = '`' . str_replace( '`', '', $args['orderby'] ) . '`';
$order_dir = 'asc' === strtolower( $args['order'] ) ? 'ASC' : 'DESC';
$query = "SELECT * FROM {$wpdb->prefix}wc_admin_notes WHERE 1=1{$where_clauses} ORDER BY {$order_by} {$order_dir}";
return $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Return a count of notes.
*
* @param string $type Comma separated list of note types.
* @param string $status Comma separated list of statuses.
* @param string $context Optional argument that the woocommerce_note_where_clauses filter can use to determine whether to apply extra conditions. Extensions should define their own contexts and use them to avoid adding to notes where clauses when not needed.
* @return string Count of objects with given type, status and context.
*/
public function get_notes_count( $type = array(), $status = array(), $context = self::WC_ADMIN_NOTE_OPER_GLOBAL ) {
global $wpdb;
$where_clauses = $this->get_notes_where_clauses(
array(
'type' => $type,
'status' => $status,
),
$context
);
if ( ! empty( $where_clauses ) ) {
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
return $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wc_admin_notes WHERE 1=1{$where_clauses}" );
}
return $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wc_admin_notes" );
}
/**
* Parses the query arguments passed in as arrays and escapes the values.
*
* @param array $args the query arguments.
* @param string $key the key of the specific argument.
* @param array|null $allowed_types optional allowed_types if only a specific set is allowed.
* @return array the escaped array of argument values.
*/
private function get_escaped_arguments_array_by_key( $args = array(), $key = '', $allowed_types = null ) {
$arg_array = array();
if ( isset( $args[ $key ] ) ) {
foreach ( $args[ $key ] as $args_type ) {
$args_type = trim( $args_type );
$allowed = is_null( $allowed_types ) || in_array( $args_type, $allowed_types, true );
if ( $allowed ) {
$arg_array[] = sprintf( "'%s'", esc_sql( $args_type ) );
}
}
}
return $arg_array;
}
/**
* Return where clauses for getting notes by status and type. For use in both the count and listing queries.
* Applies woocommerce_note_where_clauses filter.
*
* @uses args_to_where_clauses
* @param array $args Array of args to pass.
* @param string $context Optional argument that the woocommerce_note_where_clauses filter can use to determine whether to apply extra conditions. Extensions should define their own contexts and use them to avoid adding to notes where clauses when not needed.
* @return string Where clauses for the query.
*/
public function get_notes_where_clauses( $args = array(), $context = self::WC_ADMIN_NOTE_OPER_GLOBAL ) {
$where_clauses = $this->args_to_where_clauses( $args );
/**
* Filter the notes WHERE clause before retrieving the data.
*
* Allows modification of the notes select criterial.
*
* @param string $where_clauses The generated WHERE clause.
* @param array $args The original arguments for the request.
* @param string $context Optional argument that the woocommerce_note_where_clauses filter can use to determine whether to apply extra conditions. Extensions should define their own contexts and use them to avoid adding to notes where clauses when not needed.
*/
return apply_filters( 'woocommerce_note_where_clauses', $where_clauses, $args, $context );
}
/**
* Return where clauses for notes queries without applying woocommerce_note_where_clauses filter.
* INTERNAL: This method is not intended to be used by external code, and may change without notice.
*
* @param array $args Array of arguments for query conditionals.
* @return string Where clauses.
*/
protected function args_to_where_clauses( $args = array() ) {
$allowed_types = Note::get_allowed_types();
$where_type_array = $this->get_escaped_arguments_array_by_key( $args, 'type', $allowed_types );
$allowed_statuses = Note::get_allowed_statuses();
$where_status_array = $this->get_escaped_arguments_array_by_key( $args, 'status', $allowed_statuses );
$escaped_is_deleted = '';
if ( isset( $args['is_deleted'] ) ) {
$escaped_is_deleted = esc_sql( $args['is_deleted'] );
}
$where_name_array = $this->get_escaped_arguments_array_by_key( $args, 'name' );
$where_excluded_name_array = $this->get_escaped_arguments_array_by_key( $args, 'excluded_name' );
$where_source_array = $this->get_escaped_arguments_array_by_key( $args, 'source' );
$escaped_where_types = implode( ',', $where_type_array );
$escaped_where_status = implode( ',', $where_status_array );
$escaped_where_names = implode( ',', $where_name_array );
$escaped_where_excluded_names = implode( ',', $where_excluded_name_array );
$escaped_where_source = implode( ',', $where_source_array );
$where_clauses = '';
if ( ! empty( $escaped_where_types ) ) {
$where_clauses .= " AND type IN ($escaped_where_types)";
}
if ( ! empty( $escaped_where_status ) ) {
$where_clauses .= " AND status IN ($escaped_where_status)";
}
if ( ! empty( $escaped_where_names ) ) {
$where_clauses .= " AND name IN ($escaped_where_names)";
}
if ( ! empty( $escaped_where_excluded_names ) ) {
$where_clauses .= " AND name NOT IN ($escaped_where_excluded_names)";
}
if ( ! empty( $escaped_where_source ) ) {
$where_clauses .= " AND source IN ($escaped_where_source)";
}
if ( isset( $args['is_read'] ) ) {
$where_clauses .= $args['is_read'] ? ' AND is_read = 1' : ' AND is_read = 0';
}
$where_clauses .= $escaped_is_deleted ? ' AND is_deleted = 1' : ' AND is_deleted = 0';
return $where_clauses;
}
/**
* Find all the notes with a given name.
*
* @param string $name Name to search for.
* @return array An array of matching note ids.
*/
public function get_notes_with_name( $name ) {
global $wpdb;
return $wpdb->get_col(
$wpdb->prepare(
"SELECT note_id FROM {$wpdb->prefix}wc_admin_notes WHERE name = %s ORDER BY note_id ASC",
$name
)
);
}
/**
* Find the ids of all notes with a given type.
*
* @param string $note_type Type to search for.
* @return array An array of matching note ids.
*/
public function get_note_ids_by_type( $note_type ) {
global $wpdb;
return $wpdb->get_col(
$wpdb->prepare(
"SELECT note_id FROM {$wpdb->prefix}wc_admin_notes WHERE type = %s ORDER BY note_id ASC",
$note_type
)
);
}
}
DeprecatedNotes.php 0000644 00000035462 15154536651 0010353 0 ustar 00 <?php
/**
* Define deprecated classes to support changing the naming convention of
* admin notes.
*/
namespace Automattic\WooCommerce\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
/**
* WC_Admin_Note.
*
* @deprecated since 4.8.0, use Note
*/
class WC_Admin_Note extends DeprecatedClassFacade {
// These constants must be redeclared as to not break plugins that use them.
const E_WC_ADMIN_NOTE_ERROR = Note::E_WC_ADMIN_NOTE_ERROR;
const E_WC_ADMIN_NOTE_WARNING = Note::E_WC_ADMIN_NOTE_WARNING;
const E_WC_ADMIN_NOTE_UPDATE = Note::E_WC_ADMIN_NOTE_UPDATE;
const E_WC_ADMIN_NOTE_INFORMATIONAL = Note::E_WC_ADMIN_NOTE_INFORMATIONAL;
const E_WC_ADMIN_NOTE_MARKETING = Note::E_WC_ADMIN_NOTE_MARKETING;
const E_WC_ADMIN_NOTE_SURVEY = Note::E_WC_ADMIN_NOTE_SURVEY;
const E_WC_ADMIN_NOTE_PENDING = Note::E_WC_ADMIN_NOTE_PENDING;
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.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\Notes\Note';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
/**
* Note constructor. Loads note data.
*
* @param mixed $data Note data, object, or ID.
*/
public function __construct( $data = '' ) {
$this->instance = new static::$facade_over_classname( $data );
}
}
/**
* WC_Admin_Notes.
*
* @deprecated since 4.8.0, use Notes
*/
class WC_Admin_Notes extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\Notes\Notes';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Coupon_Page_Moved.
*
* @deprecated since 4.8.0, use CouponPageMoved
*/
class WC_Admin_Notes_Coupon_Page_Moved extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\CouponPageMoved';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Customize_Store_With_Blocks.
*
* @deprecated since 4.8.0, use CustomizeStoreWithBlocks
*/
class WC_Admin_Notes_Customize_Store_With_Blocks extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\CustomizeStoreWithBlocks';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Edit_Products_On_The_Move.
*
* @deprecated since 4.8.0, use EditProductsOnTheMove
*/
class WC_Admin_Notes_Edit_Products_On_The_Move extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\EditProductsOnTheMove';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_EU_VAT_Number.
*
* @deprecated since 4.8.0, use EUVATNumber
*/
class WC_Admin_Notes_EU_VAT_Number extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\EUVATNumber';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Facebook_Marketing_Expert.
*
* @deprecated since 4.8.0, use FacebookMarketingExpert
*/
class WC_Admin_Notes_Facebook_Marketing_Expert extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\Notes\FacebookMarketingExpert';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_First_Product.
*
* @deprecated since 4.8.0, use FirstProduct
*/
class WC_Admin_Notes_First_Product extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\FirstProduct';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Giving_Feedback_Notes.
*
* @deprecated since 4.8.0, use GivingFeedbackNotes
*/
class WC_Admin_Notes_Giving_Feedback_Notes extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\GivingFeedbackNotes';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Install_JP_And_WCS_Plugins.
*
* @deprecated since 4.8.0, use InstallJPAndWCSPlugins
*/
class WC_Admin_Notes_Install_JP_And_WCS_Plugins extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\InstallJPAndWCSPlugins';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Launch_Checklist.
*
* @deprecated since 4.8.0, use LaunchChecklist
*/
class WC_Admin_Notes_Launch_Checklist extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\LaunchChecklist';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Migrate_From_Shopify.
*
* @deprecated since 4.8.0, use MigrateFromShopify
*/
class WC_Admin_Notes_Migrate_From_Shopify extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\MigrateFromShopify';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Mobile_App.
*
* @deprecated since 4.8.0, use MobileApp
*/
class WC_Admin_Notes_Mobile_App extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\MobileApp';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_New_Sales_Record.
*
* @deprecated since 4.8.0, use NewSalesRecord
*/
class WC_Admin_Notes_New_Sales_Record extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\NewSalesRecord';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Onboarding_Email_Marketing.
*
* @deprecated since 4.8.0, use OnboardingEmailMarketing
*/
class WC_Admin_Notes_Onboarding_Email_Marketing extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\Notes\OnboardingEmailMarketing';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Onboarding_Payments.
*
* @deprecated since 4.8.0, use OnboardingPayments
*/
class WC_Admin_Notes_Onboarding_Payments extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\OnboardingPayments';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Online_Clothing_Store.
*
* @deprecated since 4.8.0, use OnlineClothingStore
*/
class WC_Admin_Notes_Online_Clothing_Store extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\OnlineClothingStore';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Order_Milestones.
*
* @deprecated since 4.8.0, use OrderMilestones
*/
class WC_Admin_Notes_Order_Milestones extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\OrderMilestones';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Performance_On_Mobile.
*
* @deprecated since 4.8.0, use PerformanceOnMobile
*/
class WC_Admin_Notes_Performance_On_Mobile extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\PerformanceOnMobile';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Personalize_Store.
*
* @deprecated since 4.8.0, use PersonalizeStore
*/
class WC_Admin_Notes_Personalize_Store extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\PersonalizeStore';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Real_Time_Order_Alerts.
*
* @deprecated since 4.8.0, use RealTimeOrderAlerts
*/
class WC_Admin_Notes_Real_Time_Order_Alerts extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\RealTimeOrderAlerts';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Selling_Online_Courses.
*
* @deprecated since 4.8.0, use SellingOnlineCourses
*/
class WC_Admin_Notes_Selling_Online_Courses extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\SellingOnlineCourses';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Test_Checkout.
*
* @deprecated since 4.8.0, use TestCheckout
*/
class WC_Admin_Notes_Test_Checkout extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\TestCheckout';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Tracking_Opt_In.
*
* @deprecated since 4.8.0, use TrackingOptIn
*/
class WC_Admin_Notes_Tracking_Opt_In extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\TrackingOptIn';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_Woo_Subscriptions_Notes.
*
* @deprecated since 4.8.0, use WooSubscriptionsNotes
*/
class WC_Admin_Notes_Woo_Subscriptions_Notes extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\WooSubscriptionsNotes';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_WooCommerce_Payments.
*
* @deprecated since 4.8.0, use WooCommercePayments
*/
class WC_Admin_Notes_WooCommerce_Payments extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\WooCommercePayments';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
/**
* WC_Admin_Notes_WooCommerce_Subscriptions.
*
* @deprecated since 4.8.0, use WooCommerceSubscriptions
*/
class WC_Admin_Notes_WooCommerce_Subscriptions extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Internal\Admin\Notes\WooCommerceSubscriptions';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '4.8.0';
}
NoteTraits.php 0000644 00000016143 15154536651 0007371 0 ustar 00 <?php
/**
* WC Admin Note Traits
*
* WC Admin Note Traits class that houses shared functionality across notes.
*/
namespace Automattic\WooCommerce\Admin\Notes;
use Automattic\WooCommerce\Admin\WCAdminHelper;
defined( 'ABSPATH' ) || exit;
/**
* NoteTraits class.
*/
trait NoteTraits {
/**
* Test how long WooCommerce Admin has been active.
*
* @param int $seconds Time in seconds to check.
* @return bool Whether or not WooCommerce admin has been active for $seconds.
*/
private static function wc_admin_active_for( $seconds ) {
return WCAdminHelper::is_wc_admin_active_for( $seconds );
}
/**
* Test if WooCommerce Admin has been active within a pre-defined range.
*
* @param string $range range available in WC_ADMIN_STORE_AGE_RANGES.
* @param int $custom_start custom start in range.
* @return bool Whether or not WooCommerce admin has been active within the range.
*/
private static function is_wc_admin_active_in_date_range( $range, $custom_start = null ) {
return WCAdminHelper::is_wc_admin_active_in_date_range( $range, $custom_start );
}
/**
* Check if the note has been previously added.
*
* @throws NotesUnavailableException Throws exception when notes are unavailable.
*/
public static function note_exists() {
$data_store = Notes::load_data_store();
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
return ! empty( $note_ids );
}
/**
* Checks if a note can and should be added.
*
* @return bool
* @throws NotesUnavailableException Throws exception when notes are unavailable.
*/
public static function can_be_added() {
$note = self::get_note();
if ( ! $note instanceof Note && ! $note instanceof WC_Admin_Note ) {
return;
}
if ( self::note_exists() ) {
return false;
}
if (
'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) &&
Note::E_WC_ADMIN_NOTE_MARKETING === $note->get_type()
) {
return false;
}
return true;
}
/**
* Add the note if it passes predefined conditions.
*
* @throws NotesUnavailableException Throws exception when notes are unavailable.
*/
public static function possibly_add_note() {
$note = self::get_note();
if ( ! self::can_be_added() ) {
return;
}
$note->save();
}
/**
* Alias this method for backwards compatibility.
*
* @throws NotesUnavailableException Throws exception when notes are unavailable.
*/
public static function add_note() {
self::possibly_add_note();
}
/**
* Should this note exist? (Default implementation is generous. Override as needed.)
*/
public static function is_applicable() {
return true;
}
/**
* Delete this note if it is not applicable, unless has been soft-deleted or actioned already.
*/
public static function delete_if_not_applicable() {
if ( ! self::is_applicable() ) {
$data_store = Notes::load_data_store();
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
if ( ! empty( $note_ids ) ) {
$note = Notes::get_note( $note_ids[0] );
if ( ! $note->get_is_deleted() && ( Note::E_WC_ADMIN_NOTE_ACTIONED !== $note->get_status() ) ) {
return self::possibly_delete_note();
}
}
}
}
/**
* Possibly delete the note, if it exists in the database. Note that this
* is a hard delete, for where it doesn't make sense to soft delete or
* action the note.
*
* @throws NotesUnavailableException Throws exception when notes are unavailable.
*/
public static function possibly_delete_note() {
$data_store = Notes::load_data_store();
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
foreach ( $note_ids as $note_id ) {
$note = Notes::get_note( $note_id );
if ( $note ) {
$data_store->delete( $note );
}
}
}
/**
* Update the note if it passes predefined conditions.
*
* @throws NotesUnavailableException Throws exception when notes are unavailable.
*/
public static function possibly_update_note() {
$note_in_db = Notes::get_note_by_name( self::NOTE_NAME );
if ( ! $note_in_db ) {
return;
}
if ( ! method_exists( self::class, 'get_note' ) ) {
return;
}
$note = self::get_note();
if ( ! $note instanceof Note && ! $note instanceof WC_Admin_Note ) {
return;
}
$need_save = in_array(
true,
array(
self::update_note_field_if_changed( $note_in_db, $note, 'title' ),
self::update_note_field_if_changed( $note_in_db, $note, 'content' ),
self::update_note_field_if_changed( $note_in_db, $note, 'content_data' ),
self::update_note_field_if_changed( $note_in_db, $note, 'type' ),
self::update_note_field_if_changed( $note_in_db, $note, 'locale' ),
self::update_note_field_if_changed( $note_in_db, $note, 'source' ),
self::update_note_field_if_changed( $note_in_db, $note, 'actions' )
),
true
);
if ( $need_save ) {
$note_in_db->save();
}
}
/**
* Get if the note has been actioned.
*
* @return bool
* @throws NotesUnavailableException Throws exception when notes are unavailable.
*/
public static function has_note_been_actioned() {
$data_store = Notes::load_data_store();
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
if ( ! empty( $note_ids ) ) {
$note = Notes::get_note( $note_ids[0] );
if ( Note::E_WC_ADMIN_NOTE_ACTIONED === $note->get_status() ) {
return true;
}
}
return false;
}
/**
* Update a note field of note1 if it's different from note2 with getter and setter.
*
* @param Note $note1 Note to update.
* @param Note $note2 Note to compare against.
* @param string $field_name Field to update.
* @return bool True if the field was updated.
*/
private static function update_note_field_if_changed( $note1, $note2, $field_name ) {
// We need to serialize the stdObject to compare it.
$note1_field_value = self::possibly_convert_object_to_array(
call_user_func( array( $note1, 'get_' . $field_name ) )
);
$note2_field_value = self::possibly_convert_object_to_array(
call_user_func( array( $note2, 'get_' . $field_name ) )
);
if ( 'actions' === $field_name ) {
// We need to individually compare the action fields because action object from db is different from action object of note.
// For example, action object from db has "id".
$diff = array_udiff(
$note1_field_value,
$note2_field_value,
function( $action1, $action2 ) {
if ( $action1->name === $action2->name &&
$action1->label === $action2->label &&
$action1->query === $action2->query ) {
return 0;
}
return -1;
}
);
$need_update = count( $diff ) > 0;
} else {
$need_update = $note1_field_value !== $note2_field_value;
}
if ( $need_update ) {
call_user_func(
array( $note1, 'set_' . $field_name ),
// Get note2 field again because it may have been changed during the comparison.
call_user_func( array( $note2, 'get_' . $field_name ) )
);
return true;
}
return false;
}
/**
* Convert a value to array if it's a stdClass.
*
* @param mixed $obj variable to convert.
* @return mixed
*/
private static function possibly_convert_object_to_array( $obj ) {
if ( $obj instanceof \stdClass ) {
return (array) $obj;
}
return $obj;
}
}
Notes.php 0000644 00000033451 15154536651 0006366 0 ustar 00 <?php
/**
* Handles storage and retrieval of admin notes
*/
namespace Automattic\WooCommerce\Admin\Notes;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Admin Notes class.
*/
class Notes {
/**
* Hook used for recurring "unsnooze" action.
*/
const UNSNOOZE_HOOK = 'wc_admin_unsnooze_admin_notes';
/**
* Hook appropriate actions.
*/
public static function init() {
add_action( 'admin_init', array( __CLASS__, 'schedule_unsnooze_notes' ) );
add_action( 'admin_init', array( __CLASS__, 'possibly_delete_survey_notes' ) );
add_action( 'update_option_woocommerce_show_marketplace_suggestions', array( __CLASS__, 'possibly_delete_marketing_notes' ), 10, 2 );
}
/**
* Get notes from the database.
*
* @param string $context Getting notes for what context. Valid values: view, edit.
* @param array $args Arguments to pass to the query( e.g. per_page and page).
* @return array Array of arrays.
*/
public static function get_notes( $context = 'edit', $args = array() ) {
$data_store = self::load_data_store();
$raw_notes = $data_store->get_notes( $args );
$notes = array();
foreach ( (array) $raw_notes as $raw_note ) {
try {
$note = new Note( $raw_note );
/**
* Filter the note from db. This is used to modify the note before it is returned.
*
* @since 6.9.0
* @param Note $note The note object from the database.
*/
$note = apply_filters( 'woocommerce_get_note_from_db', $note );
$note_id = $note->get_id();
$notes[ $note_id ] = $note->get_data();
$notes[ $note_id ]['name'] = $note->get_name( $context );
$notes[ $note_id ]['type'] = $note->get_type( $context );
$notes[ $note_id ]['locale'] = $note->get_locale( $context );
$notes[ $note_id ]['title'] = $note->get_title( $context );
$notes[ $note_id ]['content'] = $note->get_content( $context );
$notes[ $note_id ]['content_data'] = $note->get_content_data( $context );
$notes[ $note_id ]['status'] = $note->get_status( $context );
$notes[ $note_id ]['source'] = $note->get_source( $context );
$notes[ $note_id ]['date_created'] = $note->get_date_created( $context );
$notes[ $note_id ]['date_reminder'] = $note->get_date_reminder( $context );
$notes[ $note_id ]['actions'] = $note->get_actions( $context );
$notes[ $note_id ]['layout'] = $note->get_layout( $context );
$notes[ $note_id ]['image'] = $note->get_image( $context );
$notes[ $note_id ]['is_deleted'] = $note->get_is_deleted( $context );
} catch ( \Exception $e ) {
wc_caught_exception( $e, __CLASS__ . '::' . __FUNCTION__, array( $note_id ) );
}
}
return $notes;
}
/**
* Get admin note using it's ID
*
* @param int $note_id Note ID.
* @return Note|bool
*/
public static function get_note( $note_id ) {
if ( false !== $note_id ) {
try {
return new Note( $note_id );
} catch ( \Exception $e ) {
wc_caught_exception( $e, __CLASS__ . '::' . __FUNCTION__, array( $note_id ) );
return false;
}
}
return false;
}
/**
* Get admin note using its name.
*
* This is a shortcut for the common pattern of looking up note ids by name and then passing the first id to get_note().
* It will behave unpredictably when more than one note with the given name exists.
*
* @param string $note_name Note name.
* @return Note|bool
**/
public static function get_note_by_name( $note_name ) {
$data_store = self::load_data_store();
$note_ids = $data_store->get_notes_with_name( $note_name );
if ( empty( $note_ids ) ) {
return false;
}
return self::get_note( $note_ids[0] );
}
/**
* Get the total number of notes
*
* @param string $type Comma separated list of note types.
* @param string $status Comma separated list of statuses.
* @return int
*/
public static function get_notes_count( $type = array(), $status = array() ) {
$data_store = self::load_data_store();
return $data_store->get_notes_count( $type, $status );
}
/**
* Deletes admin notes with a given name.
*
* @param string|array $names Name(s) to search for.
*/
public static function delete_notes_with_name( $names ) {
if ( is_string( $names ) ) {
$names = array( $names );
} elseif ( ! is_array( $names ) ) {
return;
}
$data_store = self::load_data_store();
foreach ( $names as $name ) {
$note_ids = $data_store->get_notes_with_name( $name );
foreach ( (array) $note_ids as $note_id ) {
$note = self::get_note( $note_id );
if ( $note ) {
$note->delete();
}
}
}
}
/**
* Update a note.
*
* @param Note $note The note that will be updated.
* @param array $requested_updates a list of requested updates.
*/
public static function update_note( $note, $requested_updates ) {
$note_changed = false;
if ( isset( $requested_updates['status'] ) ) {
$note->set_status( $requested_updates['status'] );
$note_changed = true;
}
if ( isset( $requested_updates['date_reminder'] ) ) {
$note->set_date_reminder( $requested_updates['date_reminder'] );
$note_changed = true;
}
if ( isset( $requested_updates['is_deleted'] ) ) {
$note->set_is_deleted( $requested_updates['is_deleted'] );
$note_changed = true;
}
if ( isset( $requested_updates['is_read'] ) ) {
$note->set_is_read( $requested_updates['is_read'] );
$note_changed = true;
}
if ( $note_changed ) {
$note->save();
}
}
/**
* Soft delete of a note.
*
* @param Note $note The note that will be deleted.
*/
public static function delete_note( $note ) {
$note->set_is_deleted( 1 );
$note->save();
}
/**
* Soft delete of all the admin notes. Returns the deleted items.
*
* @param array $args Arguments to pass to the query (ex: status).
* @return array Array of notes.
*/
public static function delete_all_notes( $args = array() ) {
$data_store = self::load_data_store();
$defaults = array(
'order' => 'desc',
'orderby' => 'date_created',
'per_page' => 25,
'page' => 1,
'type' => array(
Note::E_WC_ADMIN_NOTE_INFORMATIONAL,
Note::E_WC_ADMIN_NOTE_MARKETING,
Note::E_WC_ADMIN_NOTE_WARNING,
Note::E_WC_ADMIN_NOTE_SURVEY,
),
'is_deleted' => 0,
);
$args = wp_parse_args( $args, $defaults );
// Here we filter for the same params we are using to show the note list in client side.
$raw_notes = $data_store->get_notes( $args );
$notes = array();
foreach ( (array) $raw_notes as $raw_note ) {
$note = self::get_note( $raw_note->note_id );
if ( $note ) {
self::delete_note( $note );
array_push( $notes, $note );
}
}
return $notes;
}
/**
* Clear note snooze status if the reminder date has been reached.
*/
public static function unsnooze_notes() {
$data_store = self::load_data_store();
$raw_notes = $data_store->get_notes(
array(
'status' => array( Note::E_WC_ADMIN_NOTE_SNOOZED ),
)
);
$now = new \DateTime();
foreach ( $raw_notes as $raw_note ) {
$note = self::get_note( $raw_note->note_id );
if ( false === $note ) {
continue;
}
$date_reminder = $note->get_date_reminder( 'edit' );
if ( $date_reminder < $now ) {
$note->set_status( Note::E_WC_ADMIN_NOTE_UNACTIONED );
$note->set_date_reminder( null );
$note->save();
}
}
}
/**
* Schedule unsnooze notes event.
*/
public static function schedule_unsnooze_notes() {
if ( ! wp_next_scheduled( self::UNSNOOZE_HOOK ) ) {
wp_schedule_event( time() + 5, 'hourly', self::UNSNOOZE_HOOK );
}
}
/**
* Unschedule unsnooze notes event.
*/
public static function clear_queued_actions() {
wp_clear_scheduled_hook( self::UNSNOOZE_HOOK );
}
/**
* Delete marketing notes if marketing has been opted out.
*
* @param string $old_value Old value.
* @param string $value New value.
*/
public static function possibly_delete_marketing_notes( $old_value, $value ) {
if ( 'no' !== $value ) {
return;
}
$data_store = self::load_data_store();
$note_ids = $data_store->get_note_ids_by_type( Note::E_WC_ADMIN_NOTE_MARKETING );
foreach ( $note_ids as $note_id ) {
$note = self::get_note( $note_id );
if ( $note ) {
$note->delete();
}
}
}
/**
* Delete actioned survey notes.
*/
public static function possibly_delete_survey_notes() {
$data_store = self::load_data_store();
$note_ids = $data_store->get_note_ids_by_type( Note::E_WC_ADMIN_NOTE_SURVEY );
foreach ( $note_ids as $note_id ) {
$note = self::get_note( $note_id );
if ( $note && ( $note->get_status() === Note::E_WC_ADMIN_NOTE_ACTIONED ) ) {
$note->set_is_deleted( 1 );
$note->save();
}
}
}
/**
* Get the status of a given note by name.
*
* @param string $note_name Name of the note.
* @return string|bool The note status.
*/
public static function get_note_status( $note_name ) {
$note = self::get_note_by_name( $note_name );
if ( ! $note ) {
return false;
}
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();
$event_params = 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(),
);
if ( in_array( $note->get_type(), array( 'error', 'update' ), true ) ) {
wc_admin_record_tracks_event( 'store_alert_action', $event_params );
} else {
self::record_tracks_event_without_cookies( 'inbox_action_click', $event_params );
}
return $note;
}
/**
* Record tracks event for a specific user.
*
* @param int $user_id The user id we want to record for the event.
* @param string $event_name Name of the event to record.
* @param array $params The params to send to the event recording.
*/
public static function record_tracks_event_with_user( $user_id, $event_name, $params ) {
// We save the current user id to set it back after the event recording.
$current_user_id = get_current_user_id();
wp_set_current_user( $user_id );
self::record_tracks_event_without_cookies( $event_name, $params );
wp_set_current_user( $current_user_id );
}
/**
* Record tracks event without using cookies.
*
* @param string $event_name Name of the event to record.
* @param array $params The params to send to the event recording.
*/
private static function record_tracks_event_without_cookies( $event_name, $params ) {
// We save the cookie to set it back after the event recording.
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$anon_id = isset( $_COOKIE['tk_ai'] ) ? $_COOKIE['tk_ai'] : null;
unset( $_COOKIE['tk_ai'] );
wc_admin_record_tracks_event( $event_name, $params );
if ( isset( $anon_id ) ) {
setcookie( 'tk_ai', $anon_id );
}
}
/**
* 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;
}
/**
* Loads the data store.
*
* If the "admin-note" data store is unavailable, attempts to load it
* will result in an exception.
* This method catches that exception and throws a custom one instead.
*
* @return \WC_Data_Store The "admin-note" data store.
* @throws NotesUnavailableException Throws exception if data store loading fails.
*/
public static function load_data_store() {
try {
return \WC_Data_Store::load( 'admin-note' );
} catch ( \Exception $e ) {
throw new NotesUnavailableException(
'woocommerce_admin_notes_unavailable',
__( 'Notes are unavailable because the "admin-note" data store cannot be loaded.', 'woocommerce' )
);
}
}
}
NotesUnavailableException.php 0000644 00000000541 15154536651 0012403 0 ustar 00 <?php
/**
* WooCommerce Admin Notes Unavailable Exception Class
*
* Exception class thrown when an attempt to use notes is made but notes are unavailable.
*/
namespace Automattic\WooCommerce\Admin\Notes;
defined( 'ABSPATH' ) || exit;
/**
* Notes\NotesUnavailableException class.
*/
class NotesUnavailableException extends \WC_Data_Exception {}
AddFirstProduct.php 0000644 00000005445 15154770571 0010342 0 ustar 00 <?php
/**
* WooCommerce Admin: Add First Product.
*
* Adds a note (type `email`) to bring the client back to the store setup flow.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* 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() {
if ( ! self::wc_admin_active_for( 2 * DAY_IN_SECONDS ) || self::wc_admin_active_for( 5 * 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;
}
// Don't show if there is an orders.
$args = array(
'limit' => 1,
'return' => 'ids',
);
$orders = wc_get_orders( $args );
if ( 0 !== count( $orders ) ) {
return;
}
// If you're updating the following please use sprintf to separate HTML tags.
// https://github.com/woocommerce/woocommerce-admin/pull/6617#discussion_r596889685.
$content_lines = array(
'{greetings}<br/><br/>',
/* translators: %s: line break */
sprintf( __( 'Nice one; you\'ve created a WooCommerce store! Now it\'s time to add your first product and get ready to start selling.%s', 'woocommerce' ), '<br/><br/>' ),
__( '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' ),
/* translators: %1$s is an open anchor tag (<a>) and %2$s is a close link tag (</a>). */
sprintf( __( '%1$1sExplore our docs%2$2s for more information, or just get started!', 'woocommerce' ), '<a href="https://woocommerce.com/document/managing-products/?utm_source=help_panel&utm_medium=product">', '</a>' ),
);
$additional_data = array(
'role' => 'administrator',
);
$note = new Note();
$note->set_title( __( 'Add your first product', 'woocommerce' ) );
$note->set_content( implode( '', $content_lines ) );
$note->set_content_data( (object) $additional_data );
$note->set_image(
plugins_url(
'/images/admin_notes/dashboard-widget-setup.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_url( 'admin.php?page=wc-admin&task=products' ) );
return $note;
}
}
ChoosingTheme.php 0000644 00000002706 15154770571 0010032 0 ustar 00 <?php
/**
* WooCommerce Admin (Dashboard) choosing a theme note
*
* Adds notes to the merchant's inbox about choosing a theme.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Giving_Feedback_Notes
*/
class ChoosingTheme {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-choosing-a-theme';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// We need to show choosing a theme notification after 1 day of install.
if ( ! self::is_wc_admin_active_in_date_range( 'week-1', DAY_IN_SECONDS ) ) {
return;
}
// Otherwise, create our new note.
$note = new Note();
$note->set_title( __( 'Choosing a theme?', 'woocommerce' ) );
$note->set_content( __( 'Check out the themes that are compatible with WooCommerce and choose one aligned with your brand and business needs.', 'woocommerce' ) );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_MARKETING );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'visit-the-theme-marketplace',
__( 'Visit the theme marketplace', 'woocommerce' ),
'https://woocommerce.com/product-category/themes/?utm_source=inbox&utm_medium=product'
);
return $note;
}
}
CouponPageMoved.php 0000644 00000007463 15154770571 0010336 0 ustar 00 <?php
/**
* WooCommerce Admin Coupon Page Moved provider.
*
* Adds a notice when the store manager access the coupons page via the old WooCommerce > Coupons menu.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
use Automattic\WooCommerce\Internal\Admin\CouponsMovedTrait;
use stdClass;
use WC_Data_Store;
/**
* Coupon_Page_Moved class.
*/
class CouponPageMoved {
use NoteTraits, CouponsMovedTrait;
const NOTE_NAME = 'wc-admin-coupon-page-moved';
/**
* Initialize our hooks.
*/
public function init() {
if ( ! wc_coupons_enabled() ) {
return;
}
add_action( 'admin_init', [ $this, 'possibly_add_note' ] );
add_action( 'admin_init', [ $this, 'redirect_to_coupons' ] );
add_action( 'woocommerce_newly_installed', [ $this, 'disable_legacy_menu_for_new_install' ] );
}
/**
* Checks if a note can and should be added.
*
* @return bool
*/
public static function can_be_added() {
if ( ! wc_coupons_enabled() ) {
return false;
}
// Don't add the notice if the legacy coupon menu is already disabled.
if ( ! self::should_display_legacy_menu() ) {
return false;
}
// Don't add the notice if it's been hidden by the user before.
if ( self::has_dismissed_note() ) {
return false;
}
// If we already have a notice, don't add a new one.
if ( self::has_unactioned_note() ) {
return false;
}
return isset( $_GET[ self::$query_key ] ) && (bool) $_GET[ self::$query_key ]; // phpcs:ignore WordPress.Security.NonceVerification
}
/**
* Get the note object for this class.
*
* @return Note
*/
public static function get_note() {
$note = new Note();
$note->set_title( __( 'Coupon management has moved!', 'woocommerce' ) );
$note->set_content( __( 'Coupons can now be managed from Marketing > Coupons. Click the button below to remove the legacy WooCommerce > Coupons menu item.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_UPDATE );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( new stdClass() );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'remove-legacy-coupon-menu',
__( 'Remove legacy coupon menu', 'woocommerce' ),
wc_admin_url( '&action=remove-coupon-menu' ),
Note::E_WC_ADMIN_NOTE_ACTIONED
);
return $note;
}
/**
* Find notes that have not been actioned.
*
* @return bool
*/
protected static function has_unactioned_note() {
$note = Notes::get_note_by_name( self::NOTE_NAME );
if ( ! $note ) {
return false;
}
return $note->get_status() === 'unactioned';
}
/**
* Whether any notes have been dismissed by the user previously.
*
* @return bool
*/
protected static function has_dismissed_note() {
$note = Notes::get_note_by_name( self::NOTE_NAME );
if ( ! $note ) {
return false;
}
return ! $note->get_is_deleted();
}
/**
* Get the data store object.
*
* @return DataStore The data store object.
*/
protected static function get_data_store() {
return WC_Data_Store::load( 'admin-note' );
}
/**
* Safe redirect to the coupon page to force page refresh.
*/
public function redirect_to_coupons() {
/* phpcs:disable WordPress.Security.NonceVerification */
if (
! isset( $_GET['page'] ) ||
'wc-admin' !== $_GET['page'] ||
! isset( $_GET['action'] ) ||
'remove-coupon-menu' !== $_GET['action'] ||
! defined( 'WC_ADMIN_PLUGIN_FILE' )
) {
return;
}
/* phpcs:enable */
$this->display_legacy_menu( false );
wp_safe_redirect( self::get_management_url( 'coupons' ) );
exit;
}
/**
* Disable legacy coupon menu when installing for the first time.
*/
public function disable_legacy_menu_for_new_install() {
$this->display_legacy_menu( false );
}
}
CustomizeStoreWithBlocks.php 0000644 00000004543 15154770571 0012270 0 ustar 00 <?php
/**
* WooCommerce Admin: Customize your online store with WooCommerce blocks.
*
* Adds a note to customize the client online store with WooCommerce blocks.
*
* @package WooCommerce\Admin
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Customize_Store_With_Blocks.
*/
class CustomizeStoreWithBlocks {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-customize-store-with-blocks';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
$onboarding_profile = get_option( 'woocommerce_onboarding_profile', array() );
// Confirm that $onboarding_profile is set.
if ( empty( $onboarding_profile ) ) {
return;
}
// Make sure that the person who filled out the OBW was not setting up
// the store for their customer/client.
if (
! isset( $onboarding_profile['setup_client'] ) ||
$onboarding_profile['setup_client']
) {
return;
}
// We want to show the note after fourteen days.
if ( ! self::is_wc_admin_active_in_date_range( 'week-1-4', 14 * DAY_IN_SECONDS ) ) {
return;
}
// Don't show if there aren't products.
$query = new \WC_Product_Query(
array(
'limit' => 1,
'return' => 'ids',
'status' => array( 'publish' ),
)
);
$products = $query->get_products();
if ( 0 === count( $products ) ) {
return;
}
$note = new Note();
$note->set_title( __( 'Customize your online store with WooCommerce blocks', 'woocommerce' ) );
$note->set_content( __( 'With our blocks, you can select and display products, categories, filters, and more virtually anywhere on your site — no need to use shortcodes or edit lines of code. Learn more about how to use each one of them.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'customize-store-with-blocks',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/posts/how-to-customize-your-online-store-with-woocommerce-blocks/?utm_source=inbox&utm_medium=product',
Note::E_WC_ADMIN_NOTE_ACTIONED
);
return $note;
}
}
CustomizingProductCatalog.php 0000644 00000004215 15154770571 0012442 0 ustar 00 <?php
/**
* WooCommerce Admin: How to customize your product catalog note provider
*
* Adds a note with a link to the customizer a day after adding the first product
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Class CustomizingProductCatalog
*
* @package Automattic\WooCommerce\Admin\Notes
*/
class CustomizingProductCatalog {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-customizing-product-catalog';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
$query = new \WC_Product_Query(
array(
'limit' => 1,
'paginate' => true,
'status' => array( 'publish' ),
'orderby' => 'post_date',
'order' => 'DESC',
)
);
$products = $query->get_products();
// we need at least 1 product.
if ( 0 === $products->total ) {
return;
}
$product = $products->products[0];
$created_timestamp = $product->get_date_created()->getTimestamp();
$is_a_day_old = ( time() - $created_timestamp ) >= DAY_IN_SECONDS;
// the product must be at least 1 day old.
if ( ! $is_a_day_old ) {
return;
}
// store must not been active more than 14 days.
if ( self::wc_admin_active_for( DAY_IN_SECONDS * 14 ) ) {
return;
}
$note = new Note();
$note->set_title( __( 'How to customize your product catalog', 'woocommerce' ) );
$note->set_content( __( 'You want your product catalog and images to look great and align with your brand. This guide will give you all the tips you need to get your products looking great in your store.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'day-after-first-product',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/document/woocommerce-customizer/?utm_source=inbox&utm_medium=product'
);
return $note;
}
}
EUVATNumber.php 0000644 00000003212 15154770571 0007324 0 ustar 00 <?php
/**
* WooCommerce Admin: EU VAT Number Note.
*
* Adds a note for EU store to install the EU VAT Number extension.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* EU_VAT_Number
*/
class EUVATNumber {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-eu-vat-number';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
if ( 'yes' !== get_option( 'wc_connect_taxes_enabled', 'no' ) ) {
return;
}
$country_code = WC()->countries->get_base_country();
$eu_countries = WC()->countries->get_european_union_countries();
if ( ! in_array( $country_code, $eu_countries, true ) ) {
return;
}
$content = __( "If your store is based in the EU, we recommend using the EU VAT Number extension in addition to automated taxes. It provides your checkout with a field to collect and validate a customer's EU VAT number, if they have one.", 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Collect and validate EU VAT numbers at checkout', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_MARKETING );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'learn-more',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/products/eu-vat-number/?utm_medium=product',
Note::E_WC_ADMIN_NOTE_ACTIONED
);
return $note;
}
}
EditProductsOnTheMove.php 0000644 00000003264 15154770571 0011474 0 ustar 00 <?php
/**
* WooCommerce Admin Edit products on the move note.
*
* Adds a note to download the mobile app.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Edit_Products_On_The_Move
*/
class EditProductsOnTheMove {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-edit-products-on-the-move';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// Only add this note if this store is at least a year old.
$year_in_seconds = 365 * DAY_IN_SECONDS;
if ( ! self::wc_admin_active_for( $year_in_seconds ) ) {
return;
}
// Check that the previous mobile app notes have not been actioned.
if ( MobileApp::has_note_been_actioned() ) {
return;
}
if ( RealTimeOrderAlerts::has_note_been_actioned() ) {
return;
}
if ( ManageOrdersOnTheGo::has_note_been_actioned() ) {
return;
}
if ( PerformanceOnMobile::has_note_been_actioned() ) {
return;
}
$note = new Note();
$note->set_title( __( 'Edit products on the move', 'woocommerce' ) );
$note->set_content( __( 'Edit and create new products from your mobile devices with the Woo app', 'woocommerce' ) );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'learn-more',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/mobile/?utm_source=inbox&utm_medium=product'
);
return $note;
}
}
EmailNotification.php 0000644 00000012250 15154770571 0010667 0 ustar 00 <?php
/**
* Handles emailing user notes.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Automattic\WooCommerce\Admin\Notes\Notes;
/**
* Include dependencies.
*/
if ( ! class_exists( 'WC_Email', false ) ) {
include_once WC_ABSPATH . 'includes/emails/class-wc-email.php';
}
/**
* EmailNotification Class.
*/
class EmailNotification 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/react-admin/emails/';
$this->placeholders = array(
'{greetings}' => __( 'Hi there,', 'woocommerce' ),
);
// 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 headers.
*
* @return string
*/
public function get_headers() {
$header = 'Content-Type: ' . $this->get_content_type() . "\r\n";
return apply_filters( 'woocommerce_email_headers', $header, $this->id, $this->object, $this );
}
/**
* 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->format_string( $this->get_note_content() ),
'email_heading' => $this->format_string( $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->format_string( $this->get_heading() ),
'email_content' => $this->format_string( $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 $user_email Email to send the note.
* @param int $user_id User id to to track the note.
* @param string $user_name User's name.
*/
public function trigger( $user_email, $user_id, $user_name ) {
$this->recipient = $user_email;
$this->opened_tracking_url = sprintf(
'%1$s/wp-json/wc-analytics/admin/notes/tracker/%2$d/user/%3$d',
site_url(),
$this->note->get_id(),
$user_id
);
$this->trigger_note_action_url = sprintf(
'%1$s&external_redirect=1¬e=%2$d&user=%3$d&action=',
wc_admin_url(),
$this->note->get_id(),
$user_id
);
if ( $user_name ) {
/* translators: %s = merchant name */
$this->placeholders['{greetings}'] = sprintf( __( 'Hi %s,', 'woocommerce' ), $user_name );
}
$this->send(
$this->get_recipient(),
$this->get_subject(),
$this->get_content(),
$this->get_headers(),
$this->get_attachments()
);
Notes::record_tracks_event_with_user( $user_id, 'email_note_sent', array( 'note_name' => $this->note->get_name() ) );
}
}
FirstProduct.php 0000644 00000004175 15154770571 0007730 0 ustar 00 <?php
/**
* WooCommerce Admin: Do you need help with adding your first product?
*
* Adds a note to ask the client if they need help adding their first product.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* First_Product.
*/
class FirstProduct {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-first-product';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// We want to show the note after seven days.
if ( ! self::is_wc_admin_active_in_date_range( 'week-1-4' ) ) {
return;
}
$onboarding_profile = get_option( 'woocommerce_onboarding_profile', array() );
// Confirm that $onboarding_profile is set.
if ( empty( $onboarding_profile ) ) {
return;
}
// Make sure that the person who filled out the OBW was not setting up
// the store for their customer/client.
if (
! isset( $onboarding_profile['setup_client'] ) ||
$onboarding_profile['setup_client']
) {
return;
}
// Don't show if there are products.
$query = new \WC_Product_Query(
array(
'limit' => 1,
'paginate' => true,
'return' => 'ids',
'status' => array( 'publish' ),
)
);
$products = $query->get_products();
$count = $products->total;
if ( 0 !== $count ) {
return;
}
$note = new Note();
$note->set_title( __( 'Do you need help with adding your first product?', 'woocommerce' ) );
$note->set_content( __( 'This video tutorial will help you go through the process of adding your first product in WooCommerce.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'first-product-watch-tutorial',
__( 'Watch tutorial', 'woocommerce' ),
'https://www.youtube.com/watch?v=sFtXa00Jf_o&list=PLHdG8zvZd0E575Ia8Mu3w1h750YLXNfsC&index=24'
);
return $note;
}
}
GivingFeedbackNotes.php 0000644 00000003002 15154770571 0011125 0 ustar 00 <?php
/**
* WooCommerce Admin (Dashboard) Giving feedback notes provider
*
* Adds notes to the merchant's inbox about giving feedback.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
use Automattic\WooCommerce\Internal\Admin\Survey;
/**
* Giving_Feedback_Notes
*/
class GivingFeedbackNotes {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-store-notice-giving-feedback-2';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
if ( ! self::is_wc_admin_active_in_date_range( 'week-1-4' ) ) {
return;
}
// Otherwise, create our new note.
$note = new Note();
$note->set_title( __( 'You\'re invited to share your experience', 'woocommerce' ) );
$note->set_content( __( 'Now that you’ve chosen us as a partner, our goal is to make sure we\'re providing the right tools to meet your needs. We\'re looking forward to having your feedback on the store setup experience so we can improve it in the future.', 'woocommerce' ) );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'share-feedback',
__( 'Share feedback', 'woocommerce' ),
Survey::get_url( '/store-setup-survey' )
);
return $note;
}
}
InstallJPAndWCSPlugins.php 0000644 00000011122 15154770571 0011470 0 ustar 00 <?php
/**
* WooCommerce Admin Add Install Jetpack and WooCommerce Shipping & Tax Plugin Note Provider.
*
* Adds a note to the merchant's inbox prompting them to install the Jetpack
* and WooCommerce Shipping & Tax plugins after it fails to install during
* WooCommerce setup.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
use Automattic\WooCommerce\Admin\PluginsHelper;
/**
* Install_JP_And_WCS_Plugins
*/
class InstallJPAndWCSPlugins {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-install-jp-and-wcs-plugins';
/**
* Constructor.
*/
public function __construct() {
add_action( 'woocommerce_note_action_install-jp-and-wcs-plugins', array( $this, 'install_jp_and_wcs_plugins' ) );
add_action( 'activated_plugin', array( $this, 'action_note' ) );
add_action( 'woocommerce_plugins_install_api_error', array( $this, 'on_install_error' ) );
add_action( 'woocommerce_plugins_install_error', array( $this, 'on_install_error' ) );
add_action( 'woocommerce_plugins_activate_error', array( $this, 'on_install_error' ) );
}
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
$content = __( 'We noticed that there was a problem during the Jetpack and WooCommerce Shipping & Tax install. Please try again and enjoy all the advantages of having the plugins connected to your store! Sorry for the inconvenience. The "Jetpack" and "WooCommerce Shipping & Tax" plugins will be installed & activated for free.', 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Uh oh... There was a problem during the Jetpack and WooCommerce Shipping & Tax install. Please try again.', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'install-jp-and-wcs-plugins',
__( 'Install plugins', 'woocommerce' ),
false,
Note::E_WC_ADMIN_NOTE_ACTIONED
);
return $note;
}
/**
* Action the Install Jetpack and WooCommerce Shipping & Tax note, if any exists,
* and as long as both the Jetpack and WooCommerce Shipping & Tax plugins have been
* activated.
*/
public static function action_note() {
// Make sure that both plugins are active before actioning the note.
$active_plugin_slugs = PluginsHelper::get_active_plugin_slugs();
$jp_active = in_array( 'jetpack', $active_plugin_slugs, true );
$wcs_active = in_array( 'woocommerce-services', $active_plugin_slugs, true );
if ( ! $jp_active || ! $wcs_active ) {
return;
}
// Action any notes with a matching name.
$data_store = Notes::load_data_store();
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
foreach ( $note_ids as $note_id ) {
$note = Notes::get_note( $note_id );
if ( $note ) {
$note->set_status( Note::E_WC_ADMIN_NOTE_ACTIONED );
$note->save();
}
}
}
/**
* Install the Jetpack and WooCommerce Shipping & Tax plugins in response to the action
* being clicked in the admin note.
*
* @param Note $note The note being actioned.
*/
public function install_jp_and_wcs_plugins( $note ) {
if ( self::NOTE_NAME !== $note->get_name() ) {
return;
}
$this->install_and_activate_plugin( 'jetpack' );
$this->install_and_activate_plugin( 'woocommerce-services' );
}
/**
* Installs and activates the specified plugin.
*
* @param string $plugin The plugin slug.
*/
private function install_and_activate_plugin( $plugin ) {
$install_request = array( 'plugin' => $plugin );
$installer = new \Automattic\WooCommerce\Admin\API\OnboardingPlugins();
$result = $installer->install_plugin( $install_request );
// @todo Use the error statuses to decide whether or not to action the note.
if ( is_wp_error( $result ) ) {
return;
}
$activate_request = array( 'plugins' => $plugin );
$installer->activate_plugins( $activate_request );
}
/**
* Create an alert notification in response to an error installing a plugin.
*
* @param string $slug The slug of the plugin being installed.
*/
public function on_install_error( $slug ) {
// Exit early if we're not installing the Jetpack or the WooCommerce Shipping & Tax plugins.
if ( 'jetpack' !== $slug && 'woocommerce-services' !== $slug ) {
return;
}
self::possibly_add_note();
}
}
LaunchChecklist.php 0000644 00000003270 15154770571 0010337 0 ustar 00 <?php
/**
* WooCommerce Admin Launch Checklist Note.
*
* Adds a note to cover pre-launch checklist items for store owners.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Launch_Checklist
*/
class LaunchChecklist {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-launch-checklist';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// Only add this note if completing the task list or completed 3 tasks in 10 days.
$completed_tasks = get_option( 'woocommerce_task_list_tracked_completed_tasks', array() );
$ten_days_in_seconds = 10 * DAY_IN_SECONDS;
if (
! get_option( 'woocommerce_task_list_complete' ) &&
(
count( $completed_tasks ) < 3 ||
self::is_wc_admin_active_in_date_range( 'week-1-4', $ten_days_in_seconds )
)
) {
return;
}
$content = __( 'To make sure you never get that sinking "what did I forget" feeling, we\'ve put together the essential pre-launch checklist.', 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Ready to launch your store?', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'learn-more', __( 'Learn more', 'woocommerce' ), 'https://woocommerce.com/posts/pre-launch-checklist-the-essentials/?utm_source=inbox&utm_medium=product' );
return $note;
}
}
MagentoMigration.php 0000644 00000004714 15154770571 0010543 0 ustar 00 <?php
/**
* WooCommerce Admin note on how to migrate from Magento.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Features\Onboarding;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* MagentoMigration
*/
class MagentoMigration {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-magento-migration';
/**
* Attach hooks.
*/
public function __construct() {
add_action( 'update_option_' . OnboardingProfile::DATA_OPTION, array( __CLASS__, 'possibly_add_note' ) );
add_action( 'woocommerce_admin_magento_migration_note', array( __CLASS__, 'save_note' ) );
}
/**
* Add the note if it passes predefined conditions.
*/
public static function possibly_add_note() {
$onboarding_profile = get_option( OnboardingProfile::DATA_OPTION, array() );
if ( empty( $onboarding_profile ) ) {
return;
}
if (
! isset( $onboarding_profile['other_platform'] ) ||
'magento' !== $onboarding_profile['other_platform']
) {
return;
}
if (
! isset( $onboarding_profile['setup_client'] ) ||
$onboarding_profile['setup_client']
) {
return;
}
WC()->queue()->schedule_single( time() + ( 5 * MINUTE_IN_SECONDS ), 'woocommerce_admin_magento_migration_note' );
}
/**
* Save the note to the database.
*/
public static function save_note() {
$note = self::get_note();
if ( self::note_exists() ) {
return;
}
$note->save();
}
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
$note = new Note();
$note->set_title( __( 'How to Migrate from Magento to WooCommerce', 'woocommerce' ) );
$note->set_content( __( 'Changing platforms might seem like a big hurdle to overcome, but it is easier than you might think to move your products, customers, and orders to WooCommerce. This article will help you with going through this process.', 'woocommerce' ) );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'learn-more',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/posts/how-migrate-from-magento-to-woocommerce/?utm_source=inbox'
);
return $note;
}
}
ManageOrdersOnTheGo.php 0000644 00000003051 15154770571 0011063 0 ustar 00 <?php
/**
* WooCommerce Admin Manage orders on the go note.
*
* Adds a note to download the mobile app to manage orders on the go.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Manage_Orders_On_The_Go
*/
class ManageOrdersOnTheGo {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-manage-orders-on-the-go';
/**
* Get the note.
*
* @return Note|null
*/
public static function get_note() {
// Only add this note if this store is at least 6 months old.
if ( ! self::is_wc_admin_active_in_date_range( 'month-6+' ) ) {
return;
}
// Check that the previous mobile app notes have not been actioned.
if ( MobileApp::has_note_been_actioned() ) {
return;
}
if ( RealTimeOrderAlerts::has_note_been_actioned() ) {
return;
}
$note = new Note();
$note->set_title( __( 'Manage your orders on the go', 'woocommerce' ) );
$note->set_content( __( 'Look for orders, customer info, and process refunds in one click with the Woo app.', 'woocommerce' ) );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'learn-more',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/mobile/?utm_source=inbox&utm_medium=product'
);
return $note;
}
}
MarketingJetpack.php 0000644 00000007265 15154770571 0010526 0 ustar 00 <?php
/**
* WooCommerce Admin Jetpack Marketing Note Provider.
*
* Adds notes to the merchant's inbox concerning Jetpack Backup.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
use Automattic\WooCommerce\Admin\PluginsHelper;
/**
* Suggest Jetpack Backup to Woo users.
*
* Note: This should probably live in the Jetpack plugin in the future.
*
* @see https://developer.woocommerce.com/2020/10/16/using-the-admin-notes-inbox-in-woocommerce/
*/
class MarketingJetpack {
// Shared Note Traits.
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-marketing-jetpack-backup';
/**
* Product IDs that include Backup.
*/
const BACKUP_IDS = [
2010,
2011,
2012,
2013,
2014,
2015,
2100,
2101,
2102,
2103,
2005,
2006,
2000,
2003,
2001,
2004,
];
/**
* Maybe add a note on Jetpack Backups for Jetpack sites older than a week without Backups.
*/
public static function possibly_add_note() {
/**
* Check if Jetpack is installed.
*/
$installed_plugins = PluginsHelper::get_installed_plugin_slugs();
if ( ! in_array( 'jetpack', $installed_plugins, true ) ) {
return;
}
$data_store = \WC_Data_Store::load( 'admin-note' );
// Do we already have this note?
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
if ( ! empty( $note_ids ) ) {
$note_id = array_pop( $note_ids );
$note = Notes::get_note( $note_id );
if ( false === $note ) {
return;
}
// If Jetpack Backups was purchased after the note was created, mark this note as actioned.
if ( self::has_backups() && Note::E_WC_ADMIN_NOTE_ACTIONED !== $note->get_status() ) {
$note->set_status( Note::E_WC_ADMIN_NOTE_ACTIONED );
$note->save();
}
return;
}
// Check requirements.
if ( ! self::is_wc_admin_active_in_date_range( 'week-1-4', DAY_IN_SECONDS * 3 ) || ! self::can_be_added() || self::has_backups() ) {
return;
}
// Add note.
$note = self::get_note();
$note->save();
}
/**
* Get the note.
*/
public static function get_note() {
$note = new Note();
$note->set_title( __( 'Protect your WooCommerce Store with Jetpack Backup.', 'woocommerce' ) );
$note->set_content( __( 'Store downtime means lost sales. One-click restores get you back online quickly if something goes wrong.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_MARKETING );
$note->set_name( self::NOTE_NAME );
$note->set_layout( 'thumbnail' );
$note->set_image(
WC_ADMIN_IMAGES_FOLDER_URL . '/admin_notes/marketing-jetpack-2x.png'
);
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-admin-notes' );
$note->add_action(
'jetpack-backup-woocommerce',
__( 'Get backups', 'woocommerce' ),
esc_url( 'https://jetpack.com/upgrade/backup-woocommerce/?utm_source=inbox&utm_medium=automattic_referred&utm_campaign=jp_backup_to_woo' ),
Note::E_WC_ADMIN_NOTE_ACTIONED
);
return $note;
}
/**
* Check if this blog already has a Jetpack Backups product.
*
* @return boolean Whether or not this blog has backups.
*/
protected static function has_backups() {
$product_ids = [];
$plan = get_option( 'jetpack_active_plan' );
if ( ! empty( $plan ) ) {
$product_ids[] = $plan['product_id'];
}
$products = get_option( 'jetpack_site_products' );
if ( ! empty( $products ) ) {
foreach ( $products as $product ) {
$product_ids[] = $product['product_id'];
}
}
return (bool) array_intersect( self::BACKUP_IDS, $product_ids );
}
}
MerchantEmailNotifications.php 0000644 00000006601 15154770571 0012537 0 ustar 00 <?php
/**
* Handles merchant email notifications
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
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['user'] ) ||
! isset( $_GET['note'] ) ||
! isset( $_GET['action'] )
) {
return;
}
$note_id = intval( $_GET['note'] );
$action_id = intval( $_GET['action'] );
$user_id = intval( $_GET['user'] );
/* phpcs:enable */
$note = Notes::get_note( $note_id );
if ( ! $note || Note::E_WC_ADMIN_NOTE_EMAIL !== $note->get_type() ) {
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 = Notes::load_data_store();
$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();
}
}
}
/**
* Send the notification to the merchant.
*
* @param object $note The note to send.
*/
public static function send_merchant_notification( $note ) {
\WC_Emails::instance();
$users = self::get_notification_recipients( $note );
$email = new EmailNotification( $note );
foreach ( $users as $user ) {
if ( is_email( $user->user_email ) ) {
$name = self::get_merchant_preferred_name( $user );
$email->trigger( $user->user_email, $user->ID, $name );
}
}
}
/**
* Get the preferred name for user. First choice is
* the user's first name, and then display_name.
*
* @param WP_User $user Recipient to send the note to.
* @return string User's name.
*/
public static function get_merchant_preferred_name( $user ) {
$first_name = get_user_meta( $user->ID, 'first_name', true );
if ( $first_name ) {
return $first_name;
}
if ( $user->display_name ) {
return $user->display_name;
}
return '';
}
/**
* Get users by role to notify.
*
* @param object $note The note to send.
* @return array Users to notify
*/
public static function get_notification_recipients( $note ) {
$content_data = $note->get_content_data();
$role = 'administrator';
if ( isset( $content_data->role ) ) {
$role = $content_data->role;
}
$args = array( 'role' => $role );
return get_users( $args );
}
}
MigrateFromShopify.php 0000644 00000004330 15154770571 0011047 0 ustar 00 <?php
/**
* WooCommerce Admin: Migrate from Shopify to WooCommerce.
*
* Adds a note to ask the client if they want to migrate from Shopify to WooCommerce.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Migrate_From_Shopify.
*/
class MigrateFromShopify {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-migrate-from-shopify';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// We want to show the note after two days.
$two_days = 2 * DAY_IN_SECONDS;
if ( ! self::is_wc_admin_active_in_date_range( 'week-1', $two_days ) ) {
return;
}
$onboarding_profile = get_option( 'woocommerce_onboarding_profile', array() );
if (
! isset( $onboarding_profile['setup_client'] ) ||
! isset( $onboarding_profile['selling_venues'] ) ||
! isset( $onboarding_profile['other_platform'] )
) {
return;
}
// Make sure the client is not setup.
if ( $onboarding_profile['setup_client'] ) {
return;
}
// We will show the notification when the client already is selling and is using Shopify.
if (
'other' !== $onboarding_profile['selling_venues'] ||
'shopify' !== $onboarding_profile['other_platform']
) {
return;
}
$note = new Note();
$note->set_title( __( 'Do you want to migrate from Shopify to WooCommerce?', 'woocommerce' ) );
$note->set_content( __( 'Changing eCommerce platforms might seem like a big hurdle to overcome, but it is easier than you might think to move your products, customers, and orders to WooCommerce. This article will help you with going through this process.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'migrate-from-shopify',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/posts/migrate-from-shopify-to-woocommerce/?utm_source=inbox&utm_medium=product',
Note::E_WC_ADMIN_NOTE_ACTIONED
);
return $note;
}
}
MobileApp.php 0000644 00000002615 15154770571 0007145 0 ustar 00 <?php
/**
* WooCommerce Admin Mobile App Note Provider.
*
* Adds a note to the merchant's inbox showing the benefits of the mobile app.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Mobile_App
*/
class MobileApp {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-mobile-app';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// We want to show the mobile app note after day 2.
$two_days_in_seconds = 2 * DAY_IN_SECONDS;
if ( ! self::is_wc_admin_active_in_date_range( 'week-1', $two_days_in_seconds ) ) {
return;
}
$content = __( 'Install the WooCommerce mobile app to manage orders, receive sales notifications, and view key metrics — wherever you are.', 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Install Woo mobile app', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'learn-more', __( 'Learn more', 'woocommerce' ), 'https://woocommerce.com/mobile/?utm_medium=product' );
return $note;
}
}
NewSalesRecord.php 0000644 00000012404 15154770571 0010152 0 ustar 00 <?php
/**
* WooCommerce Admin (Dashboard) New Sales Record Note Provider.
*
* Adds a note to the merchant's inbox when the previous day's sales are a new record.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* New_Sales_Record
*/
class NewSalesRecord {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-new-sales-record';
/**
* Option name for the sales record date in ISO 8601 (YYYY-MM-DD) date.
*/
const RECORD_DATE_OPTION_KEY = 'woocommerce_sales_record_date';
/**
* Option name for the sales record amount.
*/
const RECORD_AMOUNT_OPTION_KEY = 'woocommerce_sales_record_amount';
/**
* Returns the total of yesterday's sales.
*
* @param string $date Date for sales to sum (i.e. YYYY-MM-DD).
* @return floatval
*/
public static function sum_sales_for_date( $date ) {
$order_query = new \WC_Order_Query( array( 'date_created' => $date ) );
$orders = $order_query->get_orders();
$total = 0;
foreach ( (array) $orders as $order ) {
$total += $order->get_total();
}
return $total;
}
/**
* Possibly add a sales record note.
*/
public static function possibly_add_note() {
/**
* Filter to allow for disabling sales record milestones.
*
* @since 3.7.0
*
* @param boolean default true
*/
$sales_record_notes_enabled = apply_filters( 'woocommerce_admin_sales_record_milestone_enabled', true );
if ( ! $sales_record_notes_enabled ) {
return;
}
$yesterday = gmdate( 'Y-m-d', current_time( 'timestamp', 0 ) - DAY_IN_SECONDS );
$total = self::sum_sales_for_date( $yesterday );
// No sales yesterday? Bail.
if ( 0 >= $total ) {
return;
}
$record_date = get_option( self::RECORD_DATE_OPTION_KEY, '' );
$record_amt = floatval( get_option( self::RECORD_AMOUNT_OPTION_KEY, 0 ) );
// No previous entry? Just enter what we have and return without generating a note.
if ( empty( $record_date ) ) {
update_option( self::RECORD_DATE_OPTION_KEY, $yesterday );
update_option( self::RECORD_AMOUNT_OPTION_KEY, $total );
return;
}
// Otherwise, if yesterdays total bested the record, update AND generate a note.
if ( $total > $record_amt ) {
update_option( self::RECORD_DATE_OPTION_KEY, $yesterday );
update_option( self::RECORD_AMOUNT_OPTION_KEY, $total );
// We only want one sales record note at any time in the inbox, so we delete any other first.
Notes::delete_notes_with_name( self::NOTE_NAME );
$note = self::get_note_with_record_data( $record_date, $record_amt, $yesterday, $total );
$note->save();
}
}
/**
* Get the note with record data.
*
* @param string $record_date record date Y-m-d.
* @param float $record_amt record amount.
* @param string $yesterday yesterday's date Y-m-d.
* @param string $total total sales for yesterday.
*
* @return Note
*/
public static function get_note_with_record_data( $record_date, $record_amt, $yesterday, $total ) {
// Use F jS (March 7th) format for English speaking countries.
if ( substr( get_user_locale(), 0, 2 ) === 'en' ) {
$date_format = 'F jS';
} else {
// otherwise, fallback to the system date format.
$date_format = get_option( 'date_format' );
}
$formatted_yesterday = date_i18n( $date_format, strtotime( $yesterday ) );
$formatted_total = html_entity_decode( wp_strip_all_tags( wc_price( $total ) ) );
$formatted_record_date = date_i18n( $date_format, strtotime( $record_date ) );
$formatted_record_amt = html_entity_decode( wp_strip_all_tags( wc_price( $record_amt ) ) );
$content = sprintf(
/* translators: 1 and 4: Date (e.g. October 16th), 2 and 3: Amount (e.g. $160.00) */
__( 'Woohoo, %1$s was your record day for sales! Net sales was %2$s beating the previous record of %3$s set on %4$s.', 'woocommerce' ),
$formatted_yesterday,
$formatted_total,
$formatted_record_amt,
$formatted_record_date
);
$content_data = (object) array(
'old_record_date' => $record_date,
'old_record_amt' => $record_amt,
'new_record_date' => $yesterday,
'new_record_amt' => $total,
);
$report_url = '?page=wc-admin&path=/analytics/revenue&period=custom&compare=previous_year&after=' . $yesterday . '&before=' . $yesterday;
// And now, create our new note.
$note = new Note();
$note->set_title( __( 'New sales record!', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( $content_data );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'view-report', __( 'View report', 'woocommerce' ), $report_url );
return $note;
}
/**
* Get the note. This is used for localizing the note.
*
* @return Note
*/
public static function get_note() {
$note = Notes::get_note_by_name( self::NOTE_NAME );
if ( ! $note ) {
return false;
}
$content_data = $note->get_content_data();
return self::get_note_with_record_data(
$content_data->old_record_date,
$content_data->old_record_amt,
$content_data->new_record_date,
$content_data->new_record_amt
);
}
}
OnboardingPayments.php 0000644 00000003364 15154770571 0011102 0 ustar 00 <?php
/**
* WooCommerce Admin: Payments reminder note.
*
* Adds a notes to complete the payment methods.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Onboarding_Payments.
*/
class OnboardingPayments {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-onboarding-payments-reminder';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// We want to show the note after five days.
if ( ! self::is_wc_admin_active_in_date_range( 'week-1-4', 5 * DAY_IN_SECONDS ) ) {
return;
}
// Check to see if any gateways have been added.
$gateways = WC()->payment_gateways->get_available_payment_gateways();
$enabled_gateways = array_filter(
$gateways,
function( $gateway ) {
return 'yes' === $gateway->enabled;
}
);
if ( ! empty( $enabled_gateways ) ) {
return;
}
$note = new Note();
$note->set_title( __( 'Start accepting payments on your store!', 'woocommerce' ) );
$note->set_content( __( 'Take payments with the provider that’s right for you - choose from 100+ payment gateways for WooCommerce.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'view-payment-gateways',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/product-category/woocommerce-extensions/payment-gateways/?utm_medium=product',
Note::E_WC_ADMIN_NOTE_ACTIONED,
true
);
return $note;
}
}
OnlineClothingStore.php 0000644 00000005271 15154770571 0011227 0 ustar 00 <?php
/**
* WooCommerce Admin: Start your online clothing store.
*
* Adds a note to ask the client if they are considering starting an online
* clothing store.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Online_Clothing_Store.
*/
class OnlineClothingStore {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-online-clothing-store';
/**
* Returns whether the industries includes fashion-apparel-accessories.
*
* @param array $industries The industries to search.
*
* @return bool Whether the industries includes fashion-apparel-accessories.
*/
private static function is_in_fashion_industry( $industries ) {
foreach ( $industries as $industry ) {
if ( 'fashion-apparel-accessories' === $industry['slug'] ) {
return true;
}
}
return false;
}
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// We want to show the note after two days.
if ( ! self::is_wc_admin_active_in_date_range( 'week-1', 2 * DAY_IN_SECONDS ) ) {
return;
}
$onboarding_profile = get_option( 'woocommerce_onboarding_profile', array() );
// Confirm that $onboarding_profile is set.
if ( empty( $onboarding_profile ) ) {
return;
}
// Make sure that the person who filled out the OBW was not setting up
// the store for their customer/client.
if (
! isset( $onboarding_profile['setup_client'] ) ||
$onboarding_profile['setup_client']
) {
return;
}
// We need to show the notification when the industry is
// fashion/apparel/accessories.
if ( ! isset( $onboarding_profile['industry'] ) ) {
return;
}
if ( ! self::is_in_fashion_industry( $onboarding_profile['industry'] ) ) {
return;
}
$note = new Note();
$note->set_title( __( 'Start your online clothing store', 'woocommerce' ) );
$note->set_content( __( 'Starting a fashion website is exciting but it may seem overwhelming as well. In this article, we\'ll walk you through the setup process, teach you to create successful product listings, and show you how to market to your ideal audience.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'online-clothing-store',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/posts/starting-an-online-clothing-store/?utm_source=inbox&utm_medium=product',
Note::E_WC_ADMIN_NOTE_ACTIONED
);
return $note;
}
}
OrderMilestones.php 0000644 00000022226 15154770571 0010413 0 ustar 00 <?php
/**
* WooCommerce Admin (Dashboard) Order Milestones Note Provider.
*
* Adds a note to the merchant's inbox when certain order milestones are reached.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
/**
* Order_Milestones
*/
class OrderMilestones {
/**
* Name of the "other milestones" note.
*/
const NOTE_NAME = 'wc-admin-orders-milestone';
/**
* Option key name to store last order milestone.
*/
const LAST_ORDER_MILESTONE_OPTION_KEY = 'woocommerce_admin_last_orders_milestone';
/**
* Hook to process order milestones.
*/
const PROCESS_ORDERS_MILESTONE_HOOK = 'wc_admin_process_orders_milestone';
/**
* Allowed order statuses for calculating milestones.
*
* @var array
*/
protected $allowed_statuses = array(
'pending',
'processing',
'completed',
);
/**
* Orders count cache.
*
* @var int
*/
protected $orders_count = null;
/**
* Further order milestone thresholds.
*
* @var array
*/
protected $milestones = array(
1,
10,
100,
250,
500,
1000,
5000,
10000,
500000,
1000000,
);
/**
* Delay hook attachment until after the WC post types have been registered.
*
* This is required for retrieving the order count.
*/
public function __construct() {
/**
* Filter Order statuses that will count towards milestones.
*
* @since 3.5.0
*
* @param array $allowed_statuses Order statuses that will count towards milestones.
*/
$this->allowed_statuses = apply_filters( 'woocommerce_admin_order_milestone_statuses', $this->allowed_statuses );
add_action( 'woocommerce_after_register_post_type', array( $this, 'init' ) );
register_deactivation_hook( WC_PLUGIN_FILE, array( $this, 'clear_scheduled_event' ) );
}
/**
* Hook everything up.
*/
public function init() {
if ( ! wp_next_scheduled( self::PROCESS_ORDERS_MILESTONE_HOOK ) ) {
wp_schedule_event( time(), 'hourly', self::PROCESS_ORDERS_MILESTONE_HOOK );
}
add_action( 'wc_admin_installed', array( $this, 'backfill_last_milestone' ) );
add_action( self::PROCESS_ORDERS_MILESTONE_HOOK, array( $this, 'possibly_add_note' ) );
}
/**
* Clear out our hourly milestone hook upon plugin deactivation.
*/
public function clear_scheduled_event() {
wp_clear_scheduled_hook( self::PROCESS_ORDERS_MILESTONE_HOOK );
}
/**
* Get the total count of orders (in the allowed statuses).
*
* @param bool $no_cache Optional. Skip cache.
* @return int Total orders count.
*/
public function get_orders_count( $no_cache = false ) {
if ( $no_cache || is_null( $this->orders_count ) ) {
$status_counts = array_map( 'wc_orders_count', $this->allowed_statuses );
$this->orders_count = array_sum( $status_counts );
}
return $this->orders_count;
}
/**
* Backfill the store's current milestone.
*
* Used to avoid celebrating milestones that were reached before plugin activation.
*/
public function backfill_last_milestone() {
// If the milestone notes have been disabled via filter, bail.
if ( ! $this->are_milestones_enabled() ) {
return;
}
$this->set_last_milestone( $this->get_current_milestone() );
}
/**
* Get the store's last milestone.
*
* @return int Last milestone reached.
*/
public function get_last_milestone() {
return get_option( self::LAST_ORDER_MILESTONE_OPTION_KEY, 0 );
}
/**
* Update the last reached milestone.
*
* @param int $milestone Last milestone reached.
*/
public function set_last_milestone( $milestone ) {
update_option( self::LAST_ORDER_MILESTONE_OPTION_KEY, $milestone );
}
/**
* Calculate the current orders milestone.
*
* Based on the threshold values in $this->milestones.
*
* @return int Current orders milestone.
*/
public function get_current_milestone() {
$milestone_reached = 0;
$orders_count = $this->get_orders_count();
foreach ( $this->milestones as $milestone ) {
if ( $milestone <= $orders_count ) {
$milestone_reached = $milestone;
}
}
return $milestone_reached;
}
/**
* Get the appropriate note title for a given milestone.
*
* @param int $milestone Order milestone.
* @return string Note title for the milestone.
*/
public static function get_note_title_for_milestone( $milestone ) {
switch ( $milestone ) {
case 1:
return __( 'First order received', 'woocommerce' );
case 10:
case 100:
case 250:
case 500:
case 1000:
case 5000:
case 10000:
case 500000:
case 1000000:
return sprintf(
/* translators: Number of orders processed. */
__( 'Congratulations on processing %s orders!', 'woocommerce' ),
wc_format_decimal( $milestone )
);
default:
return '';
}
}
/**
* Get the appropriate note content for a given milestone.
*
* @param int $milestone Order milestone.
* @return string Note content for the milestone.
*/
public static function get_note_content_for_milestone( $milestone ) {
switch ( $milestone ) {
case 1:
return __( 'Congratulations on getting your first order! Now is a great time to learn how to manage your orders.', 'woocommerce' );
case 10:
return __( "You've hit the 10 orders milestone! Look at you go. Browse some WooCommerce success stories for inspiration.", 'woocommerce' );
case 100:
case 250:
case 500:
case 1000:
case 5000:
case 10000:
case 500000:
case 1000000:
return __( 'Another order milestone! Take a look at your Orders Report to review your orders to date.', 'woocommerce' );
default:
return '';
}
}
/**
* Get the appropriate note action for a given milestone.
*
* @param int $milestone Order milestone.
* @return array Note actoion (name, label, query) for the milestone.
*/
public static function get_note_action_for_milestone( $milestone ) {
switch ( $milestone ) {
case 1:
return array(
'name' => 'learn-more',
'label' => __( 'Learn more', 'woocommerce' ),
'query' => 'https://woocommerce.com/document/managing-orders/?utm_source=inbox&utm_medium=product',
);
case 10:
return array(
'name' => 'browse',
'label' => __( 'Browse', 'woocommerce' ),
'query' => 'https://woocommerce.com/success-stories/?utm_source=inbox&utm_medium=product',
);
case 100:
case 250:
case 500:
case 1000:
case 5000:
case 10000:
case 500000:
case 1000000:
return array(
'name' => 'review-orders',
'label' => __( 'Review your orders', 'woocommerce' ),
'query' => '?page=wc-admin&path=/analytics/orders',
);
default:
return array(
'name' => '',
'label' => '',
'query' => '',
);
}
}
/**
* Convenience method to see if the milestone notes are enabled.
*
* @return boolean True if milestone notifications are enabled.
*/
public function are_milestones_enabled() {
/**
* Filter to allow for disabling order milestones.
*
* @since 3.7.0
*
* @param boolean default true
*/
$milestone_notes_enabled = apply_filters( 'woocommerce_admin_order_milestones_enabled', true );
return $milestone_notes_enabled;
}
/**
* Get the note. This is used for localizing the note.
*
* @return Note
*/
public static function get_note() {
$note = Notes::get_note_by_name( self::NOTE_NAME );
if ( ! $note ) {
return false;
}
$content_data = $note->get_content_data();
if ( ! isset( $content_data->current_milestone ) ) {
return false;
}
return self::get_note_by_milestone(
$content_data->current_milestone
);
}
/**
* Get the note by milestones.
*
* @param int $current_milestone Current milestone.
*
* @return Note
*/
public static function get_note_by_milestone( $current_milestone ) {
$content_data = (object) array(
'current_milestone' => $current_milestone,
);
$note = new Note();
$note->set_title( self::get_note_title_for_milestone( $current_milestone ) );
$note->set_content( self::get_note_content_for_milestone( $current_milestone ) );
$note->set_content_data( $content_data );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note_action = self::get_note_action_for_milestone( $current_milestone );
$note->add_action( $note_action['name'], $note_action['label'], $note_action['query'] );
return $note;
}
/**
* Checks if a note can and should be added.
*
* @return bool
*/
public function can_be_added() {
// If the milestone notes have been disabled via filter, bail.
if ( ! $this->are_milestones_enabled() ) {
return false;
}
$last_milestone = $this->get_last_milestone();
$current_milestone = $this->get_current_milestone();
if ( $current_milestone <= $last_milestone ) {
return false;
}
return true;
}
/**
* Add milestone notes for other significant thresholds.
*/
public function possibly_add_note() {
if ( ! self::can_be_added() ) {
return;
}
$current_milestone = $this->get_current_milestone();
$this->set_last_milestone( $current_milestone );
// We only want one milestone note at any time.
Notes::delete_notes_with_name( self::NOTE_NAME );
$note = $this->get_note_by_milestone( $current_milestone );
$note->save();
}
}
PaymentsMoreInfoNeeded.php 0000644 00000004010 15154770571 0011630 0 ustar 00 <?php
/**
* WooCommerce Admin Payments More Info Needed Inbox Note Provider
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
use Automattic\WooCommerce\Internal\Admin\WcPayWelcomePage;
defined( 'ABSPATH' ) || exit;
/**
* PaymentsMoreInfoNeeded
*/
class PaymentsMoreInfoNeeded {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-payments-more-info-needed';
/**
* Should this note exist?
*/
public static function is_applicable() {
return self::should_display_note();
}
/**
* Returns true if we should display the note.
*
* @return bool
*/
public static function should_display_note() {
// WCPay welcome page must not be visible.
if ( WcPayWelcomePage::instance()->must_be_visible() ) {
return false;
}
// More than 30 days since viewing the welcome page.
$exit_survey_timestamp = get_option( 'wcpay_welcome_page_exit_survey_more_info_needed_timestamp', false );
if ( ! $exit_survey_timestamp ||
( time() - $exit_survey_timestamp < 30 * DAY_IN_SECONDS )
) {
return false;
}
return true;
}
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
if ( ! self::should_display_note() ) {
return;
}
$content = __( 'We recently asked you if you wanted more information about WooPayments. Run your business and manage your payments in one place with the solution built and supported by WooCommerce.', 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Payments made simple with WooPayments', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'learn-more', __( 'Learn more here', 'woocommerce' ), 'https://woocommerce.com/payments/' );
return $note;
}
}
PaymentsRemindMeLater.php 0000644 00000003676 15154770571 0011516 0 ustar 00 <?php
/**
* WooCommerce Admin Payment Reminder Me later
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
use Automattic\WooCommerce\Internal\Admin\WcPayWelcomePage;
defined( 'ABSPATH' ) || exit;
/**
* PaymentsRemindMeLater
*/
class PaymentsRemindMeLater {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-payments-remind-me-later';
/**
* Should this note exist?
*/
public static function is_applicable() {
return self::should_display_note();
}
/**
* Returns true if we should display the note.
*
* @return bool
*/
public static function should_display_note() {
// WCPay welcome page must be visible.
if ( ! WcPayWelcomePage::instance()->must_be_visible() ) {
return false;
}
// Less than 3 days since viewing welcome page.
$view_timestamp = get_option( 'wcpay_welcome_page_viewed_timestamp', false );
if ( ! $view_timestamp ||
( time() - $view_timestamp < 3 * DAY_IN_SECONDS )
) {
return false;
}
return true;
}
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
if ( ! self::should_display_note() ) {
return;
}
$content = __( 'Save up to $800 in fees by managing transactions with WooPayments. With WooPayments, you can securely accept major cards, Apple Pay, and payments in over 100 currencies.', 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Save big with WooPayments', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'learn-more', __( 'Learn more', 'woocommerce' ), admin_url( 'admin.php?page=wc-admin&path=/wc-pay-welcome-page' ) );
return $note;
}
}
PerformanceOnMobile.php 0000644 00000003215 15154770571 0011160 0 ustar 00 <?php
/**
* WooCommerce Admin Performance on mobile note.
*
* Adds a note to download the mobile app, performance on mobile.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Performance_On_Mobile
*/
class PerformanceOnMobile {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-performance-on-mobile';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// Only add this note if this store is at least 9 months old.
$nine_months_in_seconds = MONTH_IN_SECONDS * 9;
if ( ! self::wc_admin_active_for( $nine_months_in_seconds ) ) {
return;
}
// Check that the previous mobile app notes have not been actioned.
if ( MobileApp::has_note_been_actioned() ) {
return;
}
if ( RealTimeOrderAlerts::has_note_been_actioned() ) {
return;
}
if ( ManageOrdersOnTheGo::has_note_been_actioned() ) {
return;
}
$note = new Note();
$note->set_title( __( 'Track your store performance on mobile', 'woocommerce' ) );
$note->set_content( __( 'Monitor your sales and high performing products with the Woo app.', 'woocommerce' ) );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'learn-more',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/mobile/?utm_source=inbox&utm_medium=product'
);
return $note;
}
}
PersonalizeStore.php 0000644 00000003646 15154770571 0010612 0 ustar 00 <?php
/**
* WooCommerce Admin Personalize Your Store Note Provider.
*
* Adds a note to the merchant's inbox prompting them to personalize their store.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Personalize_Store
*/
class PersonalizeStore {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-personalize-store';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// Only show the note to stores with homepage.
$homepage_id = get_option( 'woocommerce_onboarding_homepage_post_id', false );
if ( ! $homepage_id ) {
return;
}
// Show the note after task list is done.
$is_task_list_complete = get_option( 'woocommerce_task_list_complete', false );
// We want to show the note after day 5.
$five_days_in_seconds = 5 * DAY_IN_SECONDS;
if ( ! self::is_wc_admin_active_in_date_range( 'week-1-4', $five_days_in_seconds ) && ! $is_task_list_complete ) {
return;
}
$content = __( 'The homepage is one of the most important entry points in your store. When done right it can lead to higher conversions and engagement. Don\'t forget to personalize the homepage that we created for your store during the onboarding.', 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Personalize your store\'s homepage', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'personalize-homepage', __( 'Personalize homepage', 'woocommerce' ), admin_url( 'post.php?post=' . $homepage_id . '&action=edit' ), Note::E_WC_ADMIN_NOTE_ACTIONED );
return $note;
}
}
RealTimeOrderAlerts.php 0000644 00000003012 15154770571 0011136 0 ustar 00 <?php
/**
* WooCommerce Admin Real Time Order Alerts Note.
*
* Adds a note to download the mobile app to monitor store activity.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Real_Time_Order_Alerts
*/
class RealTimeOrderAlerts {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-real-time-order-alerts';
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
// Only add this note if the store is 3 months old.
if ( ! self::is_wc_admin_active_in_date_range( 'month-3-6' ) ) {
return;
}
// Check that the previous mobile app note was not actioned.
if ( MobileApp::has_note_been_actioned() ) {
return;
}
$content = __( 'Get notifications about store activity, including new orders and product reviews directly on your mobile devices with the Woo app.', 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Get real-time order alerts anywhere', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'learn-more', __( 'Learn more', 'woocommerce' ), 'https://woocommerce.com/mobile/?utm_source=inbox&utm_medium=product' );
return $note;
}
}
SellingOnlineCourses.php 0000644 00000004600 15154770571 0011377 0 ustar 00 <?php
/**
* WooCommerce Admin: Selling Online Courses note
*
* Adds a note to encourage selling online courses.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
/**
* Selling_Online_Courses
*/
class SellingOnlineCourses {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-selling-online-courses';
/**
* Attach hooks.
*/
public function __construct() {
add_action(
'update_option_' . OnboardingProfile::DATA_OPTION,
array( $this, 'check_onboarding_profile' ),
10,
3
);
}
/**
* Check to see if the profiler options match before possibly adding note.
*
* @param object $old_value The old option value.
* @param object $value The new option value.
* @param string $option The name of the option.
*/
public static function check_onboarding_profile( $old_value, $value, $option ) {
// Skip adding if this store is in the education/learning industry.
if ( ! isset( $value['industry'] ) ) {
return;
}
$industry_slugs = array_column( $value['industry'], 'slug' );
if ( ! in_array( 'education-and-learning', $industry_slugs, true ) ) {
return;
}
self::possibly_add_note();
}
/**
* Get the note.
*
* @return Note
*/
public static function get_note() {
$note = new Note();
$note->set_title( __( 'Do you want to sell online courses?', 'woocommerce' ) );
$note->set_content( __( 'Online courses are a great solution for any business that can teach a new skill. Since courses don’t require physical product development or shipping, they’re affordable, fast to create, and can generate passive income for years to come. In this article, we provide you more information about selling courses using WooCommerce.', 'woocommerce' ) );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_MARKETING );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'learn-more',
__( 'Learn more', 'woocommerce' ),
'https://woocommerce.com/posts/how-to-sell-online-courses-wordpress/?utm_source=inbox&utm_medium=product',
Note::E_WC_ADMIN_NOTE_ACTIONED
);
return $note;
}
}
TestCheckout.php 0000644 00000005327 15154770571 0007705 0 ustar 00 <?php
/**
* WooCommerce Admin Test Checkout.
*
* Adds a note to remind the user to test their store checkout.
*
* @package WooCommerce\Admin
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Test_Checkout
*/
class TestCheckout {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-test-checkout';
/**
* Completed tasks option name.
*/
const TASK_LIST_TRACKED_TASKS = 'woocommerce_task_list_tracked_completed_tasks';
/**
* Constructor.
*/
public function __construct() {
add_action( 'update_option_' . self::TASK_LIST_TRACKED_TASKS, array( $this, 'possibly_add_note' ) );
}
/**
* Get the note.
*
* @return Note|null
*/
public static function get_note() {
$onboarding_profile = get_option( 'woocommerce_onboarding_profile', array() );
// Confirm that $onboarding_profile is set.
if ( empty( $onboarding_profile ) ) {
return;
}
// Make sure that the person who filled out the OBW was not setting up
// the store for their customer/client.
if (
! isset( $onboarding_profile['setup_client'] ) ||
$onboarding_profile['setup_client']
) {
return;
}
// Make sure payments task was completed.
$completed_tasks = get_option( self::TASK_LIST_TRACKED_TASKS, array() );
if ( ! in_array( 'payments', $completed_tasks, true ) ) {
return;
}
// Make sure that products were added within the previous 1/2 hour.
$query = new \WC_Product_Query(
array(
'limit' => 1,
'status' => 'publish',
'orderby' => 'date',
'order' => 'ASC',
)
);
$products = $query->get_products();
if ( 0 === count( $products ) ) {
return;
}
$oldest_product_timestamp = $products[0]->get_date_created()->getTimestamp();
$half_hour_in_seconds = 30 * MINUTE_IN_SECONDS;
if ( ( time() - $oldest_product_timestamp ) > $half_hour_in_seconds ) {
return;
}
$content = __( 'Make sure that your checkout is working properly before you launch your store. Go through your checkout process in its entirety: from adding a product to your cart, choosing a shipping location, and making a payment.', 'woocommerce' );
$note = new Note();
$note->set_title( __( 'Don\'t forget to test your checkout', 'woocommerce' ) );
$note->set_content( $content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'test-checkout', __( 'Test checkout', 'woocommerce' ), wc_get_page_permalink( 'shop' ) );
return $note;
}
}
TrackingOptIn.php 0000644 00000005405 15154770571 0010011 0 ustar 00 <?php
/**
* WooCommerce Admin Usage Tracking Opt In Note Provider.
*
* Adds a Usage Tracking Opt In extension note.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* Tracking_Opt_In
*/
class TrackingOptIn {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-usage-tracking-opt-in';
/**
* Attach hooks.
*/
public function __construct() {
add_action( 'woocommerce_note_action_tracking-opt-in', array( $this, 'opt_in_to_tracking' ) );
}
/**
* Get the note.
*
* @return Note|null
*/
public static function get_note() {
// Only show this note to stores that are opted out.
if ( 'yes' === get_option( 'woocommerce_allow_tracking', 'no' ) ) {
return;
}
// We want to show the note after one week.
if ( ! self::is_wc_admin_active_in_date_range( 'week-1-4' ) ) {
return;
}
/* translators: 1: open link to WooCommerce.com settings, 2: open link to WooCommerce.com tracking documentation, 3: close link tag. */
$content_format = __(
'Gathering usage data allows us to improve WooCommerce. Your store will be considered as we evaluate new features, judge the quality of an update, or determine if an improvement makes sense. You can always visit the %1$sSettings%3$s and choose to stop sharing data. %2$sRead more%3$s about what data we collect.',
'woocommerce'
);
$note_content = sprintf(
$content_format,
'<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=woocommerce_com' ) ) . '" target="_blank">',
'<a href="https://woocommerce.com/usage-tracking?utm_medium=product" target="_blank">',
'</a>'
);
$note = new Note();
$note->set_title( __( 'Help WooCommerce improve with usage tracking', 'woocommerce' ) );
$note->set_content( $note_content );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'tracking-opt-in', __( 'Activate usage tracking', 'woocommerce' ), false, Note::E_WC_ADMIN_NOTE_ACTIONED, true );
return $note;
}
/**
* Opt in to usage tracking when note is actioned.
*
* @param Note $note Note being acted upon.
*/
public function opt_in_to_tracking( $note ) {
if ( self::NOTE_NAME === $note->get_name() ) {
// Opt in to tracking and schedule the first data update.
// Same mechanism as in WC_Admin_Setup_Wizard::wc_setup_store_setup_save().
update_option( 'woocommerce_allow_tracking', 'yes' );
wp_schedule_single_event( time() + 10, 'woocommerce_tracker_send_event', array( true ) );
}
}
}
UnsecuredReportFiles.php 0000644 00000004112 15154770571 0011403 0 ustar 00 <?php
/**
* WooCommerce Admin Unsecured Files Note.
*
* Adds a warning about potentially unsecured files.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
if ( ! class_exists( Note::class ) ) {
class_alias( WC_Admin_Note::class, Note::class );
}
/**
* Unsecured_Report_Files
*/
class UnsecuredReportFiles {
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-remove-unsecured-report-files';
/**
* Get the note.
*
* @return Note|null
*/
public static function get_note() {
$note = new Note();
$note->set_title( __( 'Potentially unsecured files were found in your uploads directory', 'woocommerce' ) );
$note->set_content(
sprintf(
/* translators: 1: opening analytics docs link tag. 2: closing link tag */
__( 'Files that may contain %1$sstore analytics%2$s reports were found in your uploads directory - we recommend assessing and deleting any such files.', 'woocommerce' ),
'<a href="https://woocommerce.com/document/woocommerce-analytics/" target="_blank">',
'</a>'
)
);
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_ERROR );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'learn-more',
__( 'Learn more', 'woocommerce' ),
'https://developer.woocommerce.com/?p=10410',
Note::E_WC_ADMIN_NOTE_UNACTIONED,
true
);
$note->add_action(
'dismiss',
__( 'Dismiss', 'woocommerce' ),
wc_admin_url(),
Note::E_WC_ADMIN_NOTE_ACTIONED,
false
);
return $note;
}
/**
* Add the note if it passes predefined conditions.
*/
public static function possibly_add_note() {
$note = self::get_note();
if ( self::note_exists() ) {
return;
}
$note->save();
}
/**
* Check if the note has been previously added.
*/
public static function note_exists() {
$data_store = \WC_Data_Store::load( 'admin-note' );
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
return ! empty( $note_ids );
}
}
WooCommercePayments.php 0000644 00000014400 15154770571 0011230 0 ustar 00 <?php
/**
* WooCommerce Admin WooCommerce Payments Note Provider.
*
* Adds a note to the merchant's inbox showing the benefits of the WooCommerce Payments.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
/**
* WooCommerce_Payments
*/
class WooCommercePayments {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-woocommerce-payments';
/**
* Name of the note for use in the database.
*/
const PLUGIN_SLUG = 'woocommerce-payments';
/**
* Name of the note for use in the database.
*/
const PLUGIN_FILE = 'woocommerce-payments/woocommerce-payments.php';
/**
* Attach hooks.
*/
public function __construct() {
add_action( 'init', array( $this, 'install_on_action' ) );
add_action( 'wc-admin-woocommerce-payments_add_note', array( $this, 'add_note' ) );
}
/**
* Maybe add a note on WooCommerce Payments for US based sites older than a week without the plugin installed.
*/
public static function possibly_add_note() {
if ( ! self::is_wc_admin_active_in_date_range( 'week-1-4' ) || 'US' !== WC()->countries->get_base_country() ) {
return;
}
$data_store = Notes::load_data_store();
// We already have this note? Then mark the note as actioned.
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
if ( ! empty( $note_ids ) ) {
$note_id = array_pop( $note_ids );
$note = Notes::get_note( $note_id );
if ( false === $note ) {
return;
}
// If the WooCommerce Payments plugin was installed after the note was created, make sure it's marked as actioned.
if ( self::is_installed() && Note::E_WC_ADMIN_NOTE_ACTIONED !== $note->get_status() ) {
$note->set_status( Note::E_WC_ADMIN_NOTE_ACTIONED );
$note->save();
}
return;
}
$current_date = new \DateTime();
$publish_date = new \DateTime( '2020-04-14' );
if ( $current_date >= $publish_date ) {
$note = self::get_note();
if ( self::can_be_added() ) {
$note->save();
}
return;
} else {
$hook_name = sprintf( '%s_add_note', self::NOTE_NAME );
if ( ! WC()->queue()->get_next( $hook_name ) ) {
WC()->queue()->schedule_single( $publish_date->getTimestamp(), $hook_name );
}
}
}
/**
* Add a note about WooCommerce Payments.
*
* @return Note
*/
public static function get_note() {
$note = new Note();
$note->set_title( __( 'Try the new way to get paid', 'woocommerce' ) );
$note->set_content(
__( 'Securely accept credit and debit cards on your site. Manage transactions without leaving your WordPress dashboard. Only with <strong>WooPayments</strong>.', 'woocommerce' ) .
'<br><br>' .
sprintf(
/* translators: 1: opening link tag, 2: closing tag */
__( 'By clicking "Get started", you agree to our %1$sTerms of Service%2$s', 'woocommerce' ),
'<a href="https://wordpress.com/tos/" target="_blank">',
'</a>'
)
);
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_MARKETING );
$note->set_name( self::NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action( 'learn-more', __( 'Learn more', 'woocommerce' ), 'https://woocommerce.com/payments/?utm_medium=product', Note::E_WC_ADMIN_NOTE_UNACTIONED );
$note->add_action( 'get-started', __( 'Get started', 'woocommerce' ), wc_admin_url( '&action=setup-woocommerce-payments' ), Note::E_WC_ADMIN_NOTE_ACTIONED, true );
$note->add_nonce_to_action( 'get-started', 'setup-woocommerce-payments', '' );
// Create the note as "actioned" if the plugin is already installed.
if ( self::is_installed() ) {
$note->set_status( Note::E_WC_ADMIN_NOTE_ACTIONED );
}
return $note;
}
/**
* Check if the WooCommerce Payments plugin is active or installed.
*/
protected static function is_installed() {
if ( defined( 'WC_Payments' ) ) {
return true;
}
include_once ABSPATH . '/wp-admin/includes/plugin.php';
return 0 === validate_plugin( self::PLUGIN_FILE );
}
/**
* Install and activate WooCommerce Payments.
*
* @return boolean Whether the plugin was successfully activated.
*/
private function install_and_activate_wcpay() {
$install_request = array( 'plugins' => self::PLUGIN_SLUG );
$installer = new \Automattic\WooCommerce\Admin\API\Plugins();
$result = $installer->install_plugins( $install_request );
if ( is_wp_error( $result ) ) {
return false;
}
wc_admin_record_tracks_event( 'woocommerce_payments_install', array( 'context' => 'inbox' ) );
$activate_request = array( 'plugins' => self::PLUGIN_SLUG );
$result = $installer->activate_plugins( $activate_request );
if ( is_wp_error( $result ) ) {
return false;
}
return true;
}
/**
* Install & activate WooCommerce Payments plugin, and redirect to setup.
*/
public function install_on_action() {
// TODO: Need to validate this request more strictly since we're taking install actions directly?
if (
! isset( $_GET['page'] ) ||
'wc-admin' !== $_GET['page'] ||
! isset( $_GET['action'] ) ||
'setup-woocommerce-payments' !== $_GET['action']
) {
return;
}
$data_store = Notes::load_data_store();
// We already have this note? Then mark the note as actioned.
$note_ids = $data_store->get_notes_with_name( self::NOTE_NAME );
if ( empty( $note_ids ) ) {
return;
}
$note_id = array_pop( $note_ids );
$note = Notes::get_note( $note_id );
if ( false === $note ) {
return;
}
$action = $note->get_action( 'get-started' );
if ( ! $action ||
( isset( $action->nonce_action ) &&
(
empty( $_GET['_wpnonce'] ) ||
! wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), $action->nonce_action ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
)
)
) {
return;
}
if ( ! current_user_can( 'install_plugins' ) ) {
return;
}
$this->install_and_activate_wcpay();
// WooCommerce Payments is installed at this point, so link straight into the onboarding flow.
$connect_url = add_query_arg(
array(
'wcpay-connect' => '1',
'_wpnonce' => wp_create_nonce( 'wcpay-connect' ),
),
admin_url()
);
wp_safe_redirect( $connect_url );
exit;
}
}
WooCommerceSubscriptions.php 0000644 00000003602 15154770571 0012301 0 ustar 00 <?php
/**
* WooCommerce Admin: WooCommerce Subscriptions.
*
* Adds a note to learn more about WooCommerce Subscriptions.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\NoteTraits;
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
/**
* WooCommerce_Subscriptions.
*/
class WooCommerceSubscriptions {
/**
* Note traits.
*/
use NoteTraits;
/**
* Name of the note for use in the database.
*/
const NOTE_NAME = 'wc-admin-woocommerce-subscriptions';
/**
* Get the note.
*
* @return Note|null
*/
public static function get_note() {
$onboarding_data = get_option( OnboardingProfile::DATA_OPTION, array() );
if ( ! isset( $onboarding_data['product_types'] ) || ! in_array( 'subscriptions', $onboarding_data['product_types'], true ) ) {
return;
}
if ( ! self::is_wc_admin_active_in_date_range( 'week-1', DAY_IN_SECONDS ) ) {
return;
}
$note = new Note();
$note->set_title( __( 'Do you need more info about WooCommerce Subscriptions?', 'woocommerce' ) );
$note->set_content( __( 'WooCommerce Subscriptions allows you to introduce a variety of subscriptions for physical or virtual products and services. Create product-of-the-month clubs, weekly service subscriptions or even yearly software billing packages. Add sign-up fees, offer free trials, or set expiration periods.', 'woocommerce' ) );
$note->set_type( Note::E_WC_ADMIN_NOTE_MARKETING );
$note->set_name( self::NOTE_NAME );
$note->set_content_data( (object) array() );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'learn-more',
__( 'Learn More', 'woocommerce' ),
'https://woocommerce.com/products/woocommerce-subscriptions/?utm_source=inbox&utm_medium=product',
Note::E_WC_ADMIN_NOTE_UNACTIONED,
true
);
return $note;
}
}
WooSubscriptionsNotes.php 0000644 00000034446 15154770571 0011651 0 ustar 00 <?php
/**
* WooCommerce Admin (Dashboard) WooCommerce.com Extension Subscriptions Note Provider.
*
* Adds notes to the merchant's inbox concerning WooCommerce.com extension subscriptions.
*/
namespace Automattic\WooCommerce\Internal\Admin\Notes;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Admin\PageController;
/**
* Woo_Subscriptions_Notes
*/
class WooSubscriptionsNotes {
const LAST_REFRESH_OPTION_KEY = 'woocommerce_admin-wc-helper-last-refresh';
const NOTE_NAME = 'wc-admin-wc-helper-connection';
const CONNECTION_NOTE_NAME = 'wc-admin-wc-helper-connection';
const SUBSCRIPTION_NOTE_NAME = 'wc-admin-wc-helper-subscription';
const NOTIFY_WHEN_DAYS_LEFT = 60;
/**
* We want to bubble up expiration notices when they cross certain age
* thresholds. PHP 5.2 doesn't support constant arrays, so we do this.
*
* @return array
*/
private function get_bump_thresholds() {
return array( 60, 45, 20, 7, 1 ); // days.
}
/**
* Hook all the things.
*/
public function __construct() {
add_action( 'admin_head', array( $this, 'admin_head' ) );
add_action( 'update_option_woocommerce_helper_data', array( $this, 'update_option_woocommerce_helper_data' ), 10, 2 );
}
/**
* Reacts to changes in the helper option.
*
* @param array $old_value The previous value of the option.
* @param array $value The new value of the option.
*/
public function update_option_woocommerce_helper_data( $old_value, $value ) {
if ( ! is_array( $old_value ) ) {
$old_value = array();
}
if ( ! is_array( $value ) ) {
$value = array();
}
$old_auth = array_key_exists( 'auth', $old_value ) ? $old_value['auth'] : array();
$new_auth = array_key_exists( 'auth', $value ) ? $value['auth'] : array();
$old_token = array_key_exists( 'access_token', $old_auth ) ? $old_auth['access_token'] : '';
$new_token = array_key_exists( 'access_token', $new_auth ) ? $new_auth['access_token'] : '';
// The site just disconnected.
if ( ! empty( $old_token ) && empty( $new_token ) ) {
$this->remove_notes();
$this->add_no_connection_note();
return;
}
// The site is connected.
if ( $this->is_connected() ) {
$this->remove_notes();
$this->refresh_subscription_notes();
return;
}
}
/**
* Runs on `admin_head` hook. Checks the connection and refreshes subscription notes on relevant pages.
*/
public function admin_head() {
if ( ! PageController::is_admin_or_embed_page() ) {
// To avoid unnecessarily calling Helper API, we only want to refresh subscription notes,
// if the request is initiated from the wc admin dashboard or a WC related page which includes
// the Activity button in WC header.
return;
}
$this->check_connection();
if ( $this->is_connected() ) {
$refresh_notes = false;
// Did the user just do something on the helper page?.
if ( isset( $_GET['wc-helper-status'] ) ) { // @codingStandardsIgnoreLine.
$refresh_notes = true;
}
// Has it been more than a day since we last checked?
// Note: We do it this way and not wp_scheduled_task since WC_Helper_Options is not loaded for cron.
$time_now_gmt = current_time( 'timestamp', 0 );
$last_refresh = intval( get_option( self::LAST_REFRESH_OPTION_KEY, 0 ) );
if ( $last_refresh + DAY_IN_SECONDS <= $time_now_gmt ) {
update_option( self::LAST_REFRESH_OPTION_KEY, $time_now_gmt );
$refresh_notes = true;
}
if ( $refresh_notes ) {
$this->refresh_subscription_notes();
}
}
}
/**
* Checks the connection. Adds a note (as necessary) if there is no connection.
*/
public function check_connection() {
if ( ! $this->is_connected() ) {
$data_store = Notes::load_data_store();
$note_ids = $data_store->get_notes_with_name( self::CONNECTION_NOTE_NAME );
if ( ! empty( $note_ids ) ) {
// We already have a connection note. Exit early.
return;
}
$this->remove_notes();
$this->add_no_connection_note();
}
}
/**
* Whether or not we think the site is currently connected to WooCommerce.com.
*
* @return bool
*/
public function is_connected() {
$auth = \WC_Helper_Options::get( 'auth' );
return ( ! empty( $auth['access_token'] ) );
}
/**
* Returns the WooCommerce.com provided site ID for this site.
*
* @return int|false
*/
public function get_connected_site_id() {
if ( ! $this->is_connected() ) {
return false;
}
$auth = \WC_Helper_Options::get( 'auth' );
return absint( $auth['site_id'] );
}
/**
* Returns an array of product_ids whose subscriptions are active on this site.
*
* @return array
*/
public function get_subscription_active_product_ids() {
$site_id = $this->get_connected_site_id();
if ( ! $site_id ) {
return array();
}
$product_ids = array();
if ( $this->is_connected() ) {
$subscriptions = \WC_Helper::get_subscriptions();
foreach ( (array) $subscriptions as $subscription ) {
if ( in_array( $site_id, $subscription['connections'], true ) ) {
$product_ids[] = $subscription['product_id'];
}
}
}
return $product_ids;
}
/**
* Clears all connection or subscription notes.
*/
public function remove_notes() {
Notes::delete_notes_with_name( self::CONNECTION_NOTE_NAME );
Notes::delete_notes_with_name( self::SUBSCRIPTION_NOTE_NAME );
}
/**
* Adds a note prompting to connect to WooCommerce.com.
*/
public function add_no_connection_note() {
$note = self::get_note();
$note->save();
}
/**
* Get the WooCommerce.com connection note
*/
public static function get_note() {
$note = new Note();
$note->set_title( __( 'Connect to WooCommerce.com', 'woocommerce' ) );
$note->set_content( __( 'Connect to get important product notifications and updates.', 'woocommerce' ) );
$note->set_content_data( (object) array() );
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_name( self::CONNECTION_NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->add_action(
'connect',
__( 'Connect', 'woocommerce' ),
'?page=wc-addons§ion=helper',
Note::E_WC_ADMIN_NOTE_UNACTIONED
);
return $note;
}
/**
* Gets the product_id (if any) associated with a note.
*
* @param Note $note The note object to interrogate.
* @return int|false
*/
public function get_product_id_from_subscription_note( &$note ) {
$content_data = $note->get_content_data();
if ( property_exists( $content_data, 'product_id' ) ) {
return intval( $content_data->product_id );
}
return false;
}
/**
* Removes notes for product_ids no longer active on this site.
*/
public function prune_inactive_subscription_notes() {
$active_product_ids = $this->get_subscription_active_product_ids();
$data_store = Notes::load_data_store();
$note_ids = $data_store->get_notes_with_name( self::SUBSCRIPTION_NOTE_NAME );
foreach ( (array) $note_ids as $note_id ) {
$note = Notes::get_note( $note_id );
$product_id = $this->get_product_id_from_subscription_note( $note );
if ( ! empty( $product_id ) ) {
if ( ! in_array( $product_id, $active_product_ids, true ) ) {
$note->delete();
}
}
}
}
/**
* Finds a note for a given product ID, if the note exists at all.
*
* @param int $product_id The product ID to search for.
* @return Note|false
*/
public function find_note_for_product_id( $product_id ) {
$product_id = intval( $product_id );
$data_store = Notes::load_data_store();
$note_ids = $data_store->get_notes_with_name( self::SUBSCRIPTION_NOTE_NAME );
foreach ( (array) $note_ids as $note_id ) {
$note = Notes::get_note( $note_id );
$found_product_id = $this->get_product_id_from_subscription_note( $note );
if ( $product_id === $found_product_id ) {
return $note;
}
}
return false;
}
/**
* Deletes a note for a given product ID, if the note exists at all.
*
* @param int $product_id The product ID to search for.
*/
public function delete_any_note_for_product_id( $product_id ) {
$product_id = intval( $product_id );
$note = $this->find_note_for_product_id( $product_id );
if ( $note ) {
$note->delete();
}
}
/**
* Adds or updates a note for an expiring subscription.
*
* @param array $subscription The subscription to work with.
*/
public function add_or_update_subscription_expiring( $subscription ) {
$product_id = $subscription['product_id'];
$product_name = $subscription['product_name'];
$expires = intval( $subscription['expires'] );
$time_now_gmt = current_time( 'timestamp', 0 );
$days_until_expiration = intval( ceil( ( $expires - $time_now_gmt ) / DAY_IN_SECONDS ) );
$note = $this->find_note_for_product_id( $product_id );
if ( $note ) {
$content_data = $note->get_content_data();
if ( property_exists( $content_data, 'days_until_expiration' ) ) {
// Note: There is no reason this property should not exist. This is just defensive programming.
$note_days_until_expiration = intval( $content_data->days_until_expiration );
if ( $days_until_expiration === $note_days_until_expiration ) {
// Note is already up to date. Bail.
return;
}
// If we have a note and we are at or have crossed a threshold, we should delete
// the old note and create a new one, thereby "bumping" the note to the top of the inbox.
$bump_thresholds = $this->get_bump_thresholds();
$crossing_threshold = false;
foreach ( (array) $bump_thresholds as $bump_threshold ) {
if ( ( $note_days_until_expiration > $bump_threshold ) && ( $days_until_expiration <= $bump_threshold ) ) {
$note->delete();
$note = false;
continue;
}
}
}
}
$note_title = sprintf(
/* translators: name of the extension subscription expiring soon */
__( '%s subscription expiring soon', 'woocommerce' ),
$product_name
);
$note_content = sprintf(
/* translators: number of days until the subscription expires */
__( 'Your subscription expires in %d days. Enable autorenew to avoid losing updates and access to support.', 'woocommerce' ),
$days_until_expiration
);
$note_content_data = (object) array(
'product_id' => $product_id,
'product_name' => $product_name,
'expired' => false,
'days_until_expiration' => $days_until_expiration,
);
if ( ! $note ) {
$note = new Note();
}
// Reset everything in case we are repurposing an expired note as an expiring note.
$note->set_title( $note_title );
$note->set_type( Note::E_WC_ADMIN_NOTE_WARNING );
$note->set_name( self::SUBSCRIPTION_NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->clear_actions();
$note->add_action(
'enable-autorenew',
__( 'Enable Autorenew', 'woocommerce' ),
'https://woocommerce.com/my-account/my-subscriptions/?utm_medium=product'
);
$note->set_content( $note_content );
$note->set_content_data( $note_content_data );
$note->save();
}
/**
* Adds a note for an expired subscription, or updates an expiring note to expired.
*
* @param array $subscription The subscription to work with.
*/
public function add_or_update_subscription_expired( $subscription ) {
$product_id = $subscription['product_id'];
$product_name = $subscription['product_name'];
$product_page = $subscription['product_url'];
$expires = intval( $subscription['expires'] );
$expires_date = gmdate( 'F jS', $expires );
$note = $this->find_note_for_product_id( $product_id );
if ( $note ) {
$note_content_data = $note->get_content_data();
if ( $note_content_data->expired ) {
// We've already got a full fledged expired note for this. Bail.
// Expired notes' content don't change with time.
return;
}
}
$note_title = sprintf(
/* translators: name of the extension subscription that expired */
__( '%s subscription expired', 'woocommerce' ),
$product_name
);
$note_content = sprintf(
/* translators: date the subscription expired, e.g. Jun 7th 2018 */
__( 'Your subscription expired on %s. Get a new subscription to continue receiving updates and access to support.', 'woocommerce' ),
$expires_date
);
$note_content_data = (object) array(
'product_id' => $product_id,
'product_name' => $product_name,
'expired' => true,
'expires' => $expires,
'expires_date' => $expires_date,
);
if ( ! $note ) {
$note = new Note();
}
$note->set_title( $note_title );
$note->set_content( $note_content );
$note->set_content_data( $note_content_data );
$note->set_type( Note::E_WC_ADMIN_NOTE_WARNING );
$note->set_name( self::SUBSCRIPTION_NOTE_NAME );
$note->set_source( 'woocommerce-admin' );
$note->clear_actions();
$note->add_action(
'renew-subscription',
__( 'Renew Subscription', 'woocommerce' ),
$product_page
);
$note->save();
}
/**
* For each active subscription on this site, checks the expiration date and creates/updates/deletes notes.
*/
public function refresh_subscription_notes() {
if ( ! $this->is_connected() ) {
return;
}
$this->prune_inactive_subscription_notes();
$subscriptions = \WC_Helper::get_subscriptions();
$active_product_ids = $this->get_subscription_active_product_ids();
foreach ( (array) $subscriptions as $subscription ) {
// Only concern ourselves with active products.
$product_id = $subscription['product_id'];
if ( ! in_array( $product_id, $active_product_ids, true ) ) {
continue;
}
// If the subscription will auto-renew, clean up and exit.
if ( $subscription['autorenew'] ) {
$this->delete_any_note_for_product_id( $product_id );
continue;
}
// If the subscription is not expiring by the first threshold, clean up and exit.
$bump_thresholds = $this->get_bump_thresholds();
$first_threshold = DAY_IN_SECONDS * $bump_thresholds[0];
$expires = intval( $subscription['expires'] );
$time_now_gmt = current_time( 'timestamp', 0 );
if ( $expires > $time_now_gmt + $first_threshold ) {
$this->delete_any_note_for_product_id( $product_id );
continue;
}
// Otherwise, if the subscription can still have auto-renew enabled, let them know that now.
if ( $expires > $time_now_gmt ) {
$this->add_or_update_subscription_expiring( $subscription );
continue;
}
// If we got this far, the subscription has completely expired, let them know.
$this->add_or_update_subscription_expired( $subscription );
}
}
}