PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.0
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.0
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 1.7.0.9 1.8.0 All 210 releases
charitable / includes / admin / charitable-core-admin-functions.php

charitable-core-admin-functions.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.0, at includes/admin/charitable-core-admin-functions.php

1,104 lines 28.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Charitable Core Admin Functions
4 *
5 * General core functions available only within the admin area.
6 *
7 * @package Charitable/Functions/Admin
8 * @version 1.0.0
9 * @author David Bisset
10 * @copyright Copyright (c) 2023, WP Charitable LLC
11 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
12 */
13
14 // Exit if accessed directly.
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 /**
20 * Load a view from the admin/views folder.
21 *
22 * If the view is not found, an Exception will be thrown.
23 *
24 * Example usage: charitable_admin_view('metaboxes/campaign-title');
25 *
26 * @since 1.0.0
27 *
28 * @param string $view The view to display.
29 * @param array $view_args Optional. Arguments to pass through to the view itself.
30 * @return boolean True if the view exists and was rendered. False otherwise.
31 */
32 function charitable_admin_view( $view, $view_args = array(), $return_html = false ) {
33 $base_path = array_key_exists( 'base_path', $view_args ) ? $view_args['base_path'] : charitable()->get_path( 'admin' ) . 'views/';
34
35 /**
36 * Filter the path to the view.
37 *
38 * @since 1.0.0
39 *
40 * @param string $path The default path.
41 * @param string $view The view.
42 * @param array $view_args View args.
43 */
44 $filename = apply_filters( 'charitable_admin_view_path', $base_path . $view . '.php', $view, $view_args );
45
46 if ( ! is_readable( $filename ) ) {
47 charitable_get_deprecated()->doing_it_wrong(
48 __FUNCTION__,
49 sprintf(
50 /* translators: %s: Filename of passed view */
51 __( 'Passed view (%s) not found or is not readable.', 'charitable' ),
52 $filename
53 ),
54 '1.0.0'
55 );
56
57 return false;
58 }
59
60 ob_start();
61
62 include $filename;
63
64 if ( $return_html ) {
65 $output = ob_get_contents();
66 ob_end_clean();
67 return $output;
68 }
69
70 ob_end_flush();
71
72 return true;
73 }
74
75 /**
76 * Returns the Charitable_Settings helper.
77 *
78 * @since 1.0.0
79 *
80 * @return Charitable_Settings
81 */
82 function charitable_get_admin_settings() {
83 return Charitable_Settings::get_instance();
84 }
85
86 /**
87 * Returns the Charitable_Admin_Notices helper.
88 *
89 * @since 1.4.6
90 *
91 * @return Charitable_Admin_Notices
92 */
93 function charitable_get_admin_notices() {
94 return charitable()->registry()->get( 'admin_notices' );
95 }
96
97 /**
98 * Returns whether we are currently viewing the Charitable settings area.
99 *
100 * @since 1.2.0
101 *
102 * @param string $tab Optional. If passed, the function will also check that we are on the given tab.
103 * @return boolean
104 */
105 function charitable_is_settings_view( $tab = '' ) {
106 if ( ! empty( $_POST ) ) {
107 $is_settings = array_key_exists( 'option_page', $_POST ) && 'charitable_settings' === $_POST['option_page'];
108
109 if ( ! $is_settings || empty( $tab ) ) {
110 return $is_settings;
111 }
112
113 return array_key_exists( 'charitable_settings', $_POST ) && array_key_exists( $tab, $_POST['charitable_settings'] );
114 }
115
116 $is_settings = isset( $_GET['page'] ) && 'charitable-settings' == $_GET['page'];
117
118 if ( ! $is_settings || empty( $tab ) ) {
119 return $is_settings;
120 }
121
122 /* The general tab can be loaded when tab is not set. */
123 if ( 'general' == $tab ) {
124 return ! isset( $_GET['tab'] ) || 'general' == $_GET['tab'];
125 }
126
127 return isset( $_GET['tab'] ) && $tab == $_GET['tab'];
128 }
129
130 /**
131 * Print out the settings fields for a particular settings section.
132 *
133 * This is based on WordPress' do_settings_fields but allows the possibility
134 * of leaving out a field lable/title, for fullwidth fields.
135 *
136 * @see do_settings_fields
137 *
138 * @since 1.0.0
139 *
140 * @global $wp_settings_fields Storage array of settings fields and their pages/sections
141 *
142 * @param string $page Slug title of the admin page who's settings fields you want to show.
143 * @param string $section Slug title of the settings section who's fields you want to show.
144 * @return string
145 */
146 function charitable_do_settings_fields( $page, $section ) {
147 global $wp_settings_fields;
148
149 if ( ! isset( $wp_settings_fields[ $page ][ $section ] ) ) {
150 return;
151 }
152
153 foreach ( (array) $wp_settings_fields[ $page ][ $section ] as $field ) {
154 $class = '';
155
156 if ( ! empty( $field['args']['class'] ) ) {
157 $class = ' class="' . esc_attr( $field['args']['class'] ) . '"';
158 }
159
160 echo "<tr{$class}>";
161
162 if ( ! empty( $field['args']['label_for'] ) ) {
163 echo '<th scope="row"><label for="' . esc_attr( $field['args']['label_for'] ) . '">' . $field['title'] . '</label></th>';
164 echo '<td>';
165 call_user_func( $field['callback'], $field['args'] );
166 echo '</td>';
167 } elseif ( ! empty( $field['title'] ) ) {
168 if ( $field['args']['type'] === 'heading' && isset( $field['args']['help'] ) ) { // if this is a heading display a "help" as a subheading
169 echo '<th scope="row" colspan="2"><h4>' . $field['title'] . '</h4>';
170 echo '<p>' . $field['args']['help'] . '</p>';
171 } else {
172 echo '<th scope="row"><h4>' . $field['title'] . '</h4>';
173 }
174 echo '</th>';
175 if ( $field['args']['type'] !== 'heading' ) {
176 echo '<td>';
177 call_user_func( $field['callback'], $field['args'] );
178 echo '</td>';
179 }
180 } else {
181 echo '<td colspan="2" class="charitable-fullwidth">';
182 call_user_func( $field['callback'], $field['args'] );
183 echo '</td>';
184 }
185
186 echo '</tr>';
187 }
188 }
189
190 /**
191 * Add new tab to the Charitable settings area.
192 *
193 * @since 1.3.0
194 *
195 * @param string[] $tabs
196 * @param string $key
197 * @param string $name
198 * @param mixed[] $args
199 * @return string[]
200 */
201 function charitable_add_settings_tab( $tabs, $key, $name, $args = array() ) {
202 $defaults = array(
203 'index' => 3,
204 );
205
206 $args = wp_parse_args( $args, $defaults );
207 $keys = array_keys( $tabs );
208 $values = array_values( $tabs );
209
210 array_splice( $keys, $args['index'], 0, $key );
211 array_splice( $values, $args['index'], 0, $name );
212
213 return array_combine( $keys, $values );
214 }
215
216 /**
217 * Return the donation actions class.
218 *
219 * @since 1.5.0
220 *
221 * @return Charitable_Donation_Admin_Actions
222 */
223 function charitable_get_donation_actions() {
224 return Charitable_Admin::get_instance()->get_donation_actions();
225 }
226
227
228 /**
229 * Adds the "Upgrade to Pro" menu item to the very end of the submenu.
230 *
231 * @since 1.7.0
232 */
233 function charitable_add_upgrade_item() {
234 global $submenu;
235
236 if ( charitable_is_pro() ) {
237 return;
238 }
239
240 $submenu['charitable'][99] = array(
241 __( 'Upgrade to Pro', 'chartiable' ),
242 'manage_options',
243 charitable_ga_url(
244 'https://wpcharitable.com/lite-vs-pro/',
245 urlencode( 'Admin Menu Link' ),
246 urlencode( 'Upgrade to Pro' )
247 ),
248 );
249 }
250 add_action( 'admin_menu', 'charitable_add_upgrade_item' );
251
252
253 /**
254 * Outputs "please rate" text.
255 *
256 * @since 1.7.0
257 *
258 * @param string $footer_text Footer text.
259 * @return string
260 */
261 function charitable_add_footer_text( $footer_text ) {
262 if ( ! charitable_is_admin_screen() ) {
263 return $footer_text;
264 }
265
266 return sprintf(
267 /* translators: %1$s Opening strong tag, do not translate. %2$s Closing strong tag, do not translate. %3$s Opening anchor tag, do not translate. %4$s Closing anchor tag, do not translate. */
268 __( 'Please rate %1$sWP Charitable%2$s %3$s�
269
270
271
272
273 %4$s on %3$sWordPress.org%4$s to help us spread the word. Thank you from the WP Charitable team!', 'stripe' ),
274 '<strong>',
275 '</strong>',
276 '<a href="https://wordpress.org/support/plugin/charitable/reviews/?filter=5#new-post" rel="noopener noreferrer" target="_blank">',
277 '</a>'
278 );
279 }
280 add_filter( 'admin_footer_text', 'charitable_add_footer_text' );
281
282 /**
283 * Check if a screen is a plugin admin view.
284 * Returns the screen id if true, false (bool) if not.
285 *
286 * @since 1.7.0
287 *
288 * @return string|bool
289 */
290 function charitable_is_admin_screen() {
291 $screen = \get_current_screen();
292
293 if (
294 'charitable' === $screen->post_type ||
295 'campaign' === $screen->post_type ||
296 'charitable' === $screen->parent_file
297 ) {
298 return 'charitable';
299 }
300
301 if ( isset( $_GET['page'] ) ) {
302 if ( 'charitable' == $_GET['page'] ) {
303 return 'charitable';
304 }
305
306 }
307
308 return false;
309 }
310
311 /**
312 * Appends UTM parameters to a given URL.
313 *
314 * @since 1.7.0
315 *
316 * @param string $base_url Base URL.
317 * @param string $utm_medium utm_medium parameter.
318 * @param string $utm_content Optional. utm_content parameter.
319 * @return string $url Full Google Analytics campaign URL.
320 */
321 function charitable_ga_url( $base_url, $utm_medium, $utm_content = false ) {
322 /**
323 * Filters the UTM campaign for generated links.
324 *
325 * @since 1.7.0
326 *
327 * @param string $utm_campaign
328 */
329 $utm_campaign = apply_filters( 'charitable_utm_campaign', 'WP+Charitable' );
330
331 $args = array(
332 'utm_source' => 'WordPress',
333 'utm_campaign' => $utm_campaign,
334 'utm_medium' => $utm_medium,
335 );
336
337 if ( ! empty( $utm_content ) ) {
338 $args['utm_content'] = $utm_content;
339 }
340
341 return esc_url( add_query_arg( $args, $base_url ) );
342 }
343
344 /**
345 * URL for upgrading to Pro (or another Pro licecnse).
346 *
347 * @since 1.7.0
348 *
349 * @param string $utm_medium utm_medium parameter.
350 * @param string $utm_content Optional. utm_content parameter.
351 * @return string
352 */
353 function charitable_pro_upgrade_url( $utm_medium, $utm_content = '' ) {
354 return apply_filters(
355 'charitable_upgrade_link',
356 charitable_ga_url(
357 'https://wpcharitable.com/lite-vs-pro/',
358 urlencode( $utm_medium ),
359 urlencode( $utm_content )
360 ),
361 $utm_medium,
362 $utm_content
363 );
364 }
365
366 /**
367 * Get the current installation license type (always lowercase).
368 *
369 * @since 1.7.0.3
370 *
371 * @return string|false
372 */
373 function charitable_get_license_type() {
374
375 $type = charitable_setting( 'type', '', 'charitable_license' );
376
377 if ( empty( $type ) || ! charitable()->is_pro() ) {
378 return false;
379 }
380
381 return strtolower( $type );
382 }
383
384 /**
385 * Get when WPCharitable was first installed.
386 *
387 * @since 1.6.0
388 *
389 * @param string $type Specific install type to check for.
390 *
391 * @return int|false Unix timestamp. False on failure.
392 */
393 function charitable_get_activated_timestamp( $type = '' ) {
394
395 $activated = (array) get_option( 'charitable_activated', [] );
396
397 if ( empty( $activated ) ) {
398 return false;
399 }
400
401 // When a passed install type is empty, then get it from a DB.
402 // If it is installed/activated first, it is saved first.
403 $type = empty( $type ) ? (string) array_keys( $activated )[0] : $type;
404
405 if ( ! empty( $activated[ $type ] ) ) {
406 return absint( $activated[ $type ] );
407 }
408
409 // Fallback.
410 $types = array_diff( [ 'lite', 'pro' ], [ $type ] );
411
412 foreach ( $types as $_type ) {
413 if ( ! empty( $activated[ $_type ] ) ) {
414 return absint( $activated[ $_type ] );
415 }
416 }
417
418 return false;
419 }
420
421 /**
422 * Determines whether the current request is a WP CLI request.
423 *
424 * @since 1.7.0.3
425 *
426 * @return bool
427 */
428 function charitable_doing_wp_cli() {
429
430 return defined( 'WP_CLI' ) && WP_CLI;
431 }
432
433 /**
434 * Modify the default USer-Agent generated by wp_remote_*() to include additional information.
435 *
436 * @since 1.7.0.3
437 *
438 * @return string
439 */
440 function charitable_get_default_user_agent() {
441
442 $wpcharitable_type = function_exists( 'charitable_is_pro' ) && charitable_is_pro() ? 'Paid' : 'Lite';
443
444 return 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) . '; WPCharitable/' . $wpcharitable_type;
445 }
446
447
448 function charitable_theme_template_paths() {
449
450 $template_dir = 'charitable';
451
452 $file_paths = array(
453 1 => trailingslashit( get_stylesheet_directory() ) . $template_dir,
454 10 => trailingslashit( get_template_directory() ) . $template_dir,
455 100 => trailingslashit( CHARTIABLE_DIRECTORY_PATH ) . 'includes',
456 );
457
458 $file_paths = apply_filters( 'charitable_helpers_templates_get_theme_template_paths', $file_paths );
459
460 // Sort the file paths based on priority.
461 ksort( $file_paths, SORT_NUMERIC );
462
463 return array_map( 'trailingslashit', $file_paths );
464 }
465
466 /**
467 * Locate a template and return the path for inclusion.
468 *
469 * @since 1.7.0.3
470 *
471 * @param string $template_name Template name.
472 *
473 * @return string
474 */
475 function charitable_locate_template( $template_name ) {
476
477 // Trim off any slashes from the template name.
478 $template_name = ltrim( $template_name, '/' );
479
480 if ( empty( $template_name ) ) {
481 return apply_filters( 'charitable_helpers_templates_locate', '', $template_name );
482 }
483
484 $located = '';
485
486 // Try locating this template file by looping through the template paths.
487 foreach ( charitable_theme_template_paths() as $template_path ) {
488 if ( file_exists( $template_path . $template_name ) ) {
489 $located = $template_path . $template_name;
490 break;
491 }
492 }
493
494 return apply_filters( 'charitable_helpers_templates_locate', $located, $template_name );
495 }
496
497 /**
498 * Include a template.
499 * Use 'require' if $args are passed or 'load_template' if not.
500 *
501 * @since 1.7.0.3
502 *
503 * @param string $template_name Template name.
504 * @param array $args Arguments.
505 * @param bool $extract Extract arguments.
506 *
507 * @throws \RuntimeException If extract() tries to modify the scope.
508 */
509 function charitable_admin_include_html( $template_name, $args = array(), $extract = false ) {
510
511 $template_name .= '.php';
512
513 // Allow 3rd party plugins to filter template file from their plugin.
514 $located = apply_filters( 'charitable_helpers_templates_include_html_located', charitable_locate_template( $template_name ), $template_name, $args, $extract );
515 $args = apply_filters( 'charitable_helpers_templates_include_html_args', $args, $template_name, $extract );
516
517 if ( empty( $located ) || ! \is_readable( $located ) ) {
518 return;
519 }
520
521 // Load template WP way if no arguments were passed.
522 if ( empty( $args ) ) {
523 load_template( $located, false );
524 return;
525 }
526
527 $extract = apply_filters( 'charitable_helpers_templates_include_html_extract_args', $extract, $template_name, $args );
528
529 if ( $extract && is_array( $args ) ) {
530
531 $created_vars_count = extract( $args, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract
532
533 // Protecting existing scope from modification.
534 if ( count( $args ) !== $created_vars_count ) {
535 throw new \RuntimeException( 'Extraction failed: variable names are clashing with the existing ones.' );
536 }
537 }
538
539 require $located;
540 }
541
542 /**
543 * Like self::include_html, but returns the HTML instead of including.
544 *
545 * @since 1.7.0.3
546 *
547 * @param string $template_name Template name.
548 * @param array $args Arguments.
549 * @param bool $extract Extract arguments.
550 *
551 * @return string
552 */
553 function charitable_admin_get_html( $template_name, $args = array(), $extract = false ) {
554 ob_start();
555 charitable_admin_include_html( $template_name, $args, $extract );
556 return ob_get_clean();
557 }
558
559 /**
560 * Include a template - alias to \charitable\Helpers\Template::get_html.
561 * Use 'require' if $args are passed or 'load_template' if not.
562 *
563 * @since 1.7.0.3
564 *
565 * @param string $template_name Template name.
566 * @param array $args Arguments.
567 * @param bool $extract Extract arguments.
568 *
569 * @throws \RuntimeException If extract() tries to modify the scope.
570 *
571 * @return string Compiled HTML.
572 */
573 function charitable_render( $template_name, $args = array(), $extract = false ) {
574 return charitable_admin_get_html( $template_name, $args, $extract );
575 }
576
577 /**
578 * Determine if the plugin/addon installations are allowed.
579 *
580 * @since 1.6.2.3
581 *
582 * @param string $type Should be `plugin` or `addon`.
583 *
584 * @return bool
585 */
586 function charitable_can_install( $type ) {
587
588 return charitable_can_do( 'install', $type );
589 }
590
591 /**
592 * Determine if the plugin/addon activations are allowed.
593 *
594 * @since 1.7.3
595 *
596 * @param string $type Should be `plugin` or `addon`.
597 *
598 * @return bool
599 */
600 function charitable_can_activate( $type ) {
601
602 return charitable_can_do( 'activate', $type );
603 }
604
605 /**
606 * Determine if the plugin/addon installations/activations are allowed.
607 *
608 * @since 1.7.3
609 *
610 * @internal Use charitable_can_activate() or charitable_can_install() instead.
611 *
612 * @param string $what Should be 'activate' or 'install'.
613 * @param string $type Should be `plugin` or `addon`.
614 *
615 * @return bool
616 */
617 function charitable_can_do( $what, $type ) {
618
619 if ( ! in_array( $what, [ 'install', 'activate' ], true ) ) {
620 return false;
621 }
622
623 if ( ! in_array( $type, [ 'plugin', 'addon' ], true ) ) {
624 return false;
625 }
626
627 $capability = $what . '_plugins';
628
629 if ( ! current_user_can( $capability ) ) {
630 return false;
631 }
632
633 // Determine whether file modifications are allowed and it is activation permissions checking.
634 if ( $what === 'install' && ! wp_is_file_mod_allowed( 'charitable_can_install' ) ) {
635 return false;
636 }
637
638 // All plugin checks are done.
639 if ( $type === 'plugin' ) {
640 return true;
641 }
642
643 // Addons require additional license checks.
644 // $license = get_option( 'charitable_license', [] );
645
646 // // Allow addons installation if license is not expired, enabled and valid.
647 // return empty( $license['is_expired'] ) && empty( $license['is_disabled'] ) && empty( $license['is_invalid'] );
648
649 return true;
650 }
651
652 /**
653 * Perform test connection to verify that the current web host can successfully
654 * make outbound SSL connections.
655 *
656 * @since 1.4.5
657 */
658 function charitable_verify_ssl() {
659
660 // Run a security check.
661 check_ajax_referer( 'charitable-admin', 'nonce' );
662
663 // Check for permissions.
664 if ( ! charitable_current_user_can() ) {
665 wp_send_json_error(
666 array(
667 'msg' => esc_html__( 'You do not have permission to perform this operation.', 'charitable' ),
668 )
669 );
670 }
671
672 $response = wp_remote_post( 'https://wpcharitable.com/connection-test.php' );
673
674 if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
675 wp_send_json_success(
676 array(
677 'msg' => esc_html__( 'Success! Your server can make SSL connections.', 'charitable' ),
678 )
679 );
680 }
681
682 wp_send_json_error(
683 array(
684 'msg' => esc_html__( 'There was an error and the connection failed. Please contact your web host with the technical details below.', 'charitable' ),
685 'debug' => '<pre>' . print_r( map_deep( $response, 'wp_strip_all_tags' ), true ) . '</pre>',
686 )
687 );
688 }
689 add_action( 'wp_ajax_charitable_verify_ssl', 'charitable_verify_ssl' );
690
691 /**
692 * Deactivate addon.
693 *
694 * @since 1.0.0
695 * @since 1.6.2.3 Updated the permissions checking.
696 */
697 function charitable_deactivate_addon() {
698
699 // Run a security check.
700 check_ajax_referer( 'charitable-admin', 'nonce' );
701
702 // Check for permissions.
703 if ( ! current_user_can( 'deactivate_plugins' ) ) {
704 wp_send_json_error( esc_html__( 'Plugin deactivation is disabled for you on this site.', 'charitable' ) );
705 }
706
707 $type = empty( $_POST['type'] ) ? 'addon' : sanitize_key( $_POST['type'] );
708
709 if ( isset( $_POST['plugin'] ) ) {
710 $plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
711
712 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
713 error_log( 'charitable_deactivate_addon' );
714 error_log( print_r( $plugin, true ) );
715 error_log( print_r( $_POST, true ) );
716 error_log( print_r( $type, true ) );
717 }
718
719 deactivate_plugins( $plugin );
720
721 do_action( 'charitable_plugin_deactivated', $plugin );
722
723 if ( $type === 'plugin' ) {
724 wp_send_json_success( esc_html__( 'Plugin deactivated.', 'charitable' ) );
725 } else {
726 wp_send_json_success( esc_html__( 'Addon deactivated.', 'charitable' ) );
727 }
728 }
729
730 wp_send_json_error( esc_html__( 'Could not deactivate the addon. Please deactivate from the Plugins page.', 'charitable' ) );
731 }
732 add_action( 'wp_ajax_charitable_deactivate_addon', 'charitable_deactivate_addon' );
733
734 /**
735 * Activate addon.
736 *
737 * @since 1.0.0
738 * @since 1.6.2.3 Updated the permissions checking.
739 */
740 function charitable_ajax_activate_addon() {
741
742 // Run a security check.
743 check_ajax_referer( 'charitable-admin', 'nonce' );
744
745 // Check for permissions.
746 if ( ! current_user_can( 'activate_plugins' ) ) {
747 wp_send_json_error( esc_html__( 'Plugin activation is disabled for you on this site.', 'charitable' ) );
748 }
749
750 $type = 'addon';
751
752 if ( isset( $_POST['plugin'] ) ) {
753
754 if ( ! empty( $_POST['type'] ) ) {
755 $type = sanitize_key( $_POST['type'] );
756 }
757
758 $plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
759
760 $activate = activate_plugins( $plugin );
761
762 /**
763 * Fire after plugin activating via the Charitable installer.
764 *
765 * @since 1.6.3.1
766 *
767 * @param string $plugin Path to the plugin file relative to the plugins directory.
768 */
769 do_action( 'charitable_plugin_activated', $plugin );
770
771 if ( ! is_wp_error( $activate ) ) {
772 if ( $type === 'plugin' ) {
773 wp_send_json_success( esc_html__( 'Plugin activated.', 'charitable' ) );
774 } else {
775 wp_send_json_success( esc_html__( 'Addon activated.', 'charitable' ) );
776 }
777 }
778 }
779
780 if ( $type === 'plugin' ) {
781 wp_send_json_error( esc_html__( 'Could not activate the plugin. Please activate it on the Plugins page.', 'charitable' ) );
782 }
783
784 wp_send_json_error( esc_html__( 'Could not activate the addon. Please activate it on the Plugins page.', 'charitable' ) );
785 }
786 add_action( 'wp_ajax_charitable_activate_addon', 'charitable_ajax_activate_addon' );
787
788
789 /**
790 * Installs an Charitable addon.
791 *
792 * @since 1.0.0
793 */
794 function charitable_ajax_install_addon() {
795
796 // Run a security check first.
797 check_admin_referer( 'charitable-admin', 'nonce' );
798
799 // Permission check.
800 if ( ! current_user_can( 'install_plugins' ) ) {
801 wp_send_json_error( esc_html__( 'Plugin install is disabled for you on this site.', 'charitable' ) );
802 }
803
804 // Install the addon.
805 if ( isset( $_POST['plugin'] ) ) {
806 $download_url = esc_url_raw( wp_unslash( $_POST['plugin'] ) );
807 global $hook_suffix;
808
809 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
810 error_log( 'charitable_ajax_install_addon' );
811 error_log( print_r( $_POST, true ) );
812 error_log( print_r( $download_url, true ) );
813 }
814
815 // Set the current screen to avoid undefined notices.
816 set_current_screen();
817
818 // Prepare variables.
819 $method = '';
820 $url = add_query_arg(
821 array(
822 'page' => 'charitable-settings',
823 ),
824 admin_url( 'admin.php' )
825 );
826 $url = esc_url( $url );
827
828 // Start output bufferring to catch the filesystem form if credentials are needed.
829 ob_start();
830 $creds = request_filesystem_credentials( $url, $method, false, false, null );
831 if ( false === $creds ) {
832 $form = ob_get_clean();
833 echo wp_json_encode( array( 'form' => $form ) );
834 die;
835 }
836
837 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
838 error_log( 'charitable_ajax_install_addon creds' );
839 error_log( print_r( $creds, true ) );
840 }
841
842 // If we are not authenticated, make it happen now.
843 if ( ! WP_Filesystem( $creds ) ) {
844 ob_start();
845 request_filesystem_credentials( $url, $method, true, false, null );
846 $form = ob_get_clean();
847 echo wp_json_encode( array( 'form' => $form ) );
848 die;
849 }
850
851 // Do not allow WordPress to search/download translations, as this will break JS output.
852 remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 );
853
854 // We do not need any extra credentials if we have gotten this far, so let's install the plugin.
855 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
856 require_once plugin_dir_path( CHARTIABLE_DIRECTORY_PATH ) . 'charitable/includes/utilities/Skin.php';
857
858 // Create the plugin upgrader with our custom skin.
859 $skin = new Charitable_Skin();
860 $installer = new Plugin_Upgrader( $skin );
861 $installer->install( $download_url );
862
863 // Flush the cache and return the newly installed plugin basename.
864 wp_cache_flush();
865
866 if ( $installer->plugin_info() ) {
867 $plugin_basename = $installer->plugin_info();
868 $plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
869 // attempt to activate the installed addon, save the user a step.
870 $activate = activate_plugins( $plugin_basename );
871 if ( ! is_wp_error( $activate ) ) {
872 wp_send_json_success(
873 array(
874 'basename' => $plugin_basename,
875 'is_activated' => true,
876 'msg' => esc_html__( 'Addon installed and activated.', 'charitable' ),
877 )
878 );
879 } else {
880 wp_send_json_success(
881 array(
882 'basename' => $plugin_basename,
883 'msg' => esc_html__( 'Addon installed.', 'charitable' ),
884 )
885 );
886 }
887
888 die;
889 }
890 }
891
892 // Send back a response.
893 echo wp_json_encode( true );
894 die;
895 }
896 add_action( 'wp_ajax_charitable_install_addon', 'charitable_ajax_install_addon' );
897
898 /**
899 * Keep an option updated to know what/when a warning went out for a third party plugin, theme, etc.
900 *
901 * @since 1.7.0.8
902 */
903 function charitable_update_third_party_warning_option() {
904
905 $third_party_warnings = get_option( 'charitable_third_party_warnings' );
906 $updated = false;
907
908 // If the option is empty or wiped (which is possible), initialize it with an empty array.
909 $third_party_warnings = ( false === $third_party_warnings ) ? array( 'plugins' => array() ) : $third_party_warnings = json_decode( $third_party_warnings, ARRAY_A );
910
911 if ( is_plugin_active( 'code-snippets/code-snippets.php' ) && ! array_key_exists( 'code-snippets/code-snippets.php', $third_party_warnings['plugins'] ) ) {
912 $third_party_warnings['plugins']['code-snippets/code-snippets.php'] = 'noted';
913 $updated = true;
914 } elseif ( ! is_plugin_active( 'code-snippets/code-snippets.php' ) && array_key_exists( 'code-snippets/code-snippets.php', $third_party_warnings['plugins'] ) && $third_party_warnings['plugins']['code-snippets/code-snippets.php'] === 'noted' ) {
915 unset( $third_party_warnings['plugins']['code-snippets/code-snippets.php'] );
916 $updated = true;
917 }
918
919 $third_party_warnings = apply_filters( 'charitable_update_third_party_warning_option', $third_party_warnings );
920
921 if ( $updated ) {
922 $result = update_option( 'charitable_third_party_warnings', json_encode( $third_party_warnings ) );
923 }
924 }
925 add_action( 'admin_init', 'charitable_update_third_party_warning_option' );
926
927 /**
928 * Get an option related to third party warning ( null, noted, dismissed )
929 *
930 * @since 1.7.0.8
931 */
932 function charitable_get_third_party_warning_option( $plugin_path = false, $category = 'plugins' ) {
933
934 if ( false === $plugin_path ) {
935 return false;
936 }
937
938 $third_party_warnings = get_option( 'charitable_third_party_warnings' );
939
940 if ( false === $third_party_warnings ) {
941 return false;
942 }
943
944 $third_party_warnings = json_decode( $third_party_warnings, ARRAY_A );
945
946 if ( ! isset( $third_party_warnings[ $category ][ $plugin_path ] ) ) {
947 return false;
948 }
949
950 return esc_html( $third_party_warnings[ $category ][ $plugin_path ] );
951 }
952
953 /**
954 * Get an option related to third party warning ( null, noted, dismissed )
955 *
956 * @since 1.7.0.8
957 */
958 function charitable_set_third_party_warning_option( $plugin_path = false, $value = false, $category = 'plugins' ) {
959
960 if ( false === $plugin_path ) {
961 return;
962 }
963
964 $third_party_warnings = get_option( 'charitable_third_party_warnings' );
965
966 if ( false === $third_party_warnings ) {
967 return;
968 }
969
970 $third_party_warnings = json_decode( $third_party_warnings, ARRAY_A );
971
972 $third_party_warnings[ $category ][ $plugin_path ] = $value;
973
974 $result = update_option( 'charitable_third_party_warnings', json_encode( $third_party_warnings ) );
975
976 return $result;
977 }
978
979 /**
980 * Return the latest versions of Charitable plugins.
981 *
982 * @since 1.8.0
983 *
984 * @return array
985 */
986 function charitable_get_addons_data_from_server( $licenses = false, $update_url = 'https://wpcharitable.com' ) {
987
988 $versions = false;
989
990 // if ( ! defined( 'CHARITABLE_DEBUG' ) ) {
991 $versions = get_transient( '_charitable_plugin_versions' );
992 // }
993
994 if ( false === $versions ) {
995
996 if ( false === $licenses ) {
997
998 $licenses = array();
999
1000 foreach ( charitable_get_licenses() as $license ) {
1001 if ( isset( $license['license'] ) ) {
1002 $licenses[] = $license['license'];
1003 }
1004 }
1005 }
1006
1007 $response = wp_remote_post(
1008 $update_url . '/edd-api/versions-v3/',
1009 array(
1010 'sslverify' => false,
1011 'timeout' => 15,
1012 'body' => array(
1013 'licenses' => $licenses,
1014 'url' => home_url(),
1015 ),
1016 )
1017 );
1018
1019 $response_body = wp_remote_retrieve_body( $response );
1020 $response_code = wp_remote_retrieve_response_code( $response );
1021
1022 // Bail out early if there are any errors.
1023 if ( (int) $response_code !== 200 || is_wp_error( $response_body ) ) {
1024 return false;
1025 }
1026
1027 $versions = json_decode( $response_body, true );
1028
1029 set_transient( '_charitable_plugin_versions', $versions, DAY_IN_SECONDS );
1030
1031 } // end if
1032
1033 return $versions;
1034 }
1035
1036 /**
1037 * Return the list of licenses.
1038 *
1039 * Note: The licenses are not necessarily valid. If a user enters an invalid
1040 * license, the license will be stored but it will be flagged as invalid.
1041 *
1042 * @since 1.8.0
1043 *
1044 * @return array[]
1045 */
1046 function charitable_get_licenses() {
1047 return charitable_get_option( 'licenses', array() );
1048 }
1049
1050 /**
1051 * Get license label from plan id.
1052 *
1053 * @since 1.8.0
1054 *
1055 * @param boolean $plan_id Plan ID.
1056 * @return string
1057 */
1058 function charitable_get_license_label_from_plan_id( $plan_id = false ) {
1059
1060 if ( ! $plan_id ) {
1061 $settings = get_option( 'charitable_settings' );
1062 $plan_id = ! empty( $settings['licenses']['charitable-v2']['plan_id'] ) ? intval( $settings['licenses']['charitable-v2']['plan_id'] ) : false;
1063 }
1064
1065 if ( ! $plan_id ) {
1066 $plan_name = 'Lite';
1067 }
1068
1069 switch ( $plan_id ) {
1070 case 1:
1071 $plan_name = 'Basic';
1072 break;
1073 case 2:
1074 $plan_name = 'Plus';
1075 break;
1076 case 3:
1077 $plan_name = 'Pro';
1078 break;
1079 case 4:
1080 $plan_name = 'Elite';
1081 break;
1082 default:
1083 $plan_name = 'Lite';
1084 break;
1085 }
1086
1087 return $plan_name;
1088 }
1089
1090 /**
1091 * Get license label from plan id.
1092 *
1093 * @since 1.8.0
1094 *
1095 * @param boolean $plan_id Plan ID.
1096 * @return string
1097 */
1098 function charitable_get_license_slug_from_plan_id( $plan_id = false ) {
1099
1100 return sanitize_title( charitable_get_license_label_from_plan_id( $plan_id ) );
1101 }
1102
1103
1104