WC_Product_Dependencies

Plugin Name: WooCommerce Product Dependencies Plugin URI: http://somewherewarm.gr/ Description: Restrict access to WooCommerce products, depending on the ownership and/or purchase of other, prerequisite products.

Defined (1)

The class is defined in the following location(s).

/woocommerce-product-dependencies.php  
  1. class WC_Product_Dependencies { 
  2.  
  3. /** 
  4. * The single instance of the class. 
  5. * @var WC_Product_Dependencies 
  6. * @since 1.1.0 
  7. */ 
  8. protected static $_instance = null; 
  9.  
  10. /** 
  11. * Required WC version. 
  12. * @var string 
  13. */ 
  14. private $required = '2.2'; 
  15.  
  16. /** 
  17. * Main WC_Product_Dependencies instance. 
  18. * Ensures only one instance of WC_Product_Dependencies is loaded or can be loaded - @see 'WC_Product_Dependencies()'. 
  19. * @since 1.1.0 
  20. * @static 
  21. * @return WC_Product_Dependencies - Main instance 
  22. */ 
  23. public static function instance() { 
  24. if ( is_null( self::$_instance ) ) { 
  25. self::$_instance = new self(); 
  26. return self::$_instance; 
  27.  
  28. /** 
  29. * Cloning is forbidden. 
  30. * @since 1.1.0 
  31. */ 
  32. public function __clone() { 
  33. _doing_it_wrong( __FUNCTION__, __( 'Foul!', 'woocommerce-product-dependencies' ), '1.1.0' ); 
  34.  
  35. /** 
  36. * Unserializing instances of this class is forbidden. 
  37. * @since 1.1.0 
  38. */ 
  39. public function __wakeup() { 
  40. _doing_it_wrong( __FUNCTION__, __( 'Foul!', 'woocommerce-product-dependencies' ), '1.1.0' ); 
  41.  
  42. /** 
  43. * Fire in the hole! 
  44. */ 
  45. public function __construct() { 
  46.  
  47. // Load plugin on 'plugins_loaded' hook. 
  48. add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) ); 
  49.  
  50. /** 
  51. * Initialize. 
  52. * @return void 
  53. */ 
  54. public function plugins_loaded() { 
  55.  
  56. // Core compatibility functions. 
  57. require_once( 'class-wc-pd-core-compatibility.php' ); 
  58.  
  59. if ( ! function_exists( 'WC' ) || ! WC_PD_Core_Compatibility::is_wc_version_gte_2_2() ) { 
  60. return; 
  61.  
  62. // Helper functions. 
  63. require_once( 'class-wc-pd-helpers.php' ); 
  64.  
  65. // Init textdomain. 
  66. add_action( 'init', array( $this, 'init') ); 
  67.  
  68. // Validate add-to-cart action. 
  69. add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'add_to_cart_validation' ), 10, 3 ); 
  70.  
  71. // Validate products in cart. 
  72. add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_items' ), 1 ); 
  73.  
  74. if ( is_admin() ) { 
  75.  
  76. // Save admin options. 
  77. if ( WC_PD_Core_Compatibility::is_wc_version_gte_2_7() ) { 
  78. add_action( 'woocommerce_admin_process_product_object', array( $this, 'process_product_data' ) ); 
  79. } else { 
  80. add_action( 'woocommerce_process_product_meta', array( $this, 'process_meta' ), 10, 2 ); 
  81.  
  82. // Add the "Dependencies" tab in Product Data. 
  83. if ( WC_PD_Core_Compatibility::is_wc_version_gte_2_5() ) { 
  84. add_action( 'woocommerce_product_data_tabs', array( __CLASS__, 'dependencies_product_data_tab' ) ); 
  85. } else { 
  86. add_action( 'woocommerce_product_write_panel_tabs', array( $this, 'dependencies_product_data_panel_tab' ) ); 
  87.  
  88. // Add the "Dependencies" tab content in Product Data. 
  89. if ( WC_PD_Core_Compatibility::is_wc_version_gte_2_7() ) { 
  90. add_action( 'woocommerce_product_data_panels', array( $this, 'dependencies_product_data_panel' ) ); 
  91. } else { 
  92. add_action( 'woocommerce_product_write_panels', array( $this, 'dependencies_product_data_panel' ) ); 
  93.  
  94. /** 
  95. * Init textdomain. 
  96. * @return void 
  97. */ 
  98. public function init() { 
  99. load_plugin_textdomain( 'woocommerce-product-dependencies', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); 
  100.  
  101. /** 
  102. * Validates a product when adding to cart. 
  103. * @param boolean $add 
  104. * @param int $item_id 
  105. * @param int $quantity 
  106. * @return boolean 
  107. */ 
  108. public function add_to_cart_validation( $add, $item_id, $quantity ) { 
  109.  
  110. return $add && $this->evaluate_dependencies( $item_id ); 
  111.  
  112. /** 
  113. * Validates cart contents. 
  114. */ 
  115. public function check_cart_items() { 
  116.  
  117. $cart_items = WC()->cart->cart_contents; 
  118.  
  119. foreach ( $cart_items as $cart_item ) { 
  120.  
  121. $product = $cart_item[ 'data' ]; 
  122.  
  123. $this->evaluate_dependencies( $product ); 
  124.  
  125. /** 
  126. * Check conditions. 
  127. * @param mixed $item 
  128. * @return boolean 
  129. */ 
  130. public function evaluate_dependencies( $item ) { 
  131.  
  132. if ( is_a( $item, 'WC_Product' ) ) { 
  133. $product = $item; 
  134. $product_id = $product->is_type( 'variation' ) ? WC_PD_Core_Compatibility::get_parent_id( $product ) : WC_PD_Core_Compatibility::get_id( $product ); 
  135. } else { 
  136. $product_id = absint( $item ); 
  137. $product = wc_get_product( $product_id ); 
  138.  
  139. if ( ! $product ) { 
  140. return; 
  141.  
  142. if ( WC_PD_Core_Compatibility::is_wc_version_gte_2_7() ) { 
  143. $tied_product_ids = $product->get_meta( '_tied_products', true ); 
  144. $dependency_type = absint( $product->get_meta( '_dependency_type', true ) ); 
  145. } else { 
  146. $tied_product_ids = (array) get_post_meta( $product_id, '_tied_products', true ); 
  147. $dependency_type = absint( get_post_meta( $product_id, '_dependency_type', true ) ); 
  148.  
  149. $product_title = $product->get_title(); 
  150. $tied_products = array(); 
  151.  
  152. // Ensure dependencies exist and are purchasable. 
  153. if ( ! empty( $tied_product_ids ) ) { 
  154. foreach ( $tied_product_ids as $id ) { 
  155. $tied_product = wc_get_product( $id ); 
  156. if ( $tied_product && $tied_product->is_purchasable() ) { 
  157. $tied_products[ $id ] = $tied_product; 
  158.  
  159. if ( ! empty( $tied_products ) ) { 
  160.  
  161. $tied_product_ids = array_keys( $tied_products ); 
  162.  
  163. // Check cart. 
  164. if ( $dependency_type === 2 || $dependency_type === 3 ) { 
  165.  
  166. $cart_contents = WC()->cart->cart_contents; 
  167.  
  168. foreach ( $cart_contents as $cart_item ) { 
  169. $product_id = $cart_item[ 'product_id' ]; 
  170. $variation_id = $cart_item[ 'variation_id' ]; 
  171. if ( in_array( $product_id, $tied_product_ids ) || in_array( $variation_id, $tied_product_ids ) ) { 
  172. return true; 
  173.  
  174. // Check ownership. 
  175. if ( is_user_logged_in() && ( $dependency_type === 1 || $dependency_type === 3 ) ) { 
  176.  
  177. $current_user = wp_get_current_user(); 
  178. $is_owner = false; 
  179.  
  180. foreach ( $tied_product_ids as $id ) { 
  181. if ( wc_customer_bought_product( $current_user->user_email, $current_user->ID, $id ) ) { 
  182. $is_owner = true; 
  183.  
  184. if ( ! $is_owner ) { 
  185.  
  186. $merged_titles = WC_PD_Helpers::merge_product_titles( $tied_products ); 
  187.  
  188. if ( $dependency_type === 1 ) { 
  189. wc_add_notice( sprintf( __( 'Access to "%2$s" is restricted only to verified owners of %1$s.', 'woocommerce-product-dependencies' ), $merged_titles, $product_title ), 'error' ); 
  190. } else { 
  191. wc_add_notice( sprintf( __( 'Access to "%2$s" is restricted only to verified owners of %1$s. Alternatively, access to this item will be granted after adding a %1$s to the cart.', 'woocommerce-product-dependencies' ), $merged_titles, $product_title ), 'error' ); 
  192. return false; 
  193.  
  194. } else { 
  195.  
  196. $merged_titles = WC_PD_Helpers::merge_product_titles( $tied_products ); 
  197.  
  198. $msg = ''; 
  199.  
  200. if ( $dependency_type === 1 ) { 
  201. $msg = __( 'Access to "%2$s" is restricted only to verified owners of %1$s. The verification is automatic and simply requires you to be <a href="%3$s">logged in</a>.', 'woocommerce-product-dependencies' ); 
  202. } elseif ( $dependency_type === 2 ) { 
  203. $msg = __( '"%2$s" can be purchased only in combination with %1$s. Access to this item will be granted after adding a %1$s to the cart.', 'woocommerce-product-dependencies' ); 
  204. } else { 
  205. $msg = __( '"%2$s" requires the purchase of %1$s. Ownership can be verified by simply <a href="%3$s">logging in</a>. Alternatively, access to this item will be granted after adding a %1$s to the cart.', 'woocommerce-product-dependencies' ); 
  206.  
  207. wc_add_notice( sprintf( $msg, $merged_titles, $product_title, wp_login_url() ), 'error' ); 
  208.  
  209. return false; 
  210.  
  211. return true; 
  212.  
  213. /** 
  214. |-------------------------------------------------------------------------- 
  215. | Admin Filters. 
  216. |-------------------------------------------------------------------------- 
  217. */ 
  218.  
  219. /** 
  220. * Add Product Data tab. 
  221. * @return void 
  222. */ 
  223. public function dependencies_product_data_panel_tab() { 
  224. echo '<li class="tied_products_tab related_product_options linked_product_options"><a href="#tied_products_data">' . __( 'Dependencies', 'woocommerce-product-dependencies' ) . '</a></li>'; 
  225.  
  226. /** 
  227. * Add the "Bundled Products" panel tab. 
  228. * @param array $tabs 
  229. * @return array 
  230. */ 
  231. public static function dependencies_product_data_tab( $tabs ) { 
  232.  
  233. $tabs[ 'dependencies' ] = array( 
  234. 'label' => __( 'Dependencies', 'woocommerce-product-dependencies' ),  
  235. 'target' => 'tied_products_data',  
  236. 'class' => array( 'show_if_simple', 'show_if_variable', 'show_if_bundle', 'show_if_composite', 'linked_product_options' ) 
  237. ); 
  238.  
  239. return $tabs; 
  240.  
  241. /** 
  242. * Add Product Data tab section. 
  243. * @return void 
  244. */ 
  245. public function dependencies_product_data_panel() { 
  246.  
  247. global $post, $product_object; 
  248.  
  249. if ( WC_PD_Core_Compatibility::is_wc_version_gte_2_7() ) { 
  250. $tied_products = $product_object->get_meta( '_tied_products', true ); 
  251. $dependency_type = $product_object->get_meta( '_dependency_type', true ); 
  252. } else { 
  253. $tied_products = get_post_meta( $post->ID, '_tied_products', true ); 
  254. $dependency_type = get_post_meta( $post->ID, '_dependency_type', true ); 
  255.  
  256. if ( ! $dependency_type ) { 
  257. $dependency_type = 3; 
  258.  
  259. $product_id_options = array(); 
  260.  
  261. if ( $tied_products ) { 
  262. foreach ( $tied_products as $item_id ) { 
  263.  
  264. $title = WC_PD_Helpers::get_product_title( $item_id ); 
  265.  
  266. if ( $title ) { 
  267. $product_id_options[ $item_id ] = $title; 
  268.  
  269. ?> 
  270. <div id="tied_products_data" class="panel woocommerce_options_panel wc-metaboxes-wrapper"> 
  271.  
  272. <p class="form-field"> 
  273. <label> 
  274. <?php _e( 'Product Dependencies', 'woocommerce-product-dependencies' ); ?> 
  275. </label><?php 
  276.  
  277. if ( WC_PD_Core_Compatibility::is_wc_version_gte_2_7() ) { 
  278.  
  279. ?><select id="tied_products" name="tied_products[]" class="wc-product-search" multiple="multiple" style="width: 75%;" data-limit="500" data-action="woocommerce_json_search_products_and_variations" data-placeholder="<?php echo __( 'Search for products and variations…', 'woocommerce-product-dependencies' ); ?>"><?php 
  280.  
  281. if ( ! empty( $product_id_options ) ) { 
  282.  
  283. foreach ( $product_id_options as $product_id => $product_name ) { 
  284. echo '<option value="' . $product_id . '" selected="selected">' . $product_name . '</option>'; 
  285.  
  286. ?></select><?php 
  287.  
  288. } elseif ( WC_PD_Core_Compatibility::is_wc_version_gte_2_3() ) { 
  289.  
  290. ?><input type="hidden" id="tied_products" name="tied_products" class="wc-product-search" style="width: 75%;" data-placeholder="<?php _e( 'Search for products…', 'woocommerce-product-dependencies' ); ?>" data-action="woocommerce_json_search_products" data-multiple="true" data-selected="<?php 
  291.  
  292. echo esc_attr( json_encode( $product_id_options ) ); 
  293.  
  294. ?>" value="<?php echo implode( ', ', array_keys( $product_id_options ) ); ?>" /><?php 
  295.  
  296. } else { 
  297.  
  298. ?><select id="tied_products" multiple="multiple" name="tied_products[]" data-placeholder="<?php _e( 'Search for products…', 'woocommerce-product-dependencies' ); ?>" class="ajax_chosen_select_products"><?php 
  299.  
  300. if ( ! empty( $product_id_options ) ) { 
  301. foreach ( $product_id_options as $product_id => $product_name ) { 
  302. echo '<option value="' . $product_id . '" selected="selected">' . $product_name . '</option>'; 
  303. ?></select><?php 
  304.  
  305.  
  306. echo WC_PD_Core_Compatibility::wc_help_tip( __( 'Restrict product access based on the ownership or purchase of <strong>any</strong> product or variation added to this list.', 'woocommerce-product-dependencies' ) ); 
  307.  
  308. ?> 
  309. </p> 
  310. <p class="form-field"> 
  311. <label><?php _e( 'Dependency Type', 'woocommerce-product-dependencies' ); ?> 
  312. </label> 
  313. <select name="dependency_type" id="dependency_type" style="min-width:150px;"> 
  314. <option value="1" <?php echo $dependency_type == 1 ? 'selected="selected"' : ''; ?>><?php _e( 'Ownership', 'woocommerce-product-dependencies' ); ?></option> 
  315. <option value="2" <?php echo $dependency_type == 2 ? 'selected="selected"' : ''; ?>><?php _e( 'Purchase', 'woocommerce-product-dependencies' ); ?></option> 
  316. <option value="3" <?php echo $dependency_type == 3 ? 'selected="selected"' : ''; ?>><?php _e( 'Either', 'woocommerce-product-dependencies' ); ?></option> 
  317. </select> 
  318. </p> 
  319. </div> 
  320. <?php 
  321.  
  322. /** 
  323. * Save dependencies data. WC >= 2.7. 
  324. * @param WC_Product $product 
  325. * @return void 
  326. */ 
  327. public function process_product_data( $product ) { 
  328.  
  329. if ( ! isset( $_POST[ 'tied_products' ] ) || empty( $_POST[ 'tied_products' ] ) ) { 
  330.  
  331. $product->delete_meta_data( '_tied_products' ); 
  332.  
  333. } elseif ( isset( $_POST[ 'tied_products' ] ) && ! empty( $_POST[ 'tied_products' ] ) ) { 
  334.  
  335. $tied_ids = $_POST[ 'tied_products' ]; 
  336.  
  337. if ( is_array( $tied_ids ) ) { 
  338. $tied_ids = array_map( 'intval', $tied_ids ); 
  339. } else { 
  340. $tied_ids = array_filter( array_map( 'intval', explode( ', ', $tied_ids ) ) ); 
  341.  
  342. $product->add_meta_data( '_tied_products', $tied_ids, true ); 
  343.  
  344. if ( isset( $_POST[ 'dependency_type' ] ) && ! empty( $_POST[ 'dependency_type' ] ) ) { 
  345. $product->add_meta_data( '_dependency_type', stripslashes( $_POST[ 'dependency_type' ] ), true ); 
  346.  
  347. /** 
  348. * Save dependencies meta. WC <= 2.6. 
  349. * @param int $post_id 
  350. * @param WC_Post $post 
  351. * @return void 
  352. */ 
  353. public function process_meta( $post_id, $post ) { 
  354.  
  355. global $post; 
  356.  
  357. if ( ! isset( $_POST[ 'tied_products' ] ) || empty( $_POST[ 'tied_products' ] ) ) { 
  358.  
  359. delete_post_meta( $post_id, '_tied_products' ); 
  360.  
  361. } elseif ( isset( $_POST[ 'tied_products' ] ) && ! empty( $_POST[ 'tied_products' ] ) ) { 
  362.  
  363. $tied_ids = $_POST[ 'tied_products' ]; 
  364.  
  365. if ( is_array( $tied_ids ) ) { 
  366. $tied_ids = array_map( 'intval', $tied_ids ); 
  367. } else { 
  368. $tied_ids = array_filter( array_map( 'intval', explode( ', ', $tied_ids ) ) ); 
  369.  
  370. update_post_meta( $post_id, '_tied_products', $tied_ids ); 
  371.  
  372. if ( isset( $_POST[ 'dependency_type' ] ) && ! empty( $_POST[ 'dependency_type' ] ) ) { 
  373. update_post_meta( $post_id, '_dependency_type', stripslashes( $_POST[ 'dependency_type' ] ) ); 
  374.  
  375. /** 
  376. |-------------------------------------------------------------------------- 
  377. | Deprecated methods. 
  378. |-------------------------------------------------------------------------- 
  379. */ 
  380.  
  381. /** 
  382. * Check conditions. 
  383. * @deprecated 1.1.0 
  384. * @param int $item_id 
  385. * @return boolean 
  386. */ 
  387. public function woo_tied_evaluate_access( $id ) { 
  388. _deprecated_function( __METHOD__ . '()', '1.1.0', __CLASS__ . '::evaluate_access()' ); 
  389. return WC_Product_Dependencies()->evaluate_dependencies( $id );