<?php /** * Shipping by Rules generic helper class (WP/WooCommerce-specific) * Reinhold Kainhofer, Open Tools, office@open-tools.net * @copyright (C) 2012-2016 - Reinhold Kainhofer * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html **/ if ( !defined( 'ABSPATH' ) ) { die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' ); } require_once( plugin_dir_path( __FILE__ ) . '/../library/rules_shipping_framework.php'); class RulesShippingFrameworkWooCommerce extends RulesShippingFramework { protected static $_method_ordering = 'woocommerce_shipping_rules_ordering'; function __construct() { parent::__construct(); load_plugin_textdomain('woocommerce-shipping-by-rules', false, basename( dirname( __FILE__ ) ) . '/languages' ); $this->registerScopings(array( "categories" => 'categories', "subcategories" => 'subcategories', "products" => 'products', "skus" => 'products', "vendors" => 'vendors', )); } static function getHelper() { static $helper = null; if (!$helper) { $helper = new RulesShippingFrameworkWooCommerce(); $helper->setup(); } return $helper; } function urlPath($type, $file) { return plugins_url('library/' . $type . '/' . $file, __FILE__); } function isAdvanced() { return false; } function getCustomFunctions() { // Let other plugins add custom functions! // The opentools_shipping_by_rules_replacements filter is expected to return an array of the form: // array ('functionname1' => 'function-to-be-called', // 'functionname2' => array($classobject, 'memberfunc')), // ...); return apply_filters( 'opentools_shipping_by_rules_replacements', array()); } public function printMessage($message, $type) { // Keep track of warning messages, so we don't print them twice: global $printed_messages; if (!isset($printed_messages)) $printed_messages = array(); if ($type == "debug") { if ( true === WP_DEBUG ) { if ( is_array( $message ) || is_object( $message ) ) { error_log( print_r( $message, true ) ); } else { error_log( $message ); } } } elseif (!in_array($message, $printed_messages)) { switch ($type) { case 'error': case 'warning': wc_add_notice( $message, 'error'); break; case 'message': wc_add_notice( $message, 'success'); break; case 'notice': default: wc_add_notice( $message, 'notice'); break; } $printed_messages[] = $message; } } /** * HELPER FUNCTIONS, WooCommerce-specific */ public function __($string) { $args = func_get_args(); $string = $this->readableString($string); $string = __($string, 'opentools-shippingrules'); if (count($args)>1) { $args[0] = $string; return call_user_func_array("sprintf", $args); } else { return $string; } } protected function getCartProducts($package, $method) { return $package['contents']; } protected function getMethodId($method) { return $method->get_rate_id(); } protected function getMethodName($method) { return $method->title; } // TODO: Legacy protected function parseMethodRules (&$method) { return $this->parseMethodRule( /* Rules */ isset($method->rules)?$method->rules:'', /* Countries */array(), /* Rule info */array(), /* Method */ $method); } /** * Functions to calculate the cart variables: * - getOrderCounts($cart, $products, $method) * - getOrderDimensions */ /** Functions to calculate all the different variables for the given cart and given (sub)set of products in the cart */ protected function getOrderCounts ($cart, $products, $method) { $counts = array( 'articles' => 0, 'products' => count($products), 'quantity' => 0, 'minquantity' => 9999999999, 'maxquantity' => 0, ); foreach ($products as $product) { $counts['articles'] += $product['quantity']; $counts['maxquantity'] = max ($counts['maxquantity'], $product['quantity']); $counts['minquantity'] = min ($counts['minquantity'], $product['quantity']); } $counts['quantity'] = $counts['articles']; return $counts; } protected function getOrderDimensions ($cart, $products, $method) { /* Cache the value in a static variable and calculate it only once! */ $dimensions=array( 'volume' => 0, 'maxvolume' => 0, 'minvolume' => 99999999, 'maxlength' => 0, 'minlength' => 99999999, 'totallength' => 0, 'maxwidth' => 0, 'minwidth' => 99999999, 'totalwidth' => 0, 'maxheight' => 0, 'minheight' => 99999999, 'totalheight' => 0, ); foreach ($products as $product) { $l = $product['data']->get_length(); $w = $product['data']->get_width(); $h = $product['data']->get_height(); $volume = $l * $w * $h; $dimensions['volume'] += $volume * $product['quantity']; $dimensions['maxvolume'] = max ($dimensions['maxvolume'], $volume); $dimensions['minvolume'] = min ($dimensions['minvolume'], $volume); $dimensions['totallength'] += $l * $product['quantity']; $dimensions['maxlength'] = max ($dimensions['maxlength'], $l); $dimensions['minlength'] = min ($dimensions['minlength'], $l); $dimensions['totalwidth'] += $w * $product['quantity']; $dimensions['maxwidth'] = max ($dimensions['maxwidth'], $w); $dimensions['minwidth'] = min ($dimensions['minwidth'], $w); $dimensions['totalheight'] += $h * $product['quantity']; $dimensions['maxheight'] = max ($dimensions['maxheight'], $h); $dimensions['minheight'] = min ($dimensions['minheight'], $h); } return $dimensions; } protected function getOrderWeights ($cart, $products, $method) { $dimensions=array( 'weight' => 0, 'maxweight' => 0, 'minweight' => 9999999999, ); foreach ($products as $product) { $w = $product['data']->get_weight(); $dimensions['maxweight'] = max ($dimensions['maxweight'], $w); $dimensions['minweight'] = min ($dimensions['minweight'], $w); $dimensions['weight'] += $w * $product['quantity']; } return $dimensions; } protected function getOrderListProperties ($cart, $products, $method) { $categories = array(); $skus = array(); $tags = array(); $shipping_classes = array(); foreach ($products as $product) { $id = $product['data']->get_id(); if ($product['data']->get_sku()) { $skus[] = $product['data']->get_sku(); } foreach (wc_get_product_terms( $id, 'product_cat') as $c) { $categories[] = $c->slug; } foreach (wc_get_product_terms( $id, 'product_tag') as $c) { $tags[] = $c->slug; } $shipclass = $product['data']->get_shipping_class(); if ($shipclass) { $shipping_classes[] = $shipclass; } } $skus = array_unique($skus); $categories = array_unique($categories); $tags = array_unique($tags); $shipping_classes = array_unique($shipping_classes); $data = array ( 'skus' => $skus, 'categories' => $categories, 'tags' => $tags, 'shippingclasses' => $shipping_classes, ); // THIRD-PARTY SUPPORT // "WC Vendors" support (vendors stored as post author) if (class_exists("WC_Vendors")) { $vendorids = array(); foreach ($products as $product) { $vendorids[] = $product['data']->post->post_author; } $data['vendorids'] = array_unique($vendorids); $vendors = array(); // Requires "WC Vendors" or "WooThemes Product Vendors" plugin $vendornames = array(); foreach ($data['vendorids'] as $v) { $vnd = get_user_by('id', $v); // Get user name by user id if (is_object($vnd)) { $vendornames[] = $vnd->display_name; $vendors[] = $vnd->user_login; } } $data['vendornames'] = array_unique($vendornames); $data['vendors'] = array_unique($vendors); } // "WooThemes Vendor Products" support (vendors stored in its own taxonomy) if (class_exists("WooCommerce_Product_Vendors") && function_exists("get_product_vendors")) { $vendors = array(); $vendornames = array(); $vendorids = array(); // The plugin provides its own function to retrieve the vendor for a product foreach ($products as $product) { foreach (get_product_vendors($product['data']->get_id()) as $vendor) { // $this->printWarning("<pre>vendor: ".print_r($vendor,1)."</pre>"); $vendors[] = $vendor->slug; $vendornames[] = $vendor->title; $vendorids[] = $vendor->ID; } } $data['vendors'] = array_unique($vendors); $data['vendornames'] = array_unique($vendornames); $data['vendorids'] = array_unique($vendorids); } // "YITH WooCommerce Multi Vendor" support (vendors stored in its own taxonomy) if (function_exists("yith_get_vendor")) { $vendors = array(); $vendornames = array(); $vendorids = array(); // The plugin provides its own function to retrieve the vendor for a product foreach ($products as $product) { $vendor = yith_get_vendor($product['data']->get_id(), 'product'); // $this->printWarning("<pre>vendor: ".print_r($vendor,1)."</pre>"); if ($vendor->is_valid()) { $vendors[] = $vendor->slug; $vendornames[] = $vendor->name; $vendorids[] = $vendor->term_id; } } $data['vendors'] = array_unique($vendors); $data['vendornames'] = array_unique($vendornames); $data['vendorids'] = array_unique($vendorids); } // END THIRD-PARTY SUPPORT return $data; } protected function getOrderAddress ($cart, $method) { $address = $cart['destination']; $zip = isset($address['postcode'])?trim($address['postcode']):''; $data = array( 'zip' => $zip, 'postcode' => $zip, 'zip1' => substr($zip,0,1), 'zip2' => substr($zip,0,2), 'zip3' => substr($zip,0,3), 'zip4' => substr($zip,0,4), 'zip5' => substr($zip,0,5), 'zip6' => substr($zip,0,6), 'zipnumeric' => preg_replace('/[^0-9]/', '', $zip), 'zipalphanum' => preg_replace('/[^a-zA-Z0-9]/', '', $zip), 'city' => trim($address['city']), 'country' => trim($address['country']), 'state' => trim($address['state']), 'address1' => trim($address['address']), 'address2' => trim($address['address_2']), ); /* Get the user from the package information and extract further information about the buyer */ $user = $cart['user']; $data['userid'] = $user['ID']; $data['userroles'] = array(); $data['username'] = ''; $data['first_name'] = ''; $data['last_name'] = ''; $data['email'] = ''; if ($user['ID']>0) { $userinfo = get_userdata($user['ID']); $data['userroles'] = $userinfo->roles; $data['username'] = $userinfo->user_login; $data['first_name'] = $userinfo->first_name; $data['last_name'] = $userinfo->last_name; $data['email'] = isset($address['email'])?$address['email']:''; // TODO: Extract more user fields! /** $data['company'] = isset($address['company'])?$address['company']:''; $data['title'] = isset($address['title'])?$address['title']:''; $data['middle_name'] = isset($address['middle_name'])?$address['middle_name']:''; $data['phone1'] = isset($address['phone_1'])?$address['phone_1']:''; $data['phone2'] = isset($address['phone_2'])?$address['phone_2']:''; $data['fax'] = isset($address['fax'])?$address['fax']:''; */ } // The country check needs the countryid variable, so duplicate from country: $data['countryid'] = $data['country']; return $data; } protected function getOrderPrices ($cart, $products, /*$cart_prices, */$method) { $data = array( 'total' => 0, 'subtotal' => 0, 'taxtotal' => 0, 'taxsubtotal' => 0, 'cost' => 0, ); // Calculate the prices from the individual products! // Possible problems are discounts on the order total foreach ($products as $product) { $data['total'] += $product['line_total']; $data['subtotal'] += $product['line_subtotal']; $data['taxtotal'] += $product['line_tax']; $data['taxsubtotal'] += $product['line_subtotal_tax']; $data['cost'] += $product['line_total'] + $product['line_tax']; } $data['amount'] = $data['cost']; $data['amountwithtax'] = $data['cost']; return $data; } /** Allow child classes to add additional variables for the rules or modify existing one */ protected function addCustomCartValues ($cart, $products, $method, &$values) { } protected function addPluginCartValues($cart, $products, $method, &$values) { return apply_filters( 'opentools_shipping_by_rules_get_cart_values', array(&$values, $cart, $products, $method)); } /** Filter the given array of products and return only those that belong to the categories, manufacturers, * vendors or products given in the $filter_conditions. The $filter_conditions is an array of the form: * array( 'products'=>array(....), 'categories'=>array(1,2,3,42)) * Notice that giving an empty array for any of the keys means "no restriction" and is exactly the same * as leaving out the entry altogether */ public function filterProducts($products, $filter_conditions) { $result = array(); // For the subcategories scoping we need all subcategories of the conditions: $subcategories = array(); if (isset($filter_conditions['subcategories']) && !empty($filter_conditions['subcategories'])) { foreach ($filter_conditions['subcategories'] as $catslug) { // Get the term itself (we have only the slug!) $cat = get_term_by('slug', $catslug, 'product_cat'); if (empty($cat)) continue; $subcategories[] = $cat->slug; // Get the list of all subcategories of the given categories $args=array('child_of' => $cat->term_id, 'hierarchical' => 1); foreach (get_terms( 'product_cat', $args) as $subcat) { $subcategories[] = $subcat->slug; } } $subcategories = array_unique($subcategories); } // Now filter out all products that do not match the conditions foreach ($products as $p) { $prodcategories = array(); foreach (wc_get_product_terms( $p['data']->id, 'product_cat') as $cat) { $prodcategories[] = $cat->slug; } if (!empty($filter_conditions['products']) && !in_array($p['data']->get_sku(), $filter_conditions['products'])) continue; if (!empty($filter_conditions['categories']) && count(array_intersect($filter_conditions['categories'], $prodcategories))==0) continue; if (!empty($filter_conditions['subcategories']) && count(array_intersect($subcategories, $prodcategories))==0) continue; if (!empty($filter_conditions['vendors'])) { // Collect all vendors (ids and slug/login_name - PLUGIN-specific!) // for the current product. If any of them is in the vendor conditions // list, this product should not be filtered out! $vnd_props = array(); // THIRD-PARTY SUPPORT // "WC Vendors" support (vendors stored as post author) if (class_exists("WC_Vendors")) { $vendor = $p['data']->post->post_author; $vnd = get_user_by('id', $vendor); // Get user name by user id $vnd_props[] = $vendor; $vnd_props[] = $vnd->user_login; } // "WooThemes Vendor Products" support (vendors stored in its own taxonomy) if (class_exists("WooCommerce_Product_Vendors") && function_exists("get_product_vendors")) { foreach (get_product_vendors($p['data']->id) as $vendor) { $vnd_props[] = $vendor->slug; } } // "YITH WooCommerce Multi Vendor" support (vendors stored in its own taxonomy) if (function_exists("yith_get_vendor")) { $vendor = yith_get_vendor($p['data']->id, 'product'); if ($vendor->is_valid()) { $vnd_props[] = $vendor->slug; } } // END THIRD-PARTY SUPPORT // Check if any of the vendor properties is matched by the conditions; If not => skip product if (count(array_intersect($vnd_props, $filter_conditions['vendors']))==0) continue; } $result[] = $p; } return $result; } protected function createMethodRule ($r, $countries, $ruleinfo) { return new ShippingRule($this, $r, $countries, $ruleinfo); } public function getUpgradeNagSettings() { $settings = array(); if (!$this->isAdvanced()) { $settings['opentools_shippingbyrules_upgrade'] = array( 'name' => $this->__( 'Upgrade to the ADVANCED VERSION of the OpenTools Shipping by Rules plugin'), 'type' => 'opentools_shippingbyrules_upgrade', 'link' => 'http://open-tools.net/woocommerce/advanced-shipping-by-rules-for-woocommerce.html', ); } return $settings; } public function printUpgradeNagBox($settings) { include plugin_dir_path( __FILE__ ) . 'admin/html/html-upgrade-nag.php'; } }