← All changes
|
includes/admin/reports/class-charitable-reports.php
+151
-57
1.8.11
→
1.8.13.1
View file →
| @@ -458,9 +458,9 @@ | ||
| 458 | 458 | } |
| 459 | 459 | $badge_css = strtolower( $donation_parent ) === 'recurring' ? 'recurring' : 'one-time'; |
| 460 | 460 | $badge_label = strtolower( $donation_parent ) === 'recurring' ? esc_html__( 'Recurring', 'charitable' ) : esc_html__( 'One-Time', 'charitable' ); |
| 461 | 461 | |
| 462 | - $display_amount = ! empty( $top_donor->total_donation_amount ) ? charitable_format_money( $top_donor->total_donation_amount, 2, true ) : charitable_format_money( esc_html( $top_donor->amount ), 2, true ); | |
| 462 | + $display_amount = ! empty( $top_donor->total_donation_amount ) ? charitable_format_money( $top_donor->total_donation_amount, false, true ) : charitable_format_money( esc_html( $top_donor->amount ), false, true ); | |
| 463 | 463 | $display_count = ! empty( $top_donor->total_donation_count ) ? intval( $top_donor->total_donation_count ) : 1; |
| 464 | 464 | |
| 465 | 465 | // based on display count, override the badge css/label. |
| 466 | 466 | if ( $display_count > 1 ) { |
| @@ -761,15 +761,15 @@ | ||
| 761 | 761 | case 'charitable-failed': |
| 762 | 762 | $admin_campaign_url = ! empty( $activity->campaign_id ) ? charitable_get_admin_campaign_edit_url( $activity->campaign_id ) : '#'; |
| 763 | 763 | $admin_donation_url = ! empty( $activity->item_id ) ? charitable_get_admin_donation_edit_url( $activity->item_id ) : false; |
| 764 | 764 | $campaign_title = ! empty( $activity->campaign_title ) ? ' to <a target="_blank" href="' . $admin_campaign_url . '"><span class="campaign-title">' . esc_html( $activity->campaign_title ) . '</span></a> ' : ''; |
| 765 | - $secondary_info = $admin_donation_url ? '<p class="charitable-reports-activity-secondary-info amount"><a href="' . $admin_donation_url . '" target="_blank">' . charitable_format_money( $activity->amount, 2, true ) . '</a>' . $campaign_title . '</p>' : '<p class="charitable-reports-activity-secondary-info amount">' . charitable_format_money( $activity->amount, 2, true ) . $campaign_title . '</p>'; | |
| 765 | + $secondary_info = $admin_donation_url ? '<p class="charitable-reports-activity-secondary-info amount"><a href="' . $admin_donation_url . '" target="_blank">' . charitable_format_money( $activity->amount, false, true ) . '</a>' . $campaign_title . '</p>' : '<p class="charitable-reports-activity-secondary-info amount">' . charitable_format_money( $activity->amount, false, true ) . $campaign_title . '</p>'; | |
| 766 | 766 | break; |
| 767 | 767 | default: |
| 768 | 768 | $admin_campaign_url = ! empty( $activity->campaign_id ) ? charitable_get_admin_campaign_edit_url( $activity->campaign_id ) : '#'; |
| 769 | 769 | $admin_donation_url = ! empty( $activity->item_id ) ? charitable_get_admin_donation_edit_url( $activity->item_id ) : false; |
| 770 | 770 | $campaign_title = ! empty( $activity->campaign_title ) ? ' to <a target="_blank" href="' . $admin_campaign_url . '"><span class="campaign-title">' . esc_html( $activity->campaign_title ) . '</span></a> ' : ''; |
| 771 | - $secondary_info = $admin_donation_url ? '<p class="charitable-reports-activity-secondary-info amount"><a href="' . $admin_donation_url . '" target="_blank">' . charitable_format_money( $activity->amount, 2, true ) . '</a>' . $campaign_title . '</p>' : '<p class="charitable-reports-activity-secondary-info amount">' . charitable_format_money( $activity->amount, 2, true ) . $campaign_title . '</p>'; | |
| 771 | + $secondary_info = $admin_donation_url ? '<p class="charitable-reports-activity-secondary-info amount"><a href="' . $admin_donation_url . '" target="_blank">' . charitable_format_money( $activity->amount, false, true ) . '</a>' . $campaign_title . '</p>' : '<p class="charitable-reports-activity-secondary-info amount">' . charitable_format_money( $activity->amount, false, true ) . $campaign_title . '</p>'; | |
| 772 | 772 | break; |
| 773 | 773 | } |
| 774 | 774 | |
| 775 | 775 | return $secondary_info; |
| @@ -1125,9 +1125,9 @@ | ||
| 1125 | 1125 | foreach ( $donor_data as $index => $data ) : |
| 1126 | 1126 | |
| 1127 | 1127 | $donor_id = isset( $data->donor_id ) ? intval( $data->donor_id ) : 0; |
| 1128 | 1128 | $donor_email = isset( $data->email ) ? esc_html( $data->email ) : ''; |
| 1129 | - $donor_avatar = ( is_email( $donor_email ) ) ? get_avatar_url( $donor_email ) : charitable()->get_path( 'assets', false ) . '/images/misc/placeholder-avatar-small.jpg'; | |
| 1129 | + $donor_avatar = ( is_email( $donor_email ) ) ? get_avatar_url( $donor_email ) : charitable()->get_path( 'assets', false ) . 'images/misc/placeholder-avatar-small.jpg'; | |
| 1130 | 1130 | $donor_name = esc_html( $data->first_name ) . ' ' . esc_html( $data->last_name ); |
| 1131 | 1131 | $donor_number_donations = isset( $data->total_count_donations ) ? intval( $data->total_count_donations ) : 0; |
| 1132 | 1132 | $donor_number_campaigns = isset( $data->total_count_campaigns ) ? intval( $data->total_count_campaigns ) : 0; |
| 1133 | 1133 | $donor_average = $donor_number_donations > 0 ? $data->total_amount / $donor_number_donations : 0; |
| @@ -1172,11 +1172,11 @@ | ||
| 1172 | 1172 | } elseif ( $is_sample_data ) { |
| 1173 | 1173 | $last_donation_html = $last_donation_campaign_name; |
| 1174 | 1174 | } |
| 1175 | 1175 | } elseif ( $last_donation_date && $last_donation_campaign_name && $last_donation_campaign_id ) { |
| 1176 | - $last_donation_html = charitable_format_money( esc_html( $data->amount ), 2, true ) . '<br/><a href="' . $action_link . '" target="_blank">' . $last_donation_campaign_name . '</a><br/>' . $last_donation_date; | |
| 1176 | + $last_donation_html = charitable_format_money( esc_html( $data->amount ), false, true ) . '<br/><a href="' . $action_link . '" target="_blank">' . $last_donation_campaign_name . '</a><br/>' . $last_donation_date; | |
| 1177 | 1177 | } elseif ( $is_sample_data ) { |
| 1178 | - $last_donation_html = charitable_format_money( esc_html( $data->amount ), 2, true ) . '<br/>' . $last_donation_campaign_name . '<br/>' . $last_donation_date; | |
| 1178 | + $last_donation_html = charitable_format_money( esc_html( $data->amount ), false, true ) . '<br/>' . $last_donation_campaign_name . '<br/>' . $last_donation_date; | |
| 1179 | 1179 | } |
| 1180 | 1180 | |
| 1181 | 1181 | if ( $report_type === 'donors-first-time' ) { |
| 1182 | 1182 | ?> |
| @@ -1183,15 +1183,15 @@ | ||
| 1183 | 1183 | |
| 1184 | 1184 | <tr id="type-<?php echo esc_attr( $report_type ); ?>-donor-<?php echo intval( $donor_id ); ?>" class="type-<?php echo esc_attr( $report_type ); ?>"> |
| 1185 | 1185 | <td class="charitable-avatar" style="width: 32px;"><img src="<?php echo esc_url( $donor_avatar ); ?>" class="avatar avatar-32 photo charitable-avatar-donor-<?php echo intval( $donor_id ); ?>" width="32" height="32" alt="<?php echo esc_html__( 'Profile picture of', 'charitable' ); ?> <?php echo esc_html( $donor_name ); ?>"></td> |
| 1186 | 1186 | <td class="donated column-date" data-colname="<?php echo esc_html__( 'Name / Email', 'charitable' ); ?>"><?php echo esc_html( $donor_name ); ?><br/><?php echo esc_html( $donor_email ); ?></td> |
| 1187 | - <td class="donated column-donation-amount" data-colname="<?php echo esc_html__( 'Donation Amount', 'charitable' ); ?>"><?php echo charitable_format_money( esc_html( $data->total_amount ), 2, true ); // phpcs:ignore ?></td> | |
| 1187 | + <td class="donated column-donation-amount" data-colname="<?php echo esc_html__( 'Donation Amount', 'charitable' ); ?>"><?php echo charitable_format_money( esc_html( $data->total_amount ), false, true ); // phpcs:ignore ?></td> | |
| 1188 | 1188 | <td class="donated column-campaign" data-colname="<?php echo esc_html__( 'Campaign', 'charitable' ); ?>"><?php echo $last_donation_html; // phpcs:ignore ?></td> |
| 1189 | 1189 | <td class="donated column-date" data-colname="<?php echo esc_html__( 'Date', 'charitable' ); ?>"><?php echo esc_html( $last_donation_date ); ?></td> |
| 1190 | 1190 | <?php |
| 1191 | 1191 | $display = '<td class="donated column-actions " data-colname="Actions">'; |
| 1192 | 1192 | if ( $action_label !== false ) { |
| 1193 | - $display .= '<a class="charitable-campaign-action-button" title="' . $action_label . '" href="' . esc_url( $action_link ) . '" target="_blank"><img src="' . charitable()->get_path( 'assets', false ) . '/images/icons/eye.svg" width="14" height="14" alt="' . $action_label . '" /></a>'; | |
| 1193 | + $display .= '<a class="charitable-campaign-action-button" title="' . $action_label . '" href="' . esc_url( $action_link ) . '" target="_blank"><img src="' . charitable()->get_path( 'assets', false ) . 'images/icons/eye.svg" width="14" height="14" alt="' . $action_label . '" /></a>'; | |
| 1194 | 1194 | } |
| 1195 | 1195 | $display .= '</td>'; |
| 1196 | 1196 | |
| 1197 | 1197 | echo $display; // phpcs:ignore |
| @@ -1202,17 +1202,17 @@ | ||
| 1202 | 1202 | |
| 1203 | 1203 | <tr id="type-<?php echo esc_attr( $report_type ); ?>-donor-<?php echo intval( $donor_id ); ?>" class="type-<?php echo esc_attr( $report_type ); ?>"> |
| 1204 | 1204 | <td class="charitable-avatar" style="width: 32px;"><img src="<?php echo esc_url( $donor_avatar ); ?>" class="avatar avatar-32 photo charitable-avatar-donor-<?php echo intval( $donor_id ); ?>" width="32" height="32" alt="<?php echo esc_html__( 'Profile picture of', 'charitable' ); ?> <?php echo esc_html( $donor_name ); ?>"></td> |
| 1205 | 1205 | <td class="donated column-date" data-colname="<?php echo esc_html__( 'Name / Email', 'charitable' ); ?>"><?php echo esc_html( $donor_name ); ?><br/><?php echo esc_html( $donor_email ); ?></td> |
| 1206 | - <td class="donated column-total-donations" data-colname="<?php echo esc_html__( 'Total Donations', 'charitable' ); ?>"><?php echo charitable_format_money( esc_html( $data->total_amount ), 2, true ); // phpcs:ignore ?></td> | |
| 1206 | + <td class="donated column-total-donations" data-colname="<?php echo esc_html__( 'Total Donations', 'charitable' ); ?>"><?php echo charitable_format_money( esc_html( $data->total_amount ), false, true ); // phpcs:ignore ?></td> | |
| 1207 | 1207 | <td class="donated column-number-donations" data-colname="Number of Donations"><?php echo esc_html( $donor_number_donations ); ?></td> |
| 1208 | - <td class="donated column-average" data-colname="Average"><?php echo charitable_format_money( $donor_average, 2, true ); // phpcs:ignore ?></td> | |
| 1208 | + <td class="donated column-average" data-colname="Average"><?php echo charitable_format_money( $donor_average, false, true ); // phpcs:ignore ?></td> | |
| 1209 | 1209 | <td class="donated column-number-campaigns" data-colname="Number of Campaigns"><?php echo esc_html( $donor_number_campaigns ); ?></td> |
| 1210 | 1210 | <td class="donated column-last-donation" data-colname="Last Donation"><?php echo $last_donation_html; // phpcs:ignore ?></td> |
| 1211 | 1211 | <?php |
| 1212 | 1212 | $display = '<td class="donated column-actions" data-colname="Actions">'; |
| 1213 | 1213 | if ( $action_label !== false ) { |
| 1214 | - $display .= '<a class="charitable-campaign-action-button" title="' . $action_label . '" href="' . esc_url( $action_link ) . '" target="_blank"><img src="' . charitable()->get_path( 'assets', false ) . '/images/icons/eye.svg" width="14" height="14" alt="' . $action_label . '" /</a>'; | |
| 1214 | + $display .= '<a class="charitable-campaign-action-button" title="' . $action_label . '" href="' . esc_url( $action_link ) . '" target="_blank"><img src="' . charitable()->get_path( 'assets', false ) . 'images/icons/eye.svg" width="14" height="14" alt="' . $action_label . '" /</a>'; | |
| 1215 | 1215 | } |
| 1216 | 1216 | $display .= '</td>'; |
| 1217 | 1217 | |
| 1218 | 1218 | echo $display; // phpcs:ignore |
| @@ -1443,18 +1443,34 @@ | ||
| 1443 | 1443 | $donations_by_day = array(); |
| 1444 | 1444 | $days = $this->get_days_between_dates( $this->start_date, $this->end_date ); |
| 1445 | 1445 | $refunded_statuses = array( 'charitable-refunded' ); |
| 1446 | 1446 | |
| 1447 | + /** | |
| 1448 | + * Two grouped queries for the whole range, rather than two queries per calendar | |
| 1449 | + * day. The old per-day loop made the cost a function of how long the organisation | |
| 1450 | + * had existed rather than how much data it had, so a 24-year range issued ~18,000 | |
| 1451 | + * queries and hit max_execution_time before returning anything. | |
| 1452 | + * | |
| 1453 | + * The range is taken from $days rather than from $this->start_date / | |
| 1454 | + * $this->end_date, because get_days_between_dates() normalises a falsy date to | |
| 1455 | + * "now". Reading the bounds back off $days guarantees the query covers exactly | |
| 1456 | + * the days this loop is about to render, however those dates were normalised. | |
| 1457 | + */ | |
| 1458 | + $day_keys = array_keys( $days ); | |
| 1459 | + $range_start = reset( $day_keys ); | |
| 1460 | + $range_end = end( $day_keys ); | |
| 1461 | + | |
| 1462 | + $completed_by_day = $campaign_donations_table->get_donations_summary_by_day_range( $range_start, $range_end, array( 'charitable-completed', 'charitable-refunded' ) ); | |
| 1463 | + $refunded_by_day = $campaign_donations_table->get_donations_summary_by_day_range( $range_start, $range_end, $refunded_statuses ); | |
| 1464 | + | |
| 1447 | 1465 | foreach ( $days as $day_unformatted => $day_label ) { |
| 1448 | 1466 | |
| 1449 | - $donation_today_completed = $campaign_donations_table->get_donations_summary_by_period( gmdate( 'Y-m-d', strtotime( $day_unformatted ) ), array( 'charitable-completed', 'charitable-refunded' ) ); | |
| 1450 | - $donation_today_refunded = $campaign_donations_table->get_donations_summary_by_period( gmdate( 'Y-m-d', strtotime( $day_unformatted ) ), $refunded_statuses ); | |
| 1467 | + $donation_day = gmdate( 'Y-m-d', strtotime( $day_unformatted ) ); | |
| 1451 | 1468 | |
| 1452 | - $donation_day = gmdate( 'Y-m-d', strtotime( $day_unformatted ) ); | |
| 1453 | 1469 | $donations_by_day[ $donation_day ]['label'] = gmdate( 'F d, Y', strtotime( $day_unformatted ) ); |
| 1454 | - $donations_by_day[ $donation_day ]['amount'] = $donation_today_completed->amount; | |
| 1470 | + $donations_by_day[ $donation_day ]['amount'] = isset( $completed_by_day[ $donation_day ] ) ? $completed_by_day[ $donation_day ]->amount : 0; | |
| 1455 | 1471 | $donations_by_day[ $donation_day ]['donors'] = 0; |
| 1456 | - $donations_by_day[ $donation_day ]['refunds'] = $donation_today_refunded->amount; | |
| 1472 | + $donations_by_day[ $donation_day ]['refunds'] = isset( $refunded_by_day[ $donation_day ] ) ? $refunded_by_day[ $donation_day ]->amount : 0; | |
| 1457 | 1473 | $donations_by_day[ $donation_day ]['net'] = 0; |
| 1458 | 1474 | } |
| 1459 | 1475 | |
| 1460 | 1476 | // Go through the array and group the amounts and number of donors by day. |
| @@ -2831,50 +2847,58 @@ | ||
| 2831 | 2847 | |
| 2832 | 2848 | $refunds_by_day = array(); |
| 2833 | 2849 | $refunded_donations_total_amount = 0; |
| 2834 | 2850 | $refunded_donations_total_count = 0; |
| 2835 | - $processed_donation_ids = array(); // Track unique donation IDs to avoid double-counting | |
| 2836 | 2851 | |
| 2837 | 2852 | $days = $this->get_days_between_dates( $this->start_date, $this->end_date ); |
| 2838 | 2853 | |
| 2839 | - // First, calculate totals from all refunded donations (regardless of day matching) | |
| 2840 | - // This ensures count and amount are consistent | |
| 2854 | + /** | |
| 2855 | + * Bucket the refunds by day once, up front, then read from that. | |
| 2856 | + * | |
| 2857 | + * The day loop below used to iterate the day LABEL ('M d') rather than the | |
| 2858 | + * 'Y-m-d' key, so strtotime() stamped every bucket with the CURRENT year. The | |
| 2859 | + * Refunds column and Net in the breakdown table therefore read 0 for any range | |
| 2860 | + * outside the current year, and for a multi-year range refunds from different | |
| 2861 | + * years merged into a single month-day bucket. (The totals were already | |
| 2862 | + * computed up front, so the Refunds card itself was correct.) | |
| 2863 | + * | |
| 2864 | + * Buckets on post_date, matching the WHERE clause above and the donation series | |
| 2865 | + * in get_donations_by_day(). Bucketing on post_date_gmt while filtering on | |
| 2866 | + * post_date could put a late-evening refund in a day outside the range on a | |
| 2867 | + * non-UTC site. | |
| 2868 | + * | |
| 2869 | + * The GROUP BY p.ID in the query already guarantees one row per donation, so no | |
| 2870 | + * per-day dedup is needed. The previous "$donation_id > 0" guard also silently | |
| 2871 | + * dropped a refunded donation that has no campaign_donations row from the totals; | |
| 2872 | + * such a refund is now counted, with a zero amount, so it stays visible. | |
| 2873 | + */ | |
| 2874 | + $refunds_by_date = array(); | |
| 2875 | + | |
| 2841 | 2876 | if ( ! empty( $refunded_donations ) ) { |
| 2842 | 2877 | foreach ( $refunded_donations as $refunded_donation ) { |
| 2843 | - // Only count each unique donation ID once | |
| 2844 | - $donation_id = isset( $refunded_donation->donation_id ) ? intval( $refunded_donation->donation_id ) : 0; | |
| 2845 | - if ( $donation_id > 0 && ! in_array( $donation_id, $processed_donation_ids, true ) ) { | |
| 2846 | - $refunded_donations_total_amount += floatval( $refunded_donation->amount ); | |
| 2847 | - $refunded_donations_total_count++; | |
| 2848 | - $processed_donation_ids[] = $donation_id; | |
| 2878 | + $refund_day = gmdate( 'Y-m-d', strtotime( $refunded_donation->post_date ) ); | |
| 2879 | + | |
| 2880 | + if ( ! isset( $refunds_by_date[ $refund_day ] ) ) { | |
| 2881 | + $refunds_by_date[ $refund_day ] = array( | |
| 2882 | + 'amount' => 0, | |
| 2883 | + 'count' => 0, | |
| 2884 | + ); | |
| 2849 | 2885 | } |
| 2886 | + | |
| 2887 | + $refunds_by_date[ $refund_day ]['amount'] += floatval( $refunded_donation->amount ); | |
| 2888 | + ++$refunds_by_date[ $refund_day ]['count']; | |
| 2889 | + | |
| 2890 | + $refunded_donations_total_amount += floatval( $refunded_donation->amount ); | |
| 2891 | + ++$refunded_donations_total_count; | |
| 2850 | 2892 | } |
| 2851 | 2893 | } |
| 2852 | 2894 | |
| 2853 | - // Then, organize by day for the breakdown | |
| 2854 | - foreach ( $days as $day ) { | |
| 2855 | - $donation_day = gmdate( 'Y-m-d', strtotime( $day ) ); | |
| 2856 | - $refunded_donation_amount = 0; | |
| 2857 | - $refunded_donation_count = 0; | |
| 2858 | - $processed_day_donation_ids = array(); // Track unique donation IDs per day | |
| 2859 | - if ( ! empty( $refunded_donations ) ) : | |
| 2860 | - foreach ( $refunded_donations as $refunded_donation ) { | |
| 2861 | - // if the refunded donation post date is the same day as the donation_day, add it to the refunded total. | |
| 2862 | - $refund_date = gmdate( 'Y-m-d', strtotime( $refunded_donation->post_date_gmt ) ); | |
| 2863 | - if ( $refund_date === $donation_day ) { | |
| 2864 | - $donation_id = isset( $refunded_donation->donation_id ) ? intval( $refunded_donation->donation_id ) : 0; | |
| 2865 | - // Only count each unique donation ID once per day | |
| 2866 | - if ( $donation_id > 0 && ! in_array( $donation_id, $processed_day_donation_ids, true ) ) { | |
| 2867 | - $refunded_donation_amount += floatval( $refunded_donation->amount ); | |
| 2868 | - ++$refunded_donation_count; | |
| 2869 | - $processed_day_donation_ids[] = $donation_id; | |
| 2870 | - } | |
| 2871 | - } | |
| 2872 | - } | |
| 2873 | - endif; | |
| 2874 | - $refunds_by_day[ $donation_day ]['label'] = gmdate( 'F d, Y', strtotime( $day ) ); | |
| 2875 | - $refunds_by_day[ $donation_day ]['amount'] = $refunded_donation_amount; | |
| 2876 | - $refunds_by_day[ $donation_day ]['donors'] = $refunded_donation_count; | |
| 2895 | + foreach ( $days as $day_unformatted => $day_label ) { | |
| 2896 | + $donation_day = gmdate( 'Y-m-d', strtotime( $day_unformatted ) ); | |
| 2897 | + | |
| 2898 | + $refunds_by_day[ $donation_day ]['label'] = gmdate( 'F d, Y', strtotime( $day_unformatted ) ); | |
| 2899 | + $refunds_by_day[ $donation_day ]['amount'] = isset( $refunds_by_date[ $donation_day ] ) ? $refunds_by_date[ $donation_day ]['amount'] : 0; | |
| 2900 | + $refunds_by_day[ $donation_day ]['donors'] = isset( $refunds_by_date[ $donation_day ] ) ? $refunds_by_date[ $donation_day ]['count'] : 0; | |
| 2877 | 2901 | } |
| 2878 | 2902 | |
| 2879 | 2903 | return array( |
| 2880 | 2904 | 'donations' => $refunded_donations, |
| @@ -3204,14 +3228,14 @@ | ||
| 3204 | 3228 | <p><?php echo esc_html__( 'Donors', 'charitable' ); ?></p> |
| 3205 | 3229 | <p><?php echo esc_html( $to_date_range ); ?></p> |
| 3206 | 3230 | </div> |
| 3207 | 3231 | <div class="charitable-container charitable-report-ui charitable-card"> |
| 3208 | - <strong><span id="charitable-top-donor-count"><?php echo charitable_format_money( $total_amount_donations_from, 2, true ); // phpcs:ignore ?></span></strong> | |
| 3232 | + <strong><span id="charitable-top-donor-count"><?php echo charitable_format_money( $total_amount_donations_from, false, true ); // phpcs:ignore ?></span></strong> | |
| 3209 | 3233 | <p><?php echo esc_html__( 'Donations', 'charitable' ); ?></p> |
| 3210 | 3234 | <p><?php echo esc_html( $from_date_range ); ?></p> |
| 3211 | 3235 | </div> |
| 3212 | 3236 | <div class="charitable-container charitable-report-ui charitable-card"> |
| 3213 | - <strong><span id="charitable-top-donor-count"><?php echo charitable_format_money( $total_amount_donations_to, 2, true ); // phpcs:ignore ?></span></strong> | |
| 3237 | + <strong><span id="charitable-top-donor-count"><?php echo charitable_format_money( $total_amount_donations_to, false, true ); // phpcs:ignore ?></span></strong> | |
| 3214 | 3238 | <p><?php echo esc_html__( 'Donations', 'charitable' ); ?></p> |
| 3215 | 3239 | <p><?php echo esc_html( $to_date_range ); ?></p> |
| 3216 | 3240 | </div> |
| 3217 | 3241 | </div> |
| @@ -3447,9 +3471,9 @@ | ||
| 3447 | 3471 | foreach ( $donors_data as $donor_id => $data ) : |
| 3448 | 3472 | |
| 3449 | 3473 | $donor_name = $data->donor_first_name . ' ' . $data->donor_last_name; |
| 3450 | 3474 | $donor_email = $data->donor_email; |
| 3451 | - $donor_avatar = ( is_email( $donor_email ) ) ? get_avatar_url( $donor_email ) : charitable()->get_path( 'assets', false ) . '/images/misc/placeholder-avatar-small.jpg'; | |
| 3475 | + $donor_avatar = ( is_email( $donor_email ) ) ? get_avatar_url( $donor_email ) : charitable()->get_path( 'assets', false ) . 'images/misc/placeholder-avatar-small.jpg'; | |
| 3452 | 3476 | $total_donation_count_compare_to = $data->total_donation_count_compare_to; |
| 3453 | 3477 | $total_donation_amount_compare_to = number_format( $data->total_donation_amount_compare_to, 2 ); |
| 3454 | 3478 | $total_donation_count_compare_from = $data->total_donation_count_compare_from; |
| 3455 | 3479 | $total_donation_amount_compare_from = number_format( $data->total_donation_amount_compare_from, 2 ); |
| @@ -3464,22 +3488,22 @@ | ||
| 3464 | 3488 | <tr id="type-lybunt-donor-<?php echo intval( $donor_id ); ?>" class="row-type-lybunt"> |
| 3465 | 3489 | <td class="charitable-avatar" style="width: 32px;"><img src="<?php echo esc_url( $donor_avatar ); ?>" class="avatar avatar-<?php echo esc_html( $donor_id ); ?> photo" width="32" height="32" alt="<?php esc_html__( 'Profile picture of', 'charitable' ); ?> <?php echo esc_html( $donor_name ); ?>"></td> |
| 3466 | 3490 | <td class="donated column-date" data-colname="<?php echo esc_html__( 'Date.', 'charitable' ); ?>"><?php echo esc_html( $donor_name ); ?><br/><?php echo esc_html( $donor_email ); ?></td> |
| 3467 | 3491 | <td class="donated column-donations" data-colname="Donations"><?php echo charitable_format_money( $last_donation_amount ); // phpcs:ignore ?></td> |
| 3468 | - <td class="donated column-donors" data-colname="Number of Donors"><?php echo esc_html( $total_donation_count_compare_from ); ?> <?php echo esc_html__( 'Donations', 'charitable' ); ?><br/><small><?php echo charitable_format_money( $total_donation_amount_compare_from, 2, true ); // phpcs:ignore ?></small></td> | |
| 3469 | - <td class="donated column-donors" data-colname="Number of Donors"><?php echo esc_html( $total_donation_count_compare_to ); ?> <?php echo esc_html__( 'Donations', 'charitable' ); ?><br/><small><?php echo charitable_format_money( $total_donation_amount_compare_to, 2, true ); // phpcs:ignore ?></small></td> | |
| 3492 | + <td class="donated column-donors" data-colname="Number of Donors"><?php echo esc_html( $total_donation_count_compare_from ); ?> <?php echo esc_html__( 'Donations', 'charitable' ); ?><br/><small><?php echo charitable_format_money( $total_donation_amount_compare_from, false, true ); // phpcs:ignore ?></small></td> | |
| 3493 | + <td class="donated column-donors" data-colname="Number of Donors"><?php echo esc_html( $total_donation_count_compare_to ); ?> <?php echo esc_html__( 'Donations', 'charitable' ); ?><br/><small><?php echo charitable_format_money( $total_donation_amount_compare_to, false, true ); // phpcs:ignore ?></small></td> | |
| 3470 | 3494 | <td class="donated column-refunds" data-colname="Refunds"><?php echo $campaign_links; // phpcs:ignore ?></td> |
| 3471 | - <td class="donated column-net " data-colname="Net"><?php echo charitable_format_money( $total_life_amount, 2, true ); // phpcs:ignore ?></td> | |
| 3495 | + <td class="donated column-net " data-colname="Net"><?php echo charitable_format_money( $total_life_amount, false, true ); // phpcs:ignore ?></td> | |
| 3472 | 3496 | <?php |
| 3473 | 3497 | |
| 3474 | 3498 | if ( $show_actions ) : |
| 3475 | 3499 | if ( $link && charitable_is_pro() ) : |
| 3476 | 3500 | |
| 3477 | - $actions_display = '<td class="donated column-actions " data-colname="Actions"><a class="charitable-campaign-action-button" title="' . esc_html__( 'View User', 'charitable' ) . '" href="' . esc_url( $link ) . '" target="_blank"><img src="' . charitable()->get_path( 'assets', false ) . '/images/icons/eye.svg" width="14" height="14" alt="' . esc_html__( 'View User', 'charitable' ) . '" /></td>'; | |
| 3501 | + $actions_display = '<td class="donated column-actions " data-colname="Actions"><a class="charitable-campaign-action-button" title="' . esc_html__( 'View User', 'charitable' ) . '" href="' . esc_url( $link ) . '" target="_blank"><img src="' . charitable()->get_path( 'assets', false ) . 'images/icons/eye.svg" width="14" height="14" alt="' . esc_html__( 'View User', 'charitable' ) . '" /></td>'; | |
| 3478 | 3502 | |
| 3479 | 3503 | else : |
| 3480 | 3504 | |
| 3481 | - $actions_display = '<td class="donated column-actions " data-colname="Actions"><a class="charitable-campaign-action-button" title="' . esc_html__( 'View User', 'charitable' ) . '" href="#" target="_blank"><img src="' . charitable()->get_path( 'assets', false ) . '/images/icons/eye.svg" width="14" height="14" alt="' . esc_html__( 'View User', 'charitable' ) . '" /></td>'; | |
| 3505 | + $actions_display = '<td class="donated column-actions " data-colname="Actions"><a class="charitable-campaign-action-button" title="' . esc_html__( 'View User', 'charitable' ) . '" href="#" target="_blank"><img src="' . charitable()->get_path( 'assets', false ) . 'images/icons/eye.svg" width="14" height="14" alt="' . esc_html__( 'View User', 'charitable' ) . '" /></td>'; | |
| 3482 | 3506 | |
| 3483 | 3507 | endif; |
| 3484 | 3508 | endif; |
| 3485 | 3509 | |
| @@ -3605,8 +3629,69 @@ | ||
| 3605 | 3629 | return false; |
| 3606 | 3630 | } |
| 3607 | 3631 | |
| 3608 | 3632 | /** |
| 3633 | + * Get the date of the earliest donation on the site. | |
| 3634 | + * | |
| 3635 | + * Used to build the "All Time" preset in the reporting date picker, so that | |
| 3636 | + * lifetime figures are one click away rather than a hand-typed range. | |
| 3637 | + * | |
| 3638 | + * Every valid donation status is considered, not just completed ones, because | |
| 3639 | + * the reports can be filtered by status and a narrower floor would hide the | |
| 3640 | + * earliest refunded or cancelled donation. | |
| 3641 | + * | |
| 3642 | + * @since 1.8.12.4 | |
| 3643 | + * | |
| 3644 | + * @return string Date in Y/m/d format. Falls back to one month ago when the | |
| 3645 | + * site has no donations yet. | |
| 3646 | + */ | |
| 3647 | + public function get_earliest_donation_date() { | |
| 3648 | + | |
| 3649 | + $cached = get_transient( 'charitable-reports-earliest-donation-date' ); | |
| 3650 | + | |
| 3651 | + if ( false !== $cached ) { | |
| 3652 | + return $cached; | |
| 3653 | + } | |
| 3654 | + | |
| 3655 | + global $wpdb; | |
| 3656 | + | |
| 3657 | + $statuses = array_keys( charitable_get_valid_donation_statuses() ); | |
| 3658 | + | |
| 3659 | + // `charitable_donation_statuses` is a public filter, and an empty list would | |
| 3660 | + // build `IN ( )`. Nothing to measure in that case, so use the fallback. | |
| 3661 | + if ( empty( $statuses ) ) { | |
| 3662 | + return gmdate( 'Y/m/d', strtotime( '-1 month' ) ); | |
| 3663 | + } | |
| 3664 | + | |
| 3665 | + $placeholders = implode( ', ', array_fill( 0, count( $statuses ), '%s' ) ); | |
| 3666 | + | |
| 3667 | + $earliest = $wpdb->get_var( // phpcs:ignore | |
| 3668 | + $wpdb->prepare( | |
| 3669 | + "SELECT MIN( post_date ) FROM {$wpdb->posts} | |
| 3670 | + WHERE post_type = 'donation' | |
| 3671 | + AND post_status IN ( {$placeholders} )", // phpcs:ignore | |
| 3672 | + $statuses | |
| 3673 | + ) | |
| 3674 | + ); | |
| 3675 | + | |
| 3676 | + if ( empty( $earliest ) || '0000-00-00 00:00:00' === $earliest ) { | |
| 3677 | + /* | |
| 3678 | + * Deliberately not cached. A site with no donations today may be one | |
| 3679 | + * import away from four years of history, and a cached fallback would | |
| 3680 | + * leave "All Time" starting a month ago for a full day afterwards - | |
| 3681 | + * which is the exact wrong-figure trap this preset exists to close. | |
| 3682 | + */ | |
| 3683 | + return gmdate( 'Y/m/d', strtotime( '-1 month' ) ); | |
| 3684 | + } | |
| 3685 | + | |
| 3686 | + $date = gmdate( 'Y/m/d', strtotime( $earliest ) ); | |
| 3687 | + | |
| 3688 | + set_transient( 'charitable-reports-earliest-donation-date', $date, DAY_IN_SECONDS ); | |
| 3689 | + | |
| 3690 | + return $date; | |
| 3691 | + } | |
| 3692 | + | |
| 3693 | + /** | |
| 3609 | 3694 | * Get localized strings. |
| 3610 | 3695 | * |
| 3611 | 3696 | * @since 1.8.1 |
| 3612 | 3697 | * |
| @@ -3665,9 +3750,18 @@ | ||
| 3665 | 3750 | 'payment_keys' => (array) array_reverse( $this->payment_keys ), |
| 3666 | 3751 | ), |
| 3667 | 3752 | 'default_start_date' => current_time( gmdate( 'Y-m-d', strtotime( '-1 month' ) ) ), |
| 3668 | 3753 | 'default_end_date' => current_time( gmdate( 'Y-m-d' ) ), |
| 3754 | + 'earliest_donation_date' => $this->get_earliest_donation_date(), | |
| 3755 | + // The date picker uses each preset's label as its object key, so the | |
| 3756 | + // label has to travel to JS rather than being written there. It rides | |
| 3757 | + // this same array as `earliest_donation_date` on purpose: the | |
| 3758 | + // reporting JS contains no wp.i18n calls, so a second localization | |
| 3759 | + // mechanism for one string would be the inconsistent choice. | |
| 3760 | + 'all_time_label' => __( 'All Time', 'charitable' ), | |
| 3669 | 3761 | 'advanced_reports' => $charitable_reports_advanced_report_strings, |
| 3762 | + 'i18n_report_timeout' => __( 'This report took too long to load and was stopped by the server. The figures have been cleared so they are not mistaken for the range you asked for. Try a shorter date range.', 'charitable' ), | |
| 3763 | + 'i18n_report_error' => __( 'This report could not be loaded. The figures have been cleared so they are not mistaken for the range you asked for. Please try again.', 'charitable' ), | |
| 3670 | 3764 | ); |
| 3671 | 3765 | |
| 3672 | 3766 | $strings = apply_filters( 'charitable_reporting_strings', $strings ); |
| 3673 | 3767 | |