PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.17
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.17
1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 1.9.13 All 162 releases
woocommerce-pos / includes / wcpos-functions.php

wcpos-functions.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.17, at includes/wcpos-functions.php

632 lines 18.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Global helper functions for WCPOS.
4 *
5 * @author Paul Kilmurray <paul@kilbot.com>
6 *
7 * @see http://wcpos.com
8 * @package WCPOS\WooCommercePOS
9 */
10
11 use WCPOS\WooCommercePOS\Admin\Permalink;
12 use WCPOS\WooCommercePOS\Logger;
13 use WCPOS\WooCommercePOS\Services\Settings;
14 use WCPOS\WooCommercePOS\Template_Router;
15 use const WCPOS\WooCommercePOS\PLUGIN_PATH;
16 use const WCPOS\WooCommercePOS\SHORT_NAME;
17 use const WCPOS\WooCommercePOS\VERSION;
18
19 /*
20 * ============================================================================
21 * WCPOS Functions
22 * ============================================================================
23 *
24 * Primary functions using the wcpos_ prefix.
25 */
26
27 /*
28 * getallheaders() is an alias of apache_response_headers()
29 * This function provides compatibility for nginx servers
30 */
31 if ( ! \function_exists( 'getallheaders' ) ) {
32 /**
33 * Polyfill for getallheaders() on nginx servers.
34 *
35 * @return array The request headers.
36 */
37 function getallheaders(): array { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- polyfill for missing PHP function.
38 $headers = array();
39 foreach ( $_SERVER as $name => $value ) {
40 // RFC2616 (HTTP/1.1) defines header fields as case-insensitive entities.
41 if ( 'http_' == strtolower( substr( $name, 0, 5 ) ) ) {
42 $headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value;
43 }
44 }
45
46 return $headers;
47 }
48 }
49
50 /*
51 * Resolve the URL scheme for POS permalinks.
52 *
53 * @return string|null 'https' when force_ssl is enabled, null for the home scheme.
54 */
55 if ( ! \function_exists( 'wcpos_url_scheme' ) ) {
56 /**
57 * Resolve the URL scheme for POS permalinks.
58 *
59 * See Settings::url_scheme() for the policy.
60 *
61 * @return string|null 'https' when force_ssl is enabled, null for the home scheme.
62 */
63 function wcpos_url_scheme(): ?string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
64 return Settings::instance()->url_scheme();
65 }
66 }
67
68 /*
69 * Construct the POS permalink.
70 *
71 * @param string $page Page slug.
72 * @return string POS URL.
73 */
74 if ( ! \function_exists( 'wcpos_url' ) ) {
75 /**
76 * Construct the POS permalink.
77 *
78 * The trailing slash follows the site's permalink structure, via
79 * user_trailingslashit().
80 *
81 * @param string $page Page slug.
82 *
83 * @return string POS URL.
84 */
85 function wcpos_url( $page = '' ): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
86 $slug = Permalink::get_slug();
87
88 return home_url( user_trailingslashit( $slug . '/' . $page ), wcpos_url_scheme() );
89 }
90 }
91
92 /*
93 * Construct a POS checkout permalink.
94 *
95 * @param string $path Path relative to the wcpos-checkout endpoint.
96 * @return string POS checkout URL.
97 */
98 if ( ! \function_exists( 'wcpos_checkout_url' ) ) {
99 /**
100 * Construct a POS checkout permalink.
101 *
102 * Respects the force_ssl setting, like wcpos_url(), so checkout and receipt
103 * links work when the site home URL is http but the POS is served over https,
104 * eg: behind an SSL-terminating proxy.
105 *
106 * Like home_url(), this performs no encoding — pass trusted path segments
107 * only and escape the result on output.
108 *
109 * The trailing slash follows the site's permalink structure, via
110 * user_trailingslashit(). Slash-less URLs can trip origin rewrite rules
111 * that force a trailing slash — some redirect to a hardcoded http://
112 * target, which the browser then blocks as mixed content. The slash is
113 * appended to the end of the string, so $path must not contain a query
114 * string or fragment; append query args to the returned URL instead.
115 *
116 * @param string $path Path relative to the wcpos-checkout endpoint, eg: 'order-pay/123'.
117 *
118 * @return string POS checkout URL.
119 */
120 function wcpos_checkout_url( $path = '' ): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
121 $full_path = Template_Router::CHECKOUT_PATH . '/' . ltrim( $path, '/' );
122
123 return home_url( user_trailingslashit( $full_path ), wcpos_url_scheme() );
124 }
125 }
126
127 /*
128 * Test for POS requests to the server.
129 *
130 * @param string $type Request type: 'query_var', 'header', or 'all'.
131 * @return bool Whether this is a POS request.
132 */
133 if ( ! \function_exists( 'wcpos_request' ) ) {
134 /**
135 * Test for POS requests to the server.
136 *
137 * Core's rest_api_loaded() reads this query var, which remains the original
138 * outer route during internal re-dispatches; this behavior is load-bearing.
139 *
140 * @param string $type Request type: 'query_var', 'header', 'rest_route', or 'all'.
141 *
142 * @return bool Whether this is a POS request.
143 */
144 function wcpos_request( $type = 'all' ): bool { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
145 // check query_vars, eg: ?wcpos=1 or /pos rewrite rule.
146 if ( 'all' == $type || 'query_var' == $type ) {
147 global $wp;
148 if ( 1 == isset( $wp->query_vars[ SHORT_NAME ] ) && $wp->query_vars[ SHORT_NAME ] ) {
149 return true;
150 }
151 }
152
153 // check headers, eg: from ajax request.
154 if ( 'all' == $type || 'header' == $type ) {
155 $headers = array_change_key_case( getallheaders() ); // convert headers to lowercase.
156 if ( 1 == isset( $headers[ 'x-' . SHORT_NAME ] ) && $headers[ 'x-' . SHORT_NAME ] ) {
157 return true;
158 }
159 }
160
161 if ( ( 'all' == $type || 'rest_route' == $type ) && isset( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
162 $route = '/' . ltrim( (string) $GLOBALS['wp']->query_vars['rest_route'], '/' );
163 return 1 === preg_match( '#^/' . preg_quote( SHORT_NAME, '#' ) . '/v\d+(?:/|$)#', $route );
164 }
165
166 return false;
167 }
168 }
169
170 /*
171 * Check for POS admin requests.
172 *
173 * @return mixed Admin request header value or false.
174 */
175 if ( ! \function_exists( 'wcpos_admin_request' ) ) {
176 /**
177 * Check for POS admin requests.
178 *
179 * @return mixed Admin request header value or false.
180 */
181 function wcpos_admin_request() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
182 if ( \function_exists( 'getallheaders' ) ) {
183 $headers = getallheaders();
184 if ( $headers && isset( $headers['X-WC-POS-ADMIN'] ) ) {
185 return $headers['X-WC-POS-ADMIN'];
186 }
187 }
188 if ( isset( $_SERVER['HTTP_X_woocommerce_pos_ADMIN'] ) ) {
189 return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_woocommerce_pos_ADMIN'] ) );
190 }
191
192 return false;
193 }
194 }
195
196 /*
197 * Helper function to get WCPOS settings.
198 *
199 * @param string $id Settings ID.
200 * @param string $key Optional settings key.
201 * @return mixed Settings value.
202 */
203 if ( ! \function_exists( 'wcpos_get_settings' ) ) {
204 /**
205 * Helper function to get WCPOS settings.
206 *
207 * @param string $id Settings ID.
208 * @param string $key Optional settings key.
209 *
210 * @return mixed Settings value.
211 */
212 function wcpos_get_settings( $id, $key = null ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
213 $settings_service = Settings::instance();
214
215 return $settings_service->get_settings( $id, $key );
216 }
217 }
218
219 /*
220 * Determine whether WCPOS Pro is active.
221 *
222 * @return bool Whether WCPOS Pro is active.
223 */
224 if ( ! \function_exists( 'wcpos_is_pro_active' ) ) {
225 /**
226 * Determine whether WCPOS Pro is active.
227 */
228 function wcpos_is_pro_active(): bool { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
229 return (bool) apply_filters( 'woocommerce_pos_is_pro_active', \defined( 'WCPOS\WooCommercePOSPro\VERSION' ) );
230 }
231 }
232
233 /*
234 * Get the site UUID (Plugin State), generating and persisting it on first use.
235 *
236 * @return string Site UUID.
237 */
238 if ( ! \function_exists( 'wcpos_get_site_uuid' ) ) {
239 /**
240 * Get the site UUID, generating and persisting it on first use.
241 *
242 * Single owner for the woocommerce_pos_uuid option — the
243 * generate-if-missing logic previously lived in three places (REST index,
244 * POS frontend, analytics) and could race.
245 *
246 * @return string Site UUID.
247 */
248 function wcpos_get_site_uuid(): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
249 $uuid = get_option( 'woocommerce_pos_uuid', '' );
250 if ( \is_string( $uuid ) && '' !== $uuid ) {
251 return $uuid;
252 }
253
254 $uuid = \Ramsey\Uuid\Uuid::uuid4()->toString();
255
256 // add_option() is a no-op when the option already exists, so a
257 // concurrent request that won the race keeps its value.
258 if ( ! add_option( 'woocommerce_pos_uuid', $uuid ) ) {
259 $existing = get_option( 'woocommerce_pos_uuid', '' );
260 if ( \is_string( $existing ) && '' !== $existing ) {
261 return $existing;
262 }
263 update_option( 'woocommerce_pos_uuid', $uuid );
264 }
265
266 return $uuid;
267 }
268 }
269
270 /*
271 * Simple wrapper for json_encode.
272 *
273 * Use JSON_FORCE_OBJECT for PHP 5.3 or higher with fallback for
274 * PHP less than 5.3.
275 *
276 * @param mixed $data Data to encode.
277 * @return string|false JSON string or false on failure.
278 */
279 if ( ! \function_exists( 'wcpos_json_encode' ) ) {
280 /**
281 * Simple wrapper for json_encode with JSON_FORCE_OBJECT.
282 *
283 * @param mixed $data Data to encode.
284 *
285 * @return string|false JSON string or false on failure.
286 */
287 function wcpos_json_encode( $data ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
288 $args = array( $data, JSON_FORCE_OBJECT );
289
290 return \call_user_func_array( 'json_encode', $args );
291 }
292 }
293
294 /*
295 * Return template path for a given template.
296 *
297 * @param string $template Template name.
298 * @return string|null Template path or null if not found.
299 */
300 if ( ! \function_exists( 'wcpos_locate_template' ) ) {
301 /**
302 * Return template path for a given template.
303 *
304 * @param string $template Template name.
305 *
306 * @return string|null Template path or null if not found.
307 */
308 function wcpos_locate_template( $template = '' ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
309 // check theme directory first.
310 $path = locate_template(
311 array(
312 'woocommerce-pos/' . $template,
313 )
314 );
315
316 // if not, use plugin template.
317 if ( ! $path ) {
318 $path = PLUGIN_PATH . 'templates/' . $template;
319 }
320
321 /**
322 * Filters the template path.
323 *
324 * @hook woocommerce_pos_locate_template
325 *
326 * @since 1.0.0
327 *
328 * @param string $path The full path to the template.
329 * @param string $template The template name, eg: 'receipt.php'.
330 *
331 * @return string $path The full path to the template.
332 */
333 $filtered_path = apply_filters( 'woocommerce_pos_locate_template', $path, $template ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- legacy hook name.
334
335 // Check if the filtered template file exists.
336 if ( file_exists( $filtered_path ) ) {
337 return $filtered_path;
338 }
339
340 // Echo a message or handle the error as needed if the file path does not exist.
341 echo "The template file '" . esc_html( $filtered_path ) . "' does not exist.";
342
343 return null;
344 }
345 }
346
347 /*
348 * Remove newlines and code spacing.
349 *
350 * @param string $str HTML string to trim.
351 * @return string Trimmed string.
352 */
353 if ( ! \function_exists( 'wcpos_trim_html_string' ) ) {
354 /**
355 * Remove newlines and code spacing from an HTML string.
356 *
357 * @param string $str HTML string to trim.
358 *
359 * @return string Trimmed string.
360 */
361 function wcpos_trim_html_string( $str ): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
362 return preg_replace( '/^\s+|\n|\r|\s+$/m', '', $str );
363 }
364 }
365
366 /*
367 * Get documentation URL.
368 *
369 * @param string $page Documentation page.
370 * @return string Documentation URL.
371 */
372 if ( ! \function_exists( 'wcpos_doc_url' ) ) {
373 /**
374 * Get documentation URL.
375 *
376 * @param string $page Documentation page.
377 *
378 * @return string Documentation URL.
379 */
380 function wcpos_doc_url( $page ): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
381 return 'http://docs.wcpos.com/v/' . VERSION . '/en/' . $page;
382 }
383 }
384
385 /*
386 * Get FAQ URL.
387 *
388 * @param string $page FAQ page.
389 * @return string FAQ URL.
390 */
391 if ( ! \function_exists( 'wcpos_faq_url' ) ) {
392 /**
393 * Get FAQ URL.
394 *
395 * @param string $page FAQ page.
396 *
397 * @return string FAQ URL.
398 */
399 function wcpos_faq_url( $page ): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
400 return 'http://faq.wcpos.com/v/' . VERSION . '/en/' . $page;
401 }
402 }
403
404 /*
405 * Helper function to check whether an order is a POS order.
406 *
407 * @param \WC_Order|int $order Order object or ID.
408 * @return bool Whether the order is a POS order.
409 */
410 if ( ! \function_exists( 'wcpos_is_pos_order' ) ) {
411 /**
412 * Helper function to check whether an order is a POS order.
413 *
414 * @param \WC_Order|int $order Order object or ID.
415 *
416 * @return bool Whether the order is a POS order.
417 */
418 function wcpos_is_pos_order( $order ): bool { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
419 // Handle various input types and edge cases.
420 if ( ! $order instanceof WC_Order ) {
421 // Sometimes the order is passed as an ID.
422 if ( is_numeric( $order ) ) {
423 $order = wc_get_order( $order );
424 }
425
426 // If we still don't have a valid order, return false.
427 if ( ! $order instanceof WC_Order ) {
428 return false;
429 }
430 }
431
432 $legacy = $order->get_meta( '_pos', true );
433 $created_via = $order->get_created_via();
434
435 return 'woocommerce-pos' === $created_via || '1' === $legacy;
436 }
437 }
438
439 /*
440 * Get a default WooCommerce template.
441 *
442 * @param string $template_name Template name.
443 * @param array $args Arguments.
444 */
445 if ( ! \function_exists( 'wcpos_get_woocommerce_template' ) ) {
446 /**
447 * Get a default WooCommerce template.
448 *
449 * @param string $template_name Template name.
450 * @param array $args Arguments.
451 */
452 function wcpos_get_woocommerce_template( $template_name, $args = array() ): void { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix.
453 $plugin_path = WC()->plugin_path();
454 $template = trailingslashit( $plugin_path . '/templates' ) . $template_name;
455
456 /**
457 * Filter the default WooCommerce template path.
458 *
459 * @param string $template Template path.
460 * @param string $template_name Template name.
461 * @param array $args Arguments.
462 */
463 $template = apply_filters( 'wcpos_locate_woocommerce_template', $template, $template_name, $args ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- uses wcpos_ prefix.
464
465 if ( ! file_exists( $template ) ) {
466 Logger::log( \sprintf( 'WooCommerce default template not found: %s', $template ) );
467
468 return;
469 }
470
471 if ( $args && \is_array( $args ) ) {
472 extract( $args ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
473 }
474
475 include $template;
476 }
477 }
478
479 /*
480 * ============================================================================
481 * Legacy Aliases
482 * ============================================================================
483 *
484 * These functions use the old woocommerce_pos_ prefix.
485 * They are kept for backwards compatibility but new code should use wcpos_ prefix.
486 *
487 * @deprecated Use wcpos_* functions instead.
488 */
489
490 if ( ! \function_exists( 'woocommerce_pos_url' ) ) {
491 /**
492 * Legacy alias for wcpos_url().
493 *
494 * @deprecated Use wcpos_url() instead.
495 *
496 * @param mixed $page The page slug.
497 */
498 function woocommerce_pos_url( $page = '' ): string {
499 return wcpos_url( $page );
500 }
501 }
502
503 if ( ! \function_exists( 'woocommerce_pos_request' ) ) {
504 /**
505 * Legacy alias for wcpos_request().
506 *
507 * @deprecated Use wcpos_request() instead.
508 *
509 * @param mixed $type The request type.
510 */
511 function woocommerce_pos_request( $type = 'all' ): bool {
512 return wcpos_request( $type );
513 }
514 }
515
516 if ( ! \function_exists( 'woocommerce_pos_admin_request' ) ) {
517 /**
518 * Legacy alias for wcpos_admin_request().
519 *
520 * @deprecated Use wcpos_admin_request() instead.
521 */
522 function woocommerce_pos_admin_request() {
523 return wcpos_admin_request();
524 }
525 }
526
527 if ( ! \function_exists( 'woocommerce_pos_get_settings' ) ) {
528 /**
529 * Legacy alias for wcpos_get_settings().
530 *
531 * @deprecated Use wcpos_get_settings() instead.
532 *
533 * @param mixed $id The settings ID.
534 * @param null|mixed $key The settings key.
535 */
536 function woocommerce_pos_get_settings( $id, $key = null ) {
537 return wcpos_get_settings( $id, $key );
538 }
539 }
540
541 if ( ! \function_exists( 'woocommerce_pos_get_anon_id' ) ) {
542 /**
543 * Returns the anonymous analytics id (wcpos_anon_id), creating it on first use.
544 *
545 * Supported accessor for the Pro plugin's licence-activation request and the
546 * wcpos.com purchase reconciler join (landing-experiments spec §5.3c).
547 *
548 * @return string v4 UUID.
549 */
550 function woocommerce_pos_get_anon_id(): string {
551 return ( new \WCPOS\WooCommercePOS\Services\Anon_ID() )->get();
552 }
553 }
554
555 if ( ! \function_exists( 'woocommerce_pos_json_encode' ) ) {
556 /**
557 * Legacy alias for wcpos_json_encode().
558 *
559 * @deprecated Use wcpos_json_encode() instead.
560 *
561 * @param mixed $data The data to encode.
562 */
563 function woocommerce_pos_json_encode( $data ) {
564 return wcpos_json_encode( $data );
565 }
566 }
567
568 if ( ! \function_exists( 'woocommerce_pos_locate_template' ) ) {
569 /**
570 * Legacy alias for wcpos_locate_template().
571 *
572 * @deprecated Use wcpos_locate_template() instead.
573 *
574 * @param mixed $template The template name.
575 */
576 function woocommerce_pos_locate_template( $template = '' ) {
577 return wcpos_locate_template( $template );
578 }
579 }
580
581 if ( ! \function_exists( 'woocommerce_pos_trim_html_string' ) ) {
582 /**
583 * Legacy alias for wcpos_trim_html_string().
584 *
585 * @deprecated Use wcpos_trim_html_string() instead.
586 *
587 * @param mixed $str The HTML string.
588 */
589 function woocommerce_pos_trim_html_string( $str ): string {
590 return wcpos_trim_html_string( $str );
591 }
592 }
593
594 if ( ! \function_exists( 'woocommerce_pos_doc_url' ) ) {
595 /**
596 * Legacy alias for wcpos_doc_url().
597 *
598 * @deprecated Use wcpos_doc_url() instead.
599 *
600 * @param mixed $page The documentation page.
601 */
602 function woocommerce_pos_doc_url( $page ): string {
603 return wcpos_doc_url( $page );
604 }
605 }
606
607 if ( ! \function_exists( 'woocommerce_pos_faq_url' ) ) {
608 /**
609 * Legacy alias for wcpos_faq_url().
610 *
611 * @deprecated Use wcpos_faq_url() instead.
612 *
613 * @param mixed $page The FAQ page.
614 */
615 function woocommerce_pos_faq_url( $page ): string {
616 return wcpos_faq_url( $page );
617 }
618 }
619
620 if ( ! \function_exists( 'woocommerce_pos_is_pos_order' ) ) {
621 /**
622 * Legacy alias for wcpos_is_pos_order().
623 *
624 * @deprecated Use wcpos_is_pos_order() instead.
625 *
626 * @param mixed $order The order object or ID.
627 */
628 function woocommerce_pos_is_pos_order( $order ): bool {
629 return wcpos_is_pos_order( $order );
630 }
631 }
632