PluginProbe
Shop as Client for WooCommerce – Manual, Phone & Email Orders / trunk
Shop as Client for WooCommerce – Manual, Phone & Email Orders vtrunk
8.2 8.1 8.0.2 8.0.1 trunk 0.1 0.2 0.3 0.4 0.5 0.6 1.0 1.0.1 1.0.2 1.0.3 1.3.0 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.6.0 1.6.2 1.6.3 1.6.4 All 71 releases
shop-as-client / shop-as-client.php

shop-as-client.php in Shop as Client for WooCommerce – Manual, Phone & Email Orders trunk, at shop-as-client.php

844 lines 33.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Shop as Client for WooCommerce - Manual, Phone & Email Orders
4 * Plugin URI: https://nakedcatplugins.com/product/shop-as-client-for-woocommerce-pro-add-on/
5 * Description: Create manual, phone, POS, or email orders in WooCommerce. Shop admins and staff can place customer orders directly from the frontend checkout.
6 * Version: 8.2
7 * Author: Naked Cat Plugins (by Webdados)
8 * Author URI: https://nakedcatplugins.com/
9 * Text Domain: shop-as-client
10 * Domain Path: /languages
11 * Requires at least: 6.4
12 * Tested up to: 7.1
13 * Requires PHP: 7.4
14 * WC requires at least: 9.0
15 * WC tested up to: 11.1
16 * Requires Plugins: woocommerce
17 * License: GPLv3
18 **/
19
20 /* WooCommerce CRUD ready */
21
22 if ( ! defined( 'ABSPATH' ) ) {
23 exit; // Exit if accessed directly
24 }
25
26 if ( ! defined( 'SHOPASCLIENT_REQUIRED_WC' ) ) {
27 define( 'SHOPASCLIENT_REQUIRED_WC', '9.0' );
28 }
29 define( 'SHOPASCLIENT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
30 define( 'SHOPASCLIENT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
31 define( 'SHOPASCLIENT_PRO_OUT_LINK', 'https://nakedcatplugins.com/product/shop-as-client-for-woocommerce-pro-add-on/?utm_source=' . rawurlencode( esc_url( home_url( '/' ) ) ) . '&utm_medium=link&utm_campaign=shop_as_client_plugin' );
32
33 // If Pro is also active, we need at least this version of it
34 define( 'SHOPASCLIENT_FREE_REQUIRED_PRO', '8.0' );
35
36 /**
37 * Check if WooCommerce is active
38 * plugins_loaded is too soon for code in functions.php to run - This needs to be addressed, but it probably won't work because on the initialization of blocks
39 */
40 add_action(
41 'plugins_loaded',
42 function () {
43 if ( class_exists( 'WooCommerce' ) && defined( 'WC_VERSION' ) && version_compare( WC_VERSION, SHOPASCLIENT_REQUIRED_WC, '>=' ) ) {
44
45 /**
46 * Version
47 */
48 if ( ! function_exists( 'get_plugin_data' ) ) {
49 include ABSPATH . '/wp-admin/includes/plugin.php';
50 }
51 $temp_plugin_data = get_plugin_data( __FILE__, false, false );
52 define( 'SHOPASCLIENT_VERSION', $temp_plugin_data['Version'] );
53
54 /**
55 * Languages and scripts
56 * Do not change this function name as is needed for the PRO add-on to work properly
57 */
58 function shop_as_client_init() {
59 // Load scripts
60 add_action( 'wp_enqueue_scripts', 'shop_as_client_enqueue_classic_scripts' );
61 }
62 add_action( 'init', 'shop_as_client_init' ); // Used to be plugins_loaded, but init makes more sense
63
64 /**
65 * Enqueue scripts - Classic checkout only - Blocks checkout in includes/class-shop-as-client-checkout-blocks.php
66 */
67 function shop_as_client_enqueue_classic_scripts() {
68 if (
69 function_exists( 'is_checkout' )
70 &&
71 is_checkout()
72 &&
73 ( ! has_block( 'woocommerce/checkout' ) ) // Not on the Blocks checkout
74 ) {
75 wp_enqueue_script( 'shop-as-client', plugins_url( 'js/functions.js', __FILE__ ), array( 'jquery' ), '1.3.0', true );
76 wp_localize_script(
77 'shop-as-client',
78 'shop_as_client',
79 array(
80 'txt_pro' => sprintf(
81 '<p><a href="%s" target="_blank">%s</a></p>',
82 esc_url( SHOPASCLIENT_PRO_OUT_LINK ),
83 __( 'Do you want to load the customer details automatically?<br/>Get the PRO add-on!', 'shop-as-client' )
84 ),
85 )
86 );
87 }
88 }
89
90 /**
91 * Fake settings link
92 *
93 * @param array $links The current links.
94 */
95 function shop_as_client_add_settings_link( $links ) {
96 $action_links = array(
97 sprintf(
98 '<a href="admin.php?page=wc-settings&amp;tab=account&amp;section=shop_as_client">%s</a>',
99 __( 'Settings', 'shop-as-client' )
100 ),
101 );
102 return array_merge( $links, $action_links );
103 }
104 add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'shop_as_client_add_settings_link' );
105
106 /**
107 * Register the section under Accounts & Privacy
108 */
109 add_filter(
110 'woocommerce_get_sections_account',
111 function ( $sections ) {
112 $sections['shop_as_client'] = __( 'Shop as Client', 'shop-as-client' );
113 return $sections;
114 }
115 );
116
117 /**
118 * Settings - Free plugin only (hidden when PRO is active)
119 */
120 function shop_as_client_get_settings() {
121 // A screen-reader-only note (WCAG 3.2.5: opening a new tab
122 // without warning is an unannounced context change).
123 $description = sprintf(
124 /* translators: %1$s: link open, %2$s: link close */
125 __( 'Available on the %1$sPRO Add-on%2$s', 'shop-as-client' ),
126 '<a href="' . esc_url( SHOPASCLIENT_PRO_OUT_LINK ) . '" target="_blank">',
127 '<span class="screen-reader-text">' . esc_html__( ' (opens in a new tab)', 'shop-as-client' ) . '</span></a>'
128 );
129 return array(
130 array(
131 'title' => __( 'Shop as Client', 'shop-as-client' ) . sprintf(
132 ' (Free %s)',
133 SHOPASCLIENT_VERSION
134 ),
135 'type' => 'title',
136 'id' => 'shop_as_client_options',
137 ),
138 // Disabled fields here (shop as client default, create user field default, search on orders, update customer, clear checkout fields, Handler, cart price change)
139 array(
140 'title' => __( 'Shop as client field default', 'shop-as-client' ),
141 'id' => 'shop_as_client_pro_shop_as_client_default',
142 'type' => 'select',
143 'options' => array(
144 'yes' => __( 'Yes', 'shop-as-client' ),
145 'no' => __( 'No', 'shop-as-client' ),
146 ),
147 'default' => 'yes',
148 'desc' => $description,
149 'custom_attributes' => array(
150 'disabled' => 'disabled',
151 ),
152 ),
153 array(
154 'title' => __( 'Create user field default', 'shop-as-client' ),
155 'id' => 'shop_as_client_pro_create_user_default',
156 'type' => 'select',
157 'options' => array(
158 'yes' => __( 'Yes', 'shop-as-client' ),
159 'no' => __( 'No', 'shop-as-client' ),
160 ),
161 'default' => 'yes',
162 'desc' => $description,
163 'custom_attributes' => array(
164 'disabled' => 'disabled',
165 ),
166 ),
167 array(
168 'title' => __( 'Search on orders', 'shop-as-client' ),
169 'desc' => __( 'By default, the search is only performed on registered users by their registration and billing email, but if you enable this option it will also be done on orders (if not found as user), so you can get data from guest customers.', 'shop-as-client' ) . '<br/>' . $description,
170 'id' => 'shop_as_client_pro_search_orders',
171 'type' => 'select',
172 'options' => array(
173 'yes' => __( 'Yes', 'shop-as-client' ),
174 'no' => __( 'No', 'shop-as-client' ),
175 ),
176 'default' => 'yes',
177 'custom_attributes' => array(
178 'disabled' => 'disabled',
179 ),
180 ),
181 array(
182 'title' => __( 'Update customer', 'shop-as-client' ),
183 'desc' => __( 'Update the customer details on his profile', 'shop-as-client' ) . '<br/>' . $description,
184 'id' => 'shop_as_client_pro_update_customer',
185 'type' => 'select',
186 'options' => array(
187 'yes' => __( 'Yes', 'shop-as-client' ),
188 'no' => __( 'No', 'shop-as-client' ),
189 ),
190 'default' => 'yes',
191 'custom_attributes' => array(
192 'disabled' => 'disabled',
193 ),
194 ),
195 array(
196 'title' => __( 'Clear checkout fields', 'shop-as-client' ),
197 'desc' => (
198 __( 'Default all checkout fields to blank for Administrators and Shop Managers', 'shop-as-client' )
199 .
200 '<br/>'
201 .
202 __( 'Only on the classic checkout', 'shop-as-client' ) . '<br/>' . $description
203 ),
204 'id' => 'shop_as_client_pro_empty_checkout',
205 'type' => 'select',
206 'options' => array(
207 'yes' => __( 'Yes', 'shop-as-client' ),
208 'no' => __( 'No', 'shop-as-client' ),
209 ),
210 'default' => 'yes',
211 'custom_attributes' => array(
212 'disabled' => 'disabled',
213 ),
214 ),
215 array(
216 'title' => __( 'Handler on orders list', 'shop-as-client' ),
217 'desc' => __( 'Add a column with the order handler and allow filtering by handler on the admin orders list', 'shop-as-client' ) . '<br/>' . $description,
218 'id' => 'shop_as_client_pro_handler_order_list',
219 'type' => 'select',
220 'options' => array(
221 'yes' => __( 'Yes', 'shop-as-client' ),
222 'no' => __( 'No', 'shop-as-client' ),
223 ),
224 'default' => 'yes',
225 'custom_attributes' => array(
226 'disabled' => 'disabled',
227 ),
228 ),
229 array(
230 'title' => __( 'Allow changing cart price', 'shop-as-client' ),
231 'desc' => (
232 __( 'Allow Administrators and Shop Managers to edit product prices in the cart while shopping as customer', 'shop-as-client' )
233 . ' (' . __( 'Experimental', 'shop-as-client' ) . ')'
234 . '<br/>' . $description
235 ),
236 'id' => 'shop_as_client_pro_cart_price_change',
237 'type' => 'select',
238 'options' => array(
239 'yes' => __( 'Yes', 'shop-as-client' ),
240 'no' => __( 'No', 'shop-as-client' ),
241 ),
242 'default' => 'no',
243 'custom_attributes' => array(
244 'disabled' => 'disabled',
245 ),
246 ),
247 array(
248 'type' => 'sectionend',
249 'id' => 'shop_as_client_options',
250 ),
251 );
252 }
253 add_filter(
254 'woocommerce_get_settings_account',
255 function ( $settings, $current_section ) {
256 if ( 'shop_as_client' === $current_section && ! defined( 'SHOPASCLIENT_PRO_PLUGIN_FILE' ) ) {
257 return shop_as_client_get_settings();
258 }
259 return $settings;
260 },
261 10,
262 2
263 );
264
265 /**
266 * Can checkout with shop as client? - Should be used for both classic and blocks checkout
267 *
268 * @param integer $user_id The user ID to check or null to check the current logged-in user.
269 */
270 function shop_as_client_can_checkout( $user_id = null ) {
271 if ( is_null( $user_id ) ) {
272 // The shop_as_client_allow_checkout filter can be used to allow other user roles to use this functionality - Use carefully and wisely
273 return current_user_can( 'manage_options' ) || current_user_can( 'manage_woocommerce' ) || apply_filters( 'shop_as_client_allow_checkout', false, null );
274 } elseif ( intval( $user_id ) > 0 ) {
275 return user_can( intval( $user_id ), 'manage_options' ) || user_can( intval( $user_id ), 'manage_woocommerce' ) || apply_filters( 'shop_as_client_allow_checkout', false, $user_id );
276 }
277 return false;
278 }
279
280 if ( function_exists( 'woocommerce_store_api_register_update_callback' ) ) {
281
282 /* Blocks */
283 add_action(
284 'woocommerce_blocks_loaded',
285 function () {
286 require_once __DIR__ . '/includes/class-shop-as-client-checkout-blocks.php';
287
288 add_action(
289 'woocommerce_blocks_checkout_block_registration',
290 function ( $integration_registry ) {
291 $integration_registry->register( new \ShopAsClient_Checkout_Blocks() );
292 }
293 );
294 }
295 );
296
297 /* Blocks - Extend Store endpoint */
298 add_action(
299 'woocommerce_blocks_loaded',
300 function () {
301 require_once __DIR__ . '/includes/class-shop-as-client-extend-store-endpoint.php';
302
303 ( new ShopAsClient_Extend_Store_Endpoint() )->initialize();
304 }
305 );
306
307 }
308
309 /**
310 * Bail out if the PRO add-on is active and the version is too old.
311 * The checkout is still not going to work properly, but the Pro Add-On will be able to update itself.
312 */
313 if ( function_exists( '\Webdados\ShopAsClientPro\init' ) ) {
314 // SHOPASCLIENT_PRO_PLUGIN_FILE is only defined at plugins_loaded priority 9.
315 // Read the version directly from the file header instead.
316 $pro_path = WP_PLUGIN_DIR . '/shop-as-client-pro/shop-as-client-pro.php';
317 if ( file_exists( $pro_path ) ) {
318 if ( ! function_exists( 'get_plugin_data' ) ) {
319 include ABSPATH . '/wp-admin/includes/plugin.php';
320 }
321 $pro_data = get_plugin_data( $pro_path, false, false );
322 $pro_version = $pro_data['Version'] ?? '';
323 if ( $pro_version && version_compare( $pro_version, SHOPASCLIENT_FREE_REQUIRED_PRO, '<' ) ) {
324 add_action(
325 'admin_notices',
326 function () use ( $pro_version ) {
327 ?>
328 <div class="notice notice-error">
329 <p>
330 <?php
331 echo wp_kses_post(
332 sprintf(
333 /* translators: %1$s: plugin name, %2$s: required version, %3$s: current version */
334 __( '%1$s requires at least version %2$s of the PRO add-on. You have version %3$s. Please update or disable the PRO add-on.', 'shop-as-client' ),
335 '<strong>Shop as Client</strong>',
336 SHOPASCLIENT_FREE_REQUIRED_PRO,
337 $pro_version
338 )
339 );
340 ?>
341 </p>
342 </div>
343 <?php
344 }
345 );
346 // Get out of here — can't run with an old PRO version.
347 return;
348 }
349 }
350 }
351
352 /**
353 * Our field - Classic checkout only - Blocks checkout in includes/class-shop-as-client-checkout-blocks.php
354 *
355 * @param array $fields The fields.
356 */
357 function shop_as_client_init_woocommerce_checkout_fields( $fields ) {
358 if ( shop_as_client_can_checkout() ) {
359 $priority = apply_filters( 'shop_as_client_field_priority', 990 );
360 // Shop as client?
361 $fields['billing']['billing_shop_as_client'] = array(
362 'label' => __( 'Shop as client', 'shop-as-client' ),
363 'required' => true,
364 'class' => apply_filters( 'shop_as_client_field_class', array( 'form-row-wide' ) ),
365 'input_class' => apply_filters( 'shop_as_client_field_input_class', array() ),
366 'label_class' => apply_filters( 'shop_as_client_field_label_class', array() ),
367 'clear' => true,
368 'priority' => $priority,
369 'type' => 'select',
370 'options' => array(
371 'yes' => __( 'Yes', 'shop-as-client' ),
372 'no' => __( 'No', 'shop-as-client' ),
373 ),
374 'default' => apply_filters( 'shop_as_client_default_shop_as_client', 'yes' ),
375 );
376 $priority++;
377 // Create user if it doesn't exist?
378 $fields['billing']['billing_shop_as_client_create_user'] = array(
379 'label' => __( 'Create user (if not found by email)?', 'shop-as-client' ),
380 'required' => true,
381 'class' => apply_filters( 'shop_as_client_create_user_field_class', array( 'form-row-wide' ) ),
382 'input_class' => apply_filters( 'shop_as_client_create_user_field_input_class', array() ),
383 'label_class' => apply_filters( 'shop_as_client_create_user_field_label_class', array() ),
384 'clear' => true,
385 'priority' => $priority,
386 'type' => 'select',
387 'options' => array(
388 'yes' => __( 'Yes', 'shop-as-client' ),
389 'no' => __( 'No (leave as guest)', 'shop-as-client' ),
390 ),
391 'default' => apply_filters( 'shop_as_client_default_create_user', 'no' ),
392 );
393 }
394 return $fields;
395 }
396 add_filter( 'woocommerce_checkout_fields', 'shop_as_client_init_woocommerce_checkout_fields', PHP_INT_MAX );
397
398 /**
399 * Force our field defaults - Shop as Client
400 */
401 function shop_as_client_default_checkout_billing_shop_as_client() {
402 return apply_filters( 'shop_as_client_default_shop_as_client', 'yes' );
403 }
404 add_filter( 'default_checkout_billing_shop_as_client', 'shop_as_client_default_checkout_billing_shop_as_client', 10, 0 );
405
406 /**
407 * Force our field defaults - Create user
408 */
409 function shop_as_client_default_checkout_billing_shop_as_client_create_user() {
410 return apply_filters( 'shop_as_client_default_create_user', 'no' );
411 }
412 add_filter( 'default_checkout_billing_shop_as_client_create_user', 'shop_as_client_default_checkout_billing_shop_as_client_create_user', 10, 0 );
413
414 /**
415 * Get order "shop as client"
416 *
417 * @param WC_Order $order The order.
418 */
419 function shop_as_client_get_order_status( $order ) {
420 return 'yes' === $order->get_meta( '_billing_shop_as_client' );
421 }
422
423 /**
424 * Return yes to woocommerce_registration_generate_password
425 *
426 * @param string $value If the password should be generated.
427 */
428 function shop_as_client_woocommerce_registration_generate_password( $value ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
429 return 'yes';
430 }
431
432 /**
433 * Set order user - Inspiration: https://gist.github.com/twoelevenjay/80294a635969a54e4693
434 * Classic checkout only - Blocks alternative missing - https://github.com/woocommerce/woocommerce/issues/44530
435 *
436 * @param integer $user_id The original order user ID.
437 * @throws Exception Error message.
438 */
439 function shop_as_client_woocommerce_checkout_customer_id( $user_id ) {
440 // WooCommerce took care of nonces
441 // phpcs:disable WordPress.Security.NonceVerification.Missing
442 if ( shop_as_client_can_checkout() ) {
443 $billing_shop_as_client = isset( $_POST['billing_shop_as_client'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['billing_shop_as_client'] ) ) ) : '';
444 if ( 'yes' === $billing_shop_as_client ) {
445 $user_id = 0;
446 // Check if an exisiting user already uses this email address.
447 $user_email = isset( $_POST['billing_email'] ) ? sanitize_email( wp_unslash( $_POST['billing_email'] ) ) : '';
448 if ( empty( $user_email ) ) {
449 $user_email = apply_filters( 'shop_as_client_user_email_if_empty', $user_email, $_POST );
450 }
451 // Get user by profile email
452 $user = get_user_by( 'email', $user_email );
453 if ( $user ) {
454 // User found
455 $user_id = $user->ID;
456 // Should we update the user details? - This is by WooCommerce on WC_Checkout process_customer - Working on Blocks too
457 } elseif (
458 ( ! empty( $user_email ) )
459 &&
460 // TRADEOFF: wp_usermeta.meta_value is not indexed, so this is
461 // effectively a full scan of every 'billing_email' row on stores
462 // with a large customer table, and it runs on this plugin's core,
463 // expected-to-be-frequent path (phone/email order, billing email
464 // differs from the matched account's own profile email). Known,
465 // accepted tradeoff for now (see GitHub issue #29 on the PRO
466 // add-on repo, filed there per our disclosure policy) - do not
467 // "fix" the ignored linter warnings below by adding more of this
468 // meta_query pattern elsewhere without addressing the root cause
469 // (e.g. caching the lookup, or using wc_get_orders() instead).
470 ( $users = get_users( // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found, Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
471 // Get user by WooCommerce billing email
472 array(
473 'meta_key' => 'billing_email', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
474 'meta_value' => $user_email, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
475 'meta_compare' => '=',
476 )
477 ) )
478 ) {
479 // User found - We should check for more than one... (There's no real solution for this)
480 $user_id = $users[0]->ID;
481 } else {
482 // Create user or guest?
483 $billing_shop_as_client_create_user = isset( $_POST['billing_shop_as_client_create_user'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['billing_shop_as_client_create_user'] ) ) ) : '';
484 if ( 'yes' === $billing_shop_as_client_create_user ) {
485 $billing_first_name = isset( $_POST['billing_first_name'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['billing_first_name'] ) ) ) : '';
486 $billing_last_name = isset( $_POST['billing_last_name'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['billing_last_name'] ) ) ) : '';
487 $temp_user_id = shop_as_client_create_customer( $user_email, $billing_first_name, $billing_last_name );
488 if ( ! is_wp_error( $temp_user_id ) ) {
489 $user_id = $temp_user_id;
490 } else {
491 $message = sprintf(
492 /* translators: %s: error message */
493 __( 'Shop as Client failed to create user: %s', 'shop-as-client' ),
494 $temp_user_id->get_error_message()
495 );
496 throw new Exception( esc_html( $message ) );
497 }
498 }
499 }
500 }
501 }
502 return $user_id;
503 // phpcs:enable WordPress.Security.NonceVerification.Missing
504 }
505 add_filter( 'woocommerce_checkout_customer_id', 'shop_as_client_woocommerce_checkout_customer_id' );
506
507 /**
508 * Create the user/customer - Should be used for both classic and blocks checkout
509 *
510 * @param string $user_email The user email.
511 * @param string $user_first_name The user first name.
512 * @param string $user_last_name The user last name.
513 */
514 function shop_as_client_create_customer( $user_email, $user_first_name, $user_last_name ) {
515 // Username
516 if ( 'yes' === get_option( 'woocommerce_registration_generate_username' ) ) {
517 $username = '';
518 } else {
519 $username = $user_email;
520 }
521 // Force password generation by WooCommerce (and sending via email), even if the option is not set
522 if ( apply_filters( 'shop_as_client_email_password', true ) ) {
523 add_filter( 'option_woocommerce_registration_generate_password', 'shop_as_client_woocommerce_registration_generate_password' );
524 $password = '';
525 } else {
526 $password = wp_generate_password();
527 }
528 $user_id = wc_create_new_customer( $user_email, $username, $password );
529 if ( apply_filters( 'shop_as_client_email_password', true ) ) {
530 remove_filter( 'option_woocommerce_registration_generate_password', 'shop_as_client_woocommerce_registration_generate_password' );
531 }
532 if ( ! is_wp_error( $user_id ) ) {
533 wp_update_user(
534 array(
535 'ID' => $user_id,
536 'first_name' => $user_first_name,
537 'last_name' => $user_last_name,
538 'display_name' => trim( $user_first_name . ' ' . $user_last_name ),
539 // phpcs:ignore Squiz.PHP.CommentedOutCode.Found
540 // 'role' => 'customer',
541 )
542 );
543 } else {
544 $message = 'Shop as Client failed to create user: ' . $user_id->get_error_message();
545 // We should notify the admin user somehow - WooCommerce already does that
546 }
547 return $user_id;
548 }
549
550 /**
551 * Prevent logged in user to be updated
552 * Not running on the blocks checkout but it seems not to be necessary as only the target user is being updated and not the logged-in one
553 */
554 function shop_as_client_woocommerce_checkout_process() {
555 if ( shop_as_client_can_checkout() ) {
556 // WooCommerce took care of nonces
557 // phpcs:ignore WordPress.Security.NonceVerification.Missing
558 $billing_shop_as_client = isset( $_POST['billing_shop_as_client'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['billing_shop_as_client'] ) ) ) : '';
559 if ( 'yes' === $billing_shop_as_client ) {
560 if ( ! apply_filters( 'shop_as_client_update_customer_data', false ) ) {
561 add_filter( 'woocommerce_checkout_update_customer_data', '__return_false' );
562 }
563 }
564 }
565 }
566 add_action( 'woocommerce_checkout_process', 'shop_as_client_woocommerce_checkout_process' );
567
568 /**
569 * Save logged in user id as order handler - Classic checkout only - Blocks alternative missing
570 *
571 * @param integer $order_id The order ID.
572 * @param array $data The checkout data.
573 */
574 function shop_as_client_woocommerce_checkout_update_order_meta( $order_id, $data ) {
575 // The "correct" way to check for our fields
576 $billing_shop_as_client = isset( $data['billing_shop_as_client'] ) ? trim( $data['billing_shop_as_client'] ) : '';
577 if ( empty( $billing_shop_as_client ) ) {
578 // Because when using Funnelkit our fields are not present on the $data array
579 // WooCommerce took care of nonces
580 // phpcs:ignore WordPress.Security.NonceVerification.Missing
581 $billing_shop_as_client = isset( $_POST['billing_shop_as_client'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['billing_shop_as_client'] ) ) ) : '';
582 }
583 if ( shop_as_client_can_checkout() && 'yes' === $billing_shop_as_client ) {
584 $order = wc_get_order( $order_id );
585 $order->update_meta_data( '_billing_shop_as_client_handler_user_id', get_current_user_id() );
586 $order->update_meta_data( '_billing_shop_as_client_checkout', 'classic' );
587 $order->save();
588 }
589 }
590 add_action( 'woocommerce_checkout_update_order_meta', 'shop_as_client_woocommerce_checkout_update_order_meta', 10, 2 );
591
592 /**
593 * Information on the order edit screen
594 *
595 * @param WC_Order $order The order.
596 */
597 /**
598 * Render the Shop as Client metabox content.
599 *
600 * @param WP_Post|WC_Order $post_or_order Post object (posts mode) or order object (HPOS mode).
601 */
602 function shop_as_client_order_metabox( $post_or_order ) {
603 $order = $post_or_order instanceof \WP_Post ? wc_get_order( $post_or_order->ID ) : $post_or_order;
604 if ( ! $order || ! shop_as_client_get_order_status( $order ) ) {
605 return;
606 }
607 ?>
608 <p>
609 <strong><?php esc_html_e( 'Shop as client', 'shop-as-client' ); ?>:</strong>
610 <br>
611 <?php esc_html_e( 'Yes', 'shop-as-client' ); ?>
612 </p>
613 <?php
614 $user_id = $order->get_meta( '_billing_shop_as_client_handler_user_id' );
615 if ( $user_id ) {
616 ?>
617 <p>
618 <strong><?php esc_html_e( 'Order handled by', 'shop-as-client' ); ?>:</strong>
619 <br>
620 <?php
621 $user = get_user_by( 'ID', $user_id );
622 if ( $user ) {
623 echo wp_kses_post(
624 sprintf(
625 // A screen-reader-only "(opens in a new tab)" note is
626 // required (WCAG 3.2.5) since target="_blank" changes
627 // context without warning otherwise.
628 '<a href="%s" target="_blank">%s<span class="screen-reader-text">%s</span></a>',
629 esc_url( add_query_arg( 'user_id', $user_id, admin_url( 'user-edit.php' ) ) ),
630 sprintf(
631 '%s (%s)',
632 $user->display_name,
633 $user->nickname
634 ),
635 /* translators: hidden text appended to a link that opens in a new browser tab */
636 esc_html__( ' (opens in a new tab)', 'shop-as-client' )
637 )
638 );
639 } else {
640 echo esc_html(
641 sprintf(
642 /* translators: %d: user id */
643 __( 'User %d', 'shop-as-client' ),
644 $user_id
645 )
646 );
647 }
648 do_action( 'shop_as_client_after_order_handler', $order, $user_id );
649 ?>
650 </p>
651 <?php
652 }
653 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
654 $checkout = $order->get_meta( '_billing_shop_as_client_checkout' );
655 ?>
656 <p>
657 <strong><?php esc_html_e( 'Checkout', 'shop-as-client' ); ?>:</strong>
658 <br>
659 <?php echo esc_html( $checkout ); ?>
660 </p>
661 <?php
662 }
663 do_action( 'shop_as_client_after_order_details', $order );
664 }
665 add_action(
666 'add_meta_boxes',
667 function () {
668 foreach ( array( 'shop_order', 'woocommerce_page_wc-orders' ) as $screen ) {
669 add_meta_box(
670 'shop-as-client',
671 __( 'Shop as Client', 'shop-as-client' ),
672 'shop_as_client_order_metabox',
673 $screen,
674 'side',
675 'default'
676 );
677 }
678 }
679 );
680
681 /**
682 * Thank you page warning - https://github.com/woocommerce/woocommerce/pull/38983/
683 *
684 * @param string|null $tag The shortcode tag, used to detect the checkout shortcode. Ignored when $is_checkout_shortcode is set.
685 * @param bool|null $is_checkout_shortcode Pass true when the caller already knows this is the checkout (e.g. the woocommerce/classic-shortcode block).
686 */
687 function shop_as_client_is_our_checkout_order_received( $tag = null, $is_checkout_shortcode = null ) {
688 if ( is_null( $is_checkout_shortcode ) ) {
689 $is_checkout_shortcode = ( $tag === apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) );
690 }
691 if ( $is_checkout_shortcode && is_order_received_page() && shop_as_client_can_checkout() ) {
692 global $wp;
693 if ( isset( $wp->query_vars['order-received'] ) && intval( $wp->query_vars['order-received'] ) > 0 ) {
694 $order = wc_get_order( $wp->query_vars['order-received'] );
695 if ( $order ) {
696 $key = isset( $_GET['key'] ) ? sanitize_text_field( wp_unslash( $_GET['key'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
697 if ( hash_equals( $order->get_order_key(), $key ) ) {
698 $order_customer_id = $order->get_customer_id();
699 if ( $order_customer_id && get_current_user_id() !== $order_customer_id ) {
700 // We're pretty sure there's no access right now, so we need also to make sure it's a shop as client order and the handler is logged in
701 if ( $order->get_meta( '_billing_shop_as_client' ) === 'yes' && intval( $order->get_meta( '_billing_shop_as_client_handler_user_id' ) ) === intval( get_current_user_id() ) ) {
702 // We dealt with the order
703 return $order;
704 }
705 }
706 }
707 }
708 }
709 }
710 return false;
711 }
712 add_filter( 'do_shortcode_tag', 'shop_as_client_checkout_order_received', 10, 2 );
713 add_filter( 'render_block_woocommerce/classic-shortcode', 'shop_as_client_checkout_order_received_block', 10, 2 );
714
715 /**
716 * Markup for the "Order received" notice shown to the order handler
717 * when WooCommerce 7.8.1+ hides the order details from non-owners.
718 */
719 function shop_as_client_checkout_order_received_notice_html() {
720 ob_start();
721 ?>
722 <div class="woocommerce-error">
723 <a href="<?php echo esc_url( SHOPASCLIENT_PRO_OUT_LINK ); ?>" class="button wc-forward" target="_blank">
724 <?php esc_html_e( 'Get the PRO add-on to fix this', 'shop-as-client' ); ?>
725 </a>
726 <?php echo wp_kses_post( __( '<strong>Shop as client</strong><br/>Since WooCommerce 7.8.1 only the order owner/customer is able to see the "Order received" details.', 'shop-as-client' ) ); ?>
727 </div>
728 <?php
729 return ob_get_clean();
730 }
731
732 /**
733 * Filters the [woocommerce_checkout] shortcode output, on the
734 * "Order received" page, to prepend the notice from
735 * shop_as_client_checkout_order_received_notice_html() when the
736 * current user is the order's handler but not its owner.
737 *
738 * @param string $output Shortcode output.
739 * @param string $tag Shortcode tag.
740 * @return string Filtered shortcode output.
741 */
742 function shop_as_client_checkout_order_received( $output, $tag ) {
743 $order = shop_as_client_is_our_checkout_order_received( $tag );
744 if ( $order ) {
745 $output = shop_as_client_checkout_order_received_notice_html() . $output;
746 }
747 return $output;
748 }
749
750 /**
751 * Same notice as shop_as_client_checkout_order_received(), but for
752 * Checkout pages built with the woocommerce/classic-shortcode block,
753 * which renders WC_Shortcode_Checkout::output() directly and never
754 * fires do_shortcode_tag.
755 *
756 * @param string $block_content Rendered block HTML.
757 * @param array $block Parsed block, including attrs.
758 */
759 function shop_as_client_checkout_order_received_block( $block_content, $block ) {
760 if ( isset( $block['attrs']['shortcode'] ) && 'checkout' === $block['attrs']['shortcode'] ) {
761 $order = shop_as_client_is_our_checkout_order_received( null, true );
762 if ( $order ) {
763 $block_content = shop_as_client_checkout_order_received_notice_html() . $block_content;
764 }
765 }
766 return $block_content;
767 }
768
769 /* Simple Order Approval nag */
770 add_action(
771 'admin_init',
772 function () {
773 if (
774 // Not for PRO add-on users
775 ( ! class_exists( 'Shop_As_Client_Pro' ) )
776 &&
777 // Not for users that already have the nag loaded for any reason
778 ( ! defined( 'PTWOO_SIMPLE_ORDER_APPROVAL_NAG' ) )
779 &&
780 // Not for users that already have the advertised plugin
781 ( ! class_exists( '\PTWooPlugins\SWOA\SWOA' ) )
782 &&
783 // Check if dismissed by this user in the last 365 days
784 ( intval( get_user_meta( get_current_user_id(), 'ptwoo_simple_order_approval_nag_dismiss_until', true ) ) < time() )
785 &&
786 // Check if 90-day dismissal is active - Legacy support
787 empty( get_transient( 'ptwoo_simple_order_approval_nag' ) )
788 // Removed by filter
789 &&
790 apply_filters( 'shop_as_client_ptwoo_simple_order_approval_nag', true )
791 ) {
792 define( 'PTWOO_SIMPLE_ORDER_APPROVAL_NAG', true );
793 require_once 'simple_order_approval_nag/simple_order_approval_nag.php';
794 }
795 }
796 );
797 }
798 },
799 6
800 );
801
802 /* HPOS & Blocks Checkout Compatible */
803 add_action(
804 'before_woocommerce_init',
805 function () {
806 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
807 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__, true );
808 }
809 );
810
811 /**
812 * On activation, set a transient so we can redirect to the settings page.
813 */
814 register_activation_hook(
815 __FILE__,
816 function () {
817 set_transient( 'shop_as_client_activation_redirect_' . get_current_user_id(), true, 30 );
818 }
819 );
820
821 /**
822 * Redirect to the settings page after single (non-bulk) activation.
823 */
824 add_action(
825 'admin_init',
826 function () {
827 // Do not redirect during AJAX requests.
828 if ( wp_doing_ajax() ) {
829 return;
830 }
831 $transient_key = 'shop_as_client_activation_redirect_' . get_current_user_id();
832 if ( get_transient( $transient_key ) ) {
833 delete_transient( $transient_key );
834 // Do not redirect on bulk activation.
835 if ( ! isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
836 wp_safe_redirect( admin_url( 'admin.php?page=wc-settings&tab=account&section=shop_as_client' ) );
837 exit;
838 }
839 }
840 }
841 );
842
843 /* If you're reading this you must know what you're doing ;-) Greetings from sunny Portugal! */
844