PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.11.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.11.3
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / admin / settings / class-charitable-settings.php

class-charitable-settings.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.11.3, at includes/admin/settings/class-charitable-settings.php

1,185 lines 45.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Charitable Settings UI.
4 *
5 * @package Charitable/Classes/Charitable_Settings
6 * @author David Bisset
7 * @copyright Copyright (c) 2023, WP Charitable LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.0.0
10 * @version 1.0.0
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Settings' ) ) :
19
20 /**
21 * Charitable_Settings
22 *
23 * @final
24 * @since 1.0.0
25 */
26 final class Charitable_Settings {
27
28 /**
29 * The single instance of this class.
30 *
31 * @var Charitable_Settings|null
32 */
33 private static $instance = null;
34
35 /**
36 * Dynamic groups.
37 *
38 * @since 1.5.7
39 *
40 * @var array
41 */
42 private $dynamic_groups;
43
44 /**
45 * List of static pages, used in some settings.
46 *
47 * @since 1.5.9
48 *
49 * @var array
50 */
51 private $pages;
52
53 /**
54 * Create object instance.
55 *
56 * @since 1.0.0
57 */
58 private function __construct() {
59 do_action( 'charitable_admin_settings_start', $this );
60
61 add_action( 'charitable_after_admin_settings', array( $this, 'settings_cta' ) );
62 }
63
64 /**
65 * Returns and/or create the single instance of this class.
66 *
67 * @since 1.2.0
68 *
69 * @return Charitable_Settings
70 */
71 public static function get_instance() {
72 if ( is_null( self::$instance ) ) {
73 self::$instance = new self();
74 }
75
76 return self::$instance;
77 }
78
79 /**
80 * Return the array of tabs used on the settings page.
81 *
82 * @since 1.0.0
83 *
84 * @return string[]
85 */
86 public function get_sections() {
87 /**
88 * Filter the settings tabs.
89 *
90 * @since 1.0.0
91 * @version 1.8.1.6 removed 'tools' tab
92 * @version 1.8.5.1 added marketing and security tabs depending on addon status.
93 *
94 * @param string[] $tabs List of tabs in key=>label format.
95 */
96 $tabs = array(
97 'general' => __( 'General', 'charitable' ),
98 'gateways' => __( 'Payment Gateways', 'charitable' ),
99 'emails' => __( 'Emails', 'charitable' ),
100 'donors' => __( 'Donors', 'charitable' ),
101 'privacy' => __( 'Privacy', 'charitable' ),
102 'advanced' => __( 'Advanced', 'charitable' ),
103 );
104
105 // Only show marketing tab if newsletter connect addon is not active.
106 if ( ! class_exists( 'Charitable_Newsletter_Connect' ) ) {
107 $tabs = charitable_add_settings_tab(
108 $tabs,
109 'marketing',
110 __( 'Marketing', 'charitable' ),
111 array(
112 'index' => 5,
113 )
114 );
115 }
116
117 // Always show security tab.
118 // When spam blocker is active, it will handle the settings.
119 // When spam blocker is inactive, core security features will handle the settings.
120 $tabs = charitable_add_settings_tab(
121 $tabs,
122 'security',
123 __( 'Security', 'charitable' ),
124 array(
125 'index' => 6,
126 )
127 );
128
129 // Ensure the advanced tab is always last by moving it to the end of the array.
130 if ( isset( $tabs['advanced'] ) ) {
131 $advanced_tab = $tabs['advanced'];
132 unset( $tabs['advanced'] );
133 $tabs['advanced'] = $advanced_tab;
134 }
135
136 return apply_filters( 'charitable_settings_tabs', $tabs );
137 }
138
139 /**
140 * Optionally add the extensions tab.
141 *
142 * @since 1.3.0
143 * @version 1.8.5.1 Adjust index so it comes before the advanced tab.
144 *
145 * @param string[] $tabs The existing set of tabs.
146 * @return string[]
147 */
148 public function maybe_add_extensions_tab( $tabs ) {
149 $actual_tab = isset( $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : 'general'; // phpcs:ignore
150
151 /* Set the tab to 'extensions' */
152 $_GET['tab'] = 'extensions';
153
154 /**
155 * Filter the settings in the extensions tab.
156 *
157 * @since 1.3.0
158 *
159 * @param array $fields Array of fields. Empty by default.
160 */
161 $settings = apply_filters( 'charitable_settings_tab_fields_extensions', array() );
162
163 /* Set the tab back to whatever it actually is */
164 $_GET['tab'] = $actual_tab;
165
166 if ( ! empty( $settings ) ) {
167 $tabs = charitable_add_settings_tab(
168 $tabs,
169 'extensions',
170 __( 'Extensions', 'charitable' ),
171 array(
172 'index' => 7,
173 )
174 );
175 }
176
177 // Ensure the advanced tab is always last by moving it to the end of the array.
178 if ( isset( $tabs['advanced'] ) ) {
179 $advanced_tab = $tabs['advanced'];
180 unset( $tabs['advanced'] );
181 $tabs['advanced'] = $advanced_tab;
182 }
183
184 return $tabs;
185 }
186
187 /**
188 * Add the hidden "extensions" section field.
189 *
190 * @since 1.6.7
191 *
192 * @param array $fields All the settings fields.
193 * @return array
194 */
195 public function add_hidden_extensions_setting_field( $fields ) {
196 if ( ! array_key_exists( 'extensions', $fields ) ) {
197 return $fields;
198 }
199
200 $fields['extensions']['section'] = array(
201 'title' => '',
202 'type' => 'hidden',
203 'priority' => 10000,
204 'value' => 'extensions',
205 'save' => false,
206 );
207
208 return $fields;
209 }
210
211 /**
212 * Register setting.
213 *
214 * @since 1.0.0
215 *
216 * @return void
217 */
218 public function register_settings() {
219 if ( ! charitable_is_settings_view() ) {
220 return;
221 }
222
223 register_setting( 'charitable_settings', 'charitable_settings', array( $this, 'sanitize_settings' ) );
224
225 $fields = $this->get_fields();
226
227 if ( empty( $fields ) ) {
228 return;
229 }
230
231 $sections = array_merge( $this->get_sections(), $this->get_dynamic_groups() );
232
233 /* Register each section */
234 foreach ( $sections as $section_key => $section ) {
235 $section_id = 'charitable_settings_' . $section_key;
236
237 add_settings_section(
238 $section_id,
239 __return_null(),
240 '__return_false',
241 $section_id
242 );
243
244 if ( ! isset( $fields[ $section_key ] ) || empty( $fields[ $section_key ] ) ) {
245 continue;
246 }
247
248 /* Sort by priority */
249 $section_fields = $fields[ $section_key ];
250 uasort( $section_fields, 'charitable_priority_sort' );
251
252 /* Add the individual fields within the section */
253 foreach ( $section_fields as $key => $field ) {
254 $this->register_field( $field, array( $section_key, $key ) );
255 }
256 }
257 }
258
259 /**
260 * Sanitize submitted settings before saving to the database
261 * This includes: "workaround" for Stripe keys with 1.7.0.0, additional settings options after 1.7.0.6+
262 *
263 * @since 1.0.0
264 *
265 * @param array $values The submitted values.
266 * @return string
267 */
268 public function sanitize_settings( $values ) {
269
270 $old_values = get_option( 'charitable_settings', array() );
271 $new_values = array();
272
273 if ( ! is_array( $old_values ) ) {
274 $old_values = array();
275 }
276
277 if ( ! is_array( $values ) ) {
278 $values = array();
279 }
280
281 /* Loop through all fields, merging the submitted values into the master array */
282 foreach ( $values as $section => $submitted ) {
283 $new_values = array_merge( $new_values, $this->get_section_submitted_values( $section, $submitted ) );
284 }
285
286 $settings_keys = array_keys( $new_values );
287
288 if ( charitable_is_debug() ) {
289 error_log( 'santiize_settings' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
290 error_log( 'values:' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
291 error_log( print_r( $values, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
292 error_log( 'old_values:' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
293 error_log( print_r( $old_values, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
294 error_log( 'new_values:' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
295 error_log( print_r( $new_values, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
296 }
297
298 // determine if Charitable gateway "test_mode" is being changed, and if so add a notice to the user.
299 if ( array_key_exists( 'test_mode', $new_values ) && array_key_exists( 'test_mode', $old_values ) && $old_values['test_mode'] !== $new_values['test_mode'] ) {
300 $old_settings_keys = array_keys( $old_values );
301 $dismissible = true;
302
303 // Check if Square has connection issues for the new mode
304 $square_has_issues = $this->check_square_connection_for_mode_change( $new_values['test_mode'] );
305
306 // Determine notice type and message
307 $notice_type = $square_has_issues ? 'error' : 'warning';
308 $gateway_list = array();
309
310 if ( in_array( 'gateways_stripe', $old_settings_keys ) ) {
311 $gateway_list[] = 'Stripe';
312 }
313
314 if ( $square_has_issues ) {
315 $gateway_list[] = 'Square';
316 }
317
318 if ( ! empty( $gateway_list ) ) {
319 $gateway_text = implode( ' and ', $gateway_list );
320 $message = sprintf(
321 /* translators: %s: comma-separated list of gateway names */
322 __( 'Some active payment gateways <strong>(including %s)</strong> might have reset their connections due to an update in the test mode. Please check your active payment gateways to ensure they are still connected.', 'charitable' ),
323 $gateway_text
324 );
325 } else {
326 $message = __( 'Some active payment gateways might have reset their connections due to an update in the test mode. Please check your active payment gateways to ensure they are still connected.', 'charitable' );
327 }
328
329 // Add additional Square-specific message if Square has connection issues
330 if ( $square_has_issues ) {
331 $mode_label = $new_values['test_mode'] ? __( 'test', 'charitable' ) : __( 'live', 'charitable' );
332 $message .= '<br><br><strong>' . sprintf(
333 /* translators: %s: mode (test or live) */
334 __( 'Square payment gateway is enabled but not connected in %s mode. Please connect your Square account to continue processing payments.', 'charitable' ),
335 $mode_label
336 ) . '</strong>';
337 }
338
339 charitable_get_admin_notices()->add_notice( $message, $notice_type, false, $dismissible );
340 }
341
342 if ( in_array( 'gateways_stripe', $settings_keys ) && charitable()->is_stripe_connect_addon() && false === charitable_using_stripe_connect() ) {
343 // non-existant array keys in the array for API keys might be a result of the "manual update key" feature which is based on ajax and might not load keys into form if the user doesn't click on the link in the settings to view/edit them.
344 if ( ! array_key_exists( 'live_secret_key', $values['gateways_stripe'] ) && isset( $old_values['gateways_stripe']['live_secret_key'] ) && ( false !== $old_values['gateways_stripe']['live_secret_key'] ) ) {
345 $values['gateways_stripe']['live_secret_key'] = $old_values['gateways_stripe']['live_secret_key'];
346 }
347 if ( ! array_key_exists( 'live_public_key', $values['gateways_stripe'] ) && isset( $old_values['gateways_stripe']['live_public_key'] ) && ( false !== $old_values['gateways_stripe']['live_public_key'] ) ) {
348 $values['gateways_stripe']['live_public_key'] = $old_values['gateways_stripe']['live_secret_key'];
349 }
350 if ( ! array_key_exists( 'test_secret_key', $values['gateways_stripe'] ) && isset( $old_values['gateways_stripe']['test_secret_key'] ) && ( false !== $old_values['gateways_stripe']['test_secret_key'] ) ) {
351 $values['gateways_stripe']['test_secret_key'] = $old_values['gateways_stripe']['test_secret_key'];
352 }
353 if ( ! array_key_exists( 'test_public_key', $values['gateways_stripe'] ) && isset( $old_values['gateways_stripe']['test_public_key'] ) && ( false !== $old_values['gateways_stripe']['test_public_key'] ) ) {
354 $values['gateways_stripe']['test_public_key'] = $old_values['gateways_stripe']['test_public_key'];
355 }
356 if ( charitable_is_debug() ) {
357 error_log( 'update:' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
358 error_log( print_r( $values, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
359 }
360 }
361
362 // determine if stripe is being returned as new values - if so, then perform the API key save workaround.
363 if ( in_array( 'gateways_stripe', $settings_keys ) && charitable()->is_stripe_connect_addon() && false === charitable_using_stripe_connect() ) {
364 if ( charitable_is_debug() ) {
365 error_log( 'there is a stripe connect addon but we are not using stripe connect' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
366 }
367 // the stripe connect addon is installed AND the AM Stripe Connect isn't being used... so it's possible the keys could be manual and the user could be updating them... therefore let's just make sure the new values match the values incoming.
368 $new_values['gateways_stripe']['live_secret_key'] = ( isset( $values['gateways_stripe']['live_secret_key'] ) && ( false !== $values['gateways_stripe']['live_secret_key'] ) ) ? $values['gateways_stripe']['live_secret_key'] : null;
369 $new_values['gateways_stripe']['live_public_key'] = ( isset( $values['gateways_stripe']['live_public_key'] ) && ( false !== $values['gateways_stripe']['live_public_key'] ) ) ? $values['gateways_stripe']['live_public_key'] : null;
370 $new_values['gateways_stripe']['test_secret_key'] = ( isset( $values['gateways_stripe']['test_secret_key'] ) && ( false !== $values['gateways_stripe']['test_secret_key'] ) ) ? $values['gateways_stripe']['test_secret_key'] : null;
371 $new_values['gateways_stripe']['test_public_key'] = ( isset( $values['gateways_stripe']['test_public_key'] ) && ( false !== $values['gateways_stripe']['test_public_key'] ) ) ? $values['gateways_stripe']['test_public_key'] : null;
372 } elseif ( in_array( 'gateways_stripe', $settings_keys ) ) {
373 // otherwise we preserve the keys.
374 if ( charitable_is_debug() ) {
375 error_log( 'charitable debug, final with debug on:' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
376 $new_values['gateways_stripe']['live_secret_key'] = ( isset( $values['gateways_stripe']['live_secret_key'] ) && ( false !== $values['gateways_stripe']['live_secret_key'] ) ) ? $values['gateways_stripe']['live_secret_key'] : null;
377 $new_values['gateways_stripe']['live_public_key'] = ( isset( $values['gateways_stripe']['live_public_key'] ) && ( false !== $values['gateways_stripe']['live_public_key'] ) ) ? $values['gateways_stripe']['live_public_key'] : null;
378 $new_values['gateways_stripe']['test_secret_key'] = ( isset( $values['gateways_stripe']['test_secret_key'] ) && ( false !== $values['gateways_stripe']['test_secret_key'] ) ) ? $values['gateways_stripe']['test_secret_key'] : null;
379 $new_values['gateways_stripe']['test_public_key'] = ( isset( $values['gateways_stripe']['test_public_key'] ) && ( false !== $values['gateways_stripe']['test_public_key'] ) ) ? $values['gateways_stripe']['test_public_key'] : null;
380 } else {
381 $new_values['gateways_stripe']['live_secret_key'] = ( isset( $old_values['gateways_stripe']['live_secret_key'] ) && ( false !== $old_values['gateways_stripe']['live_secret_key'] ) ) ? $old_values['gateways_stripe']['live_secret_key'] : null;
382 $new_values['gateways_stripe']['live_public_key'] = ( isset( $old_values['gateways_stripe']['live_public_key'] ) && ( false !== $old_values['gateways_stripe']['live_public_key'] ) ) ? $old_values['gateways_stripe']['live_public_key'] : null;
383 $new_values['gateways_stripe']['test_secret_key'] = ( isset( $old_values['gateways_stripe']['test_secret_key'] ) && ( false !== $old_values['gateways_stripe']['test_secret_key'] ) ) ? $old_values['gateways_stripe']['test_secret_key'] : null;
384 $new_values['gateways_stripe']['test_public_key'] = ( isset( $old_values['gateways_stripe']['test_public_key'] ) && ( false !== $old_values['gateways_stripe']['test_public_key'] ) ) ? $old_values['gateways_stripe']['test_public_key'] : null;
385 }
386 }
387
388 // preserve any webhook keys, regardless if stripe connect addon is active or not.
389 if ( in_array( 'gateways_stripe', $settings_keys ) ) {
390 if ( ! empty( $old_values['gateways_stripe'] ) && array_key_exists( 'test_webhook_id', $old_values['gateways_stripe'] ) && isset( $old_values['gateways_stripe']['test_webhook_id'] ) ) {
391 $new_values['gateways_stripe']['test_webhook_id'] = $old_values['gateways_stripe']['test_webhook_id'];
392 }
393 if ( ! empty( $old_values['gateways_stripe'] ) && array_key_exists( 'live_webhook_id', $old_values['gateways_stripe'] ) && isset( $old_values['gateways_stripe']['live_webhook_id'] ) ) {
394 $new_values['gateways_stripe']['live_webhook_id'] = $old_values['gateways_stripe']['live_webhook_id'];
395 }
396 }
397
398 // account for incoming legacy licenses (1.7.0.4+)
399 // all we are doing is adding the licenses into the values so that the save_license hook can retreieve them and validate, etc.
400 if ( ! empty( $values['legacy_licenses'] ) && isset( $values['advanced']['section_hidden_licenses'] ) && 'licenses' === $values['advanced']['section_hidden_licenses'] ) {
401 if ( ! isset( $new_values['licenses']['legacy'] ) ) {
402 $new_values['licenses_legacy'] = (array) $values['legacy_licenses'];
403 }
404 }
405
406 // ensure any hex color text files are valid hex fields.
407 if ( isset( $new_values['donation_form_default_highlight_colour'] ) ) {
408 $color_to_test = esc_html( $new_values['donation_form_default_highlight_colour'] );
409 $color_valid = false;
410 // check for a hex color string
411 if ( preg_match( '/^#[a-f0-9]{6}$/i', $color_to_test ) ) {
412 // hex color is valid
413 $color_valid = true;
414 }
415 // Check for a hex color string without hash.
416 if ( preg_match( '/^[a-f0-9]{6}$/i', $color_to_test ) ) {
417 // hex color is valid
418 $color_valid = true;
419 $new_values['donation_form_default_highlight_colour'] = '#' . $color_to_test;
420 }
421 if ( false === $color_valid ) {
422 // not valid, make sure it is blank.
423 $new_values['donation_form_default_highlight_colour'] = false;
424 }
425 // clear the transient that stores the color styles.
426 delete_transient( 'charitable_custom_styles' );
427 }
428
429 $values = wp_parse_args( $new_values, $old_values );
430
431 // Sanitize decimal_count to prevent XSS attacks.
432 if ( isset( $values['decimal_count'] ) ) {
433 $values['decimal_count'] = absint( $values['decimal_count'] );
434 // Ensure decimal_count is between 0 and 10.
435 $values['decimal_count'] = min( max( $values['decimal_count'], 0 ), 10 );
436 }
437
438 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
439 if ( charitable_is_debug() ) {
440 error_log( 'santiize_settings values updated' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
441 error_log( print_r( $values, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
442 }
443 }
444
445 /**
446 * Filter sanitized settings.
447 *
448 * @since 1.0.0
449 *
450 * @param array $values All values, merged.
451 * @param array $new_values Newly submitted values.
452 * @param array $old_values Old settings.
453 */
454
455 $values = apply_filters( 'charitable_save_settings', $values, $new_values, $old_values );
456
457 // save a temp transient for legacy licenses (1.7.0.8+)
458 // purpose is to provide better error reporting (transient should be deleted after settings page (advanced tab) is loaded).
459 // phpcs:disable WordPress.Security.NonceVerification.Missing
460 if ( isset( $_POST['charitable_settings']['legacy_licenses'] ) ) {
461 $referer = wp_get_referer();
462 if ( false !== strpos( $referer, 'admin.php?page=charitable-settings&tab=advanced' ) ) {
463 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Array data, sanitized later
464 $legacy_licenses = wp_unslash( $_POST['charitable_settings']['legacy_licenses'] );
465 set_transient( '_charitable_legacy_license_info', $legacy_licenses, MINUTE_IN_SECONDS );
466 }
467 }
468 // phpcs:enable WordPress.Security.NonceVerification.Missing
469
470 $this->add_update_message( __( 'Settings saved', 'charitable' ), 'success' );
471
472 return $values;
473 }
474
475 /**
476 * Checkbox settings should always be either 1 or 0.
477 *
478 * @since 1.0.0
479 *
480 * @param mixed $value Submitted value for field.
481 * @param array $field Field definition.
482 * @return int
483 */
484 public function sanitize_checkbox_value( $value, $field ) {
485 if ( isset( $field['type'] ) && 'checkbox' == $field['type'] ) {
486 $value = intval( $value && 'on' == $value );
487 }
488
489 return $value;
490 }
491
492 /**
493 * Render field. This is the default callback used for all fields, unless an alternative callback has been specified.
494 *
495 * @since 1.0.0
496 *
497 * @param array $args Field definition.
498 * @return void
499 */
500 public function render_field( $args ) {
501 $field_type = isset( $args['type'] ) ? $args['type'] : 'text';
502
503 charitable_admin_view( 'settings/' . $field_type, $args );
504 }
505
506 /**
507 * Returns an array of all pages in the id=>title format.
508 *
509 * @since 1.0.0
510 *
511 * @return string[]
512 */
513 public function get_pages() {
514 if ( ! isset( $this->pages ) ) {
515 $this->pages = charitable_get_pages_options();
516 }
517
518 return $this->pages;
519 }
520
521 /**
522 * Add an update message.
523 *
524 * @since 1.4.6
525 *
526 * @param string $message The message text.
527 * @param string $type The type of message. Options: 'error', 'success', 'warning', 'info'.
528 * @param boolean $dismissible Whether the message can be dismissed.
529 * @return void
530 */
531 public function add_update_message( $message, $type = 'error', $dismissible = true ) {
532 if ( ! in_array( $type, array( 'error', 'success', 'warning', 'info' ) ) ) {
533 $type = 'error';
534 }
535
536 charitable_get_admin_notices()->add_notice( $message, $type, false, $dismissible );
537 }
538
539 /**
540 * Recursively add settings fields, given an array.
541 *
542 * @since 1.0.0
543 * @version 1.8.7
544 *
545 * @param array $field The setting field.
546 * @param array $keys Array containing the section key and field key.
547 * @return void
548 */
549 private function register_field( $field, $keys ) {
550 $section_id = 'charitable_settings_' . $keys[0];
551
552 if ( isset( $field['render'] ) && ! $field['render'] ) {
553 return;
554 }
555
556 /* Drop the first key, which is the section identifier */
557 $field['name'] = implode( '][', $keys );
558
559 if ( ! $this->is_dynamic_group( $keys[0] ) ) {
560 array_shift( $keys );
561 }
562
563 $field['key'] = $keys;
564 $field['classes'] = $this->get_field_classes( $field ); // deprecated?
565 $field['class'] = $this->get_field_classes( $field );
566 $callback = isset( $field['callback'] ) ? $field['callback'] : array( $this, 'render_field' );
567 $label = $this->get_field_label( $field, end( $keys ) );
568
569 add_settings_field(
570 sprintf( 'charitable_settings_%s', implode( '_', $keys ) ),
571 $label,
572 $callback,
573 $section_id,
574 $section_id,
575 $field
576 );
577 }
578
579 /**
580 * Return the label for the given field.
581 *
582 * @since 1.0.0
583 *
584 * @param array $field The field definition.
585 * @param string $key The field key.
586 * @return string
587 */
588 private function get_field_label( $field, $key ) { // phpcs:ignore
589 $label = '';
590
591 if ( isset( $field['label_for'] ) ) {
592 $label = $field['label_for'];
593 }
594
595 if ( isset( $field['title'] ) ) {
596 $label = $field['title'];
597 }
598
599 return $label;
600 }
601
602 /**
603 * Return a space separated string of classes for the given field.
604 *
605 * @since 1.0.0
606 *
607 * @param array $field Field definition.
608 * @return string
609 */
610 private function get_field_classes( $field ) {
611 $classes = array( 'charitable-settings-field' );
612
613 if ( isset( $field['class'] ) ) {
614 $classes[] = $field['class'];
615 }
616
617 /**
618 * Filter the list of classes to apply to settings fields.
619 *
620 * @since 1.0.0
621 *
622 * @param array $classes The list of classes.
623 * @param array $field The field definition.
624 */
625 $classes = apply_filters( 'charitable_settings_field_classes', $classes, $field );
626
627 return implode( ' ', $classes );
628 }
629
630 /**
631 * Return an array with all the fields & sections to be displayed.
632 *
633 * @uses charitable_settings_fields
634 * @see Charitable_Settings::register_setting()
635 * @since 1.0.0
636 *
637 * @return array
638 */
639 private function get_fields() {
640 /**
641 * Use the charitable_settings_tab_fields to include the fields for new tabs.
642 * DO NOT use it to add individual fields. That should be done with the
643 * filters within each of the methods.
644 */
645 $fields = array();
646
647 foreach ( $this->get_sections() as $section_key => $section ) {
648 /**
649 * Filter the array of fields to display in a particular tab.
650 *
651 * @since 1.0.0
652 *
653 * @param array $fields Array of fields.
654 */
655 $fields[ $section_key ] = apply_filters( 'charitable_settings_tab_fields_' . $section_key, array() );
656 }
657
658 /**
659 * Filter the array of settings fields.
660 *
661 * @since 1.0.0
662 *
663 * @param array $fields Array of fields.
664 */
665 return apply_filters( 'charitable_settings_tab_fields', $fields );
666 }
667
668 /**
669 * Get the submitted value for a particular setting.
670 *
671 * @since 1.0.0
672 *
673 * @param string $key The key of the setting being saved.
674 * @param array $field The setting field.
675 * @param array $submitted The submitted values.
676 * @param string $section The section being saved.
677 * @return mixed|null Returns null if the value was not submitted or is not applicable.
678 */
679 private function get_setting_submitted_value( $key, $field, $submitted, $section ) {
680 $value = null;
681
682 if ( isset( $field['save'] ) && ! $field['save'] ) {
683 return $value;
684 }
685
686 $field_type = isset( $field['type'] ) ? $field['type'] : '';
687
688 switch ( $field_type ) {
689
690 case 'checkbox':
691 $value = intval( array_key_exists( $key, $submitted ) && ( 'on' === $submitted[ $key ] || 1 === (int) $submitted[ $key ] ) );
692 break;
693
694 case 'multi-checkbox':
695 $value = isset( $submitted[ $key ] ) ? $submitted[ $key ] : array();
696 break;
697
698 case '':
699 case 'heading':
700 return $value;
701
702 default:
703 if ( ! array_key_exists( $key, $submitted ) ) {
704 return $value;
705 }
706
707 $value = $submitted[ $key ];
708
709 }//end switch
710
711 /**
712 * General way to sanitize values. If you only need to sanitize a
713 * specific setting, used the filter below instead.
714 *
715 * @since 1.0.0
716 *
717 * @param mixed $value The current setting value.
718 * @param array $field The field configuration.
719 * @param array $submitted All submitted data.
720 * @param string $key The setting key.
721 * @param string $section The section being saved.
722 */
723 $value = apply_filters( 'charitable_sanitize_value', $value, $field, $submitted, $key, $section );
724
725 /**
726 * Sanitize the setting value.
727 *
728 * The filter hook is formatted like this: charitable_sanitize_value_{$section}_{$key}.
729 *
730 * @since 1.5.0
731 *
732 * @param mixed $value The current setting value.
733 * @param array $field The field configuration.
734 * @param array $submitted All submitted data.
735 */
736 return apply_filters( 'charitable_sanitize_value_' . $section . '_' . $key, $value, $field, $submitted );
737 }
738
739 /**
740 * Return the submitted values for the given section.
741 *
742 * @since 1.0.0
743 *
744 * @param string $section The section being edited.
745 * @param array $submitted The submitted values.
746 * @return array
747 */
748 private function get_section_submitted_values( $section, $submitted ) {
749 $values = array();
750 $form_fields = $this->get_fields();
751
752 if ( ! isset( $form_fields[ $section ] ) ) {
753 return $values;
754 }
755
756 foreach ( $form_fields[ $section ] as $key => $field ) {
757 $value = $this->get_setting_submitted_value( $key, $field, $submitted, $section );
758
759 if ( is_null( $value ) ) {
760 continue;
761 }
762
763 if ( $this->is_dynamic_group( $section ) ) {
764 $values[ $section ][ $key ] = $value;
765 continue;
766 }
767
768 $values[ $key ] = $value;
769 }
770
771 return $values;
772 }
773
774 /**
775 * Return list of dynamic groups.
776 *
777 * @since 1.0.0
778 *
779 * @return string[]
780 */
781 private function get_dynamic_groups() {
782 if ( ! isset( $this->dynamic_groups ) ) {
783 /**
784 * Filter the list of dynamic groups.
785 *
786 * @since 1.0.0
787 *
788 * @param array $groups The dynamic groups.
789 */
790 $this->dynamic_groups = apply_filters( 'charitable_dynamic_groups', array() );
791 }
792
793 return $this->dynamic_groups;
794 }
795
796 /**
797 * Returns whether the given key indicates the start of a new section of the settings.
798 *
799 * @since 1.0.0
800 *
801 * @param string $composite_key The unique key for this group.
802 * @return boolean
803 */
804 private function is_dynamic_group( $composite_key ) {
805 return array_key_exists( $composite_key, $this->get_dynamic_groups() );
806 }
807
808 /* DEPRECATED FUNCTIONS */
809
810 /**
811 * Get the update messages.
812 *
813 * @deprecated 1.7.0
814 *
815 * @since 1.4.13 Deprecated.
816 */
817 public function get_update_messages() {
818 charitable_get_deprecated()->deprecated_function(
819 __METHOD__,
820 '1.4.13',
821 'Charitable_Admin_Notices::get_notices()'
822 );
823
824 return charitable_get_admin_notices()->get_notices();
825 }
826
827 /**
828 * Display upgrade notice at the bottom on the plugin settings pages.
829 *
830 * @since 1.7.0.4
831 * @version 1.8.8
832 *
833 * @param string $view Current view inside the plugin settings page.
834 * @param string $css_class CSS class for the cta.
835 * @param bool $show_close_button Whether to show the close button.
836 */
837 public function settings_cta( $view = false, $css_class = 'reports-lite-cta', $show_close_button = false ) { // phpcs:ignore
838
839 $no_show_cta = array(
840 'donors',
841 'security',
842 'marketing',
843 );
844
845 if ( in_array( $view, $no_show_cta ) ) {
846 return;
847 }
848
849 if ( charitable_is_pro() ) {
850 // no need to display this cta since they have a valid license.
851 return;
852 }
853
854 if ( get_option( 'charitable_lite_settings_upgrade', false ) || apply_filters( 'charitable_lite_settings_upgrade', false ) ) {
855 return;
856 }
857 ?>
858 <?php if ( $show_close_button ) : ?>
859 <div class="charitable-cta-dismiss-container">
860 <button type="button" class="button-link charitable-banner-dismiss dismiss">x</button>
861 </div>
862 <?php endif; ?>
863 <?php charitable_render_global_upgrade_cta( $css_class ); ?>
864 <script type="text/javascript">
865 jQuery( function ( $ ) {
866 $( document ).on( 'click', '.settings-lite-cta .dismiss', function ( event ) {
867 event.preventDefault();
868 $.post( ajaxurl, {
869 action: 'charitable_lite_settings_upgrade',
870 charitable_action: 'remove_lite_cta'
871 } );
872 $( '.settings-lite-cta' ).remove();
873 } );
874 } );
875 </script>
876 <?php
877 }
878
879 /**
880 * Show the settings CTA.
881 *
882 * @since 1.8.5.1
883 * @version 1.8.9
884 *
885 * @return void
886 */
887 public function show_settings_cta() {
888
889 $tab_values_to_check = array(
890 'marketing',
891 'donors',
892 'security',
893 );
894
895 $tab_value = isset( $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : ''; // phpcs:ignore
896
897 if ( in_array( $tab_value, $tab_values_to_check ) ) { // phpcs:ignore
898 ob_start();
899 ?>
900
901 <div class="charitable-education-page">
902
903 <?php
904 if ( 'marketing' === $tab_value ) {
905 $this->get_settings_cta_education_content_marketing_settings();
906 } elseif ( 'donors' === $tab_value ) {
907 $this->get_settings_cta_education_content_donor_settings();
908 } elseif ( 'security' === $tab_value && ! charitable_is_pro() ) {
909 $this->get_settings_cta_education_content_advanced_security_settings();
910 }
911 ?>
912 <?php if ( charitable_is_pro() ) : ?>
913 <div class="charitable-education-page-disabled-download">
914 <p>
915 <?php
916 printf(
917 /* translators: %s: Documentation URL */
918 esc_html__( 'Please add your license and upgrade to Charitable Pro for more advanced security features. Please %s on how to download Charitable Pro.', 'charitable' ),
919 '<a href="https://wpcharitable.com/docs/download-charitable-pro/" target="_blank">' . esc_html__( 'see our documentation', 'charitable' ) . '</a>'
920 );
921 ?>
922 </p>
923 </div>
924 <?php else : ?>
925 <div class="charitable-education-page-button">
926 <a href="<?php echo esc_url( charitable_utm_link( 'https://wpcharitable.com/lite-upgrade/', $tab_value, __( 'Upgrade to Charitable Pro', 'charitable' ) ) ); ?>" class="button button-primary" target="_blank"><?php esc_html_e( 'Upgrade to Charitable Pro', 'charitable' ); ?></a>
927 </div>
928 <?php endif; ?>
929 </div>
930 <?php
931 echo ob_get_clean(); // phpcs:ignore
932 }
933 }
934
935 /**
936 * Get the settings CTA education content for the donor referrals sub tab.
937 *
938 * @since 1.8.5.1
939 *
940 * @return void
941 */
942 public function get_settings_cta_education_content_donor_settings() {
943 ?>
944
945 <div class="charitable-education-page-heading">
946 <h4><?php esc_html_e( 'New Donor Features', 'charitable' ); ?></h4>
947 <p><?php esc_html_e( 'Give donors the power to manage their contributions to your organization with a new donor dashboard. Encourage donors to spread the word about your organization by offering rewards for successful referrals. And give donors the ability to post comments to campaigns when they donate.', 'charitable' ); ?></p>
948 <p><?php esc_html_e( 'All of this is possible with the Charitable Pro plugin.', 'charitable' ); ?></p>
949 </div>
950
951 <div class="charitable-education-page-media">
952 <div class="charitable-education-page-images">
953 <figure>
954 <div class="charitable-education-page-images-image">
955 <img src="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/donor-dashboard/education-1.png" alt="<?php esc_attr_e( 'Donor Dashboard', 'charitable' ); ?>">
956 <a href="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/donor-dashboard/education-1.png" class="hover" data-lity="" data-lity-desc="<?php esc_attr_e( 'Donor Dashboard', 'charitable' ); ?>"></a>
957 </div>
958 <figcaption><?php esc_html_e( 'Donor Dashboard', 'charitable' ); ?></figcaption>
959 </figure>
960 <figure>
961 <div class="charitable-education-page-images-image">
962 <img src="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/donor-referrals/education-2.png" alt="<?php esc_attr_e( 'Donor Referrals', 'charitable' ); ?>">
963 <a href="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/donor-referrals/education-2.png" class="hover" data-lity="" data-lity-desc="<?php esc_attr_e( 'Donor Referrals', 'charitable' ); ?>"></a>
964 </div>
965 <figcaption><?php esc_html_e( 'Donor Referrals', 'charitable' ); ?></figcaption>
966 </figure>
967 <figure>
968 <div class="charitable-education-page-images-image">
969 <img src="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/donor-comments/education-3.png" alt="<?php esc_attr_e( 'Donor Comments', 'charitable' ); ?>">
970 <a href="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/donor-comments/education-3.png" class="hover" data-lity="" data-lity-desc="<?php esc_attr_e( 'Donor Comments', 'charitable' ); ?>"></a>
971 </div>
972 <figcaption><?php esc_html_e( 'Donor Comments', 'charitable' ); ?></figcaption>
973 </figure>
974 </div>
975 </div>
976
977 <div class="charitable-education-page-caps">
978 <p><?php esc_html_e( 'Give donors the power to manage their contributions to your organization.', 'charitable' ); ?></p>
979 <ul>
980 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Increased Donor Engagement', 'charitable' ); ?></li>
981 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Customizable Dashboard', 'charitable' ); ?></li>
982 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Donation History', 'charitable' ); ?></li>
983 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'View Campaigns', 'charitable' ); ?></li>
984 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'View Comments', 'charitable' ); ?></li>
985 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Download Donation Receipts', 'charitable' ); ?></li>
986 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Tracking Personalized Notifications', 'charitable' ); ?></li>
987 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Donor Profile Management', 'charitable' ); ?></li>
988 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'See Each Donor\'s Referrals', 'charitable' ); ?></li>
989 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Track Referral Success Rates', 'charitable' ); ?></li>
990 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Customizable Referral Rewards', 'charitable' ); ?></li>
991 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Easy Promotion of Campaigns', 'charitable' ); ?></li>
992
993 </ul>
994 </div>
995 <?php
996 }
997
998 /**
999 * Get the settings CTA education content for the donor referrals sub tab.
1000 *
1001 * @since 1.8.5.1
1002 *
1003 * @return void
1004 */
1005 public function get_settings_cta_education_content_marketing_settings() {
1006 ?>
1007
1008 <div class="charitable-education-page-heading">
1009 <h4><?php esc_html_e( 'Marketing', 'charitable' ); ?></h4>
1010 <p><?php esc_html_e( 'Turn one-time donors into ongoing supporters with your newsletter list. This feature allows you to connect to your email marketing account and automatically add donors to your email newsletter list. You can integrate your campaigns seamlessly with MailChimp, Campaign Monitor, MailerLite, ActiveCampaign, Mailster and MailPoet, allowing you to enable one or more providers.', 'charitable' ); ?></p>
1011 </div>
1012
1013 <div class="charitable-education-page-media">
1014 <div class="charitable-education-page-images">
1015 <figure>
1016 <div class="charitable-education-page-images-image">
1017 <img src="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/marketing/education-1.png" alt="<?php esc_attr_e( 'Marketing', 'charitable' ); ?>">
1018 <a href="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/marketing/education-1.png" class="hover" data-lity="" data-lity-desc="<?php esc_attr_e( 'Marketing', 'charitable' ); ?>"></a>
1019 </div>
1020 <figcaption><?php esc_html_e( 'Choose a default list', 'charitable' ); ?></figcaption>
1021 </figure>
1022 <figure>
1023 <div class="charitable-education-page-images-image">
1024 <img src="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/marketing/education-2.png" alt="<?php esc_attr_e( 'Marketing', 'charitable' ); ?>">
1025 <a href="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/marketing/education-2.png" class="hover" data-lity="" data-lity-desc="<?php esc_attr_e( 'Marketing', 'charitable' ); ?>"></a>
1026 </div>
1027 <figcaption><?php esc_html_e( 'Sync lists', 'charitable' ); ?></figcaption>
1028 </figure>
1029 <figure>
1030 <div class="charitable-education-page-images-image">
1031 <img src="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/marketing/education-3.png" alt="<?php esc_attr_e( 'Marketing', 'charitable' ); ?>">
1032 <a href="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/marketing/education-3.png" class="hover" data-lity="" data-lity-desc="<?php esc_attr_e( 'Marketing', 'charitable' ); ?>"></a>
1033 </div>
1034 <figcaption><?php esc_html_e( 'Donation form options', 'charitable' ); ?></figcaption>
1035 </figure>
1036 </div>
1037 </div>
1038
1039 <div class="charitable-education-page-caps">
1040 <p><?php esc_html_e( 'Turn one-time donors into ongoing supporters.', 'charitable' ); ?></p>
1041 <ul>
1042 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Easy Integration', 'charitable' ); ?></li>
1043 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Built-in contact consent options', 'charitable' ); ?></li>
1044 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Automatically add donors to your email list', 'charitable' ); ?></li>
1045 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Organize donors with tags', 'charitable' ); ?></li>
1046 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Send email series to keep donors engaged', 'charitable' ); ?></li>
1047 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Enable multiple email services', 'charitable' ); ?></li>
1048 </ul>
1049 </div>
1050 <?php
1051 }
1052
1053 /**
1054 * Get the settings CTA education content for the donor referrals sub tab.
1055 *
1056 * @since 1.8.5.1
1057 *
1058 * @return void
1059 */
1060 public function get_settings_cta_education_content_security_settings() {
1061 ?>
1062
1063 <div class="charitable-education-page-heading">
1064 <h4><?php esc_html_e( 'Security', 'charitable' ); ?></h4>
1065 <p><?php esc_html_e( 'It is important to protect against potential misuse while maintaining donor trust. CAPTCHA integration adds an essential layer of protection against automated attacks and spam donations. Every transaction is meticulously logged with IP address documentation and timestamp verification, providing a clear audit trail and helping prevent fraudulent activities.', 'charitable' ); ?></p>
1066 </div>
1067
1068 <div class="charitable-education-page-media">
1069 <div class="charitable-education-page-images">
1070 <figure>
1071 <div class="charitable-education-page-images-image">
1072 <img src="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/security/education-1.png" alt="<?php esc_attr_e( 'Security', 'charitable' ); ?>">
1073 <a href="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/security/education-1.png" class="hover" data-lity="" data-lity-desc="<?php esc_attr_e( 'Security', 'charitable' ); ?>"></a>
1074 </div>
1075 <figcaption><?php esc_html_e( 'Captchas', 'charitable' ); ?></figcaption>
1076 </figure>
1077 <figure>
1078 <div class="charitable-education-page-images-image">
1079 <img src="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/security/education-3.png" alt="<?php esc_attr_e( 'Security', 'charitable' ); ?>">
1080 <a href="<?php echo esc_url( charitable()->get_path( 'assets', false ) ); ?>images/education/security/education-3.png" class="hover" data-lity="" data-lity-desc="<?php esc_attr_e( 'Security', 'charitable' ); ?>"></a>
1081 </div>
1082 <figcaption><?php esc_html_e( 'Form options', 'charitable' ); ?></figcaption>
1083 </figure>
1084 </div>
1085 </div>
1086
1087 <div class="charitable-education-page-caps">
1088 <p><?php esc_html_e( 'Protect against potential misuse while maintaining donor trust.', 'charitable' ); ?></p>
1089 <ul>
1090 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Easy Activation', 'charitable' ); ?></li>
1091 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Record IP Addresses', 'charitable' ); ?></li>
1092 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Block Bad Bots', 'charitable' ); ?></li>
1093 </ul>
1094 </div>
1095 <?php
1096 }
1097
1098 /**
1099 * Get the settings CTA education content for advanced security features.
1100 *
1101 * @since 1.8.9
1102 *
1103 * @return void
1104 */
1105 public function get_settings_cta_education_content_advanced_security_settings() {
1106 ?>
1107
1108 <div class="charitable-education-page-heading">
1109 <h4><?php esc_html_e( 'Advanced Security Features', 'charitable' ); ?></h4>
1110 <p><?php esc_html_e( 'Charitable Pro offers a fortified layer of safety designed for high-volume campaigns. Upgrade to unlock sophisticated spam filtering and enhanced payment protection, ensuring the highest level of trust and security for your donors.', 'charitable' ); ?></p>
1111 </div>
1112 <div class="charitable-education-page-caps">
1113 <p><?php esc_html_e( 'Protect against potential misuse while maintaining donor trust.', 'charitable' ); ?></p>
1114 <ul>
1115 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'General Rate Limiting', 'charitable' ); ?></li>
1116 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Stripe Rate Limiting', 'charitable' ); ?></li>
1117 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'IP Blacklist/Whitelist', 'charitable' ); ?></li>
1118 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Email Pattern Blocking', 'charitable' ); ?></li>
1119 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Minimum Donation Features', 'charitable' ); ?></li>
1120 <li><i class="fa fa-solid fa-check"></i> <?php esc_html_e( 'Math Field Protection', 'charitable' ); ?></li>
1121 </ul>
1122 </div>
1123 <?php
1124 }
1125
1126 /**
1127 * Dismiss upgrade notice at the bottom on the plugin settings pages.
1128 *
1129 * @since 1.7.0.4
1130 *
1131 * @return void
1132 */
1133 public function settings_cta_dismiss() {
1134
1135 if ( ! charitable_current_user_can() ) {
1136 wp_send_json_error();
1137 }
1138
1139 update_option( 'charitable_lite_settings_upgrade', time() );
1140
1141 wp_send_json_success();
1142 }
1143
1144 /**
1145 * Check Square connection for the new mode.
1146 *
1147 * @since 1.8.5
1148 *
1149 * @param string $new_mode The new test mode.
1150 * @return boolean True if Square has connection issues, false otherwise.
1151 */
1152 private function check_square_connection_for_mode_change( $new_mode ) {
1153 // Check if Square gateway is active.
1154 $active_gateways = charitable_get_helper( 'gateways' )->get_active_gateways();
1155
1156 if ( ! is_array( $active_gateways ) || ! in_array( 'Charitable_Gateway_Square', $active_gateways ) ) {
1157 return false;
1158 }
1159
1160 // Determine the mode string.
1161 $mode = $new_mode ? 'test' : 'live';
1162
1163 // Check if there's a Square connection for this mode.
1164 $connection = Charitable_Square_Connection::get( $mode );
1165
1166 // Check if connection exists and is properly configured.
1167 if ( ! $connection || ! $connection->is_configured() || ! $connection->is_valid() ) {
1168
1169 // Set a warning flag that will be checked on the payment page.
1170 update_option( 'charitable_square_mode_connection_warning', array(
1171 'mode' => $mode,
1172 'timestamp' => time(),
1173 ) );
1174
1175 return true; // Square has connection issues.
1176 } else {
1177 // Clear the warning if connection exists.
1178 delete_option( 'charitable_square_mode_connection_warning' );
1179 return false; // Square has no connection issues.
1180 }
1181 }
1182 }
1183
1184 endif;
1185