diff --git a/scripts/fix_k2_authors.php b/scripts/fix_k2_authors.php new file mode 100644 index 0000000..ade6f55 --- /dev/null +++ b/scripts/fix_k2_authors.php @@ -0,0 +1,94 @@ +created_bynombre», generado desde Joomla: + * IDS= # de wp: meta _fgj2wp_old_k2_id + * mysql --skip-ssl ... fejoomla3 -N -e \ + * "SELECT i.id, i.created_by, COALESCE(u.name,'') \ + * FROM ew4r_k2_items i LEFT JOIN ew4r_users u ON u.id=i.created_by \ + * WHERE i.id IN ($IDS);" > /tmp/autores143.tsv + * + * Uso (en el servidor, dentro de /web/wp-nuevo): + * FEA_TSV=/tmp/autores143.tsv wp eval-file scripts/fix_k2_authors.php # dry-run + * APPLY=1 FEA_TSV=/tmp/autores143.tsv wp eval-file scripts/fix_k2_authors.php # aplica + * + * Notas: + * - Los autores con created_by cuyo usuario Joomla ya no existe llegan con + * nombre vacío en el TSV → se SALTAN (no recuperable; firma en el cuerpo). + * - El nombre literal «Fe Adulta» se salta (es legítimo). + * - Los nuevos usuarios quedan sin foto_perfil (avatar genérico). Si se quiere + * avatar propio, generarlo aparte (ver flujo de avatares #62). + */ + +$APPLY = getenv('APPLY') === '1'; +$TSV = getenv('FEA_TSV') ?: '/tmp/autores143.tsv'; +if (!is_readable($TSV)) { fwrite(STDERR, "No puedo leer TSV: $TSV\n"); exit(1); } + +global $wpdb; +$GENERIC = [1, 890]; +$SKIP_NAMES = ['Fe Adulta']; + +$byname = []; +foreach (file($TSV) as $line) { + $r = explode("\t", rtrim($line, "\n")); + if (count($r) < 3) continue; + $name = trim($r[2]); + if ($name === '' || in_array($name, $SKIP_NAMES, true)) continue; + $byname[$name][] = (int) $r[0]; +} + +$created = 0; $reassigned = 0; $log = []; +foreach ($byname as $name => $k2ids) { + $login = sanitize_user(sanitize_title($name), true); + $u = get_user_by('login', $login); + if (!$u) { + $email = $login . '@feadulta.com'; $i = 2; + while (email_exists($email)) { $email = $login . $i . '@feadulta.com'; $i++; } + if ($APPLY) { + $uid = wp_insert_user([ + 'user_login' => $login, + 'user_pass' => wp_generate_password(20), + 'user_email' => $email, + 'display_name' => $name, + 'nickname' => $name, + 'role' => 'subscriber', + ]); + if (is_wp_error($uid)) { $log[] = "ERROR crear '$name': " . $uid->get_error_message(); continue; } + $u = get_userdata($uid); $created++; + $log[] = "USER creado: '$name' -> id $uid ($login / $email)"; + } else { + $log[] = "[dry] crearia USER '$name' ($login / $email)"; $created++; + } + } else { + $log[] = "USER ya existe: '$name' -> id {$u->ID} ($login)"; + } + $uid = $u ? $u->ID : 0; + foreach ($k2ids as $k2) { + $pids = $wpdb->get_col($wpdb->prepare( + "SELECT DISTINCT post_id FROM {$wpdb->postmeta} + WHERE meta_key='_fgj2wp_old_k2_id' AND meta_value=%s", (string) $k2)); + foreach ($pids as $pid) { + $a = (int) get_post_field('post_author', $pid); + if (!in_array($a, $GENERIC, true)) continue; + if ($APPLY && $uid) { + wp_update_post(['ID' => (int) $pid, 'post_author' => $uid]); + $reassigned++; $log[] = " post $pid (k2 $k2) author $a -> $uid"; + } else { + $reassigned++; $log[] = " [dry] post $pid (k2 $k2) author $a -> '$name'"; + } + } + } +} +echo implode("\n", $log) . "\n"; +echo "\nRESUMEN: usuarios " . ($APPLY ? 'creados' : 'a crear') . ": $created ; " + . "posts " . ($APPLY ? 'reasignados' : 'a reasignar') . ": $reassigned " + . "(modo " . ($APPLY ? 'APPLY' : 'DRY-RUN') . ")\n"; diff --git a/scripts/gen_avatars_initials.py b/scripts/gen_avatars_initials.py new file mode 100644 index 0000000..2637f26 --- /dev/null +++ b/scripts/gen_avatars_initials.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""Genera avatares de INICIALES (mismo formato que #62) para autores sin foto. + +Formato replicado del #62: PNG 200x200 RGBA, círculo de color sólido (paleta +determinista de 10 tonos elegida por hash del nombre) con las iniciales en blanco, +DejaVuSans-Bold, esquinas transparentes (borde circular supersampleado). + +Entrada: TSV «uiddisplay_name» (env FEA_TSV, por defecto /tmp/users29.tsv). +Salida: uploads/avatares/autores/autor-.png + +Uso: + FEA_TSV=/tmp/users29.tsv python3 scripts/gen_avatars_initials.py +""" +import hashlib, os, unicodedata +from PIL import Image, ImageDraw, ImageFont + +OUT = "/home/rafa/joomla-migration/wordpress/wp-content/uploads/avatares/autores" +TSV = os.environ.get("FEA_TSV", "/tmp/users29.tsv") +FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" +SIZE, SS = 200, 4 + +# Paleta del #62 (extraída de los avatares existentes; 10 tonos apagados) +PALETTE = [ + (91, 110, 80), (100, 110, 60), (160, 110, 70), (176, 122, 57), (150, 90, 110), + (80, 80, 110), (74, 95, 120), (60, 90, 90), (139, 26, 46), (120, 82, 72), +] +STOP = {"la", "las", "el", "los", "un", "una", "de", "del", "y", "e", "da", "do", "the"} + + +def strip_accents(s: str) -> str: + return "".join(c for c in unicodedata.normalize("NFD", s) + if unicodedata.category(c) != "Mn") + + +def initials(name: str) -> str: + raw = name.replace("/", " ").replace('"', " ").replace("'", " ") + toks = [t for t in raw.split() if t and t.lower() not in STOP] + if not toks: + return "?" + picks = toks[:2] if len(toks) >= 2 else toks[:1] + return strip_accents("".join(t[0] for t in picks)).upper() + + +def color_for(name: str): + h = int(hashlib.md5(name.encode("utf-8")).hexdigest(), 16) + return PALETTE[h % len(PALETTE)] + + +def make(uid: str, name: str): + big = SIZE * SS + im = Image.new("RGBA", (big, big), (0, 0, 0, 0)) + d = ImageDraw.Draw(im) + d.ellipse((0, 0, big - 1, big - 1), fill=color_for(name) + (255,)) + txt = initials(name) + # ajusta tamaño hasta cap-height ~77px (en escala SS) + font = ImageFont.truetype(FONT, int(104 * SS)) + bbox = d.textbbox((0, 0), txt, font=font) + tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1] + x = (big - tw) / 2 - bbox[0] + y = (big - th) / 2 - bbox[1] + d.text((x, y), txt, font=font, fill=(255, 255, 255, 255)) + im = im.resize((SIZE, SIZE), Image.LANCZOS) + im.save(f"{OUT}/autor-{uid}.png") + return txt + + +os.makedirs(OUT, exist_ok=True) +n = 0 +for line in open(TSV, encoding="utf-8"): + parts = line.rstrip("\n").split("\t") + if len(parts) < 2: + continue + uid, name = parts[0].strip(), parts[1].strip() + ini = make(uid, name) + print(f"autor-{uid}.png {ini:3} {name}") + n += 1 +print(f"\n{n} avatares generados en {OUT}") diff --git a/scripts/import_avatars_143.php b/scripts/import_avatars_143.php new file mode 100644 index 0000000..cedda96 --- /dev/null +++ b/scripts/import_avatars_143.php @@ -0,0 +1,56 @@ +.png + * y reapunta foto_perfil (ACF user_meta). Backup del valor previo en + * user_meta _foto_perfil_pre143. Idempotente (si ya apunta, solo regenera metadata). + * + * Entrada: TSV «uiddisplay_name» (env FEA_TSV, por defecto /tmp/users29.tsv). + * Uso (en el servidor, dentro de /web/wp-nuevo): + * FEA_TSV=/tmp/users29.tsv wp eval-file scripts/import_avatars_143.php # dry-run + * APPLY=1 FEA_TSV=/tmp/users29.tsv wp eval-file scripts/import_avatars_143.php # aplica + */ +require_once ABSPATH . 'wp-admin/includes/image.php'; + +$apply = getenv('APPLY') === '1'; +$tsv = getenv('FEA_TSV') ?: '/tmp/users29.tsv'; +$updir = wp_get_upload_dir(); +if (!is_readable($tsv)) { echo "No puedo leer TSV: $tsv\n"; return; } + +$done = $regen = $err = 0; +foreach (file($tsv) as $line) { + $p = explode("\t", rtrim($line, "\n")); + if (count($p) < 2) continue; + $uid = (int) $p[0]; + $name = trim($p[1]); + $rel = "avatares/autores/autor-{$uid}.png"; + $abs = $updir['basedir'] . '/' . $rel; + if (!file_exists($abs)) { echo "MISSING uid=$uid ($name)\n"; $err++; continue; } + + $cur = (int) get_user_meta($uid, 'foto_perfil', true); + if ($cur && get_post_meta($cur, '_wp_attached_file', true) === $rel) { + echo "REGEN #$uid $name (attachment $cur ya apunta al PNG)\n"; + if ($apply) wp_update_attachment_metadata($cur, wp_generate_attachment_metadata($cur, $abs)); + $regen++; continue; + } + + echo "NUEVO #$uid $name (foto_perfil actual: " . ($cur ?: 'ninguna') . ")\n"; + if (!$apply) { $done++; continue; } + + if (get_user_meta($uid, '_foto_perfil_pre143', true) === '') { + update_user_meta($uid, '_foto_perfil_pre143', $cur); + } + $aid = wp_insert_attachment([ + 'post_mime_type' => 'image/png', + 'post_title' => "Avatar {$name}", + 'post_status' => 'inherit', + 'guid' => $updir['baseurl'] . '/' . $rel, + ], $abs, 0, true); + if (is_wp_error($aid)) { echo " ERR: " . $aid->get_error_message() . "\n"; $err++; continue; } + wp_update_attachment_metadata($aid, wp_generate_attachment_metadata($aid, $abs)); + update_user_meta($uid, 'foto_perfil', $aid); + $done++; +} +echo "\n" . ($apply ? "APLICADO" : "DRY-RUN") . ": nuevos=$done regen=$regen errores=$err\n";