Issues (3)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/class-wcs-shortcodes.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Subscription Shortcodes
4
 *
5
 * @class WCSS_Shortcodes
6
 * @since 1.0.0
7
 */
8
9
class WCSS_Shortcodes {
10
11
	/**
12
	 * Initialize the shortcodes.
13
	 */
14
	public static function init() {
15
		$shortcodes = array(
16
			'subscription_price'           => 'get_subscription_price',
17
			'subscription_price_meta'      => 'get_subscription_price_meta',
18
			'subscription_discount'        => 'get_subscription_discount',
19
			'subscription_period'          => 'get_subscription_period',
20
			'subscription_period_interval' => 'get_subscription_period_interval',
21
			'subscription_length'          => 'get_subscription_length',
22
			'subscription_sign_up_fee'     => 'get_subscription_sign_up_fee',
23
			'subscription_trial'           => 'get_subscription_trial_string',
24
			'subscription_trial_length'    => 'get_subscription_trial_length',
25
			'subscription_trial_period'    => 'get_subscription_trial_period',
26
			'subscription_first_payment'   => 'get_subscription_first_payment',
27
			'subscription_initial_payment' => 'get_subscription_initial',
28
		);
29
30
		foreach ( $shortcodes as $shortcode => $function ) {
31
			add_shortcode( apply_filters( "{$shortcode}_shortcode_tag", $shortcode ), array( __CLASS__, $function ) );
32
		} // END foreach()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
33
34
		// Adds alternative subscription price from the WooCommerce extension "Subscribe to All the Things" and returns the lowest scheme price.
35
		add_action( 'woocommerce_subscriptions_shortcode_get_price', array( __CLASS__, 'get_satt_lowest_price' ), 10, 1 );
36
37
		// Adds the product types supported from the WooCommerce extension "Subscribe to All the Things".
38
		add_filter( 'wcss_product_types', array( __CLASS__, 'support_product_types_for_wc_satt' ), 10, 1 );
39
	} // END init()
40
41
	/**
42
	 * Get the supported product types.
43
	 * By default, this is only the subscription product types.
44
	 * However, it can be filtered to allow other product types should you need to.
45
	 *
46
	 * @return array
47
	 */
48
	public static function get_supported_product_types() {
49
		return apply_filters( 'wcss_product_types', array(
50
			'subscription', 
51
			'subscription-variation', 
52
		) );
53
	} // END get_supported_product_types()
54
55
	/**
56
	 * Returns the subscription price string.
57
	 *
58
	 * @param  WC_Product $product
59
	 * @return string
60
	 */
61
	public static function get_price( $product ) {
62
		if ( WC_Subscriptions_Product::get_price( $product->id ) > 0 ) {
63
64
			return ecs_html( WC_Subscriptions_Product::get_price( $product->id, array(
65
				'subscription_period' => false,
66
				'subscription_length' => false,
67
				'sign_up_fee'         => false,
68
				'trial_length'        => false,
69
			) ) );
70
71
		} else {
72
73
			/**
74
			 * This hook enables for other price possibilities.
75
			 *
76
			 * hooked: get_satt_lowest_price - 10
77
			 */
78
			do_action( 'woocommerce_subscriptions_shortcode_get_price', $product );
79
80
		}
81
	} // END get_price()
82
83
	/**
84
	 * Returns the lowest subscription scheme.
85
	 * Only works with WooCommerce Subscribe to All the Things v1.1.0+
86
	 *
87
	 * @param  WC_Product $product
88
	 * @return string
89
	 */
90
	public static function get_satt_lowest_scheme_data( $product ) {
91
		if ( class_exists( 'WCS_ATT_Schemes' ) && class_exists( 'WCS_ATT_Scheme_Prices' ) ) {
92
			$product_level_schemes = WCS_ATT_Schemes::get_product_subscription_schemes( $product );
93
94
			return WCS_ATT_Scheme_Prices::get_lowest_price_subscription_scheme_data( $product, $product_level_schemes );
95
		}
96
	} // END get_satt_lowest_scheme()
97
98
	/**
99
	 * Returns the lowest subscription scheme price string.
100
	 * Only works with WooCommerce Subscribe to All the Things v1.1.0+
101
	 *
102
	 * @param  WC_Product $product
103
	 * @return string
104
	 */
105
	public static function get_satt_lowest_price( $product ) {
106
		$scheme = self::get_satt_lowest_scheme_data( $product );
107
108
		if ( !empty( $scheme ) && is_array( $scheme ) ) {
109
			// Override price?
110
			$override = $scheme['scheme']['subscription_pricing_method'];
111
112
			// Discount?
113
			$discount = $scheme['scheme']['subscription_discount'];
114
115
			// Prices
116
			$prices = array(
117
				'price'                      => $scheme['price'],
118
				'regular_price'              => $scheme['regular_price'],
119
				'sale_price'                 => $scheme['sale_price'],
120
				'subscription_price'         => $scheme['scheme']['subscription_price'],
121
				'subscription_regular_price' => $scheme['scheme']['subscription_regular_price'],
122
				'subscription_sale_price'    => $scheme['scheme']['subscription_sale_price']
123
			);
124
125
			// Prepare the price
126
			$price = '';
127
128
			if ( 'inherit' == $override ) {
129
				$price = empty( $discount ) ? $price : ( empty( $prices[ 'regular_price' ] ) ? $prices[ 'regular_price' ] : round( ( double ) $prices[ 'regular_price' ] * ( 100 - $discount ) / 100, wc_get_price_decimals() ) );
130
			} else if ( 'override' == $override ) {
131
				$price = $prices['subscription_price'];
132
133
				if ( $prices[ 'subscription_price' ] < $prices[ 'subscription_regular_price' ] ) {
134
					$price = $prices[ 'subscription_sale_price' ];
135
				}
136
			}
137
138
			// If the price is returned as an array, return just the first.
139
			if ( is_array( $price ) ) {
140
				$price = $price[0];
141
			}
142
143
			return $price;
144
		}
145
	} // END get_price_satt()
146
147
	/**
148
	 * Displays the price of the subscription product and 
149
	 * returns only the price information you wish to return.
150
	 *
151
	 * @global $wpdb
152
	 * @global WP_Post $post
153
	 * @param  array   $atts
154
	 * @return string
155
	 */
156
	public static function get_subscription_price( $atts ) {
157
		global $wpdb, $post;
158
159
		$defaults = shortcode_atts( array(
160
			'id'           => '',
161
			'sku'          => '',
162
			'period'       => false,
163
			'length'       => false,
164
			'sign_up_fee'  => false,
165
			'trial_length' => false,
166
			'before_price' => '<span class="price subscription-price">',
167
			'after_price'  => '</span>',
168
		), $atts );
169
170
		$atts = wp_parse_args( $atts, $defaults );
171
172
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
173
			$product_data = wc_get_product( $atts['id'] );
174
		} elseif ( ! empty( $atts['sku'] ) ) {
175
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
176
			$product_data = get_post( $product_id );
177
		} else {
178
			$product_data = wc_get_product( $post->ID );
179
		}
180
181
		// Check that the product type is supported. Return blank if not supported.
182
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
183
			return '';
184
		}
185
186
		ob_start();
187
188
		$price_html = WC_Subscriptions_Product::get_price_string( $product_data->id, array(
189
			'price'               => self::get_price( $product_data ),
190
			'subscription_period' => isset( $atts['period'] ) ? $atts['period'] : true,
191
			'subscription_length' => isset( $atts['length'] ) ? $atts['length'] : true,
192
			'sign_up_fee'         => isset( $atts['sign_up_fee'] ) ? $atts['sign_up_fee'] : true,
193
			'trial_length'        => isset( $atts['trial_length'] ) ? $atts['trial_length'] : true,
194
		) );
195
196
		// Clean the subscription price wrapper.
197
		$price_html = str_replace('<span class="subscription-details">', '', $price_html);
198
		$price_html = str_replace('</span">', '', $price_html);
199
200
		// Trim the whitespace.
201
		$price_html = trim( $price_html );
202
203
		// Convert to Price Tag.
204
		$price_html = wc_price( $price_html );
205
206
		$price_html = sprintf( __( '%s%s%s', WCSS::TEXT_DOMAIN ), $atts['before_price'], $price_html, $atts['after_price'] );
207
208
		echo html_entity_decode( $price_html );
209
210
		return ob_get_clean();
211
	} // END get_subscription_price()
212
213
	/**
214
	 * Displays the price meta of the subscription product.
215
	 *
216
	 * @global $wpdb
217
	 * @global WP_Post $post
218
	 * @param  array   $atts
219
	 * @return string
220
	 */
221
	public static function get_subscription_price_meta( $atts ) {
222
		global $wpdb, $post;
223
224
		$defaults = shortcode_atts( array(
225
			'id'           => '',
226
			'sku'          => '',
227
			'meta'         => 'both',
228
			'before_price' => '',
229
			'after_price'  => '',
230
		), $atts );
231
232
		$atts = wp_parse_args( $atts, $defaults );
233
234
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
235
			$product_data = wc_get_product( $atts['id'] );
236
		} elseif ( ! empty( $atts['sku'] ) ) {
237
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
238
			$product_data = get_post( $product_id );
239
		} else {
240
			$product_data = wc_get_product( $post->ID );
241
		}
242
243
		// Check that the product type is supported. Return blank if not supported.
244
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
245
			return '';
246
		}
247
248
		ob_start();
249
250
		$price = WC_Subscriptions_Product::get_price( $product_data->id );
251
252
		// Remove the subscription price wrapper.
253
		$price_html = str_replace('<span class="subscription-details">', '', $price);
254
		$price = str_replace('</span">', '', $price_html);
255
256
		// If the subscription product has no price, then look for alternative.
257
		if ( empty( $price ) ) {
258
			$scheme = self::get_satt_lowest_scheme_data( $product_data );
259
260
			if ( !empty( $scheme ) && is_array( $scheme ) ) {
261
262
				// These values will be overridden. They are defined here to acknowledge their existence.
263
				$price = '';
264
				$regular_price = '';
265
				$sale_price = '';
266
267
				$prices = array(
268
					'price'                      => $scheme['price'],
269
					'regular_price'              => $scheme['regular_price'],
270
					'sale_price'                 => $scheme['sale_price'],
271
					'method'                     => $scheme['scheme']['subscription_pricing_method'],
272
					'subscription_price'         => $scheme['scheme']['subscription_price'],
273
					'subscription_regular_price' => $scheme['scheme']['subscription_regular_price'],
274
					'subscription_sale_price'    => $scheme['scheme']['subscription_sale_price']
275
				);
276
277
				// Return the subscription price based on the pricing method.
278
				switch( $prices['method'] ) {
279
					case 'override':
280
						$price         = $prices['subscription_price'];
281
						$regular_price = $prices['subscription_regular_price'];
282
						$sale_price    = $prices['subscription_sale_price'];
283
						break;
284
					case 'inherit':
285
						$discount      = $scheme['scheme']['subscription_discount'];
286
						$price         = $prices['price'];
287
						$regular_price = $prices['regular_price'];
288
						$sale_price    = $prices['sale_price'];
289
290
						if ( !empty( $discount ) && $discount > 0 ) {
291
							$sale_price = round( ( double ) $regular_price * ( 100 - $discount ) / 100, wc_get_price_decimals() );
292
						}
293
294
						break;
295
				}
296
297
				// Display both the regular price striked out and the sale price 
298
				// should the active price be less than the regular price.
299
				if ( $atts['meta'] != 'active' && !empty( $sale_price ) && $price < $regular_price ) {
300
					$price = '<del>' . ( ( is_numeric( $regular_price ) ) ? wc_price( $regular_price ) : $regular_price ) . '</del> <ins>' . ( ( is_numeric( $sale_price ) ) ? wc_price( $sale_price ) : $sale_price ) . '</ins>';
301
302
					// Trim the whitespace.
303
					$price = trim( $price );
304
				}
305
306
				// Override the value should only one value be returned.
307
				if ( $atts['meta'] != 'both' ) {
308
					if ( $atts['meta'] == 'active' ) {
309
						$price = $price;
0 ignored issues
show
Why assign $price to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
310
					}
311
312
					if ( $atts['meta'] == 'regular' ) {
313
						$price = $regular_price;
314
					}
315
316
					if ( $atts['meta'] == 'sale' ) {
317
						$price = $sale_price;
318
					}
319
				}
320
321
				if ( is_numeric( $price ) ) {
322
					$price = wc_price( $price );
323
				}
324
325
				// Clean the price tag.
326
				$price = self::clean_wc_price( $price );
327
328
			}
329
330
		}
331
332
		$price_html = sprintf( __( '%s%s%s', WCSS::TEXT_DOMAIN ), $atts['before_price'], $price, $atts['after_price'] );
333
334
		echo html_entity_decode( $price_html );
335
336
		return ob_get_clean();
337
	} // END get_subscription_price_meta()
338
339
	/**
340
	 * Displays the subscription discount of the subscription product.
341
	 * This shortcode only work with products using the mini-extension
342
	 * "WooCommerce Subscribe to All the Things".
343
	 *
344
	 * @param  array $atts
345
	 * @return string
346
	 */
347
	public static function get_subscription_discount( $atts ) {
348
		global $wpdb, $post;
349
350
		$defaults = shortcode_atts( array(
351
			'id'  => '',
352
			'sku' => '',
353
		), $atts );
354
355
		$atts = wp_parse_args( $atts, $defaults );
356
357
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
358
			$product_data = wc_get_product( $atts['id'] );
359
		} elseif ( ! empty( $atts['sku'] ) ) {
360
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
361
			$product_data = get_post( $product_id );
362
		} else {
363
			$product_data = wc_get_product( $post->ID );
364
		}
365
366
		// Check that the product type is supported. Return blank if not supported.
367
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
368
			return '';
369
		}
370
371
		ob_start();
372
373
		$override = 'inherit'; // Returns inherit by default. Will be overridden later.
374
		$discount = ''; // Returns empty by default.
375
376
		// Get Subscription Discount - Only available with the WooCommerce extension "Subscribe to All the Things".
377
		$scheme = self::get_satt_lowest_scheme_data( $product_data );
378
379 View Code Duplication
		if ( !empty( $scheme ) && is_array( $scheme ) ) {
380
			// Override price?
381
			$override = $scheme['scheme']['subscription_pricing_method'];
382
383
			// Discount ?
384
			$discount = $scheme['scheme']['subscription_discount'];
385
		}
386
387
		if ( ! empty( $discount ) && is_numeric( $discount ) && $override == 'inherit' ) {
388
			$discount = sprintf( __( '%s%s %s', WCSS::TEXT_DOMAIN ), $discount, '%', apply_filters( 'wcs_shortcodes_sub_discount_string', __( 'discount', WCSS::TEXT_DOMAIN ) ) );
389
		}
390
391
		echo $discount;
392
393
		return ob_get_clean();
394
	} // END get_subscription_discount()
395
396
	/**
397
	 * Displays the subscription period of the subscription product.
398
	 *
399
	 * @param  array $atts
400
	 * @return string
401
	 */
402
	public static function get_subscription_period( $atts ) {
403
		global $wpdb, $post;
404
405
		$defaults = shortcode_atts( array(
406
			'id'  => '',
407
			'sku' => '',
408
			'raw' => false
409
		), $atts );
410
411
		$atts = wp_parse_args( $atts, $defaults );
412
413
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
414
			$product_data = wc_get_product( $atts['id'] );
415
		} elseif ( ! empty( $atts['sku'] ) ) {
416
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
417
			$product_data = get_post( $product_id );
418
		} else {
419
			$product_data = wc_get_product( $post->ID );
420
		}
421
422
		// Check that the product type is supported. Return blank if not supported.
423
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
424
			return '';
425
		}
426
427
		ob_start();
428
429
		// Get Subscription Period
430
		$period = WC_Subscriptions_Product::get_period( $product_data );
431
432
		// If the period is empty, look for alternative.
433
		if ( empty( $period ) ) {
434
			$scheme = self::get_satt_lowest_scheme_data( $product_data );
435
436
			if ( !empty( $scheme ) && is_array( $scheme ) ) {
437
				$period = $scheme['scheme']['subscription_period'];
438
			}
439
		}
440
441
		if ( ! $atts['raw'] ) {
442
			$period = sprintf( __( 'Per %s', WCSS::TEXT_DOMAIN ), $period );
443
			$period = ucwords($period);
444
		}
445
446
		echo $period;
447
448
		return ob_get_clean();
449
	} // END get_subscription_period()
450
451
	/**
452
	 * Displays the subscription period interval of the subscription product.
453
	 *
454
	 * @param  array $atts
455
	 * @return string
456
	 */
457 View Code Duplication
	public static function get_subscription_period_interval( $atts ) {
458
		global $wpdb, $post;
459
460
		$defaults = shortcode_atts( array(
461
			'id'  => '',
462
			'sku' => '',
463
		), $atts );
464
465
		$atts = wp_parse_args( $atts, $defaults );
466
467
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
468
			$product_data = wc_get_product( $atts['id'] );
469
		} elseif ( ! empty( $atts['sku'] ) ) {
470
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
471
			$product_data = get_post( $product_id );
472
		} else {
473
			$product_data = wc_get_product( $post->ID );
474
		}
475
476
		// Check that the product type is supported. Return blank if not supported.
477
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
478
			return '';
479
		}
480
481
		ob_start();
482
483
		// Get Subscription Period Interval
484
		$period_interval = WC_Subscriptions_Product::get_interval( $product_data );
485
486
		// If the period is empty, look for alternative.
487
		if ( empty( $period_interval ) ) {
488
			$scheme = self::get_satt_lowest_scheme_data( $product_data );
489
490
			if ( !empty( $scheme ) && is_array( $scheme ) ) {
491
				$period_interval = $scheme['scheme']['subscription_period_interval'];
492
			}
493
		}
494
495
		echo $period_interval;
496
497
		return ob_get_clean();
498
	} // END get_subscription_period_interval()
499
500
	/**
501
	 * Displays the subscription length of the subscription product.
502
	 *
503
	 * @param  array $atts
504
	 * @return string
505
	 */
506
	public static function get_subscription_length( $atts ) {
507
		global $wpdb, $post;
508
509
		$defaults = shortcode_atts( array(
510
			'id'  => '',
511
			'sku' => '',
512
			'raw' => false,
513
		), $atts );
514
515
		$atts = wp_parse_args( $atts, $defaults );
516
517
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
518
			$product_data = wc_get_product( $atts['id'] );
519
		} elseif ( ! empty( $atts['sku'] ) ) {
520
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
521
			$product_data = get_post( $product_id );
522
		} else {
523
			$product_data = wc_get_product( $post->ID );
524
		}
525
526
		// Check that the product type is supported. Return blank if not supported.
527
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
528
			return '';
529
		}
530
531
		ob_start();
532
533
		// Get Subscription Length
534
		$length = WC_Subscriptions_Product::get_length( $product_data );
535
536
		// If the length is empty, look for alternative.
537
		if ( empty( $length ) ) {
538
			$scheme = self::get_satt_lowest_scheme_data( $product_data );
539
540
			if ( !empty( $scheme ) && is_array( $scheme ) ) {
541
542
				$period = self::get_subscription_period( array( 'id' => $product_data->id, 'raw' => true ) );
543
				$length = $scheme['scheme']['subscription_length'];
544
545
				// If we are not returning raw data then making it readable for humans.
546
				if ( ! $atts['raw'] ) {
547
548
					if ( $length > 0 ) {
549
						$length = sprintf( '%s %s', $length, $period );
550
					} else {
551
						$length = sprintf( __( 'Every %s', WCSS::TEXT_DOMAIN ), $period );
552
					}
553
554
					$length = ucfirst($length);
555
556
				}
557
558
			}
559
560
		}
561
562
		echo $length;
563
564
		return ob_get_clean();
565
	} // END get_subscription_length()
566
567
	/**
568
	 * Displays the subscription sign-up fee of the subscription product.
569
	 *
570
	 * @param  array $atts
571
	 * @return string
572
	 */
573
	public static function get_subscription_sign_up_fee( $atts ) {
574
		global $wpdb, $post;
575
576
		$defaults = shortcode_atts( array(
577
			'id'           => '',
578
			'sku'          => '',
579
			'raw'          => false,
580
			'before_price' => '',
581
			'after_price'  => '',
582
		), $atts );
583
584
		$atts = wp_parse_args( $atts, $defaults );
585
586
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
587
			$product_data = wc_get_product( $atts['id'] );
588
		} elseif ( ! empty( $atts['sku'] ) ) {
589
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
590
			$product_data = get_post( $product_id );
591
		} else {
592
			$product_data = wc_get_product( $post->ID );
593
		}
594
595
		// Check that the product type is supported. Return blank if not supported.
596
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
597
			return '';
598
		}
599
600
		ob_start();
601
602
		// Get Subscription Sign Up Fee
603
		$sign_up_fee = WC_Subscriptions_Product::get_sign_up_fee( $product_data );
604
605
		// If the sign up fee is empty, look for alternative.
606
		if ( empty( $sign_up_fee ) ) {
607
			$scheme = self::get_satt_lowest_scheme_data( $product_data );
608
609
			if ( !empty( $scheme ) && is_array( $scheme ) ) {
610
				$sign_up_fee = $scheme['scheme']['subscription_sign_up_fee'];
611
			}
612
		}
613
614
		if ( ! $atts['raw'] ) {
615
			// Convert number into a price tag.
616
			if ( is_numeric( $sign_up_fee ) ) {
617
				$sign_up_fee = wc_price( $sign_up_fee );
618
			}
619
620
			// Clean the price tag.
621
			$sign_up_fee = self::clean_wc_price( $sign_up_fee );
622
623
			$price_html = sprintf( __( '%s%s%s', WCSS::TEXT_DOMAIN ), $atts['before_price'], $sign_up_fee, $atts['after_price'] );
624
625
			$sign_up_fee = html_entity_decode( $price_html );
626
		}
627
628
		echo $sign_up_fee;
629
630
		return ob_get_clean();
631
	} // END get_subscription_sign_up_fee()
632
633
	/**
634
	 * Displays the subscription trial details of the subscription product.
635
	 *
636
	 * @param  array $atts
637
	 * @return string
638
	 */
639
	public static function get_subscription_trial_string( $atts ) {
640
		global $wpdb, $post;
641
642
		$defaults = shortcode_atts( array(
643
			'id'  => '',
644
			'sku' => '',
645
		), $atts );
646
647
		$atts = wp_parse_args( $atts, $defaults );
648
649
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
650
			$product_data = wc_get_product( $atts['id'] );
651
		} elseif ( ! empty( $atts['sku'] ) ) {
652
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
653
			$product_data = get_post( $product_id );
654
		} else {
655
			$product_data = wc_get_product( $post->ID );
656
		}
657
658
		// Check that the product type is supported. Return blank if not supported.
659
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
660
			return '';
661
		}
662
663
		ob_start();
664
665
		// Get Subscription Trial Length
666
		$trial_length = self::get_subscription_trial_length( array( 'id' => $product_data->id ) );
667
668
		// Get Subscription Trial Period
669
		$trial_period = self::get_subscription_trial_period( array( 'id' => $product_data->id, 'raw' => true ) );
670
671
		if ( ! empty( $trial_length ) && $trial_length > 0 ) {
672
673
			switch ( $trial_period ) {
674
				case 'day':
675
					echo sprintf( _n( '%s day', '%s days', $trial_length, WCSS::TEXT_DOMAIN ), $trial_length );
676
					break;
677
678
				case 'week':
679
					echo sprintf( _n( '%s week', '%s weeks', $trial_length, WCSS::TEXT_DOMAIN ), $trial_length );
680
					break;
681
682
				case 'month':
683
					echo sprintf( _n( '%s month', '%s months', $trial_length, WCSS::TEXT_DOMAIN ), $trial_length );
684
					break;
685
686
				case 'year':
687
					echo sprintf( _n( '%s year', '%s years', $trial_length, WCSS::TEXT_DOMAIN ), $trial_length );
688
					break;
689
			}
690
691
		}
692
693
		return ob_get_clean();
694
	} // END get_subscription_trial_string()
695
696
	/**
697
	 * Displays the subscription trial length of the subscription product.
698
	 *
699
	 * @param  array $atts
700
	 * @return string
701
	 */
702 View Code Duplication
	public static function get_subscription_trial_length( $atts ) {
703
		global $wpdb, $post;
704
705
		$defaults = shortcode_atts( array(
706
			'id'  => '',
707
			'sku' => '',
708
		), $atts );
709
710
		$atts = wp_parse_args( $atts, $defaults );
711
712
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
713
			$product_data = wc_get_product( $atts['id'] );
714
		} elseif ( ! empty( $atts['sku'] ) ) {
715
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
716
			$product_data = get_post( $product_id );
717
		} else {
718
			$product_data = wc_get_product( $post->ID );
719
		}
720
721
		// Check that the product type is supported. Return blank if not supported.
722
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
723
			return '';
724
		}
725
726
		ob_start();
727
728
		// Get Subscription Trial Length
729
		$trial_length = WC_Subscriptions_Product::get_trial_length( $product_data );
730
731
		// If the trial length is empty, look for alternative.
732
		if ( empty( $trial_length ) ) {
733
			$scheme = self::get_satt_lowest_scheme_data( $product_data );
734
735
			if ( !empty( $scheme ) && is_array( $scheme ) ) {
736
				$trial_length = $scheme['scheme']['subscription_trial_length'];
737
			}
738
		}
739
740
		echo $trial_length;
741
742
		return ob_get_clean();
743
	} // END get_subscription_trial_length()
744
745
	/**
746
	 * Displays the subscription trial period of the subscription product.
747
	 *
748
	 * @param  array $atts
749
	 * @return string
750
	 */
751
	public static function get_subscription_trial_period( $atts ) {
752
		global $wpdb, $post;
753
754
		$defaults = shortcode_atts( array(
755
			'id'  => '',
756
			'sku' => '',
757
			'raw' => false,
758
		), $atts );
759
760
		$atts = wp_parse_args( $atts, $defaults );
761
762
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
763
			$product_data = wc_get_product( $atts['id'] );
764
		} elseif ( ! empty( $atts['sku'] ) ) {
765
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
766
			$product_data = get_post( $product_id );
767
		} else {
768
			$product_data = wc_get_product( $post->ID );
769
		}
770
771
		// Check that the product type is supported. Return blank if not supported.
772
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
773
			return '';
774
		}
775
776
		ob_start();
777
778
		// Get Subscription Trial Length
779
		$trial_length = self::get_subscription_trial_length( array( 'id' => $product_data->id ) );
780
781
		// Get Subscription Trial Period
782
		$trial_period = WC_Subscriptions_Product::get_trial_period( $product_data );
783
784
		// If the trial length is empty or is not zero, look for alternative.
785
		if ( empty( $trial_length ) || $trial_length != 0 ) {
786
			$scheme = self::get_satt_lowest_scheme_data( $product_data );
787
788 View Code Duplication
			if ( !empty( $scheme ) && is_array( $scheme ) ) {
789
				$trial_length = $scheme['scheme']['subscription_trial_length'];
790
				$trial_period = $scheme['scheme']['subscription_trial_period'];
791
			}
792
		}
793
794
		if ( ! empty( $trial_length ) && $trial_length > 0 ) {
795
796
			if ( ! $atts['raw'] ) {
797
				$trial_period = ucfirst($trial_period);
798
			}
799
800
		}
801
802
		echo $trial_period;
803
804
		return ob_get_clean();
805
	} // END get_subscription_trial_period()
806
807
	/**
808
	 * Displays the date and/or time of the first payment of the subscription.
809
	 *
810
	 * @param  array $atts
811
	 * @return string
812
	 */
813
	public static function get_subscription_first_payment( $atts ) {
814
		global $wpdb, $post;
815
816
		$defaults = shortcode_atts( array(
817
			'id'        => '',
818
			'sku'       => '',
819
			'show_time' => false,
820
			'from_date' => '',
821
			'timezone'  => 'gmt',
822
			'format'    => 'timestamp'
823
		), $atts );
824
825
		$atts = wp_parse_args( $atts, $defaults );
826
827
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
828
			$product_data = wc_get_product( $atts['id'] );
829
		} elseif ( ! empty( $atts['sku'] ) ) {
830
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
831
			$product_data = get_post( $product_id );
832
		} else {
833
			$product_data = wc_get_product( $post->ID );
834
		}
835
836
		// Check that the product type is supported. Return blank if not supported.
837
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
838
			return '';
839
		}
840
841
		ob_start();
842
843
		$billing_interval = self::get_subscription_period_interval( array( 'id' => $product_data->id ) );
844
		$billing_length   = self::get_subscription_length( array( 'id' => $product_data->id, 'raw' => true ) );
845
		$trial_length     = self::get_subscription_trial_length( array( 'id' => $product_data->id ) );
846
847
		$from_date = $atts['from_date'];
848
849
		if ( $billing_interval !== $billing_length || $trial_length > 0 ) {
850
			if ( empty( $from_date ) ) {
851
				$from_date = gmdate( 'Y-m-d H:i:s' );
852
			}
853
854
			// If the subscription has a free trial period, the first renewal is the same as the expiration of the free trial.
855
			if ( $trial_length > 0 ) {
856
				$first_renewal_timestamp = strtotime( self::get_trial_expiration_date( $product_data->id, $from_date ) );
857
			} else {
858
				$from_timestamp = strtotime( $from_date );
859
				$billing_period = self::get_subscription_period( array( 'id' => $product_data->id, 'raw' => true ) );
860
861
				if ( 'month' == $billing_period ) {
862
					$first_renewal_timestamp = wcs_add_months( $from_timestamp, $billing_interval );
863
				} else {
864
					$first_renewal_timestamp = strtotime( "+ $billing_interval {$billing_period}s", $from_timestamp );
865
				}
866
867
				if ( 'site' == $atts['timezone'] ) {
868
					$first_renewal_timestamp += ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
869
				}
870
			}
871
		} else {
872
			$first_renewal_timestamp = 0;
873
		}
874
875
		$date_format = ''; // Will be overridden later on.
876
877
		if ( $first_renewal_timestamp > 0 ) {
878
			if ( $atts['show_time'] ) {
879 View Code Duplication
				if ( 'timestamp' == $atts['format'] ) {
880
					$date_format = 'Y-m-d H:i:s';
881
				} else if ( 'string' == $atts['format'] ) {
882
					$date_format = 'D jS F Y H:i A';
883
				}
884 View Code Duplication
			} else {
885
				if ( 'timestamp' == $atts['format'] ) {
886
					$date_format = 'Y-m-d';
887
				} else if ( 'string' == $atts['format'] ) {
888
					$date_format = 'D jS F Y';
889
				}
890
			}
891
892
			$date_format = apply_filters( 'wcss_first_payment_date_format', $date_format, $atts );
893
894
			$first_payment = date( $date_format, $first_renewal_timestamp );
895
		} else {
896
			$first_payment = '';
897
		}
898
899
		echo $first_payment;
900
901
		return ob_get_clean();
902
	} // END get_subscription_first_payment()
903
904
	/**
905
	 * Displays the price of the initial payment of the subscription.
906
	 *
907
	 * @param  array $atts
908
	 * @return string
909
	 */
910
	public static function get_subscription_initial( $atts ) {
911
		global $wpdb, $post;
912
913
		$defaults = shortcode_atts( array(
914
			'id'        => '',
915
			'sku'       => '',
916
			'total'     => false
917
		), $atts );
918
919
		$atts = wp_parse_args( $atts, $defaults );
920
921
		if ( ! empty( $atts['id'] ) && $atts['id'] > 0 ) {
922
			$product_data = wc_get_product( $atts['id'] );
923
		} elseif ( ! empty( $atts['sku'] ) ) {
924
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
925
			$product_data = get_post( $product_id );
926
		} else {
927
			$product_data = wc_get_product( $post->ID );
928
		}
929
930
		// Check that the product type is supported. Return blank if not supported.
931
		if ( ! is_object( $product_data ) || ! in_array( $product_data->product_type, self::get_supported_product_types() ) ) {
932
			return '';
933
		}
934
935
		ob_start();
936
937
		// Subscription Active Price
938
		$initial_payment = self::get_subscription_price_meta( array( 'id' => $product_data->id, 'meta' => 'active' ) );
939
940
		// Free Trial ?
941
		$trial_length = self::get_subscription_trial_length( array( 'id' => $product_data->id ) );
942
943
		// If there is a free trial then the initial payment is Zero.
944
		if ( $trial_length > 0 ) {
945
			$initial_payment = 0;
946
		}
947
948
		// Sign up fee ?
949
		$sign_up_fee = self::get_subscription_sign_up_fee( array( 'id' => $product_data->id, 'raw' => true ) );
950
951
		// Apply the sign up fee if it exists.
952
		if ( !empty( $sign_up_fee ) && $sign_up_fee > 0 ) {
953
954
			if ( ! $atts['total'] ) {
955
				$initial_payment = sprintf( __( '%s with a %s sign up fee.', WCSS::TEXT_DOMAIN ), wc_price( $initial_payment ), wc_price( $sign_up_fee ) );
956
			} else {
957
				$initial_payment = round( ( double ) $initial_payment+$sign_up_fee, wc_get_price_decimals() );
958
			}
959
960
		}
961
962
		// Convert number into a price tag.
963
		if ( is_numeric( $initial_payment ) ) {
964
			$initial_payment = wc_price( $initial_payment );
965
		}
966
967
		// Clean the price tag.
968
		$initial_payment = self::clean_wc_price( $initial_payment );
969
970
		echo $initial_payment;
971
972
		return ob_get_clean();
973
	} // END get_subscription_initial()
974
975
	/**
976
	 * Adds the product types supported from the WooCommerce extension "Subscribe to All the Things".
977
	 *
978
	 * @param  $product_types
979
	 * @return array
980
	 */
981
	public static function support_product_types_for_wc_satt( $product_types ) {
982
		// Only add the product types from the WooCommerce extension "Subscribe to All the Things" if it is active.
983
		if ( class_exists( 'WCS_ATT' ) ) {
984
			$satt_product_types = WCS_ATT()->get_supported_product_types();
985
			$product_types = array_merge( $satt_product_types, $product_types );
986
		}
987
988
		return $product_types;
989
	} // support_product_types_for_wc_satt()
990
991
	/**
992
	 * This function returns the formatted price tag clean without
993
	 * WooCommerce price span wrapper which was added in version 2.6
994
	 *
995
	 * @param  string $price
996
	 * @global $woocommerce
997
	 * @return string
998
	 */
999
	public static function clean_wc_price( $price ) {
1000
		global $woocommerce;
1001
1002
		if ( version_compare( $woocommerce->version, '2.6.0' ) >= 0 ) {
1003
1004
			$find = array(
1005
				'<span class="woocommerce-Price-amount amount">', 
1006
				'<span class="woocommerce-Price-currencySymbol">', 
1007
				'</span>'
1008
			);
1009
1010
			foreach( $find as $remove ) {
1011
				$price = str_replace( $remove, '', $price );
1012
			}
1013
1014
		}
1015
1016
		return $price;
1017
	} // END clean_wc_price
1018
1019
	/**
1020
	 * Takes a subscription product's ID and returns the date on which the subscription trial will expire,
1021
	 * based on the subscription's trial length and calculated from either the $from_date if specified,
1022
	 * or the current date/time.
1023
	 *
1024
	 * @param int $product_id The product/post ID of the subscription
1025
	 * @param mixed $from_date A MySQL formatted date/time string from which to calculate the expiration date (in UTC timezone), or empty (default), which will use today's date/time (in UTC timezone).
1026
	 */
1027
	public static function get_trial_expiration_date( $product_id, $from_date = '' ) {
1028
		$trial_expiration_date = WC_Subscriptions_Product::get_trial_expiration_date( $product_id, $from_date );
1029
1030
		// If returned empty then try alternative.
1031
		if ( empty( $trial_expiration_date ) ) {
1032
1033
			$trial_period = self::get_subscription_trial_period( array( 'id' => $product_id, 'raw' => true ) );
1034
			$trial_length = self::get_subscription_trial_length( array( 'id' => $product_id ) );
1035
1036
			if ( $trial_length > 0 ) {
1037
1038
				if ( empty( $from_date ) ) {
1039
					$from_date = gmdate( 'Y-m-d H:i:s' );
1040
				}
1041
1042
				if ( 'month' == $trial_period ) {
1043
					$trial_expiration_date = date( 'Y-m-d H:i:s', wcs_add_months( strtotime( $from_date ), $trial_length ) );
1044
				} else { // Safe to just add the billing periods
1045
					$trial_expiration_date = date( 'Y-m-d H:i:s', strtotime( "+ {$trial_length} {$trial_period}s", strtotime( $from_date ) ) );
1046
				}
1047
1048
			} else {
1049
				$trial_expiration_date = 0;
1050
			}
1051
1052
		}
1053
1054
		return $trial_expiration_date;
1055
	} // END get_trial_expiration_date()
1056
1057
} // END WCSS_Shortcodes
1058
1059
WCSS_Shortcodes::init();