woocommerce/plugins/woocommerce-blocks/woocommerce-gutenberg-produ...

556 lines
17 KiB
PHP

<?php
/**
* Plugin Name: WooCommerce Blocks
* Plugin URI: https://github.com/woocommerce/woocommerce-gutenberg-products-block
* Description: WooCommerce blocks for the Gutenberg editor.
* Version: 1.5.0-dev
* Author: Automattic
* Author URI: https://woocommerce.com
* Text Domain: woo-gutenberg-products-block
* WC requires at least: 3.3
* WC tested up to: 3.5
*
* @package WooCommerce\Blocks
*/
defined( 'ABSPATH' ) || die();
define( 'WGPB_VERSION', '1.4.0' );
define( 'WGPB_DEVELOPMENT_MODE', true );
/**
* Load up the assets if Gutenberg is active.
*/
function wgpb_initialize() {
$files_exist = file_exists( plugin_dir_path( __FILE__ ) . '/build/products-block.js' );
if ( $files_exist && function_exists( 'register_block_type' ) ) {
add_action( 'init', 'wgpb_register_blocks' );
add_action( 'init', 'wgpb_register_scripts' );
add_filter( 'block_categories', 'wgpb_add_block_category' );
}
if ( defined( 'WGPB_DEVELOPMENT_MODE' ) && WGPB_DEVELOPMENT_MODE && ! $files_exist ) {
add_action( 'admin_notices', 'wgpb_plugins_notice' );
}
add_action( 'rest_api_init', 'wgpb_register_api_routes' );
}
add_action( 'woocommerce_loaded', 'wgpb_initialize' );
/**
* Display a warning about building files.
*/
function wgpb_plugins_notice() {
echo '<div class="error"><p>';
printf(
/* Translators: %1$s is the install command, %2$s is the build command, %3$s is the watch command. */
esc_html__( 'WooCommerce Blocks development mode requires files to be built. From the plugin directory, run %1$s to install dependencies, %2$s to build the files or %3$s to build the files and watch for changes.', 'woo-gutenberg-products-block' ),
'<code>npm install</code>',
'<code>npm run build</code>',
'<code>npm start</code>'
);
echo '</p></div>';
}
/**
* Adds a WooCommerce category to the block inserter.
*
* @param array $categories Array of categories.
* @return array Array of block categories.
*/
function wgpb_add_block_category( $categories ) {
return array_merge(
$categories,
array(
array(
'slug' => 'woocommerce',
'title' => __( 'WooCommerce', 'woo-gutenberg-products-block' ),
'icon' => 'woocommerce',
),
)
);
}
/**
* Register the Products block and its scripts.
*/
function wgpb_register_blocks() {
include_once dirname( __FILE__ ) . '/includes/blocks/class-wc-block-featured-product.php';
// Legacy block.
register_block_type(
'woocommerce/products',
array(
'editor_script' => 'woocommerce-products-block-editor',
'editor_style' => 'woocommerce-products-block-editor',
)
);
// New blocks.
register_block_type(
'woocommerce/handpicked-products',
array(
'editor_script' => 'wc-handpicked-products',
'editor_style' => 'wc-handpicked-products-editor',
)
);
register_block_type(
'woocommerce/product-best-sellers',
array(
'editor_script' => 'wc-product-best-sellers',
'editor_style' => 'wc-product-best-sellers-editor',
)
);
register_block_type(
'woocommerce/product-category',
array(
'editor_script' => 'wc-product-category',
'editor_style' => 'wc-product-category-editor',
)
);
register_block_type(
'woocommerce/product-new',
array(
'editor_script' => 'wc-product-new',
'editor_style' => 'wc-product-new-editor',
)
);
register_block_type(
'woocommerce/product-on-sale',
array(
'editor_script' => 'wc-product-on-sale',
'editor_style' => 'wc-product-on-sale-editor',
)
);
register_block_type(
'woocommerce/product-top-rated',
array(
'editor_script' => 'wc-product-top-rated',
'editor_style' => 'wc-product-top-rated-editor',
)
);
register_block_type(
'woocommerce/products-by-attribute',
array(
'editor_script' => 'wc-products-attribute',
'editor_style' => 'wc-products-attribute-editor',
)
);
register_block_type(
'woocommerce/featured-product',
array(
'render_callback' => array( 'WC_Block_Featured_Product', 'render' ),
'editor_script' => 'wc-featured-product',
'style' => 'wc-featured-product-editor',
)
);
}
/**
* Register extra scripts needed.
*/
function wgpb_register_scripts() {
if ( ! function_exists( 'wc_get_theme_support' ) ) {
return;
}
$block_dependencies = array(
'wp-api-fetch',
'wp-blocks',
'wp-components',
'wp-compose',
'wp-data',
'wp-element',
'wp-editor',
'wp-i18n',
'wp-url',
'lodash',
'wc-vendors',
);
// @todo Remove this dependency (as it adds a separate react instance).
wp_register_script(
'react-transition-group',
plugins_url( 'assets/js/vendor/react-transition-group.js', __FILE__ ),
array(),
'2.2.1',
true
);
wp_register_script(
'wc-vendors',
plugins_url( 'build/vendors.js', __FILE__ ),
array(),
wgpb_get_file_version( '/build/vendors.js' ),
true
);
wp_register_style(
'wc-vendors',
plugins_url( 'build/vendors.css', __FILE__ ),
array(),
wgpb_get_file_version( '/build/vendors.css' )
);
wp_register_style(
'wc-products-grid',
plugins_url( 'build/products-grid.css', __FILE__ ),
array(),
wgpb_get_file_version( '/build/products-grid.css' )
);
wp_register_script(
'wc-handpicked-products',
plugins_url( 'build/handpicked-products.js', __FILE__ ),
$block_dependencies,
wgpb_get_file_version( '/build/handpicked-products.js' ),
true
);
wp_register_style(
'wc-handpicked-products-editor',
plugins_url( 'build/handpicked-products.css', __FILE__ ),
array( 'wc-vendors', 'wp-edit-blocks', 'wc-products-grid' ),
wgpb_get_file_version( '/build/handpicked-products.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-handpicked-products', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
wp_register_script(
'wc-product-best-sellers',
plugins_url( 'build/product-best-sellers.js', __FILE__ ),
$block_dependencies,
wgpb_get_file_version( '/build/product-best-sellers.js' ),
true
);
wp_register_style(
'wc-product-best-sellers-editor',
plugins_url( 'build/product-best-sellers.css', __FILE__ ),
array( 'wc-vendors', 'wp-edit-blocks', 'wc-products-grid' ),
wgpb_get_file_version( '/build/product-best-sellers.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-product-best-sellers', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
wp_register_script(
'wc-product-category',
plugins_url( 'build/product-category.js', __FILE__ ),
$block_dependencies,
wgpb_get_file_version( '/build/product-category.js' ),
true
);
wp_register_style(
'wc-product-category-editor',
plugins_url( 'build/product-category.css', __FILE__ ),
array( 'wc-vendors', 'wp-edit-blocks', 'wc-products-grid' ),
wgpb_get_file_version( '/build/product-category.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-product-category', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
wp_register_script(
'wc-product-new',
plugins_url( 'build/product-new.js', __FILE__ ),
$block_dependencies,
wgpb_get_file_version( '/build/product-new.js' ),
true
);
wp_register_style(
'wc-product-new-editor',
plugins_url( 'build/product-new.css', __FILE__ ),
array( 'wc-vendors', 'wp-edit-blocks', 'wc-products-grid' ),
wgpb_get_file_version( '/build/product-new.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-product-new', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
wp_register_script(
'wc-product-on-sale',
plugins_url( 'build/product-on-sale.js', __FILE__ ),
$block_dependencies,
wgpb_get_file_version( '/build/product-on-sale.js' ),
true
);
wp_register_style(
'wc-product-on-sale-editor',
plugins_url( 'build/product-on-sale.css', __FILE__ ),
array( 'wc-vendors', 'wp-edit-blocks', 'wc-products-grid' ),
wgpb_get_file_version( '/build/product-on-sale.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-product-on-sale', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
wp_register_script(
'wc-product-top-rated',
plugins_url( 'build/product-top-rated.js', __FILE__ ),
$block_dependencies,
wgpb_get_file_version( '/build/product-top-rated.js' ),
true
);
wp_register_style(
'wc-product-top-rated-editor',
plugins_url( 'build/product-top-rated.css', __FILE__ ),
array( 'wc-vendors', 'wp-edit-blocks', 'wc-products-grid' ),
wgpb_get_file_version( '/build/product-top-rated.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-product-top-rated', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
wp_register_script(
'wc-featured-product',
plugins_url( 'build/featured-product.js', __FILE__ ),
$block_dependencies,
wgpb_get_file_version( '/build/featured-product.js' ),
true
);
wp_register_style(
'wc-featured-product-editor',
plugins_url( 'build/featured-product.css', __FILE__ ),
array( 'wc-vendors' ),
wgpb_get_file_version( '/build/featured-product.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-featured-product', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
wp_register_script(
'wc-products-attribute',
plugins_url( 'build/products-attribute.js', __FILE__ ),
$block_dependencies,
wgpb_get_file_version( '/build/products-attribute.js' ),
true
);
wp_register_style(
'wc-products-attribute-editor',
plugins_url( 'build/products-attribute.css', __FILE__ ),
array( 'wc-vendors', 'wp-edit-blocks', 'wc-products-grid' ),
wgpb_get_file_version( '/build/products-attribute.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-products-attribute', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
wp_register_script(
'woocommerce-products-block-editor',
plugins_url( 'build/products-block.js', __FILE__ ),
array( 'wp-api-fetch', 'wp-element', 'wp-components', 'wp-blocks', 'wp-editor', 'wp-i18n', 'react-transition-group' ),
wgpb_get_file_version( '/build/products-block.js' ),
true
);
wp_register_style(
'woocommerce-products-block-editor',
plugins_url( 'build/products-block.css', __FILE__ ),
array( 'wc-vendors', 'wp-edit-blocks' ),
wgpb_get_file_version( '/build/products-block.css' )
);
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'woocommerce-products-block-editor', 'woo-gutenberg-products-block', plugin_dir_path( __FILE__ ) . 'languages' );
}
add_action( 'admin_print_footer_scripts', 'wgpb_print_script_settings', 1 );
}
/**
* Get the file modified time as a cache buster if we're in dev mode.
*
* @param string $file Local path to the file.
* @return string The cache buster value to use for the given file.
*/
function wgpb_get_file_version( $file ) {
if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
$file = trim( $file, '/' );
return filemtime( plugin_dir_path( __FILE__ ) . $file );
}
return WGPB_VERSION;
}
/**
* Output the wcSettings global before printing any script tags.
*/
function wgpb_print_script_settings() {
global $wp_locale;
$code = get_woocommerce_currency();
// NOTE: wcSettings is not used directly, it's only for @woocommerce/components
//
// Settings and variables can be passed here for access in the app.
// Will need `wcAdminAssetUrl` if the ImageAsset component is used.
// Will need `dataEndpoints.countries` if Search component is used with 'country' type.
// Will need `orderStatuses` if the OrderStatus component is used.
// Deliberately excluding: `embedBreadcrumbs`, `trackingEnabled`.
$settings = array(
'adminUrl' => admin_url(),
'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ),
'siteLocale' => esc_attr( get_bloginfo( 'language' ) ),
'currency' => array(
'code' => $code,
'precision' => wc_get_price_decimals(),
'symbol' => get_woocommerce_currency_symbol( $code ),
'position' => get_option( 'woocommerce_currency_pos' ),
),
'stockStatuses' => wc_get_product_stock_status_options(),
'siteTitle' => get_bloginfo( 'name' ),
'dataEndpoints' => array(),
'l10n' => array(
'userLocale' => get_user_locale(),
'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
),
);
// Global settings used in each block.
$block_settings = array(
'min_columns' => wc_get_theme_support( 'product_grid::min_columns', 1 ),
'max_columns' => wc_get_theme_support( 'product_grid::max_columns', 6 ),
'default_columns' => wc_get_default_products_per_row(),
'min_rows' => wc_get_theme_support( 'product_grid::min_rows', 1 ),
'max_rows' => wc_get_theme_support( 'product_grid::max_rows', 6 ),
'default_rows' => wc_get_default_product_rows_per_page(),
'placeholderImgSrc' => wc_placeholder_img_src(),
'min_height' => wc_get_theme_support( 'featured_block::min_height', 500 ),
'default_height' => wc_get_theme_support( 'featured_block::default_height', 500 ),
);
?>
<script type="text/javascript">
<?php // Use the wcSettings from wc-admin if already present. ?>
var wcSettings = wcSettings || <?php echo wp_json_encode( $settings ); ?>;
var wc_product_block_data = <?php echo wp_json_encode( $block_settings ); ?>;
</script>
<?php
}
/**
* Register extra API routes with functionality specific for product blocks.
*/
function wgpb_register_api_routes() {
include_once dirname( __FILE__ ) . '/includes/class-wgpb-products-controller.php';
include_once dirname( __FILE__ ) . '/includes/class-wgpb-product-categories-controller.php';
include_once dirname( __FILE__ ) . '/includes/class-wgpb-product-attributes-controller.php';
include_once dirname( __FILE__ ) . '/includes/class-wgpb-product-attribute-terms-controller.php';
$products = new WGPB_Products_Controller();
$products->register_routes();
$categories = new WGPB_Product_Categories_Controller();
$categories->register_routes();
$attributes = new WGPB_Product_Attributes_Controller();
$attributes->register_routes();
$attribute_terms = new WGPB_Product_Attribute_Terms_Controller();
$attribute_terms->register_routes();
}
/**
* Brings some extra required shortcode features from WC core 3.4+ to this feature plugin.
*
* @todo Remove this function when merging into core because it won't be necessary.
*
* @param array $args WP_Query args.
* @param array $attributes Shortcode attributes.
* @param string $type Type of shortcode currently processing.
*/
function wgpb_extra_shortcode_features( $args, $attributes, $type ) {
if ( 'products' !== $type ) {
return $args;
}
// Enable term ids in the category shortcode.
if ( ! empty( $attributes['category'] ) ) {
$categories = array_map( 'sanitize_title', explode( ',', $attributes['category'] ) );
$field = 'slug';
if ( empty( $args['tax_query'] ) ) {
$args['tax_query'] = array(); // WPCS: slow query ok.
}
// Unset old category tax query.
foreach ( $args['tax_query'] as $index => $tax_query ) {
if ( 'product_cat' === $tax_query['taxonomy'] ) {
unset( $args['tax_query'][ $index ] );
}
}
if ( is_numeric( $categories[0] ) ) {
$categories = array_map( 'absint', $categories );
$field = 'term_id';
}
$args['tax_query'][] = array(
'taxonomy' => 'product_cat',
'terms' => $categories,
'field' => $field,
'operator' => $attributes['cat_operator'],
// See https://github.com/woocommerce/woocommerce/pull/20207/files#diff-9982e2749834d5232f1ed411b6c20312.
'include_children' => 'AND' === $attributes['cat_operator'] ? false : true,
);
}
// Enable term ids in the attributes shortcode and just-attribute queries.
if ( ! empty( $attributes['attribute'] ) || ! empty( $attributes['terms'] ) ) {
$taxonomy = strstr( $attributes['attribute'], 'pa_' ) ? sanitize_title( $attributes['attribute'] ) : 'pa_' . sanitize_title( $attributes['attribute'] );
$terms = $attributes['terms'] ? array_map( 'sanitize_title', explode( ',', $attributes['terms'] ) ) : array();
$field = 'slug';
if ( empty( $args['tax_query'] ) ) {
$args['tax_query'] = array(); // WPCS: slow query ok.
}
// Unset old attribute tax query.
foreach ( $args['tax_query'] as $index => $tax_query ) {
if ( $taxonomy === $tax_query['taxonomy'] ) {
unset( $args['tax_query'][ $index ] );
}
}
if ( $terms && is_numeric( $terms[0] ) ) {
$terms = array_map( 'absint', $terms );
$field = 'term_id';
}
// If no terms were specified get all products that are in the attribute taxonomy.
if ( ! $terms ) {
$terms = get_terms(
array(
'taxonomy' => $taxonomy,
'fields' => 'ids',
)
);
$field = 'term_id';
}
$args['tax_query'][] = array(
'taxonomy' => $taxonomy,
'terms' => $terms,
'field' => $field,
'operator' => $attributes['terms_operator'],
);
}
return $args;
}
add_filter( 'woocommerce_shortcode_products_query', 'wgpb_extra_shortcode_features', 10, 3 );