Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/paid-memberships-pro/adminpages/memberslist-csv.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 +
3 + if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_memberslistcsv")))
4 + {
5 + die(esc_html__("You do not have permissions to perform this action.", 'paid-memberships-pro' ));
6 + }
7 +
8 + if (!defined('PMPRO_BENCHMARK'))
9 + define('PMPRO_BENCHMARK', false);
10 +
11 + if (PMPRO_BENCHMARK)
12 + {
13 + error_log(str_repeat('-', 10) . date_i18n('Y-m-d H:i:s') . str_repeat('-', 10));
14 + $start_time = microtime(true);
15 + $start_memory = memory_get_usage(true);
16 + }
17 +
18 +
19 + /**
20 + * Filter to set max number of records to process at a time
21 + * for the export (helps manage memory footprint)
22 + *
23 + * Rule of thumb: 2000 records: ~50-60 MB of addl. memory (memory_limit needs to be between 128MB and 256MB)
24 + * 4000 records: ~70-100 MB of addl. memory (memory_limit needs to be >= 256MB)
25 + * 6000 records: ~100-140 MB of addl. memory (memory_limit needs to be >= 256MB)
26 + *
27 + * NOTE: Use the pmpro_before_members_list_csv_export hook to increase memory "on-the-fly"
28 + * Can reset with the pmpro_after_members_list_csv_export hook
29 + *
30 + * @since 1.8.7
31 + */
32 + //set the number of users we'll load to try and protect ourselves from OOM errors
33 + $max_users_per_loop = intval( apply_filters( 'pmpro_set_max_user_per_export_loop', 2000 ) );
34 +
35 + //If the filter returns odd value, reset to default.
36 + if ( $max_users_per_loop < 1 ) {
37 + $max_users_per_loop = 2000;
38 + }
39 + global $wpdb;
40 +
41 + //get users (search input field)
42 + $search_key = false;
43 + if( isset( $_REQUEST['s'] ) ) {
44 + $s = trim( sanitize_text_field( $_REQUEST['s'] ) );
45 + } else {
46 + $s = '';
47 + }
48 +
49 + // If there's a colon in the search, let's split it out.
50 + if( ! empty( $s ) && strpos( $s, ':' ) !== false ) {
51 + $parts = explode( ':', $s );
52 + $search_key = array_shift( $parts );
53 + $s = implode( ':', $parts );
54 + }
55 +
56 + // Treat * as wild cards.
57 + $s = str_replace( '*', '%', $s );
58 +
59 + // requested a level id
60 + if(isset($_REQUEST['l']))
61 + $l = sanitize_text_field($_REQUEST['l']);
62 + else
63 + $l = false;
64 +
65 + //some vars for the search
66 + if(!empty($_REQUEST['pn']))
67 + $pn = intval($_REQUEST['pn']);
68 + else
69 + $pn = 1;
70 +
71 + if(!empty($_REQUEST['limit']))
72 + $limit = intval($_REQUEST['limit']);
73 + else
74 + $limit = false;
75 +
76 + if($limit)
77 + {
78 + $end = $pn * $limit;
79 + $start = $end - $limit;
80 + }
81 + else
82 + {
83 + $end = NULL;
84 + $start = NULL;
85 + }
86 +
87 + $headers = array();
88 + $headers[] = "Content-Type: text/csv";
89 + $headers[] = "Cache-Control: max-age=0, no-cache, no-store";
90 + $headers[] = "Pragma: no-cache";
91 + $headers[] = "Connection: close";
92 +
93 + if ( $s && $l == "oldmembers" ) {
94 + $headers[] = 'Content-Disposition: attachment; filename="members_list_oldmembers_' . sanitize_file_name($s) . '.csv"';
95 + } elseif($s && $l) {
96 + $headers[] = 'Content-Disposition: attachment; filename="members_list_' . intval($l) . '_level_' . sanitize_file_name($s) . '.csv"';
97 + } elseif($s) {
98 + $headers[] = 'Content-Disposition: attachment; filename="members_list_' . sanitize_file_name($s) . '.csv"';
99 + } elseif($l == "oldmembers") {
100 + $headers[] = 'Content-Disposition: attachment; filename="members_list_oldmembers.csv"';
101 + } elseif($l == 'expired' ) {
102 + $headers[] = 'Content-Disposition: attachment; filename="members_list_expired.csv"';
103 + } else {
104 + $headers[] = 'Content-Disposition: attachment; filename="members_list.csv"';
105 + }
106 +
107 + //set default CSV file headers, using comma as delimiter
108 + $csv_file_header = "id,username,firstname,lastname,email,membership,discount_code_id,discount_code,subscription_transaction_id,billing_amount,cycle_number,cycle_period,next_payment_date,joined,startdate";
109 +
110 + if($l == "oldmembers")
111 + $csv_file_header .= ",ended";
112 + else
113 + $csv_file_header .= ",expires";
114 +
115 + //these are the meta_keys for the fields (arrays are object, property. so e.g. $theuser->ID)
116 + $default_columns = array(
117 + array("theuser", "ID"),
118 + array("theuser", "user_login"),
119 + array("metavalues", "first_name"),
120 + array("metavalues", "last_name"),
121 + array("theuser", "user_email"),
122 + array("theuser", "membership"),
123 + array("discount_code", "id"),
124 + array("discount_code", "code")
125 + // Subscription information, joindate, and enddate are handled specifically below
126 + );
127 +
128 + //filter
129 + $default_columns = apply_filters("pmpro_members_list_csv_default_columns", $default_columns);
130 +
131 + //set the preferred date format:
132 + $dateformat = apply_filters("pmpro_memberslist_csv_dateformat","Y-m-d");
133 +
134 + //any extra columns
135 + $extra_columns = apply_filters("pmpro_members_list_csv_extra_columns", array());
136 + if(!empty($extra_columns))
137 + {
138 + foreach($extra_columns as $heading => $callback)
139 + {
140 + $csv_file_header .= "," . $heading;
141 + }
142 + }
143 +
144 + $csv_file_header = apply_filters("pmpro_members_list_csv_heading", $csv_file_header);
145 + $csv_file_header .= "\n";
146 +
147 + //generate SQL for list of users to process
148 + $sqlQuery = "
149 + SELECT
150 + DISTINCT u.ID
151 + FROM $wpdb->users u ";
152 +
153 + if ( $s ) {
154 + if ( ! empty( $search_key ) ) {
155 + // If there's a colon in the search string, make the search smarter.
156 + if( in_array( $search_key, array( 'login', 'nicename', 'email', 'url', 'display_name' ), true ) ) {
157 + $key_column = 'u.user_' . $search_key; // All options for $search_key are safe for use in a query.
158 + $search = " AND $key_column LIKE '%" . esc_sql( $s ) . "%' ";
159 + } elseif ( $search_key === 'discount' || $search_key === 'discount_code' || $search_key === 'dc' ) {
160 + $user_ids = $wpdb->get_col( "SELECT dcu.user_id FROM $wpdb->pmpro_discount_codes_uses dcu LEFT JOIN $wpdb->pmpro_discount_codes dc ON dcu.code_id = dc.id WHERE dc.code = '" . esc_sql( $s ) . "'" );
161 + if ( empty( $user_ids ) ) {
162 + $user_ids = array(0); // Avoid warning, but ensure 0 results.
163 + }
164 + $search = " AND u.ID IN(" . implode( ",", $user_ids ) . ") ";
165 + } else {
166 + $user_ids = $wpdb->get_col( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '" . esc_sql( $search_key ) . "' AND meta_value lIKE '%" . esc_sql( $s ) . "%'" );
167 + if ( empty( $user_ids ) ) {
168 + $user_ids = array(0); // Avoid warning, but ensure 0 results.
169 + }
170 + $search = " AND u.ID IN(" . implode( ",", $user_ids ) . ") ";
171 + }
172 + } elseif( function_exists( 'wp_is_large_user_count' ) && wp_is_large_user_count() ) {
173 + // Don't check user meta at all on big sites.
174 + $search_query = " AND ( u.user_login LIKE '%" . esc_sql($s) . "%' OR u.user_email LIKE '%" . esc_sql($s) . "%' OR u.display_name LIKE '%" . esc_sql($s) . "%' ) ";
175 + } else {
176 + // Default search checks a few fields.
177 + $sqlQuery .= "LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id ";
178 + $search = " AND ( u.user_login LIKE '%" . esc_sql($s) . "%' OR u.user_email LIKE '%" . esc_sql($s) . "%' OR um.meta_value LIKE '%" . esc_sql($s) . "%' OR u.display_name LIKE '%" . esc_sql($s) . "%' ) ";
179 + }
180 + } else {
181 + $search = '';
182 + }
183 +
184 + $sqlQuery .= "LEFT JOIN {$wpdb->pmpro_memberships_users} mu ON u.ID = mu.user_id ";
185 + $sqlQuery .= "LEFT JOIN {$wpdb->pmpro_membership_levels} m ON mu.membership_id = m.id ";
186 +
187 + $sqlQuery .= "WHERE mu.membership_id > 0 ";
188 +
189 + // looking for a specific user
190 + if ( ! empty( $s ) ) {
191 + $sqlQuery .= $search;
192 + }
193 +
194 + $filter = null;
195 +
196 + //records where the user is NOT an active member
197 + //if $l == "oldmembers"
198 + $filter = ($l == "oldmembers" ? " AND mu.status <> 'active' " : $filter);
199 +
200 + // prepare the status to use in the filter
201 + // elseif ($l == "expired") elseif ($l == "cancelled")
202 + $f_status = ($l == "expired" ? array( 'expired' ) : ( $l == "cancelled" ? array('cancelled', 'admin_cancelled') : null));
203 +
204 + //records where the user is expired or cancelled
205 + $filter = ( ($l == "expired" || $l == "cancelled") && is_null($filter)) ? "AND mu.status IN ('" . implode("','", $f_status) . "') " : $filter;
206 +
207 + if ( in_array($l, array( "oldmembers", "expired", "cancelled") ) ) {
208 + $filter .= " AND NOT EXISTS (
209 + SELECT 1
210 + FROM {$wpdb->pmpro_memberships_users} mu2
211 + WHERE mu2.user_id = u.ID
212 + AND mu2.status = 'active'
213 + ) ";
214 + }
215 +
216 + //records for active users with the requested membership level
217 + // elseif($l)
218 + $filter = ( (is_null($filter) && is_numeric($l)) ? " AND mu.status = 'active' AND mu.membership_id = " . (int) $l . " " : $filter);
219 +
220 + //any active users
221 + // else
222 + $filter = (is_null($filter) ? " AND mu.status = 'active' " : $filter);
223 +
224 + //add the filter
225 + $sqlQuery .= $filter;
226 +
227 + //process based on limit value(s).
228 + $sqlQuery .= "ORDER BY u.ID ";
229 +
230 + if(!empty($limit))
231 + $sqlQuery .= "LIMIT {$start}, {$limit}";
232 +
233 + /**
234 + * Filter to change/manipulate the SQL for the list of members export
235 + * @since v1.9.0 Re-introduced
236 + */
237 + $sqlQuery = apply_filters('pmpro_members_list_sql', $sqlQuery);
238 +
239 + // Generate a temporary file to store the data in.
240 + $tmp_dir = sys_get_temp_dir();
241 + $filename = tempnam( $tmp_dir, 'pmpro_ml_');
242 +
243 + // open in append mode
244 + $csv_fh = fopen($filename, 'a');
245 +
246 + //write the CSV header to the file
247 + fprintf($csv_fh, '%s', $csv_file_header );
248 +
249 + //get users
250 + $theusers = $wpdb->get_col($sqlQuery);
251 +
252 + //if no records just transmit file with only CSV header as content
253 + if (empty($theusers) && empty($_REQUEST['pmpro_no_download'])) {
254 +
255 + // send the data to the remote browser
256 + pmpro_transmit_content($csv_fh, $filename, $headers);
257 + }
258 +
259 + $users_found = count($theusers);
260 +
261 + if (PMPRO_BENCHMARK)
262 + {
263 + $pre_action_time = microtime(true);
264 + $pre_action_memory = memory_get_usage(true);
265 + }
266 +
267 + do_action('pmpro_before_members_list_csv_export', $theusers);
268 +
269 + $i_start = 0;
270 + $i_limit = 0;
271 + $iterations = 1;
272 +
273 + $csvoutput = array();
274 +
275 + if($users_found >= $max_users_per_loop)
276 + {
277 + $iterations = ceil($users_found / $max_users_per_loop);
278 + $i_limit = $max_users_per_loop;
279 + }
280 +
281 + $end = 0;
282 + $time_limit = ini_get('max_execution_time');
283 +
284 + if (PMPRO_BENCHMARK)
285 + {
286 + error_log("PMPRO_BENCHMARK - Total records to process: {$users_found}");
287 + error_log("PMPRO_BENCHMARK - Will process {$iterations} iterations of max {$max_users_per_loop} records per iteration.");
288 + $pre_iteration_time = microtime(true);
289 + $pre_iteration_memory = memory_get_usage(true);
290 + }
291 +
292 + //to manage memory footprint, we'll iterate through the membership list multiple times
293 + for ( $ic = 1 ; $ic <= $iterations ; $ic++ ) {
294 +
295 + if (PMPRO_BENCHMARK)
296 + {
297 + $start_iteration_time = microtime(true);
298 + $start_iteration_memory = memory_get_usage(true);
299 + }
300 +
301 + //make sure we don't timeout
302 + if ($end != 0) {
303 +
304 + $iteration_diff = $end - $start;
305 + $new_time_limit = ceil($iteration_diff*$iterations * 1.2);
306 +
307 + if ($time_limit < $new_time_limit )
308 + {
309 + $time_limit = $new_time_limit;
310 + set_time_limit( $time_limit );
311 + }
312 + }
313 +
314 + $start = current_time('timestamp');
315 +
316 + $i_end = min( $i_start + $max_users_per_loop - 1, $users_found - 1 );
317 +
318 + $spl = array_slice( $theusers, $i_start, $i_end - $i_start + 1 );
319 + //increment starting position
320 + $i_start = $i_end + 1;
321 +
322 + //escape the % for LIKE comparison with $wpdb
323 + if(!empty($search))
324 + $search = str_replace('%', '%%', $search);
325 +
326 + $userSql = "
327 + SELECT
328 + DISTINCT u.ID,
329 + u.user_login,
330 + u.user_email,
331 + UNIX_TIMESTAMP(CONVERT_TZ(u.user_registered, '+00:00', @@global.time_zone)) as joindate,
332 + u.user_login,
333 + u.user_nicename,
334 + u.user_url,
335 + u.user_registered,
336 + u.user_status,
337 + u.display_name,
338 + mu.membership_id,
339 + UNIX_TIMESTAMP(CONVERT_TZ(min(mu.startdate), '+00:00', @@global.time_zone)) as startdate,
340 + UNIX_TIMESTAMP(CONVERT_TZ(max(mu.enddate), '+00:00', @@global.time_zone)) as enddate,
341 + m.name as membership
342 + FROM {$wpdb->users} u
343 + LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id
344 + LEFT JOIN {$wpdb->pmpro_memberships_users} mu ON u.ID = mu.user_id
345 + LEFT JOIN {$wpdb->pmpro_membership_levels} m ON mu.membership_id = m.id
346 + WHERE u.ID in ( " . implode(', ', array_fill(0, count( $spl ), '%d' ) ) . " ) AND mu.membership_id > 0 {$filter} {$search}
347 + GROUP BY u.ID, mu.membership_id
348 + ORDER BY u.ID
349 + ";
350 + $userSql = call_user_func( array( $wpdb, 'prepare' ), $userSql, $spl );
351 + $usr_data = $wpdb->get_results($userSql); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
352 + $userSql = null;
353 +
354 + if (PMPRO_BENCHMARK)
355 + {
356 + $pre_userdata_time = microtime(true);
357 + $pre_userdata_memory = memory_get_usage(true);
358 + }
359 +
360 + // process the actual data we want to export
361 + foreach($usr_data as $theuser) {
362 +
363 + $csvoutput = array();
364 +
365 + //process usermeta
366 + $metavalues = new stdClass();
367 +
368 + // Returns array of meta keys containing array(s) of metavalues.
369 + $um_values = get_user_meta($theuser->ID);
370 +
371 + foreach( $um_values as $key => $value ) {
372 +
373 + $metavalues->{$key} = isset( $value[0] ) ? $value[0] : null;
374 + }
375 +
376 + $theuser->metavalues = $metavalues;
377 +
378 + $um_values = null;
379 +
380 + //grab discount code info
381 + $disSql = $wpdb->prepare("
382 + SELECT
383 + c.id,
384 + c.code
385 + FROM {$wpdb->pmpro_discount_codes_uses} cu
386 + LEFT JOIN $wpdb->pmpro_discount_codes c ON cu.code_id = c.id
387 + WHERE cu.user_id = %d
388 + ORDER BY c.id DESC
389 + LIMIT 1",
390 + $theuser->ID
391 + );
392 +
393 + $discount_code = $wpdb->get_row($disSql);
394 +
395 + //make sure there's data for the discount code info
396 + if (empty($discount_code))
397 + {
398 + $empty_dc = new stdClass();
399 + $empty_dc->id = '';
400 + $empty_dc->code = '';
401 + $discount_code = $empty_dc;
402 + }
403 +
404 + unset($disSql);
405 +
406 + //default columns
407 + if(!empty($default_columns))
408 + {
409 + $count = 0;
410 + foreach($default_columns as $col)
411 + {
412 + //checking $object->property. note the double $$
413 + $val = isset(${$col[0]}->{$col[1]}) ? ${$col[0]}->{$col[1]} : null;
414 + array_push($csvoutput, pmpro_enclose($val)); //output the value
415 + }
416 + }
417 +
418 + // Subscription transaction ID, billing amount, cycle number, and cycle period.
419 + $subscriptions = PMPro_Subscription::get_subscriptions_for_user( $theuser->ID, $theuser->membership_id );
420 + array_push($csvoutput, pmpro_enclose( ( empty( $subscriptions ) ? '' : $subscriptions[0]->get_subscription_transaction_id() ) ) );
421 + array_push($csvoutput, pmpro_enclose( ( empty( $subscriptions ) ? '' : $subscriptions[0]->get_billing_amount() ) ) );
422 + array_push($csvoutput, pmpro_enclose( ( empty( $subscriptions ) ? '' : $subscriptions[0]->get_cycle_number() ) ) );
423 + array_push($csvoutput, pmpro_enclose( ( empty( $subscriptions ) ? '' : $subscriptions[0]->get_cycle_period() ) ) );
424 + array_push($csvoutput, pmpro_enclose( ( empty( $subscriptions ) ? '' : date_i18n($dateformat, $subscriptions[0]->get_next_payment_date() ) ) ) );
425 +
426 + //joindate, startdate, and enddate
427 + array_push($csvoutput, pmpro_enclose(date_i18n($dateformat, $theuser->joindate)));
428 +
429 + if ( $theuser->startdate ) {
430 + array_push( $csvoutput, pmpro_enclose( date_i18n( $dateformat, $theuser->startdate ) ) );
431 + } else {
432 + array_push( $csvoutput, pmpro_enclose( __( 'N/A', 'paid-memberships-pro' ) ) );
433 + }
434 + // We are no longer filtering the expiration date text for performance reasons.
435 + if ( $theuser->enddate ) {
436 + array_push( $csvoutput, pmpro_enclose( date_i18n( $dateformat, $theuser->enddate ) ) );
437 + } else {
438 + array_push( $csvoutput, pmpro_enclose( __( 'N/A', 'paid-memberships-pro' ) ) );
439 + }
440 +
441 + //any extra columns
442 + if(!empty($extra_columns))
443 + {
444 + foreach($extra_columns as $heading => $callback)
445 + {
446 + $val = call_user_func($callback, $theuser, $heading);
447 + $val = ( is_string( $val ) || ! empty($val) ) ? $val : null;
448 + array_push( $csvoutput, pmpro_enclose($val) );
449 + }
450 + }
451 +
452 + //free memory for user records
453 + $metavalues = null;
454 + $discount_code = null;
455 + $theuser = null;
456 +
457 + // $csvoutput .= "\n";
458 + $line = implode(',', $csvoutput) . "\n";
459 +
460 + fprintf($csv_fh, "%s", $line);
461 +
462 + //reset
463 + $line = null;
464 + $csvoutput = null;
465 + } // end of foreach usr_data
466 +
467 + if (PMPRO_BENCHMARK)
468 + {
469 + $end_of_iteration_time = microtime(true);
470 + $end_of_iteration_memory = memory_get_usage(true);
471 + }
472 +
473 + //keep memory consumption low(ish)
474 + wp_cache_flush();
475 +
476 + if (PMPRO_BENCHMARK)
477 + {
478 + $after_flush_time = microtime(true);
479 + $after_flush_memory = memory_get_usage(true);
480 +
481 + $time_in_iteration = $end_of_iteration_time - $start_iteration_time;
482 + $time_flushing = $after_flush_time - $end_of_iteration_time;
483 + $userdata_time = $end_of_iteration_time - $pre_userdata_time;
484 +
485 + list($iteration_sec, $iteration_usec) = explode('.', $time_in_iteration);
486 + list($udata_sec, $udata_usec) = explode('.', $userdata_time);
487 + list($flush_sec, $flush_usec) = explode('.', $time_flushing);
488 +
489 + $memory_used = $end_of_iteration_memory - $start_iteration_memory;
490 +
491 + error_log("PMPRO_BENCHMARK - For iteration #{$ic} of {$iterations} - Records processed: " . count($usr_data));
492 + error_log("PMPRO_BENCHMARK - \tTime processing whole iteration: " . date_i18n("H:i:s", $iteration_sec) . ".{$iteration_sec}");
493 + error_log("PMPRO_BENCHMARK - \tTime processing user data for iteration: " . date_i18n("H:i:s", $udata_sec) . ".{$udata_sec}");
494 + error_log("PMPRO_BENCHMARK - \tTime flushing cache: " . date_i18n("H:i:s", $flush_sec) . ".{$flush_usec}");
495 + error_log("PMPRO_BENCHMARK - \tAdditional memory used during iteration: ".number_format($memory_used, 2, '.', ',') . " bytes");
496 + }
497 +
498 + //need to increase max running time?
499 + $end = current_time('timestamp');
500 +
501 + } // end of foreach iteration
502 +
503 + if (PMPRO_BENCHMARK)
504 + {
505 + $after_data_time = microtime(true);
506 + $after_data_memory = memory_get_peak_usage(true);
507 +
508 + $time_processing_data = $after_data_time - $start_time;
509 + $memory_processing_data = $after_data_memory - $start_memory;
510 +
511 + list($sec, $usec) = explode('.', $time_processing_data);
512 +
513 + error_log("PMPRO_BENCHMARK - Time processing data: {$sec}.{$usec} seconds");
514 + error_log("PMPRO_BENCHMARK - Peak memory usage: " . number_format($memory_processing_data, false, '.', ',') . " bytes");
515 + }
516 +
517 + // free memory
518 + $usr_data = null;
519 +
520 + // send the data to the remote browser, if this was not run via the Toolkit API
521 + if ( empty( $_REQUEST['pmpro_no_download'] ) ) {
522 + pmpro_transmit_content($csv_fh, $filename, $headers);
523 + exit;
524 + }
525 +
526 +
527 + function pmpro_enclose($s) {
528 + $s = (string) $s;
529 + return "\"" . str_replace("\"", "\\\"", $s) . "\"";
530 + }
531 +
532 + // responsible for trasnmitting content of file to remote browser
533 + function pmpro_transmit_content( $csv_fh, $filename, $headers = array() ) {
534 +
535 + //close the temp file
536 + fclose($csv_fh);
537 +
538 + if (version_compare(phpversion(), '5.3.0', '>')) {
539 +
540 + //make sure we get the right file size
541 + clearstatcache( true, $filename );
542 + } else {
543 + // for any PHP version prior to v5.3.0
544 + clearstatcache();
545 + }
546 +
547 + //did we accidentally send errors/warnings to browser?
548 + if (headers_sent())
549 + {
550 + echo esc_html( str_repeat('-', 75) ) . "<br/>\n";
551 + echo 'Please open a support case and paste in the warnings/errors you see above this text to\n ';
552 + echo 'the <a href="http://paidmembershipspro.com/support/?utm_source=plugin&utm_medium=banner&utm_campaign=memberslist_csv" target="_blank">Paid Memberships Pro support forum</a><br/>\n';
553 + echo esc_html( str_repeat('-', 75) ) . "<br/>\n";
554 + echo wp_kses_post( file_get_contents($filename) );
555 + echo esc_html( str_repeat('-', 75) ) . "<br/>\n";
556 + }
557 +
558 + //transmission
559 + if (! empty($headers) )
560 + {
561 + //set the download size
562 + $headers[] = "Content-Length: " . filesize($filename);
563 +
564 + //set headers
565 + foreach($headers as $header)
566 + {
567 + header($header . "\r\n");
568 + }
569 +
570 + // disable compression for the duration of file download
571 + if(ini_get('zlib.output_compression')){
572 + ini_set('zlib.output_compression', 'Off');
573 + }
574 +
575 + if( function_exists( 'fpassthru' ) ) {
576 + // use fpassthru to output the csv
577 + $csv_fh = fopen( $filename, 'rb' );
578 + fpassthru( $csv_fh );
579 + fclose( $csv_fh );
580 + } else {
581 + // use readfile() if fpassthru() is disabled (like on Flywheel Hosted)
582 + readfile( $filename );
583 + }
584 +
585 + // remove the temp file
586 + unlink( $filename );
587 + }
588 +
589 + //allow user to clean up after themselves
590 + do_action('pmpro_after_members_list_csv_export');
591 + exit;
592 + }
593 +