| 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', [ $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 |
* |
| 92 |
* @param string[] $tabs List of tabs in key=>label format. |
| 93 |
*/ |
| 94 |
return apply_filters( |
| 95 |
'charitable_settings_tabs', |
| 96 |
array( |
| 97 |
'general' => __( 'General', 'charitable' ), |
| 98 |
'gateways' => __( 'Payment Gateways', 'charitable' ), |
| 99 |
'emails' => __( 'Emails', 'charitable' ), |
| 100 |
'privacy' => __( 'Privacy', 'charitable' ), |
| 101 |
'tools' => __( 'Tools', 'charitable' ), |
| 102 |
'advanced' => __( 'Advanced', 'charitable' ), |
| 103 |
) |
| 104 |
); |
| 105 |
|
| 106 |
} |
| 107 |
|
| 108 |
/** |
| 109 |
* Optionally add the extensions tab. |
| 110 |
* |
| 111 |
* @since 1.3.0 |
| 112 |
* |
| 113 |
* @param string[] $tabs The existing set of tabs. |
| 114 |
* @return string[] |
| 115 |
*/ |
| 116 |
public function maybe_add_extensions_tab( $tabs ) { |
| 117 |
$actual_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : 'general'; |
| 118 |
|
| 119 |
/* Set the tab to 'extensions' */ |
| 120 |
$_GET['tab'] = 'extensions'; |
| 121 |
|
| 122 |
/** |
| 123 |
* Filter the settings in the extensions tab. |
| 124 |
* |
| 125 |
* @since 1.3.0 |
| 126 |
* |
| 127 |
* @param array $fields Array of fields. Empty by default. |
| 128 |
*/ |
| 129 |
$settings = apply_filters( 'charitable_settings_tab_fields_extensions', array() ); |
| 130 |
|
| 131 |
/* Set the tab back to whatever it actually is */ |
| 132 |
$_GET['tab'] = $actual_tab; |
| 133 |
|
| 134 |
if ( ! empty( $settings ) ) { |
| 135 |
$tabs = charitable_add_settings_tab( |
| 136 |
$tabs, |
| 137 |
'extensions', |
| 138 |
__( 'Extensions', 'charitable' ), |
| 139 |
array( |
| 140 |
'index' => 4, |
| 141 |
) |
| 142 |
); |
| 143 |
} |
| 144 |
|
| 145 |
return $tabs; |
| 146 |
} |
| 147 |
|
| 148 |
/** |
| 149 |
* Add the hidden "extensions" section field. |
| 150 |
* |
| 151 |
* @since 1.6.7 |
| 152 |
* |
| 153 |
* @param array $fields All the settings fields. |
| 154 |
* @return array |
| 155 |
*/ |
| 156 |
public function add_hidden_extensions_setting_field( $fields ) { |
| 157 |
if ( ! array_key_exists( 'extensions', $fields ) ) { |
| 158 |
return $fields; |
| 159 |
} |
| 160 |
|
| 161 |
$fields['extensions']['section'] = array( |
| 162 |
'title' => '', |
| 163 |
'type' => 'hidden', |
| 164 |
'priority' => 10000, |
| 165 |
'value' => 'extensions', |
| 166 |
'save' => false, |
| 167 |
); |
| 168 |
|
| 169 |
return $fields; |
| 170 |
} |
| 171 |
|
| 172 |
/** |
| 173 |
* Register setting. |
| 174 |
* |
| 175 |
* @since 1.0.0 |
| 176 |
* |
| 177 |
* @return void |
| 178 |
*/ |
| 179 |
public function register_settings() { |
| 180 |
if ( ! charitable_is_settings_view() ) { |
| 181 |
return; |
| 182 |
} |
| 183 |
|
| 184 |
register_setting( 'charitable_settings', 'charitable_settings', array( $this, 'sanitize_settings' ) ); |
| 185 |
|
| 186 |
$fields = $this->get_fields(); |
| 187 |
|
| 188 |
if ( empty( $fields ) ) { |
| 189 |
return; |
| 190 |
} |
| 191 |
|
| 192 |
$sections = array_merge( $this->get_sections(), $this->get_dynamic_groups() ); |
| 193 |
|
| 194 |
/* Register each section */ |
| 195 |
foreach ( $sections as $section_key => $section ) { |
| 196 |
$section_id = 'charitable_settings_' . $section_key; |
| 197 |
|
| 198 |
add_settings_section( |
| 199 |
$section_id, |
| 200 |
__return_null(), |
| 201 |
'__return_false', |
| 202 |
$section_id |
| 203 |
); |
| 204 |
|
| 205 |
if ( ! isset( $fields[ $section_key ] ) || empty( $fields[ $section_key ] ) ) { |
| 206 |
continue; |
| 207 |
} |
| 208 |
|
| 209 |
/* Sort by priority */ |
| 210 |
$section_fields = $fields[ $section_key ]; |
| 211 |
uasort( $section_fields, 'charitable_priority_sort' ); |
| 212 |
|
| 213 |
/* Add the individual fields within the section */ |
| 214 |
foreach ( $section_fields as $key => $field ) { |
| 215 |
$this->register_field( $field, array( $section_key, $key ) ); |
| 216 |
} |
| 217 |
} |
| 218 |
} |
| 219 |
|
| 220 |
/** |
| 221 |
* Sanitize submitted settings before saving to the database |
| 222 |
* This includes: "workaround" for Stripe keys with 1.7.0.0, additional settings options after 1.7.0.6+ |
| 223 |
* |
| 224 |
* @since 1.0.0 |
| 225 |
* |
| 226 |
* @param array $values The submitted values. |
| 227 |
* @return string |
| 228 |
*/ |
| 229 |
public function sanitize_settings( $values ) { |
| 230 |
|
| 231 |
$old_values = get_option( 'charitable_settings', array() ); |
| 232 |
$new_values = array(); |
| 233 |
|
| 234 |
if ( ! is_array( $old_values ) ) { |
| 235 |
$old_values = array(); |
| 236 |
} |
| 237 |
|
| 238 |
if ( ! is_array( $values ) ) { |
| 239 |
$values = array(); |
| 240 |
} |
| 241 |
|
| 242 |
/* Loop through all fields, merging the submitted values into the master array */ |
| 243 |
foreach ( $values as $section => $submitted ) { |
| 244 |
$new_values = array_merge( $new_values, $this->get_section_submitted_values( $section, $submitted ) ); |
| 245 |
} |
| 246 |
|
| 247 |
$settings_keys = array_keys( $new_values ); |
| 248 |
|
| 249 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 250 |
error_log( 'santiize_settings' ); |
| 251 |
error_log( 'values:' ); |
| 252 |
error_log( print_r( $values, true ) ); |
| 253 |
error_log( 'old_values:' ); |
| 254 |
error_log( print_r( $old_values, true ) ); |
| 255 |
error_log( 'new_values:' ); |
| 256 |
error_log( print_r( $new_values, true ) ); |
| 257 |
} |
| 258 |
|
| 259 |
// determine if Charitable gateway "test mode" is being changed, and if so add a notice to the user. |
| 260 |
if ( array_key_exists('test_mode', $new_values ) && array_key_exists('test_mode', $old_values ) && $old_values['test_mode'] !== $new_values['test_mode'] ) { |
| 261 |
$old_settings_keys = array_keys( $old_values ); |
| 262 |
$dismissible = true; |
| 263 |
if ( in_array( 'gateways_stripe', $old_settings_keys ) ) { |
| 264 |
charitable_get_admin_notices()->add_notice( 'Some active payment gateways <strong>(including Stripe)</strong> might have reset their connections due to an update in the test mode. Please check your active payment gateways in ensure they are still connected.', 'warning', false, $dismissible ); |
| 265 |
} else { |
| 266 |
charitable_get_admin_notices()->add_notice( 'Some active payment gateways might have reset their connections due to an update in the test mode. Please check your active payment gateways in ensure they are still connected.', 'warning', false, $dismissible ); |
| 267 |
} |
| 268 |
} |
| 269 |
|
| 270 |
if ( in_array( 'gateways_stripe', $settings_keys ) && charitable()->is_stripe_connect_addon() && false === charitable_using_stripe_connect() ) { |
| 271 |
// 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. |
| 272 |
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'] ) ) { |
| 273 |
$values['gateways_stripe']['live_secret_key'] = $old_values['gateways_stripe']['live_secret_key']; |
| 274 |
} |
| 275 |
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'] ) ) { |
| 276 |
$values['gateways_stripe']['live_public_key'] = $old_values['gateways_stripe']['live_secret_key']; |
| 277 |
} |
| 278 |
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'] ) ) { |
| 279 |
$values['gateways_stripe']['test_secret_key'] = $old_values['gateways_stripe']['test_secret_key']; |
| 280 |
} |
| 281 |
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'] ) ) { |
| 282 |
$values['gateways_stripe']['test_public_key'] = $old_values['gateways_stripe']['test_public_key']; |
| 283 |
} |
| 284 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 285 |
error_log( 'update:' ); |
| 286 |
error_log( print_r( $values, true ) ); |
| 287 |
} |
| 288 |
} |
| 289 |
|
| 290 |
// determine if stripe is being returned as new values - if so, then perform the API key save workaround |
| 291 |
if ( in_array( 'gateways_stripe', $settings_keys ) && charitable()->is_stripe_connect_addon() && false === charitable_using_stripe_connect() ) { |
| 292 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 293 |
error_log( 'there is a stripe connect addon but we are not using stripe connect' ); |
| 294 |
} |
| 295 |
// 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. |
| 296 |
$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; |
| 297 |
$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; |
| 298 |
$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; |
| 299 |
$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; |
| 300 |
} else if ( in_array( 'gateways_stripe', $settings_keys ) ) { |
| 301 |
// otherwise we preserve the keys |
| 302 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 303 |
error_log( 'charitable debug, final with debug on:' ); |
| 304 |
$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; |
| 305 |
$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; |
| 306 |
$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; |
| 307 |
$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; |
| 308 |
} else { |
| 309 |
$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; |
| 310 |
$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; |
| 311 |
$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; |
| 312 |
$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; |
| 313 |
} |
| 314 |
} |
| 315 |
|
| 316 |
// preserve any webhook keys, regardless if stripe connect addon is active or not. |
| 317 |
if ( in_array( 'gateways_stripe', $settings_keys ) ) { |
| 318 |
if ( array_key_exists( 'test_webhook_id', $old_values['gateways_stripe'] ) && isset( $old_values['gateways_stripe']['test_webhook_id'] ) ) { |
| 319 |
$new_values['gateways_stripe']['test_webhook_id'] = $old_values['gateways_stripe']['test_webhook_id']; |
| 320 |
} |
| 321 |
if ( array_key_exists( 'live_webhook_id', $old_values['gateways_stripe'] ) && isset( $old_values['gateways_stripe']['live_webhook_id'] ) ) { |
| 322 |
$new_values['gateways_stripe']['live_webhook_id'] = $old_values['gateways_stripe']['live_webhook_id']; |
| 323 |
} |
| 324 |
} |
| 325 |
|
| 326 |
// account for incoming legacy licenses (1.7.0.4+) |
| 327 |
// all we are doing is adding the licenses into the values so that the save_license hook can retreieve them and validate, etc. |
| 328 |
if ( ! empty( $values['legacy_licenses'] ) && isset( $values['advanced']['section'] ) && 'licenses' === $values['advanced']['section'] ) { |
| 329 |
if ( ! isset( $new_values['licenses']['legacy'] ) ) { |
| 330 |
$new_values['licenses_legacy'] = (array) $values['legacy_licenses']; |
| 331 |
} |
| 332 |
} |
| 333 |
|
| 334 |
// ensure any hex color text files are valid hex fields |
| 335 |
if ( isset( $new_values['donation_form_default_highlight_colour'] ) ) { |
| 336 |
$color_to_test = esc_html( $new_values['donation_form_default_highlight_colour'] ); |
| 337 |
$color_valid = false; |
| 338 |
// check for a hex color string |
| 339 |
if ( preg_match( '/^#[a-f0-9]{6}$/i', $color_to_test ) ) { |
| 340 |
// hex color is valid |
| 341 |
$color_valid = true; |
| 342 |
} |
| 343 |
// Check for a hex color string without hash |
| 344 |
if ( preg_match( '/^[a-f0-9]{6}$/i', $color_to_test ) ) { |
| 345 |
// hex color is valid |
| 346 |
$color_valid = true; |
| 347 |
$new_values['donation_form_default_highlight_colour'] = '#' . $color_to_test; |
| 348 |
} |
| 349 |
if ( false === $color_valid ) { |
| 350 |
// not valid, make sure it is blank. |
| 351 |
$new_values['donation_form_default_highlight_colour'] = false; |
| 352 |
} |
| 353 |
// clear the transient that stores the color styles |
| 354 |
delete_transient( 'charitable_custom_styles' ); |
| 355 |
} |
| 356 |
|
| 357 |
$values = wp_parse_args( $new_values, $old_values ); |
| 358 |
|
| 359 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 360 |
error_log( 'santiize_settings values updated' ); |
| 361 |
error_log( print_r( $values, true ) ); |
| 362 |
} |
| 363 |
|
| 364 |
/** |
| 365 |
* Filter sanitized settings. |
| 366 |
* |
| 367 |
* @since 1.0.0 |
| 368 |
* |
| 369 |
* @param array $values All values, merged. |
| 370 |
* @param array $new_values Newly submitted values. |
| 371 |
* @param array $old_values Old settings. |
| 372 |
*/ |
| 373 |
|
| 374 |
$values = apply_filters( 'charitable_save_settings', $values, $new_values, $old_values ); |
| 375 |
|
| 376 |
// save a temp transient for legacy licenses (1.7.0.8+) |
| 377 |
// purpose is to provide better error reporting (transient should be deleted after settings page (advanced tab) is loaded). |
| 378 |
if ( isset( $_POST['charitable_settings']['legacy_licenses'] ) ) { |
| 379 |
$referer = wp_get_referer(); |
| 380 |
if ( false !== strpos( $referer, 'admin.php?page=charitable-settings&tab=advanced' ) ) { |
| 381 |
set_transient( '_charitable_legacy_license_info', $_POST['charitable_settings']['legacy_licenses'], MINUTE_IN_SECONDS ); |
| 382 |
} |
| 383 |
} |
| 384 |
|
| 385 |
$this->add_update_message( __( 'Settings saved', 'charitable' ), 'success' ); |
| 386 |
|
| 387 |
return $values; |
| 388 |
} |
| 389 |
|
| 390 |
/** |
| 391 |
* Checkbox settings should always be either 1 or 0. |
| 392 |
* |
| 393 |
* @since 1.0.0 |
| 394 |
* |
| 395 |
* @param mixed $value Submitted value for field. |
| 396 |
* @param array $field Field definition. |
| 397 |
* @return int |
| 398 |
*/ |
| 399 |
public function sanitize_checkbox_value( $value, $field ) { |
| 400 |
if ( isset( $field['type'] ) && 'checkbox' == $field['type'] ) { |
| 401 |
$value = intval( $value && 'on' == $value ); |
| 402 |
} |
| 403 |
|
| 404 |
return $value; |
| 405 |
} |
| 406 |
|
| 407 |
/** |
| 408 |
* Render field. This is the default callback used for all fields, unless an alternative callback has been specified. |
| 409 |
* |
| 410 |
* @since 1.0.0 |
| 411 |
* |
| 412 |
* @param array $args Field definition. |
| 413 |
* @return void |
| 414 |
*/ |
| 415 |
public function render_field( $args ) { |
| 416 |
$field_type = isset( $args['type'] ) ? $args['type'] : 'text'; |
| 417 |
|
| 418 |
charitable_admin_view( 'settings/' . $field_type, $args ); |
| 419 |
} |
| 420 |
|
| 421 |
/** |
| 422 |
* Returns an array of all pages in the id=>title format. |
| 423 |
* |
| 424 |
* @since 1.0.0 |
| 425 |
* |
| 426 |
* @return string[] |
| 427 |
*/ |
| 428 |
public function get_pages() { |
| 429 |
if ( ! isset( $this->pages ) ) { |
| 430 |
$this->pages = charitable_get_pages_options(); |
| 431 |
} |
| 432 |
|
| 433 |
return $this->pages; |
| 434 |
} |
| 435 |
|
| 436 |
/** |
| 437 |
* Add an update message. |
| 438 |
* |
| 439 |
* @since 1.4.6 |
| 440 |
* |
| 441 |
* @param string $message The message text. |
| 442 |
* @param string $type The type of message. Options: 'error', 'success', 'warning', 'info'. |
| 443 |
* @param boolean $dismissible Whether the message can be dismissed. |
| 444 |
* @return void |
| 445 |
*/ |
| 446 |
public function add_update_message( $message, $type = 'error', $dismissible = true ) { |
| 447 |
if ( ! in_array( $type, array( 'error', 'success', 'warning', 'info' ) ) ) { |
| 448 |
$type = 'error'; |
| 449 |
} |
| 450 |
|
| 451 |
charitable_get_admin_notices()->add_notice( $message, $type, false, $dismissible ); |
| 452 |
} |
| 453 |
|
| 454 |
/** |
| 455 |
* Recursively add settings fields, given an array. |
| 456 |
* |
| 457 |
* @since 1.0.0 |
| 458 |
* |
| 459 |
* @param array $field The setting field. |
| 460 |
* @param array $keys Array containing the section key and field key. |
| 461 |
* @return void |
| 462 |
*/ |
| 463 |
private function register_field( $field, $keys ) { |
| 464 |
$section_id = 'charitable_settings_' . $keys[0]; |
| 465 |
|
| 466 |
if ( isset( $field['render'] ) && ! $field['render'] ) { |
| 467 |
return; |
| 468 |
} |
| 469 |
|
| 470 |
/* Drop the first key, which is the section identifier */ |
| 471 |
$field['name'] = implode( '][', $keys ); |
| 472 |
|
| 473 |
if ( ! $this->is_dynamic_group( $keys[0] ) ) { |
| 474 |
array_shift( $keys ); |
| 475 |
} |
| 476 |
|
| 477 |
$field['key'] = $keys; |
| 478 |
$field['classes'] = $this->get_field_classes( $field ); |
| 479 |
$callback = isset( $field['callback'] ) ? $field['callback'] : array( $this, 'render_field' ); |
| 480 |
$label = $this->get_field_label( $field, end( $keys ) ); |
| 481 |
|
| 482 |
add_settings_field( |
| 483 |
sprintf( 'charitable_settings_%s', implode( '_', $keys ) ), |
| 484 |
$label, |
| 485 |
$callback, |
| 486 |
$section_id, |
| 487 |
$section_id, |
| 488 |
$field |
| 489 |
); |
| 490 |
} |
| 491 |
|
| 492 |
/** |
| 493 |
* Return the label for the given field. |
| 494 |
* |
| 495 |
* @since 1.0.0 |
| 496 |
* |
| 497 |
* @param array $field The field definition. |
| 498 |
* @param string $key The field key. |
| 499 |
* @return string |
| 500 |
*/ |
| 501 |
private function get_field_label( $field, $key ) { |
| 502 |
$label = ''; |
| 503 |
|
| 504 |
if ( isset( $field['label_for'] ) ) { |
| 505 |
$label = $field['label_for']; |
| 506 |
} |
| 507 |
|
| 508 |
if ( isset( $field['title'] ) ) { |
| 509 |
$label = $field['title']; |
| 510 |
} |
| 511 |
|
| 512 |
return $label; |
| 513 |
} |
| 514 |
|
| 515 |
/** |
| 516 |
* Return a space separated string of classes for the given field. |
| 517 |
* |
| 518 |
* @since 1.0.0 |
| 519 |
* |
| 520 |
* @param array $field Field definition. |
| 521 |
* @return string |
| 522 |
*/ |
| 523 |
private function get_field_classes( $field ) { |
| 524 |
$classes = array( 'charitable-settings-field' ); |
| 525 |
|
| 526 |
if ( isset( $field['class'] ) ) { |
| 527 |
$classes[] = $field['class']; |
| 528 |
} |
| 529 |
|
| 530 |
/** |
| 531 |
* Filter the list of classes to apply to settings fields. |
| 532 |
* |
| 533 |
* @since 1.0.0 |
| 534 |
* |
| 535 |
* @param array $classes The list of classes. |
| 536 |
* @param array $field The field definition. |
| 537 |
*/ |
| 538 |
$classes = apply_filters( 'charitable_settings_field_classes', $classes, $field ); |
| 539 |
|
| 540 |
return implode( ' ', $classes ); |
| 541 |
} |
| 542 |
|
| 543 |
/** |
| 544 |
* Return an array with all the fields & sections to be displayed. |
| 545 |
* |
| 546 |
* @uses charitable_settings_fields |
| 547 |
* @see Charitable_Settings::register_setting() |
| 548 |
* @since 1.0.0 |
| 549 |
* |
| 550 |
* @return array |
| 551 |
*/ |
| 552 |
private function get_fields() { |
| 553 |
/** |
| 554 |
* Use the charitable_settings_tab_fields to include the fields for new tabs. |
| 555 |
* DO NOT use it to add individual fields. That should be done with the |
| 556 |
* filters within each of the methods. |
| 557 |
*/ |
| 558 |
$fields = array(); |
| 559 |
|
| 560 |
foreach ( $this->get_sections() as $section_key => $section ) { |
| 561 |
/** |
| 562 |
* Filter the array of fields to display in a particular tab. |
| 563 |
* |
| 564 |
* @since 1.0.0 |
| 565 |
* |
| 566 |
* @param array $fields Array of fields. |
| 567 |
*/ |
| 568 |
$fields[ $section_key ] = apply_filters( 'charitable_settings_tab_fields_' . $section_key, array() ); |
| 569 |
} |
| 570 |
|
| 571 |
/** |
| 572 |
* Filter the array of settings fields. |
| 573 |
* |
| 574 |
* @since 1.0.0 |
| 575 |
* |
| 576 |
* @param array $fields Array of fields. |
| 577 |
*/ |
| 578 |
return apply_filters( 'charitable_settings_tab_fields', $fields ); |
| 579 |
} |
| 580 |
|
| 581 |
/** |
| 582 |
* Get the submitted value for a particular setting. |
| 583 |
* |
| 584 |
* @since 1.0.0 |
| 585 |
* |
| 586 |
* @param string $key The key of the setting being saved. |
| 587 |
* @param array $field The setting field. |
| 588 |
* @param array $submitted The submitted values. |
| 589 |
* @param string $section The section being saved. |
| 590 |
* @return mixed|null Returns null if the value was not submitted or is not applicable. |
| 591 |
*/ |
| 592 |
private function get_setting_submitted_value( $key, $field, $submitted, $section ) { |
| 593 |
$value = null; |
| 594 |
|
| 595 |
if ( isset( $field['save'] ) && ! $field['save'] ) { |
| 596 |
return $value; |
| 597 |
} |
| 598 |
|
| 599 |
$field_type = isset( $field['type'] ) ? $field['type'] : ''; |
| 600 |
|
| 601 |
switch ( $field_type ) { |
| 602 |
|
| 603 |
case 'checkbox': |
| 604 |
$value = intval( array_key_exists( $key, $submitted ) && 'on' == $submitted[ $key ] ); |
| 605 |
break; |
| 606 |
|
| 607 |
case 'multi-checkbox': |
| 608 |
$value = isset( $submitted[ $key ] ) ? $submitted[ $key ] : array(); |
| 609 |
break; |
| 610 |
|
| 611 |
case '': |
| 612 |
case 'heading': |
| 613 |
return $value; |
| 614 |
|
| 615 |
default: |
| 616 |
if ( ! array_key_exists( $key, $submitted ) ) { |
| 617 |
return $value; |
| 618 |
} |
| 619 |
|
| 620 |
$value = $submitted[ $key ]; |
| 621 |
|
| 622 |
}//end switch |
| 623 |
|
| 624 |
/** |
| 625 |
* General way to sanitize values. If you only need to sanitize a |
| 626 |
* specific setting, used the filter below instead. |
| 627 |
* |
| 628 |
* @since 1.0.0 |
| 629 |
* |
| 630 |
* @param mixed $value The current setting value. |
| 631 |
* @param array $field The field configuration. |
| 632 |
* @param array $submitted All submitted data. |
| 633 |
* @param string $key The setting key. |
| 634 |
* @param string $section The section being saved. |
| 635 |
*/ |
| 636 |
$value = apply_filters( 'charitable_sanitize_value', $value, $field, $submitted, $key, $section ); |
| 637 |
|
| 638 |
/** |
| 639 |
* Sanitize the setting value. |
| 640 |
* |
| 641 |
* The filter hook is formatted like this: charitable_sanitize_value_{$section}_{$key}. |
| 642 |
* |
| 643 |
* @since 1.5.0 |
| 644 |
* |
| 645 |
* @param mixed $value The current setting value. |
| 646 |
* @param array $field The field configuration. |
| 647 |
* @param array $submitted All submitted data. |
| 648 |
*/ |
| 649 |
return apply_filters( 'charitable_sanitize_value_' . $section . '_' . $key, $value, $field, $submitted ); |
| 650 |
} |
| 651 |
|
| 652 |
/** |
| 653 |
* Return the submitted values for the given section. |
| 654 |
* |
| 655 |
* @since 1.0.0 |
| 656 |
* |
| 657 |
* @param string $section The section being edited. |
| 658 |
* @param array $submitted The submitted values. |
| 659 |
* @return array |
| 660 |
*/ |
| 661 |
private function get_section_submitted_values( $section, $submitted ) { |
| 662 |
$values = array(); |
| 663 |
$form_fields = $this->get_fields(); |
| 664 |
|
| 665 |
if ( ! isset( $form_fields[ $section ] ) ) { |
| 666 |
return $values; |
| 667 |
} |
| 668 |
|
| 669 |
foreach ( $form_fields[ $section ] as $key => $field ) { |
| 670 |
$value = $this->get_setting_submitted_value( $key, $field, $submitted, $section ); |
| 671 |
|
| 672 |
if ( is_null( $value ) ) { |
| 673 |
continue; |
| 674 |
} |
| 675 |
|
| 676 |
if ( $this->is_dynamic_group( $section ) ) { |
| 677 |
$values[ $section ][ $key ] = $value; |
| 678 |
continue; |
| 679 |
} |
| 680 |
|
| 681 |
$values[ $key ] = $value; |
| 682 |
} |
| 683 |
|
| 684 |
return $values; |
| 685 |
} |
| 686 |
|
| 687 |
/** |
| 688 |
* Return list of dynamic groups. |
| 689 |
* |
| 690 |
* @since 1.0.0 |
| 691 |
* |
| 692 |
* @return string[] |
| 693 |
*/ |
| 694 |
private function get_dynamic_groups() { |
| 695 |
if ( ! isset( $this->dynamic_groups ) ) { |
| 696 |
/** |
| 697 |
* Filter the list of dynamic groups. |
| 698 |
* |
| 699 |
* @since 1.0.0 |
| 700 |
* |
| 701 |
* @param array $groups The dynamic groups. |
| 702 |
*/ |
| 703 |
$this->dynamic_groups = apply_filters( 'charitable_dynamic_groups', array() ); |
| 704 |
} |
| 705 |
|
| 706 |
return $this->dynamic_groups; |
| 707 |
} |
| 708 |
|
| 709 |
/** |
| 710 |
* Returns whether the given key indicates the start of a new section of the settings. |
| 711 |
* |
| 712 |
* @since 1.0.0 |
| 713 |
* |
| 714 |
* @param string $composite_key The unique key for this group. |
| 715 |
* @return boolean |
| 716 |
*/ |
| 717 |
private function is_dynamic_group( $composite_key ) { |
| 718 |
return array_key_exists( $composite_key, $this->get_dynamic_groups() ); |
| 719 |
} |
| 720 |
|
| 721 |
/* DEPRECATED FUNCTIONS */ |
| 722 |
|
| 723 |
/** |
| 724 |
* Get the update messages. |
| 725 |
* |
| 726 |
* @deprecated 1.7.0 |
| 727 |
* |
| 728 |
* @since 1.4.13 Deprecated. |
| 729 |
*/ |
| 730 |
public function get_update_messages() { |
| 731 |
charitable_get_deprecated()->deprecated_function( |
| 732 |
__METHOD__, |
| 733 |
'1.4.13', |
| 734 |
'Charitable_Admin_Notices::get_notices()' |
| 735 |
); |
| 736 |
|
| 737 |
return charitable_get_admin_notices()->get_notices(); |
| 738 |
} |
| 739 |
|
| 740 |
/** |
| 741 |
* Display upgrade notice at the bottom on the plugin settings pages. |
| 742 |
* |
| 743 |
* @since 1.7.0.4 |
| 744 |
* |
| 745 |
* @param string $view Current view inside the plugin settings page. |
| 746 |
*/ |
| 747 |
public function settings_cta( $view = false ) { |
| 748 |
|
| 749 |
if ( charitable_is_pro() ) { |
| 750 |
// no need to display this cta since they have a valid license. |
| 751 |
return; |
| 752 |
} |
| 753 |
|
| 754 |
if ( get_option( 'charitable_lite_settings_upgrade', false ) || apply_filters( 'charitable_lite_settings_upgrade', false ) ) { |
| 755 |
return; |
| 756 |
} |
| 757 |
?> |
| 758 |
<div class="settings-lite-cta"> |
| 759 |
<button type="button" class="button-link charitable-banner-dismiss dismiss">x</button> |
| 760 |
<h5><?php esc_html_e( 'Get Charitable Pro and Unlock all the Powerful Features', 'charitable' ); ?></h5> |
| 761 |
<p><?php esc_html_e( 'Thanks for being a loyal Charitable Lite user. Upgrade to Charitable Pro to unlock all the awesome features and experience why Charitable is consistently rated a top WordPress donation and fundraising plugin.', 'charitable' ); ?></p> |
| 762 |
<p> |
| 763 |
<?php |
| 764 |
printf( __( 'We know that you will truly love Charitable. Over 10,000+ non-profits who have chosen Charitable to get more donations from their website can\'t be wrong!', 'charitable' ) ); |
| 765 |
?> |
| 766 |
</p> |
| 767 |
<h6><?php esc_html_e( 'Pro Features:', 'charitable' ); ?></h6> |
| 768 |
<div class="list"> |
| 769 |
<ul> |
| 770 |
<li><?php esc_html_e( 'Offer recurring donations to donors', 'charitable' ); ?></li> |
| 771 |
<li><?php esc_html_e( 'Use fee relief to keep more donation dollars', 'charitable' ); ?></li> |
| 772 |
<li><?php esc_html_e( 'Integrate with popular email marketing platforms', 'charitable' ); ?></li> |
| 773 |
<li><?php esc_html_e( 'Expand your reach with peer-to-peer fundraising', 'charitable' ); ?></li> |
| 774 |
<li><?php esc_html_e( 'Allow donors to crowdfund donations', 'charitable' ); ?></li> |
| 775 |
</ul> |
| 776 |
<ul> |
| 777 |
<li><?php esc_html_e( 'Automate common tasks with Zapier and smart workflows', 'charitable' ); ?></li> |
| 778 |
<li><?php esc_html_e( 'Run campaigns with ambassador and team support', 'charitable' ); ?></li> |
| 779 |
<li><?php esc_html_e( 'Advanced donation management with annual receipts' ); ?></li> |
| 780 |
<li><?php esc_html_e( 'Allow donors to give donations anonymously', 'charitable' ); ?></li> |
| 781 |
<li><?php esc_html_e( 'Add videos and updates to all campaigns', 'charitable' ); ?></li> |
| 782 |
</ul> |
| 783 |
</div> |
| 784 |
<p> |
| 785 |
<a href="<?php echo esc_url( charitable_pro_upgrade_url( 'settings-upgrade' ) ); ?>" target="_blank" rel="noopener noreferrer"> |
| 786 |
<?php esc_html_e( 'Get Charitable Pro Today and Unlock all the Powerful Features »', 'charitable' ); ?> |
| 787 |
</a> |
| 788 |
</p> |
| 789 |
<p> |
| 790 |
<?php |
| 791 |
echo wp_kses( |
| 792 |
__( '<strong>Bonus:</strong> Charitable Lite users get up to <span class="green">$300 off regular price</span>, automatically applied at checkout.', 'charitable' ), |
| 793 |
[ |
| 794 |
'strong' => [], |
| 795 |
'span' => [ |
| 796 |
'class' => [], |
| 797 |
], |
| 798 |
] |
| 799 |
); |
| 800 |
?> |
| 801 |
</p> |
| 802 |
</div> |
| 803 |
<script type="text/javascript"> |
| 804 |
jQuery( function ( $ ) { |
| 805 |
$( document ).on( 'click', '.settings-lite-cta .dismiss', function ( event ) { |
| 806 |
event.preventDefault(); |
| 807 |
$.post( ajaxurl, { |
| 808 |
action: 'charitable_lite_settings_upgrade', |
| 809 |
chartiable_action: 'remove_lite_cta' |
| 810 |
} ); |
| 811 |
$( '.settings-lite-cta' ).remove(); |
| 812 |
} ); |
| 813 |
} ); |
| 814 |
</script> |
| 815 |
<?php |
| 816 |
} |
| 817 |
|
| 818 |
/** |
| 819 |
* Dismiss upgrade notice at the bottom on the plugin settings pages. |
| 820 |
* |
| 821 |
* @since 1.7.0.4 |
| 822 |
*/ |
| 823 |
public function settings_cta_dismiss() { |
| 824 |
|
| 825 |
if ( ! charitable_current_user_can() ) { |
| 826 |
wp_send_json_error(); |
| 827 |
} |
| 828 |
|
| 829 |
update_option( 'charitable_lite_settings_upgrade', time() ); |
| 830 |
|
| 831 |
wp_send_json_success(); |
| 832 |
} |
| 833 |
} |
| 834 |
|
| 835 |
endif; |
| 836 |
|