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

912 lines 24.4 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) 2022, 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', 'chariable' ),
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 // if ( 'charitable_settings' == $_GET['page'] ) {
307 // return 'charitable_settings';
308 // }
309
310 // if ( 'charitable_system_status' == $_GET['page'] ) {
311 // return 'charitable_system_status';
312 // }
313 }
314
315 return false;
316 }
317
318 /**
319 * Appends UTM parameters to a given URL.
320 *
321 * @since 1.7.0
322 *
323 * @param string $base_url Base URL.
324 * @param string $utm_medium utm_medium parameter.
325 * @param string $utm_content Optional. utm_content parameter.
326 * @return string $url Full Google Analytics campaign URL.
327 */
328 function charitable_ga_url( $base_url, $utm_medium, $utm_content = false ) {
329 /**
330 * Filters the UTM campaign for generated links.
331 *
332 * @since 3.0.0
333 *
334 * @param string $utm_campaign
335 */
336 $utm_campaign = apply_filters( 'charitable_utm_campaign', 'WP+Charitable' );
337
338 $args = array(
339 'utm_source' => 'WordPress',
340 'utm_campaign' => $utm_campaign,
341 'utm_medium' => $utm_medium,
342 );
343
344 if ( ! empty( $utm_content ) ) {
345 $args['utm_content'] = $utm_content;
346 }
347
348 return esc_url( add_query_arg( $args, $base_url ) );
349 }
350
351 /**
352 * URL for upgrading to Pro (or another Pro licecnse).
353 *
354 * @since 1.7.0
355 *
356 * @param string $utm_medium utm_medium parameter.
357 * @param string $utm_content Optional. utm_content parameter.
358 * @return string
359 */
360 function charitable_pro_upgrade_url( $utm_medium, $utm_content = '' ) {
361 return apply_filters(
362 'charitable_upgrade_link',
363 charitable_ga_url(
364 'https://wpcharitable.com/lite-vs-pro/',
365 urlencode( $utm_medium ),
366 urlencode( $utm_content )
367 ),
368 $utm_medium,
369 $utm_content
370 );
371 }
372
373 /**
374 * Get the current installation license type (always lowercase).
375 *
376 * @since 1.7.0.3
377 *
378 * @return string|false
379 */
380 function charitable_get_license_type() {
381
382 $type = charitable_setting( 'type', '', 'charitable_license' );
383
384 if ( empty( $type ) || ! charitable()->is_pro() ) {
385 return false;
386 }
387
388 return strtolower( $type );
389 }
390
391 /**
392 * Get when WPCharitable was first installed.
393 *
394 * @since 1.6.0
395 *
396 * @param string $type Specific install type to check for.
397 *
398 * @return int|false Unix timestamp. False on failure.
399 */
400 function charitable_get_activated_timestamp( $type = '' ) {
401
402 $activated = (array) get_option( 'charitable_activated', [] );
403
404 if ( empty( $activated ) ) {
405 return false;
406 }
407
408 // When a passed install type is empty, then get it from a DB.
409 // If it is installed/activated first, it is saved first.
410 $type = empty( $type ) ? (string) array_keys( $activated )[0] : $type;
411
412 if ( ! empty( $activated[ $type ] ) ) {
413 return absint( $activated[ $type ] );
414 }
415
416 // Fallback.
417 $types = array_diff( [ 'lite', 'pro' ], [ $type ] );
418
419 foreach ( $types as $_type ) {
420 if ( ! empty( $activated[ $_type ] ) ) {
421 return absint( $activated[ $_type ] );
422 }
423 }
424
425 return false;
426 }
427
428 /**
429 * Check permissions for currently logged in user, taken from Charitable.
430 * Both short (e.g. 'view_own_forms') or long (e.g. 'charitable_view_own_forms') capability name can be used.
431 * Only Charitable capabilities get processed.
432 *
433 * @since 1.7.0.3
434 *
435 * @param array|string $caps Capability name(s).
436 * @param int $id ID of the specific object to check against if capability is a "meta" cap. "Meta"
437 * capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used by
438 * map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
439 * edit_others_posts', etc. Accessed via func_get_args() and passed to
440 * WP_User::has_cap(), then map_meta_cap().
441 *
442 * @return bool
443 */
444 function charitable_current_user_can( $caps = [], $id = 0 ) {
445
446 $user_can = current_user_can( $caps , $id );
447
448 return apply_filters( 'charitable_current_user_can', $user_can, $caps, $id );
449 }
450
451 /**
452 * Determines whether the current request is a WP CLI request.
453 *
454 * @since 1.7.0.3
455 *
456 * @return bool
457 */
458 function charitable_doing_wp_cli() {
459
460 return defined( 'WP_CLI' ) && WP_CLI;
461 }
462
463 /**
464 * Modify the default USer-Agent generated by wp_remote_*() to include additional information.
465 *
466 * @since 1.7.0.3
467 *
468 * @return string
469 */
470 function charitable_get_default_user_agent() {
471
472 $wpcharitable_type = function_exists( 'charitable_is_pro' ) && charitable_is_pro() ? 'Paid' : 'Lite';
473
474 return 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) . '; WPCharitable/' . $wpcharitable_type;
475 }
476
477
478 function charitable_theme_template_paths() {
479
480 $template_dir = 'charitable';
481
482 $file_paths = array(
483 1 => trailingslashit( get_stylesheet_directory() ) . $template_dir,
484 10 => trailingslashit( get_template_directory() ) . $template_dir,
485 100 => trailingslashit( CHARTIABLE_DIRECTORY_PATH ) . 'includes',
486 );
487
488 $file_paths = apply_filters( 'charitable_helpers_templates_get_theme_template_paths', $file_paths );
489
490 // Sort the file paths based on priority.
491 ksort( $file_paths, SORT_NUMERIC );
492
493 return array_map( 'trailingslashit', $file_paths );
494 }
495
496 /**
497 * Locate a template and return the path for inclusion.
498 *
499 * @since 1.7.0.3
500 *
501 * @param string $template_name Template name.
502 *
503 * @return string
504 */
505 function charitable_locate_template( $template_name ) {
506
507 // Trim off any slashes from the template name.
508 $template_name = ltrim( $template_name, '/' );
509
510 if ( empty( $template_name ) ) {
511 return apply_filters( 'charitable_helpers_templates_locate', '', $template_name );
512 }
513
514 $located = '';
515
516 // Try locating this template file by looping through the template paths.
517 foreach ( charitable_theme_template_paths() as $template_path ) {
518 if ( file_exists( $template_path . $template_name ) ) {
519 $located = $template_path . $template_name;
520 break;
521 }
522 }
523
524 return apply_filters( 'charitable_helpers_templates_locate', $located, $template_name );
525 }
526
527 /**
528 * Include a template.
529 * Use 'require' if $args are passed or 'load_template' if not.
530 *
531 * @since 1.7.0.3
532 *
533 * @param string $template_name Template name.
534 * @param array $args Arguments.
535 * @param bool $extract Extract arguments.
536 *
537 * @throws \RuntimeException If extract() tries to modify the scope.
538 */
539 function charitable_admin_include_html( $template_name, $args = array(), $extract = false ) {
540
541 $template_name .= '.php';
542
543 // Allow 3rd party plugins to filter template file from their plugin.
544 $located = apply_filters( 'charitable_helpers_templates_include_html_located', charitable_locate_template( $template_name ), $template_name, $args, $extract );
545 $args = apply_filters( 'charitable_helpers_templates_include_html_args', $args, $template_name, $extract );
546
547 if ( empty( $located ) || ! \is_readable( $located ) ) {
548 return;
549 }
550
551 // Load template WP way if no arguments were passed.
552 if ( empty( $args ) ) {
553 load_template( $located, false );
554 return;
555 }
556
557 $extract = apply_filters( 'charitable_helpers_templates_include_html_extract_args', $extract, $template_name, $args );
558
559 if ( $extract && is_array( $args ) ) {
560
561 $created_vars_count = extract( $args, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract
562
563 // Protecting existing scope from modification.
564 if ( count( $args ) !== $created_vars_count ) {
565 throw new \RuntimeException( 'Extraction failed: variable names are clashing with the existing ones.' );
566 }
567 }
568
569 require $located;
570 }
571
572 /**
573 * Like self::include_html, but returns the HTML instead of including.
574 *
575 * @since 1.7.0.3
576 *
577 * @param string $template_name Template name.
578 * @param array $args Arguments.
579 * @param bool $extract Extract arguments.
580 *
581 * @return string
582 */
583 function charitable_admin_get_html( $template_name, $args = array(), $extract = false ) {
584 ob_start();
585 charitable_admin_include_html( $template_name, $args, $extract );
586 return ob_get_clean();
587 }
588
589 /**
590 * Include a template - alias to \charitable\Helpers\Template::get_html.
591 * Use 'require' if $args are passed or 'load_template' if not.
592 *
593 * @since 1.7.0.3
594 *
595 * @param string $template_name Template name.
596 * @param array $args Arguments.
597 * @param bool $extract Extract arguments.
598 *
599 * @throws \RuntimeException If extract() tries to modify the scope.
600 *
601 * @return string Compiled HTML.
602 */
603 function charitable_render( $template_name, $args = array(), $extract = false ) {
604 return charitable_admin_get_html( $template_name, $args, $extract );
605 }
606
607 /**
608 * Determine if the plugin/addon installations are allowed.
609 *
610 * @since 1.6.2.3
611 *
612 * @param string $type Should be `plugin` or `addon`.
613 *
614 * @return bool
615 */
616 function charitable_can_install( $type ) {
617
618 return charitable_can_do( 'install', $type );
619 }
620
621 /**
622 * Determine if the plugin/addon activations are allowed.
623 *
624 * @since 1.7.3
625 *
626 * @param string $type Should be `plugin` or `addon`.
627 *
628 * @return bool
629 */
630 function charitable_can_activate( $type ) {
631
632 return charitable_can_do( 'activate', $type );
633 }
634
635 /**
636 * Determine if the plugin/addon installations/activations are allowed.
637 *
638 * @since 1.7.3
639 *
640 * @internal Use charitable_can_activate() or charitable_can_install() instead.
641 *
642 * @param string $what Should be 'activate' or 'install'.
643 * @param string $type Should be `plugin` or `addon`.
644 *
645 * @return bool
646 */
647 function charitable_can_do( $what, $type ) {
648
649 if ( ! in_array( $what, [ 'install', 'activate' ], true ) ) {
650 return false;
651 }
652
653 if ( ! in_array( $type, [ 'plugin', 'addon' ], true ) ) {
654 return false;
655 }
656
657 $capability = $what . '_plugins';
658
659 if ( ! current_user_can( $capability ) ) {
660 return false;
661 }
662
663 // Determine whether file modifications are allowed and it is activation permissions checking.
664 if ( $what === 'install' && ! wp_is_file_mod_allowed( 'charitable_can_install' ) ) {
665 return false;
666 }
667
668 // All plugin checks are done.
669 if ( $type === 'plugin' ) {
670 return true;
671 }
672
673 // Addons require additional license checks.
674 // $license = get_option( 'charitable_license', [] );
675
676 // // Allow addons installation if license is not expired, enabled and valid.
677 // return empty( $license['is_expired'] ) && empty( $license['is_disabled'] ) && empty( $license['is_invalid'] );
678
679 return true;
680 }
681
682 /**
683 * Perform test connection to verify that the current web host can successfully
684 * make outbound SSL connections.
685 *
686 * @since 1.4.5
687 */
688 function charitable_verify_ssl() {
689
690 // Run a security check.
691 check_ajax_referer( 'charitable-admin', 'nonce' );
692
693 // Check for permissions.
694 if ( ! charitable_current_user_can() ) {
695 wp_send_json_error(
696 array(
697 'msg' => esc_html__( 'You do not have permission to perform this operation.', 'charitable' ),
698 )
699 );
700 }
701
702 $response = wp_remote_post( 'https://wpcharitable.com/connection-test.php' );
703
704 if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
705 wp_send_json_success(
706 array(
707 'msg' => esc_html__( 'Success! Your server can make SSL connections.', 'charitable' ),
708 )
709 );
710 }
711
712 wp_send_json_error(
713 array(
714 'msg' => esc_html__( 'There was an error and the connection failed. Please contact your web host with the technical details below.', 'charitable' ),
715 'debug' => '<pre>' . print_r( map_deep( $response, 'wp_strip_all_tags' ), true ) . '</pre>',
716 )
717 );
718 }
719 add_action( 'wp_ajax_charitable_verify_ssl', 'charitable_verify_ssl' );
720
721 /**
722 * Deactivate addon.
723 *
724 * @since 1.0.0
725 * @since 1.6.2.3 Updated the permissions checking.
726 */
727 function charitable_deactivate_addon() {
728
729 // Run a security check.
730 check_ajax_referer( 'charitable-admin', 'nonce' );
731
732 // Check for permissions.
733 if ( ! current_user_can( 'deactivate_plugins' ) ) {
734 wp_send_json_error( esc_html__( 'Plugin deactivation is disabled for you on this site.', 'charitable' ) );
735 }
736
737 $type = empty( $_POST['type'] ) ? 'addon' : sanitize_key( $_POST['type'] );
738
739 if ( isset( $_POST['plugin'] ) ) {
740 $plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
741
742 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
743 error_log( 'charitable_deactivate_addon' );
744 error_log( print_r( $plugin, true ) );
745 error_log( print_r( $_POST, true ) );
746 error_log( print_r( $type, true ) );
747 }
748
749 deactivate_plugins( $plugin );
750
751 do_action( 'charitable_plugin_deactivated', $plugin );
752
753 if ( $type === 'plugin' ) {
754 wp_send_json_success( esc_html__( 'Plugin deactivated.', 'charitable' ) );
755 } else {
756 wp_send_json_success( esc_html__( 'Addon deactivated.', 'charitable' ) );
757 }
758 }
759
760 wp_send_json_error( esc_html__( 'Could not deactivate the addon. Please deactivate from the Plugins page.', 'charitable' ) );
761 }
762 add_action( 'wp_ajax_charitable_deactivate_addon', 'charitable_deactivate_addon' );
763
764 /**
765 * Activate addon.
766 *
767 * @since 1.0.0
768 * @since 1.6.2.3 Updated the permissions checking.
769 */
770 function charitable_ajax_activate_addon() {
771
772 // Run a security check.
773 check_ajax_referer( 'charitable-admin', 'nonce' );
774
775 // Check for permissions.
776 if ( ! current_user_can( 'activate_plugins' ) ) {
777 wp_send_json_error( esc_html__( 'Plugin activation is disabled for you on this site.', 'charitable' ) );
778 }
779
780 $type = 'addon';
781
782 if ( isset( $_POST['plugin'] ) ) {
783
784 if ( ! empty( $_POST['type'] ) ) {
785 $type = sanitize_key( $_POST['type'] );
786 }
787
788 $plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
789
790 $activate = activate_plugins( $plugin );
791
792 /**
793 * Fire after plugin activating via the Charitable installer.
794 *
795 * @since 1.6.3.1
796 *
797 * @param string $plugin Path to the plugin file relative to the plugins directory.
798 */
799 do_action( 'charitable_plugin_activated', $plugin );
800
801 if ( ! is_wp_error( $activate ) ) {
802 if ( $type === 'plugin' ) {
803 wp_send_json_success( esc_html__( 'Plugin activated.', 'charitable' ) );
804 } else {
805 wp_send_json_success( esc_html__( 'Addon activated.', 'charitable' ) );
806 }
807 }
808 }
809
810 if ( $type === 'plugin' ) {
811 wp_send_json_error( esc_html__( 'Could not activate the plugin. Please activate it on the Plugins page.', 'charitable' ) );
812 }
813
814 wp_send_json_error( esc_html__( 'Could not activate the addon. Please activate it on the Plugins page.', 'charitable' ) );
815 }
816 add_action( 'wp_ajax_charitable_activate_addon', 'charitable_ajax_activate_addon' );
817
818
819 /**
820 * Installs an Charitable addon.
821 *
822 * @since 1.0.0
823 */
824 function charitable_ajax_install_addon() {
825
826 // Run a security check first.
827 check_admin_referer( 'charitable-admin', 'nonce' );
828
829 // Install the addon.
830 if ( isset( $_POST['plugin'] ) ) {
831 $download_url = esc_url_raw( wp_unslash( $_POST['plugin'] ) );
832 global $hook_suffix;
833
834 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
835 error_log( 'charitable_ajax_install_addon' );
836 error_log( print_r( $_POST, true ) );
837 error_log( print_r( $download_url, true ) );
838 }
839
840 // Set the current screen to avoid undefined notices.
841 set_current_screen();
842
843 // Prepare variables.
844 $method = '';
845 $url = add_query_arg(
846 array(
847 'page' => 'charitable-settings',
848 ),
849 admin_url( 'admin.php' )
850 );
851 $url = esc_url( $url );
852
853 // Start output bufferring to catch the filesystem form if credentials are needed.
854 ob_start();
855 $creds = request_filesystem_credentials( $url, $method, false, false, null );
856 if ( false === $creds ) {
857 $form = ob_get_clean();
858 echo wp_json_encode( array( 'form' => $form ) );
859 die;
860 }
861
862 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
863 error_log( 'charitable_ajax_install_addon creds' );
864 error_log( print_r( $creds, true ) );
865 }
866
867 // If we are not authenticated, make it happen now.
868 if ( ! WP_Filesystem( $creds ) ) {
869 ob_start();
870 request_filesystem_credentials( $url, $method, true, false, null );
871 $form = ob_get_clean();
872 echo wp_json_encode( array( 'form' => $form ) );
873 die;
874 }
875
876 // Do not allow WordPress to search/download translations, as this will break JS output.
877 remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 );
878
879 // We do not need any extra credentials if we have gotten this far, so let's install the plugin.
880 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
881 require_once plugin_dir_path( CHARTIABLE_DIRECTORY_PATH ) . 'charitable/includes/utilities/Skin.php';
882
883 // Create the plugin upgrader with our custom skin.
884 $skin = new Charitable_Skin();
885 $installer = new Plugin_Upgrader( $skin );
886 $installer->install( $download_url );
887
888 // Flush the cache and return the newly installed plugin basename.
889 wp_cache_flush();
890
891 if ( $installer->plugin_info() ) {
892 $plugin_basename = $installer->plugin_info();
893 $plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
894 // attempt to activate the installed addon, save the user a step
895 $activate = activate_plugins( $plugin_basename );
896 if ( ! is_wp_error( $activate ) ) {
897 wp_send_json_success( array( 'basename' => $plugin_basename, 'is_activated' => true, 'msg' => esc_html__( 'Addon installed and activated.', 'charitable' ) ) );
898 } else {
899 wp_send_json_success( array( 'basename' => $plugin_basename, 'msg' => esc_html__( 'Addon installed.', 'charitable' ) ) );
900 }
901 // wp_send_json_success( esc_html__( 'Addon installed.', 'charitable' ) );
902 die;
903 }
904 }
905
906 // Send back a response.
907 echo wp_json_encode( true );
908 die;
909
910 }
911 add_action( 'wp_ajax_charitable_install_addon', 'charitable_ajax_install_addon' );
912