prepare( "
SELECT $wpdb->terms.term_id FROM $wpdb->terms
INNER JOIN $wpdb->term_taxonomy ON $wpdb->term_taxonomy.term_id = $wpdb->terms.term_id
WHERE $wpdb->term_taxonomy.taxonomy = %s
AND $wpdb->terms.slug IN ('" . implode( "','", array_map( 'esc_attr', $slug_terms ) ) . "')
", $taxonomy ); // WPCS: unprepared SQL ok.
$search_terms_ids = $wpdb->get_results( $query, ARRAY_A ); // WPCS: unprepared SQL ok.
$result = array();
// Flat array.
array_walk_recursive( $search_terms_ids, function ( $v, $k ) use ( &$result ) {
$result[] = absint( $v );
} );
return $result;
}
/**
* Set the help tab for admin pages
*
* @since 1.3.0
*
* @param array $help_tabs
* @param Object $obj
*/
public static function add_help_tab( $help_tabs, $obj ) {
$screen = get_current_screen();
foreach ( $help_tabs as $help_tab ) {
$screen->add_help_tab( array_merge( array(
'id' => ATUM_PREFIX . get_class( $obj ) . '_help_tabs_' . $help_tab['name'],
'callback' => array( $obj, 'help_tabs_content' ),
), $help_tab ) );
}
$screen->set_help_sidebar( self::load_view_to_string( 'help-tabs/help-sidebar' ) );
}
/**
* Converts an associative array to HTML data attributes
*
* @since 1.4.0
*
* @param array $array The array to convert.
* @param string $prefix Optional. Prefix for the data key names.
*
* @return string
*/
public static function array_to_data( $array, $prefix = '' ) {
$data_array = array_map( function( $key, $value ) use ( $prefix ) {
return "data-{$prefix}{$key}='$value'";
}, array_keys( $array ), $array );
return implode( ' ', $data_array );
}
/**
* Outputs the add-on to append/prepend to ATUM fields
*
* @since 1.4.1
*
* @param string $side
*/
public static function atum_field_input_addon( $side = 'prepend' ) {
?>
$value ) {
// Recursive calls.
$data_att .= self::get_data_att( $name, $value, $quote_symbol );
}
}
else {
$data_att = ' data-' . $att . '=' . $quote_symbol . $value . $quote_symbol;
}
return $data_att;
}
/**
* Get a formatted HTML attribute string or an empty string if has an empty value
*
* @since 0.0.2
*
* @param string $att The attribute name.
* @param string|int $value The attribute value.
* @param bool $force Force the attribute output without checking if it's empty.
*
* @return string
*/
protected function get_att( $att, $value, $force = FALSE ) {
if ( ! empty( $value ) || $force ) {
return ' ' . $att . '="' . $value . '"';
}
return '';
}
/**
* Get a list of all the products used for calculating stats
*
* @since 1.4.1
*
* @param array $args
* @param bool $remove_variables
*
* @return array
*/
public static function get_all_products( $args = array(), $remove_variables = FALSE ) {
$defaults = array(
'post_type' => 'product',
'post_status' => current_user_can( 'edit_private_products' ) ? [ 'private', 'publish' ] : [ 'publish' ],
'posts_per_page' => - 1,
'fields' => 'ids',
);
$transient_name = $remove_variables ? 'all_products_no_variables' : 'all_products';
$args = (array) apply_filters( 'atum/get_all_products/args', array_merge( $defaults, $args ) );
$product_ids_transient = AtumCache::get_transient_key( $transient_name, $args );
$products = AtumCache::get_transient( $product_ids_transient );
if ( ! $products ) {
$products = get_posts( $args );
if ( $remove_variables ) {
$args = (array) array_merge( $args, array(
'post_type' => 'product_variation',
'post__in' => $products,
'fields' => 'id=>parent',
) );
$variables = array_unique( get_posts( $args ) );
$products = array_diff( $products, $variables );
}
AtumCache::set_transient( $product_ids_transient, $products, HOUR_IN_SECONDS );
}
return $products;
}
/**
* Get the right ATUM Product class when instantiating a WC product
*
* @since 1.5.0
*
* @param string $product_type
*
* @return string
*/
public static function get_atum_product_class( $product_type ) {
$namespace = '\Atum\Models\Products';
$product_type = self::sanitize_psr4_class_name( $product_type );
$class_name = "$namespace\AtumProduct{$product_type}";
if ( class_exists( $class_name ) ) {
return $class_name;
}
// As fallback, return the simple product class.
return "$namespace\AtumProductSimple";
}
/**
* Formats a class name following PSR-4 naming conventions
*
* @since 1.5.1
*
* @param string $class_name
*
* @return string
*/
public static function sanitize_psr4_class_name( $class_name ) {
return str_replace( array( '_', '-' ), '', ucwords( $class_name, ' _-' ) );
}
/**
* Returns an array with the orders filtered by the atts array
*
* @since 0.0.1
*
* @param array|string $atts {
* Optional. Filters for the orders' query.
*
* @type array|string $type Order post type(s).
* @type array|string $status Order status(es).
* @type array $orders_in Array of order's IDs we want to get.
* @type int $number Max number of orders (-1 gets all).
* @type string $meta_key Key of the meta field to filter/order (depending of orderby value).
* @type mixed $meta_value Value of the meta field to filter/order(depending of orderby value).
* @type string $meta_type Meta key type. Default value is 'CHAR'.
* @type string $meta_compare Operator to test the meta value when filtering (See possible values: https://codex.wordpress.org/Class_Reference/WP_Meta_Query ).
* @type string $order ASC/DESC, default to DESC.
* @type string $orderby Field used to sort results (see WP_QUERY). Default to date (post_date).
* @type int $date_start If has value, filters the orders between this and the $order_date_end (must be a string format convertible with strtotime).
* @type int $date_end Requires $date_start. If has value, filters the orders completed/processed before this date (must be a string format convertible with strtotime). Default: Now.
* @type string $fields If empty will return all the order posts. For returning only IDs the value must be 'ids'.
* }
*
* @return \WC_Order|array
*/
public static function get_orders( $atts = array() ) {
$atts = (array) apply_filters( 'atum/get_orders/params', wp_parse_args( $atts, array(
'type' => 'shop_order',
'status' => '',
'orders_in' => '',
'number' => - 1,
'meta_key' => '',
'meta_value' => '',
'meta_type' => '',
'meta_compare' => '',
'order' => '',
'orderby' => '',
'date_start' => '',
'date_end' => '',
'fields' => '',
) ) );
$cache_key = AtumCache::get_cache_key( 'orders', $atts );
$orders = AtumCache::get_cache( $cache_key, ATUM_TEXT_DOMAIN, FALSE, $has_cache );
if ( $has_cache ) {
return $orders;
}
/**
* Extract params
*
* @var array|string $type
* @var array|string $status
* @var array|string $orders_in
* @var int $number
* @var string $meta_key
* @var mixed $meta_value
* @var string $meta_type
* @var string $meta_compare
* @var string $order
* @var string $orderby
* @var string $date_start
* @var string $date_end
* @var string $fields
*/
extract( $atts );
// WP_Query arguments.
$args = array(
'offset' => 0,
);
// Post Type.
$wc_order_types = wc_get_order_types();
$order_types = (array) $type;
$valid_order_types = array();
// Validate order types.
foreach ( $order_types as $ot ) {
if ( in_array( $ot, $wc_order_types ) ) {
$valid_order_types[] = $ot;
}
}
$args['post_type'] = $valid_order_types;
// Order Status.
$valid_order_statuses = array();
$wc_order_statuses = array_keys( wc_get_order_statuses() );
$order_statuses = (array) $status;
// Validate post statuses.
foreach ( $order_statuses as $os ) {
if ( in_array( $os, $wc_order_statuses ) ) {
$valid_order_statuses[] = $os;
}
}
$args['post_status'] = ! empty( $valid_order_statuses ) ? $valid_order_statuses : $wc_order_statuses;
// Selected posts.
if ( $orders_in ) {
if ( ! is_array( $orders_in ) ) {
$orders_in = explode( ',', $orders_in );
}
$args['post__in'] = array_map( 'absint', $orders_in );
}
$args['posts_per_page'] = intval( $number );
// Filter/Order by meta key.
if ( $meta_key ) {
$meta_query = array(
'key' => esc_attr( $meta_key ),
);
$meta_type = strtoupper( esc_attr( $meta_type ) );
if ( in_array( $meta_type, [ 'NUMERIC', 'DECIMAL' ] ) ) {
$meta_query['value'] = floatval( $meta_value );
$meta_query['type'] = $meta_type;
}
else {
$meta_query['value'] = esc_attr( $meta_value );
if ( $meta_type ) {
$meta_query['type'] = $meta_type;
}
}
if ( ! empty( $meta_compare ) ) {
$args['compare'] = esc_attr( $meta_compare );
}
$args['meta_query'][] = $meta_query;
}
if ( ! empty( $order ) ) {
$args['order'] = in_array( $order, [ 'ASC', 'DESC' ] ) ? $order : 'DESC';
}
if ( ! empty( $orderby ) ) {
$args['orderby'] = esc_attr( $orderby );
}
// Filter by date.
if ( $date_start ) {
$args['date_query'][] = array(
'after' => $date_start,
'before' => $date_end ?: 'now',
'inclusive' => TRUE,
);
}
// Return only ID's.
if ( $fields ) {
$args['fields'] = $fields;
}
$orders = array();
$query = new \WP_Query( $args );
if ( $query->post_count > 0 ) {
if ( $fields ) {
$orders = $query->posts;
}
else {
foreach ( $query->posts as $post ) {
// We need the WooCommerce order, not the post.
$orders[] = new \WC_Order( $post->ID );
}
}
}
AtumCache::set_cache( $cache_key, $orders );
return $orders;
}
/**
* Get the items' sales since $date_start or between $date_start and $date_end
*
* @since 1.2.3
*
* @param array|int $items Array of Product IDs (or single ID) we want to calculate sales from.
* @param int $date_start The GMT date from when to start the items' sales calculations (must be a string format convertible with strtotime).
* @param int $date_end Optional. The max GMT date to calculate the items' sales (must be a string format convertible with strtotime).
* @param array $colums Optional. Which columns to return from DB. Possible values: "qty", "total" and "prod_id".
*
* @return array
*/
public static function get_sold_last_days( $items, $date_start, $date_end = NULL, $colums = [ 'qty' ] ) {
$items_sold = array();
if ( ! empty( $items ) && ! empty( $colums ) ) {
global $wpdb;
// Prepare the SQL query to get the orders in the specified time window.
$date_start = gmdate( 'Y-m-d H:i:s', strtotime( $date_start ) );
$date_where = $wpdb->prepare( 'WHERE post_date_gmt >= %s', $date_start );
if ( $date_end ) {
$date_end = gmdate( 'Y-m-d H:i:s', strtotime( $date_end ) );
$date_where .= $wpdb->prepare( ' AND post_date_gmt <= %s', $date_end );
}
$orders_query = "
SELECT ID FROM $wpdb->posts
$date_where
AND post_type = 'shop_order' AND post_status IN ('wc-processing', 'wc-completed')
";
if ( is_array( $items ) ) {
$products_where = 'IN (' . implode( ',', $items ) . ')';
}
else {
$products_where = "= $items";
}
$query_columns = $query_joins = [];
if ( in_array( 'qty', $colums ) ) {
$query_columns[] = 'SUM(`mt_qty`.`meta_value`) AS `QTY`';
$query_joins[] = "INNER JOIN `$wpdb->order_itemmeta` AS `mt_qty` ON (`mt_id`.`order_item_id` = `mt_qty`.`order_item_id`) AND `mt_qty`.`meta_key` = '_qty'";
}
if ( in_array( 'total', $colums ) ) {
$query_columns[] = 'SUM(`mt_total`.`meta_value`) AS `TOTAL`';
$query_joins[] = "INNER JOIN `$wpdb->order_itemmeta` AS `mt_total` ON (`mt_id`.`order_item_id` = `mt_total`.`order_item_id`) AND `mt_total`.`meta_key` = '_line_total'";
}
if ( in_array( 'prod_id', $colums ) ) {
$query_columns[] = 'MAX(CAST(`mt_id`.`meta_value` AS SIGNED)) AS `PROD_ID`';
}
$query_columns_str = implode( ', ', $query_columns );
$query_joins_str = implode( "\n", $query_joins );
// TODO: USE LOOK UP TABLE AVAILABLE SINCE WC 3.6 FOR BETTER PERFORMANCE.
$query = "
SELECT $query_columns_str
FROM `$wpdb->posts` AS `orders`
INNER JOIN `{$wpdb->prefix}woocommerce_order_items` AS `items` ON (`orders`.`ID` = `items`.`order_id`)
INNER JOIN `$wpdb->order_itemmeta` AS `mt_id` ON (`items`.`order_item_id` = `mt_id`.`order_item_id`)
$query_joins_str
WHERE `orders`.`ID` IN ($orders_query) AND `mt_id`.`meta_value` $products_where
AND `mt_id`.`meta_key` IN ('_product_id', '_variation_id')
GROUP BY `mt_id`.`meta_value`
HAVING (`QTY` IS NOT NULL);
";
// For single products.
if ( ! is_array( $items ) || count( $items ) === 1 ) {
// When only 1 single result is requested.
if ( count( $colums ) === 1 ) {
$items_sold = $wpdb->get_var( $query ); // WPCS: unprepared SQL ok.
}
// Multiple results requested.
else {
$items_sold = $wpdb->get_results( $query, ARRAY_A ); // WPCS: unprepared SQL ok.
}
}
// For multiple products.
else {
// When only 1 single result for each product is requested.
if ( count( $colums ) === 1 ) {
$items_sold = $wpdb->get_col( $query ); // WPCS: unprepared SQL ok.
}
// Multiple results requested for each product.
else {
$items_sold = $wpdb->get_results( $query, ARRAY_A ); // WPCS: unprepared SQL ok.
}
}
}
return $items_sold;
}
/**
* Get the lost sales of a specified product during the last days
*
* @since 1.2.3
*
* @param int|\WC_Product $product The product ID or product object to calculate the lost sales.
* @param int $days Optional. By default the calculation is made for 7 days average.
*
* @return bool|float Returns the lost sales or FALSE if never had lost sales
*/
public static function get_product_lost_sales( $product, $days = 7 ) {
$lost_sales = FALSE;
if ( ! is_a( $product, '\WC_Product' ) ) {
$product = self::get_atum_product( $product );
}
/* @noinspection PhpUndefinedMethodInspection */
$out_of_stock_date = $product->get_out_stock_date();
if ( $out_of_stock_date && $days > 0 ) {
$days_out_of_stock = self::get_product_out_stock_days( $product );
if ( is_numeric( $days_out_of_stock ) ) {
// Get the average sales for the past days when in stock.
$days = absint( $days );
$sold_last_days = self::get_sold_last_days( $product->get_id(), "$out_of_stock_date -$days days", $out_of_stock_date );
$lost_sales = 0;
if ( $sold_last_days > 0 ) {
$average_sales = $sold_last_days / $days;
$price = floatval( $product->get_regular_price() );
$lost_sales = $days_out_of_stock * $average_sales * $price;
}
}
}
return $lost_sales;
}
/**
* Get the number of days that a product was "Out of Stock"
*
* @since 1.2.3
*
* @param int|\WC_Product $product The product ID or product object.
*
* @return bool|null Returns the number of days or NULL if is not "Out of Stock".
*/
public static function get_product_out_stock_days( $product ) {
$out_stock_days = NULL;
if ( ! is_a( $product, '\WC_Product' ) ) {
$product = self::get_atum_product( $product );
}
// Check if the current product has the "Out of stock" date recorded.
/* @noinspection PhpUndefinedMethodInspection */
$out_stock_date = $product->get_out_stock_date();
if ( $out_stock_date ) {
try {
$out_date_time = new \DateTime( $out_stock_date );
$now_date_time = new \DateTime( 'now' );
$interval = date_diff( $out_date_time, $now_date_time );
$out_stock_days = $interval->days;
} catch ( \Exception $e ) {
error_log( __METHOD__ . ' || Product: ' . $product->get_id() . ' || ' . $e->getMessage() );
return $out_stock_days;
}
}
return $out_stock_days;
}
/**
* Get the Inventory Log item quantity for a specific type of log
*
* @since 1.2.4
*
* @param string $log_type Type of log.
* @param \WC_Product $product Product to check.
* @param string $log_status Optional. Log status (completed or pending).
* @param bool $force Optional. Force to retrieve the data from db.
*
* @return int|float
*/
public static function get_log_item_qty( $log_type, &$product, $log_status = 'pending', $force = FALSE ) {
$log_types = Log::get_log_type_columns();
$column_name = isset( $log_types[ $log_type ] ) ? $log_types[ $log_type ] : '';
if ( ! $force && $column_name && is_callable( array( $product, "get_$column_name" ) ) ) {
$qty = call_user_func( array( $product, "get_$column_name" ) );
}
if ( ! isset( $qty ) || is_null( $qty ) ) {
$cache_key = AtumCache::get_cache_key( 'log_item_qty', [ $product->get_id(), $log_type, $log_status ] );
$qty = AtumCache::get_cache( $cache_key, ATUM_TEXT_DOMAIN, FALSE, $has_cache );
if ( ! $has_cache || $force ) {
$log_ids = self::get_logs( $log_type, $log_status );
if ( ! empty( $log_ids ) ) {
global $wpdb;
// Get the sum of quantities for the specified product in the logs of that type.
$query = $wpdb->prepare( "
SELECT SUM(meta_value)
FROM $wpdb->prefix" . AtumOrderPostType::ORDER_ITEM_META_TABLE . " om
LEFT JOIN $wpdb->prefix" . AtumOrderPostType::ORDER_ITEMS_TABLE . ' oi ON om.order_item_id = oi.order_item_id
WHERE order_id IN (' . implode( ',', $log_ids ) . ") AND order_item_type = 'line_item'
AND meta_key = '_qty' AND om.order_item_id IN (
SELECT order_item_id FROM $wpdb->prefix" . AtumOrderPostType::ORDER_ITEM_META_TABLE . "
WHERE meta_key IN ('_product_id', '_variation_id') AND meta_value = %d
)",
$product->get_id()
); // WPCS: unprepared SQL ok.
$qty = $wpdb->get_var( $query ); // WPCS: unprepared SQL ok.
}
else {
$qty = 0;
}
// Save it for future quicker access.
if ( $column_name && is_callable( array( $product, "set_$column_name" ) ) ) {
call_user_func( array( $product, "set_$column_name" ), $qty );
}
}
AtumCache::set_cache( $cache_key, $qty );
}
return floatval( $qty );
}
/**
* Helper function to return a plugin option value.
* If no value has been saved, it returns $default.
* Needed because options are saved as serialized strings.
*
* @since 0.0.2
*
* @param string $name The option key to retrieve.
* @param mixed $default The default value returned if the option was not found.
* @param boolean $echo If the option has to be returned or printed.
*
* @return mixed
*/
public static function get_option( $name, $default = FALSE, $echo = FALSE ) {
// Save it as a global variable to not get the value each time.
global $atum_global_options;
// The option key it's built using ADP_PREFIX and theme slug to avoid overwrites.
$atum_global_options = empty( $atum_global_options ) ? get_option( Settings::OPTION_NAME ) : $atum_global_options;
$option = isset( $atum_global_options[ $name ] ) ? $atum_global_options[ $name ] : $default;
if ( $echo ) {
echo apply_filters( "atum/print_option/$name", $option ); // WPCS: XSS ok.
return FALSE;
}
return apply_filters( "atum/get_option/$name", $option );
}
/**
* Helper function to return the entire plugin option value.
* If no option has been saved, it returns empty array.
*
* @since 0.0.2
*
* @return array
*/
public static function get_options() {
// Save it as a global variable to not get the value each time.
global $atum_global_options;
// The option key it's built using ADP_PREFIX and theme slug to avoid overwrites.
$atum_global_options = empty( $atum_global_options ) ? get_option( Settings::OPTION_NAME ) : $atum_global_options;
if ( ! $atum_global_options ) {
$atum_global_options = array();
}
return apply_filters( 'atum/get_options', $atum_global_options );
}
/**
* Get the a setting for the specified product
* First checks if has a meta key, if the meta value is distinct than global, returns that value,
* but if it's set to global, returns the global ATUM setting default for it.
*
* NOTE: The global setting must have the same name as the individual meta key but starting with the keyword "default".
*
* @since 1.4.18
*
* @param int $product_id The product ID.
* @param string $meta_key The meta key name.
* @param mixed $default The default value for the global option.
* @param string $prefix Optional. The ATUM add-ons should use a prefix for their settings.
* @param bool $allow_global Optional. If FALSE, only can return meta value or default. If TRUE, it could return 'global'.
*
* @return mixed
*/
public static function get_product_setting( $product_id, $meta_key, $default, $prefix = '', $allow_global = FALSE ) {
$meta_value = get_post_meta( $product_id, $meta_key, TRUE );
// If has no value saved, get the default.
if ( ! $meta_value || 'global' === $meta_value ) {
$option_name = "default{$meta_key}";
if ( ! empty( $prefix ) ) {
if ( '_' !== substr( $prefix, - 1, 1 ) ) {
$prefix .= '_';
}
$option_name = $prefix . $option_name;
}
$meta_value = ! $allow_global ? self::get_option( $option_name, $default ) : 'global';
}
return $meta_value;
}
/**
* Get sold_last_days address var if set and valid, or the sales_last_ndays options/ Settings::DEFAULT_SALE_DAYS if set
*
* @since 1.4.11
*
* @return int days between 1 and 31
*/
public static function get_sold_last_days_option() {
if ( isset( $_REQUEST['sold_last_days'] ) ) {
// Sanitize.
$value = absint( $_REQUEST['sold_last_days'] );
if ( $value > 0 && $value < 31 ) {
return $value;
}
}
return absint( self::get_option( 'sales_last_ndays', Settings::DEFAULT_SALE_DAYS ) );
}
/**
* If the site is not using the new tables, use the legacy methods
*
* @since 1.5.0
* @deprecated Only for backwards compatibility and will be removed in a future version.
*/
use HelpersLegacyTrait;
/**
* Get an array of products that are not managed by WC
*
* @since 1.4.1
*
* @param array $post_types
* @param bool $get_stock_status Whether to get also the WC stock_status of the unmanaged products.
*
* @return array
*/
public static function get_unmanaged_products( $post_types, $get_stock_status = FALSE ) {
/**
* If the site is not using the new tables, use the legacy method
*
* @since 1.5.0
* @deprecated Only for backwards compatibility and will be removed in a future version.
*/
if ( ! self::is_using_new_wc_tables() ) {
return self::get_unmanaged_products_legacy( $post_types, $get_stock_status );
}
global $wpdb;
$unmng_fields = array( 'posts.ID' );
$unmng_join = array(
"LEFT JOIN $wpdb->postmeta AS mt1 ON (posts.ID = mt1.post_id AND mt1.meta_key = '_manage_stock')",
"LEFT JOIN {$wpdb->prefix}wc_products wpd ON (posts.ID = wpd.product_id)",
);
$post_statuses = current_user_can( 'edit_private_products' ) ? [ 'private', 'publish' ] : [ 'publish' ];
if ( $get_stock_status ) {
$unmng_fields[] = 'wpd.stock_status';
}
$unmng_join = (array) apply_filters( 'atum/get_unmanaged_products/join_query', $unmng_join );
// Exclude the inheritable products from query (as are just containers in ATUM List Tables).
$excluded_types = Globals::get_inheritable_product_types();
$unmng_where = array(
"WHERE posts.post_type IN ('" . implode( "','", $post_types ) . "')",
"AND posts.post_status IN ('" . implode( "','", $post_statuses ) . "')",
"AND (mt1.post_id IS NULL OR (mt1.meta_key = '_manage_stock' AND mt1.meta_value = 'no'))",
"AND wpd.type NOT IN ('" . implode( "','", $excluded_types ) . "')",
);
$unmng_where = (array) apply_filters( 'atum/get_unmanaged_products/where_query', $unmng_where );
$sql = 'SELECT DISTINCT ' . implode( ',', $unmng_fields ) . "\n FROM $wpdb->posts posts \n" . implode( "\n", $unmng_join ) . "\n" . implode( "\n", $unmng_where );
return $wpdb->get_results( $sql, ARRAY_N ); // WPCS: unprepared SQL ok.
}
/**
* Get the price formatted with no HTML tags
*
* @since 1.2.3
*
* @param float $price The price number to format.
* @param array $args The format configuration array.
*
* @return string
*/
public static function format_price( $price, $args = array() ) {
// Do not add zeros as decimals.
if ( ! empty( $args['trim_zeros'] ) && TRUE === $args['trim_zeros'] ) {
add_filter( 'woocommerce_price_trim_zeros', '__return_true' );
}
return apply_filters( 'atum/format_price', wp_strip_all_tags( wc_price( round( $price, 2 ), $args ) ) );
}
/**
* Display the template for the given view
*
* @since 0.0.2
*
* @param string $view View file that should be loaded.
* @param array $args Optional. Variables that will be passed to the view.
* @param bool $allow_theme_override Optional. Allow overriding views from the theme.
*
* @return void
*/
public static function load_view( $view, $args = [], $allow_theme_override = TRUE ) {
$file = apply_filters( "atum/load_view/$view", $view );
$args = apply_filters( "atum/load_view_args/$view", $args );
// Whether or not .php was added.
if ( '.php' !== substr( $file, - 4 ) ) {
$file .= '.php';
}
if ( $allow_theme_override ) {
$file = self::locate_template( array( $view ), $file );
}
// Allow using full paths as view name.
if ( is_file( $file ) ) {
$file_path = $file;
}
else {
$file_path = ATUM_PATH . "views/$file";
if ( ! is_file( $file_path ) ) {
return;
}
}
if ( ! empty( $args ) && is_array( $args ) ) {
extract( $args );
}
if ( ATUM_DEBUG ) {
/* @noinspection PhpIncludeInspection */
include $file_path;
}
else {
/* @noinspection PhpIncludeInspection */
@include $file_path; // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
}
}
/**
* Get the template for the given view and return it as string
*
* @since 0.0.1
*
* @param string $view View file that should be loaded.
* @param array $args Optional. Variables that will be passed to the view.
* @param bool $allow_theme_override Optional. Allow overriding views from the theme.
*
* @return string View template
*/
public static function load_view_to_string( $view, $args = [], $allow_theme_override = TRUE ) {
ob_start();
self::load_view( $view, $args, $allow_theme_override );
return ob_get_clean();
}
/**
* Locate the template file, either in the current theme or the public views directory
*
* @since 1.3.3
*
* @param array $possibilities
* @param string $default
*
* @return string
*/
protected static function locate_template( $possibilities, $default = '' ) {
$possibilities = apply_filters( 'atum/locate_template/possibilities', $possibilities );
// Check if the theme has an override for the template.
$theme_overrides = array();
foreach ( $possibilities as $p ) {
$theme_overrides[] = Globals::TEMPLATE_DIR . "/$p";
}
$found = locate_template( $theme_overrides, FALSE );
if ( $found ) {
return $found;
}
// Check for it in the public directory.
foreach ( $possibilities as $p ) {
if ( file_exists( ATUM_PATH . "views/$p" ) ) {
return ATUM_PATH . "views/$p";
}
}
// Not template found.
return $default;
}
/**
* Checks if ATUM is managing the WC stock for a specific product
*
* @since 1.4.1
*
* @param int|\WC_Product $product
*
* @return bool
*/
public static function is_atum_controlling_stock( $product ) {
return 'yes' === self::get_atum_control_status( $product );
}
/**
* Checks whether the product type passed is an inheritable type
*
* @since 1.4.1
*
* @param string $type
*
* @return bool
*/
public static function is_inheritable_type( $type ) {
return in_array( $type, Globals::get_inheritable_product_types() );
}
/**
* Checks whether the product type passed is a child type
*
* @since 1.4.1
*
* @param string $type
*
* @return bool
*/
public static function is_child_type( $type ) {
return in_array( $type, Globals::get_child_product_types() );
}
/**
* Gets the ATUM control switch status for the specified product
*
* @since 1.4.1
*
* @param int|\WC_Product $product
*
* @return string|bool yes if On of FALSE if Off
*/
public static function get_atum_control_status( $product ) {
if ( ! is_a( $product, '\WC_product' ) ) {
$product = self::get_atum_product( $product );
}
/* @noinspection PhpUndefinedMethodInspection */
return $product->get_atum_controlled();
}
/**
* Updates the ATUM control switch for the specified product
*
* @since 1.4.1
*
* @param int|\WC_Product $product The product ID or product object.
* @param string $status Optional. Can be 'enable' or 'disable'.
*/
public static function update_atum_control( $product, $status = 'enable' ) {
if ( ! is_a( $product, '\WC_product' ) ) {
$product = self::get_atum_product( $product );
}
/* @noinspection PhpUndefinedMethodInspection */
$product->set_atum_controlled( ( 'enable' === $status ? 'yes' : 'no' ) );
/* @noinspection PhpUndefinedMethodInspection */
$product->save_atum_data();
}
/**
* Updates the WC's manage stock for the specified product
*
* @since 1.4.5
*
* @param int|\WC_Product $product The product ID or product object.
* @param string $status Optional. Can be 'enable' or 'disable'.
*/
public static function update_wc_manage_stock( $product, $status = 'enable' ) {
if ( ! is_a( $product, '\WC_product' ) ) {
$product = wc_get_product( $product ); // We don't need to use the ATUM models here.
}
/* @noinspection PhpUndefinedMethodInspection */
$product->set_manage_stock( ( 'enable' === $status ? 'yes' : 'no' ) );
$product->save();
}
/**
* Check whether a specific plugin is installed
*
* @since 1.2.0
*
* @param string $plugin The plugin name/slug.
* @param string $by Optional. It can be cheched by 'slug' or by 'name'.
* @param bool $return_bool Optional. May return a boolean (true/false) or an associative array with the plugin data.
*
* @return bool|array
*/
public static function is_plugin_installed( $plugin, $by = 'slug', $return_bool = TRUE ) {
foreach ( get_plugins() as $plugin_file => $plugin_data ) {
// Get the plugin slug from its path.
$installed_plugin_key = 'slug' === $by ? explode( DIRECTORY_SEPARATOR, $plugin_file )[0] : $plugin_data['Title'];
if ( $installed_plugin_key === $plugin ) {
return $return_bool ? TRUE : array( $plugin_file => $plugin_data );
}
}
return FALSE;
}
/**
* Display a notice an ATUM's admin notice
*
* @since 1.2.0
*
* @param string $type The notice type: error, success, warning or info.
* @param string $message The message within the notice.
* @param bool $is_dismissible Optional. Whether to make the notice dismissible.
* @param string $key Optional. Only needed for dismissible notices. Is the key used to save the dismissal on db.
*/
public static function display_notice( $type, $message, $is_dismissible = FALSE, $key = '' ) {
$notice_classes = array( "notice-$type" );
if ( $is_dismissible ) {
// Check if the notice was already dismissed.
if ( $key && self::is_notice_dismissed( $key ) ) {
return;
}
$notice_classes[] = 'is-dismissible';
}
?>