Issues (865)

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/register-settings.php (4 issues)

Labels
Severity
1
<?php
2
/**
3
 * Contains settings related functions
4
 *
5
 * @package Invoicing
6
 * @since   1.0.0
7
 */
8
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Retrieves all default settings.
13
 *
14
 * @return array
15
 */
16
function wpinv_get_settings() {
17
    $defaults = array();
18
19
    foreach ( array_values( wpinv_get_registered_settings() ) as $tab_settings ) {
20
21
        foreach ( array_values( $tab_settings ) as $section_settings ) {
22
23
            foreach ( $section_settings as $key => $setting ) {
24
                if ( isset( $setting['std'] ) ) {
25
                    $defaults[ $key ] = $setting['std'];
26
                }
27
            }
28
		}
29
	}
30
31
    return $defaults;
32
33
}
34
35
/**
36
 * Retrieves all settings.
37
 *
38
 * @return array
39
 */
40
function wpinv_get_options() {
41
    global $wpinv_options;
42
43
    // Try fetching the saved options.
44
    if ( empty( $wpinv_options ) ) {
45
        $wpinv_options = get_option( 'wpinv_settings' );
46
    }
47
48
    // If that fails, don't fetch the default settings to prevent a loop.
49
    if ( ! is_array( $wpinv_options ) ) {
50
        $wpinv_options = array();
51
    }
52
53
    return $wpinv_options;
54
}
55
56
/**
57
 * Retrieves a single setting.
58
 *
59
 * @param string $key the setting key.
60
 * @param mixed $default The default value to use if the setting has not been set.
61
 * @return mixed
62
 */
63
function wpinv_get_option( $key = '', $default = false ) {
64
65
    $options = wpinv_get_options();
66
    $value   = isset( $options[ $key ] ) ? $options[ $key ] : $default;
67
    $value   = apply_filters( 'wpinv_get_option', $value, $key, $default );
68
69
    return apply_filters( 'wpinv_get_option_' . $key, $value, $key, $default );
70
}
71
72
/**
73
 * Updates all settings.
74
 *
75
 * @param array $options the new options.
76
 * @return bool
77
 */
78
function wpinv_update_options( $options ) {
79
    global $wpinv_options;
80
81
    // update the option.
82
    if ( is_array( $options ) && update_option( 'wpinv_settings', $options ) ) {
83
        $wpinv_options = $options;
84
        return true;
85
    }
86
87
    return false;
88
}
89
90
/**
91
 * Updates a single setting.
92
 *
93
 * @param string $key the setting key.
94
 * @param mixed $value The setting value.
95
 * @return bool
96
 */
97
function wpinv_update_option( $key = '', $value = false ) {
98
99
    // If no key, exit.
100
    if ( empty( $key ) ) {
101
        return false;
102
    }
103
104
    // Maybe delete the option instead.
105
    if ( is_null( $value ) ) {
106
        return wpinv_delete_option( $key );
107
    }
108
109
    // Prepare the new options.
110
    $options         = wpinv_get_options();
111
    $options[ $key ] = apply_filters( 'wpinv_update_option', $value, $key );
112
113
    // Save the new options.
114
    return wpinv_update_options( $options );
115
116
}
117
118
/**
119
 * Deletes a single setting.
120
 *
121
 * @param string $key the setting key.
122
 * @return bool
123
 */
124
function wpinv_delete_option( $key = '' ) {
125
126
    // If no key, exit
127
    if ( empty( $key ) ) {
128
        return false;
129
    }
130
131
    $options = wpinv_get_options();
132
133
    if ( isset( $options[ $key ] ) ) {
134
        unset( $options[ $key ] );
135
        return wpinv_update_options( $options );
136
    }
137
138
    return true;
139
140
}
141
142
/**
143
 * Register settings after admin inits.
144
 *
145
 */
146
function wpinv_register_settings() {
147
	do_action( 'getpaid_before_register_settings' );
148
149
    // Loop through all tabs.
150
    foreach ( wpinv_get_registered_settings() as $tab => $sections ) {
151
152
        // In each tab, loop through sections.
153
        foreach ( $sections as $section => $settings ) {
154
155
            // Check for backwards compatibility
156
            $section_tabs = wpinv_get_settings_tab_sections( $tab );
157
            if ( ! is_array( $section_tabs ) || ! array_key_exists( $section, $section_tabs ) ) {
158
                $section = 'main';
159
                $settings = $sections;
160
            }
161
162
			do_action( "getpaid_register_{$tab}_{$section}" );
163
164
            // Register the setting section.
165
            add_settings_section(
166
                'wpinv_settings_' . $tab . '_' . $section,
167
                __return_null(),
0 ignored issues
show
Are you sure the usage of __return_null() 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...
168
                '__return_false',
169
                'wpinv_settings_' . $tab . '_' . $section
170
            );
171
172
            foreach ( $settings as $option ) {
173
                if ( ! empty( $option['id'] ) ) {
174
                    wpinv_register_settings_option( $tab, $section, $option );
175
                }
176
            }
177
}
178
    }
179
180
    // Creates our settings in the options table.
181
    register_setting( 'wpinv_settings', 'wpinv_settings', 'wpinv_settings_sanitize' );
0 ignored issues
show
'wpinv_settings_sanitize' of type string is incompatible with the type array expected by parameter $args of register_setting(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

181
    register_setting( 'wpinv_settings', 'wpinv_settings', /** @scrutinizer ignore-type */ 'wpinv_settings_sanitize' );
Loading history...
182
183
	do_action( 'getpaid_after_register_settings' );
184
}
185
add_action( 'admin_init', 'wpinv_register_settings' );
186
187
/**
188
 * Register a single settings option.
189
 *
190
 * @param string $tab
191
 * @param string $section
192
 * @param string $option
193
 *
194
 */
195
function wpinv_register_settings_option( $tab, $section, $option ) {
196
197
    $name       = isset( $option['name'] ) ? $option['name'] : '';
198
    $cb         = "wpinv_{$option['type']}_callback";
199
    $section    = "wpinv_settings_{$tab}_$section";
200
	$is_wizzard = is_admin() && isset( $_GET['page'] ) && 'gp-setup' == $_GET['page'];
201
202
	if ( isset( $option['desc'] ) && ( ! $is_wizzard && ! empty( $option['help-tip'] ) ) ) {
203
		$tip   = wpinv_clean( $option['desc'] );
204
		$name .= "<span class='dashicons dashicons-editor-help wpi-help-tip' title='$tip'></span>";
205
		unset( $option['desc'] );
206
	}
207
208
    // Loop through all tabs.
209
    add_settings_field(
210
        'wpinv_settings[' . $option['id'] . ']',
211
        $name,
212
        function_exists( $cb ) ? $cb : 'wpinv_missing_callback',
213
        $section,
214
        $section,
215
        array(
216
            'section'         => $section,
217
            'id'              => isset( $option['id'] ) ? $option['id'] : uniqid( 'wpinv-' ),
218
            'desc'            => isset( $option['desc'] ) ? $option['desc'] : '',
219
            'name'            => $name,
220
            'size'            => isset( $option['size'] ) ? $option['size'] : null,
221
            'options'         => isset( $option['options'] ) ? $option['options'] : '',
222
            'selected'        => isset( $option['selected'] ) ? $option['selected'] : null,
223
            'std'             => isset( $option['std'] ) ? $option['std'] : '',
224
            'min'             => isset( $option['min'] ) ? $option['min'] : 0,
225
            'max'             => isset( $option['max'] ) ? $option['max'] : 999999,
226
            'step'            => isset( $option['step'] ) ? $option['step'] : 1,
227
            'placeholder'     => isset( $option['placeholder'] ) ? $option['placeholder'] : null,
228
            'allow_blank'     => isset( $option['allow_blank'] ) ? $option['allow_blank'] : true,
229
            'readonly'        => isset( $option['readonly'] ) ? $option['readonly'] : false,
230
            'faux'            => isset( $option['faux'] ) ? $option['faux'] : false,
231
            'onchange'        => isset( $option['onchange'] ) ? $option['onchange'] : '',
232
            'custom'          => isset( $option['custom'] ) ? $option['custom'] : '',
233
			'default_content' => isset( $option['default_content'] ) ? $option['default_content'] : '',
234
			'class'           => isset( $option['class'] ) ? $option['class'] : '',
235
			'style'           => isset( $option['style'] ) ? $option['style'] : '',
236
            'cols'            => isset( $option['cols'] ) && (int) $option['cols'] > 0 ? (int) $option['cols'] : 50,
237
            'rows'            => isset( $option['rows'] ) && (int) $option['rows'] > 0 ? (int) $option['rows'] : 5,
238
        )
239
    );
240
241
}
242
243
/**
244
 * Returns an array of all registered settings.
245
 *
246
 * @return array
247
 */
248
function wpinv_get_registered_settings() {
249
	return array_filter( apply_filters( 'wpinv_registered_settings', wpinv_get_data( 'admin-settings' ) ) );
250
}
251
252
/**
253
 * Returns an array of all integration settings.
254
 *
255
 * @return array
256
 */
257
function getpaid_get_integration_settings() {
258
    return apply_filters( 'getpaid_integration_settings', array() );
259
}
260
261
/**
262
 * Sanitizes settings before they are saved.
263
 *
264
 * @return array
265
 */
266
function wpinv_settings_sanitize( $input = array() ) {
267
268
	$wpinv_options = wpinv_get_options();
269
	$raw_referrer  = wp_get_raw_referer();
270
271
    if ( empty( $raw_referrer ) ) {
272
		return array_merge( $wpinv_options, $input );
273
    }
274
275
    wp_parse_str( $raw_referrer, $referrer );
276
277
	if ( in_array( 'gp-setup', $referrer ) ) {
278
		return array_merge( $wpinv_options, $input );
279
	}
280
281
    $settings = wpinv_get_registered_settings();
282
    $tab      = isset( $referrer['tab'] ) ? $referrer['tab'] : 'general';
283
    $section  = isset( $referrer['section'] ) ? $referrer['section'] : 'main';
284
285
    $input = $input ? $input : array();
286
    $input = apply_filters( 'wpinv_settings_tab_' . $tab . '_sanitize', $input );
287
    $input = apply_filters( 'wpinv_settings_' . $tab . '-' . $section . '_sanitize', $input );
288
289
    // Loop through each setting being saved and pass it through a sanitization filter
290
    foreach ( $input as $key => $value ) {
291
292
        // Get the setting type (checkbox, select, etc)
293
        $type = isset( $settings[ $tab ][ $section ][ $key ]['type'] ) ? $settings[ $tab ][ $section ][ $key ]['type'] : false;
294
295
        if ( $type ) {
296
            // Field type specific filter
297
            $input[ $key ] = apply_filters( "wpinv_settings_sanitize_$type", $value, $key );
298
        }
299
300
        // General filter
301
		$input[ $key ] = apply_filters( 'wpinv_settings_sanitize', $input[ $key ], $key );
302
303
		// Key specific filter.
304
		$input[ $key ] = apply_filters( "wpinv_settings_sanitize_$key", $input[ $key ] );
305
    }
306
307
    // Loop through the whitelist and unset any that are empty for the tab being saved
308
    $main_settings    = isset( $settings[ $tab ] ) ? $settings[ $tab ] : array(); // Check for extensions that aren't using new sections
309
    $section_settings = ! empty( $settings[ $tab ][ $section ] ) ? $settings[ $tab ][ $section ] : array();
310
311
    $found_settings   = array_merge( $main_settings, $section_settings );
312
313
    if ( ! empty( $found_settings ) ) {
314
        foreach ( $found_settings as $key => $value ) {
315
316
            // settings used to have numeric keys, now they have keys that match the option ID. This ensures both methods work
317
            if ( is_numeric( $key ) ) {
318
                $key = $value['id'];
319
            }
320
321
            if ( ! isset( $input[ $key ] ) && isset( $wpinv_options[ $key ] ) ) {
322
                unset( $wpinv_options[ $key ] );
323
            }
324
        }
325
    }
326
327
    // Merge our new settings with the existing
328
    $output = array_merge( $wpinv_options, $input );
329
330
    add_settings_error( 'wpinv-notices', '', __( 'Settings updated.', 'invoicing' ), 'updated' );
331
332
    return $output;
333
}
334
add_filter( 'wpinv_settings_sanitize_text', 'trim', 10, 1 );
335
add_filter( 'wpinv_settings_sanitize_tax_rate', 'wpinv_sanitize_amount' );
336
337
function wpinv_settings_sanitize_tax_rates( $input ) {
338
    if ( ! wpinv_current_user_can_manage_invoicing() ) {
339
        return $input;
340
    }
341
342
    $new_rates = ! empty( $_POST['tax_rates'] ) ? wp_kses_post_deep( array_values( $_POST['tax_rates'] ) ) : array();
343
    $tax_rates = array();
344
345
    foreach ( $new_rates as $rate ) {
346
347
		$rate['rate']    = wpinv_sanitize_amount( $rate['rate'] );
348
		$rate['name']    = sanitize_text_field( $rate['name'] );
349
		$rate['state']   = sanitize_text_field( $rate['state'] );
350
		$rate['country'] = sanitize_text_field( $rate['country'] );
351
		$rate['global']  = empty( $rate['state'] );
352
		$tax_rates[]     = $rate;
353
354
	}
355
356
    update_option( 'wpinv_tax_rates', $tax_rates );
357
358
    return $input;
359
}
360
add_filter( 'wpinv_settings_taxes-rates_sanitize', 'wpinv_settings_sanitize_tax_rates' );
361
362
function wpinv_settings_sanitize_tax_rules( $input ) {
363
    if ( ! wpinv_current_user_can_manage_invoicing() ) {
364
        return $input;
365
    }
366
367
	if ( empty( $_POST['wpinv_tax_rules_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_tax_rules_nonce'], 'wpinv_tax_rules' ) ) {
368
		return $input;
369
	}
370
371
    $new_rules = ! empty( $_POST['tax_rules'] ) ? wp_kses_post_deep( array_values( $_POST['tax_rules'] ) ) : array();
372
    $tax_rules = array();
373
374
    foreach ( $new_rules as $rule ) {
375
376
		$rule['key']      = sanitize_title_with_dashes( $rule['key'] );
377
		$rule['label']    = sanitize_text_field( $rule['label'] );
378
		$rule['tax_base'] = sanitize_text_field( $rule['tax_base'] );
379
		$tax_rules[]      = $rule;
380
381
	}
382
383
    update_option( 'wpinv_tax_rules', $tax_rules );
384
385
    return $input;
386
}
387
add_filter( 'wpinv_settings_taxes-rules_sanitize', 'wpinv_settings_sanitize_tax_rules' );
388
389
function wpinv_get_settings_tabs() {
390
    $tabs             = array();
391
    $tabs['general']  = __( 'General', 'invoicing' );
392
    $tabs['gateways'] = __( 'Payment Gateways', 'invoicing' );
393
    $tabs['taxes']    = __( 'Taxes', 'invoicing' );
394
	$tabs['emails']   = __( 'Emails', 'invoicing' );
395
396
	if ( count( getpaid_get_integration_settings() ) > 0 ) {
397
		$tabs['integrations'] = __( 'Integrations', 'invoicing' );
398
	}
399
400
    $tabs['privacy']  = __( 'Privacy', 'invoicing' );
401
    $tabs['misc']     = __( 'Misc', 'invoicing' );
402
    $tabs['tools']    = __( 'Tools', 'invoicing' );
403
404
    return apply_filters( 'wpinv_settings_tabs', $tabs );
405
}
406
407
function wpinv_get_settings_tab_sections( $tab = false ) {
408
    $tabs     = false;
409
    $sections = wpinv_get_registered_settings_sections();
410
411
    if ( $tab && ! empty( $sections[ $tab ] ) ) {
412
        $tabs = $sections[ $tab ];
413
    }
414
415
    return $tabs;
416
}
417
418
function wpinv_get_registered_settings_sections() {
419
    static $sections = false;
420
421
    if ( false !== $sections ) {
422
        return $sections;
423
    }
424
425
    $sections = array(
426
        'general'      => apply_filters(
427
            'wpinv_settings_sections_general',
428
            array(
429
				'main'             => __( 'General Settings', 'invoicing' ),
430
				'page_section'     => __( 'Page Settings', 'invoicing' ),
431
				'currency_section' => __( 'Currency Settings', 'invoicing' ),
432
				'labels'           => __( 'Label Texts', 'invoicing' ),
433
            )
434
        ),
435
        'gateways'     => apply_filters(
436
            'wpinv_settings_sections_gateways',
437
            array(
438
				'main' => __( 'Gateway Settings', 'invoicing' ),
439
            )
440
        ),
441
        'taxes'        => apply_filters(
442
            'wpinv_settings_sections_taxes',
443
            array(
444
				'main'  => __( 'Tax Settings', 'invoicing' ),
445
				'rules' => __( 'Tax Rules', 'invoicing' ),
446
				'rates' => __( 'Tax Rates', 'invoicing' ),
447
				'vat'   => __( 'EU VAT Settings', 'invoicing' ),
448
            )
449
        ),
450
        'emails'       => apply_filters(
451
            'wpinv_settings_sections_emails',
452
            array(
453
				'main' => __( 'Email Settings', 'invoicing' ),
454
            )
455
        ),
456
457
		'integrations' => wp_list_pluck( getpaid_get_integration_settings(), 'label', 'id' ),
458
459
        'privacy'      => apply_filters(
460
            'wpinv_settings_sections_privacy',
461
            array(
462
				'main' => __( 'Privacy policy', 'invoicing' ),
463
            )
464
        ),
465
        'misc'         => apply_filters(
466
            'wpinv_settings_sections_misc',
467
            array(
468
				'main'       => __( 'Miscellaneous', 'invoicing' ),
469
				'custom-css' => __( 'Custom CSS', 'invoicing' ),
470
            )
471
        ),
472
        'tools'        => apply_filters(
473
            'wpinv_settings_sections_tools',
474
            array(
475
				'main' => __( 'Diagnostic Tools', 'invoicing' ),
476
            )
477
        ),
478
    );
479
480
    $sections = apply_filters( 'wpinv_settings_sections', $sections );
481
482
    return $sections;
483
}
484
485
function wpinv_get_pages( $with_slug = false, $default_label = null ) {
486
    global $wpdb, $gp_tmpl_page_options;
487
488
    // Same function, lets not call it twice if we don't need to.
489
    if ( function_exists( 'sd_template_page_options' ) ) {
490
        $args = array(
491
            'with_slug' => $with_slug,
492
            'default_label' => $default_label
493
        );
494
495
        return sd_template_page_options( $args );
496
    }
497
498
    if ( ! empty( $gp_tmpl_page_options ) ) {
499
        return $gp_tmpl_page_options;
500
    }
501
502
    $exclude_pages = array();
503
    if ( $page_on_front = get_option( 'page_on_front' ) ) {
504
        $exclude_pages[] = $page_on_front;
505
    }
506
507
    if ( $page_for_posts = get_option( 'page_for_posts' ) ) {
508
        $exclude_pages[] = $page_for_posts;
509
    }
510
511
    $exclude_pages_placeholders = '';
512
    if ( ! empty( $exclude_pages ) ) {
513
        // Sanitize the array of excluded pages and implode it for the SQL query
514
        $exclude_pages_placeholders = implode(',', array_fill(0, count($exclude_pages), '%d'));
515
    }
516
517
    // Prepare the base SQL query, including child_of = 0 (only root-level pages)
518
    $sql = "
519
		SELECT ID, post_title, post_name
520
		FROM $wpdb->posts
521
		WHERE post_type = 'page'
522
		AND post_status = 'publish'
523
	";
524
525
    // Add the exclusion if there are pages to exclude
526
    if ( ! empty( $exclude_pages ) ) {
527
        $sql .= " AND ID NOT IN ($exclude_pages_placeholders)";
528
    }
529
530
    // Add sorting
531
    $sql .= " ORDER BY post_title ASC";
532
533
    // Add a sanity limit
534
    $limit = absint( apply_filters('wpinv_get_pages_limit',500) );
535
    $sql .= " LIMIT " . absint($limit);
536
537
    // Prepare the SQL query to include the excluded pages only if we have placeholders
538
    $pages = $exclude_pages_placeholders ? $wpdb->get_results( $wpdb->prepare( $sql, ...$exclude_pages ) ) : $wpdb->get_results( $sql );
539
540
	$pages_options = array();
541
542
    if ( $pages ) {
543
        foreach ( $pages as $page ) {
544
            $title = $with_slug ? $page->post_title . ' (' . $page->post_name . ')' : $page->post_title;
545
            $pages_options[ $page->ID ] = $title;
546
        }
547
    }
548
549
550
551
    $gp_tmpl_page_options = $pages_options;
552
553
    if ( $default_label !== null && $default_label !== false ) {
554
        $pages_options = array( '' => $default_label ) + $pages_options; // Blank option
555
    }
556
557
	return $pages_options;
558
}
559
560
function wpinv_header_callback( $args ) {
561
	if ( ! empty( $args['desc'] ) ) {
562
        echo wp_kses_post( $args['desc'] );
563
    }
564
}
565
566
function wpinv_hidden_callback( $args ) {
567
568
	$std     = isset( $args['std'] ) ? $args['std'] : '';
569
	$value   = wpinv_get_option( $args['id'], $std );
570
571
	if ( isset( $args['set_value'] ) ) {
572
		$value = $args['set_value'];
573
	}
574
575
	if ( isset( $args['faux'] ) && true === $args['faux'] ) {
576
		$args['readonly'] = true;
577
		$name  = '';
578
	} else {
579
		$name = 'wpinv_settings[' . esc_attr( $args['id'] ) . ']';
580
	}
581
582
	echo '<input type="hidden" id="wpinv_settings[' . esc_attr( $args['id'] ) . ']" name="' . esc_attr( $name ) . '" value="' . esc_attr( stripslashes( $value ) ) . '" />';
583
584
}
585
586
/**
587
 * Displays a checkbox settings callback.
588
 */
589
function wpinv_checkbox_callback( $args ) {
590
591
	$std = isset( $args['std'] ) ? $args['std'] : '';
592
	$std = wpinv_get_option( $args['id'], $std );
593
	$id  = esc_attr( $args['id'] );
594
595
	getpaid_hidden_field( "wpinv_settings[$id]", '0' );
596
	?>
597
		<label>
598
			<input id="wpinv-settings-<?php echo esc_attr( $id ); ?>" name="wpinv_settings[<?php echo esc_attr( $id ); ?>]" <?php checked( empty( $std ), false ); ?> value="1" type="checkbox" />
599
			<?php echo wp_kses_post( $args['desc'] ); ?>
600
		</label>
601
	<?php
602
}
603
604
function wpinv_multicheck_callback( $args ) {
605
606
	$sanitize_id = wpinv_sanitize_key( $args['id'] );
607
	$class = ! empty( $args['class'] ) ? ' ' . esc_attr( $args['class'] ) : '';
608
609
	if ( ! empty( $args['options'] ) ) {
610
611
		$std     = isset( $args['std'] ) ? $args['std'] : array();
612
		$value   = wpinv_get_option( $args['id'], $std );
613
614
		echo '<div class="wpi-mcheck-rows wpi-mcheck-' . esc_attr( $sanitize_id . $class ) . '">';
615
        foreach ( $args['options'] as $key => $option ) :
616
			$sanitize_key = esc_attr( wpinv_sanitize_key( $key ) );
617
			if ( in_array( $sanitize_key, $value ) ) {
618
				$enabled = $sanitize_key;
619
			} else {
620
				$enabled = null;
621
			}
622
			echo '<div class="wpi-mcheck-row"><input name="wpinv_settings[' . esc_attr( $sanitize_id ) . '][' . esc_attr( $sanitize_key ) . ']" id="wpinv_settings[' . esc_attr( $sanitize_id ) . '][' . esc_attr( $sanitize_key ) . ']" type="checkbox" value="' . esc_attr( $sanitize_key ) . '" ' . checked( $sanitize_key, $enabled, false ) . '/>&nbsp;';
623
			echo '<label for="wpinv_settings[' . esc_attr( $sanitize_id ) . '][' . esc_attr( $sanitize_key ) . ']">' . wp_kses_post( $option ) . '</label></div>';
624
		endforeach;
625
		echo '</div>';
626
		echo '<p class="description">' . wp_kses_post( $args['desc'] ) . '</p>';
627
	}
628
}
629
630
function wpinv_payment_icons_callback( $args ) {
631
632
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
633
	$value   = wpinv_get_option( $args['id'], false );
634
635
	if ( ! empty( $args['options'] ) ) {
636
		foreach ( $args['options'] as $key => $option ) {
637
            $sanitize_key = wpinv_sanitize_key( $key );
638
639
			if ( empty( $value ) ) {
640
				$enabled = $option;
641
			} else {
642
				$enabled = null;
643
			}
644
645
			echo '<label for="wpinv_settings[' . esc_attr( $sanitize_id ) . '][' . esc_attr( $sanitize_key ) . ']" style="margin-right:10px;line-height:16px;height:16px;display:inline-block;">';
646
647
				echo '<input name="wpinv_settings[' . esc_attr( $sanitize_id ) . '][' . esc_attr( $sanitize_key ) . ']" id="wpinv_settings[' . esc_attr( $sanitize_id ) . '][' . esc_attr( $sanitize_key ) . ']" type="checkbox" value="' . esc_attr( $option ) . '" ' . checked( $option, $enabled, false ) . '/>&nbsp;';
648
649
				if ( wpinv_string_is_image_url( $key ) ) {
650
				echo '<img class="payment-icon" src="' . esc_url( $key ) . '" style="width:32px;height:24px;position:relative;top:6px;margin-right:5px;"/>';
651
				} else {
652
				$card = strtolower( str_replace( ' ', '', $option ) );
653
654
				if ( has_filter( 'wpinv_accepted_payment_' . $card . '_image' ) ) {
655
					$image = apply_filters( 'wpinv_accepted_payment_' . $card . '_image', '' );
656
					} else {
657
					$image       = wpinv_locate_template( 'images' . DIRECTORY_SEPARATOR . 'icons' . DIRECTORY_SEPARATOR . $card . '.gif', false );
0 ignored issues
show
false of type false is incompatible with the type string expected by parameter $template_path of wpinv_locate_template(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

657
					$image       = wpinv_locate_template( 'images' . DIRECTORY_SEPARATOR . 'icons' . DIRECTORY_SEPARATOR . $card . '.gif', /** @scrutinizer ignore-type */ false );
Loading history...
658
					$content_dir = WP_CONTENT_DIR;
659
660
					if ( function_exists( 'wp_normalize_path' ) ) {
661
						// Replaces backslashes with forward slashes for Windows systems
662
						$image = wp_normalize_path( $image );
663
						$content_dir = wp_normalize_path( $content_dir );
664
						}
665
666
					$image = str_replace( $content_dir, content_url(), $image );
667
					}
668
669
				echo '<img class="payment-icon" src="' . esc_url( $image ) . '" style="width:32px;height:24px;position:relative;top:6px;margin-right:5px;"/>';
670
				}
671
			echo wp_kses_post( $option ) . '</label>';
672
		}
673
		echo '<p class="description" style="margin-top:16px;">' . wp_kses_post( $args['desc'] ) . '</p>';
674
	}
675
}
676
677
/**
678
 * Displays a radio settings field.
679
 */
680
function wpinv_radio_callback( $args ) {
681
682
	$std = isset( $args['std'] ) ? $args['std'] : '';
683
	$std = wpinv_get_option( $args['id'], $std );
684
	?>
685
		<fieldset>
686
			<ul id="wpinv-settings-<?php echo esc_attr( $args['id'] ); ?>" style="margin-top: 0;">
687
				<?php foreach ( $args['options'] as $key => $option ) : ?>
688
					<li>
689
						<label>
690
							<input name="wpinv_settings[<?php echo esc_attr( $args['id'] ); ?>]" <?php checked( $std, $key ); ?> value="<?php echo esc_attr( $key ); ?>" type="radio">
691
							<?php echo wp_kses_post( $option ); ?>
692
						</label>
693
					</li>
694
				<?php endforeach; ?>
695
			</ul>
696
		</fieldset>
697
	<?php
698
	getpaid_settings_description_callback( $args );
699
}
700
701
/**
702
 * Displays a description if available.
703
 */
704
function getpaid_settings_description_callback( $args ) {
705
706
	if ( ! empty( $args['desc'] ) ) {
707
		$description = $args['desc'];
708
		echo wp_kses_post( "<p class='description'>$description</p>" );
709
	}
710
711
}
712
713
/**
714
 * Displays a list of available gateways.
715
 */
716
function wpinv_gateways_callback() {
717
718
	?>
719
		</td>
720
	</tr>
721
	<tr class="bsui">
722
    	<td colspan="2" class="p-0">
723
			<?php include plugin_dir_path( __FILE__ ) . 'views/html-gateways-edit.php'; ?>
724
725
	<?php
726
}
727
728
function wpinv_gateway_select_callback( $args ) {
729
730
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
731
    $class = ! empty( $args['class'] ) ? ' ' . esc_attr( $args['class'] ) : '';
732
	$std     = isset( $args['std'] ) ? $args['std'] : '';
733
	$value   = wpinv_get_option( $args['id'], $std );
734
735
	echo '<select name="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"" id="wpinv_settings[' . esc_attr( $sanitize_id ) . ']" class="' . esc_attr( $class ) . '" >';
736
737
	foreach ( $args['options'] as $key => $option ) :
738
739
		echo '<option value="' . esc_attr( $key ) . '" ';
740
741
		if ( isset( $args['selected'] ) && $args['selected'] !== null && $args['selected'] !== false ) {
742
            selected( $key, $args['selected'] );
743
        } else {
744
            selected( $key, $value );
745
        }
746
747
		echo '>' . esc_html( $option['admin_label'] ) . '</option>';
748
	endforeach;
749
750
	echo '</select>';
751
	echo '<label for="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"> ' . wp_kses_post( $args['desc'] ) . '</label>';
752
}
753
754
/**
755
 * Generates attributes.
756
 *
757
 * @param array $args
758
 * @return string
759
 */
760
function wpinv_settings_attrs_helper( $args ) {
761
762
	$value = isset( $args['std'] ) ? $args['std'] : '';
763
	$id    = esc_attr( $args['id'] );
764
	$value = is_scalar( $value ) ? $value : '';
765
766
	$attrs = array(
767
		'name'     => ! empty( $args['faux'] ) ? false : "wpinv_settings[$id]",
768
		'readonly' => ! empty( $args['faux'] ),
769
		'value'    => ! empty( $args['faux'] ) ? $value : wpinv_get_option( $args['id'], $value ),
770
		'id'       => 'wpinv-settings-' . $args['id'],
771
		'style'    => $args['style'],
772
		'class'    => $args['class'],
773
		'placeholder' => $args['placeholder'],
774
		'data-placeholder' => $args['placeholder'],
775
	);
776
777
	if ( ! empty( $args['onchange'] ) ) {
778
		$attrs['onchange'] = $args['onchange'];
779
	}
780
781
	foreach ( $attrs as $key => $value ) {
782
783
		if ( false === $value ) {
784
			continue;
785
		}
786
787
		if ( true === $value ) {
788
			echo ' ' . esc_attr( $key );
789
		} else {
790
			echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
791
		}
792
793
	}
794
795
}
796
797
/**
798
 * Displays a text input settings callback.
799
 */
800
function wpinv_text_callback( $args ) {
801
802
	?>
803
		<label style="width: 100%;">
804
			<input type="text" <?php wpinv_settings_attrs_helper( $args ); ?>>
805
			<?php getpaid_settings_description_callback( $args ); ?>
806
		</label>
807
	<?php
808
809
}
810
811
/**
812
 * Displays a number input settings callback.
813
 */
814
function wpinv_number_callback( $args ) {
815
816
	?>
817
		<label style="width: 100%;">
818
			<input type="number" step="<?php echo esc_attr( $args['step'] ); ?>" max="<?php echo intval( $args['max'] ); ?>" min="<?php echo intval( $args['min'] ); ?>" <?php wpinv_settings_attrs_helper( $args ); ?>>
819
			<?php getpaid_settings_description_callback( $args ); ?>
820
		</label>
821
	<?php
822
823
}
824
825
function wpinv_textarea_callback( $args ) {
826
827
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
828
	$std     = isset( $args['std'] ) ? $args['std'] : '';
829
	$value   = wpinv_get_option( $args['id'], $std );
830
831
    $size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
832
    $class = ( isset( $args['class'] ) && ! is_null( $args['class'] ) ) ? $args['class'] : 'large-text';
833
834
	echo '<textarea class="' . esc_attr( $class ) . ' txtarea-' . esc_attr( $size ) . ' wpi-' . esc_attr( sanitize_html_class( $sanitize_id ) ) . ' " cols="' . esc_attr( $args['cols'] ) . '" rows="' . esc_attr( $args['rows'] ) . '" id="wpinv_settings[' . esc_attr( $sanitize_id ) . ']" name="wpinv_settings[' . esc_attr( $args['id'] ) . ']">' . esc_textarea( stripslashes( $value ) ) . '</textarea>';
835
	echo '<br /><label for="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"> ' . wp_kses_post( $args['desc'] ) . '</label>';
836
837
}
838
839
function wpinv_password_callback( $args ) {
840
841
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
842
	$std     = isset( $args['std'] ) ? $args['std'] : '';
843
	$value   = wpinv_get_option( $args['id'], $std );
844
845
	$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
846
	echo '<input type="password" class="' . esc_attr( $size ) . '-text" id="wpinv_settings[' . esc_attr( $sanitize_id ) . ']" name="wpinv_settings[' . esc_attr( $args['id'] ) . ']" value="' . esc_attr( $value ) . '"/>';
847
	echo '<label for="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"> ' . wp_kses_post( $args['desc'] ) . '</label>';
848
849
}
850
851
function wpinv_missing_callback( $args ) {
852
	printf(
853
		esc_html__( 'The callback function used for the %s setting is missing.', 'invoicing' ),
854
		'<strong>' . esc_html( $args['id'] ) . '</strong>'
855
	);
856
}
857
858
/**
859
 * Displays a number input settings callback.
860
 */
861
function wpinv_select_callback( $args ) {
862
863
	$desc   = wp_kses_post( $args['desc'] );
864
	$desc   = empty( $desc ) ? '' : "<p class='description'>$desc</p>";
865
	$value  = isset( $args['std'] ) ? $args['std'] : '';
866
	$value  = wpinv_get_option( $args['id'], $value );
867
	$rand   = uniqid( 'random_id' );
868
869
	?>
870
		<label style="width: 100%;">
871
			<select <?php wpinv_settings_attrs_helper( $args ); ?> data-allow-clear="true">
872
				<?php foreach ( $args['options'] as $option => $name ) : ?>
873
					<option value="<?php echo esc_attr( $option ); ?>" <?php echo selected( $option, $value ); ?>><?php echo esc_html( $name ); ?></option>
874
				<?php endforeach; ?>
875
			</select>
876
877
			<?php if ( substr( $args['id'], -5 ) === '_page' && is_numeric( $value ) ) : ?>
878
				<a href="<?php echo esc_url( get_edit_post_link( $value ) ); ?>" target="_blank" class="button getpaid-page-setting-edit"><?php esc_html_e( 'Edit Page', 'invoicing' ); ?></a>
0 ignored issues
show
$value of type string is incompatible with the type WP_Post|integer expected by parameter $post of get_edit_post_link(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

878
				<a href="<?php echo esc_url( get_edit_post_link( /** @scrutinizer ignore-type */ $value ) ); ?>" target="_blank" class="button getpaid-page-setting-edit"><?php esc_html_e( 'Edit Page', 'invoicing' ); ?></a>
Loading history...
879
			<?php endif; ?>
880
881
			<?php if ( substr( $args['id'], -5 ) === '_page' && ! empty( $args['default_content'] ) ) : ?>
882
				&nbsp;<a href="#TB_inline?&width=400&height=550&inlineId=<?php echo esc_attr( $rand ); ?>" class="button thickbox getpaid-page-setting-view-default"><?php esc_html_e( 'View Default Content', 'invoicing' ); ?></a>
883
				<div id='<?php echo esc_attr( $rand ); ?>' style='display:none;'>
884
					<div>
885
						<h3><?php esc_html_e( 'Original Content', 'invoicing' ); ?></h3>
886
						<textarea readonly onclick="this.select()" rows="8" style="width: 100%;"><?php echo wp_kses_post( gepaid_trim_lines( $args['default_content'] ) ); ?></textarea>
887
						<h3><?php esc_html_e( 'Current Content', 'invoicing' ); ?></h3>
888
						<textarea readonly onclick="this.select()" rows="8" style="width: 100%;"><?php $_post = get_post( $value ); echo empty( $_post ) ? '' : wp_kses_post( gepaid_trim_lines( $_post->post_content ) ); ?></textarea>
889
					</div>
890
				</div>
891
			<?php endif; ?>
892
893
			<?php echo wp_kses_post( $desc ); ?>
894
		</label>
895
	<?php
896
897
}
898
899
function wpinv_color_select_callback( $args ) {
900
901
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
902
	$std     = isset( $args['std'] ) ? $args['std'] : '';
903
	$value   = wpinv_get_option( $args['id'], $std );
904
905
	echo '<select id="wpinv_settings[' . esc_attr( $sanitize_id ) . ']" name="wpinv_settings[' . esc_attr( $args['id'] ) . ']"/>';
906
907
	foreach ( $args['options'] as $option => $color ) {
908
		echo '<option value="' . esc_attr( $option ) . '" ' . selected( $option, $value ) . '>' . esc_html( $color['label'] ) . '</option>';
909
	}
910
911
	echo '</select>';
912
	echo '<label for="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"> ' . wp_kses_post( $args['desc'] ) . '</label>';
913
914
}
915
916
function wpinv_rich_editor_callback( $args ) {
917
	global $wp_version;
918
919
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
920
921
	$std     = isset( $args['std'] ) ? $args['std'] : '';
922
	$value   = wpinv_get_option( $args['id'], $std );
923
924
	if ( ! empty( $args['allow_blank'] ) && empty( $value ) ) {
925
		$value = $std;
926
	}
927
928
	$rows = isset( $args['size'] ) ? $args['size'] : 20;
929
930
	echo '<div class="getpaid-settings-editor-input">';
931
	if ( $wp_version >= 3.3 && function_exists( 'wp_editor' ) ) {
932
		wp_editor(
933
            stripslashes( $value ),
934
            'wpinv_settings_' . esc_attr( $args['id'] ),
935
            array(
936
				'textarea_name' => 'wpinv_settings[' . esc_attr( $args['id'] ) . ']',
937
				'textarea_rows' => absint( $rows ),
938
				'media_buttons' => false,
939
            )
940
        );
941
	} else {
942
		echo '<textarea class="large-text" rows="10" id="wpinv_settings[' . esc_attr( $sanitize_id ) . ']" name="wpinv_settings[' . esc_attr( $args['id'] ) . ']" class="wpi-' . esc_attr( sanitize_html_class( $args['id'] ) ) . '">' . esc_textarea( stripslashes( $value ) ) . '</textarea>';
943
	}
944
945
	echo '</div><br/><label for="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"> ' . wp_kses_post( $args['desc'] ) . '</label>';
946
947
}
948
949
function wpinv_upload_callback( $args ) {
950
951
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
952
953
	$std     = isset( $args['std'] ) ? $args['std'] : '';
954
	$value   = wpinv_get_option( $args['id'], $std );
955
956
	$size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
957
	echo '<input type="text" class="' . sanitize_html_class( $size ) . '-text" id="wpinv_settings[' . esc_attr( $sanitize_id ) . ']" name="wpinv_settings[' . esc_attr( $args['id'] ) . ']" value="' . esc_attr( stripslashes( $value ) ) . '"/>';
958
	echo '<span>&nbsp;<input type="button" class="wpinv_settings_upload_button button-secondary" value="' . esc_attr__( 'Upload File', 'invoicing' ) . '"/></span>';
959
	echo '<label for="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"> ' . wp_kses_post( $args['desc'] ) . '</label>';
960
961
}
962
963
function wpinv_color_callback( $args ) {
964
965
	$std         = isset( $args['std'] ) ? $args['std'] : '';
966
	$value       = wpinv_get_option( $args['id'], $std );
967
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
968
969
	echo '<input type="text" class="wpinv-color-picker" id="wpinv_settings[' . esc_attr( $sanitize_id ) . ']" name="wpinv_settings[' . esc_attr( $args['id'] ) . ']" value="' . esc_attr( $value ) . '" data-default-color="' . esc_attr( $std ) . '" />';
970
	echo '<label for="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"> ' . wp_kses_post( $args['desc'] ) . '</label>';
971
972
}
973
974
function wpinv_country_states_callback( $args ) {
975
976
	$std     = isset( $args['std'] ) ? $args['std'] : '';
977
	$value   = wpinv_get_option( $args['id'], $std );
978
979
    $sanitize_id = wpinv_sanitize_key( $args['id'] );
980
981
	if ( isset( $args['placeholder'] ) ) {
982
		$placeholder = $args['placeholder'];
983
	} else {
984
		$placeholder = '';
985
	}
986
987
	$states = wpinv_get_country_states();
988
989
	$class = empty( $states ) ? 'wpinv-no-states' : 'wpi_select2';
990
	echo '<select id="wpinv_settings[' . esc_attr( $sanitize_id ) . ']" name="wpinv_settings[' . esc_attr( $args['id'] ) . ']" class="' . esc_attr( $class ) . '" data-placeholder="' . esc_html( $placeholder ) . '"/>';
991
992
	foreach ( $states as $option => $name ) {
993
		echo '<option value="' . esc_attr( $option ) . '" ' . selected( $option, $value ) . '>' . esc_html( $name ) . '</option>';
994
	}
995
996
	echo '</select>';
997
	echo '<label for="wpinv_settings[' . esc_attr( $sanitize_id ) . ']"> ' . wp_kses_post( $args['desc'] ) . '</label>';
998
999
}
1000
1001
/**
1002
 * Displays the tax rates edit table.
1003
 */
1004
function wpinv_tax_rates_callback() {
1005
1006
	?>
1007
		</td>
1008
	</tr>
1009
	<tr class="bsui">
1010
    	<td colspan="2" class="p-0">
1011
			<?php include plugin_dir_path( __FILE__ ) . 'views/html-tax-rates-edit.php'; ?>
1012
1013
	<?php
1014
1015
}
1016
1017
/**
1018
 * Displays a tax rate' edit row.
1019
 */
1020
function wpinv_tax_rate_callback( $tax_rate, $key ) {
1021
1022
	$key                      = sanitize_key( $key );
1023
	$tax_rate['reduced_rate'] = empty( $tax_rate['reduced_rate'] ) ? 0 : $tax_rate['reduced_rate'];
1024
	include plugin_dir_path( __FILE__ ) . 'views/html-tax-rate-edit.php';
1025
1026
}
1027
1028
/**
1029
 * Displays the tax rules edit table.
1030
 */
1031
function wpinv_tax_rules_callback() {
1032
1033
	?>
1034
		</td>
1035
	</tr>
1036
	<tr class="bsui">
1037
    	<td colspan="2" class="p-0">
1038
			<?php include plugin_dir_path( __FILE__ ) . 'views/html-tax-rules-edit.php'; ?>
1039
1040
	<?php
1041
1042
}
1043
1044
function wpinv_tools_callback( $args ) {
1045
    ?>
1046
    </td><tr>
1047
    <td colspan="2" class="wpinv_tools_tdbox">
1048
    <?php
1049
    if ( $args['desc'] ) {
1050
?>
1051
<p><?php echo wp_kses_post( $args['desc'] ); ?></p><?php } ?>
1052
    <?php do_action( 'wpinv_tools_before' ); ?>
1053
    <table id="wpinv_tools_table" class="wp-list-table widefat fixed posts">
1054
        <thead>
1055
            <tr>
1056
                <th scope="col" class="wpinv-th-tool"><?php esc_html_e( 'Tool', 'invoicing' ); ?></th>
1057
                <th scope="col" class="wpinv-th-desc"><?php esc_html_e( 'Description', 'invoicing' ); ?></th>
1058
                <th scope="col" class="wpinv-th-action"><?php esc_html_e( 'Action', 'invoicing' ); ?></th>
1059
            </tr>
1060
        </thead>
1061
1062
        <tbody>
1063
			<tr>
1064
                <td><?php esc_html_e( 'Check Pages', 'invoicing' ); ?></td>
1065
                <td>
1066
                    <small><?php esc_html_e( 'Creates any missing GetPaid pages.', 'invoicing' ); ?></small>
1067
                </td>
1068
                <td>
1069
					<a href="
1070
                    <?php
1071
						echo esc_url(
1072
							wp_nonce_url(
1073
								add_query_arg( 'getpaid-admin-action', 'create_missing_pages' ),
1074
								'getpaid-nonce',
1075
								'getpaid-nonce'
1076
							)
1077
						);
1078
					?>
1079
                    " class="button button-primary"><?php esc_html_e( 'Run', 'invoicing' ); ?></a>
1080
                </td>
1081
            </tr>
1082
			<tr>
1083
                <td><?php esc_html_e( 'Refresh Permalinks', 'invoicing' ); ?></td>
1084
                <td>
1085
                    <small><?php esc_html_e( 'Might fix the page not found error when viewing an invoice.', 'invoicing' ); ?></small>
1086
                </td>
1087
                <td>
1088
					<a href="
1089
                    <?php
1090
						echo esc_url(
1091
							wp_nonce_url(
1092
								add_query_arg( 'getpaid-admin-action', 'refresh_permalinks' ),
1093
								'getpaid-nonce',
1094
								'getpaid-nonce'
1095
							)
1096
						);
1097
					?>
1098
                    " class="button button-primary"><?php esc_html_e( 'Run', 'invoicing' ); ?></a>
1099
                </td>
1100
            </tr>
1101
			<tr>
1102
                <td><?php esc_html_e( 'Repair Database Tables', 'invoicing' ); ?></td>
1103
                <td>
1104
                    <small><?php esc_html_e( 'Run this tool to create any missing database tables.', 'invoicing' ); ?></small>
1105
                </td>
1106
                <td>
1107
					<a href="
1108
                    <?php
1109
						echo esc_url(
1110
							wp_nonce_url(
1111
								add_query_arg( 'getpaid-admin-action', 'create_missing_tables' ),
1112
								'getpaid-nonce',
1113
								'getpaid-nonce'
1114
							)
1115
						);
1116
					?>
1117
                    " class="button button-primary"><?php esc_html_e( 'Run', 'invoicing' ); ?></a>
1118
                </td>
1119
            </tr>
1120
			<tr>
1121
                <td><?php esc_html_e( 'Migrate old invoices', 'invoicing' ); ?></td>
1122
                <td>
1123
                    <small><?php esc_html_e( 'If your old invoices were not migrated after updating from Invoicing to GetPaid, you can use this tool to migrate them.', 'invoicing' ); ?></small>
1124
                </td>
1125
                <td>
1126
					<a href="
1127
                    <?php
1128
						echo esc_url(
1129
							wp_nonce_url(
1130
								add_query_arg( 'getpaid-admin-action', 'migrate_old_invoices' ),
1131
								'getpaid-nonce',
1132
								'getpaid-nonce'
1133
							)
1134
						);
1135
					?>
1136
                    " class="button button-primary"><?php esc_html_e( 'Run', 'invoicing' ); ?></a>
1137
                </td>
1138
            </tr>
1139
1140
			<tr>
1141
                <td><?php esc_html_e( 'Recalculate Discounts', 'invoicing' ); ?></td>
1142
                <td>
1143
                    <small><?php esc_html_e( 'Recalculate discounts for existing invoices that have discount codes but are not discounted.', 'invoicing' ); ?></small>
1144
                </td>
1145
                <td>
1146
					<a href="
1147
                    <?php
1148
						echo esc_url(
1149
							wp_nonce_url(
1150
								add_query_arg( 'getpaid-admin-action', 'recalculate_discounts' ),
1151
								'getpaid-nonce',
1152
								'getpaid-nonce'
1153
							)
1154
						);
1155
					?>
1156
                    " class="button button-primary"><?php esc_html_e( 'Run', 'invoicing' ); ?></a>
1157
                </td>
1158
            </tr>
1159
1160
			<tr>
1161
				<td><?php esc_html_e( 'Database Text Translation', 'invoicing' ); ?></td>
1162
				<td>
1163
					<small><?php esc_html_e( 'This tool will collect any strings stored in the database and put them in the file db-language.php, so those strings can be used to translate by translation tools.', 'invoicing' ); ?></small>
1164
				</td>
1165
				<td>
1166
					<a href=" <?php echo esc_url( wp_nonce_url( add_query_arg( 'getpaid-admin-action', 'translate_db_texts' ), 'getpaid-nonce', 'getpaid-nonce' ) ); ?>" class="button button-primary"><?php esc_html_e( 'Run', 'invoicing' ); ?></a>
1167
				</td>
1168
			</tr>
1169
1170
			<tr>
1171
                <td><?php esc_html_e( 'Set-up Wizard', 'invoicing' ); ?></td>
1172
                <td>
1173
                    <small><?php esc_html_e( 'Launch the quick set-up wizard.', 'invoicing' ); ?></small>
1174
                </td>
1175
                <td>
1176
					<a href="
1177
                    <?php
1178
						echo esc_url( admin_url( 'index.php?page=gp-setup' ) );
1179
					?>
1180
                    " class="button button-primary"><?php esc_html_e( 'Launch', 'invoicing' ); ?></a>
1181
                </td>
1182
            </tr>
1183
1184
			<?php do_action( 'wpinv_tools_row' ); ?>
1185
        </tbody>
1186
    </table>
1187
    <?php do_action( 'wpinv_tools_after' ); ?>
1188
    <?php
1189
}
1190
1191
1192
function wpinv_descriptive_text_callback( $args ) {
1193
	echo wp_kses_post( $args['desc'] );
1194
}
1195
1196
function wpinv_raw_html_callback( $args ) {
1197
	echo wp_kses( $args['desc'], getpaid_allowed_html() );
1198
}
1199
1200
function wpinv_hook_callback( $args ) {
1201
	do_action( 'wpinv_' . $args['id'], $args );
1202
}
1203
1204
function wpinv_set_settings_cap() {
1205
	return wpinv_get_capability();
1206
}
1207
add_filter( 'option_page_capability_wpinv_settings', 'wpinv_set_settings_cap' );
1208
1209
1210
function wpinv_on_update_settings( $old_value, $value, $option ) {
1211
    $old = ! empty( $old_value['remove_data_on_unistall'] ) ? 1 : '';
1212
    $new = ! empty( $value['remove_data_on_unistall'] ) ? 1 : '';
1213
1214
    if ( $old != $new ) {
1215
        update_option( 'wpinv_remove_data_on_invoice_unistall', $new );
1216
    }
1217
}
1218
add_action( 'update_option_wpinv_settings', 'wpinv_on_update_settings', 10, 3 );
1219
1220
1221
/**
1222
 * Retrieve merge tags for email templates.
1223
 * 
1224
 * Returns an array of merge tags that can be used in email templates for invoices & subscriptions.
1225
 * 
1226
 * @since    2.1.8
1227
 *
1228
 * @return array
1229
 */
1230
function wpinv_get_email_merge_tags( $subscription = false ) {
1231
	$merge_tags = array(
1232
		'{site_title}'           => __( 'Site Title', 'invoicing' ),
1233
		'{name}'                 => __( "Customer's full name", 'invoicing' ),
1234
		'{first_name}'           => __( "Customer's first name", 'invoicing' ),
1235
		'{last_name}'            => __( "Customer's last name", 'invoicing' ),
1236
		'{email}'                => __( "Customer's email address", 'invoicing' ),
1237
		'{invoice_number}'       => __( 'The invoice number', 'invoicing' ),
1238
		'{invoice_currency}'     => __( 'The invoice currency', 'invoicing' ),
1239
		'{invoice_total}'        => __( 'The invoice total', 'invoicing' ),
1240
		'{invoice_link}'         => __( 'The invoice link', 'invoicing' ),
1241
		'{invoice_pay_link}'     => __( 'The payment link', 'invoicing' ),
1242
		'{invoice_receipt_link}' => __( 'The receipt link', 'invoicing' ),
1243
		'{invoice_date}'         => __( 'The date the invoice was created', 'invoicing' ),
1244
		'{invoice_due_date}'     => __( 'The date the invoice is due', 'invoicing' ),
1245
		'{date}'                 => __( "Today's date", 'invoicing' ),
1246
		'{is_was}'               => __( 'If due date of invoice is past, displays "was" otherwise displays "is"', 'invoicing' ),
1247
		'{invoice_label}'        => __( 'Invoices/quotes singular name. Ex: Invoice/Quote', 'invoicing' ),
1248
		'{invoice_quote}'        => __( 'Invoices/quotes singular name in small letters. Ex: invoice/quote', 'invoicing' ),
1249
		'{invoice_description}'  => __( 'The description of the invoice', 'invoicing' ),
1250
	);
1251
1252
	if ( $subscription ) {
1253
		$merge_tags = array_merge(
1254
			$merge_tags,
1255
			array(
1256
				'{subscription_renewal_date}'     => __( 'The next renewal date of the subscription', 'invoicing' ),
1257
				'{subscription_created}'          => __( "The subscription's creation date", 'invoicing' ),
1258
				'{subscription_status}'           => __( "The subscription's status", 'invoicing' ),
1259
				'{subscription_profile_id}'       => __( "The subscription's remote profile id", 'invoicing' ),
1260
				'{subscription_id}'               => __( "The subscription's id", 'invoicing' ),
1261
				'{subscription_recurring_amount}' => __( 'The renewal amount of the subscription', 'invoicing' ),
1262
				'{subscription_initial_amount}'   => __( 'The initial amount of the subscription', 'invoicing' ),
1263
				'{subscription_recurring_period}' => __( 'The recurring period of the subscription (e.g 1 year)', 'invoicing' ),
1264
				'{subscription_bill_times}'       => __( 'The maximum number of times the subscription can be renewed', 'invoicing' ),
1265
				'{subscription_url}'              => __( 'The URL to manage a subscription', 'invoicing' ),
1266
				'{subscription_name}'             => __( 'The name of the recurring item', 'invoicing' ),
1267
			)
1268
		);
1269
	}
1270
1271
	return $merge_tags;
1272
}
1273
1274
1275
/**
1276
 * Returns the merge tags help text.
1277
 *
1278
 * @since    2.1.8
1279
 *
1280
 * @return string
1281
 */
1282
function wpinv_get_merge_tags_help_text( $subscription = false ) {
1283
	$merge_tags = wpinv_get_email_merge_tags( $subscription );
1284
1285
	$output = '<div class="bsui">';
1286
1287
	$link = sprintf(
1288
		'<strong class="getpaid-merge-tags text-primary" role="button">%s</strong>',
1289
		esc_html__( 'View available merge tags.', 'invoicing' )
1290
	);
1291
1292
	$description = esc_html__( 'The content of the email (Merge Tags and HTML are allowed).', 'invoicing' );
1293
	
1294
	$output .= "$description $link";
1295
1296
	$output .= '<div class="getpaid-merge-tags-content mt-2 p-1 d-none">';
1297
	$output .= '<p class="mb-2">' . esc_html__( 'The following wildcards can be used in email subjects, heading and content:', 'invoicing' ) . '</p>';
1298
1299
	$output .= '<ul class="p-0 m-0">';
1300
	foreach($merge_tags as $tag => $tag_description) {
1301
		$output .= "<li class='mb-2'><strong class='text-dark'>$tag</strong> &mdash; $tag_description</li>";
1302
	}
1303
1304
	$output .= '</ul></div></div>';
1305
1306
	return $output;
1307
}
1308