Issues (850)

Security Analysis    4 potential vulnerabilities

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

  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.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  Code Injection (1)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection (2)
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  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.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  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.
  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.
  Cross-Site Scripting (1)
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.
  Header Injection
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/admin/subscriptions.php (11 issues)

1
<?php
2
/**
3
 * Contains functions that display the subscriptions admin page.
4
 */
5
6
defined( 'ABSPATH' ) || exit;
7
8
/**
9
 * Render the Subscriptions page
10
 *
11
 * @access      public
12
 * @since       1.0.0
13
 * @return      void
14
 */
15
function wpinv_subscriptions_page() {
16
17
	?>
18
19
	<div class="wrap">
20
		<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
21
		<div class="bsui">
22
23
			<?php
24
25
				// Verify user permissions.
26
				if ( ! wpinv_current_user_can_manage_invoicing() ) {
27
28
				aui()->alert(
29
                    array(
30
						'type'    => 'danger',
31
						'content' => __( 'You are not permitted to view this page.', 'invoicing' ),
32
					),
33
					true
34
                );
35
36
				} elseif ( ! empty( $_GET['id'] ) && is_numeric( $_GET['id'] ) ) {
37
38
				// Display a single subscription.
39
				wpinv_recurring_subscription_details();
40
				} else {
41
42
				// Display a list of available subscriptions.
43
				getpaid_print_subscriptions_list();
44
				}
45
46
			?>
47
48
		</div>
49
	</div>
50
51
	<?php
52
}
53
54
/**
55
 * Render the Subscriptions table
56
 *
57
 * @access      public
58
 * @since       1.0.19
59
 * @return      void
60
 */
61
function getpaid_print_subscriptions_list() {
62
63
	$subscribers_table = new WPInv_Subscriptions_List_Table();
64
	$subscribers_table->prepare_items();
65
66
	?>
67
	<?php $subscribers_table->views(); ?>
68
	<form id="subscribers-filter" class="bsui" method="get">
69
		<input type="hidden" name="page" value="wpinv-subscriptions" />
70
		<?php $subscribers_table->search_box( __( 'Search Subscriptions', 'invoicing' ), 'getpaid-search-subscriptions' ); ?>
71
		<?php $subscribers_table->display(); ?>
72
	</form>
73
	<?php
74
}
75
76
/**
77
 * Render a single subscription.
78
 *
79
 * @access      public
80
 * @since       1.0.0
81
 * @return      void
82
 */
83
function wpinv_recurring_subscription_details() {
84
85
	// Fetch the subscription.
86
	$sub = new WPInv_Subscription( (int) $_GET['id'] );
87
	if ( ! $sub->exists() ) {
88
89
		aui()->alert(
90
			array(
91
				'type'    => 'danger',
92
				'content' => __( 'Subscription not found.', 'invoicing' ),
93
			),
94
			true
95
		);
96
97
		return;
98
	}
99
100
	// Use metaboxes to display the subscription details.
101
	add_meta_box( 'getpaid_admin_subscription_details_metabox', __( 'Subscription Details', 'invoicing' ), 'getpaid_admin_subscription_details_metabox', get_current_screen(), 'normal', 'high' );
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
102
	add_meta_box( 'getpaid_admin_subscription_update_metabox', __( 'Change Status', 'invoicing' ), 'getpaid_admin_subscription_update_metabox', get_current_screen(), 'side' );
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
103
104
	$subscription_id     = $sub->get_id();
105
	$subscription_groups = getpaid_get_invoice_subscription_groups( $sub->get_parent_invoice_id() );
106
	$subscription_group  = wp_list_filter( $subscription_groups, compact( 'subscription_id' ) );
107
108
	if ( 1 < count( $subscription_groups ) ) {
109
		add_meta_box( 'getpaid_admin_subscription_related_subscriptions_metabox', __( 'Related Subscriptions', 'invoicing' ), 'getpaid_admin_subscription_related_subscriptions_metabox', get_current_screen(), 'advanced' );
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
110
	}
111
112
	if ( ! empty( $subscription_group ) ) {
113
		add_meta_box( 'getpaid_admin_subscription_item_details_metabox', __( 'Subscription Items', 'invoicing' ), 'getpaid_admin_subscription_item_details_metabox', get_current_screen(), 'normal', 'low' );
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
114
	}
115
116
	add_meta_box( 'getpaid_admin_subscription_invoice_details_metabox', __( 'Related Invoices', 'invoicing' ), 'getpaid_admin_subscription_invoice_details_metabox', get_current_screen(), 'advanced' );
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
117
118
	do_action( 'getpaid_admin_single_subscription_register_metabox', $sub );
119
120
	?>
121
122
		<form method="post" action="<?php echo esc_url( admin_url( 'admin.php?page=wpinv-subscriptions&id=' . absint( $sub->get_id() ) ) ); ?>">
123
124
			<?php wp_nonce_field( 'getpaid-nonce', 'getpaid-nonce' ); ?>
125
			<?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
126
			<?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
127
			<input type="hidden" name="getpaid-admin-action" value="update_single_subscription" />
128
			<input type="hidden" name="subscription_id" value="<?php echo (int) $sub->get_id(); ?>" />
129
130
			<div id="poststuff">
131
				<div id="post-body" class="metabox-holder columns-<?php echo 1 == get_current_screen()->get_columns() ? '1' : '2'; ?>">
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
132
133
					<div id="postbox-container-1" class="postbox-container">
134
						<?php do_meta_boxes( get_current_screen(), 'side', $sub ); ?>
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
135
					</div>
136
137
					<div id="postbox-container-2" class="postbox-container">
138
						<?php do_meta_boxes( get_current_screen(), 'normal', $sub ); ?>
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
139
						<?php do_meta_boxes( get_current_screen(), 'advanced', $sub ); ?>
0 ignored issues
show
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
140
					</div>
141
142
				</div>
143
			</div>
144
145
		</form>
146
147
		<script>jQuery(document).ready(function(){ postboxes.add_postbox_toggles('getpaid_page_wpinv-subscriptions'); });</script>
148
149
	<?php
150
151
}
152
153
/**
154
 * Displays the subscription details metabox.
155
 *
156
 * @param WPInv_Subscription $sub
157
 */
158
function getpaid_admin_subscription_details_metabox( $sub ) {
159
160
	// Subscription items.
161
	$subscription_group = getpaid_get_invoice_subscription_group( $sub->get_parent_invoice_id(), $sub->get_id() );
162
	$items_count        = empty( $subscription_group ) ? 1 : count( $subscription_group['items'] );
163
164
	// Prepare subscription detail columns.
165
	$fields = apply_filters(
166
		'getpaid_subscription_admin_page_fields',
167
		array(
168
			'subscription' => __( 'Subscription', 'invoicing' ),
169
			'customer'     => __( 'Customer', 'invoicing' ),
170
			'amount'       => __( 'Amount', 'invoicing' ),
171
			'start_date'   => __( 'Start Date', 'invoicing' ),
172
			'renews_on'    => __( 'Next Payment', 'invoicing' ),
173
			'renewals'     => __( 'Collected Payments', 'invoicing' ),
174
			'item'         => $items_count > 1 ? __( 'Items', 'invoicing' ) : __( 'Item', 'invoicing' ),
175
			'gateway'      => __( 'Payment Method', 'invoicing' ),
176
			'profile_id'   => __( 'Profile ID', 'invoicing' ),
177
			'status'       => __( 'Status', 'invoicing' ),
178
		)
179
	);
180
181
	if ( ! $sub->is_active() ) {
182
183
		if ( isset( $fields['renews_on'] ) ) {
184
			unset( $fields['renews_on'] );
185
		}
186
187
		if ( isset( $fields['gateway'] ) ) {
188
			unset( $fields['gateway'] );
189
		}
190
	} elseif ( $sub->is_last_renewal() ) {
191
192
		if ( isset( $fields['renews_on'] ) ) {
193
			$fields['renews_on'] = __( 'End Date', 'invoicing' );
194
		}
195
	}
196
197
	$profile_id = $sub->get_profile_id();
198
	if ( empty( $profile_id ) && isset( $fields['profile_id'] ) ) {
199
		unset( $fields['profile_id'] );
200
	}
201
202
	?>
203
204
		<table class="table table-borderless" style="font-size: 14px;">
205
			<tbody>
206
207
				<?php foreach ( $fields as $key => $label ) : ?>
208
209
					<tr class="getpaid-subscription-meta-<?php echo esc_attr( $key ); ?>">
210
211
						<th class="w-25" style="font-weight: 500;">
212
							<?php echo esc_html( $label ); ?>
213
						</th>
214
215
						<td class="w-75 text-muted">
216
							<?php do_action( 'getpaid_subscription_admin_display_' . sanitize_key( $key ), $sub, $subscription_group ); ?>
217
						</td>
218
219
					</tr>
220
221
				<?php endforeach; ?>
222
223
			</tbody>
224
		</table>
225
226
	<?php
227
}
228
229
/**
230
 * Displays the subscription customer.
231
 *
232
 * @param WPInv_Subscription $subscription
233
 */
234
function getpaid_admin_subscription_metabox_display_customer( $subscription ) {
235
236
	$username = __( '(Missing User)', 'invoicing' );
237
238
	$user = get_userdata( $subscription->get_customer_id() );
239
	if ( $user ) {
240
241
		$username = sprintf(
242
			'<a href="user-edit.php?user_id=%s">%s</a>',
243
			absint( $user->ID ),
244
			! empty( $user->display_name ) ? esc_html( $user->display_name ) : sanitize_email( $user->user_email )
245
		);
246
247
	}
248
249
	echo wp_kses_post( $username );
250
}
251
add_action( 'getpaid_subscription_admin_display_customer', 'getpaid_admin_subscription_metabox_display_customer' );
252
253
/**
254
 * Displays the subscription amount.
255
 *
256
 * @param WPInv_Subscription $subscription
257
 */
258
function getpaid_admin_subscription_metabox_display_amount( $subscription ) {
259
	$amount    = getpaid_get_formatted_subscription_amount( $subscription );
260
	echo wp_kses_post( "<span>$amount</span>" );
261
}
262
add_action( 'getpaid_subscription_admin_display_amount', 'getpaid_admin_subscription_metabox_display_amount' );
263
264
/**
265
 * Displays the subscription id.
266
 *
267
 * @param WPInv_Subscription $subscription
268
 */
269
function getpaid_admin_subscription_metabox_display_id( $subscription ) {
270
271
	printf(
272
		'<a href="%s">#%s</a>',
273
		esc_url( admin_url( 'admin.php?page=wpinv-subscriptions&id=' . absint( $subscription->get_id() ) ) ),
274
		absint( $subscription->get_id() )
275
	);
276
277
}
278
add_action( 'getpaid_subscription_admin_display_subscription', 'getpaid_admin_subscription_metabox_display_id' );
279
280
/**
281
 * Displays the subscription renewal date.
282
 *
283
 * @param WPInv_Subscription $subscription
284
 */
285
function getpaid_admin_subscription_metabox_display_start_date( $subscription ) {
286
287
	if ( $subscription->has_status( 'active trialling' ) && getpaid_payment_gateway_supports( $subscription->get_gateway(), 'subscription_date_change' ) ) {
288
		aui()->input(
289
			array(
290
				'type'        => 'text',
291
				'id'          => 'wpinv_subscription_date_created',
292
				'name'        => 'wpinv_subscription_date_created',
293
				'label'       => __( 'Start Date', 'invoicing' ),
294
				'label_type'  => 'hidden',
295
				'placeholder' => 'YYYY-MM-DD',
296
				'value'       => esc_attr( $subscription->get_date_created( 'edit' ) ),
297
				'no_wrap'     => true,
298
				'size'        => 'sm',
299
			),
300
			true
301
		);
302
	} else {
303
		echo esc_html( getpaid_format_date_value( $subscription->get_date_created() ) );
304
	}
305
306
}
307
add_action( 'getpaid_subscription_admin_display_start_date', 'getpaid_admin_subscription_metabox_display_start_date' );
308
309
/**
310
 * Displays the subscription renewal date.
311
 *
312
 * @param WPInv_Subscription $subscription
313
 */
314
function getpaid_admin_subscription_metabox_display_renews_on( $subscription ) {
315
316
	if ( $subscription->has_status( 'active trialling' ) && getpaid_payment_gateway_supports( $subscription->get_gateway(), 'subscription_date_change' ) ) {
317
		aui()->input(
318
			array(
319
				'type'        => 'text',
320
				'id'          => 'wpinv_subscription_expiration',
321
				'name'        => 'wpinv_subscription_expiration',
322
				'label'       => __( 'Renews On', 'invoicing' ),
323
				'label_type'  => 'hidden',
324
				'placeholder' => 'YYYY-MM-DD',
325
				'value'       => esc_attr( $subscription->get_expiration( 'edit' ) ),
326
				'no_wrap'     => true,
327
				'size'        => 'sm',
328
			),
329
			true
330
		);
331
	} else {
332
		echo esc_html( getpaid_format_date_value( $subscription->get_expiration() ) );
333
	}
334
}
335
add_action( 'getpaid_subscription_admin_display_renews_on', 'getpaid_admin_subscription_metabox_display_renews_on' );
336
337
/**
338
 * Displays the subscription renewal count.
339
 *
340
 * @param WPInv_Subscription $subscription
341
 */
342
function getpaid_admin_subscription_metabox_display_renewals( $subscription ) {
343
344
	$max_bills    = $subscription->get_bill_times();
345
	$times_billed = (int) $subscription->get_times_billed();
346
347
	if ( $subscription->has_status( 'active trialling' ) && getpaid_payment_gateway_supports( $subscription->get_gateway(), 'subscription_bill_times_change' ) ) {
348
		aui()->input(
349
			array(
350
				'type'             => 'number',
351
				'id'               => 'wpinv_subscription_max_bill_times',
352
				'name'             => 'wpinv_subscription_max_bill_times',
353
				'label'            => __( 'Maximum bill times', 'invoicing' ),
354
				'label_type'       => 'hidden',
355
				'placeholder'      => __( 'Unlimited', 'invoicing' ),
356
				'value'            => empty( $max_bills ) ? '' : (int) $max_bills,
357
				'no_wrap'          => true,
358
				'size'             => 'sm',
359
				'input_group_left' => sprintf(
360
					// translators: %d: Number of times billed
361
					__( '%d of', 'invoicing' ),
362
					$times_billed
363
				),
364
			),
365
			true
366
		);
367
	} else {
368
		echo esc_html( $times_billed ) . ' / ' . ( empty( $max_bills ) ? '&infin;' : (int) $max_bills );
369
	}
370
}
371
add_action( 'getpaid_subscription_admin_display_renewals', 'getpaid_admin_subscription_metabox_display_renewals' );
372
373
/**
374
 * Displays the subscription item.
375
 *
376
 * @param WPInv_Subscription $subscription
377
 * @param false|array $subscription_group
378
 */
379
function getpaid_admin_subscription_metabox_display_item( $subscription, $subscription_group = false ) {
380
381
	if ( empty( $subscription_group ) ) {
382
		echo wp_kses_post( WPInv_Subscriptions_List_Table::generate_item_markup( $subscription->get_product_id() ) );
383
		return;
384
	}
385
386
	$markup = array_map( array( 'WPInv_Subscriptions_List_Table', 'generate_item_markup' ), array_keys( $subscription_group['items'] ) );
387
	echo wp_kses_post( implode( ' | ', $markup ) );
388
389
}
390
add_action( 'getpaid_subscription_admin_display_item', 'getpaid_admin_subscription_metabox_display_item', 10, 2 );
391
392
/**
393
 * Displays the subscription gateway.
394
 *
395
 * @param WPInv_Subscription $subscription
396
 */
397
function getpaid_admin_subscription_metabox_display_gateway( $subscription ) {
398
399
	$gateway = $subscription->get_gateway();
400
401
	if ( ! empty( $gateway ) ) {
402
		echo esc_html( wpinv_get_gateway_admin_label( $gateway ) );
403
	} else {
404
		echo '&mdash;';
405
	}
406
407
}
408
add_action( 'getpaid_subscription_admin_display_gateway', 'getpaid_admin_subscription_metabox_display_gateway' );
409
410
/**
411
 * Displays the subscription status.
412
 *
413
 * @param WPInv_Subscription $subscription
414
 */
415
function getpaid_admin_subscription_metabox_display_status( $subscription ) {
416
	echo wp_kses_post( $subscription->get_status_label_html() );
417
}
418
add_action( 'getpaid_subscription_admin_display_status', 'getpaid_admin_subscription_metabox_display_status' );
419
420
/**
421
 * Displays the subscription profile id.
422
 *
423
 * @param WPInv_Subscription $subscription
424
 */
425
function getpaid_admin_subscription_metabox_display_profile_id( $subscription ) {
426
427
	$profile_id = $subscription->get_profile_id();
428
429
	aui()->input(
430
		array(
431
			'type'              => 'text',
432
			'id'                => 'wpinv_subscription_profile_id',
433
			'name'              => 'wpinv_subscription_profile_id',
434
			'label'             => __( 'Profile Id', 'invoicing' ),
435
			'label_type'        => 'hidden',
436
			'placeholder'       => __( 'Profile Id', 'invoicing' ),
437
			'value'             => esc_attr( $profile_id ),
438
			'input_group_right' => '',
439
			'no_wrap'           => true,
440
			'size'              => 'sm',
441
		),
442
		true
443
	);
444
445
	$url = apply_filters( 'getpaid_remote_subscription_profile_url', '', $subscription );
446
	if ( ! empty( $url ) ) {
447
		echo '&nbsp;<a href="' . esc_url_raw( $url ) . '" title="' . esc_attr__( 'View in Gateway', 'invoicing' ) . '" target="_blank"><i class="fas fa-external-link-alt fa-xs fa-fw align-top"></i></a>';
448
	}
449
450
}
451
add_action( 'getpaid_subscription_admin_display_profile_id', 'getpaid_admin_subscription_metabox_display_profile_id' );
452
453
/**
454
 * Displays the subscriptions update metabox.
455
 *
456
 * @param WPInv_Subscription $subscription
457
 */
458
function getpaid_admin_subscription_update_metabox( $subscription ) {
459
	global $aui_bs5;
460
461
	?>
462
	<div class="mt-3">
463
		<?php
464
			aui()->select(
465
				array(
466
					'options'   => getpaid_get_subscription_statuses(),
467
					'name'      => 'subscription_status',
468
					'id'        => 'subscription_status_update_select',
469
					'required'  => true,
470
					'no_wrap'   => false,
471
					'label'     => __( 'Subscription Status', 'invoicing' ),
472
					'help_text' => __( 'Updating the status will trigger related actions and hooks', 'invoicing' ),
473
					'select2'   => true,
474
					'value'     => $subscription->get_status( 'edit' ),
475
				),
476
				true
477
			);
478
		?>
479
480
		<div class="mt-2 px-3 py-2 bg-light border-top" style="margin:-12px">
481
		<?php
482
			submit_button( __( 'Update', 'invoicing' ), 'primary', 'submit', false );
483
484
			$url    = wp_nonce_url( add_query_arg( 'getpaid-admin-action', 'subscription_manual_renew' ), 'getpaid-nonce', 'getpaid-nonce' );
485
			$anchor = __( 'Renew Subscription', 'invoicing' );
486
			$title  = esc_attr__( 'Are you sure you want to extend the subscription and generate a new invoice that will be automatically marked as paid?', 'invoicing' );
487
488
			if ( $subscription->is_active() ) {
489
				echo "<a href='" . esc_url( $url ) . "' class='" . ( $aui_bs5 ? 'float-end' : 'float-right' ) . " button button-secondary' onclick='return confirm(\"" . esc_attr( $title ) . "\")' title='" . esc_attr__( 'Renew subscription manually', 'invoicing' ) . "'>" . esc_html( $anchor ) . "</a>";
490
			}
491
492
	echo '</div></div>';
493
}
494
495
/**
496
 * Displays the subscriptions invoices metabox.
497
 *
498
 * @param WPInv_Subscription $subscription
499
 * @param bool $strict Whether or not to skip invoices of sibling subscriptions
500
 */
501
function getpaid_admin_subscription_invoice_details_metabox( $subscription, $strict = true ) {
502
503
	$columns = apply_filters(
504
		'getpaid_subscription_related_invoices_columns',
505
		array(
506
			'invoice'      => __( 'Invoice', 'invoicing' ),
507
			'relationship' => __( 'Relationship', 'invoicing' ),
508
			'date'         => __( 'Date', 'invoicing' ),
509
			'status'       => __( 'Status', 'invoicing' ),
510
			'total'        => __( 'Total', 'invoicing' ),
511
		),
512
		$subscription
513
	);
514
515
	// Prepare the invoices.
516
	$payments = $subscription->get_child_payments( ! is_admin() );
517
	$parent   = $subscription->get_parent_invoice();
518
519
	if ( $parent->exists() ) {
520
		$payments = array_merge( array( $parent ), $payments );
521
	}
522
523
	$table_class = 'w-100 bg-white';
524
525
	if ( ! is_admin() ) {
526
		$table_class = 'table table-bordered';
527
	}
528
529
	?>
530
		<div class="m-0" style="overflow: auto;">
531
532
			<table class="<?php echo esc_attr( $table_class ); ?>">
533
534
				<thead>
535
					<tr>
536
						<?php
537
							foreach ( $columns as $key => $label ) {
538
							echo "<th class='subscription-invoice-field-" . esc_attr( $key ) . " bg-light p-2 text-left color-dark font-weight-bold'>" . esc_html( $label ) . "</th>";
539
							}
540
						?>
541
					</tr>
542
				</thead>
543
544
				<tbody>
545
546
					<?php if ( empty( $payments ) ) : ?>
547
						<tr>
548
							<td colspan="<?php echo count( $columns ); ?>" class="p-2 text-left text-muted">
549
								<?php esc_html_e( 'This subscription has no invoices.', 'invoicing' ); ?>
550
							</td>
551
						</tr>
552
					<?php endif; ?>
553
554
					<?php
555
556
						foreach ( $payments as $payment ) :
557
558
						// Ensure that we have an invoice.
559
						$payment = new WPInv_Invoice( $payment );
560
561
						// Abort if the invoice is invalid...
562
						if ( ! $payment->exists() ) {
563
							continue;
564
							}
565
566
						// ... or belongs to a different subscription.
567
						if ( $strict && $payment->is_renewal() && $payment->get_subscription_id() && $payment->get_subscription_id() != $subscription->get_id() ) {
568
							continue;
569
							}
570
571
						echo '<tr>';
572
573
						foreach ( array_keys( $columns ) as $key ) {
574
575
							echo "<td class='p-2 text-left'>";
576
577
								switch ( $key ) {
578
579
								case 'total':
580
										echo '<strong>';
581
										wpinv_the_price( $payment->get_total(), $payment->get_currency() );
582
										echo '</strong>';
583
									break;
584
585
								case 'relationship':
586
										echo $payment->is_renewal() ? esc_html__( 'Renewal Invoice', 'invoicing' ) : esc_html__( 'Initial Invoice', 'invoicing' );
587
									break;
588
589
								case 'date':
590
									echo esc_html( getpaid_format_date_value( $payment->get_date_created() ) );
591
									break;
592
593
								case 'status':
594
										$status = $payment->get_status_nicename();
595
										if ( is_admin() ) {
596
										$status = $payment->get_status_label_html();
597
										}
598
599
										echo wp_kses_post( $status );
600
									break;
601
602
								case 'invoice':
603
										if ( ! is_admin() ) {
604
											$link = $payment->get_view_url();
605
										} else {
606
											$link = get_edit_post_link( $payment->get_id() );
607
										}
608
609
										$invoice = esc_html( $payment->get_number() );
610
611
										echo wp_kses_post( "<a href='" . ( $link ? esc_url( $link ) : '#' ) . "'>$invoice</a>" );
612
									break;
613
										}
614
615
								echo '</td>';
616
617
							}
618
619
						echo '</tr>';
620
621
						endforeach;
622
					?>
623
624
				</tbody>
625
626
			</table>
627
628
		</div>
629
630
	<?php
631
}
632
633
/**
634
 * Displays the subscriptions items metabox.
635
 *
636
 * @param WPInv_Subscription $subscription
637
 */
638
function getpaid_admin_subscription_item_details_metabox( $subscription ) {
639
640
	// Fetch the subscription group.
641
	$subscription_group = getpaid_get_invoice_subscription_group( $subscription->get_parent_payment_id(), $subscription->get_id() );
642
643
	if ( empty( $subscription_group ) || empty( $subscription_group['items'] ) ) {
644
		return;
645
	}
646
647
	// Prepare table columns.
648
	$columns = apply_filters(
649
		'getpaid_subscription_item_details_columns',
650
		array(
651
			'item_name' => __( 'Item', 'invoicing' ),
652
			'price'     => __( 'Price', 'invoicing' ),
653
			'tax'       => __( 'Tax', 'invoicing' ),
654
			'discount'  => __( 'Discount', 'invoicing' ),
655
			//'initial'      => __( 'Initial Amount', 'invoicing' ),
656
			'recurring' => __( 'Subtotal', 'invoicing' ),
657
		),
658
		$subscription
659
	);
660
661
	// Prepare the invoices.
662
663
	$invoice = $subscription->get_parent_invoice();
664
665
	if ( ( ! wpinv_use_taxes() || ! $invoice->is_taxable() ) && isset( $columns['tax'] ) ) {
666
		unset( $columns['tax'] );
667
	}
668
669
	$table_class = 'w-100 bg-white';
670
671
	if ( ! is_admin() ) {
672
		$table_class = 'table table-bordered';
673
	}
674
675
	?>
676
		<div class="m-0" style="overflow: auto;">
677
678
			<table class="<?php echo esc_attr( $table_class ); ?>">
679
680
				<thead>
681
					<tr>
682
						<?php
683
684
							foreach ( $columns as $key => $label ) {
685
							echo "<th class='subscription-item-field-" . esc_attr( $key ) . " bg-light p-2 text-left color-dark font-weight-bold'>" . esc_html( $label ) . "</th>";
686
							}
687
						?>
688
					</tr>
689
				</thead>
690
691
				<tbody>
692
693
					<?php
694
695
						foreach ( $subscription_group['items'] as $subscription_group_item ) :
696
697
						echo '<tr>';
698
699
						foreach ( array_keys( $columns ) as $key ) {
700
701
							$class = 'text-left';
702
703
							echo "<td class='p-2 text-left'>";
704
705
								switch ( $key ) {
706
707
								case 'item_name':
708
										$item_name = get_the_title( $subscription_group_item['item_id'] );
709
										$item_name = empty( $item_name ) ? $subscription_group_item['item_name'] : $item_name;
710
711
										if ( $invoice->get_template() == 'amount' || 1 == (float) $subscription_group_item['quantity'] ) {
712
										echo esc_html( $item_name );
713
										} else {
714
										printf( '%1$s x %2$d', esc_html( $item_name ), (float) $subscription_group_item['quantity'] );
715
											}
716
717
									break;
718
719
								case 'price':
720
									wpinv_the_price( $subscription_group_item['item_price'], $invoice->get_currency() );
721
									break;
722
723
								case 'tax':
724
									wpinv_the_price( $subscription_group_item['tax'], $invoice->get_currency() );
725
									break;
726
727
								case 'discount':
728
									wpinv_the_price( $subscription_group_item['discount'], $invoice->get_currency() );
729
									break;
730
731
								case 'initial':
732
									wpinv_the_price( $subscription_group_item['price'] * $subscription_group_item['quantity'], $invoice->get_currency() );
733
									break;
734
735
								case 'recurring':
736
										echo wp_kses_post( '<strong>' . wpinv_price( $subscription_group_item['price'] * $subscription_group_item['quantity'], $invoice->get_currency() ) . '</strong>' );
737
									break;
738
739
										}
740
741
								echo '</td>';
742
743
							}
744
745
						echo '</tr>';
746
747
						endforeach;
748
749
						foreach ( $subscription_group['fees'] as $subscription_group_fee ) :
750
751
						echo '<tr>';
752
753
						foreach ( array_keys( $columns ) as $key ) {
754
755
							$class = 'text-left';
0 ignored issues
show
The assignment to $class is dead and can be removed.
Loading history...
756
757
							echo "<td class='p-2 text-left'>";
758
759
								switch ( $key ) {
760
761
								case 'item_name':
762
										echo esc_html( $subscription_group_fee['name'] );
763
									break;
764
765
								case 'price':
766
									wpinv_the_price( $subscription_group_fee['initial_fee'], $invoice->get_currency() );
767
									break;
768
769
								case 'tax':
770
									echo '&mdash;';
771
									break;
772
773
								case 'discount':
774
										echo '&mdash;';
775
									break;
776
777
								case 'initial':
778
									wpinv_the_price( $subscription_group_fee['initial_fee'], $invoice->get_currency() );
779
									break;
780
781
								case 'recurring':
782
										echo wp_kses_post( '<strong>' . wpinv_price( $subscription_group_fee['recurring_fee'], $invoice->get_currency() ) . '</strong>' );
783
									break;
784
785
										}
786
787
								echo '</td>';
788
789
							}
790
791
						echo '</tr>';
792
793
						endforeach;
794
					?>
795
796
				</tbody>
797
798
			</table>
799
800
		</div>
801
802
	<?php
803
}
804
805
/**
806
 * Displays the related subscriptions metabox.
807
 *
808
 * @param WPInv_Subscription $subscription
809
 * @param bool $skip_current
810
 */
811
function getpaid_admin_subscription_related_subscriptions_metabox( $subscription, $skip_current = true ) {
812
813
	// Fetch the subscription groups.
814
	$subscription_groups = getpaid_get_invoice_subscription_groups( $subscription->get_parent_payment_id() );
815
816
	if ( empty( $subscription_groups ) ) {
817
		return;
818
	}
819
820
	// Prepare table columns.
821
	$columns = apply_filters(
822
		'getpaid_subscription_related_subscriptions_columns',
823
		array(
824
			'subscription' => __( 'Subscription', 'invoicing' ),
825
			'start_date'   => __( 'Start Date', 'invoicing' ),
826
			'renewal_date' => __( 'Next Payment', 'invoicing' ),
827
			'renewals'     => __( 'Payments', 'invoicing' ),
828
			'item'         => __( 'Items', 'invoicing' ),
829
			'status'       => __( 'Status', 'invoicing' ),
830
		),
831
		$subscription
832
	);
833
834
	if ( $subscription->get_status() == 'pending' ) {
835
		unset( $columns['start_date'], $columns['renewal_date'] );
836
	}
837
838
	$table_class = 'w-100 bg-white';
839
840
	if ( ! is_admin() ) {
841
		$table_class = 'table table-bordered';
842
	}
843
844
	?>
845
		<div class="m-0" style="overflow: auto;">
846
847
			<table class="<?php echo esc_attr( $table_class ); ?>">
848
849
				<thead>
850
					<tr>
851
						<?php
852
853
							foreach ( $columns as $key => $label ) {
854
							echo "<th class='related-subscription-field-" . esc_attr( $key ) . " bg-light p-2 text-left color-dark font-weight-bold'>" . esc_html( $label ) . "</th>";
855
							}
856
						?>
857
					</tr>
858
				</thead>
859
860
				<tbody>
861
862
					<?php
863
864
						foreach ( $subscription_groups as $subscription_group ) :
865
866
						// Do not list current subscription.
867
						if ( $skip_current && (int) $subscription_group['subscription_id'] === $subscription->get_id() ) {
868
							continue;
869
							}
870
871
						// Ensure the subscription exists.
872
						$_suscription = new WPInv_Subscription( $subscription_group['subscription_id'] );
873
874
						if ( ! $_suscription->exists() ) {
875
							continue;
876
							}
877
878
						echo '<tr>';
879
880
						foreach ( array_keys( $columns ) as $key ) {
881
882
							$class = 'text-left';
0 ignored issues
show
The assignment to $class is dead and can be removed.
Loading history...
883
884
							echo "<td class='p-2 text-left'>";
885
886
								switch ( $key ) {
887
888
								case 'status':
889
										echo wp_kses_post( $_suscription->get_status_label_html() );
890
									break;
891
892
								case 'item':
893
											$markup = array_map( array( 'WPInv_Subscriptions_List_Table', 'generate_item_markup' ), array_keys( $subscription_group['items'] ) );
894
											echo wp_kses_post( implode( ' | ', $markup ) );
895
									break;
896
897
								case 'renewals':
898
									$max_bills = $_suscription->get_bill_times();
899
									echo ( (int) $_suscription->get_times_billed() ) . ' / ' . ( empty( $max_bills ) ? '&infin;' : (int) $max_bills );
900
									break;
901
902
								case 'renewal_date':
903
										echo $_suscription->is_active() ? esc_html( getpaid_format_date_value( $_suscription->get_expiration() ) ) : '&mdash;';
904
									break;
905
906
								case 'start_date':
907
										echo 'pending' == $_suscription->get_status() ? '&mdash;' : esc_html( getpaid_format_date_value( $_suscription->get_date_created() ) );
908
									break;
909
910
								case 'subscription':
911
										$url = is_admin() ? admin_url( 'admin.php?page=wpinv-subscriptions&id=' . absint( $_suscription->get_id() ) ) : $_suscription->get_view_url();
912
										printf(
913
                                            '%1$s#%2$s%3$s',
914
                                            '<a href="' . esc_url( $url ) . '">',
915
                                            '<strong>' . intval( $_suscription->get_id() ) . '</strong>',
916
											'</a>'
917
                                        );
918
919
											echo wp_kses_post( WPInv_Subscriptions_List_Table::column_amount( $_suscription ) );
920
									break;
921
922
										}
923
924
								echo '</td>';
925
926
							}
927
928
						echo '</tr>';
929
930
						endforeach;
931
					?>
932
933
				</tbody>
934
935
			</table>
936
937
		</div>
938
939
	<?php
940
}
941