Sincronizar mu-plugins/ y scripts/ con el estado real del sitio (2026-07-16)

Este repo llevaba desde el 28-jun sin actualizarse (salvo sync_carta_from_prod.py).
Se pone al dia con 3 semanas de trabajo que solo vivian en el checkout local
completo del proyecto:

- Fix del buscador (issue #178): AND obligatorio en FULLTEXT (antes devolvia
  practicamente todo el sitio con cualquier busqueda), exclusion de paginas
  indice residuales de la migracion K2, soporte de frase exacta entre comillas.
- Nuevos mu-plugins ya desplegados a prod: fea-carta-id-api, fea-cloudflare-realip,
  fea-crear-autor-api, fea-gsc-verification, fea-legacy-redirect (cutover
  Joomla->WP, ver issue #162).
- TTS multi-voz por autor, scripts de traduccion, sync de audio a prod,
  homenajes Mardones/Galarreta.

Se mantiene la estructura plana del repo (mu-plugins/ + scripts/, sin el
wordpress/wp-content/ del checkout completo) tal y como documenta el README:
es la convencion establecida para este repo, mas limpia para quien solo
necesita leer el codigo. Anadido .gitignore (cache de Python que no debia
commitearse).

Fuente: checkout local completo del proyecto, rama main (commit 69e849d).
This commit is contained in:
2026-07-15 20:26:43 -04:00
parent d044e229dc
commit 6b61250b0b
35 changed files with 1482 additions and 45 deletions
+59
View File
@@ -0,0 +1,59 @@
# Homenajes Mardones / Galarreta + fixes varios (sesión 2026-06-23)
Scripts de la sesión 2026-06-23 (issues Gitea **#130, #131, #133, #137, #138**, todas cerradas).
Todo aplicado en **local** (Docker `wordpress-web`) y **prod** (wp-nuevo, vía `ssh feadulta@… && wp eval-file`).
## Convenciones del despliegue a prod (wp-nuevo)
- Prod corre en la **raíz** del dominio (`https://wp-nuevo.feadulta.com`), local bajo `/fea`.
→ al llevar contenido con rutas, transformar `/fea/wp-content``/wp-content`.
- `proc_open` deshabilitado en prod → usar `wp eval-file` (PHP puro). `scp` no va → subir con
`ssh '… cat > /entrada/FICHERO'`; imágenes en bloque con `tar cf -``ssh 'tar x'`.
- **IDs de contenido migrado coinciden local↔prod** (posts, template parts, users, cats).
- **Lección clave:** para arreglar posts ya migrados con texto malo (idioma/contenido),
**sobrescribir EN SITIO en prod** (mismos IDs) en vez de borrar+recrear → preserva los grupos
Polylang. Para contenido NUEVO (noticia, 23 posts Mardones) crear con **meta idempotente**.
## #138 — Mardones: 23 páginas `anterior/*.htm` → 23 posts individuales
Origen: el homenaje WP **17990** (`mardones-2`, com_content Joomla 501) enlazaba 23 páginas
estáticas de la web V1 FrontPage (`/web/anterior/*.htm`, windows-1252). 8 «Aprendiendo a orar»
(serie de Mardones) + 15 homenaje (obituarios, álbum de fotos, audio→conferencia, currículo…).
Pipeline:
1. `mardones_build_manifest.py` — descarga local de los 23 `.htm`, decodifica **win-1252→UTF-8**,
limpia basura FrontPage, extrae párrafos `<p>` (descarta cabecera «cristianos siglo veintiuno»),
preserva enlaces externos, inserta imágenes. Genera `mardones_manifest.json`.
Títulos = texto del enlace en 17990; autor: serie orar = **710 (Mardones)**, homenajes = **1**.
2. `mardones_create_posts.php` (local) / `mardones_prod_create_and_repoint.php` (prod) — crea los 23
posts (lang es, cat 38, publish), idempotente por meta **`_mardones_src`**=nombre de fichero.
3. `mardones_repoint_homenaje.php` — repunta los 23 `href="anterior/X.htm"` del 17990 a los nuevos
permalinks (prod lo hace el script combinado).
4. Imágenes (8: `homenaje-album-1..7.jpg`, `homenaje-charla-mejico.jpg`) → `uploads/anterior-mardones/`.
IDs creados en local: **5357753599**. En prod: IDs propios (se resuelven por meta).
### Retoques posteriores (`mardones_fix_fecha_y_target.php`, local+prod)
- **Fecha:** los posts se crearon con fecha de hoy → reajustados a **2012-10-10 19:26:00**
(la del propio homenaje 17990) para que no aparezcan como entradas nuevas en feeds/archivos.
- **Ventana nueva:** los enlaces del homenaje 17990 heredaban `target="_blank"` del original →
eliminado (`target="_blank"`/`rel`) para que abran en la **misma ventana** (navegación interna).
*Nota:* los enlaces externos DENTRO de cada post (p.ej. la conferencia iteso.mx en homenaje-3)
sí conservan `target="_blank"`.
## #138 — Galarreta
`galarreta_repoint_presentaciones.php` (local) / `galarreta_prod_fixes.php` (prod):
- 10 presentaciones de `presentaciones-gala` (19107) estaban mal mapeadas (apuntaban a artículos
random) → repuntadas a sus destinos correctos (19105/19104/19102/19106/19101/19119/19123/19132/19131/19143).
- «Libros y e-books» (en la página 17918) → tienda externa
`https://edicionesfeadulta.com/?filters=autor[galarreta]` (13 libros), `target="_blank"`.
- iframe de YouTube forzado a `https`.
- Auditados `articulos-galarreta` (19086) y `comentarios-galarreta` (19085): sin enlaces rotos reales.
## #130 / #131 / #133 / #137 (sync a prod)
- `issue130_prod_noticia_papa.php` — crea la noticia «León XIV en España…» (Joomla 9082, no migrada)
en cat 41 Noticias de alcance, idempotente por `_fgj2wp_old_id=9082`.
- `issue131_prod_footer_suma.php` — enlaza la imagen `este_portal.gif` del footer (template 42370)
a `/numeros/` (post 18036, «La suma de todos»).
- `issue133_prod_idiomas.php` — 5 reasignaciones de idioma + sobrescritura en sitio de los 3 `fr`
que contenían inglés (43755/47152/47136) + catalán 13339 → francés, enlazado al ES 13338.
- `issue137_prod_inmemoriam.php` — repunta In memoriam (19112): Galarreta→17918, Mardones→17990,
Mari Patxi Ayerra→23519 (artículo de fallecimiento).
@@ -0,0 +1,25 @@
<?php
// 1) presentaciones-gala 19107: repuntar 10
$map=[
"sugerencias-para-las-bodas"=>19105,"el-eterno-problema-del-mundo-y-los-hijos"=>19104,
"necesitamos-un-pepe"=>19102,"lucas-18-1-8"=>19106,"el-perdon-primero"=>19101,
"alabar-a-dios-y-darle-gracias"=>19119,"2-reyes-5-14-17-2-timoteo-2-8-13"=>19123,
"creer-en-dios-es-apostar-por-la-vida"=>19132,"lucas-17-5-10"=>19131,"la-gran-evasion"=>19143,
];
$c=get_post(19107)->post_content; $n=0;
foreach($map as $bad=>$dst){
if(!get_post($dst)){ echo " dst $dst NO EXISTE\n"; continue; }
$url=get_permalink($dst);
$c2=preg_replace('#https?://[^"]*?/'.preg_quote($bad,'#').'/#',$url,$c,1,$cnt);
if($cnt>0){ $c=$c2; $n++; }
}
wp_update_post(["ID"=>19107,"post_content"=>$c]);
echo "presentaciones repuntadas: $n/10\n";
// 2) libros + youtube en 17918
$c=get_post(17918)->post_content;
$ext="https://edicionesfeadulta.com/?filters=autor[galarreta]";
$c=preg_replace('#https?://[^"]*?/libros-y-e-books/#',$ext,$c,-1,$cl);
$c=preg_replace('#(<a [^>]*href=")'.preg_quote($ext,'#').'("(?![^>]*target))#','$1'.$ext.'$2 target="_blank" rel="noopener"',$c);
$c=str_replace('src="//www.youtube.com/embed/','src="https://www.youtube.com/embed/',$c);
wp_update_post(["ID"=>17918,"post_content"=>$c]);
echo "libros->externo: $cl | youtube https aplicado | foto JE: ".(strpos($c,'edicionesfeadulta.com')!==false?'ok':'?')."\n";
@@ -0,0 +1,25 @@
<?php
// Repuntar las 10 presentaciones de presentaciones-gala (19107) a sus destinos correctos
$map=[
"sugerencias-para-las-bodas"=>19105,
"el-eterno-problema-del-mundo-y-los-hijos"=>19104,
"necesitamos-un-pepe"=>19102,
"lucas-18-1-8"=>19106,
"el-perdon-primero"=>19101,
"alabar-a-dios-y-darle-gracias"=>19119,
"2-reyes-5-14-17-2-timoteo-2-8-13"=>19123,
"creer-en-dios-es-apostar-por-la-vida"=>19132,
"lucas-17-5-10"=>19131,
"la-gran-evasion"=>19143,
];
$c=get_post(19107)->post_content; $n=0; $report=[];
foreach($map as $badslug=>$dest){
$destUrl=get_permalink($dest);
// sustituir el href que contiene /fea/<badslug>/
$pat='#https?://[^"]*?/fea/'.preg_quote($badslug,'#').'/#';
$c2=preg_replace($pat,$destUrl,$c,1,$cnt);
if($cnt>0){ $c=$c2; $n++; $report[]="OK $badslug -> $destUrl"; }
else $report[]="NO ENCONTRADO $badslug";
}
wp_update_post(["ID"=>19107,"post_content"=>$c]);
echo implode("\n",$report)."\nTotal repuntados: $n/10\n";
@@ -0,0 +1,13 @@
<?php
global $wpdb;
$ex=$wpdb->get_var($wpdb->prepare("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_fgj2wp_old_id' AND meta_value=%s","9082"));
if($ex){ echo "ya existe en prod: $ex\n"; return; }
$content=file_get_contents('/entrada/9082_content.html');
$id=wp_insert_post(["post_type"=>"post","post_status"=>"publish","post_author"=>890,
"post_title"=>"El plan (casi definitivo) de León XIV en España, al descubierto: de Carabanchel a Arguineguín, pasando por Montserrat",
"post_content"=>$content,"post_date"=>"2026-04-09 12:07:26","to_ping"=>"","pinged"=>"","post_content_filtered"=>""],true);
if(is_wp_error($id)){ echo "ERROR: ".$id->get_error_message()."\n"; return; }
update_post_meta($id,"_fgj2wp_old_id","9082");
wp_set_post_categories($id,[41]);
if(function_exists('pll_set_post_language')) pll_set_post_language($id,'es');
echo "creado prod $id cat=".implode(",",wp_get_post_categories($id))." status=".get_post_status($id)."\n";
@@ -0,0 +1,14 @@
<?php
$id=42370; $c=get_post($id)->post_content; $url=get_permalink(18036);
if(strpos($c,'<a href="'.$url.'"><img')!==false || preg_match('#<a [^>]*href="[^"]*numeros[^"]*"><img[^>]*este_portal#',$c)){ echo "ya enlazado\n"; return; }
// localizar el <img ... este_portal.gif ...> y envolverlo
if(preg_match('#<img[^>]*este_portal\.gif[^>]*/?>#i',$c,$m)){
$img=$m[0];
$c=str_replace($img,'<a href="'.esc_url($url).'">'.$img.'</a>',$c);
// linkDestination none->custom para el bloque (id 26996)
$c=preg_replace('#("id":26996,[^}]*?"linkDestination":")none(")#','${1}custom${2}',$c);
wp_update_post(["ID"=>$id,"post_content"=>$c]);
echo "OK enlazado este_portal -> $url\n";
} else echo "NO se encontró img este_portal.gif en 42370 (prod)\n";
// verificar
if(preg_match('#<a href="([^"]+)"><img[^>]*este_portal#',get_post($id)->post_content,$v)) echo "verif: -> ".$v[1]."\n";
@@ -0,0 +1,23 @@
<?php
// 1) reasignaciones de idioma (mismos IDs en prod)
$fix=[4424=>"fr",11129=>"it",12934=>"en",5891=>"en",3141=>"it"];
foreach($fix as $id=>$lang){
if(!get_post($id)){ echo " $id NO EXISTE\n"; continue; }
$old=pll_get_post_language($id); pll_set_post_language($id,$lang);
echo " $id: $old -> ".pll_get_post_language($id)."\n";
}
// 2) sobrescribir fr malos + catalan con francés correcto (overwrite en sitio)
$payload=json_decode(file_get_contents('/entrada/fr_payload.json'),true);
foreach($payload as $it){
$dst=$it['dst'];
if(!get_post($dst)){ echo " dst $dst NO EXISTE\n"; continue; }
wp_update_post(["ID"=>$dst,"post_title"=>$it['title'],"post_content"=>$it['content']]);
pll_set_post_language($dst,'fr');
echo " overwrite $dst -> fr (".mb_substr($it['title'],0,30).")\n";
}
// 3) catalan 13339: enlazar grupo con ES 13338
if(get_post(13338) && get_post(13339)){
pll_set_post_language(13338,'es');
pll_save_post_translations(["es"=>13338,"fr"=>13339]);
echo " grupo 13339: ".json_encode(pll_get_post_translations(13339))."\n";
}
@@ -0,0 +1,20 @@
<?php
$c=get_post(19112)->post_content;
$map=[
"el-bautismo-de-jesus-jesus-hijo-del-padre"=>17918, // Galarreta homenaje
"jean-baptiste-et-son-mouton"=>17990, // Mardones homenaje
"nuestra-vida-sea-servicio"=>23519, // Mari Patxi fallecimiento
];
$n=0;
foreach($map as $slug=>$id){
$url=get_permalink($id);
$pat='#https?://[^"]*?/'.preg_quote($slug,'#').'/#';
$c2=preg_replace($pat,$url,$c,1,$cnt);
if($cnt>0){ $c=$c2; $n++; echo " OK $slug -> $url\n"; } else echo " NO ENCONTRADO $slug\n";
}
if($n) wp_update_post(["ID"=>19112,"post_content"=>$c]);
// verificar 8 enlaces
preg_match_all('#href="([^"]+)"#',get_post(19112)->post_content,$m);
$ok=0;$bad=0;
foreach($m[1] as $h){ if(preg_match('#/([^/"]+)/?$#',$h,$sm)){ $p=get_posts(["name"=>$sm[1],"post_status"=>"publish","posts_per_page"=>1,"suppress_filters"=>true]); if($p)$ok++; else {$bad++; echo " ROTO $h\n";} } }
echo "repuntados=$n | enlaces in-memoriam OK=$ok bad=$bad\n";
@@ -0,0 +1,93 @@
import re, json, html, glob
IMG_BASE = "/fea/wp-content/uploads/anterior-mardones/"
# filename -> (titulo, autor_wp, [imagenes])
ORAR_AUTHOR = 710 # Mardones
HOM_AUTHOR = 1 # genérico (como página 17990)
META = {
"orar-1-dejarmequerer":("Dejarme querer",ORAR_AUTHOR,[]),
"orar-2-escuchar":("Escuchar",ORAR_AUTHOR,[]),
"orar-3-estar":("Estar",ORAR_AUTHOR,[]),
"orar-4-hacersitioadios":("Hacer sitio a Dios",ORAR_AUTHOR,[]),
"orar-5-laoracionadulta":("La oración adulta",ORAR_AUTHOR,[]),
"orar-6-orarconevangelio":("Orar con el evangelio",ORAR_AUTHOR,[]),
"orar-7-orarrepitiendo":("Orar repitiendo una palabra o frase breve",ORAR_AUTHOR,[]),
"orar-8-tuestasdentro":("Tú estás dentro",ORAR_AUTHOR,[]),
"homenaje-segundo-aniversario":("En el segundo aniversario",HOM_AUTHOR,[]),
"homenaje-14noviembre":("14 de noviembre de 2007",HOM_AUTHOR,[]),
"homenaje-PRIMERaniversario":("Primer aniversario",HOM_AUTHOR,[]),
"homenaje-aniversario":("Programa en el aniversario",HOM_AUTHOR,["homenaje-charla-mejico.jpg"]),
"homenaje-rostrointerior":("Rostro interior de José María Mardones",HOM_AUTHOR,[]),
"homenaje-cartaReyes":("Carta a un Maestro",HOM_AUTHOR,[]),
"homenaje-9":("Semblanza",HOM_AUTHOR,[]),
"homenaje-8":("Álbum de fotos de José María Mardones",HOM_AUTHOR,
["homenaje-album-%d.jpg"%i for i in range(1,8)]),
"homenaje-1":("Funeral del sábado 24 de junio",HOM_AUTHOR,[]),
"homenaje-2":("Funeral del jueves 29 de junio",HOM_AUTHOR,[]),
"homenaje-5":("Obituario en El Mundo",HOM_AUTHOR,[]),
"homenaje-6":("Necrológicas en El País",HOM_AUTHOR,[]),
"homenaje-4":("Salmo a la Encarnación de Dios",HOM_AUTHOR,[]),
"homenaje-3":("Conferencia: ¿Por qué preocuparse por los demás? Ética y convivencia",HOM_AUTHOR,[]),
"homenaje-7":("Currículo de José María Mardones",HOM_AUTHOR,[]),
}
BOILER = re.compile(r'cristianos siglo veintiuno|^I N M E M O R I A M$|^HOMENAJE$|^homenaje a JOSE|APRENDIENDO A ORAR cristianos', re.I)
def extract(fn):
raw=open(fn,'rb').read().decode('windows-1252',errors='replace')
raw=re.sub(r'(?is)<(script|style).*?</\1>','',raw)
raw=re.sub(r'(?is)<!--.*?-->','',raw)
blocks=[]
for p in re.findall(r'(?is)<p\b[^>]*>(.*?)</p>',raw):
# preservar enlaces externos
links=re.findall(r'(?is)<a\s+[^>]*href="(https?://[^"]+)"[^>]*>(.*?)</a>',p)
t=re.sub(r'(?is)<[^>]+>','',p)
t=html.unescape(t).replace('\xa0',' ')
t=re.sub(r'\s+',' ',t).strip()
if len(t)<3:
# párrafo solo-enlace
if links:
for href,txt in links:
txt=re.sub(r'<[^>]+>','',txt).strip() or href
blocks.append(('link',href,txt))
continue
if BOILER.search(t):
continue
# ¿el párrafo contiene un enlace externo embebido? añadir como link extra
blocks.append(('p',t,links))
return blocks
def build_html(blocks, imgs):
out=[]
for b in blocks:
if b[0]=='p':
if re.match(r'^https?://\S+$', b[1]):
out.append('<p><a href="%s" target="_blank" rel="noopener">%s</a></p>'%(html.escape(b[1]),html.escape(b[1])))
continue
txt=html.escape(b[1])
# re-incrustar enlaces externos que estaban en el párrafo
for href,atxt in b[2]:
atxt_clean=html.escape(re.sub(r'<[^>]+>','',atxt).strip() or href)
# no siempre está el texto en txt; añadimos al final si no
out.append("<p>%s</p>"%txt)
elif b[0]=='link':
out.append('<p><a href="%s" target="_blank" rel="noopener">%s</a></p>'%(html.escape(b[1]),html.escape(b[2])))
# imágenes al final
for im in imgs:
out.append('<p><img src="%s%s" alt="José María Mardones" style="max-width:100%%;height:auto"/></p>'%(IMG_BASE,im))
return "\n".join(out)
manifest=[]
for fn in sorted(glob.glob("*.htm")):
key=fn[:-4]
if key not in META:
print("SIN META:",key); continue
title,author,imgs=META[key]
blocks=extract(fn)
content=build_html(blocks,imgs)
manifest.append({"file":key,"title":title,"author":author,"content":content,"nparas":len([b for b in blocks if b[0]=='p']),"nimgs":len(imgs)})
json.dump(manifest,open("manifest.json","w"),ensure_ascii=False,indent=1)
print("manifest:",len(manifest),"posts")
for m in manifest: print(f" {m['file']:34} '{m['title'][:35]}' autor={m['author']} paras={m['nparas']} imgs={m['nimgs']} len={len(m['content'])}")
@@ -0,0 +1,21 @@
<?php
$man=json_decode(file_get_contents('/tmp/manifest.json'),true);
$map=[]; $created=0; $skipped=0;
foreach($man as $m){
// idempotencia
$ex=get_posts(["meta_key"=>"_mardones_src","meta_value"=>$m['file'],"post_type"=>"post","post_status"=>"any","posts_per_page"=>1,"suppress_filters"=>true]);
if($ex){ $map[$m['file']]=$ex[0]->ID; $skipped++; continue; }
$id=wp_insert_post([
"post_type"=>"post","post_status"=>"publish","post_author"=>(int)$m['author'],
"post_title"=>$m['title'],"post_content"=>$m['content'],
"to_ping"=>"","pinged"=>"","post_content_filtered"=>"",
],true);
if(is_wp_error($id)){ echo "ERROR ".$m['file'].": ".$id->get_error_message()."\n"; continue; }
update_post_meta($id,"_mardones_src",$m['file']);
wp_set_post_categories($id,[38]);
if(function_exists('pll_set_post_language')) pll_set_post_language($id,'es');
$map[$m['file']]=$id; $created++;
}
file_put_contents('/tmp/mardones_map.json',json_encode($map));
echo "creados=$created skipped=$skipped total=".count($map)."\n";
foreach($map as $f=>$id) echo " $f -> $id\n";
@@ -0,0 +1,14 @@
<?php
// 1) fecha: alinear con el homenaje 17990 (2012-10-10)
$date="2012-10-10 19:26:00"; $gmt=get_gmt_from_date($date);
$ids=get_posts(["meta_key"=>"_mardones_src","post_type"=>"post","posts_per_page"=>-1,"fields"=>"ids","post_status"=>"any","suppress_filters"=>true]);
$nd=0;
foreach($ids as $id){ wp_update_post(["ID"=>$id,"post_date"=>$date,"post_date_gmt"=>$gmt]); $nd++; }
echo "fechas actualizadas: $nd posts -> $date\n";
// 2) quitar target=_blank (y rel) de los enlaces internos del homenaje 17990
$c=get_post(17990)->post_content;
$before=substr_count(strtolower($c),'target="_blank"');
$c=str_replace([' target="_blank"',' rel="noopener"',' rel="noreferrer"'],'',$c);
wp_update_post(["ID"=>17990,"post_content"=>$c]);
$after=substr_count(strtolower(get_post(17990)->post_content),'target="_blank"');
echo "target=_blank en 17990: antes=$before despues=$after\n";
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
<?php
$man=json_decode(file_get_contents('/entrada/manifest_prod.json'),true);
$map=[]; $created=0;$skip=0;
foreach($man as $m){
$ex=get_posts(["meta_key"=>"_mardones_src","meta_value"=>$m['file'],"post_type"=>"post","post_status"=>"any","posts_per_page"=>1,"suppress_filters"=>true]);
if($ex){ $map[$m['file']]=$ex[0]->ID; $skip++; continue; }
$author=(int)$m['author']; if($author && !get_userdata($author)) $author=1;
$id=wp_insert_post(["post_type"=>"post","post_status"=>"publish","post_author"=>$author,
"post_title"=>$m['title'],"post_content"=>$m['content'],"to_ping"=>"","pinged"=>"","post_content_filtered"=>""],true);
if(is_wp_error($id)){ echo "ERR ".$m['file'].": ".$id->get_error_message()."\n"; continue; }
update_post_meta($id,"_mardones_src",$m['file']);
wp_set_post_categories($id,[38]);
if(function_exists('pll_set_post_language')) pll_set_post_language($id,'es');
$map[$m['file']]=$id; $created++;
}
echo "creados=$created skip=$skip total=".count($map)."\n";
// repuntar 17990
$c=get_post(17990)->post_content; $n=0;
foreach($map as $file=>$id){
$url=get_permalink($id);
$c2=preg_replace('#href="anterior/'.preg_quote($file,'#').'\.htm"#i','href="'.$url.'"',$c,1,$cnt);
if($cnt>0){ $c=$c2; $n++; }
}
wp_update_post(["ID"=>17990,"post_content"=>$c]);
$rest=preg_match_all('#anterior/[\w-]+\.htm#i',get_post(17990)->post_content,$mm);
echo "17990 repuntados=$n/23 | anterior/*.htm restantes=$rest\n";
@@ -0,0 +1,17 @@
<?php
$map=json_decode(file_get_contents('/tmp/mardones_map.json'),true);
$c=get_post(17990)->post_content; $n=0; $miss=[];
foreach($map as $file=>$id){
$url=get_permalink($id);
// href="anterior/FILE.htm" (puede tener mayúsculas y espacios delante)
$pat='#href="anterior/'.preg_quote($file,'#').'\.htm"#i';
$c2=preg_replace($pat,'href="'.$url.'"',$c,1,$cnt);
if($cnt>0){ $c=$c2; $n++; } else $miss[]=$file;
}
wp_update_post(["ID"=>17990,"post_content"=>$c]);
echo "repuntados=$n/23\n";
if($miss) echo "NO ENCONTRADOS: ".implode(", ",$miss)."\n";
// verificar que no quedan anterior/*.htm
$rest=preg_match_all('#anterior/[\w-]+\.htm#i',get_post(17990)->post_content,$mm);
echo "enlaces anterior/*.htm restantes: $rest\n";
if($rest) foreach($mm[0] as $x) echo " $x\n";