AsyncProductEditorCategoryField/Init.php 0000644 00000004641 15073234674 0014427 0 ustar 00 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 is_product_editor_page( wp_get_referer() ) ) {
return 'product-block-editor-v1';
}
return $source;
}
}
ProductBlockEditor/BlockRegistry.php 0000644 00000017575 15073234674 0013634 0 ustar 00 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 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 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 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 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 $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 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 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 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 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 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 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 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 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 '',
'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 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] .= ' ' . absint( $tasks_count ) . ''; // 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 is_connected()
: false;
}
return $settings;
}
}
OnboardingTasks/TaskList.php 0000644 00000022162 15073234674 0012121 0 ustar 00 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 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 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 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 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 Terms of Service (including WooPay merchant terms) and acknowledge that you have read our Privacy Policy',
'woocommerce'
);
}
return __(
'By using WooPayments you agree to be bound by our Terms of Service and acknowledge that you have read our Privacy Policy',
'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 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 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 is_connected() && $jetpack_connection_manager->has_connected_owner();
}
}
OnboardingTasks/Tasks/GetMobileApp.php 0000644 00000005014 15073234674 0013755 0 ustar 00 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 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 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 '
';
}
}
}
OnboardingTasks/Tasks/Products.php 0000644 00000007634 15073234674 0013262 0 ustar 00 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 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 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 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 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 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 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 (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