id);
$sub = self::get_submission_for_lead($lead->id);
$values = $sub ? self::get_flat_values($sub->id, $fields) : array_fill(0, count($fields), '');
$row = array(
$lead->id,
$lead->full_name,
$lead->email,
$lead->file_name ?? '',
$sub ? ($sub->form_id ?? '') : '',
$score ? $score->total_score : 0,
class_exists( 'ACQUIS_Automation_Engine' ) ? implode(', ', ACQUIS_Automation_Engine::get_lead_tags($lead->id)) : '',
class_exists( 'ACQUIS_Automation_Engine' ) ? ACQUIS_Automation_Engine::get_lead_segment($lead->id) : '',
$lead->utm_source ?? '',
$lead->utm_medium ?? '',
$lead->utm_campaign ?? '',
$lead->source_url ?? '',
$lead->ip_address,
$lead->created_at,
);
foreach ($values as $v) $row[] = $v;
fputcsv($fp, $row);
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- pairs with the php://output stream opened above
fclose($fp);
exit;
}
/**
* Generate an XLSX and stream it.
* Uses PHP's ZipArchive to build a minimal valid XLSX without dependencies.
*/
public static function export_xlsx( $filters = array() ) {
$leads = ACQUIS_P3_DB::get_leads_for_export($filters);
$fields = self::get_all_custom_field_keys();
$cols = array(
'ID','Full Name','Email','File','Form','Lead Score',
'Tags','Segment','UTM Source','UTM Medium','UTM Campaign',
'Source URL','IP Address','Date Submitted',
);
foreach ($fields as $label) $cols[] = $label;
$rows = array($cols);
foreach ($leads as $lead) {
$score = ACQUIS_P3_DB::get_lead_score($lead->id);
$sub = self::get_submission_for_lead($lead->id);
$values = $sub ? self::get_flat_values($sub->id, $fields) : array_fill(0, count($fields), '');
$row = array(
$lead->id, $lead->full_name, $lead->email,
$lead->file_name ?? '', $sub ? $sub->form_id : '',
$score ? $score->total_score : 0,
class_exists( 'ACQUIS_Automation_Engine' ) ? implode(', ', ACQUIS_Automation_Engine::get_lead_tags($lead->id)) : '',
class_exists( 'ACQUIS_Automation_Engine' ) ? ACQUIS_Automation_Engine::get_lead_segment($lead->id) : '',
$lead->utm_source ?? '', $lead->utm_medium ?? '', $lead->utm_campaign ?? '',
$lead->source_url ?? '', $lead->ip_address, $lead->created_at,
);
foreach ($values as $v) $row[] = $v;
$rows[] = $row;
}
$xlsx_data = self::build_xlsx($rows);
$filename = 'acquisitionsuite-export-' . gmdate('Y-m-d') . '.xlsx';
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . strlen($xlsx_data));
header('Pragma: no-cache');
header('Expires: 0');
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Binary XLSX file download, not HTML output
echo $xlsx_data;
exit;
}
// ── Helpers ────────────────────────────────────────────────────────────
private static function get_all_custom_field_keys() {
global $wpdb;
$keys = $wpdb->get_results(
"SELECT DISTINCT sv.field_key, sv.field_label FROM " . ACQUIS_P3_DB::sub_values_table() . " sv
ORDER BY sv.field_key ASC"
);
$map = array();
foreach ($keys as $k) $map[$k->field_key] = $k->field_label ?: $k->field_key;
return $map;
}
private static function get_submission_for_lead($lead_id) {
global $wpdb;
return $wpdb->get_row($wpdb->prepare(
"SELECT * FROM %i WHERE lead_id=%d ORDER BY id DESC LIMIT 1",
ACQUIS_P3_DB::submissions_table(), absint($lead_id)
));
}
private static function get_flat_values($submission_id, $field_map) {
global $wpdb;
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT field_key, value FROM %i WHERE submission_id=%d",
ACQUIS_P3_DB::sub_values_table(), absint($submission_id)
));
$flat = array();
foreach ($rows as $r) $flat[$r->field_key] = $r->value;
$result = array();
foreach ($field_map as $key => $label) {
$result[] = $flat[$key] ?? '';
}
return $result;
}
/**
* Build minimal XLSX binary using ZipArchive.
* No external library needed pure PHP Open XML.
*/
private static function build_xlsx( array $rows ) {
// Build sheet XML
$sheet_xml = ''
. ''
. '';
$row_num = 1;
foreach ($rows as $row) {
$sheet_xml .= '';
$col_num = 0;
foreach ($row as $cell) {
$col_letter = self::col_letter($col_num++);
$cell_ref = $col_letter . $row_num;
$val = htmlspecialchars((string)$cell, ENT_XML1, 'UTF-8');
if (is_numeric($cell) && $cell !== '') {
$sheet_xml .= '' . $val . '';
} else {
$sheet_xml .= '' . $val . '';
}
}
$sheet_xml .= '
';
$row_num++;
}
$sheet_xml .= '';
$wb_xml = ''
. ''
. '';
$wb_rels = ''
. ''
. ''
. '';
$pkg_rels = ''
. ''
. ''
. '';
$ct = ''
. ''
. ''
. ''
. ''
. ''
. '';
// Build zip in memory
$tmp = tempnam(sys_get_temp_dir(), 'acquis_xlsx_');
$zip = new ZipArchive();
$zip->open($tmp, ZipArchive::OVERWRITE);
$zip->addFromString('[Content_Types].xml', $ct);
$zip->addFromString('_rels/.rels', $pkg_rels);
$zip->addFromString('xl/workbook.xml', $wb_xml);
$zip->addFromString('xl/_rels/workbook.xml.rels', $wb_rels);
$zip->addFromString('xl/worksheets/sheet1.xml', $sheet_xml);
$zip->close();
$data = file_get_contents($tmp);
wp_delete_file( $tmp );
return $data;
}
private static function col_letter($n) {
$letter = '';
while ($n >= 0) {
$letter = chr(65 + ($n % 26)) . $letter;
$n = intdiv($n, 26) - 1;
}
return $letter;
}
}