PluginProbe ʕ •ᴥ•ʔ
WPC Custom Related Products for WooCommerce / 3.2.5
WPC Custom Related Products for WooCommerce v3.2.5
3.2.5 trunk 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4
wpc-custom-related-products / wpc-custom-related-products.php
wpc-custom-related-products Last commit date
assets 23 hours ago includes 23 hours ago languages 23 hours ago index.php 23 hours ago readme.txt 23 hours ago wpc-custom-related-products.php 23 hours ago
wpc-custom-related-products.php
1232 lines
1 <?php
2 /*
3 Plugin Name: WPC Custom Related Products for WooCommerce
4 Plugin URI: https://wpclever.net/
5 Description: WPC Custom Related Products allows you to choose custom related products for each product.
6 Version: 3.2.5
7 Author: WPClever
8 Author URI: https://wpclever.net
9 Text Domain: wpc-custom-related-products
10 Domain Path: /languages/
11 Requires Plugins: woocommerce
12 Requires at least: 5.9
13 Tested up to: 7.0
14 WC requires at least: 3.0
15 WC tested up to: 10.9
16 License: GPLv2 or later
17 License URI: http://www.gnu.org/licenses/gpl-2.0.html
18 */
19
20 defined( 'ABSPATH' ) || exit;
21
22 ! defined( 'WOOCR_VERSION' ) && define( 'WOOCR_VERSION', '3.2.5' );
23 ! defined( 'WOOCR_LITE' ) && define( 'WOOCR_LITE', __FILE__ );
24 ! defined( 'WOOCR_FILE' ) && define( 'WOOCR_FILE', __FILE__ );
25 ! defined( 'WOOCR_URI' ) && define( 'WOOCR_URI', plugin_dir_url( __FILE__ ) );
26 ! defined( 'WOOCR_DIR' ) && define( 'WOOCR_DIR', plugin_dir_path( __FILE__ ) );
27 ! defined( 'WOOCR_SUPPORT' ) && define( 'WOOCR_SUPPORT', 'https://wpclever.net/support?utm_source=support&utm_medium=woocr&utm_campaign=wporg' );
28 ! defined( 'WOOCR_REVIEWS' ) && define( 'WOOCR_REVIEWS', 'https://wordpress.org/support/plugin/wpc-custom-related-products/reviews/' );
29 ! defined( 'WOOCR_CHANGELOG' ) && define( 'WOOCR_CHANGELOG', 'https://wordpress.org/plugins/wpc-custom-related-products/#developers' );
30 ! defined( 'WOOCR_DISCUSSION' ) && define( 'WOOCR_DISCUSSION', 'https://wordpress.org/support/plugin/wpc-custom-related-products' );
31
32 // WPC Core
33 require_once __DIR__ . '/includes/wpc-core/wpc-core.php';
34 wpc_core_register( [
35 'file' => __FILE__,
36 'version' => WOOCR_VERSION,
37 'prefix' => 'woocr',
38 ] );
39
40 if ( ! function_exists( 'woocr_init' ) ) {
41 add_action( 'plugins_loaded', 'woocr_init', 11 );
42
43 function woocr_init() {
44 if ( ! function_exists( 'WC' ) || ! version_compare( WC()->version, '3.0', '>=' ) ) {
45 add_action( 'admin_notices', 'woocr_notice_wc' );
46
47 return null;
48 }
49
50 if ( ! class_exists( 'WPCleverWoocr' ) && class_exists( 'WC_Product' ) ) {
51 class WPCleverWoocr {
52 protected static $instance = null;
53 protected static $settings = [];
54 public static $rules = [];
55
56 public static function instance() {
57 if ( is_null( self::$instance ) ) {
58 self::$instance = new self();
59 }
60
61 return self::$instance;
62 }
63
64 function __construct() {
65 self::$settings = (array) get_option( 'woocr_settings', [] );
66 self::$rules = (array) get_option( 'woocr_rules', [] );
67
68 // Init
69
70 // Settings
71 add_action( 'admin_init', [ $this, 'register_settings' ] );
72 add_filter( 'pre_update_option', [ $this, 'last_saved' ], 10, 2 );
73 add_action( 'admin_menu', [ $this, 'admin_menu' ] );
74
75 // Enqueue backend scripts
76 add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
77
78 // Add settings link
79 add_filter( 'plugin_action_links', [ $this, 'action_links' ], 10, 2 );
80 add_filter( 'plugin_row_meta', [ $this, 'row_meta' ], 10, 2 );
81
82 // AJAX
83 add_action( 'wp_ajax_woocr_get_search_results', [ $this, 'ajax_get_search_results' ] );
84 add_action( 'wp_ajax_woocr_add_rule', [ $this, 'ajax_add_rule' ] );
85 add_action( 'wp_ajax_woocr_search_term', [ $this, 'ajax_search_term' ] );
86
87 // Product data tabs
88 add_filter( 'woocommerce_product_data_tabs', [ $this, 'product_data_tabs' ] );
89 add_action( 'woocommerce_product_data_panels', [ $this, 'product_data_panels' ] );
90 add_action( 'woocommerce_process_product_meta', [ $this, 'process_product_meta' ] );
91
92 // Related products
93 add_filter( 'woocommerce_product_related_posts_shuffle', '__return_false', 99 );
94 add_filter( 'woocommerce_related_products', [ $this, 'related_products' ], 99, 2 );
95 add_filter( 'woocommerce_output_related_products_args', [ $this, 'related_products_args' ], 99 );
96
97 // Exclude Unpurchasable
98 if ( self::get_setting( 'exclude_unpurchasable', 'no' ) === 'yes' ) {
99 add_filter( 'woocr_related_products_before_limit', [ $this, 'exclude_unpurchasable' ] );
100 }
101
102 // Search filters
103 if ( self::get_setting( 'search_sku', 'no' ) === 'yes' ) {
104 add_filter( 'pre_get_posts', [ $this, 'search_sku' ], 99 );
105 }
106
107 if ( self::get_setting( 'search_exact', 'no' ) === 'yes' ) {
108 add_action( 'pre_get_posts', [ $this, 'search_exact' ], 99 );
109 }
110
111 if ( self::get_setting( 'search_sentence', 'no' ) === 'yes' ) {
112 add_action( 'pre_get_posts', [ $this, 'search_sentence' ], 99 );
113 }
114 }
115 public static function get_settings() {
116 return apply_filters( 'woocr_get_settings', self::$settings );
117 }
118
119 public static function get_setting( $name, $default = false ) {
120 if ( ! empty( self::$settings ) && isset( self::$settings[ $name ] ) ) {
121 $setting = self::$settings[ $name ];
122 } else {
123 $setting = get_option( 'woocr_' . $name, $default );
124 }
125
126 return apply_filters( 'woocr_get_setting', $setting, $name, $default );
127 }
128
129 function register_settings() {
130 // settings
131 register_setting( 'woocr_settings', 'woocr_settings', [
132 'type' => 'array',
133 'sanitize_callback' => [ $this, 'sanitize_array' ],
134 ] );
135 // rules
136 register_setting( 'woocr_rules', 'woocr_rules_settings', [
137 'type' => 'array',
138 'sanitize_callback' => [ $this, 'sanitize_array' ],
139 ] );
140 register_setting( 'woocr_rules', 'woocr_rules', [
141 'type' => 'array',
142 'sanitize_callback' => [ $this, 'sanitize_array' ],
143 ] );
144 }
145
146 function last_saved( $value, $option ) {
147 if ( $option == 'woocr_settings' || $option == 'woocr_rules_settings' ) {
148 $value['_last_saved'] = current_time( 'timestamp' );
149 $value['_last_saved_by'] = get_current_user_id();
150 }
151
152 return $value;
153 }
154
155 function admin_menu() {
156 add_submenu_page( 'wpclever', esc_html__( 'WPC Custom Related Products', 'wpc-custom-related-products' ), esc_html__( 'Related Products', 'wpc-custom-related-products' ), 'manage_options', 'wpclever-woocr', [
157 $this,
158 'admin_menu_content'
159 ] );
160 }
161
162 function admin_menu_content() {
163 $active_tab = sanitize_key( wp_unslash( $_GET['tab'] ?? 'settings' ) );
164 ?>
165 <div class="wpclever_settings_page wrap">
166 <div class="wpclever_settings_page_header">
167 <a class="wpclever_settings_page_header_logo" href="https://wpclever.net/"
168 target="_blank" title="Visit wpclever.net"></a>
169 <div class="wpclever_settings_page_header_text">
170 <div class="wpclever_settings_page_title"><?php echo esc_html__( 'WPC Custom Related Products', 'wpc-custom-related-products' ) . ' ' . esc_html( WOOCR_VERSION ) . ' ' . ( defined( 'WOOCR_PREMIUM' ) ? '<span class="premium" style="display: none">' . esc_html__( 'Premium', 'wpc-custom-related-products' ) . '</span>' : '' ); ?></div>
171 <div class="wpclever_settings_page_desc about-text">
172 <p>
173 <?php printf( /* translators: stars */ esc_html__( 'Thank you for using our plugin! If you are satisfied, please reward it a full five-star %s rating.', 'wpc-custom-related-products' ), '<span style="color:#ffb900">&#9733;&#9733;&#9733;&#9733;&#9733;</span>' ); ?>
174 <br/>
175 <a href="<?php echo esc_url( WOOCR_REVIEWS ); ?>"
176 target="_blank"><?php esc_html_e( 'Reviews', 'wpc-custom-related-products' ); ?></a>
177 |
178 <a href="<?php echo esc_url( WOOCR_CHANGELOG ); ?>"
179 target="_blank"><?php esc_html_e( 'Changelog', 'wpc-custom-related-products' ); ?></a>
180 |
181 <a href="<?php echo esc_url( WOOCR_DISCUSSION ); ?>"
182 target="_blank"><?php esc_html_e( 'Discussion', 'wpc-custom-related-products' ); ?></a>
183 </p>
184 </div>
185 </div>
186 </div>
187 <h2></h2>
188 <?php if ( isset( $_GET['settings-updated'] ) && sanitize_key( wp_unslash( $_GET['settings-updated'] ?? '' ) ) ) { ?>
189 <div class="notice notice-success is-dismissible">
190 <p><?php esc_html_e( 'Settings updated.', 'wpc-custom-related-products' ); ?></p>
191 </div>
192 <?php } ?>
193 <div class="wpclever_settings_page_nav">
194 <h2 class="nav-tab-wrapper">
195 <a href="<?php echo esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=how' ) ); ?>"
196 class="<?php echo esc_attr( $active_tab === 'how' ? 'nav-tab nav-tab-active' : 'nav-tab' ); ?>">
197 <?php esc_html_e( 'How to use?', 'wpc-custom-related-products' ); ?>
198 </a>
199 <a href="<?php echo esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=settings' ) ); ?>"
200 class="<?php echo esc_attr( $active_tab === 'settings' ? 'nav-tab nav-tab-active' : 'nav-tab' ); ?>">
201 <?php esc_html_e( 'Settings', 'wpc-custom-related-products' ); ?>
202 </a>
203 <a href="<?php echo esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=smart' ) ); ?>"
204 class="<?php echo esc_attr( $active_tab === 'smart' ? 'nav-tab nav-tab-active' : 'nav-tab' ); ?>">
205 <?php esc_html_e( 'Smart Related', 'wpc-custom-related-products' ); ?>
206 </a>
207 <a href="<?php echo esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=premium' ) ); ?>"
208 class="<?php echo esc_attr( $active_tab === 'premium' ? 'nav-tab nav-tab-active' : 'nav-tab' ); ?>"
209 style="color: #c9356e">
210 <?php esc_html_e( 'Premium Version', 'wpc-custom-related-products' ); ?>
211 </a>
212 <a href="<?php echo esc_url( admin_url( 'admin.php?page=wpclever-kit' ) ); ?>"
213 class="nav-tab">
214 <?php esc_html_e( 'Essential Kit', 'wpc-custom-related-products' ); ?>
215 </a>
216 </h2>
217 </div>
218 <div class="wpclever_settings_page_content">
219 <?php if ( $active_tab === 'how' ) { ?>
220 <div class="wpclever_settings_page_content_text">
221 <p>
222 <?php esc_html_e( 'When creating/editing the product, please choose "Related Product" tab then you can search and add custom related products.', 'wpc-custom-related-products' ); ?>
223 </p>
224 <p>
225 <img src="<?php echo esc_url( WOOCR_URI . 'assets/images/how-01.jpg' ); ?>"
226 alt=""/>
227 </p>
228 </div>
229 <?php
230 } elseif ( $active_tab === 'settings' ) {
231 $exclude_unpurchasable = self::get_setting( 'exclude_unpurchasable', 'no' );
232 $default = self::get_setting( 'default', 'related' );
233 $search_limit = self::get_setting( 'search_limit', 10 );
234 $search_sku = self::get_setting( 'search_sku', 'no' );
235 $search_id = self::get_setting( 'search_id', 'no' );
236 $search_exact = self::get_setting( 'search_exact', 'no' );
237 $search_sentence = self::get_setting( 'search_sentence', 'no' );
238 ?>
239 <form method="post" action="options.php">
240 <table class="form-table">
241 <tr class="heading">
242 <th colspan="2">
243 <?php esc_html_e( 'General', 'wpc-custom-related-products' ); ?>
244 </th>
245 </tr>
246 <tr>
247 <th scope="row"><?php esc_html_e( 'Exclude unpurchasable', 'wpc-custom-related-products' ); ?></th>
248 <td>
249 <label> <select name="woocr_settings[exclude_unpurchasable]">
250 <option value="yes" <?php selected( $exclude_unpurchasable, 'yes' ); ?>><?php esc_html_e( 'Yes', 'wpc-custom-related-products' ); ?></option>
251 <option value="no" <?php selected( $exclude_unpurchasable, 'no' ); ?>><?php esc_html_e( 'No', 'wpc-custom-related-products' ); ?></option>
252 </select> </label>
253 <span class="description"><?php esc_html_e( 'Exclude unpurchasable products from related.', 'wpc-custom-related-products' ); ?></span>
254 </td>
255 </tr>
256 <tr>
257 <th><?php esc_html_e( 'Default products', 'wpc-custom-related-products' ); ?></th>
258 <td>
259 <label> <select name="woocr_settings[default]">
260 <option value="related" <?php selected( $default, 'related' ); ?>><?php esc_html_e( 'Default Related', 'wpc-custom-related-products' ); ?></option>
261 <option value="smart_related" <?php selected( $default, 'smart_related' ); ?>><?php esc_html_e( 'Smart Related', 'wpc-custom-related-products' ); ?></option>
262 <option value="up_sells" <?php selected( $default, 'up_sells' ); ?>><?php esc_html_e( 'Upsells', 'wpc-custom-related-products' ); ?></option>
263 <option value="cross_sells" <?php selected( $default, 'cross_sells' ); ?>><?php esc_html_e( 'Cross-sells', 'wpc-custom-related-products' ); ?></option>
264 <option value="up_cross_sells" <?php selected( $default, 'up_cross_sells' ); ?>><?php esc_html_e( 'Upsells & Cross-sells', 'wpc-custom-related-products' ); ?></option>
265 <option value="none" <?php selected( $default, 'none' ); ?>><?php esc_html_e( 'None', 'wpc-custom-related-products' ); ?></option>
266 </select> </label> <span class="description">If you choose "Smart Related", please configure the rules on Smart Related tab. If you choose Upsells/Cross-sells, you also can use
267 <a href="https://wordpress.org/plugins/wpc-smart-linked-products/"
268 target="_blank">WPC Smart Linked Products</a> to configure upsells/cross-sells products in bulk.
269 </span>
270 </td>
271 </tr>
272 <tr>
273 <th><?php esc_html_e( 'Default products limit', 'wpc-custom-related-products' ); ?></th>
274 <td>
275 <label>
276 <input type="number" class="small-text"
277 name="woocr_settings[default_limit]"
278 value="<?php echo esc_attr( self::get_setting( 'default_limit', 5 ) ); ?>"/>
279 </label>
280 </td>
281 </tr>
282 <tr class="heading">
283 <th colspan="2">
284 <?php esc_html_e( 'Search', 'wpc-custom-related-products' ); ?>
285 </th>
286 </tr>
287 <tr>
288 <th><?php esc_html_e( 'Search limit', 'wpc-custom-related-products' ); ?></th>
289 <td>
290 <label>
291 <input name="woocr_settings[search_limit]" type="number" min="1"
292 max="500" value="<?php echo esc_attr( $search_limit ); ?>"/>
293 </label>
294 </td>
295 </tr>
296 <tr>
297 <th><?php esc_html_e( 'Search by SKU', 'wpc-custom-related-products' ); ?></th>
298 <td>
299 <label> <select name="woocr_settings[search_sku]">
300 <option value="yes" <?php selected( $search_sku, 'yes' ); ?>><?php esc_html_e( 'Yes', 'wpc-custom-related-products' ); ?></option>
301 <option value="no" <?php selected( $search_sku, 'no' ); ?>><?php esc_html_e( 'No', 'wpc-custom-related-products' ); ?></option>
302 </select> </label>
303 </td>
304 </tr>
305 <tr>
306 <th><?php esc_html_e( 'Search by ID', 'wpc-custom-related-products' ); ?></th>
307 <td>
308 <label> <select name="woocr_settings[search_id]">
309 <option value="yes" <?php selected( $search_id, 'yes' ); ?>><?php esc_html_e( 'Yes', 'wpc-custom-related-products' ); ?></option>
310 <option value="no" <?php selected( $search_id, 'no' ); ?>><?php esc_html_e( 'No', 'wpc-custom-related-products' ); ?></option>
311 </select> </label>
312 </td>
313 </tr>
314 <tr>
315 <th><?php esc_html_e( 'Search exact', 'wpc-custom-related-products' ); ?></th>
316 <td>
317 <label> <select name="woocr_settings[search_exact]">
318 <option value="yes" <?php selected( $search_exact, 'yes' ); ?>><?php esc_html_e( 'Yes', 'wpc-custom-related-products' ); ?></option>
319 <option value="no" <?php selected( $search_exact, 'no' ); ?>><?php esc_html_e( 'No', 'wpc-custom-related-products' ); ?></option>
320 </select> </label>
321 <span class="description"><?php esc_html_e( 'Match whole product title or content?', 'wpc-custom-related-products' ); ?></span>
322 </td>
323 </tr>
324 <tr>
325 <th><?php esc_html_e( 'Search sentence', 'wpc-custom-related-products' ); ?></th>
326 <td>
327 <label> <select name="woocr_settings[search_sentence]">
328 <option value="yes" <?php selected( $search_sentence, 'yes' ); ?>><?php esc_html_e( 'Yes', 'wpc-custom-related-products' ); ?></option>
329 <option value="no" <?php selected( $search_sentence, 'no' ); ?>><?php esc_html_e( 'No', 'wpc-custom-related-products' ); ?></option>
330 </select> </label>
331 <span class="description"><?php esc_html_e( 'Do a phrase search?', 'wpc-custom-related-products' ); ?></span>
332 </td>
333 </tr>
334 <tr class="submit">
335 <th colspan="2">
336 <div class="wpclever_submit">
337 <?php
338 settings_fields( 'woocr_settings' );
339 submit_button( '', 'primary', 'submit', false );
340
341 if ( function_exists( 'wpc_last_saved' ) ) {
342 wpc_last_saved( self::get_settings() );
343 }
344 ?>
345 </div>
346 <a style="display: none;" class="wpclever_export"
347 data-key="woocr_settings"
348 data-name="settings"
349 href="#"><?php esc_html_e( 'import / export', 'wpc-custom-related-products' ); ?></a>
350 </th>
351 </tr>
352 </table>
353 </form>
354 <?php } elseif ( $active_tab === 'smart' ) {
355 self::rules( 'woocr_rules', self::$rules );
356 } elseif ( $active_tab === 'premium' ) { ?>
357 <div class="wpclever_settings_page_content_text">
358 <p>Get the Premium Version just $29!
359 <a href="https://wpclever.net/downloads/custom-related-products?utm_source=pro&utm_medium=woocr&utm_campaign=wporg"
360 target="_blank">https://wpclever.net/downloads/custom-related-products</a>
361 </p>
362 <p><strong>Extra features for Premium Version:</strong></p>
363 <ul style="margin-bottom: 0">
364 <li>- Use combined conditions for Smart Related.</li>
365 <li>- Get the lifetime update & premium support.</li>
366 </ul>
367 </div>
368 <?php } ?>
369 </div><!-- /.wpclever_settings_page_content -->
370 <div class="wpclever_settings_page_suggestion">
371 <div class="wpclever_settings_page_suggestion_label">
372 <span class="dashicons dashicons-yes-alt"></span> Suggestion
373 </div>
374 <div class="wpclever_settings_page_suggestion_content">
375 <div>
376 To display custom engaging real-time messages on any wished positions, please
377 install
378 <a href="https://wordpress.org/plugins/wpc-smart-messages/" target="_blank">WPC
379 Smart Messages</a> plugin. It's free!
380 </div>
381 <div>
382 Wanna save your precious time working on variations? Try our brand-new free plugin
383 <a href="https://wordpress.org/plugins/wpc-variation-bulk-editor/" target="_blank">WPC
384 Variation Bulk Editor</a> and
385 <a href="https://wordpress.org/plugins/wpc-variation-duplicator/" target="_blank">WPC
386 Variation Duplicator</a>.
387 </div>
388 </div>
389 </div>
390 </div>
391 <?php
392 }
393
394 function rules( $name = 'woocr_rules', $rules = [] ) {
395 ?>
396 <form method="post" action="options.php">
397 <table class="form-table">
398 <tr>
399 <td>
400 <?php esc_html_e( 'Our plugin checks rules from the top down the list. When there are products that satisfy more than 1 rule, the first rule on top will be prioritized. Please make sure you put the rules in the order of the most to the least prioritized.', 'wpc-custom-related-products' ); ?>
401 </td>
402 </tr>
403 <tr>
404 <td>
405 <div class="woocr_rules">
406 <?php
407 if ( is_array( $rules ) && ( count( $rules ) > 0 ) ) {
408 foreach ( $rules as $key => $rule ) {
409 self::rule( $key, $name, $rule, false );
410 }
411 } else {
412 self::rule( '', $name, null, true );
413 }
414 ?>
415 </div>
416 <div class="woocr_add_rule">
417 <div>
418 <a href="#" class="woocr_new_rule button"
419 data-name="<?php echo esc_attr( $name ); ?>">
420 <?php esc_html_e( '+ Add rule', 'wpc-custom-related-products' ); ?>
421 </a> <a href="#" class="woocr_expand_all">
422 <?php esc_html_e( 'Expand All', 'wpc-custom-related-products' ); ?>
423 </a> <a href="#" class="woocr_collapse_all">
424 <?php esc_html_e( 'Collapse All', 'wpc-custom-related-products' ); ?>
425 </a>
426 </div>
427 </div>
428 </td>
429 </tr>
430 <tr class="submit">
431 <th colspan="2">
432 <div class="wpclever_submit">
433 <?php
434 $log = $name . '_settings';
435 echo '<input type="hidden" name="' . esc_attr( $log ) . '[version]" value="' . esc_attr( WOOCR_VERSION ) . '"/>';
436 settings_fields( $name );
437 submit_button( '', 'primary', 'submit', false );
438
439 if ( function_exists( 'wpc_last_saved' ) ) {
440 wpc_last_saved( get_option( $log, [] ) );
441 }
442 ?>
443 </div>
444 <a style="display: none;" class="wpclever_export"
445 data-key="woocr_rules"
446 data-name="rules"
447 href="#"><?php esc_html_e( 'import / export', 'wpc-custom-related-products' ); ?></a>
448 </th>
449 </tr>
450 </table>
451 </form>
452 <?php
453 }
454
455 function rule( $key = '', $name = 'woocr_rules', $rule = null, $active = false ) {
456 if ( empty( $key ) || is_numeric( $key ) ) {
457 $key = self::generate_key();
458 }
459
460 $apply = $rule['apply'] ?? 'all';
461 $apply_products = (array) ( $rule['apply_products'] ?? [] );
462 $apply_terms = (array) ( $rule['apply_terms'] ?? [] );
463 $apply_combination = (array) ( $rule['apply_combination'] ?? [] );
464 $get = $rule['get'] ?? 'all';
465 $get_products = (array) ( $rule['get_products'] ?? [] );
466 $get_terms = (array) ( $rule['get_terms'] ?? [] );
467 $get_combination = (array) ( $rule['get_combination'] ?? [] );
468 $get_limit = absint( $rule['get_limit'] ?? 3 );
469 $get_orderby = $rule['get_orderby'] ?? 'default';
470 $get_order = $rule['get_order'] ?? 'default';
471 ?>
472 <div class="<?php echo esc_attr( $active ? 'woocr_rule active' : 'woocr_rule' ); ?>"
473 data-key="<?php echo esc_attr( $key ); ?>">
474 <div class="woocr_rule_heading">
475 <span class="woocr_rule_move"></span>
476 <span class="woocr_rule_label"><?php echo esc_html( $apply . ' / ' . $get ); ?></span>
477 <a href="#" class="woocr_rule_duplicate"
478 data-name="<?php echo esc_attr( $name ); ?>"><?php esc_html_e( 'duplicate', 'wpc-custom-related-products' ); ?></a>
479 <a href="#"
480 class="woocr_rule_remove"><?php esc_html_e( 'remove', 'wpc-custom-related-products' ); ?></a>
481 </div>
482 <div class="woocr_rule_content">
483 <div class="woocr_tr">
484 <div class="woocr_th woocr_th_full">
485 <?php esc_html_e( 'Add linked products to which?', 'wpc-custom-related-products' ); ?>
486 </div>
487 </div>
488 <?php self::source( $name, $key, $apply, $apply_products, $apply_terms, $apply_combination, 'apply' ); ?>
489 <div class="woocr_tr">
490 <div class="woocr_th woocr_th_full">
491 <?php esc_html_e( 'Define applicable linked products:', 'wpc-custom-related-products' ); ?>
492 </div>
493 </div>
494 <?php self::source( $name, $key, $get, $get_products, $get_terms, $get_combination, 'get', $get_limit, $get_orderby, $get_order ); ?>
495 </div>
496 </div>
497 <?php
498 }
499
500 function source( $name, $key, $apply, $products, $terms, $combination, $type = 'apply', $get_limit = null, $get_orderby = null, $get_order = null ) {
501 ?>
502 <div class="woocr_tr">
503 <div class="woocr_th"><?php esc_html_e( 'Source', 'wpc-custom-related-products' ); ?></div>
504 <div class="woocr_td woocr_rule_td">
505 <label>
506 <select class="woocr_source_selector woocr_source_selector_<?php echo esc_attr( $type ); ?>"
507 data-type="<?php echo esc_attr( $type ); ?>"
508 name="<?php echo esc_attr( $name ); ?>[<?php echo esc_attr( $key ); ?>][<?php echo esc_attr( $type ); ?>]">
509 <option value="all"><?php esc_html_e( 'All products', 'wpc-custom-related-products' ); ?></option>
510 <option value="products" <?php selected( $apply, 'products' ); ?>><?php esc_html_e( 'Selected products', 'wpc-custom-related-products' ); ?></option>
511 <option value="combination" <?php selected( $apply, 'combination' ); ?>
512 disabled><?php esc_html_e( 'Combined (Premium)', 'wpc-custom-related-products' ); ?></option>
513 <?php
514 $taxonomies = get_object_taxonomies( 'product', 'objects' );
515
516 foreach ( $taxonomies as $taxonomy ) {
517 echo '<option value="' . esc_attr( $taxonomy->name ) . '" ' . ( $apply === $taxonomy->name ? 'selected' : '' ) . '>' . esc_html( $taxonomy->label ) . '</option>';
518 }
519 ?>
520 </select> </label>
521 <?php if ( $type === 'get' ) { ?>
522 <span class="show_get hide_if_get_products">
523 <span><?php esc_html_e( 'Limit', 'wpc-custom-related-products' ); ?> <label>
524 <input type="number" min="1" max="50"
525 name="<?php echo esc_attr( $name ); ?>[<?php echo esc_attr( $key ); ?>][get_limit]"
526 value="<?php echo esc_attr( $get_limit ); ?>"/>
527 </label></span>
528 <span>
529 <?php esc_html_e( 'Order by', 'wpc-custom-related-products' ); ?> <label>
530 <select name="<?php echo esc_attr( $name ); ?>[<?php echo esc_attr( $key ); ?>][get_orderby]">
531 <option value="default" <?php selected( $get_orderby, 'default' ); ?>><?php esc_html_e( 'Default', 'wpc-custom-related-products' ); ?></option>
532 <option value="none" <?php selected( $get_orderby, 'none' ); ?>><?php esc_html_e( 'None', 'wpc-custom-related-products' ); ?></option>
533 <option value="ID" <?php selected( $get_orderby, 'ID' ); ?>><?php esc_html_e( 'ID', 'wpc-custom-related-products' ); ?></option>
534 <option value="name" <?php selected( $get_orderby, 'name' ); ?>><?php esc_html_e( 'Name', 'wpc-custom-related-products' ); ?></option>
535 <option value="type" <?php selected( $get_orderby, 'type' ); ?>><?php esc_html_e( 'Type', 'wpc-custom-related-products' ); ?></option>
536 <option value="rand" <?php selected( $get_orderby, 'rand' ); ?>><?php esc_html_e( 'Rand', 'wpc-custom-related-products' ); ?></option>
537 <option value="date" <?php selected( $get_orderby, 'date' ); ?>><?php esc_html_e( 'Date', 'wpc-custom-related-products' ); ?></option>
538 <option value="price" <?php selected( $get_orderby, 'price' ); ?>><?php esc_html_e( 'Price', 'wpc-custom-related-products' ); ?></option>
539 <option value="modified" <?php selected( $get_orderby, 'modified' ); ?>><?php esc_html_e( 'Modified', 'wpc-custom-related-products' ); ?></option>
540 </select>
541 </label>
542 </span>
543 <span><?php esc_html_e( 'Order', 'wpc-custom-related-products' ); ?> <label>
544 <select name="<?php echo esc_attr( $name ); ?>[<?php echo esc_attr( $key ); ?>][get_order]">
545 <option value="default" <?php selected( $get_order, 'default' ); ?>><?php esc_html_e( 'Default', 'wpc-custom-related-products' ); ?></option>
546 <option value="DESC" <?php selected( $get_order, 'DESC' ); ?>><?php esc_html_e( 'DESC', 'wpc-custom-related-products' ); ?></option>
547 <option value="ASC" <?php selected( $get_order, 'ASC' ); ?>><?php esc_html_e( 'ASC', 'wpc-custom-related-products' ); ?></option>
548 </select>
549 </label></span>
550 </span>
551 <?php } ?>
552 </div>
553 </div>
554 <div class="woocr_tr hide_<?php echo esc_attr( $type ); ?> show_if_<?php echo esc_attr( $type ); ?>_products">
555 <div class="woocr_th"><?php esc_html_e( 'Products', 'wpc-custom-related-products' ); ?></div>
556 <div class="woocr_td woocr_rule_td">
557 <label>
558 <select class="wc-product-search woocr-product-search"
559 name="<?php echo esc_attr( $name ); ?>[<?php echo esc_attr( $key ); ?>][<?php echo esc_attr( $type . '_products' ); ?>][]"
560 multiple="multiple"
561 data-placeholder="<?php esc_attr_e( 'Search for a product&hellip;', 'wpc-custom-related-products' ); ?>"
562 data-action="woocommerce_json_search_products_and_variations">
563 <?php
564 if ( ! empty( $products ) ) {
565 foreach ( $products as $_product_id ) {
566 if ( $_product = wc_get_product( $_product_id ) ) {
567 echo '<option value="' . esc_attr( $_product_id ) . '" selected>' . wp_kses_post( $_product->get_formatted_name() ) . '</option>';
568 }
569 }
570 }
571 ?>
572 </select> </label>
573 </div>
574 </div>
575 <div class="woocr_tr show_<?php echo esc_attr( $type ); ?> hide_if_<?php echo esc_attr( $type ); ?>_all hide_if_<?php echo esc_attr( $type ); ?>_products hide_if_<?php echo esc_attr( $type ); ?>_combination">
576 <div class="woocr_th woocr_<?php echo esc_attr( $type ); ?>_text"><?php esc_html_e( 'Terms', 'wpc-custom-related-products' ); ?></div>
577 <div class="woocr_td woocr_rule_td">
578 <label>
579 <select class="woocr_terms" data-type="<?php echo esc_attr( $type ); ?>"
580 name="<?php echo esc_attr( $name ); ?>[<?php echo esc_attr( $key ); ?>][<?php echo esc_attr( $type . '_terms' ); ?>][]"
581 multiple="multiple"
582 data-<?php echo esc_attr( $apply ); ?>="<?php echo esc_attr( implode( ',', $terms ) ); ?>">
583 <?php
584 if ( ! empty( $terms ) ) {
585 foreach ( $terms as $at ) {
586 if ( $term = get_term_by( 'slug', $at, $apply ) ) {
587 echo '<option value="' . esc_attr( $at ) . '" selected>' . esc_html( $term->name ) . '</option>';
588 }
589 }
590 }
591 ?>
592 </select> </label>
593 </div>
594 </div>
595 <?php
596 }
597
598 function ajax_add_rule() {
599 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'woocr_nonce' ) || ! current_user_can( 'edit_products' ) ) {
600 die( 'Permissions check failed!' );
601 }
602
603 $rule = [];
604 $name = sanitize_key( wp_unslash( $_POST['name'] ?? 'woocr_rules' ) );
605 $rule_data = isset( $_POST['rule_data'] ) ? sanitize_text_field( wp_unslash( $_POST['rule_data'] ?? '' ) ) : '';
606
607 if ( ! empty( $rule_data ) ) {
608 $form_rule = [];
609 parse_str( $rule_data, $form_rule );
610
611 if ( isset( $form_rule[ $name ] ) && is_array( $form_rule[ $name ] ) ) {
612 $rule = reset( $form_rule[ $name ] );
613 }
614 }
615
616 self::rule( '', $name, $rule, true );
617 wp_die();
618 }
619
620 function ajax_search_term() {
621 if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_REQUEST['nonce'] ) ), 'woocr_nonce' ) || ! current_user_can( 'edit_products' ) ) {
622 die( 'Permissions check failed!' );
623 }
624
625 $return = [];
626
627 $args = [
628 'taxonomy' => isset( $_REQUEST['taxonomy'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['taxonomy'] ?? '' ) ) : '',
629 'orderby' => 'id',
630 'order' => 'ASC',
631 'hide_empty' => false,
632 'fields' => 'all',
633 'name__like' => isset( $_REQUEST['q'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['q'] ?? '' ) ) : '',
634 ];
635
636 $terms = get_terms( $args );
637
638 if ( count( $terms ) ) {
639 foreach ( $terms as $term ) {
640 $return[] = [ $term->slug, $term->name ];
641 }
642 }
643
644 wp_send_json( $return );
645 }
646
647 function admin_enqueue_scripts( $hook ) {
648 if ( apply_filters( 'woocr_ignore_backend_scripts', false, $hook ) ) {
649 return null;
650 }
651
652 wp_enqueue_style( 'hint', WOOCR_URI . 'assets/css/hint.css', [], WOOCR_VERSION );
653 wp_enqueue_style( 'woocr-backend', WOOCR_URI . 'assets/css/backend.css', [ 'woocommerce_admin_styles' ], WOOCR_VERSION );
654 wp_enqueue_script( 'woocr-backend', WOOCR_URI . 'assets/js/backend.js', [
655 'jquery',
656 'jquery-ui-sortable',
657 'wc-enhanced-select',
658 'selectWoo',
659 ], WOOCR_VERSION, true );
660 wp_localize_script( 'woocr-backend', 'woocr_vars', [
661 'woocr_nonce' => wp_create_nonce( 'woocr_nonce' )
662 ] );
663 }
664
665 function action_links( $links, $file ) {
666 static $plugin;
667
668 if ( ! isset( $plugin ) ) {
669 $plugin = plugin_basename( __FILE__ );
670 }
671
672 if ( $plugin === $file ) {
673 $how = '<a href="' . esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=how' ) ) . '">' . esc_html__( 'How to use?', 'wpc-custom-related-products' ) . '</a>';
674 $settings = '<a href="' . esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=settings' ) ) . '">' . esc_html__( 'Settings', 'wpc-custom-related-products' ) . '</a>';
675 $smart = '<a href="' . esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=smart' ) ) . '">' . esc_html__( 'Smart Related', 'wpc-custom-related-products' ) . '</a>';
676 $links['wpc-premium'] = '<a href="' . esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=premium' ) ) . '">' . esc_html__( 'Premium Version', 'wpc-custom-related-products' ) . '</a>';
677 array_unshift( $links, $how, $settings, $smart );
678 }
679
680 return (array) $links;
681 }
682
683 function row_meta( $links, $file ) {
684 static $plugin;
685
686 if ( ! isset( $plugin ) ) {
687 $plugin = plugin_basename( __FILE__ );
688 }
689
690 if ( $plugin === $file ) {
691 $row_meta = [
692 'support' => '<a href="' . esc_url( WOOCR_DISCUSSION ) . '" target="_blank">' . esc_html__( 'Community support', 'wpc-custom-related-products' ) . '</a>',
693 ];
694
695 return array_merge( $links, $row_meta );
696 }
697
698 return (array) $links;
699 }
700
701 function ajax_get_search_results() {
702 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'woocr_nonce' ) || ! current_user_can( 'edit_products' ) ) {
703 die( 'Permissions check failed!' );
704 }
705
706 $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['keyword'] ?? '' ) ) : '';
707 $ids = ! empty( $_POST['ids'] ) ? array_map( 'absint', wp_unslash( $_POST['ids'] ?? [] ) ) : [];
708
709 if ( ( self::get_setting( 'search_id', 'no' ) === 'yes' ) && is_numeric( $keyword ) ) {
710 // search by id
711 $query_args = [
712 'p' => absint( $keyword ),
713 'post_type' => 'product'
714 ];
715 } else {
716 $query_args = [
717 'is_woocr' => true,
718 'post_type' => 'product',
719 'post_status' => [ 'publish', 'private' ],
720 's' => $keyword,
721 'posts_per_page' => self::get_setting( 'search_limit', 10 ),
722 'post__not_in' => $ids
723 ];
724 }
725
726 $query = new WP_Query( $query_args );
727
728 if ( $query->have_posts() ) {
729 echo '<ul>';
730
731 while ( $query->have_posts() ) {
732 $query->the_post();
733 $product = wc_get_product( get_the_ID() );
734
735 if ( ! $product ) {
736 continue;
737 }
738
739 self::product_data_li( $product, true );
740 }
741
742 echo '</ul>';
743
744 wp_reset_postdata();
745 } else {
746 echo '<ul><span>' . sprintf( /* translators: keyword */ esc_html__( 'No results found for "%s"', 'wpc-custom-related-products' ), esc_html( $keyword ) ) . '</span></ul>';
747 }
748
749 wp_die();
750 }
751
752 function product_data_li( $product, $search = false ) {
753 $product_id = $product->get_id();
754
755 if ( $search ) {
756 $remove_btn = '<span class="woocr-remove hint--left" aria-label="' . esc_html__( 'Add', 'wpc-custom-related-products' ) . '">+</span>';
757 } else {
758 $remove_btn = '<span class="woocr-remove hint--left" aria-label="' . esc_html__( 'Remove', 'wpc-custom-related-products' ) . '">×</span>';
759 }
760
761 if ( $product->is_type( 'variation' ) ) {
762 $edit_link = get_edit_post_link( $product->get_parent_id() );
763 } else {
764 $edit_link = get_edit_post_link( $product_id );
765 }
766
767 echo '<li ' . ( ! $product->is_in_stock() ? 'class="out-of-stock"' : '' ) . '><span class="move"></span><span class="name"><input type="hidden" ' . ( ! $search ? 'name="woocr_ids[]"' : '' ) . ' value="' . esc_attr( $product_id ) . '"/>' . esc_html( $product->get_name() ) . ' <span class="price">' . wp_kses_post( $product->get_price_html() ) . '</span></span><span class="type"><a target="_blank" href="' . esc_url( $edit_link ) . '">' . esc_html( $product->get_type() ) . '<br/>#' . esc_html( $product->get_id() ) . '</a></span>' . wp_kses_post( $remove_btn ) . '</li>';
768 }
769
770 function product_data_tabs( $tabs ) {
771 $tabs['woocr'] = [
772 'label' => esc_html__( 'Related Products', 'wpc-custom-related-products' ),
773 'target' => 'woocr_settings'
774 ];
775
776 return $tabs;
777 }
778
779 function product_data_panels() {
780 global $post, $thepostid, $product_object;
781
782 if ( $product_object instanceof WC_Product ) {
783 $product_id = $product_object->get_id();
784 } elseif ( is_numeric( $thepostid ) ) {
785 $product_id = $thepostid;
786 } elseif ( $post instanceof WP_Post ) {
787 $product_id = $post->ID;
788 } else {
789 $product_id = 0;
790 }
791
792 if ( ! $product_id ) {
793 ?>
794 <div id='woocr_settings' class='panel woocommerce_options_panel woocr_table'>
795 <p style="padding: 0 12px; color: #c9356e"><?php esc_html_e( 'Product wasn\'t returned.', 'wpc-custom-related-products' ); ?></p>
796 </div>
797 <?php
798 return;
799 }
800
801 $orderby = get_post_meta( $product_id, 'woocr_orderby', true ) ?: 'none';
802 $order = get_post_meta( $product_id, 'woocr_order', true ) ?: 'asc';
803 ?>
804 <div id='woocr_settings' class='panel woocommerce_options_panel woocr_table'>
805 <table>
806 <tr>
807 <th><?php esc_html_e( 'Search', 'wpc-custom-related-products' ); ?> (<a
808 href="<?php echo esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=settings#search' ) ); ?>"
809 target="_blank"><?php esc_html_e( 'settings', 'wpc-custom-related-products' ); ?></a>)
810 </th>
811 <td>
812 <div class="w100">
813 <span class="loading"
814 id="woocr_loading"><?php esc_html_e( 'searching...', 'wpc-custom-related-products' ); ?></span>
815 <label for="woocr_keyword"></label><input type="search" id="woocr_keyword"
816 placeholder="<?php esc_attr_e( 'Type any keyword to search', 'wpc-custom-related-products' ); ?>"/>
817 <div id="woocr_results" class="woocr_results"></div>
818 </div>
819 </td>
820 </tr>
821 <tr class="woocr_tr_space">
822 <th><?php esc_html_e( 'Selected', 'wpc-custom-related-products' ); ?></th>
823 <td>
824 <div class="w100">
825 <div id="woocr_selected" class="woocr_selected">
826 <ul>
827 <?php
828 echo '<li class="woocr_default">' . sprintf( /* translators: default */ esc_html__( '* If you don\'t choose any products, it can show products from %s.', 'wpc-custom-related-products' ), '<a
829 href="' . esc_url( admin_url( 'admin.php?page=wpclever-woocr&tab=settings' ) ) . '" target="_blank">' . esc_html__( 'Default Products', 'wpc-custom-related-products' ) . '</a>' ) . '</li>';
830
831 if ( ( $items = get_post_meta( $product_id, 'woocr_ids', true ) ) && is_array( $items ) && count( $items ) > 0 ) {
832 foreach ( array_unique( $items ) as $item ) {
833 $product = wc_get_product( $item );
834
835 if ( ! $product ) {
836 continue;
837 }
838
839 self::product_data_li( $product );
840 }
841 }
842 ?>
843 </ul>
844 </div>
845 </div>
846 </td>
847 </tr>
848 <tr class="woocr_tr_space">
849 <th><?php esc_html_e( 'Order by', 'wpc-custom-related-products' ); ?></th>
850 <td>
851 <label> <select name="woocr_orderby">
852 <option value="none" <?php selected( $orderby, 'none' ); ?>><?php esc_html_e( 'Selected', 'wpc-custom-related-products' ); ?></option>
853 <option value="title" <?php selected( $orderby, 'title' ); ?>><?php esc_html_e( 'Title', 'wpc-custom-related-products' ); ?></option>
854 <option value="id" <?php selected( $orderby, 'id' ); ?>><?php esc_html_e( 'ID', 'wpc-custom-related-products' ); ?></option>
855 <option value="date" <?php selected( $orderby, 'date' ); ?>><?php esc_html_e( 'Date', 'wpc-custom-related-products' ); ?></option>
856 <option value="modified" <?php selected( $orderby, 'modified' ); ?>><?php esc_html_e( 'Modified', 'wpc-custom-related-products' ); ?></option>
857 <option value="price" <?php selected( $orderby, 'price' ); ?>><?php esc_html_e( 'Price', 'wpc-custom-related-products' ); ?></option>
858 </select> </label>
859 </td>
860 </tr>
861 <tr class="woocr_tr_space">
862 <th><?php esc_html_e( 'Order', 'wpc-custom-related-products' ); ?></th>
863 <td>
864 <label> <select name="woocr_order">
865 <option value="asc" <?php selected( $order, 'asc' ); ?>><?php esc_html_e( 'ASC', 'wpc-custom-related-products' ); ?></option>
866 <option value="desc" <?php selected( $order, 'desc' ); ?>><?php esc_html_e( 'DESC', 'wpc-custom-related-products' ); ?></option>
867 </select> </label>
868 </td>
869 </tr>
870 </table>
871 </div>
872 <?php
873 }
874
875 function process_product_meta( $post_id ) {
876 if ( ! isset( $_POST['woocommerce_meta_nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['woocommerce_meta_nonce'] ) ), 'woocommerce_save_data' ) || ! current_user_can( 'edit_post', $post_id ) ) {
877 return;
878 }
879
880 if ( isset( $_POST['woocr_ids'] ) ) {
881 update_post_meta( $post_id, 'woocr_ids', array_map( 'sanitize_key', wp_unslash( $_POST['woocr_ids'] ?? [] ) ) );
882 } else {
883 delete_post_meta( $post_id, 'woocr_ids' );
884 }
885
886 if ( isset( $_POST['woocr_orderby'] ) ) {
887 update_post_meta( $post_id, 'woocr_orderby', sanitize_key( wp_unslash( $_POST['woocr_orderby'] ?? '' ) ) );
888 }
889
890 if ( isset( $_POST['woocr_order'] ) ) {
891 update_post_meta( $post_id, 'woocr_order', sanitize_key( wp_unslash( $_POST['woocr_order'] ?? '' ) ) );
892 }
893 }
894
895 function related_products_args( $args ) {
896 global $product;
897
898 if ( $product && is_a( $product, 'WC_Product' ) && ( $product_id = $product->get_id() ) ) {
899 $args['orderby'] = get_post_meta( $product_id, 'woocr_orderby', true ) ?: 'none';
900 $args['order'] = get_post_meta( $product_id, 'woocr_order', true ) ?: 'asc';
901 }
902
903 return $args;
904 }
905
906 function related_products( $related, $product_id ) {
907 if ( ( $_ids = get_post_meta( $product_id, 'woocr_ids', true ) ) && is_array( $_ids ) && ! empty( $_ids ) ) {
908 $ids = array_unique( $_ids );
909 } else {
910 $_ids = $related;
911 $default = self::get_setting( 'default', 'related' );
912 $default_limit = absint( self::get_setting( 'default_limit', 5 ) );
913
914 switch ( $default ) {
915 case 'none':
916 $_ids = [];
917 break;
918 case 'up_sells':
919 $_product = wc_get_product( $product_id );
920 $_ids = $_product->get_upsell_ids();
921 break;
922 case 'cross_sells':
923 $_product = wc_get_product( $product_id );
924 $_ids = $_product->get_cross_sell_ids();
925 break;
926 case 'up_cross_sells':
927 $_product = wc_get_product( $product_id );
928 $upsell = $_product->get_upsell_ids();
929 $cross_sell = $_product->get_cross_sell_ids();
930 $_ids = array_unique( array_merge( $upsell, $cross_sell ) );
931 break;
932 case 'smart_related':
933 if ( ! empty( self::$rules ) ) {
934 foreach ( self::$rules as $rule ) {
935 if ( self::check_apply( $product_id, $rule ) ) {
936 $_ids = self::get_products( $rule, $product_id );
937 break;
938 }
939 }
940 }
941 }
942
943 $_ids = apply_filters( 'woocr_related_products_before_limit', $_ids, $product_id );
944
945 if ( $default_limit && ! empty( $_ids ) ) {
946 $ids = array_slice( $_ids, 0, $default_limit );
947 } else {
948 $ids = $_ids;
949 }
950 }
951
952 return apply_filters( 'woocr_related_products', $ids, $product_id );
953 }
954
955 function exclude_unpurchasable( $ids ) {
956 if ( is_array( $ids ) && ! empty( $ids ) ) {
957 foreach ( $ids as $k => $id ) {
958 $product = wc_get_product( $id );
959
960 if ( ! $product || ! $product->is_purchasable() || ! $product->is_in_stock() ) {
961 unset( $ids[ $k ] );
962 }
963 }
964 }
965
966 return $ids;
967 }
968
969 public static function check_apply( $product, $rule ) {
970 if ( is_a( $product, 'WC_Product' ) ) {
971 $product_id = $product->get_id();
972 } elseif ( is_int( $product ) ) {
973 $product_id = $product;
974 } else {
975 $product_id = 0;
976 }
977
978 if ( empty( $rule['apply'] ) ) {
979 return false;
980 }
981
982 switch ( $rule['apply'] ) {
983 case 'all':
984 return true;
985 case 'products':
986 if ( ! empty( $rule['apply_products'] ) && is_array( $rule['apply_products'] ) ) {
987 if ( in_array( $product_id, $rule['apply_products'] ) ) {
988 return true;
989 }
990 }
991
992 return false;
993 case 'combination':
994 if ( ! empty( $rule['apply_combination'] ) && is_array( $rule['apply_combination'] ) ) {
995 $match_all = true;
996
997 foreach ( $rule['apply_combination'] as $combination ) {
998 $match = true;
999
1000 if ( ! empty( $combination['apply'] ) && ! empty( $combination['compare'] ) && ! empty( $combination['terms'] ) && is_array( $combination['terms'] ) ) {
1001 if ( ( $combination['compare'] === 'is' ) && ! has_term( $combination['terms'], $combination['apply'], $product_id ) ) {
1002 $match = false;
1003 }
1004
1005 if ( ( $combination['compare'] === 'is_not' ) && has_term( $combination['terms'], $combination['apply'], $product_id ) ) {
1006 $match = false;
1007 }
1008 }
1009
1010 $match_all &= $match;
1011 }
1012
1013 return $match_all;
1014 }
1015
1016 return false;
1017 default:
1018 if ( ! empty( $rule['apply_terms'] ) && is_array( $rule['apply_terms'] ) ) {
1019 if ( has_term( $rule['apply_terms'], $rule['apply'], $product_id ) ) {
1020 return true;
1021 }
1022 }
1023
1024 return false;
1025 }
1026 }
1027
1028 public static function generate_key() {
1029 $key = '';
1030 $key_str = apply_filters( 'woocr_key_characters', 'abcdefghijklmnopqrstuvwxyz0123456789' );
1031 $key_str_len = strlen( $key_str );
1032
1033 for ( $i = 0; $i < apply_filters( 'woocr_key_length', 4 ); $i ++ ) {
1034 $key .= $key_str[ random_int( 0, $key_str_len - 1 ) ];
1035 }
1036
1037 if ( is_numeric( $key ) ) {
1038 $key = self::generate_key();
1039 }
1040
1041 return apply_filters( 'woocr_generate_key', $key );
1042 }
1043
1044 public static function sanitize_array( $arr ) {
1045 foreach ( (array) $arr as $k => $v ) {
1046 if ( is_array( $v ) ) {
1047 $arr[ $k ] = self::sanitize_array( $v );
1048 } else {
1049 $arr[ $k ] = sanitize_post_field( 'post_content', $v, 0, 'db' );
1050 }
1051 }
1052
1053 return $arr;
1054 }
1055
1056 function get_products( $rule, $product = null ) {
1057 if ( ! empty( $rule['get'] ) ) {
1058 if ( is_a( $product, 'WC_Product' ) ) {
1059 $product_id = $product->get_id();
1060 } elseif ( is_int( $product ) ) {
1061 $product_id = $product;
1062 } else {
1063 $product_id = 0;
1064 }
1065
1066 $limit = absint( $rule['get_limit'] ?? 3 );
1067 $orderby = $rule['get_orderby'] ?? 'default';
1068 $order = $rule['get_order'] ?? 'default';
1069
1070 switch ( $rule['get'] ) {
1071 case 'all':
1072 return wc_get_products( [
1073 'status' => 'publish',
1074 'limit' => $limit,
1075 'orderby' => $orderby,
1076 'order' => $order,
1077 'exclude' => [ $product_id ],
1078 'return' => 'ids',
1079 ] );
1080 case 'products':
1081 if ( ! empty( $rule['get_products'] ) && is_array( $rule['get_products'] ) ) {
1082 return array_diff( $rule['get_products'], [ $product_id ] );
1083 } else {
1084 return [];
1085 }
1086 case 'combination':
1087 if ( ! empty( $rule['get_combination'] ) && is_array( $rule['get_combination'] ) ) {
1088 $tax_query = [];
1089 $terms_arr = [];
1090
1091 foreach ( $rule['get_combination'] as $combination ) {
1092 // term
1093 if ( ! empty( $combination['apply'] ) && ! empty( $combination['compare'] ) && ! empty( $combination['terms'] ) && is_array( $combination['terms'] ) ) {
1094 $tax_query[] = [
1095 'taxonomy' => $combination['apply'],
1096 'field' => 'slug',
1097 'terms' => $combination['terms'],
1098 'operator' => $combination['compare'] === 'is' ? 'IN' : 'NOT IN'
1099 ];
1100 }
1101
1102 // has same taxonomy
1103 if ( ! empty( $combination['apply'] ) && $combination['apply'] === 'same' && ! empty( $combination['same'] ) ) {
1104 $taxonomy = $combination['same'];
1105
1106 if ( empty( $terms_arr[ $taxonomy ] ) ) {
1107 $terms = get_the_terms( $product_id, $taxonomy );
1108
1109 if ( ! empty( $terms ) && is_array( $terms ) ) {
1110 foreach ( $terms as $term ) {
1111 $terms_arr[ $taxonomy ][] = $term->slug;
1112 }
1113 }
1114 }
1115
1116 if ( ! empty( $terms_arr[ $taxonomy ] ) ) {
1117 $tax_query[] = [
1118 'taxonomy' => $taxonomy,
1119 'field' => 'slug',
1120 'terms' => $terms_arr[ $taxonomy ],
1121 'operator' => 'IN'
1122 ];
1123 }
1124 }
1125 }
1126
1127 $args = [
1128 'post_type' => 'product',
1129 'post_status' => 'publish',
1130 'posts_per_page' => $limit,
1131 'orderby' => $orderby,
1132 'order' => $order,
1133 'tax_query' => $tax_query,
1134 'post__not_in' => [ $product_id ],
1135 'fields' => 'ids'
1136 ];
1137
1138 $ids = new WP_Query( $args );
1139
1140 return $ids->posts;
1141 } else {
1142 return [];
1143 }
1144 default:
1145 if ( ! empty( $rule['get_terms'] ) && is_array( $rule['get_terms'] ) ) {
1146 $args = [
1147 'post_type' => 'product',
1148 'post_status' => 'publish',
1149 'posts_per_page' => $limit,
1150 'orderby' => $orderby,
1151 'order' => $order,
1152 'tax_query' => [
1153 [
1154 'taxonomy' => $rule['get'],
1155 'field' => 'slug',
1156 'terms' => $rule['get_terms'],
1157 ],
1158 ],
1159 'post__not_in' => [ $product_id ],
1160 'fields' => 'ids'
1161 ];
1162
1163 $ids = new WP_Query( $args );
1164
1165 return $ids->posts;
1166 } else {
1167 return [];
1168 }
1169 }
1170 }
1171
1172 return [];
1173 }
1174
1175 function search_sku( $query ) {
1176 if ( $query->is_search && isset( $query->query['is_woocr'] ) ) {
1177 global $wpdb;
1178
1179 $sku = $query->query['s'];
1180 $ids = $wpdb->get_col( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND meta_value = %s;", $sku ) );
1181
1182 if ( ! $ids ) {
1183 return;
1184 }
1185
1186 $posts = [];
1187
1188 foreach ( $ids as $id ) {
1189 $post = get_post( $id );
1190
1191 if ( $post->post_type === 'product_variation' ) {
1192 $posts[] = $post->post_parent;
1193 } else {
1194 $posts[] = $post->ID;
1195 }
1196 }
1197
1198 unset( $query->query['s'], $query->query_vars['s'] );
1199 $query->set( 'post__in', $posts );
1200 }
1201 }
1202
1203 function search_exact( $query ) {
1204 if ( $query->is_search && isset( $query->query['is_woocr'] ) ) {
1205 $query->set( 'exact', true );
1206 }
1207 }
1208
1209 function search_sentence( $query ) {
1210 if ( $query->is_search && isset( $query->query['is_woocr'] ) ) {
1211 $query->set( 'sentence', true );
1212 }
1213 }
1214 }
1215
1216 return WPCleverWoocr::instance();
1217 }
1218
1219 return null;
1220 }
1221 }
1222
1223 if ( ! function_exists( 'woocr_notice_wc' ) ) {
1224 function woocr_notice_wc() {
1225 ?>
1226 <div class="error">
1227 <p><strong>WPC Custom Related Products</strong> requires WooCommerce version 3.0 or greater.</p>
1228 </div>
1229 <?php
1230 }
1231 }
1232