File manager - Edit - /home/monara/public_html/test.athavaneng.com/Features.tar
Back
AsyncProductEditorCategoryField/Init.php 0000644 00000004641 15073234674 0014427 0 ustar 00 <?php /** * WooCommerce Async Product Editor Category Field. */ namespace Automattic\WooCommerce\Admin\Features\AsyncProductEditorCategoryField; use Automattic\Jetpack\Constants; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Internal\Admin\WCAdminAssets; use Automattic\WooCommerce\Admin\PageController; /** * Loads assets related to the async category field for the product editor. */ class Init { const FEATURE_ID = 'async-product-editor-category-field'; /** * Constructor */ public function __construct() { if ( Features::is_enabled( self::FEATURE_ID ) ) { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_filter( 'woocommerce_taxonomy_args_product_cat', array( $this, 'add_metabox_args' ) ); } } /** * Adds meta_box_cb callback arguments for custom metabox. * * @param array $args Category taxonomy args. * @return array $args category taxonomy args. */ public function add_metabox_args( $args ) { if ( ! isset( $args['meta_box_cb'] ) ) { $args['meta_box_cb'] = 'WC_Meta_Box_Product_Categories::output'; $args['meta_box_sanitize_cb'] = 'taxonomy_meta_box_sanitize_cb_checkboxes'; } return $args; } /** * Enqueue scripts needed for the product form block editor. */ public function enqueue_scripts() { if ( ! PageController::is_embed_page() ) { return; } WCAdminAssets::register_script( 'wp-admin-scripts', 'product-category-metabox', true ); wp_localize_script( 'wc-admin-product-category-metabox', 'wc_product_category_metabox_params', array( 'search_categories_nonce' => wp_create_nonce( 'search-categories' ), 'search_taxonomy_terms_nonce' => wp_create_nonce( 'search-taxonomy-terms' ), ) ); wp_enqueue_script( 'product-category-metabox' ); } /** * Enqueue styles needed for the rich text editor. */ public function enqueue_styles() { if ( ! PageController::is_embed_page() ) { return; } $version = Constants::get_constant( 'WC_VERSION' ); wp_register_style( 'woocommerce_admin_product_category_metabox_styles', WCAdminAssets::get_url( 'product-category-metabox/style', 'css' ), array(), $version ); wp_style_add_data( 'woocommerce_admin_product_category_metabox_styles', 'rtl', 'replace' ); wp_enqueue_style( 'woocommerce_admin_product_category_metabox_styles' ); } } ProductBlockEditor/Tracks.php 0000644 00000002263 15073234674 0012264 0 ustar 00 <?php /** * WooCommerce Product Block Editor */ namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor; /** * Add tracks for the product block editor. */ class Tracks { /** * Initialize the tracks. */ public function init() { add_filter( 'woocommerce_product_source', array( $this, 'add_product_source' ) ); } /** * Check if a URL is a product editor page. * * @param string $url Url to check. * @return boolean */ protected function is_product_editor_page( $url ) { $query_string = wp_parse_url( wp_get_referer(), PHP_URL_QUERY ); parse_str( $query_string, $query ); if ( ! isset( $query['page'] ) || 'wc-admin' !== $query['page'] || ! isset( $query['path'] ) ) { return false; } $path_pieces = explode( '/', $query['path'] ); $route = $path_pieces[1]; return 'add-product' === $route || 'product' === $route; } /** * Update the product source if we're on the product editor page. * * @param string $source Source of product. * @return string */ public function add_product_source( $source ) { if ( $this->is_product_editor_page( wp_get_referer() ) ) { return 'product-block-editor-v1'; } return $source; } } ProductBlockEditor/BlockRegistry.php 0000644 00000017575 15073234674 0013634 0 ustar 00 <?php /** * WooCommerce Product Editor Block Registration */ namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor; use Automattic\WooCommerce\Internal\Admin\WCAdminAssets; /** * Product block registration and style registration functionality. */ class BlockRegistry { /** * Generic blocks directory. */ const GENERIC_BLOCKS_DIR = 'product-editor/blocks/generic'; /** * Product fields blocks directory. */ const PRODUCT_FIELDS_BLOCKS_DIR = 'product-editor/blocks/product-fields'; /** * Array of all available generic blocks. */ const GENERIC_BLOCKS = array( 'woocommerce/conditional', 'woocommerce/product-checkbox-field', 'woocommerce/product-collapsible', 'woocommerce/product-radio-field', 'woocommerce/product-pricing-field', 'woocommerce/product-section', 'woocommerce/product-section-description', 'woocommerce/product-tab', 'woocommerce/product-toggle-field', 'woocommerce/product-taxonomy-field', 'woocommerce/product-text-field', 'woocommerce/product-number-field', ); /** * Array of all available product fields blocks. */ const PRODUCT_FIELDS_BLOCKS = array( 'woocommerce/product-catalog-visibility-field', 'woocommerce/product-description-field', 'woocommerce/product-downloads-field', 'woocommerce/product-images-field', 'woocommerce/product-inventory-email-field', 'woocommerce/product-sku-field', 'woocommerce/product-name-field', 'woocommerce/product-regular-price-field', 'woocommerce/product-sale-price-field', 'woocommerce/product-schedule-sale-fields', 'woocommerce/product-shipping-class-field', 'woocommerce/product-shipping-dimensions-fields', 'woocommerce/product-summary-field', 'woocommerce/product-tag-field', 'woocommerce/product-inventory-quantity-field', 'woocommerce/product-variation-items-field', 'woocommerce/product-variations-fields', 'woocommerce/product-password-field', 'woocommerce/product-list-field', 'woocommerce/product-has-variations-notice', 'woocommerce/product-single-variation-notice', ); /** * Singleton instance. * * @var BlockRegistry */ private static $instance = null; /** * Get the singleton instance. */ public static function get_instance(): BlockRegistry { if ( ! self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ protected function __construct() { add_filter( 'block_categories_all', array( $this, 'register_categories' ), 10, 2 ); $this->register_product_blocks(); } /** * Get a file path for a given block file. * * @param string $path File path. * @param string $dir File directory. */ private function get_file_path( $path, $dir ) { return WC_ABSPATH . WCAdminAssets::get_path( 'js' ) . trailingslashit( $dir ) . $path; } /** * Register all the product blocks. */ private function register_product_blocks() { foreach ( self::PRODUCT_FIELDS_BLOCKS as $block_name ) { $this->register_block( $block_name, self::PRODUCT_FIELDS_BLOCKS_DIR ); } foreach ( self::GENERIC_BLOCKS as $block_name ) { $this->register_block( $block_name, self::GENERIC_BLOCKS_DIR ); } } /** * Register product related block categories. * * @param array[] $block_categories Array of categories for block types. * @param WP_Block_Editor_Context $editor_context The current block editor context. */ public function register_categories( $block_categories, $editor_context ) { if ( INIT::EDITOR_CONTEXT_NAME === $editor_context->name ) { $block_categories[] = array( 'slug' => 'woocommerce', 'title' => __( 'WooCommerce', 'woocommerce' ), 'icon' => null, ); } return $block_categories; } /** * Get the block name without the "woocommerce/" prefix. * * @param string $block_name Block name. * * @return string */ private function remove_block_prefix( $block_name ) { if ( 0 === strpos( $block_name, 'woocommerce/' ) ) { return substr_replace( $block_name, '', 0, strlen( 'woocommerce/' ) ); } return $block_name; } /** * Augment the attributes of a block by adding attributes that are used by the product editor. * * @param array $attributes Block attributes. */ private function augment_attributes( $attributes ) { // Note: If you modify this function, also update the client-side // registerWooBlockType function in @woocommerce/block-templates. return array_merge( $attributes, array( '_templateBlockId' => array( 'type' => 'string', '__experimentalRole' => 'content', ), '_templateBlockOrder' => array( 'type' => 'integer', '__experimentalRole' => 'content', ), '_templateBlockHideConditions' => array( 'type' => 'array', '__experimentalRole' => 'content', ), '_templateBlockDisableConditions' => array( 'type' => 'array', '__experimentalRole' => 'content', ), 'disabled' => isset( $attributes['disabled'] ) ? $attributes['disabled'] : array( 'type' => 'boolean', '__experimentalRole' => 'content', ), ) ); } /** * Augment the uses_context of a block by adding attributes that are used by the product editor. * * @param array $uses_context Block uses_context. */ private function augment_uses_context( $uses_context ) { // Note: If you modify this function, also update the client-side // registerProductEditorBlockType function in @woocommerce/product-editor. return array_merge( isset( $uses_context ) ? $uses_context : array(), array( 'postType', ) ); } /** * Register a single block. * * @param string $block_name Block name. * @param string $block_dir Block directory. * * @return WP_Block_Type|false The registered block type on success, or false on failure. */ private function register_block( $block_name, $block_dir ) { $block_name = $this->remove_block_prefix( $block_name ); $block_json_file = $this->get_file_path( $block_name . '/block.json', $block_dir ); return $this->register_block_type_from_metadata( $block_json_file ); } /** * Check if a block is registered. * * @param string $block_name Block name. */ public function is_registered( $block_name ): bool { $registry = \WP_Block_Type_Registry::get_instance(); return $registry->is_registered( $block_name ); } /** * Unregister a block. * * @param string $block_name Block name. */ public function unregister( $block_name ) { $registry = \WP_Block_Type_Registry::get_instance(); if ( $registry->is_registered( $block_name ) ) { $registry->unregister( $block_name ); } } /** * Register a block type from metadata stored in the block.json file. * * @param string $file_or_folder Path to the JSON file with metadata definition for the block or * path to the folder where the `block.json` file is located. * * @return \WP_Block_Type|false The registered block type on success, or false on failure. */ public function register_block_type_from_metadata( $file_or_folder ) { $metadata_file = ( ! str_ends_with( $file_or_folder, 'block.json' ) ) ? trailingslashit( $file_or_folder ) . 'block.json' : $file_or_folder; if ( ! file_exists( $metadata_file ) ) { return false; } // We are dealing with a local file, so we can use file_get_contents. // phpcs:disable WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $metadata = json_decode( file_get_contents( $metadata_file ), true ); if ( ! is_array( $metadata ) || ! $metadata['name'] ) { return false; } $this->unregister( $metadata['name'] ); return register_block_type_from_metadata( $metadata_file, array( 'attributes' => $this->augment_attributes( isset( $metadata['attributes'] ) ? $metadata['attributes'] : array() ), 'uses_context' => $this->augment_uses_context( isset( $metadata['usesContext'] ) ? $metadata['usesContext'] : array() ), ) ); } } ProductBlockEditor/RedirectionController.php 0000644 00000010406 15073234674 0015346 0 ustar 00 <?php /** * WooCommerce Product Editor Redirection Controller */ namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Internal\Admin\WCAdminAssets; /** * Handle redirecting to the old or new editor based on features and support. */ class RedirectionController { /** * Supported post types. * * @var array */ private $supported_post_types; /** * Set up the hooks used for redirection. * * @param array $supported_post_types Array of supported post types. */ public function __construct( $supported_post_types ) { $this->supported_post_types = $supported_post_types; if ( \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) { add_action( 'current_screen', array( $this, 'maybe_redirect_to_new_editor' ), 30, 0 ); add_action( 'current_screen', array( $this, 'redirect_non_supported_product_types' ), 30, 0 ); } else { add_action( 'current_screen', array( $this, 'maybe_redirect_to_old_editor' ), 30, 0 ); } } /** * Check if the current screen is the legacy add product screen. */ protected function is_legacy_add_new_screen(): bool { $screen = get_current_screen(); return 'post' === $screen->base && 'product' === $screen->post_type && 'add' === $screen->action; } /** * Check if the current screen is the legacy edit product screen. */ protected function is_legacy_edit_screen(): bool { $screen = get_current_screen(); return 'post' === $screen->base && 'product' === $screen->post_type && isset( $_GET['post'] ) && isset( $_GET['action'] ) && 'edit' === $_GET['action']; } /** * Check if a product is supported by the new experience. * * @param integer $product_id Product ID. */ protected function is_product_supported( $product_id ): bool { $product = $product_id ? wc_get_product( $product_id ) : null; $digital_product = $product->is_downloadable() || $product->is_virtual(); if ( $product && in_array( $product->get_type(), $this->supported_post_types, true ) ) { if ( Features::is_enabled( 'product-virtual-downloadable' ) ) { return true; } return ! $digital_product; } return false; } /** * Redirects from old product form to the new product form if the * feature `product_block_editor` is enabled. */ public function maybe_redirect_to_new_editor(): void { if ( $this->is_legacy_add_new_screen() ) { wp_safe_redirect( admin_url( 'admin.php?page=wc-admin&path=/add-product' ) ); exit(); } if ( $this->is_legacy_edit_screen() ) { $product_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : null; if ( ! $this->is_product_supported( $product_id ) ) { return; } wp_safe_redirect( admin_url( 'admin.php?page=wc-admin&path=/product/' . $product_id ) ); exit(); } } /** * Redirects from new product form to the old product form if the * feature `product_block_editor` is enabled. */ public function maybe_redirect_to_old_editor(): void { $route = $this->get_parsed_route(); if ( 'add-product' === $route['page'] ) { wp_safe_redirect( admin_url( 'post-new.php?post_type=product' ) ); exit(); } if ( 'product' === $route['page'] ) { wp_safe_redirect( admin_url( 'post.php?post=' . $route['product_id'] . '&action=edit' ) ); exit(); } } /** * Get the parsed WooCommerce Admin path. */ protected function get_parsed_route(): array { if ( ! \Automattic\WooCommerce\Admin\PageController::is_admin_page() || ! isset( $_GET['path'] ) ) { return array( 'page' => null, 'product_id' => null, ); } $path = esc_url_raw( wp_unslash( $_GET['path'] ) ); $path_pieces = explode( '/', wp_parse_url( $path, PHP_URL_PATH ) ); return array( 'page' => $path_pieces[1], 'product_id' => 'product' === $path_pieces[1] ? absint( $path_pieces[2] ) : null, ); } /** * Redirect non supported product types to legacy editor. */ public function redirect_non_supported_product_types(): void { $route = $this->get_parsed_route(); $product_id = $route['product_id']; if ( 'product' === $route['page'] && ! $this->is_product_supported( $product_id ) ) { wp_safe_redirect( admin_url( 'post.php?post=' . $route['product_id'] . '&action=edit' ) ); exit(); } } } ProductBlockEditor/ProductTemplates/GroupInterface.php 0000644 00000001353 15073234674 0017250 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates; use Automattic\WooCommerce\Admin\BlockTemplates\BlockContainerInterface; use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface; /** * Interface for group containers, which contain sections and blocks. */ interface GroupInterface extends BlockContainerInterface { /** * Adds a new section to the group * * @param array $block_config block config. * @return SectionInterface new block section. */ public function add_section( array $block_config ): SectionInterface; /** * Adds a new block to the group. * * @param array $block_config block config. */ public function add_block( array $block_config ): BlockInterface; } ProductBlockEditor/ProductTemplates/SectionInterface.php 0000644 00000001375 15073234674 0017564 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates; use Automattic\WooCommerce\Admin\BlockTemplates\BlockContainerInterface; use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface; /** * Interface for section containers, which contain sub-sections and blocks. */ interface SectionInterface extends BlockContainerInterface { /** * Adds a new sub-section to the section. * * @param array $block_config block config. * @return SectionInterface new block section. */ public function add_section( array $block_config ): SectionInterface; /** * Adds a new block to the section. * * @param array $block_config block config. */ public function add_block( array $block_config ): BlockInterface; } ProductBlockEditor/ProductTemplates/ProductFormTemplateInterface.php 0000644 00000002122 15073234674 0022107 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates; use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface; use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface; /** * Interface for block containers. */ interface ProductFormTemplateInterface extends BlockTemplateInterface { /** * Adds a new group block. * * @param array $block_config block config. * @return GroupInterface new group block. */ public function add_group( array $block_config ): GroupInterface; /** * Gets Group block by id. * * @param string $group_id group id. * @return GroupInterface|null */ public function get_group_by_id( string $group_id ): ?GroupInterface; /** * Gets Section block by id. * * @param string $section_id section id. * @return SectionInterface|null */ public function get_section_by_id( string $section_id ): ?SectionInterface; /** * Gets Block by id. * * @param string $block_id block id. * @return BlockInterface|null */ public function get_block_by_id( string $block_id ): ?BlockInterface; } ProductBlockEditor/Init.php 0000644 00000017357 15073234674 0011752 0 ustar 00 <?php /** * WooCommerce Product Block Editor */ namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Internal\Admin\Features\ProductBlockEditor\ProductTemplates\SimpleProductTemplate; use Automattic\WooCommerce\Internal\Admin\Features\ProductBlockEditor\ProductTemplates\ProductVariationTemplate; use Automattic\WooCommerce\Admin\PageController; use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplateRegistry; use Automattic\WooCommerce\Internal\Admin\BlockTemplates\Block; use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplateLogger; use WP_Block_Editor_Context; /** * Loads assets related to the product block editor. */ class Init { /** * The context name used to identify the editor. */ const EDITOR_CONTEXT_NAME = 'woocommerce/edit-product'; /** * Supported post types. * * @var array */ private $supported_post_types = array( 'simple' ); /** * Redirection controller. * * @var RedirectionController */ private $redirection_controller; /** * Constructor */ public function __construct() { if ( Features::is_enabled( 'product-variation-management' ) ) { array_push( $this->supported_post_types, 'variable' ); } if ( Features::is_enabled( 'product-external-affiliate' ) ) { array_push( $this->supported_post_types, 'external' ); } if ( Features::is_enabled( 'product-grouped' ) ) { array_push( $this->supported_post_types, 'grouped' ); } $this->redirection_controller = new RedirectionController( $this->supported_post_types ); if ( \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) { if ( ! Features::is_enabled( 'new-product-management-experience' ) ) { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'dequeue_conflicting_styles' ), 100 ); add_action( 'get_edit_post_link', array( $this, 'update_edit_product_link' ), 10, 2 ); } add_filter( 'woocommerce_admin_get_user_data_fields', array( $this, 'add_user_data_fields' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_filter( 'woocommerce_register_post_type_product_variation', array( $this, 'enable_rest_api_for_product_variation' ) ); add_action( 'current_screen', array( $this, 'set_current_screen_to_block_editor_if_wc_admin' ) ); // Make sure the block registry is initialized so that core blocks are registered. BlockRegistry::get_instance(); $tracks = new Tracks(); $tracks->init(); // Make sure the block template logger is initialized before any templates are created. BlockTemplateLogger::get_instance(); } } /** * Enqueue scripts needed for the product form block editor. */ public function enqueue_scripts() { if ( ! PageController::is_admin_or_embed_page() ) { return; } $this->register_product_editor_templates(); $editor_settings = $this->get_product_editor_settings(); $script_handle = 'wc-admin-edit-product'; wp_register_script( $script_handle, '', array(), '0.1.0', true ); wp_enqueue_script( $script_handle ); wp_add_inline_script( $script_handle, 'var productBlockEditorSettings = productBlockEditorSettings || ' . wp_json_encode( $editor_settings ) . ';', 'before' ); wp_add_inline_script( $script_handle, sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( $editor_settings['blockCategories'] ) ), 'before' ); wp_tinymce_inline_scripts(); wp_enqueue_media(); } /** * Enqueue styles needed for the rich text editor. */ public function enqueue_styles() { if ( ! PageController::is_admin_or_embed_page() ) { return; } wp_enqueue_style( 'wp-edit-blocks' ); wp_enqueue_style( 'wp-format-library' ); wp_enqueue_editor(); /** * Enqueue any block editor related assets. * * @since 7.1.0 */ do_action( 'enqueue_block_editor_assets' ); } /** * Dequeue conflicting styles. */ public function dequeue_conflicting_styles() { if ( ! PageController::is_admin_or_embed_page() ) { return; } // Dequeing this to avoid conflicts, until we remove the 'woocommerce-page' class. wp_dequeue_style( 'woocommerce-blocktheme' ); } /** * Update the edit product links when the new experience is enabled. * * @param string $link The edit link. * @param int $post_id Post ID. * @return string */ public function update_edit_product_link( $link, $post_id ) { $product = wc_get_product( $post_id ); if ( ! $product ) { return $link; } if ( $product->get_type() === 'simple' ) { return admin_url( 'admin.php?page=wc-admin&path=/product/' . $product->get_id() ); } return $link; } /** * Enables variation post type in REST API. * * @param array $args Array of post type arguments. * @return array Array of post type arguments. */ public function enable_rest_api_for_product_variation( $args ) { $args['show_in_rest'] = true; return $args; } /** * Adds fields so that we can store user preferences for the variations block. * * @param array $user_data_fields User data fields. * @return array */ public function add_user_data_fields( $user_data_fields ) { return array_merge( $user_data_fields, array( 'variable_product_block_tour_shown', 'local_attributes_notice_dismissed_ids', 'variable_items_without_price_notice_dismissed', ) ); } /** * Sets the current screen to the block editor if a wc-admin page. */ public function set_current_screen_to_block_editor_if_wc_admin() { $screen = get_current_screen(); // phpcs:ignore Squiz.PHP.CommentedOutCode.Found // (no idea why I need that phpcs:ignore above, but I'm tired trying to re-write this comment to get it to pass) // we can't check the 'path' query param because client-side routing is used within wc-admin, // so this action handler is only called on the initial page load from the server, which might // not be the product edit page (it mostly likely isn't). if ( PageController::is_admin_page() ) { $screen->is_block_editor( true ); wp_add_inline_script( 'wp-blocks', 'wp.blocks && wp.blocks.unstable__bootstrapServerSideBlockDefinitions && wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');' ); } } /** * Get the product editor settings. */ private function get_product_editor_settings() { $editor_settings = array(); $template_registry = wc_get_container()->get( BlockTemplateRegistry::class ); $block_template_logger = BlockTemplateLogger::get_instance(); $block_template_logger->log_template_events_to_file( 'simple-product' ); $block_template_logger->log_template_events_to_file( 'product-variation' ); $editor_settings['templates'] = array( 'product' => $template_registry->get_registered( 'simple-product' )->get_formatted_template(), 'product_variation' => $template_registry->get_registered( 'product-variation' )->get_formatted_template(), ); $editor_settings['templateEvents'] = array( 'product' => $block_template_logger->get_formatted_template_events( 'simple-product' ), 'product_variation' => $block_template_logger->get_formatted_template_events( 'product-variation' ), ); $block_editor_context = new WP_Block_Editor_Context( array( 'name' => self::EDITOR_CONTEXT_NAME ) ); return get_block_editor_settings( $editor_settings, $block_editor_context ); } /** * Register product editor templates. */ private function register_product_editor_templates() { $template_registry = wc_get_container()->get( BlockTemplateRegistry::class ); $template_registry->register( new SimpleProductTemplate() ); $template_registry->register( new ProductVariationTemplate() ); } } Navigation/Screen.php 0000644 00000013106 15073234674 0010607 0 ustar 00 <?php /** * WooCommerce Navigation Screen * * @package Woocommerce Navigation */ namespace Automattic\WooCommerce\Admin\Features\Navigation; use Automattic\WooCommerce\Admin\Features\Navigation\Menu; /** * Contains logic for the WooCommerce Navigation menu. */ class Screen { /** * Class instance. * * @var Screen instance */ protected static $instance = null; /** * Screen IDs of registered pages. * * @var array */ protected static $screen_ids = array(); /** * Registered post types. * * @var array */ protected static $post_types = array(); /** * Registered taxonomies. * * @var array */ protected static $taxonomies = array(); /** * Get class instance. */ final public static function instance() { if ( ! static::$instance ) { static::$instance = new static(); } return static::$instance; } /** * Init. */ public function init() { add_filter( 'admin_body_class', array( $this, 'add_body_class' ) ); } /** * Returns an array of filtered screen ids. */ public static function get_screen_ids() { return apply_filters( 'woocommerce_navigation_screen_ids', self::$screen_ids ); } /** * Returns an array of registered post types. */ public static function get_post_types() { return apply_filters( 'woocommerce_navigation_post_types', self::$post_types ); } /** * Returns an array of registered post types. */ public static function get_taxonomies() { return apply_filters( 'woocommerce_navigation_taxonomies', self::$taxonomies ); } /** * Check if we're on a WooCommerce page * * @return bool */ public static function is_woocommerce_page() { global $pagenow; // Get taxonomy if on a taxonomy screen. $taxonomy = ''; if ( in_array( $pagenow, array( 'edit-tags.php', 'term.php' ), true ) ) { if ( isset( $_GET['taxonomy'] ) ) { // phpcs:ignore CSRF ok. $taxonomy = sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) ); // phpcs:ignore CSRF ok. } } $taxonomies = self::get_taxonomies(); // Get post type if on a post screen. $post_type = ''; if ( in_array( $pagenow, array( 'edit.php', 'post.php', 'post-new.php' ), true ) ) { if ( isset( $_GET['post'] ) ) { // phpcs:ignore CSRF ok. $post_type = get_post_type( (int) $_GET['post'] ); // phpcs:ignore CSRF ok. } elseif ( isset( $_GET['post_type'] ) ) { // phpcs:ignore CSRF ok. $post_type = sanitize_text_field( wp_unslash( $_GET['post_type'] ) ); // phpcs:ignore CSRF ok. } } $post_types = self::get_post_types(); // Get current screen ID. $current_screen = get_current_screen(); $screen_ids = self::get_screen_ids(); $current_screen_id = $current_screen ? $current_screen->id : null; if ( in_array( $post_type, $post_types, true ) || in_array( $taxonomy, $taxonomies, true ) || self::is_woocommerce_core_taxonomy( $taxonomy ) || in_array( $current_screen_id, $screen_ids, true ) ) { return true; } return false; } /** * Check if a given taxonomy is a WooCommerce core related taxonomy. * * @param string $taxonomy Taxonomy. * @return bool */ public static function is_woocommerce_core_taxonomy( $taxonomy ) { if ( in_array( $taxonomy, array( 'product_cat', 'product_tag' ), true ) ) { return true; } if ( 'pa_' === substr( $taxonomy, 0, 3 ) ) { return true; } return false; } /** * Add navigation classes to body. * * @param string $classes Classes. * @return string */ public function add_body_class( $classes ) { if ( self::is_woocommerce_page() ) { $classes .= ' has-woocommerce-navigation'; /** * Adds the ability to skip disabling of the WP toolbar. * * @param boolean $bool WP Toolbar disabled. */ if ( apply_filters( 'woocommerce_navigation_wp_toolbar_disabled', true ) ) { $classes .= ' is-wp-toolbar-disabled'; } } return $classes; } /** * Adds a screen ID to the list of screens that use the navigtion. * Finds the parent if none is given to grab the correct screen ID. * * @param string $callback Callback or URL for page. * @param string|null $parent Parent screen ID. */ public static function add_screen( $callback, $parent = null ) { global $submenu; $plugin_page = self::get_plugin_page( $callback ); if ( ! $parent ) { $parent = Menu::get_parent_key( $callback ); } $screen_id = get_plugin_page_hookname( $plugin_page, $parent ); // This screen has already been added. if ( in_array( $screen_id, self::$screen_ids, true ) ) { return; } self::$screen_ids[] = $screen_id; } /** * Get the plugin page slug. * * @param string $callback Callback. * @return string */ public static function get_plugin_page( $callback ) { $url = Menu::get_callback_url( $callback ); $parts = wp_parse_url( $url ); if ( ! isset( $parts['query'] ) ) { return $callback; } parse_str( $parts['query'], $query ); if ( ! isset( $query['page'] ) ) { return $callback; } $plugin_page = wp_unslash( $query['page'] ); $plugin_page = plugin_basename( $plugin_page ); return $plugin_page; } /** * Register post type for use in WooCommerce Navigation screens. * * @param string $post_type Post type to add. */ public static function register_post_type( $post_type ) { if ( ! in_array( $post_type, self::$post_types, true ) ) { self::$post_types[] = $post_type; } } /** * Register taxonomy for use in WooCommerce Navigation screens. * * @param string $taxonomy Taxonomy to add. */ public static function register_taxonomy( $taxonomy ) { if ( ! in_array( $taxonomy, self::$taxonomies, true ) ) { self::$taxonomies[] = $taxonomy; } } } Navigation/Menu.php 0000644 00000054462 15073234674 0010306 0 ustar 00 <?php /** * WooCommerce Navigation Menu * * @package Woocommerce Navigation */ namespace Automattic\WooCommerce\Admin\Features\Navigation; use Automattic\WooCommerce\Admin\Features\Navigation\Favorites; use Automattic\WooCommerce\Admin\Features\Navigation\Screen; use Automattic\WooCommerce\Admin\Features\Navigation\CoreMenu; /** * Contains logic for the WooCommerce Navigation menu. */ class Menu { /** * Class instance. * * @var Menu instance */ protected static $instance = null; /** * Array index of menu capability. * * @var int */ const CAPABILITY = 1; /** * Array index of menu callback. * * @var int */ const CALLBACK = 2; /** * Array index of menu callback. * * @var int */ const SLUG = 3; /** * Array index of menu CSS class string. * * @var int */ const CSS_CLASSES = 4; /** * Array of usable menu IDs. */ const MENU_IDS = array( 'primary', 'favorites', 'plugins', 'secondary', ); /** * Store menu items. * * @var array */ protected static $menu_items = array(); /** * Store categories with menu item IDs. * * @var array */ protected static $categories = array( 'woocommerce' => array(), ); /** * Registered callbacks or URLs with migration boolean as key value pairs. * * @var array */ protected static $callbacks = array(); /** * Get class instance. */ final public static function instance() { if ( ! static::$instance ) { static::$instance = new static(); } return static::$instance; } /** * Init. */ public function init() { add_action( 'admin_menu', array( $this, 'add_core_items' ), 100 ); add_filter( 'admin_enqueue_scripts', array( $this, 'enqueue_data' ), 20 ); add_filter( 'admin_menu', array( $this, 'migrate_core_child_items' ), PHP_INT_MAX - 1 ); add_filter( 'admin_menu', array( $this, 'migrate_menu_items' ), PHP_INT_MAX - 2 ); } /** * Convert a WordPress menu callback to a URL. * * @param string $callback Menu callback. * @return string */ public static function get_callback_url( $callback ) { // Return the full URL. if ( strpos( $callback, 'http' ) === 0 ) { return $callback; } $pos = strpos( $callback, '?' ); $file = $pos > 0 ? substr( $callback, 0, $pos ) : $callback; if ( file_exists( ABSPATH . "/wp-admin/$file" ) ) { return $callback; } return 'admin.php?page=' . $callback; } /** * Get the parent key if one exists. * * @param string $callback Callback or URL. * @return string|null */ public static function get_parent_key( $callback ) { global $submenu; if ( ! $submenu ) { return null; } // This is already a parent item. if ( isset( $submenu[ $callback ] ) ) { return null; } foreach ( $submenu as $key => $menu ) { foreach ( $menu as $item ) { if ( $item[ self::CALLBACK ] === $callback ) { return $key; } } } return null; } /** * Adds a top level menu item to the navigation. * * @param array $args Array containing the necessary arguments. * $args = array( * 'id' => (string) The unique ID of the menu item. Required. * 'title' => (string) Title of the menu item. Required. * 'url' => (string) URL or callback to be used. Required. * 'order' => (int) Menu item order. * 'migrate' => (bool) Whether or not to hide the item in the wp admin menu. * 'menuId' => (string) The ID of the menu to add the category to. * ). */ private static function add_category( $args ) { if ( ! isset( $args['id'] ) || isset( self::$menu_items[ $args['id'] ] ) ) { return; } $defaults = array( 'id' => '', 'title' => '', 'order' => 100, 'migrate' => true, 'menuId' => 'primary', 'isCategory' => true, ); $menu_item = wp_parse_args( $args, $defaults ); $menu_item['title'] = wp_strip_all_tags( wp_specialchars_decode( $menu_item['title'] ) ); unset( $menu_item['url'] ); unset( $menu_item['capability'] ); if ( ! isset( $menu_item['parent'] ) ) { $menu_item['parent'] = 'woocommerce'; $menu_item['backButtonLabel'] = __( 'WooCommerce Home', 'woocommerce' ); } self::$menu_items[ $menu_item['id'] ] = $menu_item; self::$categories[ $menu_item['id'] ] = array(); self::$categories[ $menu_item['parent'] ][] = $menu_item['id']; if ( isset( $args['url'] ) ) { self::$callbacks[ $args['url'] ] = $menu_item['migrate']; } } /** * Adds a child menu item to the navigation. * * @param array $args Array containing the necessary arguments. * $args = array( * 'id' => (string) The unique ID of the menu item. Required. * 'title' => (string) Title of the menu item. Required. * 'parent' => (string) Parent menu item ID. * 'capability' => (string) Capability to view this menu item. * 'url' => (string) URL or callback to be used. Required. * 'order' => (int) Menu item order. * 'migrate' => (bool) Whether or not to hide the item in the wp admin menu. * 'menuId' => (string) The ID of the menu to add the item to. * 'matchExpression' => (string) A regular expression used to identify if the menu item is active. * ). */ private static function add_item( $args ) { if ( ! isset( $args['id'] ) ) { return; } if ( isset( self::$menu_items[ $args['id'] ] ) ) { wc_doing_it_wrong( __METHOD__, sprintf( /* translators: 1: Duplicate menu item path. */ esc_html__( 'You have attempted to register a duplicate item with WooCommerce Navigation: %1$s', 'woocommerce' ), '`' . $args['id'] . '`' ), '6.5.0' ); return; } $defaults = array( 'id' => '', 'title' => '', 'capability' => 'manage_woocommerce', 'url' => '', 'order' => 100, 'migrate' => true, 'menuId' => 'primary', ); $menu_item = wp_parse_args( $args, $defaults ); $menu_item['title'] = wp_strip_all_tags( wp_specialchars_decode( $menu_item['title'] ) ); $menu_item['url'] = self::get_callback_url( $menu_item['url'] ); if ( ! isset( $menu_item['parent'] ) ) { $menu_item['parent'] = 'woocommerce'; } $menu_item['menuId'] = self::get_item_menu_id( $menu_item ); self::$menu_items[ $menu_item['id'] ] = $menu_item; self::$categories[ $menu_item['parent'] ][] = $menu_item['id']; if ( isset( $args['url'] ) ) { self::$callbacks[ $args['url'] ] = $menu_item['migrate']; } } /** * Get an item's menu ID from its parent. * * @param array $item Item args. * @return string */ public static function get_item_menu_id( $item ) { $favorites = Favorites::get_all( get_current_user_id() ); if ( is_array( $favorites ) && ! empty( $favorites ) && in_array( $item['id'], $favorites, true ) ) { return 'favorites'; } if ( isset( $item['parent'] ) && isset( self::$menu_items[ $item['parent'] ] ) ) { $menu_id = self::$menu_items[ $item['parent'] ]['menuId']; return 'favorites' === $menu_id ? 'plugins' : $menu_id; } return $item['menuId']; } /** * Adds a plugin category. * * @param array $args Array containing the necessary arguments. * $args = array( * 'id' => (string) The unique ID of the menu item. Required. * 'title' => (string) Title of the menu item. Required. * 'url' => (string) URL or callback to be used. Required. * 'migrate' => (bool) Whether or not to hide the item in the wp admin menu. * 'order' => (int) Menu item order. * ). */ public static function add_plugin_category( $args ) { $category_args = array_merge( $args, array( 'menuId' => 'plugins', ) ); if ( ! isset( $category_args['parent'] ) ) { unset( $category_args['order'] ); } $menu_id = self::get_item_menu_id( $category_args ); if ( ! in_array( $menu_id, array( 'plugins', 'favorites' ), true ) ) { return; } $category_args['menuId'] = $menu_id; self::add_category( $category_args ); } /** * Adds a plugin item. * * @param array $args Array containing the necessary arguments. * $args = array( * 'id' => (string) The unique ID of the menu item. Required. * 'title' => (string) Title of the menu item. Required. * 'parent' => (string) Parent menu item ID. * 'capability' => (string) Capability to view this menu item. * 'url' => (string) URL or callback to be used. Required. * 'migrate' => (bool) Whether or not to hide the item in the wp admin menu. * 'order' => (int) Menu item order. * 'matchExpression' => (string) A regular expression used to identify if the menu item is active. * ). */ public static function add_plugin_item( $args ) { if ( ! isset( $args['parent'] ) ) { unset( $args['order'] ); } $item_args = array_merge( $args, array( 'menuId' => 'plugins', ) ); $menu_id = self::get_item_menu_id( $item_args ); if ( 'plugins' !== $menu_id ) { return; } self::add_item( $item_args ); } /** * Adds a plugin setting item. * * @param array $args Array containing the necessary arguments. * $args = array( * 'id' => (string) The unique ID of the menu item. Required. * 'title' => (string) Title of the menu item. Required. * 'capability' => (string) Capability to view this menu item. * 'url' => (string) URL or callback to be used. Required. * 'migrate' => (bool) Whether or not to hide the item in the wp admin menu. * ). */ public static function add_setting_item( $args ) { unset( $args['order'] ); if ( isset( $args['parent'] ) || isset( $args['menuId'] ) ) { error_log( // phpcs:ignore sprintf( /* translators: 1: Duplicate menu item path. */ esc_html__( 'The item ID %1$s attempted to register using an invalid option. The arguments `menuId` and `parent` are not allowed for add_setting_item()', 'woocommerce' ), '`' . $args['id'] . '`' ) ); } $item_args = array_merge( $args, array( 'menuId' => 'secondary', 'parent' => 'woocommerce-settings', ) ); self::add_item( $item_args ); } /** * Get menu item templates for a given post type. * * @param string $post_type Post type to add. * @param array $menu_args Arguments merged with the returned menu items. * @return array */ public static function get_post_type_items( $post_type, $menu_args = array() ) { $post_type_object = get_post_type_object( $post_type ); if ( ! $post_type_object || ! $post_type_object->show_in_menu ) { return; } $parent = isset( $menu_args['parent'] ) ? $menu_args['parent'] . '-' : ''; $match_expression = isset( $_GET['post'] ) && get_post_type( intval( $_GET['post'] ) ) === $post_type // phpcs:ignore WordPress.Security.NonceVerification ? '(edit.php|post.php)' : null; return array( 'default' => array_merge( array( 'title' => esc_attr( $post_type_object->labels->menu_name ), 'capability' => $post_type_object->cap->edit_posts, 'id' => $parent . $post_type, 'url' => "edit.php?post_type={$post_type}", 'matchExpression' => $match_expression, ), $menu_args ), 'all' => array_merge( array( 'title' => esc_attr( $post_type_object->labels->all_items ), 'capability' => $post_type_object->cap->edit_posts, 'id' => "{$parent}{$post_type}-all-items", 'url' => "edit.php?post_type={$post_type}", 'order' => 10, 'matchExpression' => $match_expression, ), $menu_args ), 'new' => array_merge( array( 'title' => esc_attr( $post_type_object->labels->add_new ), 'capability' => $post_type_object->cap->create_posts, 'id' => "{$parent}{$post_type}-add-new", 'url' => "post-new.php?post_type={$post_type}", 'order' => 20, ), $menu_args ), ); } /** * Get menu item templates for a given taxonomy. * * @param string $taxonomy Taxonomy to add. * @param array $menu_args Arguments merged with the returned menu items. * @return array */ public static function get_taxonomy_items( $taxonomy, $menu_args = array() ) { $taxonomy_object = get_taxonomy( $taxonomy ); if ( ! $taxonomy_object || ! $taxonomy_object->show_in_menu ) { return; } $parent = isset( $menu_args['parent'] ) ? $menu_args['parent'] . '-' : ''; $product_type_query = ! empty( $taxonomy_object->object_type ) ? "&post_type={$taxonomy_object->object_type[0]}" : ''; $match_expression = 'term.php'; // Match term.php pages. $match_expression .= "(?=.*[?|&]taxonomy={$taxonomy}(&|$|#))"; // Lookahead to match a taxonomy URL param. $match_expression .= '|'; // Or. $match_expression .= 'edit-tags.php'; // Match edit-tags.php pages. $match_expression .= "(?=.*[?|&]taxonomy={$taxonomy}(&|$|#))"; // Lookahead to match a taxonomy URL param. return array( 'default' => array_merge( array( 'title' => esc_attr( $taxonomy_object->labels->menu_name ), 'capability' => $taxonomy_object->cap->edit_terms, 'id' => $parent . $taxonomy, 'url' => "edit-tags.php?taxonomy={$taxonomy}{$product_type_query}", 'matchExpression' => $match_expression, ), $menu_args ), 'all' => array_merge( array( 'title' => esc_attr( $taxonomy_object->labels->all_items ), 'capability' => $taxonomy_object->cap->edit_terms, 'id' => "{$parent}{$taxonomy}-all-items", 'url' => "edit-tags.php?taxonomy={$taxonomy}{$product_type_query}", 'matchExpression' => $match_expression, 'order' => 10, ), $menu_args ), ); } /** * Add core menu items. */ public function add_core_items() { $categories = CoreMenu::get_categories(); foreach ( $categories as $category ) { self::add_category( $category ); } $items = CoreMenu::get_items(); foreach ( $items as $item ) { if ( isset( $item['is_category'] ) && $item['is_category'] ) { self::add_category( $item ); } else { self::add_item( $item ); } } } /** * Add an item or taxonomy. * * @param array $menu_item Menu item. */ public function add_item_and_taxonomy( $menu_item ) { if ( in_array( $menu_item[2], CoreMenu::get_excluded_items(), true ) ) { return; } $menu_item[2] = htmlspecialchars_decode( $menu_item[2] ); // Don't add already added items. $callbacks = self::get_callbacks(); if ( array_key_exists( $menu_item[2], $callbacks ) ) { return; } // Don't add these Product submenus because they are added elsewhere. if ( in_array( $menu_item[2], array( 'product_importer', 'product_exporter', 'product_attributes' ), true ) ) { return; } self::add_plugin_item( array( 'title' => $menu_item[0], 'capability' => $menu_item[1], 'id' => sanitize_title( $menu_item[0] ), 'url' => $menu_item[2], ) ); // Determine if migrated items are a taxonomy or post_type. If they are, register them. $parsed_url = wp_parse_url( $menu_item[2] ); $query_string = isset( $parsed_url['query'] ) ? $parsed_url['query'] : false; if ( $query_string ) { $query = array(); parse_str( $query_string, $query ); if ( isset( $query['taxonomy'] ) ) { Screen::register_taxonomy( $query['taxonomy'] ); } elseif ( isset( $query['post_type'] ) ) { Screen::register_post_type( $query['post_type'] ); } } } /** * Migrate any remaining WooCommerce child items. * * @param array $menu Menu items. * @return array */ public function migrate_core_child_items( $menu ) { global $submenu; if ( ! isset( $submenu['woocommerce'] ) && ! isset( $submenu['edit.php?post_type=product'] ) ) { return $menu; } $main_items = isset( $submenu['woocommerce'] ) ? $submenu['woocommerce'] : array(); $product_items = isset( $submenu['edit.php?post_type=product'] ) ? $submenu['edit.php?post_type=product'] : array(); foreach ( $main_items as $key => $menu_item ) { self::add_item_and_taxonomy( $menu_item ); // phpcs:disable if ( ! isset( $menu_item[ self::CSS_CLASSES ] ) ) { $submenu['woocommerce'][ $key ][] .= ' hide-if-js'; } else if ( strpos( $submenu['woocommerce'][ $key ][ self::CSS_CLASSES ], 'hide-if-js' ) !== false ) { continue; } else { $submenu['woocommerce'][ $key ][ self::CSS_CLASSES ] .= ' hide-if-js'; } // phpcs:enable } foreach ( $product_items as $key => $menu_item ) { self::add_item_and_taxonomy( $menu_item ); } return $menu; } /** * Check if a menu item's callback is registered in the menu. * * @param array $menu_item Menu item args. * @return bool */ public static function has_callback( $menu_item ) { if ( ! $menu_item || ! isset( $menu_item[ self::CALLBACK ] ) ) { return false; } $callback = $menu_item[ self::CALLBACK ]; if ( isset( self::$callbacks[ $callback ] ) && self::$callbacks[ $callback ] ) { return true; } if ( isset( self::$callbacks[ self::get_callback_url( $callback ) ] ) && self::$callbacks[ self::get_callback_url( $callback ) ] ) { return true; } return false; } /** * Hides all WP admin menus items and adds screen IDs to check for new items. */ public static function migrate_menu_items() { global $menu, $submenu; foreach ( $menu as $key => $menu_item ) { if ( self::has_callback( $menu_item ) ) { // Disable phpcs since we need to override submenu classes. // Note that `phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited` does not work to disable this check. // phpcs:disable $menu[ $key ][ self::CSS_CLASSES ] .= ' hide-if-js'; // phps:enable continue; } // WordPress core menus make the parent item the same URL as the first child. $has_children = isset( $submenu[ $menu_item[ self::CALLBACK ] ] ) && isset( $submenu[ $menu_item[ self::CALLBACK ] ][0] ); $first_child = $has_children ? $submenu[ $menu_item[ self::CALLBACK ] ][0] : null; if ( 'woocommerce' !== $menu_item[2] && self::has_callback( $first_child ) ) { // Disable phpcs since we need to override submenu classes. // Note that `phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited` does not work to disable this check. // phpcs:disable $menu[ $key ][ self::CSS_CLASSES ] .= ' hide-if-js'; // phps:enable } } // Remove excluded submenu items if ( isset( $submenu['woocommerce'] ) ) { foreach ( $submenu['woocommerce'] as $key => $submenu_item ) { if ( in_array( $submenu_item[ self::CALLBACK ], CoreMenu::get_excluded_items(), true ) ) { if ( isset( $submenu['woocommerce'][ $key ][ self::CSS_CLASSES ] ) ) { $submenu['woocommerce'][ $key ][ self::CSS_CLASSES ] .= ' hide-if-js'; } else { $submenu['woocommerce'][ $key ][] = 'hide-if-js'; } } } } foreach ( $submenu as $parent_key => $parent ) { foreach ( $parent as $key => $menu_item ) { if ( self::has_callback( $menu_item ) ) { // Disable phpcs since we need to override submenu classes. // Note that `phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited` does not work to disable this check. // phpcs:disable if ( ! isset( $menu_item[ self::SLUG ] ) ) { $submenu[ $parent_key ][ $key ][] = ''; } if ( ! isset( $menu_item[ self::CSS_CLASSES ] ) ) { $submenu[ $parent_key ][ $key ][] .= ' hide-if-js'; } else { $submenu[ $parent_key ][ $key ][ self::CSS_CLASSES ] .= ' hide-if-js'; } // phps:enable } } } foreach ( array_keys( self::$callbacks ) as $callback ) { Screen::add_screen( $callback ); } } /** * Add a callback to identify and hide pages in the WP menu. */ public static function hide_wp_menu_item( $callback ) { self::$callbacks[ $callback ] = true; } /** * Get registered menu items. * * @return array */ public static function get_items() { return apply_filters( 'woocommerce_navigation_menu_items', self::$menu_items ); } /** * Get registered menu items. * * @return array */ public static function get_category_items( $category ) { if ( ! isset( self::$categories[ $category ] ) ) { return array(); } $menu_item_ids = self::$categories[ $category ]; $category_menu_items = array(); foreach ( $menu_item_ids as $id ) { if ( isset( self::$menu_items[ $id ] ) ) { $category_menu_items[] = self::$menu_items[ $id ]; } } return apply_filters( 'woocommerce_navigation_menu_category_items', $category_menu_items ); } /** * Get registered callbacks. * * @return array */ public static function get_callbacks() { return apply_filters( 'woocommerce_navigation_callbacks', self::$callbacks ); } /** * Gets the menu item data mapped by category and menu ID. * * @return array */ public static function get_mapped_menu_items() { $menu_items = self::get_items(); $mapped_items = array(); // Sort the items by order and title. $order = array_column( $menu_items, 'order' ); $title = array_column( $menu_items, 'title' ); array_multisort( $order, SORT_ASC, $title, SORT_ASC, $menu_items ); foreach ( $menu_items as $id => $menu_item ) { $category_id = $menu_item[ 'parent' ]; $menu_id = $menu_item[ 'menuId' ]; if ( ! isset( $mapped_items[ $category_id ] ) ) { $mapped_items[ $category_id ] = array(); foreach ( self::MENU_IDS as $available_menu_id ) { $mapped_items[ $category_id ][ $available_menu_id ] = array(); } } // Incorrect menu ID. if ( ! isset( $mapped_items[ $category_id ][ $menu_id ] ) ) { continue; } // Remove the item if the user cannot access it. if ( isset( $menu_item[ 'capability' ] ) && ! current_user_can( $menu_item[ 'capability' ] ) ) { continue; } $mapped_items[ $category_id ][ $menu_id ][] = $menu_item; } return $mapped_items; } /** * Add the menu to the page output. * * @param array $menu Menu items. * @return array */ public function enqueue_data( $menu ) { $data = array( 'menuItems' => array_values( self::get_items() ), 'rootBackUrl' => get_dashboard_url(), ); wp_add_inline_script( WC_ADMIN_APP, 'window.wcNavigation = ' . wp_json_encode( $data ), 'before' ); } } Navigation/CoreMenu.php 0000644 00000030141 15073234674 0011103 0 ustar 00 <?php /** * WooCommerce Navigation Core Menu * * @package Woocommerce Admin */ namespace Automattic\WooCommerce\Admin\Features\Navigation; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Admin\Features\Navigation\Menu; use Automattic\WooCommerce\Admin\Features\Navigation\Screen; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists; use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController; /** * CoreMenu class. Handles registering Core menu items. */ class CoreMenu { /** * Class instance. * * @var Menu instance */ protected static $instance = null; /** * Get class instance. */ final public static function instance() { if ( ! static::$instance ) { static::$instance = new static(); } return static::$instance; } /** * Init. */ public function init() { add_action( 'admin_menu', array( $this, 'register_post_types' ) ); // Add this after we've finished migrating menu items to avoid hiding these items. add_action( 'admin_menu', array( $this, 'add_dashboard_menu_items' ), PHP_INT_MAX ); } /** * Add registered admin settings as menu items. */ public static function get_setting_items() { // Let the Settings feature add pages to the navigation if enabled. if ( Features::is_enabled( 'settings' ) ) { return array(); } // Calling this method adds pages to the below tabs filter on non-settings pages. \WC_Admin_Settings::get_settings_pages(); $tabs = apply_filters( 'woocommerce_settings_tabs_array', array() ); $menu_items = array(); $order = 0; foreach ( $tabs as $key => $setting ) { $order += 10; $menu_items[] = ( array( 'parent' => 'woocommerce-settings', 'title' => $setting, 'capability' => 'manage_woocommerce', 'id' => 'settings-' . $key, 'url' => 'admin.php?page=wc-settings&tab=' . $key, 'order' => $order, ) ); } return $menu_items; } /** * Get unfulfilled order count * * @return array */ public static function get_shop_order_count() { $status_counts = array_map( 'wc_orders_count', array( 'processing', 'on-hold' ) ); return array_sum( $status_counts ); } /** * Get all menu categories. * * @return array */ public static function get_categories() { $analytics_enabled = Features::is_enabled( 'analytics' ); return array( array( 'title' => __( 'Orders', 'woocommerce' ), 'id' => 'woocommerce-orders', 'badge' => self::get_shop_order_count(), 'order' => 10, ), array( 'title' => __( 'Products', 'woocommerce' ), 'id' => 'woocommerce-products', 'order' => 20, ), $analytics_enabled ? array( 'title' => __( 'Analytics', 'woocommerce' ), 'id' => 'woocommerce-analytics', 'order' => 30, ) : null, $analytics_enabled ? array( 'title' => __( 'Reports', 'woocommerce' ), 'id' => 'woocommerce-reports', 'parent' => 'woocommerce-analytics', 'order' => 200, ) : null, array( 'title' => __( 'Marketing', 'woocommerce' ), 'id' => 'woocommerce-marketing', 'order' => 40, ), array( 'title' => __( 'Settings', 'woocommerce' ), 'id' => 'woocommerce-settings', 'menuId' => 'secondary', 'order' => 20, 'url' => 'admin.php?page=wc-settings', ), array( 'title' => __( 'Tools', 'woocommerce' ), 'id' => 'woocommerce-tools', 'menuId' => 'secondary', 'order' => 30, ), ); } /** * Get all menu items. * * @return array */ public static function get_items() { $order_items = self::get_order_menu_items(); $product_items = Menu::get_post_type_items( 'product', array( 'parent' => 'woocommerce-products' ) ); $product_tag_items = Menu::get_taxonomy_items( 'product_tag', array( 'parent' => 'woocommerce-products', 'order' => 30, ) ); $product_cat_items = Menu::get_taxonomy_items( 'product_cat', array( 'parent' => 'woocommerce-products', 'order' => 20, ) ); $coupon_items = Menu::get_post_type_items( 'shop_coupon', array( 'parent' => 'woocommerce-marketing' ) ); $setting_items = self::get_setting_items(); $wca_items = array(); $wca_pages = \Automattic\WooCommerce\Admin\PageController::get_instance()->get_pages(); foreach ( $wca_pages as $page ) { if ( ! isset( $page['nav_args'] ) ) { continue; } $path = isset( $page['path'] ) ? $page['path'] : null; $item = array_merge( array( 'id' => $page['id'], 'url' => $path, 'title' => $page['title'][0], 'capability' => isset( $page['capability'] ) ? $page['capability'] : 'manage_woocommerce', ), $page['nav_args'] ); // Don't allow top-level items to be added to the primary menu. if ( ! isset( $item['parent'] ) || 'woocommerce' === $item['parent'] ) { $item['menuId'] = 'plugins'; } $wca_items[] = $item; } $home_item = array(); $setup_tasks_remaining = TaskLists::setup_tasks_remaining(); if ( defined( '\Automattic\WooCommerce\Internal\Admin\Homescreen::MENU_SLUG' ) ) { $home_item = array( 'id' => 'woocommerce-home', 'title' => __( 'Home', 'woocommerce' ), 'url' => \Automattic\WooCommerce\Internal\Admin\Homescreen::MENU_SLUG, 'order' => 0, 'matchExpression' => 'page=wc-admin((?!path=).)*$', 'badge' => $setup_tasks_remaining ? $setup_tasks_remaining : null, ); } $customers_item = array(); if ( Features::is_enabled( 'analytics' ) ) { $customers_item = array( 'id' => 'woocommerce-analytics-customers', 'title' => __( 'Customers', 'woocommerce' ), 'url' => 'wc-admin&path=/customers', 'order' => 50, ); } $add_product_mvp = array(); if ( Features::is_enabled( 'new-product-management-experience' ) ) { $add_product_mvp = array( 'id' => 'woocommerce-add-product-mbp', 'title' => __( 'Add New (MVP)', 'woocommerce' ), 'url' => 'admin.php?page=wc-admin&path=/add-product', 'parent' => 'woocommerce-products', 'order' => 50, ); } return array_merge( array( $home_item, $customers_item, $order_items['all'], $order_items['new'], $product_items['all'], $product_cat_items['default'], $product_tag_items['default'], array( 'id' => 'woocommerce-product-attributes', 'title' => __( 'Attributes', 'woocommerce' ), 'url' => 'edit.php?post_type=product&page=product_attributes', 'capability' => 'manage_product_terms', 'order' => 40, 'parent' => 'woocommerce-products', 'matchExpression' => 'edit.php(?=.*[?|&]page=product_attributes(&|$|#))|edit-tags.php(?=.*[?|&]taxonomy=pa_)(?=.*[?|&]post_type=product(&|$|#))', ), array_merge( $product_items['new'], array( 'order' => 50 ) ), $coupon_items['default'], // Marketplace category. array( 'title' => __( 'Marketplace', 'woocommerce' ), 'capability' => 'manage_woocommerce', 'id' => 'woocommerce-marketplace', 'url' => 'wc-addons', 'menuId' => 'secondary', 'order' => 10, ), $add_product_mvp, ), // Tools category. self::get_tool_items(), // WooCommerce Admin items. $wca_items, // Settings category. $setting_items, // Legacy report items. self::get_legacy_report_items() ); } /** * Supplies menu items for orders. * * This varies depending on whether we are actively using traditional post type-based orders or the new custom * table-based orders. * * @return ?array */ private static function get_order_menu_items(): ?array { if ( ! wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ) { return Menu::get_post_type_items( 'shop_order', array( 'parent' => 'woocommerce-orders' ) ); } $main_orders_menu = array( 'title' => __( 'Orders', 'woocommerce' ), 'capability' => 'edit_others_shop_orders', 'id' => 'woocommerce-orders-default', 'url' => 'admin.php?page=wc-orders', 'parent' => 'woocommerce-orders', ); $all_orders_entry = $main_orders_menu; $all_orders_entry['id'] = 'woocommerce-orders-all-items'; $all_orders_entry['order'] = 10; $new_orders_entry = $main_orders_menu; $new_orders_entry['title'] = __( 'Add order', 'woocommerce' ); $new_orders_entry['id'] = 'woocommerce-orders-add-item'; $new_orders_entry['url'] = 'admin.php?page=TBD'; $new_orders_entry['order'] = 20; return array( 'default' => $main_orders_menu, 'all' => $all_orders_entry, 'new' => $new_orders_entry, ); } /** * Get items for tools category. * * @return array */ public static function get_tool_items() { $tabs = array( 'status' => __( 'System status', 'woocommerce' ), 'tools' => __( 'Utilities', 'woocommerce' ), 'logs' => __( 'Logs', 'woocommerce' ), ); $tabs = apply_filters( 'woocommerce_admin_status_tabs', $tabs ); $order = 1; $items = array( array( 'parent' => 'woocommerce-tools', 'title' => __( 'Import / Export', 'woocommerce' ), 'capability' => 'import', 'id' => 'tools-import-export', 'url' => 'import.php', 'migrate' => false, 'order' => 0, ), ); foreach ( $tabs as $key => $tab ) { $items[] = array( 'parent' => 'woocommerce-tools', 'title' => $tab, 'capability' => 'manage_woocommerce', 'id' => 'tools-' . $key, 'url' => 'wc-status&tab=' . $key, 'order' => $order, ); $order++; } return $items; } /** * Get legacy report items. * * @return array */ public static function get_legacy_report_items() { $reports = \WC_Admin_Reports::get_reports(); $menu_items = array(); $order = 0; foreach ( $reports as $key => $report ) { $menu_items[] = array( 'parent' => 'woocommerce-reports', 'title' => $report['title'], 'capability' => 'view_woocommerce_reports', 'id' => $key, 'url' => 'wc-reports&tab=' . $key, 'order' => $order, ); $order++; } return $menu_items; } /** * Register all core post types. */ public function register_post_types() { Screen::register_post_type( 'shop_order' ); Screen::register_post_type( 'product' ); Screen::register_post_type( 'shop_coupon' ); } /** * Add the dashboard items to the WP menu to create a quick-access flyout menu. */ public function add_dashboard_menu_items() { global $submenu, $menu; $mapped_items = Menu::get_mapped_menu_items(); $top_level = $mapped_items['woocommerce']; // phpcs:disable if ( ! isset( $submenu['woocommerce'] ) || empty( $top_level ) ) { return; } $menuIds = array( 'primary', 'secondary', 'favorites', ); foreach ( $menuIds as $menuId ) { foreach( $top_level[ $menuId ] as $item ) { // Skip specific categories. if ( in_array( $item['id'], array( 'woocommerce-tools', ), true ) ) { continue; } // Use the link from the first item if it's a category. if ( ! isset( $item['url'] ) ) { $categoryMenuId = $menuId === 'favorites' ? 'plugins' : $menuId; $category_items = $mapped_items[ $item['id'] ][ $categoryMenuId ]; if ( ! empty( $category_items ) ) { $first_item = $category_items[0]; $submenu['woocommerce'][] = array( $item['title'], $first_item['capability'], isset( $first_item['url'] ) ? $first_item['url'] : null, $item['title'], ); } continue; } // Show top-level items. $submenu['woocommerce'][] = array( $item['title'], $item['capability'], isset( $item['url'] ) ? $item['url'] : null, $item['title'], ); } } // phpcs:enable } /** * Get items excluded from WooCommerce menu migration. * * @return array */ public static function get_excluded_items() { $excluded_items = array( 'woocommerce', 'wc-reports', 'wc-settings', 'wc-status', ); return apply_filters( 'woocommerce_navigation_core_excluded_items', $excluded_items ); } } Navigation/Init.php 0000644 00000007714 15073234674 0010303 0 ustar 00 <?php /** * Navigation Experience * * @package Woocommerce Admin */ namespace Automattic\WooCommerce\Admin\Features\Navigation; use Automattic\WooCommerce\Internal\Admin\Survey; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Admin\Features\Navigation\Screen; use Automattic\WooCommerce\Admin\Features\Navigation\Menu; use Automattic\WooCommerce\Admin\Features\Navigation\CoreMenu; use Automattic\WooCommerce\Internal\Admin\WCAdminAssets; /** * Contains logic for the Navigation */ class Init { /** * Option name used to toggle this feature. */ const TOGGLE_OPTION_NAME = 'woocommerce_navigation_enabled'; /** * Determines if the feature has been toggled on or off. * * @var boolean */ protected static $is_updated = false; /** * Hook into WooCommerce. */ public function __construct() { add_action( 'update_option_' . self::TOGGLE_OPTION_NAME, array( $this, 'reload_page_on_toggle' ), 10, 2 ); add_action( 'woocommerce_settings_saved', array( $this, 'maybe_reload_page' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_opt_out_scripts' ) ); if ( Features::is_enabled( 'navigation' ) ) { Menu::instance()->init(); CoreMenu::instance()->init(); Screen::instance()->init(); } } /** * Add the feature toggle to the features settings. * * @deprecated 7.0 The WooCommerce Admin features are now handled by the WooCommerce features engine (see the FeaturesController class). * * @param array $features Feature sections. * @return array */ public static function add_feature_toggle( $features ) { return $features; } /** * Determine if sufficient versions are present to support Navigation feature */ public function is_nav_compatible() { include_once ABSPATH . 'wp-admin/includes/plugin.php'; $gutenberg_minimum_version = '9.0.0'; // https://github.com/WordPress/gutenberg/releases/tag/v9.0.0. $wp_minimum_version = '5.6'; $has_gutenberg = is_plugin_active( 'gutenberg/gutenberg.php' ); $gutenberg_version = $has_gutenberg ? get_plugin_data( WP_PLUGIN_DIR . '/gutenberg/gutenberg.php' )['Version'] : false; if ( $gutenberg_version && version_compare( $gutenberg_version, $gutenberg_minimum_version, '>=' ) ) { return true; } // Get unmodified $wp_version. include ABSPATH . WPINC . '/version.php'; // Strip '-src' from the version string. Messes up version_compare(). $wp_version = str_replace( '-src', '', $wp_version ); if ( version_compare( $wp_version, $wp_minimum_version, '>=' ) ) { return true; } return false; } /** * Reloads the page when the option is toggled to make sure all nav features are loaded. * * @param string $old_value Old value. * @param string $value New value. */ public static function reload_page_on_toggle( $old_value, $value ) { if ( $old_value === $value ) { return; } if ( 'yes' !== $value ) { update_option( 'woocommerce_navigation_show_opt_out', 'yes' ); } self::$is_updated = true; } /** * Reload the page if the setting has been updated. */ public static function maybe_reload_page() { if ( ! isset( $_SERVER['REQUEST_URI'] ) || ! self::$is_updated ) { return; } wp_safe_redirect( wp_unslash( $_SERVER['REQUEST_URI'] ) ); exit(); } /** * Enqueue the opt out scripts. */ public function maybe_enqueue_opt_out_scripts() { if ( get_option( 'woocommerce_navigation_show_opt_out', 'no' ) !== 'yes' ) { return; } $rtl = is_rtl() ? '.rtl' : ''; wp_enqueue_style( 'wc-admin-navigation-opt-out', WCAdminAssets::get_url( "navigation-opt-out/style{$rtl}", 'css' ), array( 'wp-components' ), WCAdminAssets::get_file_version( 'css' ) ); WCAdminAssets::register_script( 'wp-admin-scripts', 'navigation-opt-out', true ); wp_localize_script( 'wc-admin-navigation-opt-out', 'surveyData', array( 'url' => Survey::get_url( '/new-navigation-opt-out' ), ) ); delete_option( 'woocommerce_navigation_show_opt_out' ); } } Navigation/Favorites.php 0000644 00000005171 15073234674 0011335 0 ustar 00 <?php /** * WooCommerce Navigation Favorite * * @package Woocommerce Navigation */ namespace Automattic\WooCommerce\Admin\Features\Navigation; use Automattic\WooCommerce\Internal\Admin\WCAdminUser; /** * Contains logic for the WooCommerce Navigation menu. */ class Favorites { /** * Array index of menu capability. * * @var int */ const META_NAME = 'navigation_favorites'; /** * Favorites instance. * * @var Favorites|null */ protected static $instance = null; /** * Get class instance. */ final public static function instance() { if ( ! static::$instance ) { static::$instance = new static(); } return static::$instance; } /** * Set given favorites string to the user meta data. * * @param string|number $user_id User id. * @param array $favorites Array of favorite values to set. */ private static function set_meta_value( $user_id, $favorites ) { WCAdminUser::update_user_data_field( $user_id, self::META_NAME, wp_json_encode( (array) $favorites ) ); } /** * Add item to favorites * * @param string $item_id Identifier of item to add. * @param string|number $user_id Identifier of user to add to. * @return WP_Error|Boolean Throws exception if item already exists. */ public static function add_item( $item_id, $user_id ) { $all_favorites = self::get_all( $user_id ); if ( in_array( $item_id, $all_favorites, true ) ) { return new \WP_Error( 'woocommerce_favorites_already_exists', __( 'Favorite already exists', 'woocommerce' ) ); } $all_favorites[] = $item_id; self::set_meta_value( $user_id, $all_favorites ); return true; } /** * Remove item from favorites * * @param string $item_id Identifier of item to remove. * @param string|number $user_id Identifier of user to remove from. * @return \WP_Error|Boolean Throws exception if item does not exist. */ public static function remove_item( $item_id, $user_id ) { $all_favorites = self::get_all( $user_id ); if ( ! in_array( $item_id, $all_favorites, true ) ) { return new \WP_Error( 'woocommerce_favorites_does_not_exist', __( 'Favorite item not found', 'woocommerce' ) ); } $remaining = array_values( array_diff( $all_favorites, [ $item_id ] ) ); self::set_meta_value( $user_id, $remaining ); return true; } /** * Get all registered favorites. * * @param string|number $user_id Identifier of user to query. * @return WP_Error|Array */ public static function get_all( $user_id ) { $response = WCAdminUser::get_user_data_field( $user_id, self::META_NAME ); return $response ? json_decode( $response, true ) : array(); } } PaymentGatewaySuggestions/EvaluateSuggestion.php 0000644 00000001534 15073234674 0016303 0 ustar 00 <?php /** * Evaluates the spec and returns a status. */ namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions; defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RuleEvaluator; /** * Evaluates the spec and returns the evaluated suggestion. */ class EvaluateSuggestion { /** * Evaluates the spec and returns the suggestion. * * @param object|array $spec The suggestion to evaluate. * @return object The evaluated suggestion. */ public static function evaluate( $spec ) { $rule_evaluator = new RuleEvaluator(); $suggestion = is_array( $spec ) ? (object) $spec : clone $spec; if ( isset( $suggestion->is_visible ) ) { $is_visible = $rule_evaluator->evaluate( $suggestion->is_visible ); $suggestion->is_visible = $is_visible; } return $suggestion; } } PaymentGatewaySuggestions/DefaultPaymentGateways.php 0000644 00000106464 15073234674 0017124 0 ustar 00 <?php /** * Gets a list of fallback methods if remote fetching is disabled. */ namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions; defined( 'ABSPATH' ) || exit; /** * Default Payment Gateways */ class DefaultPaymentGateways { /** * This is the default priority for countries that are not in the $recommendation_priority_map. * Priority is used to determine which payment gateway to recommend first. * The lower the number, the higher the priority. * * @var array */ private static $recommendation_priority = array( 'woocommerce_payments' => 1, 'woocommerce_payments:with-in-person-payments' => 1, 'woocommerce_payments:without-in-person-payments' => 1, 'stripe' => 2, 'woo-mercado-pago-custom' => 3, // PayPal Payments. 'ppcp-gateway' => 4, 'mollie_wc_gateway_banktransfer' => 5, 'razorpay' => 5, 'payfast' => 5, 'payubiz' => 6, 'square_credit_card' => 6, 'klarna_payments' => 6, // Klarna Checkout. 'kco' => 6, 'paystack' => 6, 'eway' => 7, 'amazon_payments_advanced' => 7, 'affirm' => 8, 'afterpay' => 9, 'zipmoney' => 10, 'payoneer-checkout' => 11, ); /** * Get default specs. * * @return array Default specs. */ public static function get_all() { $payment_gateways = array( array( 'id' => 'affirm', 'title' => __( 'Affirm', 'woocommerce' ), 'content' => __( 'Affirm’s tailored Buy Now Pay Later programs remove price as a barrier, turning browsers into buyers, increasing average order value, and expanding your customer base.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/affirm.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/affirm.png', 'plugins' => array(), 'external_link' => 'https://woo.com/products/woocommerce-gateway-affirm', 'is_visible' => array( self::get_rules_for_countries( array( 'US', 'CA', ) ), ), 'category_other' => array(), 'category_additional' => array( 'US', 'CA', ), ), array( 'id' => 'afterpay', 'title' => __( 'Afterpay', 'woocommerce' ), 'content' => __( 'Afterpay allows customers to receive products immediately and pay for purchases over four installments, always interest-free.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/afterpay.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/afterpay.png', 'plugins' => array( 'afterpay-gateway-for-woocommerce' ), 'is_visible' => array( self::get_rules_for_countries( array( 'US', 'CA', 'AU', ) ), ), 'category_other' => array(), 'category_additional' => array( 'US', 'CA', 'AU', ), ), array( 'id' => 'amazon_payments_advanced', 'title' => __( 'Amazon Pay', 'woocommerce' ), 'content' => __( 'Enable a familiar, fast checkout for hundreds of millions of active Amazon customers globally.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/amazonpay.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/amazonpay.png', 'plugins' => array( 'woocommerce-gateway-amazon-payments-advanced' ), 'is_visible' => array( self::get_rules_for_countries( array( 'US', 'AT', 'BE', 'CY', 'DK', 'ES', 'FR', 'DE', 'GB', 'HU', 'IE', 'IT', 'LU', 'NL', 'PT', 'SL', 'SE', 'JP', ) ), ), 'category_other' => array(), 'category_additional' => array( 'US', 'AT', 'BE', 'CY', 'DK', 'ES', 'FR', 'DE', 'GB', 'HU', 'IE', 'IT', 'LU', 'NL', 'PT', 'SL', 'SE', 'JP', ), ), array( 'id' => 'bacs', 'title' => __( 'Direct bank transfer', 'woocommerce' ), 'content' => __( 'Take payments via bank transfer.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/bacs.svg', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/bacs.png', 'is_visible' => array( self::get_rules_for_cbd( false ), ), 'is_offline' => true, ), array( 'id' => 'cod', 'title' => __( 'Cash on delivery', 'woocommerce' ), 'content' => __( 'Take payments in cash upon delivery.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/cod.svg', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/cod.png', 'is_visible' => array( self::get_rules_for_cbd( false ), ), 'is_offline' => true, ), array( 'id' => 'eway', 'title' => __( 'Eway', 'woocommerce' ), 'content' => __( 'The Eway extension for WooCommerce allows you to take credit card payments directly on your store without redirecting your customers to a third party site to make payment.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/eway.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/eway.png', 'plugins' => array( 'woocommerce-gateway-eway' ), 'is_visible' => array( self::get_rules_for_countries( array( 'NZ', 'HK', 'SG', 'AU', ) ), self::get_rules_for_cbd( false ), ), 'category_other' => array( 'NZ', 'HK', 'SG', 'AU', ), 'category_additional' => array(), ), array( 'id' => 'kco', 'title' => __( 'Klarna Checkout', 'woocommerce' ), 'content' => __( 'Choose the payment that you want, pay now, pay later or slice it. No credit card numbers, no passwords, no worries.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/klarna-black.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/klarna.png', 'plugins' => array( 'klarna-checkout-for-woocommerce' ), 'is_visible' => array( self::get_rules_for_countries( array( 'NO', 'SE', 'FI', ) ), self::get_rules_for_cbd( false ), ), 'category_other' => array( 'NO', 'SE', 'FI', ), 'category_additional' => array(), ), array( 'id' => 'klarna_payments', 'title' => __( 'Klarna Payments', 'woocommerce' ), 'content' => __( 'Choose the payment that you want, pay now, pay later or slice it. No credit card numbers, no passwords, no worries.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/klarna-black.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/klarna.png', 'plugins' => array( 'klarna-payments-for-woocommerce' ), 'is_visible' => array( self::get_rules_for_countries( array( 'MX', 'US', 'CA', 'AT', 'BE', 'CH', 'DK', 'ES', 'FI', 'FR', 'DE', 'GB', 'IT', 'NL', 'NO', 'PL', 'SE', 'NZ', 'AU', ) ), self::get_rules_for_cbd( false ), ), 'category_other' => array(), 'category_additional' => array( 'MX', 'US', 'CA', 'AT', 'BE', 'CH', 'DK', 'ES', 'FI', 'FR', 'DE', 'GB', 'IT', 'NL', 'NO', 'PL', 'SE', 'NZ', 'AU', ), ), array( 'id' => 'mollie_wc_gateway_banktransfer', 'title' => __( 'Mollie', 'woocommerce' ), 'content' => __( 'Effortless payments by Mollie: Offer global and local payment methods, get onboarded in minutes, and supported in your language.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/mollie.svg', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/mollie.png', 'plugins' => array( 'mollie-payments-for-woocommerce' ), 'is_visible' => array( self::get_rules_for_countries( array( 'AT', 'BE', 'CH', 'ES', 'FI', 'FR', 'DE', 'GB', 'IT', 'NL', 'PL', ) ), ), 'category_other' => array( 'AT', 'BE', 'CH', 'ES', 'FI', 'FR', 'DE', 'GB', 'IT', 'NL', 'PL', ), 'category_additional' => array(), ), array( 'id' => 'payfast', 'title' => __( 'Payfast', 'woocommerce' ), 'content' => __( 'The Payfast extension for WooCommerce enables you to accept payments by Credit Card and EFT via one of South Africa’s most popular payment gateways. No setup fees or monthly subscription costs. Selecting this extension will configure your store to use South African rands as the selected currency.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/payfast.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/payfast.png', 'plugins' => array( 'woocommerce-payfast-gateway' ), 'is_visible' => array( self::get_rules_for_countries( array( 'ZA' ) ), self::get_rules_for_cbd( false ), ), 'category_other' => array( 'ZA' ), 'category_additional' => array(), ), array( 'id' => 'payoneer-checkout', 'title' => __( 'Payoneer Checkout', 'woocommerce' ), 'content' => __( 'Payoneer Checkout is the next generation of payment processing platforms, giving merchants around the world the solutions and direction they need to succeed in today’s hyper-competitive global market.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/payoneer.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/payoneer.png', 'plugins' => array( 'payoneer-checkout' ), 'is_visible' => array( self::get_rules_for_countries( array( 'HK', 'CN', ) ), ), 'category_other' => array(), 'category_additional' => array( 'HK', 'CN', ), ), array( 'id' => 'paystack', 'title' => __( 'Paystack', 'woocommerce' ), 'content' => __( 'Paystack helps African merchants accept one-time and recurring payments online with a modern, safe, and secure payment gateway.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/paystack.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/paystack.png', 'plugins' => array( 'woo-paystack' ), 'is_visible' => array( self::get_rules_for_countries( array( 'ZA', 'GH', 'NG' ) ), self::get_rules_for_cbd( false ), ), 'category_other' => array( 'ZA', 'GH', 'NG' ), 'category_additional' => array(), ), array( 'id' => 'payubiz', 'title' => __( 'PayU for WooCommerce', 'woocommerce' ), 'content' => __( 'Enable PayU’s exclusive plugin for WooCommerce to start accepting payments in 100+ payment methods available in India including credit cards, debit cards, UPI, & more!', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/payu.svg', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/payu.png', 'plugins' => array( 'payu-india' ), 'is_visible' => array( (object) array( 'type' => 'base_location_country', 'value' => 'IN', 'operation' => '=', ), self::get_rules_for_cbd( false ), ), 'category_other' => array( 'IN' ), 'category_additional' => array(), ), array( 'id' => 'ppcp-gateway', 'title' => __( 'PayPal Payments', 'woocommerce' ), 'content' => __( "Safe and secure payments using credit cards or your customer's PayPal account.", 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/paypal.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/paypal.png', 'plugins' => array( 'woocommerce-paypal-payments' ), 'is_visible' => array( self::get_rules_for_countries( array( 'US', 'CA', 'MX', 'BR', 'AR', 'CL', 'CO', 'EC', 'PE', 'UY', 'VE', 'AT', 'BE', 'BG', 'HR', 'CH', 'CY', 'CZ', 'DK', 'EE', 'ES', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SK', 'SL', 'SE', 'AU', 'NZ', 'HK', 'JP', 'SG', 'CN', 'ID', 'IN', ) ), self::get_rules_for_cbd( false ), ), 'category_other' => array( 'US', 'CA', 'MX', 'BR', 'AR', 'CL', 'CO', 'EC', 'PE', 'UY', 'VE', 'AT', 'BE', 'BG', 'HR', 'CH', 'CY', 'CZ', 'DK', 'EE', 'ES', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SK', 'SL', 'SE', 'AU', 'NZ', 'HK', 'JP', 'SG', 'CN', 'ID', ), 'category_additional' => array( 'US', 'CA', 'ZA', 'NG', 'GH', 'EC', 'VE', 'AR', 'CL', 'CO', 'PE', 'UY', 'MX', 'BR', 'AT', 'BE', 'BG', 'HR', 'CH', 'CY', 'CZ', 'DK', 'EE', 'ES', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SK', 'SL', 'SE', 'AU', 'NZ', 'HK', 'JP', 'SG', 'CN', 'ID', 'IN', ), ), array( 'id' => 'razorpay', 'title' => __( 'Razorpay', 'woocommerce' ), 'content' => __( 'The official Razorpay extension for WooCommerce allows you to accept credit cards, debit cards, netbanking, wallet, and UPI payments.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/razorpay.svg', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/razorpay.png', 'plugins' => array( 'woo-razorpay' ), 'is_visible' => array( (object) array( 'type' => 'base_location_country', 'value' => 'IN', 'operation' => '=', ), self::get_rules_for_cbd( false ), ), 'category_other' => array( 'IN' ), 'category_additional' => array(), ), array( 'id' => 'square_credit_card', 'title' => __( 'Square', 'woocommerce' ), 'content' => __( 'Securely accept credit and debit cards with one low rate, no surprise fees (custom rates available). Sell online and in store and track sales and inventory in one place.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/square-black.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/square.png', 'plugins' => array( 'woocommerce-square' ), 'is_visible' => array( (object) array( 'type' => 'or', 'operands' => (object) array( array( self::get_rules_for_countries( array( 'US' ) ), self::get_rules_for_cbd( true ), ), array( self::get_rules_for_countries( array( 'US', 'CA', 'IE', 'ES', 'FR', 'GB', 'AU', 'JP', ) ), (object) array( 'type' => 'or', 'operands' => (object) array( self::get_rules_for_selling_venues( array( 'brick-mortar', 'brick-mortar-other' ) ), self::get_rules_selling_offline(), ), ), ), ), ), ), 'category_other' => array( 'US', 'CA', 'IE', 'ES', 'FR', 'GB', 'AU', 'JP', ), 'category_additional' => array(), ), array( 'id' => 'stripe', 'title' => __( ' Stripe', 'woocommerce' ), 'content' => __( 'Accept debit and credit cards in 135+ currencies, methods such as Alipay, and one-touch checkout with Apple Pay.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/stripe.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/stripe.png', 'plugins' => array( 'woocommerce-gateway-stripe' ), 'is_visible' => array( // https://stripe.com/global. self::get_rules_for_countries( array( 'US', 'CA', 'MX', 'BR', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DK', 'EE', 'ES', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SK', 'SL', 'SE', 'AU', 'NZ', 'HK', 'JP', 'SG', 'ID', 'IN', ) ), self::get_rules_for_cbd( false ), ), 'category_other' => array( 'US', 'CA', 'MX', 'BR', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DK', 'EE', 'ES', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SK', 'SL', 'SE', 'AU', 'NZ', 'HK', 'JP', 'SG', 'ID', 'IN', ), 'category_additional' => array(), ), array( 'id' => 'woo-mercado-pago-custom', 'title' => __( 'Mercado Pago Checkout Pro & Custom', 'woocommerce' ), 'content' => __( 'Accept credit and debit cards, offline (cash or bank transfer) and logged-in payments with money in Mercado Pago. Safe and secure payments with the leading payment processor in LATAM.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/mercadopago.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/mercadopago.png', 'plugins' => array( 'woocommerce-mercadopago' ), 'is_visible' => array( self::get_rules_for_countries( array( 'AR', 'CL', 'CO', 'EC', 'PE', 'UY', 'MX', 'BR', ) ), ), 'is_local_partner' => true, 'category_other' => array( 'AR', 'CL', 'CO', 'EC', 'PE', 'UY', 'MX', 'BR', ), 'category_additional' => array(), ), // This is for backwards compatibility only (WC < 5.10.0-dev or WCA < 2.9.0-dev). array( 'id' => 'woocommerce_payments', 'title' => __( 'WooPayments', 'woocommerce' ), 'content' => __( 'Manage transactions without leaving your WordPress Dashboard. Only with WooPayments.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg', 'plugins' => array( 'woocommerce-payments' ), 'description' => __( 'With WooPayments, you can securely accept major cards, Apple Pay, and payments in over 100 currencies. Track cash flow and manage recurring revenue directly from your store’s dashboard - with no setup costs or monthly fees.', 'woocommerce' ), 'is_visible' => array( self::get_rules_for_cbd( false ), self::get_rules_for_countries( self::get_wcpay_countries() ), (object) array( 'type' => 'plugin_version', 'plugin' => 'woocommerce', 'version' => '5.10.0-dev', 'operator' => '<', ), (object) array( 'type' => 'or', 'operands' => (object) array( (object) array( 'type' => 'not', 'operand' => [ (object) array( 'type' => 'plugins_activated', 'plugins' => [ 'woocommerce-admin' ], ), ], ), (object) array( 'type' => 'plugin_version', 'plugin' => 'woocommerce-admin', 'version' => '2.9.0-dev', 'operator' => '<', ), ), ), ), ), array( 'id' => 'woocommerce_payments:without-in-person-payments', 'title' => __( 'WooPayments', 'woocommerce' ), 'content' => __( 'Manage transactions without leaving your WordPress Dashboard. Only with WooPayments.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg', 'plugins' => array( 'woocommerce-payments' ), 'description' => __( 'With WooPayments, you can securely accept major cards, Apple Pay, and payments in over 100 currencies. Track cash flow and manage recurring revenue directly from your store’s dashboard - with no setup costs or monthly fees.', 'woocommerce' ), 'is_visible' => array( self::get_rules_for_cbd( false ), self::get_rules_for_countries( array_diff( self::get_wcpay_countries(), array( 'US', 'CA' ) ) ), (object) array( 'type' => 'or', // Older versions of WooCommerce Admin require the ID to be `woocommerce-payments` to show the suggestion card. 'operands' => (object) array( (object) array( 'type' => 'plugin_version', 'plugin' => 'woocommerce-admin', 'version' => '2.9.0-dev', 'operator' => '>=', ), (object) array( 'type' => 'plugin_version', 'plugin' => 'woocommerce', 'version' => '5.10.0-dev', 'operator' => '>=', ), ), ), ), ), // This is the same as the above, but with a different description for countries that support in-person payments such as US and CA. array( 'id' => 'woocommerce_payments:with-in-person-payments', 'title' => __( 'WooPayments', 'woocommerce' ), 'content' => __( 'Manage transactions without leaving your WordPress Dashboard. Only with WooPayments.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg', 'plugins' => array( 'woocommerce-payments' ), 'description' => __( 'With WooPayments, you can securely accept major cards, Apple Pay, and payments in over 100 currencies – with no setup costs or monthly fees – and you can now accept in-person payments with the Woo mobile app.', 'woocommerce' ), 'is_visible' => array( self::get_rules_for_cbd( false ), self::get_rules_for_countries( array( 'US', 'CA' ) ), (object) array( 'type' => 'or', // Older versions of WooCommerce Admin require the ID to be `woocommerce-payments` to show the suggestion card. 'operands' => (object) array( (object) array( 'type' => 'plugin_version', 'plugin' => 'woocommerce-admin', 'version' => '2.9.0-dev', 'operator' => '>=', ), (object) array( 'type' => 'plugin_version', 'plugin' => 'woocommerce', 'version' => '5.10.0-dev', 'operator' => '>=', ), ), ), ), ), array( 'id' => 'zipmoney', 'title' => __( 'Zip Co - Buy Now, Pay Later', 'woocommerce' ), 'content' => __( 'Give your customers the power to pay later, interest free and watch your sales grow.', 'woocommerce' ), 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/zipco.png', 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/zipco.png', 'plugins' => array( 'zipmoney-payments-woocommerce' ), 'is_visible' => array( self::get_rules_for_countries( array( 'US', 'NZ', 'AU', ) ), ), 'category_other' => array(), 'category_additional' => array( 'US', 'NZ', 'AU', ), ), ); $base_location = wc_get_base_location(); $country = $base_location['country']; foreach ( $payment_gateways as $index => $payment_gateway ) { $payment_gateways[ $index ]['recommendation_priority'] = self::get_recommendation_priority( $payment_gateway['id'], $country ); } return $payment_gateways; } /** * Get array of countries supported by WCPay depending on feature flag. * * @return array Array of countries. */ public static function get_wcpay_countries() { return array( 'US', 'PR', 'AU', 'CA', 'CY', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'IE', 'IT', 'LU', 'LT', 'LV', 'NO', 'NZ', 'MT', 'AT', 'BE', 'NL', 'PL', 'PT', 'CH', 'HK', 'SI', 'SK', 'SG', 'BG', 'CZ', 'HR', 'HU', 'RO', 'SE', 'JP', 'AE' ); } /** * Get rules that match the store base location to one of the provided countries. * * @param array $countries Array of countries to match. * @return object Rules to match. */ public static function get_rules_for_countries( $countries ) { $rules = array(); foreach ( $countries as $country ) { $rules[] = (object) array( 'type' => 'base_location_country', 'value' => $country, 'operation' => '=', ); } return (object) array( 'type' => 'or', 'operands' => $rules, ); } /** * Get rules that match the store's selling venues. * * @param array $selling_venues Array of venues to match. * @return object Rules to match. */ public static function get_rules_for_selling_venues( $selling_venues ) { $rules = array(); foreach ( $selling_venues as $venue ) { $rules[] = (object) array( 'type' => 'option', 'transformers' => array( (object) array( 'use' => 'dot_notation', 'arguments' => (object) array( 'path' => 'selling_venues', ), ), ), 'option_name' => 'woocommerce_onboarding_profile', 'operation' => '=', 'value' => $venue, 'default' => array(), ); } return (object) array( 'type' => 'or', 'operands' => $rules, ); } /** * Get rules for when selling offline for core profiler. * * @return object Rules to match. */ public static function get_rules_selling_offline() { return (object) array( 'type' => 'option', 'transformers' => array( (object) array( 'use' => 'dot_notation', 'arguments' => (object) array( 'path' => 'selling_online_answer', ), ), ), 'option_name' => 'woocommerce_onboarding_profile', 'operation' => 'contains', 'value' => 'no_im_selling_offline', 'default' => array(), ); } /** * Get default rules for CBD based on given argument. * * @param bool $should_have Whether or not the store should have CBD as an industry (true) or not (false). * @return array Rules to match. */ public static function get_rules_for_cbd( $should_have ) { return (object) array( 'type' => 'option', 'transformers' => array( (object) array( 'use' => 'dot_notation', 'arguments' => (object) array( 'path' => 'industry', ), ), (object) array( 'use' => 'array_column', 'arguments' => (object) array( 'key' => 'slug', ), ), ), 'option_name' => 'woocommerce_onboarding_profile', 'operation' => $should_have ? 'contains' : '!contains', 'value' => 'cbd-other-hemp-derived-products', 'default' => array(), ); } /** * Get recommendation priority for a given payment gateway by id and country. * If country is not supported, return null. * * @param string $gateway_id Payment gateway id. * @param string $country_code Store country code. * @return int|null Priority. Priority is 0-indexed, so 0 is the highest priority. */ private static function get_recommendation_priority( $gateway_id, $country_code ) { $recommendation_priority_map = array( 'US' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'square_credit_card', 'amazon_payments_advanced', 'affirm', 'afterpay', 'klarna_payments', 'zipmoney', ], 'CA' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'square_credit_card', 'affirm', 'afterpay', 'klarna_payments', ], 'AT' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'klarna_payments', 'amazon_payments_advanced', ], 'BE' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'klarna_payments', 'amazon_payments_advanced', ], 'BG' => [ 'stripe', 'ppcp-gateway' ], 'HR' => [ 'ppcp-gateway' ], 'CH' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'klarna_payments', ], 'CY' => [ 'stripe', 'ppcp-gateway', 'amazon_payments_advanced' ], 'CZ' => [ 'stripe', 'ppcp-gateway' ], 'DK' => [ 'stripe', 'ppcp-gateway', 'klarna_payments', 'amazon_payments_advanced', ], 'EE' => [ 'stripe', 'ppcp-gateway' ], 'ES' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'square_credit_card', 'klarna_payments', 'amazon_payments_advanced', ], 'FI' => [ 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'kco', 'klarna_payments', ], 'FR' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'square_credit_card', 'klarna_payments', 'amazon_payments_advanced', ], 'DE' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'klarna_payments', 'amazon_payments_advanced', ], 'GB' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'square_credit_card', 'klarna_payments', 'amazon_payments_advanced', ], 'GR' => [ 'stripe', 'ppcp-gateway' ], 'HU' => [ 'stripe', 'ppcp-gateway', 'amazon_payments_advanced' ], 'IE' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'square_credit_card', 'amazon_payments_advanced', ], 'IT' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'klarna_payments', 'amazon_payments_advanced', ], 'LV' => [ 'stripe', 'ppcp-gateway' ], 'LT' => [ 'stripe', 'ppcp-gateway' ], 'LU' => [ 'stripe', 'ppcp-gateway', 'amazon_payments_advanced' ], 'MT' => [ 'stripe', 'ppcp-gateway' ], 'NL' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'klarna_payments', 'amazon_payments_advanced', ], 'NO' => [ 'stripe', 'ppcp-gateway', 'kco', 'klarna_payments' ], 'PL' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'mollie_wc_gateway_banktransfer', 'klarna_payments', ], 'PT' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'amazon_payments_advanced', ], 'RO' => [ 'stripe', 'ppcp-gateway' ], 'SK' => [ 'stripe', 'ppcp-gateway' ], 'SL' => [ 'stripe', 'ppcp-gateway', 'amazon_payments_advanced' ], 'SE' => [ 'stripe', 'ppcp-gateway', 'kco', 'klarna_payments', 'amazon_payments_advanced', ], 'MX' => [ 'stripe', 'woo-mercado-pago-custom', 'ppcp-gateway', 'klarna_payments', ], 'BR' => [ 'stripe', 'woo-mercado-pago-custom', 'ppcp-gateway' ], 'AR' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], 'BO' => [], 'CL' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], 'CO' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], 'EC' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], 'FK' => [], 'GF' => [], 'GY' => [], 'PY' => [], 'PE' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], 'SR' => [], 'UY' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], 'VE' => [ 'ppcp-gateway' ], 'AU' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'square_credit_card', 'eway', 'afterpay', 'klarna_payments', 'zipmoney', ], 'NZ' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'eway', 'klarna_payments', 'zipmoney', ], 'HK' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'eway', 'payoneer-checkout', ], 'JP' => [ 'stripe', 'ppcp-gateway', 'square_credit_card', 'amazon_payments_advanced', ], 'SG' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'eway' ], 'CN' => [ 'ppcp-gateway', 'payoneer-checkout' ], 'FJ' => [], 'GU' => [], 'ID' => [ 'stripe', 'ppcp-gateway' ], 'IN' => [ 'stripe', 'razorpay', 'payubiz', 'ppcp-gateway' ], 'ZA' => [ 'payfast', 'paystack' ], 'NG' => [ 'paystack' ], 'GH' => [ 'paystack' ], ); // If the country code is not in the list, return default priority. if ( ! isset( $recommendation_priority_map[ $country_code ] ) ) { return self::get_default_recommendation_priority( $gateway_id ); } $index = array_search( $gateway_id, $recommendation_priority_map[ $country_code ], true ); // If the gateway is not in the list, return the last index + 1. if ( false === $index ) { return count( $recommendation_priority_map[ $country_code ] ); } return $index; } /** * Get the default recommendation priority for a payment gateway. * This is used when a country is not in the $recommendation_priority_map array. * * @param string $id Payment gateway id. * @return int Priority. */ private static function get_default_recommendation_priority( $id ) { if ( ! $id || ! array_key_exists( $id, self::$recommendation_priority ) ) { return null; } return self::$recommendation_priority[ $id ]; } } PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php 0000644 00000002154 15073234674 0023007 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions; use Automattic\WooCommerce\Admin\DataSourcePoller; /** * Specs data source poller class for payment gateway suggestions. */ class PaymentGatewaySuggestionsDataSourcePoller extends DataSourcePoller { /** * Data Source Poller ID. */ const ID = 'payment_gateway_suggestions'; /** * Default data sources array. */ const DATA_SOURCES = array( 'https://woocommerce.com/wp-json/wccom/payment-gateway-suggestions/1.0/suggestions.json', ); /** * Class instance. * * @var Analytics instance */ protected static $instance = null; /** * Get class instance. */ public static function get_instance() { if ( ! self::$instance ) { // Add country query param to data sources. $base_location = wc_get_base_location(); $data_sources = array_map( function( $url ) use ( $base_location ) { return add_query_arg( 'country', $base_location['country'], $url ); }, self::DATA_SOURCES ); self::$instance = new self( self::ID, $data_sources ); } return self::$instance; } } PaymentGatewaySuggestions/PaymentGatewaysController.php 0000644 00000010671 15073234674 0017655 0 ustar 00 <?php /** * Logic for extending WC_REST_Payment_Gateways_Controller. */ namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions; use Automattic\WooCommerce\Admin\Features\TransientNotices; defined( 'ABSPATH' ) || exit; /** * PaymentGateway class */ class PaymentGatewaysController { /** * Initialize payment gateway changes. */ public static function init() { add_filter( 'woocommerce_rest_prepare_payment_gateway', array( __CLASS__, 'extend_response' ), 10, 3 ); add_filter( 'admin_init', array( __CLASS__, 'possibly_do_connection_return_action' ) ); add_action( 'woocommerce_admin_payment_gateway_connection_return', array( __CLASS__, 'handle_successfull_connection' ) ); } /** * Add necessary fields to REST API response. * * @param WP_REST_Response $response Response data. * @param WC_Payment_Gateway $gateway Payment gateway object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public static function extend_response( $response, $gateway, $request ) { $data = $response->get_data(); $data['needs_setup'] = $gateway->needs_setup(); $data['post_install_scripts'] = self::get_post_install_scripts( $gateway ); $data['settings_url'] = method_exists( $gateway, 'get_settings_url' ) ? $gateway->get_settings_url() : admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . strtolower( $gateway->id ) ); $return_url = wc_admin_url( '&task=payments&connection-return=' . strtolower( $gateway->id ) . '&_wpnonce=' . wp_create_nonce( 'connection-return' ) ); $data['connection_url'] = method_exists( $gateway, 'get_connection_url' ) ? $gateway->get_connection_url( $return_url ) : null; $data['setup_help_text'] = method_exists( $gateway, 'get_setup_help_text' ) ? $gateway->get_setup_help_text() : null; $data['required_settings_keys'] = method_exists( $gateway, 'get_required_settings_keys' ) ? $gateway->get_required_settings_keys() : array(); $response->set_data( $data ); return $response; } /** * Get payment gateway scripts for post-install. * * @param WC_Payment_Gateway $gateway Payment gateway object. * @return array Install scripts. */ public static function get_post_install_scripts( $gateway ) { $scripts = array(); $wp_scripts = wp_scripts(); $handles = method_exists( $gateway, 'get_post_install_script_handles' ) ? $gateway->get_post_install_script_handles() : array(); foreach ( $handles as $handle ) { if ( isset( $wp_scripts->registered[ $handle ] ) ) { $scripts[] = $wp_scripts->registered[ $handle ]; } } return $scripts; } /** * Call an action after a gating has been successfully returned. */ public static function possibly_do_connection_return_action() { if ( ! isset( $_GET['page'] ) || 'wc-admin' !== $_GET['page'] || ! isset( $_GET['task'] ) || 'payments' !== $_GET['task'] || ! isset( $_GET['connection-return'] ) || ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( wc_clean( wp_unslash( $_GET['_wpnonce'] ) ), 'connection-return' ) ) { return; } $gateway_id = sanitize_text_field( wp_unslash( $_GET['connection-return'] ) ); do_action( 'woocommerce_admin_payment_gateway_connection_return', $gateway_id ); } /** * Handle a successful gateway connection. * * @param string $gateway_id Gateway ID. */ public static function handle_successfull_connection( $gateway_id ) { // phpcs:disable WordPress.Security.NonceVerification if ( ! isset( $_GET['success'] ) || 1 !== intval( $_GET['success'] ) ) { return; } // phpcs:enable WordPress.Security.NonceVerification $payment_gateways = WC()->payment_gateways()->payment_gateways(); $payment_gateway = isset( $payment_gateways[ $gateway_id ] ) ? $payment_gateways[ $gateway_id ] : null; if ( ! $payment_gateway ) { return; } $payment_gateway->update_option( 'enabled', 'yes' ); TransientNotices::add( array( 'user_id' => get_current_user_id(), 'id' => 'payment-gateway-connection-return-' . str_replace( ',', '-', $gateway_id ), 'status' => 'success', 'content' => sprintf( /* translators: the title of the payment gateway */ __( '%s connected successfully', 'woocommerce' ), $payment_gateway->method_title ), ) ); wc_admin_record_tracks_event( 'tasklist_payment_connect_method', array( 'payment_method' => $gateway_id, ) ); wp_safe_redirect( wc_admin_url() ); } } PaymentGatewaySuggestions/Init.php 0000644 00000006101 15073234674 0013363 0 ustar 00 <?php /** * Handles running payment gateway suggestion specs */ namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions; defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\DefaultPaymentGateways; use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\PaymentGatewaysController; /** * Remote Payment Methods engine. * This goes through the specs and gets eligible payment gateways. */ class Init { /** * Option name for dismissed payment method suggestions. */ const RECOMMENDED_PAYMENT_PLUGINS_DISMISS_OPTION = 'woocommerce_setting_payments_recommendations_hidden'; /** * Constructor. */ public function __construct() { PaymentGatewaysController::init(); add_action( 'update_option_woocommerce_default_country', array( $this, 'delete_specs_transient' ) ); } /** * Go through the specs and run them. * * @param array|null $specs payment suggestion spec array. * @return array */ public static function get_suggestions( array $specs = null ) { $suggestions = array(); if ( null === $specs ) { $specs = self::get_specs(); } foreach ( $specs as $spec ) { try { $suggestion = EvaluateSuggestion::evaluate( $spec ); $suggestions[] = $suggestion; // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch } catch ( \Throwable $e ) { // Ignore errors. } } return array_values( array_filter( $suggestions, function( $suggestion ) { return ! property_exists( $suggestion, 'is_visible' ) || $suggestion->is_visible; } ) ); } /** * Delete the specs transient. */ public static function delete_specs_transient() { PaymentGatewaySuggestionsDataSourcePoller::get_instance()->delete_specs_transient(); } /** * Get specs or fetch remotely if they don't exist. */ public static function get_specs() { if ( 'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) ) { return apply_filters( 'woocommerce_admin_payment_gateway_suggestion_specs', DefaultPaymentGateways::get_all() ); } $specs = PaymentGatewaySuggestionsDataSourcePoller::get_instance()->get_specs_from_data_sources(); // Fetch specs if they don't yet exist. if ( false === $specs || ! is_array( $specs ) || 0 === count( $specs ) ) { return apply_filters( 'woocommerce_admin_payment_gateway_suggestion_specs', DefaultPaymentGateways::get_all() ); } return apply_filters( 'woocommerce_admin_payment_gateway_suggestion_specs', $specs ); } /** * Check if suggestions should be shown in the settings screen. * * @return bool */ public static function should_display() { if ( 'yes' === get_option( self::RECOMMENDED_PAYMENT_PLUGINS_DISMISS_OPTION, 'no' ) ) { return false; } if ( 'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) ) { return false; } return apply_filters( 'woocommerce_allow_payment_recommendations', true ); } /** * Dismiss the suggestions. */ public static function dismiss() { return update_option( self::RECOMMENDED_PAYMENT_PLUGINS_DISMISS_OPTION, 'yes' ); } } Features.php 0000644 00000027274 15073234674 0007062 0 ustar 00 <?php /** * Features loader for features developed in WooCommerce Admin. */ namespace Automattic\WooCommerce\Admin\Features; use Automattic\WooCommerce\Admin\PageController; use Automattic\WooCommerce\Internal\Admin\Loader; use Automattic\WooCommerce\Internal\Admin\WCAdminAssets; /** * Features Class. */ class Features { /** * Class instance. * * @var Loader instance */ protected static $instance = null; /** * Optional features * * @var array */ protected static $optional_features = array( 'navigation' => array( 'default' => 'no' ), 'settings' => array( 'default' => 'no' ), 'analytics' => array( 'default' => 'yes' ), 'remote-inbox-notifications' => array( 'default' => 'yes' ), ); /** * Beta features * * @var array */ protected static $beta_features = array( 'navigation', 'new-product-management-experience', 'settings', ); /** * Get class instance. */ public static function get_instance() { if ( ! self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Constructor. */ public function __construct() { $this->register_internal_class_aliases(); // Load feature before WooCommerce update hooks. add_action( 'init', array( __CLASS__, 'load_features' ), 4 ); add_action( 'admin_enqueue_scripts', array( __CLASS__, 'maybe_load_beta_features_modal' ) ); add_action( 'admin_enqueue_scripts', array( __CLASS__, 'load_scripts' ), 15 ); add_filter( 'admin_body_class', array( __CLASS__, 'add_admin_body_classes' ) ); add_filter( 'update_option_woocommerce_allow_tracking', array( __CLASS__, 'maybe_disable_features' ), 10, 2 ); } /** * Gets a build configured array of enabled WooCommerce Admin features/sections, but does not respect optionally disabled features. * * @return array Enabled Woocommerce Admin features/sections. */ public static function get_features() { return apply_filters( 'woocommerce_admin_features', array() ); } /** * Gets the optional feature options as an associative array that can be toggled on or off. * * @return array */ public static function get_optional_feature_options() { $features = []; foreach ( array_keys( self::$optional_features ) as $optional_feature_key ) { $feature_class = self::get_feature_class( $optional_feature_key ); if ( $feature_class ) { $features[ $optional_feature_key ] = $feature_class::TOGGLE_OPTION_NAME; } } return $features; } /** * Returns if a specific wc-admin feature exists in the current environment. * * @param string $feature Feature slug. * @return bool Returns true if the feature exists. */ public static function exists( $feature ) { $features = self::get_features(); return in_array( $feature, $features, true ); } /** * Get the feature class as a string. * * @param string $feature Feature name. * @return string|null */ public static function get_feature_class( $feature ) { $feature = str_replace( '-', '', ucwords( strtolower( $feature ), '-' ) ); $feature_class = 'Automattic\\WooCommerce\\Admin\\Features\\' . $feature; if ( class_exists( $feature_class ) ) { return $feature_class; } // Handle features contained in subdirectory. if ( class_exists( $feature_class . '\\Init' ) ) { return $feature_class . '\\Init'; } return null; } /** * Class loader for enabled WooCommerce Admin features/sections. */ public static function load_features() { $features = self::get_features(); foreach ( $features as $feature ) { $feature_class = self::get_feature_class( $feature ); if ( $feature_class ) { new $feature_class(); } } } /** * Gets a build configured array of enabled WooCommerce Admin respecting optionally disabled features. * * @return array Enabled Woocommerce Admin features/sections. */ public static function get_available_features() { $features = self::get_features(); $optional_feature_keys = array_keys( self::$optional_features ); $optional_features_unavailable = []; /** * Filter allowing WooCommerce Admin optional features to be disabled. * * @param bool $disabled False. */ if ( apply_filters( 'woocommerce_admin_disabled', false ) ) { return array_values( array_diff( $features, $optional_feature_keys ) ); } foreach ( $optional_feature_keys as $optional_feature_key ) { $feature_class = self::get_feature_class( $optional_feature_key ); if ( $feature_class ) { $default = isset( self::$optional_features[ $optional_feature_key ]['default'] ) ? self::$optional_features[ $optional_feature_key ]['default'] : 'no'; // Check if the feature is currently being enabled, if it is continue. /* phpcs:disable WordPress.Security.NonceVerification */ $feature_option = $feature_class::TOGGLE_OPTION_NAME; if ( isset( $_POST[ $feature_option ] ) && '1' === $_POST[ $feature_option ] ) { continue; } if ( 'yes' !== get_option( $feature_class::TOGGLE_OPTION_NAME, $default ) ) { $optional_features_unavailable[] = $optional_feature_key; } } } return array_values( array_diff( $features, $optional_features_unavailable ) ); } /** * Check if a feature is enabled. * * @param string $feature Feature slug. * @return bool */ public static function is_enabled( $feature ) { $available_features = self::get_available_features(); return in_array( $feature, $available_features, true ); } /** * Enable a toggleable optional feature. * * @param string $feature Feature name. * @return bool */ public static function enable( $feature ) { $features = self::get_optional_feature_options(); if ( isset( $features[ $feature ] ) ) { update_option( $features[ $feature ], 'yes' ); return true; } return false; } /** * Disable a toggleable optional feature. * * @param string $feature Feature name. * @return bool */ public static function disable( $feature ) { $features = self::get_optional_feature_options(); if ( isset( $features[ $feature ] ) ) { update_option( $features[ $feature ], 'no' ); return true; } return false; } /** * Disable features when opting out of tracking. * * @param string $old_value Old value. * @param string $value New value. */ public static function maybe_disable_features( $old_value, $value ) { if ( 'yes' === $value ) { return; } foreach ( self::$beta_features as $feature ) { self::disable( $feature ); } } /** * Adds the Features section to the advanced tab of WooCommerce Settings * * @deprecated 7.0 The WooCommerce Admin features are now handled by the WooCommerce features engine (see the FeaturesController class). * * @param array $sections Sections. * @return array */ public static function add_features_section( $sections ) { return $sections; } /** * Adds the Features settings. * * @deprecated 7.0 The WooCommerce Admin features are now handled by the WooCommerce features engine (see the FeaturesController class). * * @param array $settings Settings. * @param string $current_section Current section slug. * @return array */ public static function add_features_settings( $settings, $current_section ) { return $settings; } /** * Conditionally loads the beta features tracking modal. * * @param string $hook Page hook. */ public static function maybe_load_beta_features_modal( $hook ) { if ( 'woocommerce_page_wc-settings' !== $hook || ! isset( $_GET['tab'] ) || 'advanced' !== $_GET['tab'] || // phpcs:ignore CSRF ok. ! isset( $_GET['section'] ) || 'features' !== $_GET['section'] // phpcs:ignore CSRF ok. ) { return; } $tracking_enabled = get_option( 'woocommerce_allow_tracking', 'no' ); if ( empty( self::$beta_features ) ) { return; } if ( 'yes' === $tracking_enabled ) { return; } $rtl = is_rtl() ? '.rtl' : ''; wp_enqueue_style( 'wc-admin-beta-features-tracking-modal', WCAdminAssets::get_url( "beta-features-tracking-modal/style{$rtl}", 'css' ), array( 'wp-components' ), WCAdminAssets::get_file_version( 'css' ) ); wp_enqueue_script( 'wc-admin-beta-features-tracking-modal', WCAdminAssets::get_url( 'wp-admin-scripts/beta-features-tracking-modal', 'js' ), array( 'wp-i18n', 'wp-element', WC_ADMIN_APP ), WCAdminAssets::get_file_version( 'js' ), true ); } /** * Loads the required scripts on the correct pages. */ public static function load_scripts() { if ( ! PageController::is_admin_or_embed_page() ) { return; } $features = self::get_features(); $enabled_features = array(); foreach ( $features as $key ) { $enabled_features[ $key ] = self::is_enabled( $key ); } wp_add_inline_script( WC_ADMIN_APP, 'window.wcAdminFeatures = ' . wp_json_encode( $enabled_features ), 'before' ); } /** * Adds body classes to the main wp-admin wrapper, allowing us to better target elements in specific scenarios. * * @param string $admin_body_class Body class to add. */ public static function add_admin_body_classes( $admin_body_class = '' ) { if ( ! PageController::is_admin_or_embed_page() ) { return $admin_body_class; } $classes = explode( ' ', trim( $admin_body_class ) ); $features = self::get_features(); foreach ( $features as $feature_key ) { $classes[] = sanitize_html_class( 'woocommerce-feature-enabled-' . $feature_key ); } $admin_body_class = implode( ' ', array_unique( $classes ) ); return " $admin_body_class "; } /** * Alias internal features classes to make them backward compatible. * We've moved our feature classes to src-internal as part of merging this * repository with WooCommerce Core to form a monorepo. * See https://wp.me/p90Yrv-2HY for details. */ private function register_internal_class_aliases() { $aliases = array( // new class => original class (this will be aliased). 'Automattic\WooCommerce\Internal\Admin\WCPayPromotion\Init' => 'Automattic\WooCommerce\Admin\Features\WcPayPromotion\Init', 'Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions\Init' => 'Automattic\WooCommerce\Admin\Features\RemoteFreeExtensions\Init', 'Automattic\WooCommerce\Internal\Admin\ActivityPanels' => 'Automattic\WooCommerce\Admin\Features\ActivityPanels', 'Automattic\WooCommerce\Internal\Admin\Analytics' => 'Automattic\WooCommerce\Admin\Features\Analytics', 'Automattic\WooCommerce\Internal\Admin\Coupons' => 'Automattic\WooCommerce\Admin\Features\Coupons', 'Automattic\WooCommerce\Internal\Admin\CouponsMovedTrait' => 'Automattic\WooCommerce\Admin\Features\CouponsMovedTrait', 'Automattic\WooCommerce\Internal\Admin\CustomerEffortScoreTracks' => 'Automattic\WooCommerce\Admin\Features\CustomerEffortScoreTracks', 'Automattic\WooCommerce\Internal\Admin\Homescreen' => 'Automattic\WooCommerce\Admin\Features\Homescreen', 'Automattic\WooCommerce\Internal\Admin\Marketing' => 'Automattic\WooCommerce\Admin\Features\Marketing', 'Automattic\WooCommerce\Internal\Admin\MobileAppBanner' => 'Automattic\WooCommerce\Admin\Features\MobileAppBanner', 'Automattic\WooCommerce\Internal\Admin\RemoteInboxNotifications' => 'Automattic\WooCommerce\Admin\Features\RemoteInboxNotifications', 'Automattic\WooCommerce\Internal\Admin\SettingsNavigationFeature' => 'Automattic\WooCommerce\Admin\Features\Settings', 'Automattic\WooCommerce\Internal\Admin\ShippingLabelBanner' => 'Automattic\WooCommerce\Admin\Features\ShippingLabelBanner', 'Automattic\WooCommerce\Internal\Admin\ShippingLabelBannerDisplayRules' => 'Automattic\WooCommerce\Admin\Features\ShippingLabelBannerDisplayRules', 'Automattic\WooCommerce\Internal\Admin\WcPayWelcomePage' => 'Automattic\WooCommerce\Admin\Features\WcPayWelcomePage', ); foreach ( $aliases as $new_class => $orig_class ) { class_alias( $new_class, $orig_class ); } } } OnboardingTasks/DeprecatedOptions.php 0000644 00000005000 15073234674 0013767 0 ustar 00 <?php /** * Filters for maintaining backwards compatibility with deprecated options. */ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\TaskList; use WC_Install; /** * DeprecatedOptions class. */ class DeprecatedOptions { /** * Initialize. */ public static function init() { add_filter( 'pre_option_woocommerce_task_list_hidden', array( __CLASS__, 'get_deprecated_options' ), 10, 2 ); add_filter( 'pre_option_woocommerce_extended_task_list_hidden', array( __CLASS__, 'get_deprecated_options' ), 10, 2 ); add_action( 'pre_update_option_woocommerce_task_list_hidden', array( __CLASS__, 'update_deprecated_options' ), 10, 3 ); add_action( 'pre_update_option_woocommerce_extended_task_list_hidden', array( __CLASS__, 'update_deprecated_options' ), 10, 3 ); } /** * Get the values from the correct source when attempting to retrieve deprecated options. * * @param string $pre_option Pre option value. * @param string $option Option name. * @return string */ public static function get_deprecated_options( $pre_option, $option ) { if ( defined( 'WC_INSTALLING' ) && WC_INSTALLING === true ) { return $pre_option; } $hidden = get_option( 'woocommerce_task_list_hidden_lists', array() ); switch ( $option ) { case 'woocommerce_task_list_hidden': return in_array( 'setup', $hidden, true ) ? 'yes' : 'no'; case 'woocommerce_extended_task_list_hidden': return in_array( 'extended', $hidden, true ) ? 'yes' : 'no'; } } /** * Updates the new option names when deprecated options are updated. * This is a temporary fallback until we can fully remove the old task list components. * * @param string $value New value. * @param string $old_value Old value. * @param string $option Option name. * @return string */ public static function update_deprecated_options( $value, $old_value, $option ) { switch ( $option ) { case 'woocommerce_task_list_hidden': $task_list = TaskLists::get_list( 'setup' ); if ( ! $task_list ) { return; } $update = 'yes' === $value ? $task_list->hide() : $task_list->unhide(); delete_option( 'woocommerce_task_list_hidden' ); return false; case 'woocommerce_extended_task_list_hidden': $task_list = TaskLists::get_list( 'extended' ); if ( ! $task_list ) { return; } $update = 'yes' === $value ? $task_list->hide() : $task_list->unhide(); delete_option( 'woocommerce_extended_task_list_hidden' ); return false; } } } OnboardingTasks/DeprecatedExtendedTask.php 0000644 00000006322 15073234674 0014727 0 ustar 00 <?php /** * A temporary class for creating tasks on the fly from deprecated tasks. */ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; /** * DeprecatedExtendedTask class. */ class DeprecatedExtendedTask extends Task { /** * ID. * * @var string */ public $id = ''; /** * Additional info. * * @var string|null */ public $additional_info = ''; /** * Content. * * @var string */ public $content = ''; /** * Whether the task is complete or not. * * @var boolean */ public $is_complete = false; /** * Snoozeable. * * @var boolean */ public $is_snoozeable = false; /** * Dismissable. * * @var boolean */ public $is_dismissable = false; /** * Whether the store is capable of viewing the task. * * @var bool */ public $can_view = true; /** * Level. * * @var int */ public $level = 3; /** * Time. * * @var string|null */ public $time; /** * Title. * * @var string */ public $title = ''; /** * Constructor. * * @param TaskList $task_list Parent task list. * @param array $args Array of task args. */ public function __construct( $task_list, $args ) { parent::__construct( $task_list ); $task_args = wp_parse_args( $args, array( 'id' => null, 'is_dismissable' => false, 'is_snoozeable' => false, 'can_view' => true, 'level' => 3, 'additional_info' => null, 'content' => '', 'title' => '', 'is_complete' => false, 'time' => null, ) ); $this->id = $task_args['id']; $this->additional_info = $task_args['additional_info']; $this->content = $task_args['content']; $this->is_complete = $task_args['is_complete']; $this->is_dismissable = $task_args['is_dismissable']; $this->is_snoozeable = $task_args['is_snoozeable']; $this->can_view = $task_args['can_view']; $this->level = $task_args['level']; $this->time = $task_args['time']; $this->title = $task_args['title']; } /** * ID. * * @return string */ public function get_id() { return $this->id; } /** * Additional info. * * @return string */ public function get_additional_info() { return $this->additional_info; } /** * Content. * * @return string */ public function get_content() { return $this->content; } /** * Level. * * @return int */ public function get_level() { return $this->level; } /** * Title * * @return string */ public function get_title() { return $this->title; } /** * Time * * @return string|null */ public function get_time() { return $this->time; } /** * Check if a task is snoozeable. * * @return bool */ public function is_snoozeable() { return $this->is_snoozeable; } /** * Check if a task is dismissable. * * @return bool */ public function is_dismissable() { return $this->is_dismissable; } /** * Check if a task is dismissable. * * @return bool */ public function is_complete() { return $this->is_complete; } /** * Check if a task is dismissable. * * @return bool */ public function can_view() { return $this->can_view; } } OnboardingTasks/TaskListSection.php 0000644 00000004466 15073234674 0013455 0 ustar 00 <?php /** * Handles storage and retrieval of a task list section */ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; /** * Task List section class. * * @deprecated 7.2.0 */ class TaskListSection { /** * Title. * * @var string */ public $id = ''; /** * Title. * * @var string */ public $title = ''; /** * Description. * * @var string */ public $description = ''; /** * Image. * * @var string */ public $image = ''; /** * Tasks. * * @var array */ public $task_names = array(); /** * Parent task list. * * @var TaskList */ protected $task_list; /** * Constructor * * @param array $data Task list data. * @param TaskList|null $task_list Parent task list. */ public function __construct( $data = array(), $task_list = null ) { $defaults = array( 'id' => '', 'title' => '', 'description' => '', 'image' => '', 'tasks' => array(), ); $data = wp_parse_args( $data, $defaults ); $this->task_list = $task_list; $this->id = $data['id']; $this->title = $data['title']; $this->description = $data['description']; $this->image = $data['image']; $this->task_names = $data['task_names']; } /** * Returns if section is complete. * * @return boolean; */ private function is_complete() { $complete = true; foreach ( $this->task_names as $task_name ) { if ( null !== $this->task_list && isset( $this->task_list->task_class_id_map[ $task_name ] ) ) { $task = $this->task_list->get_task( $this->task_list->task_class_id_map[ $task_name ] ); if ( $task->can_view() && ! $task->is_complete() ) { $complete = false; break; } } } return $complete; } /** * Get the list for use in JSON. * * @return array */ public function get_json() { return array( 'id' => $this->id, 'title' => $this->title, 'description' => $this->description, 'image' => $this->image, 'tasks' => array_map( function( $task_name ) { if ( null !== $this->task_list && isset( $this->task_list->task_class_id_map[ $task_name ] ) ) { return $this->task_list->task_class_id_map[ $task_name ]; } return ''; }, $this->task_names ), 'isComplete' => $this->is_complete(), ); } } OnboardingTasks/TaskLists.php 0000644 00000025177 15073234674 0012315 0 ustar 00 <?php /** * Handles storage and retrieval of task lists */ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\DeprecatedExtendedTask; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\ReviewShippingOptions; /** * Task Lists class. */ class TaskLists { /** * Class instance. * * @var TaskLists instance */ protected static $instance = null; /** * An array of all registered lists. * * @var array */ protected static $lists = array(); /** * Boolean value to indicate if default tasks have been added. * * @var boolean */ protected static $default_tasks_loaded = false; /** * The contents of this array is used in init_tasks() to run their init() methods. * If the classes do not have an init() method then nothing is executed. * Beyond that, adding tasks to this list has no effect, see init_default_lists() for the list of tasks. * that are added for each task list. * * @var array */ const DEFAULT_TASKS = array( 'StoreDetails', 'Products', 'WooCommercePayments', 'Payments', 'Tax', 'Shipping', 'Marketing', 'Appearance', 'AdditionalPayments', 'ReviewShippingOptions', 'GetMobileApp', ); /** * Get class instance. */ final public static function instance() { if ( ! static::$instance ) { static::$instance = new static(); } return static::$instance; } /** * Initialize the task lists. */ public static function init() { self::init_default_lists(); add_action( 'admin_init', array( __CLASS__, 'set_active_task' ), 5 ); add_action( 'init', array( __CLASS__, 'init_tasks' ) ); add_action( 'admin_menu', array( __CLASS__, 'menu_task_count' ) ); add_filter( 'woocommerce_admin_shared_settings', array( __CLASS__, 'task_list_preloaded_settings' ), 20 ); } /** * Check if an experiment is the treatment or control. * * @param string $name Name prefix of experiment. * @return bool */ public static function is_experiment_treatment( $name ) { $anon_id = isset( $_COOKIE['tk_ai'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['tk_ai'] ) ) : ''; $allow_tracking = 'yes' === get_option( 'woocommerce_allow_tracking' ); $abtest = new \WooCommerce\Admin\Experimental_Abtest( $anon_id, 'woocommerce', $allow_tracking ); $date = new \DateTime(); $date->setTimeZone( new \DateTimeZone( 'UTC' ) ); $experiment_name = sprintf( '%s_%s_%s', $name, $date->format( 'Y' ), $date->format( 'm' ) ); return $abtest->get_variation( $experiment_name ) === 'treatment'; } /** * Initialize default lists. */ public static function init_default_lists() { $tasks = array( 'CustomizeStore', 'StoreDetails', 'Products', 'Appearance', 'WooCommercePayments', 'Payments', 'Tax', 'Shipping', 'Marketing', ); if ( Features::is_enabled( 'core-profiler' ) ) { $key = array_search( 'StoreDetails', $tasks, true ); if ( false !== $key ) { unset( $tasks[ $key ] ); } } // Remove the old Personalize your store task if the new CustomizeStore is enabled. $task_to_remove = Features::is_enabled( 'customize-store' ) ? 'Appearance' : 'CustomizeStore'; $store_customisation_task_index = array_search( $task_to_remove, $tasks, true ); if ( false !== $store_customisation_task_index ) { unset( $tasks[ $store_customisation_task_index ] ); } self::add_list( array( 'id' => 'setup', 'title' => __( 'Get ready to start selling', 'woocommerce' ), 'tasks' => $tasks, 'display_progress_header' => true, 'event_prefix' => 'tasklist_', 'options' => array( 'use_completed_title' => true, ), 'visible' => true, ) ); self::add_list( array( 'id' => 'extended', 'title' => __( 'Things to do next', 'woocommerce' ), 'sort_by' => array( array( 'key' => 'is_complete', 'order' => 'asc', ), array( 'key' => 'level', 'order' => 'asc', ), ), 'tasks' => array( 'AdditionalPayments', 'GetMobileApp', ), ) ); if ( Features::is_enabled( 'shipping-smart-defaults' ) ) { self::add_task( 'extended', new ReviewShippingOptions( self::get_list( 'extended' ) ) ); // Tasklist that will never be shown in homescreen, // used for having tasks that are accessed by other means. self::add_list( array( 'id' => 'secret_tasklist', 'hidden_id' => 'setup', 'tasks' => array( 'ExperimentalShippingRecommendation', ), 'event_prefix' => 'secret_tasklist_', 'visible' => false, ) ); } if ( has_filter( 'woocommerce_admin_experimental_onboarding_tasklists' ) ) { /** * Filter to override default task lists. * * @since 7.4 * @param array $lists Array of tasklists. */ self::$lists = apply_filters( 'woocommerce_admin_experimental_onboarding_tasklists', self::$lists ); } } /** * Initialize tasks. */ public static function init_tasks() { foreach ( self::DEFAULT_TASKS as $task ) { $class = 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\\' . $task; if ( ! method_exists( $class, 'init' ) ) { continue; } $class::init(); } } /** * Temporarily store the active task to persist across page loads when necessary. * Most tasks do not need this. */ public static function set_active_task() { if ( ! isset( $_GET[ Task::ACTIVE_TASK_TRANSIENT ] ) || ! current_user_can( 'manage_woocommerce' ) ) { // phpcs:ignore csrf ok. return; } $referer = wp_get_referer(); if ( ! $referer || 0 !== strpos( $referer, wc_admin_url() ) ) { return; } $task_id = sanitize_title_with_dashes( wp_unslash( $_GET[ Task::ACTIVE_TASK_TRANSIENT ] ) ); // phpcs:ignore csrf ok. $task = self::get_task( $task_id ); if ( ! $task ) { return; } $task->set_active(); } /** * Add a task list. * * @param array $args Task list properties. * @return \WP_Error|TaskList */ public static function add_list( $args ) { if ( isset( self::$lists[ $args['id'] ] ) ) { return new \WP_Error( 'woocommerce_task_list_exists', __( 'Task list ID already exists', 'woocommerce' ) ); } self::$lists[ $args['id'] ] = new TaskList( $args ); return self::$lists[ $args['id'] ]; } /** * Add task to a given task list. * * @param string $list_id List ID to add the task to. * @param Task $task Task object. * * @return \WP_Error|Task */ public static function add_task( $list_id, $task ) { if ( ! isset( self::$lists[ $list_id ] ) ) { return new \WP_Error( 'woocommerce_task_list_invalid_list', __( 'Task list ID does not exist', 'woocommerce' ) ); } self::$lists[ $list_id ]->add_task( $task ); } /** * Add default extended task lists. * * @param array $extended_tasks list of extended tasks. */ public static function maybe_add_extended_tasks( $extended_tasks ) { $tasks = $extended_tasks ?? array(); foreach ( self::$lists as $task_list ) { if ( 'extended' !== substr( $task_list->id, 0, 8 ) ) { continue; } foreach ( $tasks as $args ) { $task = new DeprecatedExtendedTask( $task_list, $args ); $task_list->add_task( $task ); } } } /** * Get all task lists. * * @return array */ public static function get_lists() { return self::$lists; } /** * Get all task lists. * * @param array $ids list of task list ids. * @return array */ public static function get_lists_by_ids( $ids ) { return array_filter( self::$lists, function( $list ) use ( $ids ) { return in_array( $list->get_list_id(), $ids, true ); } ); } /** * Get all task list ids. * * @return array */ public static function get_list_ids() { return array_keys( self::$lists ); } /** * Clear all task lists. */ public static function clear_lists() { self::$lists = array(); return self::$lists; } /** * Get visible task lists. */ public static function get_visible() { return array_filter( self::get_lists(), function ( $task_list ) { return $task_list->is_visible(); } ); } /** * Retrieve a task list by ID. * * @param String $id Task list ID. * * @return TaskList|null */ public static function get_list( $id ) { if ( isset( self::$lists[ $id ] ) ) { return self::$lists[ $id ]; } return null; } /** * Retrieve single task. * * @param String $id Task ID. * @param String $task_list_id Task list ID. * * @return Object */ public static function get_task( $id, $task_list_id = null ) { $task_list = $task_list_id ? self::get_list( $task_list_id ) : null; if ( $task_list_id && ! $task_list ) { return null; } $tasks_to_search = $task_list ? $task_list->tasks : array_reduce( self::get_lists(), function ( $all, $curr ) { return array_merge( $all, $curr->tasks ); }, array() ); foreach ( $tasks_to_search as $task ) { if ( $id === $task->get_id() ) { return $task; } } return null; } /** * Return number of setup tasks remaining * * @return number */ public static function setup_tasks_remaining() { $setup_list = self::get_list( 'setup' ); if ( ! $setup_list || $setup_list->is_hidden() || $setup_list->is_complete() ) { return; } $remaining_tasks = array_values( array_filter( $setup_list->get_viewable_tasks(), function( $task ) { return ! $task->is_complete(); } ) ); return count( $remaining_tasks ); } /** * Add badge to homescreen menu item for remaining tasks */ public static function menu_task_count() { global $submenu; $tasks_count = self::setup_tasks_remaining(); if ( ! $tasks_count || ! isset( $submenu['woocommerce'] ) ) { return; } foreach ( $submenu['woocommerce'] as $key => $menu_item ) { if ( 0 === strpos( $menu_item[0], _x( 'Home', 'Admin menu name', 'woocommerce' ) ) ) { $submenu['woocommerce'][ $key ][0] .= ' <span class="awaiting-mod update-plugins remaining-tasks-badge woocommerce-task-list-remaining-tasks-badge"><span class="count-' . esc_attr( $tasks_count ) . '">' . absint( $tasks_count ) . '</span></span>'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited break; } } } /** * Add visible list ids to component settings. * * @param array $settings Component settings. * * @return array */ public static function task_list_preloaded_settings( $settings ) { $settings['visibleTaskListIds'] = array_keys( self::get_visible() ); return $settings; } } OnboardingTasks/Init.php 0000644 00000002116 15073234674 0011263 0 ustar 00 <?php /** * WooCommerce Onboarding Tasks */ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\DeprecatedOptions; /** * Contains the logic for completing onboarding tasks. */ class Init { /** * Class instance. * * @var OnboardingTasks instance */ protected static $instance = null; /** * Get class instance. */ public static function get_instance() { if ( ! self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ public function __construct() { DeprecatedOptions::init(); TaskLists::init(); } /** * Get task item data for settings filter. * * @return array */ public static function get_settings() { $settings = array(); $wc_pay_is_connected = false; if ( class_exists( '\WC_Payments' ) ) { $wc_payments_gateway = \WC_Payments::get_gateway(); $wc_pay_is_connected = method_exists( $wc_payments_gateway, 'is_connected' ) ? $wc_payments_gateway->is_connected() : false; } return $settings; } } OnboardingTasks/TaskList.php 0000644 00000022162 15073234674 0012121 0 ustar 00 <?php /** * Handles storage and retrieval of a task list */ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\WooCommerce\Admin\WCAdminHelper; /** * Task List class. */ class TaskList { /** * Task traits. */ use TaskTraits; /** * Option name hidden task lists. */ const HIDDEN_OPTION = 'woocommerce_task_list_hidden_lists'; /** * Option name of completed task lists. */ const COMPLETED_OPTION = 'woocommerce_task_list_completed_lists'; /** * Option name of hidden reminder bar. */ const REMINDER_BAR_HIDDEN_OPTION = 'woocommerce_task_list_reminder_bar_hidden'; /** * ID. * * @var string */ public $id = ''; /** * ID. * * @var string */ public $hidden_id = ''; /** * ID. * * @var boolean */ public $display_progress_header = false; /** * Title. * * @var string */ public $title = ''; /** * Tasks. * * @var array */ public $tasks = array(); /** * Sort keys. * * @var array */ public $sort_by = array(); /** * Event prefix. * * @var string|null */ public $event_prefix = null; /** * Task list visibility. * * @var boolean */ public $visible = true; /** * Array of custom options. * * @var array */ public $options = array(); /** * Array of TaskListSection. * * @deprecated 7.2.0 * * @var array */ private $sections = array(); /** * Key value map of task class and id used for sections. * * @deprecated 7.2.0 * * @var array */ public $task_class_id_map = array(); /** * Constructor * * @param array $data Task list data. */ public function __construct( $data = array() ) { $defaults = array( 'id' => null, 'hidden_id' => null, 'title' => '', 'tasks' => array(), 'sort_by' => array(), 'event_prefix' => null, 'options' => array(), 'visible' => true, 'display_progress_header' => false, ); $data = wp_parse_args( $data, $defaults ); $this->id = $data['id']; $this->hidden_id = $data['hidden_id']; $this->title = $data['title']; $this->sort_by = $data['sort_by']; $this->event_prefix = $data['event_prefix']; $this->options = $data['options']; $this->visible = $data['visible']; $this->display_progress_header = $data['display_progress_header']; foreach ( $data['tasks'] as $task_name ) { $class = 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\\' . $task_name; $task = new $class( $this ); $this->add_task( $task ); } $this->possibly_remove_reminder_bar(); } /** * Check if the task list is hidden. * * @return bool */ public function is_hidden() { $hidden = get_option( self::HIDDEN_OPTION, array() ); return in_array( $this->hidden_id ? $this->hidden_id : $this->id, $hidden, true ); } /** * Check if the task list is visible. * * @return bool */ public function is_visible() { if ( ! $this->visible || ! count( $this->get_viewable_tasks() ) > 0 ) { return false; } return ! $this->is_hidden(); } /** * Hide the task list. * * @return bool */ public function hide() { if ( $this->is_hidden() ) { return; } $viewable_tasks = $this->get_viewable_tasks(); $completed_count = array_reduce( $viewable_tasks, function( $total, $task ) { return $task->is_complete() ? $total + 1 : $total; }, 0 ); $this->record_tracks_event( 'completed', array( 'action' => 'remove_card', 'completed_task_count' => $completed_count, 'incomplete_task_count' => count( $viewable_tasks ) - $completed_count, ) ); $hidden = get_option( self::HIDDEN_OPTION, array() ); $hidden[] = $this->hidden_id ? $this->hidden_id : $this->id; $this->maybe_set_default_layout( $hidden ); return update_option( self::HIDDEN_OPTION, array_unique( $hidden ) ); } /** * Sets the default homepage layout to two_columns if "setup" tasklist is completed or hidden. * * @param array $completed_or_hidden_tasklist_ids Array of tasklist ids. */ public function maybe_set_default_layout( $completed_or_hidden_tasklist_ids ) { if ( in_array( 'setup', $completed_or_hidden_tasklist_ids, true ) ) { update_option( 'woocommerce_default_homepage_layout', 'two_columns' ); } } /** * Undo hiding of the task list. * * @return bool */ public function unhide() { $hidden = get_option( self::HIDDEN_OPTION, array() ); $hidden = array_diff( $hidden, array( $this->hidden_id ? $this->hidden_id : $this->id ) ); return update_option( self::HIDDEN_OPTION, $hidden ); } /** * Check if all viewable tasks are complete. * * @return bool */ public function is_complete() { foreach ( $this->get_viewable_tasks() as $viewable_task ) { if ( $viewable_task->is_complete() === false ) { return false; } } return true; } /** * Check if a task list has previously been marked as complete. * * @return bool */ public function has_previously_completed() { $complete = get_option( self::COMPLETED_OPTION, array() ); return in_array( $this->get_list_id(), $complete, true ); } /** * Add task to the task list. * * @param Task $task Task class. */ public function add_task( $task ) { if ( ! is_subclass_of( $task, 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task' ) ) { return new \WP_Error( 'woocommerce_task_list_invalid_task', __( 'Task is not a subclass of `Task`', 'woocommerce' ) ); } if ( array_search( $task, $this->tasks, true ) ) { return; } $this->tasks[] = $task; } /** * Get only visible tasks in list. * * @param string $task_id id of task. * @return Task */ public function get_task( $task_id ) { return current( array_filter( $this->tasks, function( $task ) use ( $task_id ) { return $task->get_id() === $task_id; } ) ); } /** * Get only visible tasks in list. * * @return array */ public function get_viewable_tasks() { return array_values( array_filter( $this->tasks, function( $task ) { return $task->can_view(); } ) ); } /** * Get task list sections. * * @deprecated 7.2.0 * * @return array */ public function get_sections() { wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' ); return $this->sections; } /** * Track list completion of viewable tasks. */ public function possibly_track_completion() { if ( ! $this->is_complete() ) { return; } if ( $this->has_previously_completed() ) { return; } $completed_lists = get_option( self::COMPLETED_OPTION, array() ); $completed_lists[] = $this->get_list_id(); update_option( self::COMPLETED_OPTION, $completed_lists ); $this->maybe_set_default_layout( $completed_lists ); $this->record_tracks_event( 'tasks_completed' ); } /** * Sorts the attached tasks array. * * @param array $sort_by list of columns with sort order. * @return TaskList returns $this, for chaining. */ public function sort_tasks( $sort_by = array() ) { $sort_by = count( $sort_by ) > 0 ? $sort_by : $this->sort_by; if ( 0 !== count( $sort_by ) ) { usort( $this->tasks, function( $a, $b ) use ( $sort_by ) { return Task::sort( $a, $b, $sort_by ); } ); } return $this; } /** * Prefix event for track event naming. * * @param string $event_name Event name. * @return string */ public function prefix_event( $event_name ) { if ( null !== $this->event_prefix ) { return $this->event_prefix . $event_name; } return $this->get_list_id() . '_tasklist_' . $event_name; } /** * Returns option to keep completed task list. * * @return string */ public function get_keep_completed_task_list() { return get_option( 'woocommerce_task_list_keep_completed', 'no' ); } /** * Remove reminder bar four weeks after store creation. */ public static function possibly_remove_reminder_bar() { $bar_hidden = get_option( self::REMINDER_BAR_HIDDEN_OPTION, 'no' ); $active_for_four_weeks = WCAdminHelper::is_wc_admin_active_for( WEEK_IN_SECONDS * 4 ); if ( 'yes' === $bar_hidden || ! $active_for_four_weeks ) { return; } update_option( self::REMINDER_BAR_HIDDEN_OPTION, 'yes' ); } /** * Get the list for use in JSON. * * @return array */ public function get_json() { $this->possibly_track_completion(); $tasks_json = array(); foreach ( $this->tasks as $task ) { $json = $task->get_json(); if ( $json['canView'] ) { $tasks_json[] = $json; } } return array( 'id' => $this->get_list_id(), 'title' => $this->title, 'isHidden' => $this->is_hidden(), 'isVisible' => $this->is_visible(), 'isComplete' => $this->is_complete(), 'tasks' => $tasks_json, 'eventPrefix' => $this->prefix_event( '' ), 'displayProgressHeader' => $this->display_progress_header, 'keepCompletedTaskList' => $this->get_keep_completed_task_list(), ); } } OnboardingTasks/Task.php 0000644 00000030111 15073234674 0011256 0 ustar 00 <?php /** * Handles task related methods. */ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; use Automattic\WooCommerce\Internal\Admin\WCAdminUser; /** * Task class. */ abstract class Task { /** * Task traits. */ use TaskTraits; /** * Name of the dismiss option. * * @var string */ const DISMISSED_OPTION = 'woocommerce_task_list_dismissed_tasks'; /** * Name of the snooze option. * * @var string * * @deprecated 7.2.0 */ const SNOOZED_OPTION = 'woocommerce_task_list_remind_me_later_tasks'; /** * Name of the actioned option. * * @var string */ const ACTIONED_OPTION = 'woocommerce_task_list_tracked_completed_actions'; /** * Option name of completed tasks. * * @var string */ const COMPLETED_OPTION = 'woocommerce_task_list_tracked_completed_tasks'; /** * Name of the active task transient. * * @var string */ const ACTIVE_TASK_TRANSIENT = 'wc_onboarding_active_task'; /** * Parent task list. * * @var TaskList */ protected $task_list; /** * Duration to milisecond mapping. * * @var string */ protected $duration_to_ms = array( 'day' => DAY_IN_SECONDS * 1000, 'hour' => HOUR_IN_SECONDS * 1000, 'week' => WEEK_IN_SECONDS * 1000, ); /** * Constructor * * @param TaskList|null $task_list Parent task list. */ public function __construct( $task_list = null ) { $this->task_list = $task_list; } /** * ID. * * @return string */ abstract public function get_id(); /** * Title. * * @return string */ abstract public function get_title(); /** * Content. * * @return string */ abstract public function get_content(); /** * Time. * * @return string */ abstract public function get_time(); /** * Parent ID. * * @return string */ public function get_parent_id() { if ( ! $this->task_list ) { return ''; } return $this->task_list->get_list_id(); } /** * Get task list options. * * @return array */ public function get_parent_options() { if ( ! $this->task_list ) { return array(); } return $this->task_list->options; } /** * Get custom option. * * @param string $option_name name of custom option. * @return mixed|null */ public function get_parent_option( $option_name ) { if ( $this->task_list && isset( $this->task_list->options[ $option_name ] ) ) { return $this->task_list->options[ $option_name ]; } return null; } /** * Prefix event for track event naming. * * @param string $event_name Event name. * @return string */ public function prefix_event( $event_name ) { if ( ! $this->task_list ) { return ''; } return $this->task_list->prefix_event( $event_name ); } /** * Additional info. * * @return string */ public function get_additional_info() { return ''; } /** * Additional data. * * @return mixed */ public function get_additional_data() { return null; } /** * Badge. * * @return string */ public function get_badge() { return ''; } /** * Level. * * @deprecated 7.2.0 * * @return string */ public function get_level() { wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' ); return 3; } /** * Action label. * * @return string */ public function get_action_label() { return __( "Let's go", 'woocommerce' ); } /** * Action URL. * * @return string */ public function get_action_url() { return null; } /** * Check if a task is dismissable. * * @return bool */ public function is_dismissable() { return false; } /** * Bool for task dismissal. * * @return bool */ public function is_dismissed() { if ( ! $this->is_dismissable() ) { return false; } $dismissed = get_option( self::DISMISSED_OPTION, array() ); return in_array( $this->get_id(), $dismissed, true ); } /** * Dismiss the task. * * @return bool */ public function dismiss() { if ( ! $this->is_dismissable() ) { return false; } $dismissed = get_option( self::DISMISSED_OPTION, array() ); $dismissed[] = $this->get_id(); $update = update_option( self::DISMISSED_OPTION, array_unique( $dismissed ) ); if ( $update ) { $this->record_tracks_event( 'dismiss_task', array( 'task_name' => $this->get_id() ) ); } return $update; } /** * Undo task dismissal. * * @return bool */ public function undo_dismiss() { $dismissed = get_option( self::DISMISSED_OPTION, array() ); $dismissed = array_diff( $dismissed, array( $this->get_id() ) ); $update = update_option( self::DISMISSED_OPTION, $dismissed ); if ( $update ) { $this->record_tracks_event( 'undo_dismiss_task', array( 'task_name' => $this->get_id() ) ); } return $update; } /** * Check if a task is snoozeable. * * @deprecated 7.2.0 * * @return bool */ public function is_snoozeable() { wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' ); return false; } /** * Get the snoozed until datetime. * * @deprecated 7.2.0 * * @return string */ public function get_snoozed_until() { wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' ); $snoozed_tasks = get_option( self::SNOOZED_OPTION, array() ); if ( isset( $snoozed_tasks[ $this->get_id() ] ) ) { return $snoozed_tasks[ $this->get_id() ]; } return null; } /** * Bool for task snoozed. * * @deprecated 7.2.0 * * @return bool */ public function is_snoozed() { wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' ); if ( ! $this->is_snoozeable() ) { return false; } $snoozed = get_option( self::SNOOZED_OPTION, array() ); return isset( $snoozed[ $this->get_id() ] ) && $snoozed[ $this->get_id() ] > ( time() * 1000 ); } /** * Snooze the task. * * @param string $duration Duration to snooze. day|hour|week. * * @deprecated 7.2.0 * * @return bool */ public function snooze( $duration = 'day' ) { wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' ); if ( ! $this->is_snoozeable() ) { return false; } $snoozed = get_option( self::SNOOZED_OPTION, array() ); $snoozed_until = $this->duration_to_ms[ $duration ] + ( time() * 1000 ); $snoozed[ $this->get_id() ] = $snoozed_until; $update = update_option( self::SNOOZED_OPTION, $snoozed ); if ( $update ) { if ( $update ) { $this->record_tracks_event( 'remindmelater_task', array( 'task_name' => $this->get_id() ) ); } } return $update; } /** * Undo task snooze. * * @deprecated 7.2.0 * * @return bool */ public function undo_snooze() { wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' ); $snoozed = get_option( self::SNOOZED_OPTION, array() ); unset( $snoozed[ $this->get_id() ] ); $update = update_option( self::SNOOZED_OPTION, $snoozed ); if ( $update ) { $this->record_tracks_event( 'undo_remindmelater_task', array( 'task_name' => $this->get_id() ) ); } return $update; } /** * Check if a task list has previously been marked as complete. * * @return bool */ public function has_previously_completed() { $complete = get_option( self::COMPLETED_OPTION, array() ); return in_array( $this->get_id(), $complete, true ); } /** * Track task completion if task is viewable. */ public function possibly_track_completion() { if ( ! $this->is_complete() ) { return; } if ( $this->has_previously_completed() ) { return; } $completed_tasks = get_option( self::COMPLETED_OPTION, array() ); $completed_tasks[] = $this->get_id(); update_option( self::COMPLETED_OPTION, $completed_tasks ); $this->record_tracks_event( 'task_completed', array( 'task_name' => $this->get_id() ) ); } /** * Set this as the active task across page loads. */ public function set_active() { if ( $this->is_complete() ) { return; } set_transient( self::ACTIVE_TASK_TRANSIENT, $this->get_id(), DAY_IN_SECONDS ); } /** * Check if this is the active task. */ public function is_active() { return get_transient( self::ACTIVE_TASK_TRANSIENT ) === $this->get_id(); } /** * Check if the store is capable of viewing the task. * * @return bool */ public function can_view() { return true; } /** * Check if task is disabled. * * @deprecated 7.2.0 * * @return bool */ public function is_disabled() { wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' ); return false; } /** * Check if the task is complete. * * @return bool */ public function is_complete() { return self::is_actioned(); } /** * Check if the task has been visited. * * @return bool */ public function is_visited() { $user_id = get_current_user_id(); $response = WCAdminUser::get_user_data_field( $user_id, 'task_list_tracked_started_tasks' ); $tracked_tasks = $response ? json_decode( $response, true ) : array(); return isset( $tracked_tasks[ $this->get_id() ] ) && $tracked_tasks[ $this->get_id() ] > 0; } /** * Check if should record event when task is viewed * * @return bool */ public function get_record_view_event(): bool { return false; } /** * Get the task as JSON. * * @return array */ public function get_json() { $this->possibly_track_completion(); return array( 'id' => $this->get_id(), 'parentId' => $this->get_parent_id(), 'title' => $this->get_title(), 'badge' => $this->get_badge(), 'canView' => $this->can_view(), 'content' => $this->get_content(), 'additionalInfo' => $this->get_additional_info(), 'actionLabel' => $this->get_action_label(), 'actionUrl' => $this->get_action_url(), 'isComplete' => $this->is_complete(), 'time' => $this->get_time(), 'level' => 3, 'isActioned' => $this->is_actioned(), 'isDismissed' => $this->is_dismissed(), 'isDismissable' => $this->is_dismissable(), 'isSnoozed' => false, 'isSnoozeable' => false, 'isVisited' => $this->is_visited(), 'isDisabled' => false, 'snoozedUntil' => null, 'additionalData' => self::convert_object_to_camelcase( $this->get_additional_data() ), 'eventPrefix' => $this->prefix_event( '' ), 'recordViewEvent' => $this->get_record_view_event(), ); } /** * Convert object keys to camelcase. * * @param array $data Data to convert. * @return object */ public static function convert_object_to_camelcase( $data ) { if ( ! is_array( $data ) ) { return $data; } $new_object = (object) array(); foreach ( $data as $key => $value ) { $new_key = lcfirst( implode( '', array_map( 'ucfirst', explode( '_', $key ) ) ) ); $new_object->$new_key = $value; } return $new_object; } /** * Mark a task as actioned. Used to verify an action has taken place in some tasks. * * @return bool */ public function mark_actioned() { $actioned = get_option( self::ACTIONED_OPTION, array() ); $actioned[] = $this->get_id(); $update = update_option( self::ACTIONED_OPTION, array_unique( $actioned ) ); if ( $update ) { $this->record_tracks_event( 'actioned_task', array( 'task_name' => $this->get_id() ) ); } return $update; } /** * Check if a task has been actioned. * * @return bool */ public function is_actioned() { return self::is_task_actioned( $this->get_id() ); } /** * Check if a provided task ID has been actioned. * * @param string $id Task ID. * @return bool */ public static function is_task_actioned( $id ) { $actioned = get_option( self::ACTIONED_OPTION, array() ); return in_array( $id, $actioned, true ); } /** * Sorting function for tasks. * * @param Task $a Task a. * @param Task $b Task b. * @param array $sort_by list of columns with sort order. * @return int */ public static function sort( $a, $b, $sort_by = array() ) { $result = 0; foreach ( $sort_by as $data ) { $key = $data['key']; $a_val = $a->$key ?? false; $b_val = $b->$key ?? false; if ( 'asc' === $data['order'] ) { $result = $a_val <=> $b_val; } else { $result = $b_val <=> $a_val; } if ( 0 !== $result ) { break; } } return $result; } } OnboardingTasks/TaskTraits.php 0000644 00000001713 15073234674 0012453 0 ustar 00 <?php /** * Task and TaskList Traits */ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks; defined( 'ABSPATH' ) || exit; /** * TaskTraits class. */ trait TaskTraits { /** * Record a tracks event with the prefixed event name. * * @param string $event_name Event name. * @param array $args Array of tracks arguments. * @return string Prefixed event name. */ public function record_tracks_event( $event_name, $args = array() ) { if ( ! $this->get_list_id() ) { return; } $prefixed_event_name = $this->prefix_event( $event_name ); wc_admin_record_tracks_event( $prefixed_event_name, $args ); return $prefixed_event_name; } /** * Get the task list ID. * * @return string */ public function get_list_id() { $namespaced_class = get_class( $this ); return is_subclass_of( $namespaced_class, 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task' ) ? $this->get_parent_id() : $this->id; } } OnboardingTasks/Tasks/Payments.php 0000644 00000003746 15073234674 0013257 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; /** * Payments Task */ class Payments extends Task { /** * Used to cache is_complete() method result. * @var null */ private $is_complete_result = null; /** * ID. * * @return string */ public function get_id() { return 'payments'; } /** * Title. * * @return string */ public function get_title() { if ( true === $this->get_parent_option( 'use_completed_title' ) ) { if ( $this->is_complete() ) { return __( 'You set up payments', 'woocommerce' ); } return __( 'Set up payments', 'woocommerce' ); } return __( 'Set up payments', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return __( 'Choose payment providers and enable payment methods at checkout.', 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '2 minutes', 'woocommerce' ); } /** * Task completion. * * @return bool */ public function is_complete() { if ( $this->is_complete_result === null ) { $this->is_complete_result = self::has_gateways(); } return $this->is_complete_result; } /** * Task visibility. * * @return bool */ public function can_view() { $woocommerce_payments = $this->task_list->get_task( 'woocommerce-payments' ); return Features::is_enabled( 'payment-gateway-suggestions' ) && ! $woocommerce_payments->can_view(); } /** * Check if the store has any enabled gateways. * * @return bool */ public static function has_gateways() { $gateways = WC()->payment_gateways->get_available_payment_gateways(); $enabled_gateways = array_filter( $gateways, function( $gateway ) { return 'yes' === $gateway->enabled && 'woocommerce_payments' !== $gateway->id; } ); return ! empty( $enabled_gateways ); } } OnboardingTasks/Tasks/Purchase.php 0000644 00000012127 15073234674 0013222 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProducts; use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingThemes; use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; /** * Purchase Task */ class Purchase extends Task { /** * Constructor * * @param TaskList $task_list Parent task list. */ public function __construct( $task_list ) { parent::__construct( $task_list ); add_action( 'update_option_woocommerce_onboarding_profile', array( $this, 'clear_dismissal' ), 10, 2 ); } /** * Clear dismissal on onboarding product type changes. * * @param array $old_value Old value. * @param array $new_value New value. */ public function clear_dismissal( $old_value, $new_value ) { $product_types = isset( $new_value['product_types'] ) ? (array) $new_value['product_types'] : array(); $previous_product_types = isset( $old_value['product_types'] ) ? (array) $old_value['product_types'] : array(); if ( empty( array_diff( $product_types, $previous_product_types ) ) ) { return; } $this->undo_dismiss(); } /** * Get the task arguments. * ID. * * @return string */ public function get_id() { return 'purchase'; } /** * Title. * * @return string */ public function get_title() { $products = $this->get_paid_products_and_themes(); $first_product = count( $products['purchaseable'] ) >= 1 ? $products['purchaseable'][0] : false; if ( ! $first_product ) { return null; } $product_label = isset( $first_product['label'] ) ? $first_product['label'] : $first_product['title']; $additional_count = count( $products['purchaseable'] ) - 1; if ( $this->get_parent_option( 'use_completed_title' ) && $this->is_complete() ) { return count( $products['purchaseable'] ) === 1 ? sprintf( /* translators: %1$s: a purchased product name */ __( 'You added %1$s', 'woocommerce' ), $product_label ) : sprintf( /* translators: %1$s: a purchased product name, %2$d the number of other products purchased */ _n( 'You added %1$s and %2$d other product', 'You added %1$s and %2$d other products', $additional_count, 'woocommerce' ), $product_label, $additional_count ); } return count( $products['purchaseable'] ) === 1 ? sprintf( /* translators: %1$s: a purchaseable product name */ __( 'Add %s to my store', 'woocommerce' ), $product_label ) : sprintf( /* translators: %1$s: a purchaseable product name, %2$d the number of other products to purchase */ _n( 'Add %1$s and %2$d more product to my store', 'Add %1$s and %2$d more products to my store', $additional_count, 'woocommerce' ), $product_label, $additional_count ); } /** * Content. * * @return string */ public function get_content() { $products = $this->get_paid_products_and_themes(); if ( count( $products['remaining'] ) === 1 ) { return isset( $products['purchaseable'][0]['description'] ) ? $products['purchaseable'][0]['description'] : $products['purchaseable'][0]['excerpt']; } return sprintf( /* translators: %1$s: list of product names comma separated, %2%s the last product name */ __( 'Good choice! You chose to add %1$s and %2$s to your store.', 'woocommerce' ), implode( ', ', array_slice( $products['remaining'], 0, -1 ) ) . ( count( $products['remaining'] ) > 2 ? ',' : '' ), end( $products['remaining'] ) ); } /** * Action label. * * @return string */ public function get_action_label() { return __( 'Purchase & install now', 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '2 minutes', 'woocommerce' ); } /** * Task completion. * * @return bool */ public function is_complete() { $products = $this->get_paid_products_and_themes(); return count( $products['remaining'] ) === 0; } /** * Dismissable. * * @return bool */ public function is_dismissable() { return true; } /** * Task visibility. * * @return bool */ public function can_view() { $products = $this->get_paid_products_and_themes(); return count( $products['purchaseable'] ) > 0; } /** * Get purchaseable and remaining products. * * @return array purchaseable and remaining products and themes. */ public static function get_paid_products_and_themes() { $relevant_products = OnboardingProducts::get_relevant_products(); $profiler_data = get_option( OnboardingProfile::DATA_OPTION, array() ); $theme = isset( $profiler_data['theme'] ) ? $profiler_data['theme'] : null; $paid_theme = $theme ? OnboardingThemes::get_paid_theme_by_slug( $theme ) : null; if ( $paid_theme ) { $relevant_products['purchaseable'][] = $paid_theme; if ( isset( $paid_theme['is_installed'] ) && false === $paid_theme['is_installed'] ) { $relevant_products['remaining'][] = $paid_theme['title']; } } return $relevant_products; } } OnboardingTasks/Tasks/TourInAppMarketplace.php 0000644 00000002277 15073234674 0015507 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; /** * Tour In-App Marketplace task */ class TourInAppMarketplace extends Task { /** * ID. * * @return string */ public function get_id() { return 'tour-in-app-marketplace'; } /** * Title. * * @return string */ public function get_title() { return __( 'Discover ways of extending your store with a tour of the Woo Marketplace', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return ''; } /** * Time. * * @return string */ public function get_time() { return ''; } /** * Task completion. * * @return bool */ public function is_complete() { return get_option( 'woocommerce_admin_dismissed_in_app_marketplace_tour' ) === 'yes'; } /** * Action URL. * * @return string */ public function get_action_url() { return admin_url( 'admin.php?page=wc-admin&path=%2Fextensions&tutorial=true' ); } /** * Check if should record event when task is viewed * * @return bool */ public function get_record_view_event(): bool { return true; } } OnboardingTasks/Tasks/WooCommercePayments.php 0000644 00000012420 15073234674 0015404 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\WooCommerce\Admin\PluginsHelper; use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\Init as Suggestions; use Automattic\WooCommerce\Internal\Admin\WCPayPromotion\Init as WCPayPromotionInit; /** * WooCommercePayments Task */ class WooCommercePayments extends Task { /** * Used to cache is_complete() method result. * * @var null */ private $is_complete_result = null; /** * ID. * * @return string */ public function get_id() { return 'woocommerce-payments'; } /** * Title. * * @return string */ public function get_title() { return __( 'Set up WooPayments', 'woocommerce' ); } /** * Badge. * * @return string */ public function get_badge() { /** * Filter WooPayments onboarding task badge. * * @param string $badge Badge content. * @since 8.2.0 */ return apply_filters( 'woocommerce_admin_woopayments_onboarding_task_badge', '' ); } /** * Content. * * @return string */ public function get_content() { return __( "You're only one step away from getting paid. Verify your business details to start managing transactions with WooPayments.", 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '2 minutes', 'woocommerce' ); } /** * Action label. * * @return string */ public function get_action_label() { return __( 'Finish setup', 'woocommerce' ); } /** * Additional info. * * @return string */ public function get_additional_info() { if ( WCPayPromotionInit::is_woopay_eligible() ) { return __( 'By using WooPayments you agree to be bound by our <a href="https://wordpress.com/tos/" target="_blank">Terms of Service</a> (including WooPay <a href="https://wordpress.com/tos/#more-woopay-specifically" target="_blank">merchant terms</a>) and acknowledge that you have read our <a href="https://automattic.com/privacy/" target="_blank">Privacy Policy</a>', 'woocommerce' ); } return __( 'By using WooPayments you agree to be bound by our <a href="https://wordpress.com/tos/" target="_blank">Terms of Service</a> and acknowledge that you have read our <a href="https://automattic.com/privacy/" target="_blank">Privacy Policy</a>', 'woocommerce' ); } /** * Task completion. * * @return bool */ public function is_complete() { if ( null === $this->is_complete_result ) { $this->is_complete_result = self::is_connected() && ! self::is_account_partially_onboarded(); } return $this->is_complete_result; } /** * Task visibility. * * @return bool */ public function can_view() { $payments = $this->task_list->get_task( 'payments' ); return ! $payments->is_complete() && // Do not re-display the task if the "add payments" task has already been completed. self::is_installed() && self::is_supported(); } /** * Check if the plugin was requested during onboarding. * * @return bool */ public static function is_requested() { $profiler_data = get_option( OnboardingProfile::DATA_OPTION, array() ); $product_types = isset( $profiler_data['product_types'] ) ? $profiler_data['product_types'] : array(); $business_extensions = isset( $profiler_data['business_extensions'] ) ? $profiler_data['business_extensions'] : array(); $subscriptions_and_us = in_array( 'subscriptions', $product_types, true ) && 'US' === WC()->countries->get_base_country(); return in_array( 'woocommerce-payments', $business_extensions, true ) || $subscriptions_and_us; } /** * Check if the plugin is installed. * * @return bool */ public static function is_installed() { $installed_plugins = PluginsHelper::get_installed_plugin_slugs(); return in_array( 'woocommerce-payments', $installed_plugins, true ); } /** * Check if WooCommerce Payments is connected. * * @return bool */ public static function is_connected() { if ( class_exists( '\WC_Payments' ) ) { $wc_payments_gateway = \WC_Payments::get_gateway(); return method_exists( $wc_payments_gateway, 'is_connected' ) ? $wc_payments_gateway->is_connected() : false; } return false; } /** * Check if WooCommerce Payments needs setup. * Errored data or payments not enabled. * * @return bool */ public static function is_account_partially_onboarded() { if ( class_exists( '\WC_Payments' ) ) { $wc_payments_gateway = \WC_Payments::get_gateway(); return method_exists( $wc_payments_gateway, 'is_account_partially_onboarded' ) ? $wc_payments_gateway->is_account_partially_onboarded() : false; } return false; } /** * Check if the store is in a supported country. * * @return bool */ public static function is_supported() { $suggestions = Suggestions::get_suggestions(); $suggestion_plugins = array_merge( ...array_filter( array_column( $suggestions, 'plugins' ), function( $plugins ) { return is_array( $plugins ); } ) ); $woocommerce_payments_ids = array_search( 'woocommerce-payments', $suggestion_plugins, true ); if ( false !== $woocommerce_payments_ids ) { return true; } return false; } } OnboardingTasks/Tasks/Tax.php 0000644 00000010024 15073234674 0012176 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\API\Reports\Taxes\Stats\DataStore as TaxDataStore; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\WooCommerce\Admin\PluginsHelper; use Automattic\WooCommerce\Internal\Admin\WCAdminAssets; /** * Tax Task */ class Tax extends Task { /** * Used to cache is_complete() method result. * @var null */ private $is_complete_result = null; /** * Constructor * * @param TaskList $task_list Parent task list. */ public function __construct( $task_list ) { parent::__construct( $task_list ); add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_return_notice_script' ) ); } /** * Adds a return to task list notice when completing the task. */ public function possibly_add_return_notice_script() { $page = isset( $_GET['page'] ) ? $_GET['page'] : ''; // phpcs:ignore csrf ok, sanitization ok. $tab = isset( $_GET['tab'] ) ? $_GET['tab'] : ''; // phpcs:ignore csrf ok, sanitization ok. if ( $page !== 'wc-settings' || $tab !== 'tax' ) { return; } if ( ! $this->is_active() || $this->is_complete() ) { return; } WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-tax-notice', true ); } /** * ID. * * @return string */ public function get_id() { return 'tax'; } /** * Title. * * @return string */ public function get_title() { if ( $this->get_parent_option( 'use_completed_title' ) === true ) { if ( $this->is_complete() ) { return __( 'You added tax rates', 'woocommerce' ); } return __( 'Add tax rates', 'woocommerce' ); } return __( 'Set up tax rates', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return self::can_use_automated_taxes() ? __( 'Good news! WooCommerce Tax can automate your sales tax calculations for you.', 'woocommerce' ) : __( 'Set your store location and configure tax rate settings.', 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '1 minute', 'woocommerce' ); } /** * Action label. * * @return string */ public function get_action_label() { return self::can_use_automated_taxes() ? __( 'Yes please', 'woocommerce' ) : __( "Let's go", 'woocommerce' ); } /** * Task completion. * * @return bool */ public function is_complete() { if ( $this->is_complete_result === null ) { $wc_connect_taxes_enabled = get_option( 'wc_connect_taxes_enabled' ); $is_wc_connect_taxes_enabled = ( $wc_connect_taxes_enabled === 'yes' ) || ( $wc_connect_taxes_enabled === true ); // seems that in some places boolean is used, and other places 'yes' | 'no' is used $this->is_complete_result = $is_wc_connect_taxes_enabled || count( TaxDataStore::get_taxes( array() ) ) > 0 || get_option( 'woocommerce_no_sales_tax' ) !== false; } return $this->is_complete_result; } /** * Addtional data. * * @return array */ public function get_additional_data() { return array( 'avalara_activated' => PluginsHelper::is_plugin_active( 'woocommerce-avatax' ), 'tax_jar_activated' => class_exists( 'WC_Taxjar' ), 'woocommerce_tax_countries' => self::get_automated_support_countries(), ); } /** * Check if the store has any enabled gateways. * * @return bool */ public static function can_use_automated_taxes() { if ( ! class_exists( 'WC_Taxjar' ) ) { return false; } return in_array( WC()->countries->get_base_country(), self::get_automated_support_countries(), true ); } /** * Get an array of countries that support automated tax. * * @return array */ public static function get_automated_support_countries() { // https://developers.taxjar.com/api/reference/#countries . $tax_supported_countries = array_merge( array( 'US', 'CA', 'AU', 'GB' ), WC()->countries->get_european_union_countries() ); return $tax_supported_countries; } } OnboardingTasks/Tasks/Marketing.php 0000644 00000004736 15073234674 0013400 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions\Init as RemoteFreeExtensions; /** * Marketing Task */ class Marketing extends Task { /** * ID. * * @return string */ public function get_id() { return 'marketing'; } /** * Title. * * @return string */ public function get_title() { if ( true === $this->get_parent_option( 'use_completed_title' ) ) { if ( $this->is_complete() ) { return __( 'You added sales channels', 'woocommerce' ); } return __( 'Get more sales', 'woocommerce' ); } return __( 'Set up marketing tools', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return __( 'Add recommended marketing tools to reach new customers and grow your business', 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '2 minutes', 'woocommerce' ); } /** * Task completion. * * @return bool */ public function is_complete() { return self::has_installed_extensions(); } /** * Task visibility. * * @return bool */ public function can_view() { return Features::is_enabled( 'remote-free-extensions' ) && count( self::get_plugins() ) > 0; } /** * Get the marketing plugins. * * @return array */ public static function get_plugins() { $bundles = RemoteFreeExtensions::get_extensions( array( 'task-list/reach', 'task-list/grow', ) ); return array_reduce( $bundles, function( $plugins, $bundle ) { $visible = array(); foreach ( $bundle['plugins'] as $plugin ) { if ( $plugin->is_visible ) { $visible[] = $plugin; } } return array_merge( $plugins, $visible ); }, array() ); } /** * Check if the store has installed marketing extensions. * * @return bool */ public static function has_installed_extensions() { $plugins = self::get_plugins(); $remaining = array(); $installed = array(); foreach ( $plugins as $plugin ) { if ( ! $plugin->is_installed ) { $remaining[] = $plugin; } else { $installed[] = $plugin; } } // Make sure the task has been actioned and a marketing extension has been installed. if ( count( $installed ) > 0 && Task::is_task_actioned( 'marketing' ) ) { return true; } return false; } } OnboardingTasks/Tasks/ExperimentalShippingRecommendation.php 0000644 00000003233 15073234674 0020472 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\Jetpack\Connection\Manager; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\WooCommerce\Admin\PluginsHelper; /** * Shipping Task */ class ExperimentalShippingRecommendation extends Task { /** * ID. * * @return string */ public function get_id() { return 'shipping-recommendation'; } /** * Title. * * @return string */ public function get_title() { return __( 'Set up shipping', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return ''; } /** * Time. * * @return string */ public function get_time() { return ''; } /** * Task completion. * * @return bool */ public function is_complete() { return self::has_plugins_active() && self::has_jetpack_connected(); } /** * Task visibility. * * @return bool */ public function can_view() { return Features::is_enabled( 'shipping-smart-defaults' ); } /** * Action URL. * * @return string */ public function get_action_url() { return ''; } /** * Check if the store has any shipping zones. * * @return bool */ public static function has_plugins_active() { return PluginsHelper::is_plugin_active( 'woocommerce-services' ); } /** * Check if the Jetpack is connected. * * @return bool */ public static function has_jetpack_connected() { $jetpack_connection_manager = new Manager( 'woocommerce' ); return $jetpack_connection_manager->is_connected() && $jetpack_connection_manager->has_connected_owner(); } } OnboardingTasks/Tasks/GetMobileApp.php 0000644 00000005014 15073234674 0013755 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\Jetpack\Connection\Manager; // https://github.com/Automattic/jetpack/blob/trunk/projects/packages/connection/src/class-manager.php . /** * Get Mobile App Task */ class GetMobileApp extends Task { /** * ID. * * @return string */ public function get_id() { return 'get-mobile-app'; } /** * Title. * * @return string */ public function get_title() { return __( 'Get the free WooCommerce mobile app', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return ''; } /** * Time. * * @return string */ public function get_time() { return ''; } /** * Task completion. * * @return bool */ public function is_complete() { return get_option( 'woocommerce_admin_dismissed_mobile_app_modal' ) === 'yes'; } /** * Task visibility. * Can view under these conditions: * - Jetpack is installed and connected && current site user has a wordpress.com account connected to jetpack * - Jetpack is not connected && current user is capable of installing plugins * * @return bool */ public function can_view() { $jetpack_can_be_installed = current_user_can( 'manage_woocommerce' ) && current_user_can( 'install_plugins' ) && ! self::is_jetpack_connected(); $jetpack_is_installed_and_current_user_connected = self::is_current_user_connected(); return $jetpack_can_be_installed || $jetpack_is_installed_and_current_user_connected; } /** * Determines if site has any users connected to WordPress.com via JetPack * * @return bool */ private static function is_jetpack_connected() { if ( class_exists( '\Automattic\Jetpack\Connection\Manager' ) && method_exists( '\Automattic\Jetpack\Connection\Manager', 'is_active' ) ) { $connection = new Manager(); return $connection->is_active(); } return false; } /** * Determines if the current user is connected to Jetpack. * * @return bool */ private static function is_current_user_connected() { if ( class_exists( '\Automattic\Jetpack\Connection\Manager' ) && method_exists( '\Automattic\Jetpack\Connection\Manager', 'is_user_connected' ) ) { $connection = new Manager(); return $connection->is_connection_owner(); } return false; } /** * Action URL. * * @return string */ public function get_action_url() { return admin_url( 'admin.php?page=wc-admin&mobileAppModal=true' ); } } OnboardingTasks/Tasks/AdditionalPayments.php 0000644 00000011066 15073234674 0015242 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Payments; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\WooCommercePayments; use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\Init; /** * Payments Task */ class AdditionalPayments extends Payments { /** * Used to cache is_complete() method result. * * @var null */ private $is_complete_result = null; /** * Used to cache can_view() method result. * * @var null */ private $can_view_result = null; /** * ID. * * @return string */ public function get_id() { return 'payments'; } /** * Title. * * @return string */ public function get_title() { return __( 'Set up additional payment options', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return __( 'Choose payment providers and enable payment methods at checkout.', 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '2 minutes', 'woocommerce' ); } /** * Task completion. * * @return bool */ public function is_complete() { if ( null === $this->is_complete_result ) { $this->is_complete_result = self::has_enabled_additional_gateways(); } return $this->is_complete_result; } /** * Task visibility. * * @return bool */ public function can_view() { if ( ! Features::is_enabled( 'payment-gateway-suggestions' ) ) { // Hide task if feature not enabled. return false; } if ( null !== $this->can_view_result ) { return $this->can_view_result; } // Show task if woocommerce-payments is connected or if there are any suggested gateways in other category enabled. $this->can_view_result = ( WooCommercePayments::is_connected() || self::has_enabled_other_category_gateways() ); // Early return if task is not visible. if ( ! $this->can_view_result ) { return false; } // Show task if there are any suggested gateways in additional category. $this->can_view_result = ! empty( self::get_suggestion_gateways( 'category_additional' ) ); return $this->can_view_result; } /** * Check if the store has any enabled gateways in other category. * * @return bool */ private static function has_enabled_other_category_gateways() { $other_gateways = self::get_suggestion_gateways( 'category_other' ); $other_gateways_ids = wp_list_pluck( $other_gateways, 'id' ); return self::has_enabled_gateways( function( $gateway ) use ( $other_gateways_ids ) { return in_array( $gateway->id, $other_gateways_ids, true ); } ); } /** * Check if the store has any enabled gateways in additional category. * * @return bool */ private static function has_enabled_additional_gateways() { $additional_gateways = self::get_suggestion_gateways( 'category_additional' ); $additional_gateways_ids = wp_list_pluck( $additional_gateways, 'id' ); return self::has_enabled_gateways( function( $gateway ) use ( $additional_gateways_ids ) { return 'yes' === $gateway->enabled && in_array( $gateway->id, $additional_gateways_ids, true ); } ); } /** * Check if the store has any enabled gateways based on the given criteria. * * @param callable|null $filter A callback function to filter the gateways. * @return bool */ private static function has_enabled_gateways( $filter = null ) { $gateways = WC()->payment_gateways->get_available_payment_gateways(); $enabled_gateways = array_filter( $gateways, function( $gateway ) use ( $filter ) { if ( is_callable( $filter ) ) { return 'yes' === $gateway->enabled && call_user_func( $filter, $gateway ); } else { return 'yes' === $gateway->enabled; } } ); return ! empty( $enabled_gateways ); } /** * Get the list of gateways to suggest. * * @param string $filter_by Filter by category. "category_additional" or "category_other". * * @return array */ private static function get_suggestion_gateways( $filter_by = 'category_additional' ) { $country = wc_get_base_location()['country']; $plugin_suggestions = Init::get_suggestions(); $plugin_suggestions = array_filter( $plugin_suggestions, function( $plugin ) use ( $country, $filter_by ) { if ( ! isset( $plugin->{$filter_by} ) || ! isset( $plugin->plugins[0] ) ) { return false; } return in_array( $country, $plugin->{$filter_by}, true ); } ); return $plugin_suggestions; } } OnboardingTasks/Tasks/CustomizeStore.php 0000644 00000016720 15073234674 0014452 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Jetpack_Gutenberg; /** * Customize Your Store Task */ class CustomizeStore extends Task { /** * Constructor * * @param TaskList $task_list Parent task list. */ public function __construct( $task_list ) { parent::__construct( $task_list ); add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_site_editor_scripts' ) ); add_action( 'show_admin_bar', array( $this, 'possibly_hide_wp_admin_bar' ) ); // Use "switch_theme" instead of "after_switch_theme" because the latter is fired after the next WP load and we don't want to trigger action when switching theme to TT3 via onboarding theme API. global $_GET; $theme_switch_via_cys_ai_loader = isset( $_GET['theme_switch_via_cys_ai_loader'] ) ? 1 === absint( $_GET['theme_switch_via_cys_ai_loader'] ) : false; if ( ! $theme_switch_via_cys_ai_loader ) { add_action( 'switch_theme', array( $this, 'mark_task_as_complete' ) ); } // Hook to remove unwanted UI elements when users are viewing with ?cys-hide-admin-bar=true. add_action( 'wp_head', array( $this, 'possibly_remove_unwanted_ui_elements' ) ); } /** * ID. * * @return string */ public function get_id() { return 'customize-store'; } /** * Title. * * @return string */ public function get_title() { return __( 'Customize your store ', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return ''; } /** * Time. * * @return string */ public function get_time() { return ''; } /** * Task completion. * * @return bool */ public function is_complete() { return get_option( 'woocommerce_admin_customize_store_completed' ) === 'yes'; } /** * Task visibility. * * @return bool */ public function can_view() { return true; } /** * Action URL. * * @return string */ public function get_action_url() { return admin_url( 'wp-admin/admin.php?page=wc-admin&path=%2Fcustomize-store' ); } /** * Possibly add site editor scripts. */ public function possibly_add_site_editor_scripts() { // phpcs:disable WordPress.Security.NonceVerification.Recommended $is_wc_admin_page = ( isset( $_GET['page'] ) && 'wc-admin' === $_GET['page'] && isset( $_GET['path'] ) ); $is_assembler_hub = $is_wc_admin_page && str_starts_with( wc_clean( wp_unslash( $_GET['path'] ) ), '/customize-store/assembler-hub' ); $is_transitional_page = $is_wc_admin_page && str_starts_with( wc_clean( wp_unslash( $_GET['path'] ) ), '/customize-store/transitional' ); // phpcs:enable WordPress.Security.NonceVerification.Recommended if ( ! ( $is_assembler_hub || $is_transitional_page ) ) { return; } // See: https://github.com/WordPress/WordPress/blob/master/wp-admin/site-editor.php. if ( ! wp_is_block_theme() ) { wp_die( esc_html__( 'The theme you are currently using is not compatible.', 'woocommerce' ) ); } global $editor_styles; // Flag that we're loading the block editor. $current_screen = get_current_screen(); $current_screen->is_block_editor( true ); // Default to is-fullscreen-mode to avoid jumps in the UI. add_filter( 'admin_body_class', static function( $classes ) { return "$classes is-fullscreen-mode"; } ); $block_editor_context = new \WP_Block_Editor_Context( array( 'name' => 'core/edit-site' ) ); $indexed_template_types = array(); foreach ( get_default_block_template_types() as $slug => $template_type ) { $template_type['slug'] = (string) $slug; $indexed_template_types[] = $template_type; } $custom_settings = array( 'siteUrl' => site_url(), 'postsPerPage' => get_option( 'posts_per_page' ), 'styles' => get_block_editor_theme_styles(), 'defaultTemplateTypes' => $indexed_template_types, 'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(), 'supportsLayout' => wp_theme_has_theme_json(), 'supportsTemplatePartsMode' => ! wp_is_block_theme() && current_theme_supports( 'block-template-parts' ), ); // Add additional back-compat patterns registered by `current_screen` et al. $custom_settings['__experimentalAdditionalBlockPatterns'] = \WP_Block_Patterns_Registry::get_instance()->get_all_registered( true ); $custom_settings['__experimentalAdditionalBlockPatternCategories'] = \WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered( true ); $editor_settings = get_block_editor_settings( $custom_settings, $block_editor_context ); $active_global_styles_id = \WP_Theme_JSON_Resolver::get_user_global_styles_post_id(); $active_theme = get_stylesheet(); $preload_paths = array( array( '/wp/v2/media', 'OPTIONS' ), '/wp/v2/types?context=view', '/wp/v2/types/wp_template?context=edit', '/wp/v2/types/wp_template-part?context=edit', '/wp/v2/templates?context=edit&per_page=-1', '/wp/v2/template-parts?context=edit&per_page=-1', '/wp/v2/themes?context=edit&status=active', '/wp/v2/global-styles/' . $active_global_styles_id . '?context=edit', '/wp/v2/global-styles/' . $active_global_styles_id, '/wp/v2/global-styles/themes/' . $active_theme, ); block_editor_rest_api_preload( $preload_paths, $block_editor_context ); wp_add_inline_script( 'wp-blocks', sprintf( 'window.wcBlockSettings = %s;', wp_json_encode( $editor_settings ) ) ); // Preload server-registered block schemas. wp_add_inline_script( 'wp-blocks', 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');' ); wp_add_inline_script( 'wp-blocks', sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( isset( $editor_settings['blockCategories'] ) ? $editor_settings['blockCategories'] : array() ) ), 'after' ); wp_enqueue_script( 'wp-editor' ); wp_enqueue_script( 'wp-format-library' ); // Not sure if this is needed. wp_enqueue_script( 'wp-router' ); wp_enqueue_style( 'wp-editor' ); wp_enqueue_style( 'wp-edit-site' ); wp_enqueue_style( 'wp-format-library' ); wp_enqueue_media(); if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) { wp_enqueue_style( 'wp-block-library-theme' ); } /** This action is documented in wp-admin/edit-form-blocks.php * * @since 8.0.3 */ do_action( 'enqueue_block_editor_assets' ); // Load Jetpack's block editor assets because they are not enqueued by default. if ( class_exists( 'Jetpack_Gutenberg' ) ) { Jetpack_Gutenberg::enqueue_block_editor_assets(); } } /** * Mark task as complete. */ public function mark_task_as_complete() { update_option( 'woocommerce_admin_customize_store_completed', 'yes' ); } /** * Appends a small style to hide admin bar * * @param bool $show Whether to show the admin bar. */ public function possibly_hide_wp_admin_bar( $show ) { if ( isset( $_GET['cys-hide-admin-bar'] ) ) { // @phpcs:ignore return false; } return $show; } /** * Runs script and add styles to remove unwanted elements and hide scrollbar * when users are viewing with ?cys-hide-admin-bar=true. * * @return void */ public function possibly_remove_unwanted_ui_elements() { if ( isset( $_GET['cys-hide-admin-bar'] ) ) { // @phpcs:ignore echo ' <style type="text/css"> body { overflow: hidden; } </style>'; } } } OnboardingTasks/Tasks/Products.php 0000644 00000007634 15073234674 0013262 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\WooCommerce\Internal\Admin\WCAdminAssets; /** * Products Task */ class Products extends Task { /** * Constructor * * @param TaskList $task_list Parent task list. */ public function __construct( $task_list ) { parent::__construct( $task_list ); add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_manual_return_notice_script' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_import_return_notice_script' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_load_sample_return_notice_script' ) ); } /** * ID. * * @return string */ public function get_id() { return 'products'; } /** * Title. * * @return string */ public function get_title() { if ( $this->get_parent_option( 'use_completed_title' ) === true ) { if ( $this->is_complete() ) { return __( 'You added products', 'woocommerce' ); } return __( 'Add products', 'woocommerce' ); } return __( 'Add my products', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return __( 'Start by adding the first product to your store. You can add your products manually, via CSV, or import them from another service.', 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '1 minute per product', 'woocommerce' ); } /** * Task completion. * * @return bool */ public function is_complete() { return self::has_products(); } /** * Addtional data. * * @return array */ public function get_additional_data() { return array( 'has_products' => self::has_products(), ); } /** * Adds a return to task list notice when completing the manual product task. * * @param string $hook Page hook. */ public function possibly_add_manual_return_notice_script( $hook ) { global $post; if ( $hook !== 'post.php' || $post->post_type !== 'product' ) { return; } if ( ! $this->is_active() || ! $this->is_complete() ) { return; } WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-product-notice', true ); // Clear the active task transient to only show notice once per active session. delete_transient( self::ACTIVE_TASK_TRANSIENT ); } /** * Adds a return to task list notice when completing the import product task. * * @param string $hook Page hook. */ public function possibly_add_import_return_notice_script( $hook ) { $step = isset( $_GET['step'] ) ? $_GET['step'] : ''; // phpcs:ignore csrf ok, sanitization ok. if ( $hook !== 'product_page_product_importer' || $step !== 'done' ) { return; } if ( ! $this->is_active() || $this->is_complete() ) { return; } WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-product-import-notice', true ); } /** * Adds a return to task list notice when completing the loading sample products action. * * @param string $hook Page hook. */ public function possibly_add_load_sample_return_notice_script( $hook ) { if ( $hook !== 'edit.php' || get_query_var( 'post_type' ) !== 'product' ) { return; } $referer = wp_get_referer(); if ( ! $referer || strpos( $referer, wc_admin_url() ) !== 0 ) { return; } if ( ! isset( $_GET[ Task::ACTIVE_TASK_TRANSIENT ] ) ) { return; } $task_id = sanitize_title_with_dashes( wp_unslash( $_GET[ Task::ACTIVE_TASK_TRANSIENT ] ) ); if ( $task_id !== $this->get_id() || ! $this->is_complete() ) { return; } WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-load-sample-products-notice', true ); } /** * Check if the store has any published products. * * @return bool */ public static function has_products() { $counts = wp_count_posts('product'); return isset( $counts->publish ) && $counts->publish > 0; } } OnboardingTasks/Tasks/Shipping.php 0000644 00000012252 15073234674 0013230 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use WC_Data_Store; /** * Shipping Task */ class Shipping extends Task { const ZONE_COUNT_TRANSIENT_NAME = 'woocommerce_shipping_task_zone_count_transient'; /** * Constructor * * @param TaskList $task_list Parent task list. */ public function __construct( $task_list = null ) { parent::__construct( $task_list ); // wp_ajax_woocommerce_shipping_zone_methods_save_changes // and wp_ajax_woocommerce_shipping_zones_save_changes get fired // when a new zone is added or an existing one has been changed. add_action( 'wp_ajax_woocommerce_shipping_zones_save_changes', array( __CLASS__, 'delete_zone_count_transient' ), 9 ); add_action( 'wp_ajax_woocommerce_shipping_zone_methods_save_changes', array( __CLASS__, 'delete_zone_count_transient' ), 9 ); add_action( 'woocommerce_shipping_zone_method_added', array( __CLASS__, 'delete_zone_count_transient' ), 9 ); add_action( 'woocommerce_after_shipping_zone_object_save', array( __CLASS__, 'delete_zone_count_transient' ), 9 ); } /** * ID. * * @return string */ public function get_id() { return 'shipping'; } /** * Title. * * @return string */ public function get_title() { if ( true === $this->get_parent_option( 'use_completed_title' ) ) { if ( $this->is_complete() ) { return __( 'You added shipping costs', 'woocommerce' ); } return __( 'Add shipping costs', 'woocommerce' ); } return __( 'Set up shipping', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return __( "Set your store location and where you'll ship to.", 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '1 minute', 'woocommerce' ); } /** * Task completion. * * @return bool */ public function is_complete() { return self::has_shipping_zones(); } /** * Task visibility. * * @return bool */ public function can_view() { if ( Features::is_enabled( 'shipping-smart-defaults' ) ) { if ( 'yes' === get_option( 'woocommerce_admin_created_default_shipping_zones' ) ) { // If the user has already created a default shipping zone, we don't need to show the task. return false; } /** * Do not display the task when: * - The store sells digital products only * Display the task when: * - We don't know where the store's located * - The store is located in the UK, Australia or Canada */ if ( self::is_selling_digital_type_only() ) { return false; } $default_store_country = wc_format_country_state_string( get_option( 'woocommerce_default_country', '' ) )['country']; // Check if a store address is set so that we don't default to WooCommerce's default country US. // Similar logic: https://github.com/woocommerce/woocommerce/blob/059d542394b48468587f252dcb6941c6425cd8d3/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/index.js#L511-L516. $store_country = ''; if ( ! empty( get_option( 'woocommerce_store_address', '' ) ) || 'US' !== $default_store_country ) { $store_country = $default_store_country; } // Unknown country. if ( empty( $store_country ) ) { return true; } return in_array( $store_country, array( 'CA', 'AU', 'NZ', 'SG', 'HK', 'GB', 'ES', 'IT', 'DE', 'FR', 'CL', 'AR', 'PE', 'BR', 'UY', 'GT', 'NL', 'AT', 'BE' ), true ); } return self::has_physical_products(); } /** * Action URL. * * @return string */ public function get_action_url() { return self::has_shipping_zones() ? admin_url( 'admin.php?page=wc-settings&tab=shipping' ) : null; } /** * Check if the store has any shipping zones. * * @return bool */ public static function has_shipping_zones() { $zone_count = get_transient( self::ZONE_COUNT_TRANSIENT_NAME ); if ( false !== $zone_count ) { return (int) $zone_count > 0; } $zone_count = count( WC_Data_Store::load( 'shipping-zone' )->get_zones() ); set_transient( self::ZONE_COUNT_TRANSIENT_NAME, $zone_count ); return $zone_count > 0; } /** * Check if the store has physical products. * * @return bool */ public static function has_physical_products() { $profiler_data = get_option( OnboardingProfile::DATA_OPTION, array() ); $product_types = isset( $profiler_data['product_types'] ) ? $profiler_data['product_types'] : array(); return in_array( 'physical', $product_types, true ); } /** * Delete the zone count transient used in has_shipping_zones() method * to refresh the cache. */ public static function delete_zone_count_transient() { delete_transient( self::ZONE_COUNT_TRANSIENT_NAME ); } /** * Check if the store sells digital products only. * * @return bool */ private static function is_selling_digital_type_only() { $profiler_data = get_option( OnboardingProfile::DATA_OPTION, array() ); $product_types = isset( $profiler_data['product_types'] ) ? $profiler_data['product_types'] : array(); return array( 'downloads' ) === $product_types; } } OnboardingTasks/Tasks/ReviewShippingOptions.php 0000644 00000002177 15073234674 0015773 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; /** * Review Shipping Options Task */ class ReviewShippingOptions extends Task { /** * ID. * * @return string */ public function get_id() { return 'review-shipping'; } /** * Title. * * @return string */ public function get_title() { return __( 'Review shipping options', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return ''; } /** * Time. * * @return string */ public function get_time() { return ''; } /** * Task completion. * * @return bool */ public function is_complete() { return get_option( 'woocommerce_admin_reviewed_default_shipping_zones' ) === 'yes'; } /** * Task visibility. * * @return bool */ public function can_view() { return get_option( 'woocommerce_admin_created_default_shipping_zones' ) === 'yes'; } /** * Action URL. * * @return string */ public function get_action_url() { return admin_url( 'admin.php?page=wc-settings&tab=shipping' ); } } OnboardingTasks/Tasks/StoreCreation.php 0000644 00000002044 15073234674 0014226 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\Features\Onboarding; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; /** * Store Details Task */ class StoreCreation extends Task { /** * ID. * * @return string */ public function get_id() { return 'store_creation'; } /** * Title. * * @return string */ public function get_title() { /* translators: Store name */ return sprintf( __( 'You created %s', 'woocommerce' ), get_bloginfo( 'name' ) ); } /** * Content. * * @return string */ public function get_content() { return ''; } /** * Time. * * @return string */ public function get_time() { return ''; } /** * Time. * * @return string */ public function get_action_url() { return ''; } /** * Task completion. * * @return bool */ public function is_complete() { return true; } /** * Check if task is disabled. * * @return bool */ public function is_disabled() { return true; } } OnboardingTasks/Tasks/Appearance.php 0000644 00000002513 15073234674 0013505 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Admin\PageController; use Automattic\WooCommerce\Internal\Admin\Loader; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Products; use Automattic\WooCommerce\Internal\Admin\WCAdminAssets; /** * Appearance Task */ class Appearance extends Task { /** * Constructor. */ public function __construct() { if ( ! $this->is_complete() ) { add_action( 'load-theme-install.php', array( $this, 'mark_actioned' ) ); } } /** * ID. * * @return string */ public function get_id() { return 'appearance'; } /** * Title. * * @return string */ public function get_title() { return __( 'Choose your theme', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return __( "Choose a theme that best fits your brand's look and feel, then make it your own. Change the colors, add your logo, and create pages.", 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '2 minutes', 'woocommerce' ); } /** * Action label. * * @return string */ public function get_action_label() { return __( 'Choose theme', 'woocommerce' ); } } OnboardingTasks/Tasks/StoreDetails.php 0000644 00000004207 15073234674 0014052 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks; use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; /** * Store Details Task */ class StoreDetails extends Task { /** * ID. * * @return string */ public function get_id() { return 'store_details'; } /** * Title. * * @return string */ public function get_title() { if ( true === $this->get_parent_option( 'use_completed_title' ) ) { if ( $this->is_complete() ) { return __( 'You added store details', 'woocommerce' ); } return __( 'Add store details', 'woocommerce' ); } return __( 'Store details', 'woocommerce' ); } /** * Content. * * @return string */ public function get_content() { return __( 'Your store address is required to set the origin country for shipping, currencies, and payment options.', 'woocommerce' ); } /** * Time. * * @return string */ public function get_time() { return __( '4 minutes', 'woocommerce' ); } /** * Time. * * @return string */ public function get_action_url() { return ! $this->is_complete() ? admin_url( 'admin.php?page=wc-settings&tab=general&tutorial=true' ) : admin_url( 'admin.php?page=wc-settings&tab=general' ); } /** * Task completion. * * @return bool */ public function is_complete() { $country = WC()->countries->get_base_country(); $country_locale = WC()->countries->get_country_locale(); $locale = $country_locale[ $country ] ?? array(); $hide_postcode = $locale['postcode']['hidden'] ?? false; // If postcode is hidden, just check that the store address and city are set. if ( $hide_postcode ) { return get_option( 'woocommerce_store_address', '' ) !== '' && get_option( 'woocommerce_store_city', '' ) !== ''; } // Mark as completed if the store address, city and postcode are set. We don't need to check the country because it's set by default. return get_option( 'woocommerce_store_address', '' ) !== '' && get_option( 'woocommerce_store_city', '' ) !== '' && get_option( 'woocommerce_store_postcode', '' ) !== ''; } } ShippingPartnerSuggestions/DefaultShippingPartners.php 0000644 00000024412 15073234674 0017450 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\ShippingPartnerSuggestions; /** * Default Shipping Partners */ class DefaultShippingPartners { /** * Get default specs. * * @return array Default specs. */ public static function get_all() { $asset_base_url = WC()->plugin_url() . '/assets/images/shipping_partners/'; $column_layout_features = array( array( 'icon' => $asset_base_url . 'timer.svg', 'title' => __( 'Save time', 'woocommerce' ), 'description' => __( 'Automatically import order information to quickly print your labels.', 'woocommerce' ), ), array( 'icon' => $asset_base_url . 'discount.svg', 'title' => __( 'Save money', 'woocommerce' ), 'description' => __( 'Shop for the best shipping rates, and access pre-negotiated discounted rates.', 'woocommerce' ), ), array( 'icon' => $asset_base_url . 'star.svg', 'title' => __( 'Wow your shoppers', 'woocommerce' ), 'description' => __( 'Keep your customers informed with tracking notifications.', 'woocommerce' ), ), ); $check_icon = $asset_base_url . 'check.svg'; return array( array( 'id' => 'woocommerce-shipstation-integration', 'name' => 'ShipStation', 'slug' => 'woocommerce-shipstation-integration', 'description' => __( 'Powerful yet easy-to-use solution:', 'woocommerce' ), 'layout_column' => array( 'image' => $asset_base_url . 'shipstation-column.svg', 'features' => $column_layout_features, ), 'layout_row' => array( 'image' => $asset_base_url . 'shipstation-row.svg', 'features' => array( array( 'icon' => $check_icon, 'description' => __( 'Print labels from Royal Mail, Parcel Force, DPD, and many more', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Shop for the best rates, in real-time', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Connect selling channels easily', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Advance automated workflows', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( '30-days free trial', 'woocommerce' ), ), ), ), 'learn_more_link' => 'https://wordpress.org/plugins/woocommerce-shipstation-integration/', 'is_visible' => array( self::get_rules_for_countries( array( 'AU', 'CA', 'GB' ) ), ), 'available_layouts' => array( 'row', 'column' ), ), array( 'id' => 'skydropx-cotizador-y-envios', 'name' => 'Skydropx', 'slug' => 'skydropx-cotizador-y-envios', 'layout_column' => array( 'image' => $asset_base_url . 'skydropx-column.svg', 'features' => $column_layout_features, ), 'description' => '', 'learn_more_link' => 'https://wordpress.org/plugins/skydropx-cotizador-y-envios/', 'is_visible' => array( self::get_rules_for_countries( array() ), // No countries eligible for SkydropX promotion at this time. ), 'available_layouts' => array( 'column' ), ), array( 'id' => 'envia', 'name' => 'Envia', 'slug' => '', 'description' => '', 'layout_column' => array( 'image' => $asset_base_url . 'envia-column.svg', 'features' => $column_layout_features, ), 'learn_more_link' => 'https://woo.com/products/envia-shipping-and-fulfillment/', 'is_visible' => array( self::get_rules_for_countries( array( 'CL', 'AR', 'PE', 'BR', 'UY', 'GT' ) ), ), 'available_layouts' => array( 'column' ), ), array( 'id' => 'easyship-woocommerce-shipping-rates', 'name' => 'Easyship', 'slug' => 'easyship-woocommerce-shipping-rates', 'description' => __( 'Simplified shipping with: ', 'woocommerce' ), 'layout_column' => array( 'image' => $asset_base_url . 'easyship-column.svg', 'features' => $column_layout_features, ), 'layout_row' => array( 'image' => $asset_base_url . 'easyship-row.svg', 'features' => array( array( 'icon' => $check_icon, 'description' => __( 'Highly discounted shipping rates', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Seamless order sync and label printing', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Branded tracking experience', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Built-in Tax & Duties paperwork', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Free Plan Available', 'woocommerce' ), ), ), ), 'learn_more_link' => 'https://woo.com/products/easyship-shipping-rates/', 'is_visible' => array( self::get_rules_for_countries( array( 'SG', 'HK', 'AU', 'NZ' ) ), ), 'available_layouts' => array( 'row', 'column' ), ), array( 'id' => 'sendcloud-shipping', 'name' => 'Sendcloud', 'slug' => 'sendcloud-shipping', 'description' => __( 'All-in-one shipping tool:', 'woocommerce' ), 'layout_column' => array( 'image' => $asset_base_url . 'sendcloud-column.svg', 'features' => $column_layout_features, ), 'layout_row' => array( 'image' => $asset_base_url . 'sendcloud-row.svg', 'features' => array( array( 'icon' => $check_icon, 'description' => __( 'Print labels from 80+ carriers', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Process orders in just a few clicks', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Customize checkout options', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Self-service tracking & returns', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Start with a free plan', 'woocommerce' ), ), ), ), 'learn_more_link' => 'https://wordpress.org/plugins/sendcloud-shipping/', 'is_visible' => array( self::get_rules_for_countries( array( 'NL', 'AT', 'BE', 'FR', 'DE', 'ES', 'GB', 'IT' ) ), ), 'available_layouts' => array( 'row', 'column' ), ), array( 'id' => 'packlink-pro-shipping', 'name' => 'Packlink', 'slug' => 'packlink-pro-shipping', 'description' => __( 'Optimize your full shipping process:', 'woocommerce' ), 'layout_column' => array( 'image' => $asset_base_url . 'packlink-column.svg', 'features' => $column_layout_features, ), 'layout_row' => array( 'image' => $asset_base_url . 'packlink-row.svg', 'features' => array( array( 'icon' => $check_icon, 'description' => __( 'Automated, real-time order import', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Direct access to leading carriers', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Access competitive shipping prices', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Quickly bulk print labels', 'woocommerce' ), ), array( 'icon' => $check_icon, 'description' => __( 'Free shipping platform', 'woocommerce' ), ), ), ), 'learn_more_link' => 'https://wordpress.org/plugins/packlink-pro-shipping/', 'is_visible' => array( self::get_rules_for_countries( array( 'FR', 'DE', 'ES', 'IT' ) ), ), 'available_layouts' => array( 'row', 'column' ), ), array( 'id' => 'woocommerce-services', 'name' => 'WooCommerce Shipping', 'slug' => 'woocommerce-services', 'description' => __( 'Save time and money by printing your shipping labels right from your computer with WooCommerce Shipping. Try WooCommerce Shipping for free.', 'woocommerce' ), 'dependencies' => array( 'jetpack' ), 'layout_column' => array( 'image' => $asset_base_url . 'wcs-column.svg', 'features' => array( array( 'icon' => $asset_base_url . 'printer.svg', 'title' => __( 'Buy postage when you need it', 'woocommerce' ), 'description' => __( 'No need to wonder where that stampbook went.', 'woocommerce' ), ), array( 'icon' => $asset_base_url . 'paper.svg', 'title' => __( 'Print at home', 'woocommerce' ), 'description' => __( 'Pick up an order, then just pay, print, package and post.', 'woocommerce' ), ), array( 'icon' => $asset_base_url . 'discount.svg', 'title' => __( 'Discounted rates', 'woocommerce' ), 'description' => __( 'Access discounted shipping rates with DHL and USPS.', 'woocommerce' ), ), ), ), 'learn_more_link' => 'https://woo.com/products/shipping/', 'is_visible' => array( self::get_rules_for_countries( array( 'US' ) ), ), 'available_layouts' => array( 'column' ), ), ); } /** * Get rules that match the store base location to one of the provided countries. * * @param array $countries Array of countries to match. * @return object Rules to match. */ public static function get_rules_for_countries( $countries ) { $rules = array(); foreach ( $countries as $country ) { $rules[] = (object) array( 'type' => 'base_location_country', 'value' => $country, 'operation' => '=', ); } return (object) array( 'type' => 'or', 'operands' => $rules, ); } } ShippingPartnerSuggestions/ShippingPartnerSuggestionsDataSourcePoller.php 0000644 00000001552 15073234674 0023344 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\ShippingPartnerSuggestions; use Automattic\WooCommerce\Admin\DataSourcePoller; /** * Specs data source poller class for shipping partner suggestions. */ class ShippingPartnerSuggestionsDataSourcePoller extends DataSourcePoller { /** * Data Source Poller ID. */ const ID = 'shipping_partner_suggestions'; /** * Default data sources array. */ const DATA_SOURCES = array( 'https://woocommerce.com/wp-json/wccom/shipping-partner-suggestions/1.0/suggestions.json', ); /** * Class instance. * * @var ShippingPartnerSuggestionsDataSourcePoller instance */ protected static $instance = null; /** * Get class instance. */ public static function get_instance() { if ( ! self::$instance ) { self::$instance = new self( self::ID, self::DATA_SOURCES ); } return self::$instance; } } ShippingPartnerSuggestions/ShippingPartnerSuggestions.php 0000644 00000003676 15073234674 0020224 0 ustar 00 <?php namespace Automattic\WooCommerce\Admin\Features\ShippingPartnerSuggestions; use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RuleEvaluator; /** * Class ShippingPartnerSuggestions */ class ShippingPartnerSuggestions { /** * Go through the specs and run them. * * @param array|null $specs shipping partner suggestion spec array. * @return array */ public static function get_suggestions( $specs = null ) { $suggestions = array(); if ( null === $specs ) { $specs = self::get_specs_from_datasource(); } $rule_evaluator = new RuleEvaluator(); foreach ( $specs as &$spec ) { $spec = is_array( $spec ) ? (object) $spec : $spec; if ( isset( $spec->is_visible ) ) { $is_visible = $rule_evaluator->evaluate( $spec->is_visible ); if ( $is_visible ) { $spec->is_visible = true; $suggestions[] = $spec; } } } return $suggestions; } /** * Get specs or fetch remotely if they don't exist. */ public static function get_specs_from_datasource() { if ( 'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) ) { /** * It can be used to modify shipping partner suggestions spec. * * @since 7.4.1 */ return apply_filters( 'woocommerce_admin_shipping_partner_suggestions_specs', DefaultShippingPartners::get_all() ); } $specs = ShippingPartnerSuggestionsDataSourcePoller::get_instance()->get_specs_from_data_sources(); // Fetch specs if they don't yet exist. if ( false === $specs || ! is_array( $specs ) || 0 === count( $specs ) ) { /** * It can be used to modify shipping partner suggestions spec. * * @since 7.4.1 */ return apply_filters( 'woocommerce_admin_shipping_partner_suggestions_specs', DefaultShippingPartners::get_all() ); } /** * It can be used to modify shipping partner suggestions spec. * * @since 7.4.1 */ return apply_filters( 'woocommerce_admin_shipping_partner_suggestions_specs', $specs ); } } NewProductManagementExperience.php 0000644 00000005022 15073234674 0013366 0 ustar 00 <?php /** * WooCommerce New Product Management Experience */ namespace Automattic\WooCommerce\Admin\Features; use Automattic\WooCommerce\Admin\Features\TransientNotices; use Automattic\WooCommerce\Admin\PageController; use Automattic\WooCommerce\Internal\Admin\Loader; use WP_Block_Editor_Context; /** * Loads assets related to the new product management experience page. */ class NewProductManagementExperience { /** * Option name used to toggle this feature. */ const TOGGLE_OPTION_NAME = 'woocommerce_new_product_management_enabled'; /** * Constructor */ public function __construct() { $this->maybe_show_disabled_notice(); if ( ! Features::is_enabled( 'new-product-management-experience' ) ) { return; } add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) ); add_action( 'get_edit_post_link', array( $this, 'update_edit_product_link' ), 10, 2 ); } /** * Maybe show disabled notice. */ public function maybe_show_disabled_notice() { $new_product_experience_param = 'new-product-experience-disabled'; if ( isset( $_GET[ $new_product_experience_param ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended TransientNotices::add( array( 'user_id' => get_current_user_id(), 'id' => 'new-product-experience-disbled', 'status' => 'success', 'content' => __( '🌟 Thanks for the feedback. We’ll put it to good use!', 'woocommerce' ), ) ); $url = isset( $_SERVER['REQUEST_URI'] ) ? wc_clean( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; $url = remove_query_arg( 'new-product-experience-disabled', $url ); wp_safe_redirect( $url ); exit; } } /** * Enqueue styles needed for the rich text editor. */ public function enqueue_styles() { if ( ! PageController::is_admin_or_embed_page() ) { return; } wp_enqueue_style( 'wp-edit-blocks' ); wp_enqueue_style( 'wp-format-library' ); wp_enqueue_editor(); /** * Enqueue any block editor related assets. * * @since 7.1.0 */ do_action( 'enqueue_block_editor_assets' ); } /** * Update the edit product links when the new experience is enabled. * * @param string $link The edit link. * @param int $post_id Post ID. * @return string */ public function update_edit_product_link( $link, $post_id ) { $product = wc_get_product( $post_id ); if ( ! $product ) { return $link; } if ( $product->get_type() === 'simple' ) { return admin_url( 'admin.php?page=wc-admin&path=/product/' . $product->get_id() ); } return $link; } } TransientNotices.php 0000644 00000005450 15073234674 0010570 0 ustar 00 <?php /** * WooCommerce Transient Notices */ namespace Automattic\WooCommerce\Admin\Features; use Automattic\WooCommerce\Internal\Admin\Loader; /** * Shows print shipping label banner on edit order page. */ class TransientNotices { /** * Option name for the queue. */ const QUEUE_OPTION = 'woocommerce_admin_transient_notices_queue'; /** * Constructor */ public function __construct() { add_filter( 'woocommerce_admin_preload_options', array( $this, 'preload_options' ) ); } /** * Get all notices in the queue. * * @return array */ public static function get_queue() { return get_option( self::QUEUE_OPTION, array() ); } /** * Get all notices in the queue by a given user ID. * * @param int $user_id User ID. * @return array */ public static function get_queue_by_user( $user_id ) { $notices = self::get_queue(); return array_filter( $notices, function( $notice ) use ( $user_id ) { return ! isset( $notice['user_id'] ) || null === $notice['user_id'] || $user_id === $notice['user_id']; } ); } /** * Get a notice by ID. * * @param array $notice_id Notice of ID to get. * @return array|null */ public static function get( $notice_id ) { $queue = self::get_queue(); if ( isset( $queue[ $notice_id ] ) ) { return $queue[ $notice_id ]; } return null; } /** * Add a notice to be shown. * * @param array $notice Notice. * $notice = array( * 'id' => (string) Unique ID for the notice. Required. * 'user_id' => (int|null) User ID to show the notice to. * 'status' => (string) info|error|success * 'content' => (string) Content to be shown for the notice. Required. * 'options' => (array) Array of options to be passed to the notice component. * See https://developer.wordpress.org/block-editor/reference-guides/data/data-core-notices/#createNotice for available options. * ). */ public static function add( $notice ) { $queue = self::get_queue(); $defaults = array( 'user_id' => null, 'status' => 'info', 'options' => array(), ); $notice_data = array_merge( $defaults, $notice ); $notice_data['options'] = (object) $notice_data['options']; $queue[ $notice['id'] ] = $notice_data; update_option( self::QUEUE_OPTION, $queue ); } /** * Remove a notice by ID. * * @param array $notice_id Notice of ID to remove. */ public static function remove( $notice_id ) { $queue = self::get_queue(); unset( $queue[ $notice_id ] ); update_option( self::QUEUE_OPTION, $queue ); } /** * Preload options to prime state of the application. * * @param array $options Array of options to preload. * @return array */ public function preload_options( $options ) { $options[] = self::QUEUE_OPTION; return $options; } } Onboarding.php 0000644 00000006356 15073234674 0007364 0 ustar 00 <?php /** * WooCommerce Onboarding */ namespace Automattic\WooCommerce\Admin\Features; use Automattic\WooCommerce\Admin\DeprecatedClassFacade; /** * Contains backend logic for the onboarding profile and checklist feature. * * @deprecated since 6.3.0, use WooCommerce\Internal\Admin\Onboarding. */ class Onboarding extends DeprecatedClassFacade { /** * The name of the non-deprecated class that this facade covers. * * @var string */ protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\Features\Onboarding'; /** * The version that this class was deprecated in. * * @var string */ protected static $deprecated_in_version = '6.3.0'; /** * Hook into WooCommerce. */ public function __construct() { } /** * Get a list of allowed industries for the onboarding wizard. * * @deprecated 6.3.0 * @return array */ public static function get_allowed_industries() { wc_deprecated_function( 'get_allowed_industries', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingIndustries::get_allowed_industries()' ); return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingIndustries::get_allowed_industries(); } /** * Get a list of allowed product types for the onboarding wizard. * * @deprecated 6.3.0 * @return array */ public static function get_allowed_product_types() { wc_deprecated_function( 'get_allowed_product_types', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingProducts::get_allowed_product_types()' ); return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProducts::get_allowed_product_types(); } /** * Get a list of themes for the onboarding wizard. * * @deprecated 6.3.0 * @return array */ public static function get_themes() { wc_deprecated_function( 'get_themes', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingThemes::get_themes()' ); return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingThemes::get_themes(); } /** * Get theme data used in onboarding theme browser. * * @deprecated 6.3.0 * @param WP_Theme $theme Theme to gather data from. * @return array */ public static function get_theme_data( $theme ) { wc_deprecated_function( 'get_theme_data', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingThemes::get_theme_data()' ); return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingThemes::get_theme_data(); } /** * Gets an array of themes that can be installed & activated via the onboarding wizard. * * @deprecated 6.3.0 * @return array */ public static function get_allowed_themes() { wc_deprecated_function( 'get_allowed_themes', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingThemes::get_allowed_themes()' ); return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingThemes::get_allowed_themes(); } /** * Get dynamic product data from API. * * @deprecated 6.3.0 * @param array $product_types Array of product types. * @return array */ public static function get_product_data( $product_types ) { wc_deprecated_function( 'get_product_data', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingProducts::get_product_data()' ); return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProducts::get_product_data(); } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.01 |
proxy
|
phpinfo
|
Settings