PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.16.9
GiveWP – Donation Plugin and Fundraising Platform v4.16.9
4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 All 255 releases
← All changes | includes/admin/admin-actions.php +223 -144 2.30.04.16.9 View file →
@@ -1,7 +1,8 @@
1 1 <?php
2 2
3 3 use Give\Framework\Database\DB;
4 +use Give\Helpers\Utils;
4 5 use Give\Log\ValueObjects\LogType;
5 6
6 7 /**
7 8 * Admin Actions
@@ -677,11 +678,28 @@
677 678
678 679 add_action( 'give_payments_page_top', 'give_import_page_link_callback', 11 );
679 680
680 681 /**
682 + * Avoid insecure usage of `unserialize` when the data could be submitted by the user.
683 + *
684 + * @since 3.16.1 Use Utils::giveMaybeSafeUnserialize() method
685 + * @since 3.5.0
686 + *
687 + * @param string $data Data that might be unserialized.
688 + *
689 + * @return mixed Unserialized data can be any type.
690 + */
691 +function give_maybe_safe_unserialize($data)
692 +{
693 + return Utils::maybeSafeUnserialize($data);
694 +}
695 +
696 +/**
681 697 * Load donation import ajax callback
682 698 * Fire when importing from CSV start
683 699 *
700 + * @since 4.11.0 Updated error handling to display errors in the import page.
701 + * @since 3.5.0 Extract safe unserialize logic to a function and use it in other places.
684 702 * @since 2.25.3 Append nonce to response url.
685 703 * @since 1.8.13
686 704 */
687 705 function give_donation_import_callback() {
@@ -708,12 +726,9 @@
708 726 $import_setting['delete_csv'] = $output['delete_csv'];
709 727 $import_setting['dry_run'] = $output['dry_run'];
710 728
711 729 // Parent key id.
712 - $main_key = is_serialized( $output['main_key'] )
713 - /** @since 2.26.0 Avoid insecure usage of `unserialize` when the data could be submitted by the user. */
714 - ? @unserialize( trim( $output['main_key'] ), ['allowed_classes' => false] )
715 - : $output['main_key'];
730 + $main_key = give_maybe_safe_unserialize($output['main_key']);
716 731
717 732 $current = absint( $_REQUEST['current'] );
718 733 $total_ajax = absint( $_REQUEST['total_ajax'] );
719 734 $start = absint( $_REQUEST['start'] );
@@ -727,10 +742,10 @@
727 742 $delimiter = $output['delimiter'];
728 743 }
729 744
730 745 // Processing done here.
731 - $raw_data = give_get_donation_data_from_csv( $output['csv'], $start, $end, $delimiter );
732 - $raw_key = maybe_unserialize( $output['mapto'] );
746 + $raw_data = give_get_donation_data_from_csv( $output['csv'], $start, $end, $delimiter);
747 + $raw_key = give_maybe_safe_unserialize($output['mapto']);
733 748 $import_setting['raw_key'] = $raw_key;
734 749
735 750 if ( ! empty( $output['dry_run'] ) ) {
736 751 $import_setting['csv_raw_data'] = give_get_donation_data_from_csv( $output['csv'], 1, $end, $delimiter );
@@ -747,14 +762,20 @@
747 762 remove_action( 'give_complete_donation', 'give_trigger_donation_receipt', 999 );
748 763 remove_action( 'give_insert_user', 'give_new_user_notification', 10 );
749 764 remove_action( 'give_insert_payment', 'give_payment_save_page_data' );
750 765
751 - $current_key = $start;
752 - foreach ( $raw_data as $row_data ) {
753 - $import_setting['donation_key'] = $current_key;
754 - give_save_import_donation_to_db( $raw_key, $row_data, $main_key, $import_setting );
755 - $current_key ++;
756 - }
766 + $current_key = $start;
767 + foreach ( $raw_data as $row_data ) {
768 + $import_setting['donation_key'] = $current_key;
769 + $result = give_save_import_donation_to_db( $raw_key, $row_data, $main_key, $import_setting );
770 + if ( is_string( $result ) && ! empty( $result ) ) {
771 + if ( empty( $json_data['errors'] ) ) {
772 + $json_data['errors'] = [];
773 + }
774 + $json_data['errors'][] = sprintf( __( 'Row %1$d: %2$s', 'give' ), $current_key, $result );
775 + }
776 + $current_key ++;
777 + }
757 778
758 779 // Check if function exists or not.
759 780 if ( function_exists( 'give_payment_save_page_data' ) ) {
760 781 add_action( 'give_insert_payment', 'give_payment_save_page_data' );
@@ -819,8 +840,129 @@
819 840
820 841 add_action( 'wp_ajax_give_donation_import', 'give_donation_import_callback' );
821 842
822 843 /**
844 + * Load subscription import ajax callback
845 + *
846 + * @since 4.11.0
847 + */
848 +function give_subscription_import_callback() {
849 +
850 + check_ajax_referer('give_subscription_import');
851 +
852 + if ( ! current_user_can( 'manage_give_settings' ) ) {
853 + give_die();
854 + }
855 +
856 + // Disable Give cache
857 + Give_Cache::get_instance()->disable();
858 +
859 + $import_setting = [];
860 + $fields = isset( $_POST['fields'] ) ? $_POST['fields'] : null;
861 +
862 + parse_str( $fields, $output );
863 +
864 + $import_setting['mode'] = $output['mode'];
865 + $import_setting['create_user'] = isset($output['create_user']) ? $output['create_user'] : '0';
866 + $import_setting['delimiter'] = $output['delimiter'];
867 + $import_setting['csv'] = $output['csv'];
868 + $import_setting['delete_csv'] = $output['delete_csv'];
869 + $import_setting['dry_run'] = $output['dry_run'];
870 +
871 + $main_key = give_maybe_safe_unserialize($output['main_key']);
872 +
873 + $current = absint( $_REQUEST['current'] );
874 + $total_ajax = absint( $_REQUEST['total_ajax'] );
875 + $start = absint( $_REQUEST['start'] );
876 + $end = absint( $_REQUEST['end'] );
877 + $next = absint( $_REQUEST['next'] );
878 + $total = absint( $_REQUEST['total'] );
879 + $per_page = absint( $_REQUEST['per_page'] );
880 + $delimiter = empty( $output['delimiter'] ) ? ',' : $output['delimiter'];
881 +
882 + // Ensure importer class is loaded for admin-ajax context
883 + if ( ! class_exists( 'Give_Import_Subscriptions' ) ) {
884 + require_once GIVE_PLUGIN_DIR . 'includes/admin/tools/import/class-give-import-subscriptions.php';
885 + }
886 +
887 + $importer = \Give_Import_Subscriptions::get_instance();
888 +
889 + // Processing
890 + $raw_data = $importer->get_subscription_data_from_csv( $output['csv'], $start, $end, $delimiter );
891 + $raw_key = give_maybe_safe_unserialize($output['mapto']);
892 + $import_setting['raw_key'] = $raw_key;
893 +
894 + $current_key = $start;
895 + foreach ( $raw_data as $row_data ) {
896 + $import_setting['row_key'] = $current_key;
897 + $result = $importer->import_row( $raw_key, $row_data, $main_key, $import_setting );
898 + if ( is_string( $result ) && ! empty( $result ) ) {
899 + if ( empty( $json_data['errors'] ) ) {
900 + $json_data['errors'] = [];
901 + }
902 + $json_data['errors'][] = sprintf( __( 'Row %1$d: %2$s', 'give' ), $current_key, $result );
903 + }
904 + $current_key ++;
905 + }
906 +
907 + if ( $next == false ) {
908 + $json_data = [
909 + 'success' => true,
910 + 'message' => __( 'All subscriptions uploaded successfully!', 'give' ),
911 + ];
912 + } else {
913 + $index_start = $start;
914 + $index_end = $end;
915 + $last = false;
916 + $next = true;
917 + if ( $next ) {
918 + $index_start = $index_start + $per_page;
919 + $index_end = $per_page + ( $index_start - 1 );
920 + }
921 + if ( $index_end >= $total ) {
922 + $index_end = $total;
923 + $last = true;
924 + }
925 + $json_data = [
926 + 'raw_data' => $raw_data,
927 + 'raw_key' => $raw_key,
928 + 'next' => $next,
929 + 'start' => $index_start,
930 + 'end' => $index_end,
931 + 'last' => $last,
932 + ];
933 + }
934 +
935 + $url = give_import_page_url(
936 + [
937 + 'step' => '4',
938 + 'importer-type' => 'import_subscriptions',
939 + 'csv' => $output['csv'],
940 + 'total' => $total,
941 + 'delete_csv' => $import_setting['delete_csv'],
942 + 'success' => ( isset( $json_data['success'] ) ? $json_data['success'] : '' ),
943 + 'dry_run' => $output['dry_run'],
944 + '_wpnonce' => wp_create_nonce( 'give_subscription_import_success' ),
945 + ]
946 + );
947 + $json_data['url'] = $url;
948 +
949 + $current ++;
950 + $json_data['current'] = $current;
951 +
952 + $percentage = ( 100 / ( $total_ajax + 1 ) ) * $current;
953 + $json_data['percentage'] = $percentage;
954 +
955 + // Enable Give cache
956 + Give_Cache::get_instance()->enable();
957 +
958 + $json_data = apply_filters( 'give_import_ajax_responces', $json_data, $fields );
959 + wp_die( json_encode( $json_data ) );
960 +}
961 +
962 +add_action( 'wp_ajax_give_subscription_import', 'give_subscription_import_callback' );
963 +
964 +/**
823 965 * Load core settings import ajax callback
824 966 * Fire when importing from JSON start
825 967 *
826 968 * @since 1.8.17
@@ -826,8 +968,10 @@
826 968 * @since 1.8.17
827 969 */
828 970
829 971 function give_core_settings_import_callback() {
972 + check_ajax_referer( 'give_core_settings_import' );
973 +
830 974 // Bailout.
831 975 if ( ! current_user_can( 'manage_give_settings' ) ) {
832 976 give_die();
833 977 }
@@ -1010,15 +1154,72 @@
1010 1154 }
1011 1155
1012 1156
1013 1157 /**
1158 + * Get user roles that are safe for donor registration.
1159 + *
1160 + * This excludes privileged roles like administrator, editor, give_accountant, etc.
1161 + * to prevent security issues if the default donor role setting is misconfigured.
1162 + * Only basic subscriber-level roles should be available for donor registration.
1163 + *
1164 + * @since 4.14.0
1165 + * @return array
1166 + */
1167 +function give_get_donor_safe_user_roles() {
1168 + $user_roles = [];
1169 +
1170 + // Capabilities that indicate a privileged role - exclude these
1171 + $privileged_caps = [
1172 + // WordPress privileged caps
1173 + 'manage_options',
1174 + 'edit_users',
1175 + 'delete_users',
1176 + 'create_users',
1177 + 'edit_others_posts',
1178 + 'delete_others_posts',
1179 + 'edit_pages',
1180 + 'edit_others_pages',
1181 + 'publish_pages',
1182 + 'delete_pages',
1183 + 'edit_posts',
1184 + // GiveWP privileged caps - access to sensitive donor/payment data
1185 + 'view_give_reports',
1186 + 'export_give_reports',
1187 + 'manage_give_settings',
1188 + 'view_give_sensitive_data',
1189 + 'edit_give_payments',
1190 + 'edit_give_forms',
1191 + ];
1192 +
1193 + foreach ( get_editable_roles() as $role_name => $role_info ) {
1194 + $is_privileged = false;
1195 +
1196 + // Check if role has any privileged capabilities
1197 + foreach ( $privileged_caps as $cap ) {
1198 + if ( ! empty( $role_info['capabilities'][ $cap ] ) ) {
1199 + $is_privileged = true;
1200 + break;
1201 + }
1202 + }
1203 +
1204 + // Only include non-privileged roles
1205 + if ( ! $is_privileged ) {
1206 + $user_roles[ $role_name ] = $role_info['name'];
1207 + }
1208 + }
1209 +
1210 + return $user_roles;
1211 +}
1212 +
1213 +/**
1014 1214 * Ajax handle for donor address.
1015 1215 *
1016 1216 * @since 2.0
1017 1217 * @since 2.11.0 decode url before parsing and sanitizing url when set $post.
1218 + * @since 4.9.0 rename function - PHP 8 compatibility
1018 1219 * @return void
1019 1220 */
1020 -function __give_ajax_donor_manage_addresses() {
1221 +function give_ajax_donor_manage_addresses() {
1021 1222 // Bailout.
1022 1223 if (
1023 1224 empty( $_POST['form'] ) ||
1024 1225 empty( $_POST['donorID'] )
@@ -1104,9 +1305,9 @@
1104 1305 $address_id = $is_multi_address_type ?
1105 1306 end( $array_keys ) :
1106 1307 $address_type;
1107 1308
1108 - $response_data['address_html'] = __give_get_format_address(
1309 + $response_data['address_html'] = give_get_format_address(
1109 1310 end( $donor->address['billing'] ),
1110 1311 [
1111 1312 // We can add only billing address from donor screen.
1112 1313 'type' => 'billing',
@@ -1157,9 +1358,9 @@
1157 1358 ]
1158 1359 );
1159 1360 }
1160 1361
1161 - $response_data['address_html'] = __give_get_format_address(
1362 + $response_data['address_html'] = give_get_format_address(
1162 1363 $is_multi_address_type ?
1163 1364 $donor->address[ $address_type ][ $address_id ] :
1164 1365 $donor->address[ $address_type ],
1165 1366 [
@@ -1178,13 +1379,14 @@
1178 1379
1179 1380 wp_send_json_success( $response_data );
1180 1381 }
1181 1382
1182 -add_action( 'wp_ajax_donor_manage_addresses', '__give_ajax_donor_manage_addresses' );
1383 +add_action( 'wp_ajax_donor_manage_addresses', 'give_ajax_donor_manage_addresses');
1183 1384
1184 1385 /**
1185 1386 * Admin donor billing address label
1186 1387 *
1388 + * @since 4.9.0 rename function - PHP 8 compatibility
1187 1389 * @since 2.0
1188 1390 *
1189 1391 * @param string $address_label
1190 1392 *
@@ -1189,19 +1391,20 @@
1189 1391 * @param string $address_label
1190 1392 *
1191 1393 * @return string
1192 1394 */
1193 -function __give_donor_billing_address_label( $address_label ) {
1395 +function give_donor_billing_address_label( $address_label ) {
1194 1396 $address_label = __( 'Billing Address', 'give' );
1195 1397
1196 1398 return $address_label;
1197 1399 }
1198 1400
1199 -add_action( 'give_donor_billing_address_label', '__give_donor_billing_address_label' );
1401 +add_action( 'give_donor_billing_address_label', 'give_donor_billing_address_label');
1200 1402
1201 1403 /**
1202 1404 * Admin donor personal address label
1203 1405 *
1406 + * @since 4.9.0 rename function - PHP 8 compatibility
1204 1407 * @since 2.0
1205 1408 *
1206 1409 * @param string $address_label
1207 1410 *
@@ -1206,15 +1409,15 @@
1206 1409 * @param string $address_label
1207 1410 *
1208 1411 * @return string
1209 1412 */
1210 -function __give_donor_personal_address_label( $address_label ) {
1413 +function give_donor_personal_address_label( $address_label ) {
1211 1414 $address_label = __( 'Personal Address', 'give' );
1212 1415
1213 1416 return $address_label;
1214 1417 }
1215 1418
1216 -add_action( 'give_donor_personal_address_label', '__give_donor_personal_address_label' );
1419 +add_action( 'give_donor_personal_address_label', 'give_donor_personal_address_label');
1217 1420
1218 1421 /**
1219 1422 * Update Donor Information when User Profile is updated from admin.
1220 1423 * Note: for internal use only.
@@ -1353,132 +1556,8 @@
1353 1556 }
1354 1557 }
1355 1558
1356 1559 add_action( 'wp_ajax_give_cache_flush', 'give_cache_flush', 10, 0 );
1357 -
1358 -/**
1359 - * Admin notices for errors
1360 - * note: only for internal use
1361 - *
1362 - * @access public
1363 - * @since 2.5.0
1364 - * @return void
1365 - */
1366 -function give_license_notices() {
1367 -
1368 - if ( ! current_user_can( 'manage_give_settings' ) ) {
1369 - return;
1370 - }
1371 -
1372 - // Do not show licenses notices on license tab.
1373 - if ( Give_Admin_Settings::is_setting_page( 'licenses' ) ) {
1374 - return;
1375 - }
1376 -
1377 - $give_plugins = give_get_plugins( [ 'only_premium_add_ons' => true ] );
1378 - $give_licenses = get_option( 'give_licenses', [] );
1379 - $notice_data = [];
1380 - $license_data = [];
1381 - $invalid_license_count = 0;
1382 - $addons_with_license = [];
1383 -
1384 - // Loop through Give licenses to find license status.
1385 - foreach ( $give_licenses as $key => $give_license ) {
1386 - if ( empty( $license_data[ $give_license['license'] ] ) ) {
1387 - $license_data[ $give_license['license'] ] = [
1388 - 'count' => 0,
1389 - 'add-ons' => [],
1390 - ];
1391 - }
1392 -
1393 - // Setup data for all access pass.
1394 - if ( $give_license['is_all_access_pass'] ) {
1395 - $addons_list = wp_list_pluck( $give_license['download'], 'plugin_slug' );
1396 - foreach ( $addons_list as $item ) {
1397 - $license_data[ $give_license['license'] ]['add-ons'][] = $addons_with_license[] = $item;
1398 - }
1399 - } else {
1400 - $license_data[ $give_license['license'] ]['add-ons'][] = $addons_with_license[] = $give_license['plugin_slug'];
1401 - }
1402 -
1403 - $license_data[ $give_license['license'] ]['count'] += 1;
1404 - }
1405 -
1406 - // Set data for inactive add-ons.
1407 - $inactive_addons = array_diff( wp_list_pluck( $give_plugins, 'Dir' ), $addons_with_license );
1408 -
1409 - $license_data['inactive'] = [
1410 - 'count' => count( $inactive_addons ),
1411 - 'add-ons' => array_values( $inactive_addons ),
1412 - ];
1413 -
1414 - // Unset active license add-ons as not required.
1415 - unset( $license_data['valid'] );
1416 -
1417 - // Combine site inactive with inactive and unset site_inactive because already merged information with inactive
1418 - if ( ! empty( $license_data['site_inactive'] ) ) {
1419 - $license_data['inactive']['count'] += $license_data['site_inactive']['count'];
1420 - $license_data['inactive']['add-ons'] += $license_data['site_inactive']['add-ons'];
1421 -
1422 - unset( $license_data['site_inactive'] );
1423 - }
1424 -
1425 - // Loop through license data.
1426 - foreach ( $license_data as $key => $license ) {
1427 - if ( ! $license['count'] ) {
1428 - continue;
1429 - }
1430 -
1431 - $notice_data[ $key ] = sprintf(
1432 - '%1$s %2$s',
1433 - $license['count'],
1434 - $key
1435 - );
1436 -
1437 - // This will contain sum of count expect license with valid status.
1438 - $invalid_license_count += $license['count'];
1439 - }
1440 -
1441 - // Prepare license notice description.
1442 - $prepared_notice_status = implode( ' , ', $notice_data );
1443 - $prepared_notice_status = 2 <= count( $notice_data )
1444 - ? substr_replace( $prepared_notice_status, 'and', strrpos( $prepared_notice_status, ',' ), 1 )
1445 - : $prepared_notice_status;
1446 -
1447 - $notice_description = sprintf(
1448 - _n(
1449 - 'Your GiveWP add-on is not receiving critical updates and new features because you have %1$s license key. Please <a href="%2$s" title="%3$s">activate your license</a> to receive updates and <a href="%4$s" target="_blank" title="%5$s">priority support</a>',
1450 - 'Your GiveWP add-ons are not receiving critical updates and new features because you have %1$s license keys. Please <a href="%2$s" title="%3$s">activate your license</a> to receive updates and <a href="%4$s" target="_blank" title="%5$s">priority support</a>',
1451 - $invalid_license_count,
1452 - 'give'
1453 - ),
1454 - $prepared_notice_status,
1455 - admin_url( 'edit.php?post_type=give_forms&page=give-settings&tab=licenses' ),
1456 - __( 'Activate License', 'give' ),
1457 - esc_url( 'http://docs.givewp.com/pb-priority-support' ),
1458 - __( 'Priority Support', 'give' )
1459 - );
1460 -
1461 - // Check by add-on if any give add-on activated without license.
1462 - // Do not show this notice if add-on activated with in 3 days.
1463 - $is_required_days_past = current_time( 'timestamp' ) > ( Give_Cache_Setting::get_option( 'give_addon_last_activated' ) + ( 3 * DAY_IN_SECONDS ) );
1464 -
1465 - // Default license notice arguments.
1466 - $license_notice_args = [
1467 - 'id' => 'give-invalid-expired-license',
1468 - 'type' => 'error',
1469 - 'description' => $notice_description,
1470 - 'dismissible_type' => 'user',
1471 - 'dismiss_interval' => 'shortly',
1472 - ];
1473 -
1474 - // Register Notices.
1475 - if ( $invalid_license_count && $is_required_days_past ) {
1476 - Give()->notices->register_notice( $license_notice_args );
1477 - }
1478 -}
1479 -
1480 -add_action( 'admin_notices', 'give_license_notices' );
1481 1560
1482 1561
1483 1562 /**
1484 1563 * Log give addon activation time