<?php
declare(strict_types=1);
namespace ItccaAllievi;
if (!defined('ABSPATH')) {
exit;
}
final class ExportCsv
{
public static function register(): void
{
if (defined('WP_CLI') && WP_CLI) {
\WP_CLI::add_command('itcca import-csv', [self::class, 'cli_import']);
\WP_CLI::add_command('itcca export-csv', [self::class, 'cli_export']);
}
}
public static function handle_export(): void
{
if (!current_user_can('edit_users')) {
wp_die(__('Permessi insufficienti.', 'itcca-allievi'));
}
check_admin_referer('itcca_export_csv');
$filename = 'INSARRI-' . date('Y-m-d') . '.csv';
nocache_headers();
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
$out = fopen('php://output', 'w');
if ($out === false) {
exit;
}
// BOM per Excel
fwrite($out, "\xEF\xBB\xBF");
$header = self::csv_header();
fputcsv($out, $header);
$users = get_users(['role' => ITCCA_ROLE, 'orderby' => 'meta_value', 'meta_key' => ITCCA_META_PREFIX . 'cognome']);
foreach ($users as $user) {
$row = [];
foreach ($header as $col) {
$field = Fields::by_csv($col);
$row[] = $field === null ? '' : Fields::read_value($field, $user);
}
fputcsv($out, $row);
}
fclose($out);
exit;
}
/**
* Header del CSV INSARRI: esclude i campi rimossi dallo schema (i loro dati
* restano in user_meta ma non vengono esportati).
*
* @return array<int, string>
*/
private static function csv_header(): array
{
$cols = [];
foreach (Fields::all() as $f) {
if (!empty($f['deleted'])) continue;
$csv = (string) ($f['csv'] ?? '');
if ($csv === '') continue;
$cols[] = $csv;
}
return $cols;
}
/**
* WP-CLI: import CSV in formato INSARRI come allievi.
* Usage: wp itcca import-csv /path/to/INSARRI.csv [--dry-run]
*/
public static function cli_import(array $args, array $assoc): void
{
if (!defined('WP_CLI') || !WP_CLI) return;
$path = $args[0] ?? '';
if ($path === '' || !file_exists($path)) {
\WP_CLI::error('File non trovato: ' . $path);
}
$dry = !empty($assoc['dry-run']);
$fh = fopen($path, 'r');
if ($fh === false) {
\WP_CLI::error('Impossibile aprire il file.');
}
$header = fgetcsv($fh);
if (!is_array($header)) {
\WP_CLI::error('Header CSV mancante.');
}
$col_idx = [];
foreach ($header as $i => $h) {
$field = Fields::by_csv((string) $h);
if ($field) $col_idx[$field['key']] = $i;
}
if (!isset($col_idx['cf'])) {
\WP_CLI::error('Colonna CF mancante.');
}
$created = 0; $updated = 0; $skipped = 0;
while (($row = fgetcsv($fh)) !== false) {
$data = [];
foreach ($col_idx as $key => $idx) {
$data[$key] = (string) ($row[$idx] ?? '');
}
$cf = Validators::normalize_cf((string) ($data['cf'] ?? ''));
if ($cf === '' || !Validators::is_valid_cf($cf)) {
$skipped++;
continue;
}
$existing = get_users([
'meta_key' => ITCCA_META_PREFIX . 'cf',
'meta_value' => $cf,
'fields' => 'ID',
'number' => 1,
]);
if (!empty($existing)) {
$uid = (int) $existing[0];
if (!$dry) self::apply_row($uid, $data);
$updated++;
continue;
}
if ($dry) {
$created++;
continue;
}
$uid = self::create_user_from_row($data);
if ($uid > 0) $created++;
else $skipped++;
}
fclose($fh);
\WP_CLI::success(sprintf('Creati: %d, aggiornati: %d, saltati: %d%s', $created, $updated, $skipped, $dry ? ' (dry-run)' : ''));
}
public static function cli_export(array $args, array $assoc): void
{
if (!defined('WP_CLI') || !WP_CLI) return;
$path = $args[0] ?? '';
if ($path === '') {
\WP_CLI::error('Specifica il path di output.');
}
$fh = fopen($path, 'w');
if ($fh === false) {
\WP_CLI::error('Impossibile scrivere su ' . $path);
}
fwrite($fh, "\xEF\xBB\xBF");
$header = self::csv_header();
fputcsv($fh, $header);
$users = get_users(['role' => ITCCA_ROLE]);
foreach ($users as $user) {
$row = [];
foreach ($header as $col) {
$field = Fields::by_csv($col);
$row[] = $field === null ? '' : Fields::read_value($field, $user);
}
fputcsv($fh, $row);
}
fclose($fh);
\WP_CLI::success('Export scritto in ' . $path);
}
private static function apply_row(int $user_id, array $data): void
{
foreach (Fields::storable() as $field) {
$key = $field['key'];
if (!array_key_exists($key, $data)) continue;
$val = self::normalize_for_storage($field, (string) $data[$key]);
if (!empty($field['native'])) {
if ($field['key'] === 'user_email' && $val !== '') {
wp_update_user(['ID' => $user_id, 'user_email' => $val]);
}
continue;
}
$mk = Fields::meta_key($field);
if ($val === '' && $field['type'] !== 'flag') {
delete_user_meta($user_id, $mk);
} else {
update_user_meta($user_id, $mk, $val);
}
}
}
private static function create_user_from_row(array $data): int
{
$email = sanitize_email((string) ($data['user_email'] ?? ''));
$cf = Validators::normalize_cf((string) ($data['cf'] ?? ''));
if ($email === '') {
$email = strtolower($cf) . '@no-email.itcca.local';
}
if (email_exists($email)) {
return 0;
}
$login_base = sanitize_user(
sanitize_title(remove_accents(((string) ($data['nome'] ?? '')) . '.' . ((string) ($data['cognome'] ?? '')))),
true
);
if ($login_base === '') $login_base = strtolower($cf) ?: 'allievo';
$login = $login_base;
$i = 1;
while (username_exists($login)) {
$login = $login_base . $i;
$i++;
}
$uid = wp_insert_user([
'user_login' => $login,
'user_email' => $email,
'user_pass' => wp_generate_password(20, true, true),
'first_name' => (string) ($data['nome'] ?? ''),
'last_name' => (string) ($data['cognome'] ?? ''),
'display_name' => trim(((string) ($data['nome'] ?? '')) . ' ' . ((string) ($data['cognome'] ?? ''))),
'role' => ITCCA_ROLE,
]);
if (is_wp_error($uid)) return 0;
self::apply_row((int) $uid, $data);
return (int) $uid;
}
private static function normalize_for_storage(array $field, string $value): string
{
$value = trim($value);
if ($value === '') return '';
switch ($field['type']) {
case 'date':
$d = Validators::parse_date($value);
return $d ? $d->format('Y-m-d') : '';
case 'cf':
return Validators::normalize_cf($value);
case 'cap':
return preg_replace('/\D/', '', $value) ?? '';
case 'tel':
return Validators::normalize_phone($value);
case 'flag':
$v = strtolower($value);
return in_array($v, ['1', 'x', 'sì', 'si', 'yes', 'true'], true) ? '1' : '0';
case 'decimal':
$n = (float) str_replace([',', '€', ' '], ['.', '', ''], $value);
return number_format($n, 2, '.', '');
default:
return sanitize_text_field($value);
}
}
}