if (!defined('ABSPATH')) exit; if (!defined('TN_ADMIN_PANEL_BUDGET')) define('TN_ADMIN_PANEL_BUDGET', 0.50); if (!function_exists('tn_admin_panel_can')) { function tn_admin_panel_can() { return current_user_can('manage_options'); } } if (!function_exists('tn_admin_panel_backup')) { function tn_admin_panel_backup($post) { update_post_meta($post->ID, '_tn_admin_prev', base64_encode(wp_json_encode(array( 'content' => $post->post_content, 'excerpt' => $post->post_excerpt, 'status' => $post->post_status, 'time' => current_time('mysql'), )))); $force = function () { return 5; }; add_filter('wp_revisions_to_keep', $force, 99); wp_save_post_revision($post->ID); remove_filter('wp_revisions_to_keep', $force, 99); } } if (!function_exists('tn_admin_source_name')) { function tn_admin_source_name($pid) { $map = array( 'lapatilla.com' => 'Lapatilla', 'ambito.com' => 'Ámbito', 'albertonews.com' => 'AlbertoNews', 'dw.com' => 'DW', 'diarioavance.com' => 'Diario Avance', 'elnacional.com' => 'El Nacional', 'elpais.com' => 'El País', 'vidaextra.com' => 'VidaExtra', 'bbc.com' => 'BBC', 'infobae.com' => 'Infobae', 'primicia.com.ve' => 'Primicia', 'esconusted.com' => 'Es Con Usted', 'okdiario.com' => 'OkDiario', 'elpitazo.net' => 'El Pitazo', 'runrun.es' => 'Runrun.es', 'efe.com' => 'EFE', 'clarin.com' => 'Clarín', 'cnn.com' => 'CNN', 'elmundo.es' => 'El Mundo', 'rt.com' => 'RT', 'eltiempo.com' => 'El Tiempo', 'noticiasaldiayalahora.co' => 'Noticias Al Día y a la Hora', ); $link = get_post_meta($pid, 'original_link', true); if (!$link) return ''; $host = preg_replace('/^www\./', '', strtolower((string) parse_url($link, PHP_URL_HOST))); if ($host === '') return ''; foreach ($map as $d => $n) { if ($host === $d || substr($host, -strlen('.' . $d)) === '.' . $d) return $n; } $p = explode('.', $host); $i = max(0, count($p) - 2); $label = $p[$i]; if (in_array($label, array('com','co','net','org','gob','edu'), true) && count($p) >= 3) $label = $p[count($p) - 3]; return ucwords(str_replace('-', ' ', $label)); } } if (!function_exists('tn_admin_img_identity')) { function tn_admin_img_identity(DOMElement $img) { $cand = ''; foreach (array('data-src','data-lazy-src','data-lazyload','data-original','src') as $a) { $v = trim($img->getAttribute($a)); if ($v !== '' && stripos($v, 'data:image') !== 0) { $cand = $v; break; } } if ($cand === '') { $ss = trim($img->getAttribute('srcset')); if ($ss !== '') { $first = explode(',', $ss); $cand = trim(explode(' ', trim($first[0]))[0]); } } if ($cand === '') return ''; $u = html_entity_decode($cand, ENT_QUOTES); $u = preg_replace('/[?#].*$/', '', $u); $u = preg_replace('#^[a-z]+://#i', '', $u); $u = preg_replace('#^//#', '', $u); $u = preg_replace('/^www\./i', '', $u); return strtolower(rawurldecode($u)); } } if (!function_exists('tn_admin_dedupe_images')) { function tn_admin_dedupe_images(DOMDocument $doc, DOMXPath $xp) { $root = $doc->getElementById('__tnroot'); if (!$root) return 0; $tokens = array(); $walk = function ($node) use (&$walk, &$tokens) { foreach ($node->childNodes as $c) { if ($c->nodeType === XML_TEXT_NODE) { if (trim(preg_replace('/[\s\x{00a0}]+/u', ' ', $c->nodeValue)) !== '') $tokens[] = array('t' => 'text'); continue; } if (!($c instanceof DOMElement)) continue; $tag = strtolower($c->nodeName); if ($tag === 'img') { $tokens[] = array('t' => 'img', 'node' => $c); continue; } if (in_array($tag, array('iframe','video','audio','blockquote','script','embed','object','svg'), true)) { $tokens[] = array('t' => 'media'); continue; } $walk($c); } }; $walk($root); $lastId = null; $broken = true; $uniq = 0; $toRemove = array(); foreach ($tokens as $tk) { if ($tk['t'] !== 'img') { $broken = true; continue; } $idn = tn_admin_img_identity($tk['node']); if ($idn === '') $idn = "\0u" . (++$uniq); if ($lastId !== null && !$broken && $idn === $lastId) { $toRemove[] = $tk['node']; } else { $lastId = $idn; $broken = false; } } foreach ($toRemove as $imgn) { $p = $imgn->parentNode; if ($p) $p->removeChild($imgn); while ($p instanceof DOMElement && $p->getAttribute('id') !== '__tnroot' && $p->parentNode) { $hasMedia = false; foreach (array('img','iframe','video','audio','picture','blockquote','script','embed','object','svg') as $t) { if ($p->getElementsByTagName($t)->length > 0) { $hasMedia = true; break; } } $txt = trim(preg_replace('/[\s\x{00a0}]+/u', '', $p->textContent)); if (!$hasMedia && $txt === '') { $up = $p->parentNode; $up->removeChild($p); $p = $up; } else break; } } return count($toRemove); } } if (!function_exists('tn_admin_curar_safe')) { function tn_admin_curar_safe($pid, $html) { $rep = array('source' => '', 'footer' => 'ya_existe', 'credits' => array(), 'junk' => array(), 'dupes' => 0); if (trim($html) === '') return array($html, $rep); $src = tn_admin_source_name((int) $pid); $rep['source'] = $src; $has_footer = stripos($html, 'Fuente de TenemosNoticias.com:') !== false; $prev = libxml_use_internal_errors(true); libxml_clear_errors(); $doc = new DOMDocument(); $doc->loadHTML('
' . $html . '
', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET); libxml_clear_errors(); libxml_use_internal_errors($prev); $xp = new DOMXPath($doc); $rep['dupes'] = tn_admin_dedupe_images($doc, $xp); $prot = function ($n) { if (!($n instanceof DOMElement)) return false; foreach (array('img','iframe','blockquote','script','video','audio','source','picture') as $t) { if ($n->getElementsByTagName($t)->length > 0) return true; } if (preg_match('/lazyload|wp-block-embed|twitter|instagram|tiktok|fb-|embed/i', $n->getAttribute('class') . ' ' . $n->getAttribute('id'))) return true; foreach ($n->attributes as $a) { if (stripos($a->name, 'data-src') !== false) return true; } return false; }; $junk = array( '/^Publicidad( \/ Contenido Patrocinado)?$/iu', '/^Contenido Patrocinado( \/ Recomendado para ti)?$/iu', '/^Recomendado para ti$/iu', '/Para leer la nota completa/iu', '/Sigue en directo/iu', '/Venezuela atraviesa un plan de TRES FASES/iu', ); foreach (iterator_to_array($xp->query('//p|//h2|//h3|//h4')) as $n) { if (!($n instanceof DOMElement) || !$n->parentNode) continue; if ($prot($n)) continue; $t = trim(preg_replace('/\s+/u', ' ', $n->textContent)); if ($t === '') continue; $isj = false; foreach ($junk as $p) { if (preg_match($p, $t)) { $isj = true; break; } } if ($isj) { $rep['junk'][] = mb_substr($t, 0, 45); $n->parentNode->removeChild($n); continue; } if ($n->getElementsByTagName('a')->length > 0) continue; $iscred = false; if ($src !== '' && mb_strtolower($t) === mb_strtolower($src)) $iscred = true; if (!$iscred && preg_match('/^Por:?\s+[a-z0-9.\-]+\.[a-z]{2,}$/iu', $t)) $iscred = true; if ($iscred) { $rep['credits'][] = mb_substr($t, 0, 45); $n->parentNode->removeChild($n); } } foreach (iterator_to_array($xp->query('//*[@id="mc_embed_signup"]')) as $n) { if ($n instanceof DOMElement && !$prot($n) && $n->parentNode) { $rep['junk'][] = '#mc_embed_signup'; $n->parentNode->removeChild($n); } } foreach (iterator_to_array($xp->query('//p')) as $n) { if (!($n instanceof DOMElement) || !$n->parentNode) continue; if ($prot($n)) continue; if ($n->getElementsByTagName('a')->length > 0) continue; $t = trim(preg_replace('/\x{00a0}|\s+/u', '', $n->textContent)); if ($t === '') $n->parentNode->removeChild($n); } foreach (iterator_to_array($xp->query('//*[contains(@style,"padding-bottom") or contains(@style,"padding-top") or contains(@style,"aspect-ratio")]')) as $n) { if (!($n instanceof DOMElement) || !$n->parentNode) continue; $hm = $n->getElementsByTagName('img')->length || $n->getElementsByTagName('iframe')->length || $n->getElementsByTagName('video')->length || $n->getElementsByTagName('picture')->length; if ($hm) { $st = preg_replace('/(?:^|;)\s*(?:padding-bottom|padding-top|aspect-ratio)\s*:[^;]*/i', '', (string) $n->getAttribute('style')); $st = trim($st, " ;"); if ($st === '') { $n->removeAttribute('style'); } else { $n->setAttribute('style', $st); } } elseif (!$prot($n)) { $rep['junk'][] = 'aspect_vacio'; $n->parentNode->removeChild($n); } } for ($pass = 0; $pass < 2; $pass++) { foreach (iterator_to_array($xp->query('//div|//span|//section|//figure|//aside|//ul|//ol')) as $n) { if (!($n instanceof DOMElement) || !$n->parentNode) continue; if ($n->getAttribute('id') === '__tnroot') continue; if ($prot($n)) continue; if ($n->getElementsByTagName('a')->length > 0) continue; $t2 = trim(preg_replace('/\x{00a0}|\s+/u', '', $n->textContent)); if ($t2 === '') $n->parentNode->removeChild($n); } } $root = $doc->getElementById('__tnroot'); $clean = ''; if ($root) { foreach ($root->childNodes as $c) { $clean .= $doc->saveHTML($c); } } $clean = trim($clean); $clean = preg_replace('/^(?:\s*\s*)+/i', '', $clean); $clean = preg_replace('/(?:\s*\s*)+$/i', '', $clean); $clean = preg_replace('/(?:\s*){2,}/i', '
', $clean); if (!$has_footer && $src !== '') { $clean .= "\n

Fuente de TenemosNoticias.com: " . esc_html($src) . "

"; $rep['footer'] = 'anadido'; } return array($clean, $rep); } } add_action('wp_footer', function () { if (!is_singular('post') || !tn_admin_panel_can()) return; $pid = (int) get_queried_object_id(); $status = esc_html(get_post_status($pid)); $nonce = esc_attr(wp_create_nonce('tn_admin_panel_' . $pid)); $ajax = esc_url(admin_url('admin-ajax.php')); $html = << #tn-adm-panel{display:none;margin:14px 0;padding:12px;border:1px solid #e9ecef;border-radius:10px;background:#f8f9fa;box-shadow:0 3px 10px rgba(0,0,0,.06);font-family:inherit} #tn-adm-panel .tn-adm-h{font-size:12px;font-weight:700;color:#868e96;text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px} #tn-adm-panel .tn-adm-row{display:flex;flex-wrap:wrap;gap:8px} #tn-adm-panel button{flex:1 1 auto;min-width:120px;height:40px;border:none;border-radius:6px;color:#fff;font-size:14px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px} #tn-adm-panel button:disabled{opacity:.5;cursor:default} #tn-adm-b-draft{background:#868e96}#tn-adm-b-pending{background:#f08c00}#tn-adm-b-enrich{background:#1c7ed6}#tn-adm-b-clean{background:#0ca678} #tn-adm-panel .tn-adm-st{font-weight:700;color:#212529} #tn-adm-toast-wrap{position:fixed;z-index:2147483000;right:18px;bottom:18px;display:flex;flex-direction:column;gap:10px;max-width:340px;width:calc(100vw - 36px);font-family:inherit;pointer-events:none} .tn-adm-toast{pointer-events:auto;display:flex;align-items:flex-start;gap:10px;padding:12px 13px;border-radius:10px;background:#fff;color:#212529;font-size:13.5px;line-height:1.45;box-shadow:0 10px 28px rgba(0,0,0,.16);border-left:4px solid #adb5bd;transform:translateX(120%);opacity:0;transition:transform .34s cubic-bezier(.22,1,.36,1),opacity .34s} .tn-adm-toast.tn-show{transform:translateX(0);opacity:1} .tn-adm-toast.tn-ok{border-left-color:#0ca678}.tn-adm-toast.tn-err{border-left-color:#e03131}.tn-adm-toast.tn-info{border-left-color:#1c7ed6} .tn-adm-toast .tn-ic{flex:0 0 auto;width:20px;text-align:center;font-size:15px;margin-top:1px} .tn-adm-toast.tn-ok .tn-ic{color:#0ca678}.tn-adm-toast.tn-err .tn-ic{color:#e03131}.tn-adm-toast.tn-info .tn-ic{color:#1c7ed6} .tn-adm-toast .tn-bd{flex:1 1 auto;word-break:break-word} .tn-adm-toast .tn-cl{flex:0 0 auto;cursor:pointer;color:#adb5bd;font-size:18px;line-height:1;background:none;border:none;padding:0;margin:-1px -2px 0 0;width:auto;min-width:0;height:auto} .tn-adm-toast .tn-cl:hover{color:#495057} @media (max-width:600px){#tn-adm-toast-wrap{right:10px;left:10px;bottom:10px;width:auto;max-width:none}} .tn-adm-ov{position:fixed;inset:0;z-index:2147483600;display:flex;align-items:center;justify-content:center;padding:18px;background:rgba(20,22,28,.55);opacity:0;transition:opacity .2s;font-family:inherit} .tn-adm-ov.tn-show{opacity:1} .tn-adm-ov .tn-adm-modal{width:100%;max-width:380px;background:#fff;border-radius:14px;box-shadow:0 18px 50px rgba(0,0,0,.3);padding:20px;transform:translateY(14px) scale(.97);transition:transform .22s cubic-bezier(.22,1,.36,1)} .tn-adm-ov.tn-show .tn-adm-modal{transform:translateY(0) scale(1)} .tn-adm-ov .tn-adm-mh{display:flex;align-items:center;gap:11px;margin-bottom:12px} .tn-adm-ov .tn-adm-mi{flex:0 0 auto;width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:17px} .tn-adm-ov .tn-adm-mt{font-size:17px;font-weight:700;color:#212529;line-height:1.2} .tn-adm-ov .tn-adm-mb{font-size:14px;line-height:1.5;color:#495057;margin-bottom:20px} .tn-adm-ov .tn-adm-mf{display:flex;gap:10px;justify-content:flex-end} .tn-adm-ov .tn-adm-mf button{border:none;border-radius:8px;height:42px;padding:0 18px;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;display:flex;align-items:center} .tn-adm-ov .tn-adm-no{background:#f1f3f5;color:#495057} .tn-adm-ov .tn-adm-no:hover{background:#e9ecef} .tn-adm-ov .tn-adm-yes{color:#fff} .tn-adm-ov .tn-adm-yes:hover{filter:brightness(.94)} @media (max-width:600px){.tn-adm-ov{align-items:flex-end;padding:0}.tn-adm-ov .tn-adm-modal{max-width:none;border-radius:16px 16px 0 0;padding:22px 18px calc(22px + env(safe-area-inset-bottom));transform:translateY(100%)}.tn-adm-ov.tn-show .tn-adm-modal{transform:translateY(0)}}
Admin — estado: {$status}
HTML; echo $html; }, 50); add_action('wp_ajax_tn_admin_article_action', function () { $pid = isset($_POST['pid']) ? (int) $_POST['pid'] : 0; if (!$pid || !tn_admin_panel_can() || !check_ajax_referer('tn_admin_panel_' . $pid, 'nonce', false)) { wp_send_json_error(array('msg' => 'No autorizado'), 403); } $op = isset($_POST['op']) ? sanitize_key($_POST['op']) : ''; $post = get_post($pid); if (!$post || $post->post_type !== 'post') wp_send_json_error(array('msg' => 'Articulo invalido'), 400); if (function_exists('set_time_limit')) @set_time_limit(180); if ($op === 'draft' || $op === 'pending') { $new_status = ($op === 'draft') ? 'draft' : 'pending'; $r = wp_update_post(array('ID' => $pid, 'post_status' => $new_status), true); if (is_wp_error($r)) wp_send_json_error(array('msg' => $r->get_error_message())); wp_send_json_success(array('msg' => 'Estado cambiado a ' . $new_status, 'reload' => true)); } if ($op === 'clean') { if (!function_exists('tn_admin_curar_safe')) wp_send_json_error(array('msg' => 'Limpiador no disponible')); $in = $post->post_content; list($clean, $rep) = tn_admin_curar_safe($pid, $in); if ($clean === $in) wp_send_json_success(array('msg' => 'Nada que limpiar (sin relleno ni firmas redundantes).', 'reload' => false)); foreach (array('twitter-tweet','instagram-media','tiktok-embed','fb-post',' 'Abortado por seguridad: la limpieza habria afectado un embed/imagen (' . $m . '). No se guardo nada.')); } } tn_admin_panel_backup($post); $r = wp_update_post(array('ID' => $pid, 'post_content' => $clean), true); if (is_wp_error($r)) wp_send_json_error(array('msg' => $r->get_error_message())); $parts = array(); if (!empty($rep['credits'])) $parts[] = count($rep['credits']) . ' firma(s) de fuente'; if (!empty($rep['junk'])) $parts[] = count($rep['junk']) . ' bloque(s) de relleno'; if (!empty($rep['dupes'])) $parts[] = $rep['dupes'] . ' imagen(es) duplicada(s)'; if ($rep['footer'] === 'anadido') $parts[] = 'pie de fuente anadido (' . $rep['source'] . ')'; $msg = 'Limpiado: ' . (empty($parts) ? 'ajustes menores' : implode(', ', $parts)) . '. Creditos, imagenes distintas y embeds conservados. Respaldo guardado.'; wp_send_json_success(array('msg' => $msg, 'reload' => true)); } if ($op === 'enrich') { if (!class_exists('TN_AI_Compiler\\Enrichment\\Runner')) wp_send_json_error(array('msg' => 'AI Compiler no disponible')); tn_admin_panel_backup($post); $cleanup = function () use ($pid) { global $wpdb; $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->postmeta} WHERE post_id = %d AND (meta_key LIKE %s OR meta_key LIKE %s)", $pid, $wpdb->esc_like('_tn_enrich_') . '%', $wpdb->esc_like('_tn_autopublish_') . '%')); clean_post_cache($pid); }; try { $budget = new \TN_AI_Compiler\Enrichment\Budget((float) TN_ADMIN_PANEL_BUDGET); $runner = new \TN_AI_Compiler\Enrichment\Runner(array('budget' => $budget)); $run_id = 'manual-' . $pid . '-' . time(); $r = $runner->process_one($pid, 1, $run_id, false); } catch (\Throwable $e) { $cleanup(); wp_send_json_error(array('msg' => 'Error al enriquecer: ' . $e->getMessage())); } if ((isset($r['status']) ? $r['status'] : '') === 'review' && !empty($r['draft_id'])) { $draft = get_post((int) $r['draft_id']); if ($draft instanceof \WP_Post) { wp_update_post(array('ID' => $pid, 'post_content' => $draft->post_content, 'post_excerpt' => $draft->post_excerpt), true); wp_delete_post((int) $r['draft_id'], true); } $cleanup(); wp_send_json_success(array('msg' => 'Enriquecido: modelo ' . (isset($r['model']) ? $r['model'] : '?') . ', ratio ' . round((float) (isset($r['ratio']) ? $r['ratio'] : 0), 2) . ', $' . round((float) (isset($r['cost_usd']) ? $r['cost_usd'] : 0), 3) . '. Respaldo guardado.', 'reload' => true)); } $cleanup(); wp_send_json_error(array('msg' => 'No se completo: ' . (isset($r['reason']) ? $r['reason'] : (isset($r['status']) ? $r['status'] : 'desconocido')))); } wp_send_json_error(array('msg' => 'Operacion desconocida')); }); if (!defined('ABSPATH')) exit; if (!defined('TN_ADMIN_PANEL_BUDGET')) define('TN_ADMIN_PANEL_BUDGET', 0.50); if (!function_exists('tn_admin_panel_can')) { function tn_admin_panel_can() { return current_user_can('manage_options'); } } if (!function_exists('tn_admin_panel_backup')) { function tn_admin_panel_backup($post) { update_post_meta($post->ID, '_tn_admin_prev', base64_encode(wp_json_encode(array( 'content' => $post->post_content, 'excerpt' => $post->post_excerpt, 'status' => $post->post_status, 'time' => current_time('mysql'), )))); $force = function () { return 5; }; add_filter('wp_revisions_to_keep', $force, 99); wp_save_post_revision($post->ID); remove_filter('wp_revisions_to_keep', $force, 99); } } if (!function_exists('tn_admin_source_name')) { function tn_admin_source_name($pid) { $map = array( 'lapatilla.com' => 'Lapatilla', 'ambito.com' => 'Ámbito', 'albertonews.com' => 'AlbertoNews', 'dw.com' => 'DW', 'diarioavance.com' => 'Diario Avance', 'elnacional.com' => 'El Nacional', 'elpais.com' => 'El País', 'vidaextra.com' => 'VidaExtra', 'bbc.com' => 'BBC', 'infobae.com' => 'Infobae', 'primicia.com.ve' => 'Primicia', 'esconusted.com' => 'Es Con Usted', 'okdiario.com' => 'OkDiario', 'elpitazo.net' => 'El Pitazo', 'runrun.es' => 'Runrun.es', 'efe.com' => 'EFE', 'clarin.com' => 'Clarín', 'cnn.com' => 'CNN', 'elmundo.es' => 'El Mundo', 'rt.com' => 'RT', 'eltiempo.com' => 'El Tiempo', 'noticiasaldiayalahora.co' => 'Noticias Al Día y a la Hora', ); $link = get_post_meta($pid, 'original_link', true); if (!$link) return ''; $host = preg_replace('/^www\./', '', strtolower((string) parse_url($link, PHP_URL_HOST))); if ($host === '') return ''; foreach ($map as $d => $n) { if ($host === $d || substr($host, -strlen('.' . $d)) === '.' . $d) return $n; } $p = explode('.', $host); $i = max(0, count($p) - 2); $label = $p[$i]; if (in_array($label, array('com','co','net','org','gob','edu'), true) && count($p) >= 3) $label = $p[count($p) - 3]; return ucwords(str_replace('-', ' ', $label)); } } if (!function_exists('tn_admin_img_identity')) { function tn_admin_img_identity(DOMElement $img) { $cand = ''; foreach (array('data-src','data-lazy-src','data-lazyload','data-original','src') as $a) { $v = trim($img->getAttribute($a)); if ($v !== '' && stripos($v, 'data:image') !== 0) { $cand = $v; break; } } if ($cand === '') { $ss = trim($img->getAttribute('srcset')); if ($ss !== '') { $first = explode(',', $ss); $cand = trim(explode(' ', trim($first[0]))[0]); } } if ($cand === '') return ''; $u = html_entity_decode($cand, ENT_QUOTES); $u = preg_replace('/[?#].*$/', '', $u); $u = preg_replace('#^[a-z]+://#i', '', $u); $u = preg_replace('#^//#', '', $u); $u = preg_replace('/^www\./i', '', $u); return strtolower(rawurldecode($u)); } } if (!function_exists('tn_admin_dedupe_images')) { function tn_admin_dedupe_images(DOMDocument $doc, DOMXPath $xp) { $root = $doc->getElementById('__tnroot'); if (!$root) return 0; $tokens = array(); $walk = function ($node) use (&$walk, &$tokens) { foreach ($node->childNodes as $c) { if ($c->nodeType === XML_TEXT_NODE) { if (trim(preg_replace('/[\s\x{00a0}]+/u', ' ', $c->nodeValue)) !== '') $tokens[] = array('t' => 'text'); continue; } if (!($c instanceof DOMElement)) continue; $tag = strtolower($c->nodeName); if ($tag === 'img') { $tokens[] = array('t' => 'img', 'node' => $c); continue; } if (in_array($tag, array('iframe','video','audio','blockquote','script','embed','object','svg'), true)) { $tokens[] = array('t' => 'media'); continue; } $walk($c); } }; $walk($root); $lastId = null; $broken = true; $uniq = 0; $toRemove = array(); foreach ($tokens as $tk) { if ($tk['t'] !== 'img') { $broken = true; continue; } $idn = tn_admin_img_identity($tk['node']); if ($idn === '') $idn = "\0u" . (++$uniq); if ($lastId !== null && !$broken && $idn === $lastId) { $toRemove[] = $tk['node']; } else { $lastId = $idn; $broken = false; } } foreach ($toRemove as $imgn) { $p = $imgn->parentNode; if ($p) $p->removeChild($imgn); while ($p instanceof DOMElement && $p->getAttribute('id') !== '__tnroot' && $p->parentNode) { $hasMedia = false; foreach (array('img','iframe','video','audio','picture','blockquote','script','embed','object','svg') as $t) { if ($p->getElementsByTagName($t)->length > 0) { $hasMedia = true; break; } } $txt = trim(preg_replace('/[\s\x{00a0}]+/u', '', $p->textContent)); if (!$hasMedia && $txt === '') { $up = $p->parentNode; $up->removeChild($p); $p = $up; } else break; } } return count($toRemove); } } if (!function_exists('tn_admin_curar_safe')) { function tn_admin_curar_safe($pid, $html) { $rep = array('source' => '', 'footer' => 'ya_existe', 'credits' => array(), 'junk' => array(), 'dupes' => 0); if (trim($html) === '') return array($html, $rep); $src = tn_admin_source_name((int) $pid); $rep['source'] = $src; $has_footer = stripos($html, 'Fuente de TenemosNoticias.com:') !== false; $prev = libxml_use_internal_errors(true); libxml_clear_errors(); $doc = new DOMDocument(); $doc->loadHTML('
' . $html . '
', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET); libxml_clear_errors(); libxml_use_internal_errors($prev); $xp = new DOMXPath($doc); $rep['dupes'] = tn_admin_dedupe_images($doc, $xp); $prot = function ($n) { if (!($n instanceof DOMElement)) return false; foreach (array('img','iframe','blockquote','script','video','audio','source','picture') as $t) { if ($n->getElementsByTagName($t)->length > 0) return true; } if (preg_match('/lazyload|wp-block-embed|twitter|instagram|tiktok|fb-|embed/i', $n->getAttribute('class') . ' ' . $n->getAttribute('id'))) return true; foreach ($n->attributes as $a) { if (stripos($a->name, 'data-src') !== false) return true; } return false; }; $junk = array( '/^Publicidad( \/ Contenido Patrocinado)?$/iu', '/^Contenido Patrocinado( \/ Recomendado para ti)?$/iu', '/^Recomendado para ti$/iu', '/Para leer la nota completa/iu', '/Sigue en directo/iu', '/Venezuela atraviesa un plan de TRES FASES/iu', ); foreach (iterator_to_array($xp->query('//p|//h2|//h3|//h4')) as $n) { if (!($n instanceof DOMElement) || !$n->parentNode) continue; if ($prot($n)) continue; $t = trim(preg_replace('/\s+/u', ' ', $n->textContent)); if ($t === '') continue; $isj = false; foreach ($junk as $p) { if (preg_match($p, $t)) { $isj = true; break; } } if ($isj) { $rep['junk'][] = mb_substr($t, 0, 45); $n->parentNode->removeChild($n); continue; } if ($n->getElementsByTagName('a')->length > 0) continue; $iscred = false; if ($src !== '' && mb_strtolower($t) === mb_strtolower($src)) $iscred = true; if (!$iscred && preg_match('/^Por:?\s+[a-z0-9.\-]+\.[a-z]{2,}$/iu', $t)) $iscred = true; if ($iscred) { $rep['credits'][] = mb_substr($t, 0, 45); $n->parentNode->removeChild($n); } } foreach (iterator_to_array($xp->query('//*[@id="mc_embed_signup"]')) as $n) { if ($n instanceof DOMElement && !$prot($n) && $n->parentNode) { $rep['junk'][] = '#mc_embed_signup'; $n->parentNode->removeChild($n); } } foreach (iterator_to_array($xp->query('//p')) as $n) { if (!($n instanceof DOMElement) || !$n->parentNode) continue; if ($prot($n)) continue; if ($n->getElementsByTagName('a')->length > 0) continue; $t = trim(preg_replace('/\x{00a0}|\s+/u', '', $n->textContent)); if ($t === '') $n->parentNode->removeChild($n); } foreach (iterator_to_array($xp->query('//*[contains(@style,"padding-bottom") or contains(@style,"padding-top") or contains(@style,"aspect-ratio")]')) as $n) { if (!($n instanceof DOMElement) || !$n->parentNode) continue; $hm = $n->getElementsByTagName('img')->length || $n->getElementsByTagName('iframe')->length || $n->getElementsByTagName('video')->length || $n->getElementsByTagName('picture')->length; if ($hm) { $st = preg_replace('/(?:^|;)\s*(?:padding-bottom|padding-top|aspect-ratio)\s*:[^;]*/i', '', (string) $n->getAttribute('style')); $st = trim($st, " ;"); if ($st === '') { $n->removeAttribute('style'); } else { $n->setAttribute('style', $st); } } elseif (!$prot($n)) { $rep['junk'][] = 'aspect_vacio'; $n->parentNode->removeChild($n); } } for ($pass = 0; $pass < 2; $pass++) { foreach (iterator_to_array($xp->query('//div|//span|//section|//figure|//aside|//ul|//ol')) as $n) { if (!($n instanceof DOMElement) || !$n->parentNode) continue; if ($n->getAttribute('id') === '__tnroot') continue; if ($prot($n)) continue; if ($n->getElementsByTagName('a')->length > 0) continue; $t2 = trim(preg_replace('/\x{00a0}|\s+/u', '', $n->textContent)); if ($t2 === '') $n->parentNode->removeChild($n); } } $root = $doc->getElementById('__tnroot'); $clean = ''; if ($root) { foreach ($root->childNodes as $c) { $clean .= $doc->saveHTML($c); } } $clean = trim($clean); $clean = preg_replace('/^(?:\s*\s*)+/i', '', $clean); $clean = preg_replace('/(?:\s*\s*)+$/i', '', $clean); $clean = preg_replace('/(?:\s*){2,}/i', '
', $clean); if (!$has_footer && $src !== '') { $clean .= "\n

Fuente de TenemosNoticias.com: " . esc_html($src) . "

"; $rep['footer'] = 'anadido'; } return array($clean, $rep); } } add_action('wp_footer', function () { if (!is_singular('post') || !tn_admin_panel_can()) return; $pid = (int) get_queried_object_id(); $status = esc_html(get_post_status($pid)); $nonce = esc_attr(wp_create_nonce('tn_admin_panel_' . $pid)); $ajax = esc_url(admin_url('admin-ajax.php')); $html = << #tn-adm-panel{display:none;margin:14px 0;padding:12px;border:1px solid #e9ecef;border-radius:10px;background:#f8f9fa;box-shadow:0 3px 10px rgba(0,0,0,.06);font-family:inherit} #tn-adm-panel .tn-adm-h{font-size:12px;font-weight:700;color:#868e96;text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px} #tn-adm-panel .tn-adm-row{display:flex;flex-wrap:wrap;gap:8px} #tn-adm-panel button{flex:1 1 auto;min-width:120px;height:40px;border:none;border-radius:6px;color:#fff;font-size:14px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px} #tn-adm-panel button:disabled{opacity:.5;cursor:default} #tn-adm-b-draft{background:#868e96}#tn-adm-b-pending{background:#f08c00}#tn-adm-b-enrich{background:#1c7ed6}#tn-adm-b-clean{background:#0ca678}#tn-adm-b-headline{background:#9c36b5} #tn-adm-panel .tn-adm-st{font-weight:700;color:#212529} #tn-adm-toast-wrap{position:fixed;z-index:2147483000;right:18px;bottom:18px;display:flex;flex-direction:column;gap:10px;max-width:340px;width:calc(100vw - 36px);font-family:inherit;pointer-events:none} .tn-adm-toast{pointer-events:auto;display:flex;align-items:flex-start;gap:10px;padding:12px 13px;border-radius:10px;background:#fff;color:#212529;font-size:13.5px;line-height:1.45;box-shadow:0 10px 28px rgba(0,0,0,.16);border-left:4px solid #adb5bd;transform:translateX(120%);opacity:0;transition:transform .34s cubic-bezier(.22,1,.36,1),opacity .34s} .tn-adm-toast.tn-show{transform:translateX(0);opacity:1} .tn-adm-toast.tn-ok{border-left-color:#0ca678}.tn-adm-toast.tn-err{border-left-color:#e03131}.tn-adm-toast.tn-info{border-left-color:#1c7ed6} .tn-adm-toast .tn-ic{flex:0 0 auto;width:20px;text-align:center;font-size:15px;margin-top:1px} .tn-adm-toast.tn-ok .tn-ic{color:#0ca678}.tn-adm-toast.tn-err .tn-ic{color:#e03131}.tn-adm-toast.tn-info .tn-ic{color:#1c7ed6} .tn-adm-toast .tn-bd{flex:1 1 auto;word-break:break-word} .tn-adm-toast .tn-cl{flex:0 0 auto;cursor:pointer;color:#adb5bd;font-size:18px;line-height:1;background:none;border:none;padding:0;margin:-1px -2px 0 0;width:auto;min-width:0;height:auto} .tn-adm-toast .tn-cl:hover{color:#495057} @media (max-width:600px){#tn-adm-toast-wrap{right:10px;left:10px;bottom:10px;width:auto;max-width:none}} .tn-adm-ov{position:fixed;inset:0;z-index:2147483600;display:flex;align-items:center;justify-content:center;padding:18px;background:rgba(20,22,28,.55);opacity:0;transition:opacity .2s;font-family:inherit} .tn-adm-ov.tn-show{opacity:1} .tn-adm-ov .tn-adm-modal{width:100%;max-width:380px;background:#fff;border-radius:14px;box-shadow:0 18px 50px rgba(0,0,0,.3);padding:20px;transform:translateY(14px) scale(.97);transition:transform .22s cubic-bezier(.22,1,.36,1)} .tn-adm-ov.tn-show .tn-adm-modal{transform:translateY(0) scale(1)} .tn-adm-ov .tn-adm-mh{display:flex;align-items:center;gap:11px;margin-bottom:12px} .tn-adm-ov .tn-adm-mi{flex:0 0 auto;width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:17px} .tn-adm-ov .tn-adm-mt{font-size:17px;font-weight:700;color:#212529;line-height:1.2} .tn-adm-ov .tn-adm-mb{font-size:14px;line-height:1.5;color:#495057;margin-bottom:20px} .tn-adm-ov .tn-adm-mf{display:flex;gap:10px;justify-content:flex-end} .tn-adm-ov .tn-adm-mf button{border:none;border-radius:8px;height:42px;padding:0 18px;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;display:flex;align-items:center} .tn-adm-ov .tn-adm-no{background:#f1f3f5;color:#495057} .tn-adm-ov .tn-adm-no:hover{background:#e9ecef} .tn-adm-ov .tn-adm-yes{color:#fff} .tn-adm-ov .tn-adm-yes:hover{filter:brightness(.94)} @media (max-width:600px){.tn-adm-ov{align-items:flex-end;padding:0}.tn-adm-ov .tn-adm-modal{max-width:none;border-radius:16px 16px 0 0;padding:22px 18px calc(22px + env(safe-area-inset-bottom));transform:translateY(100%)}.tn-adm-ov.tn-show .tn-adm-modal{transform:translateY(0)}}
Admin — estado: {$status}
HTML; echo $html; }, 50); add_action('wp_ajax_tn_admin_article_action', function () { $pid = isset($_POST['pid']) ? (int) $_POST['pid'] : 0; if (!$pid || !tn_admin_panel_can() || !check_ajax_referer('tn_admin_panel_' . $pid, 'nonce', false)) { wp_send_json_error(array('msg' => 'No autorizado'), 403); } $op = isset($_POST['op']) ? sanitize_key($_POST['op']) : ''; $post = get_post($pid); if (!$post || $post->post_type !== 'post') wp_send_json_error(array('msg' => 'Articulo invalido'), 400); if (function_exists('set_time_limit')) @set_time_limit(180); if ($op === 'draft' || $op === 'pending') { $new_status = ($op === 'draft') ? 'draft' : 'pending'; $r = wp_update_post(array('ID' => $pid, 'post_status' => $new_status), true); if (is_wp_error($r)) wp_send_json_error(array('msg' => $r->get_error_message())); wp_send_json_success(array('msg' => 'Estado cambiado a ' . $new_status, 'reload' => true)); } if ($op === 'clean') { if (!function_exists('tn_admin_curar_safe')) wp_send_json_error(array('msg' => 'Limpiador no disponible')); $in = $post->post_content; list($clean, $rep) = tn_admin_curar_safe($pid, $in); if ($clean === $in) wp_send_json_success(array('msg' => 'Nada que limpiar (sin relleno ni firmas redundantes).', 'reload' => false)); foreach (array('twitter-tweet','instagram-media','tiktok-embed','fb-post',' 'Abortado por seguridad: la limpieza habria afectado un embed/imagen (' . $m . '). No se guardo nada.')); } } tn_admin_panel_backup($post); $r = wp_update_post(array('ID' => $pid, 'post_content' => $clean), true); if (is_wp_error($r)) wp_send_json_error(array('msg' => $r->get_error_message())); $parts = array(); if (!empty($rep['credits'])) $parts[] = count($rep['credits']) . ' firma(s) de fuente'; if (!empty($rep['junk'])) $parts[] = count($rep['junk']) . ' bloque(s) de relleno'; if (!empty($rep['dupes'])) $parts[] = $rep['dupes'] . ' imagen(es) duplicada(s)'; if ($rep['footer'] === 'anadido') $parts[] = 'pie de fuente anadido (' . $rep['source'] . ')'; $msg = 'Limpiado: ' . (empty($parts) ? 'ajustes menores' : implode(', ', $parts)) . '. Creditos, imagenes distintas y embeds conservados. Respaldo guardado.'; wp_send_json_success(array('msg' => $msg, 'reload' => true)); } if ($op === 'enrich') { if (!class_exists('TN_AI_Compiler\\Enrichment\\Runner')) wp_send_json_error(array('msg' => 'AI Compiler no disponible')); tn_admin_panel_backup($post); $cleanup = function () use ($pid) { global $wpdb; $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->postmeta} WHERE post_id = %d AND (meta_key LIKE %s OR meta_key LIKE %s)", $pid, $wpdb->esc_like('_tn_enrich_') . '%', $wpdb->esc_like('_tn_autopublish_') . '%')); clean_post_cache($pid); }; try { $budget = new \TN_AI_Compiler\Enrichment\Budget((float) TN_ADMIN_PANEL_BUDGET); $runner = new \TN_AI_Compiler\Enrichment\Runner(array('budget' => $budget)); $run_id = 'manual-' . $pid . '-' . time(); $r = $runner->process_one($pid, 1, $run_id, false); } catch (\Throwable $e) { $cleanup(); wp_send_json_error(array('msg' => 'Error al enriquecer: ' . $e->getMessage())); } if ((isset($r['status']) ? $r['status'] : '') === 'review' && !empty($r['draft_id'])) { $draft = get_post((int) $r['draft_id']); if ($draft instanceof \WP_Post) { wp_update_post(array('ID' => $pid, 'post_content' => $draft->post_content, 'post_excerpt' => $draft->post_excerpt), true); wp_delete_post((int) $r['draft_id'], true); } $cleanup(); wp_send_json_success(array('msg' => 'Enriquecido: modelo ' . (isset($r['model']) ? $r['model'] : '?') . ', ratio ' . round((float) (isset($r['ratio']) ? $r['ratio'] : 0), 2) . ', $' . round((float) (isset($r['cost_usd']) ? $r['cost_usd'] : 0), 3) . '. Respaldo guardado.', 'reload' => true)); } $cleanup(); wp_send_json_error(array('msg' => 'No se completo: ' . (isset($r['reason']) ? $r['reason'] : (isset($r['status']) ? $r['status'] : 'desconocido')))); } if ($op === 'headline') { if (!class_exists('TN_AI_Compiler\Gemini_Provider')) wp_send_json_error(array('msg' => 'Proveedor de IA no disponible')); $old_title = (string) $post->post_title; $old_slug = (string) $post->post_name; if (mb_strlen($old_title) <= 70 && strlen($old_slug) <= 70) { wp_send_json_success(array('msg' => 'El titulo y el slug ya tienen una longitud adecuada. Nada que curar.', 'reload' => false)); } $body_txt = trim(preg_replace('/\s+/u', ' ', (string) wp_strip_all_tags($post->post_content))); $ctx_title = mb_substr($old_title, 0, 400); $ctx_body = mb_substr($body_txt, 0, 800); $system = 'Eres editor de titulares de un portal de noticias venezolano (espanol, es_VE). Redactas titulares claros, naturales y concisos. Devuelve EXCLUSIVAMENTE un objeto JSON valido, sin texto adicional.'; $user = "El campo titulo viene contaminado con el cuerpo del articulo. A partir del material, redacta:\n1) \"title\": un titular de noticia en espanol, natural, de 45 a 70 caracteres, sin punto final, sin el nombre del medio ni del sitio, conservando hechos y entidades clave (personas, lugares, cifras).\n2) \"slug\": version corta del titular en minusculas con guiones (kebab-case), sin tildes, maximo 60 caracteres.\n\nTITULO CRUDO:\n" . $ctx_title . "\n\nCUERPO (extracto):\n" . $ctx_body . "\n\nResponde solo: {\"title\":\"...\",\"slug\":\"...\"}"; try { $provider = new \TN_AI_Compiler\Gemini_Provider(); if (!$provider->is_configured()) wp_send_json_error(array('msg' => 'IA no configurada (falta API key)')); $resp = $provider->generate_with_options(array( 'model' => 'gemini-2.5-flash-lite', 'system_instruction' => $system, 'user_content' => $user, 'temperature' => 0.4, 'max_output_tokens' => 256, 'thinking_budget' => 0, 'response_mime_type' => 'application/json', )); } catch (\Throwable $e) { wp_send_json_error(array('msg' => 'Error de IA: ' . $e->getMessage())); } $data = \TN_AI_Compiler\Gemini_Provider::decode_json((string) (isset($resp['text']) ? $resp['text'] : '')); $new_title = isset($data['title']) ? sanitize_text_field((string) $data['title']) : ''; $new_title = rtrim(trim(preg_replace('/\s+/u', ' ', $new_title)), " \t."); if ($new_title === '' || mb_strlen($new_title) < 12 || mb_strlen($new_title) > 140) { wp_send_json_error(array('msg' => 'La IA no devolvio un titular valido. No se cambio nada.')); } $base = sanitize_title($new_title); if (strlen($base) > 75) { $base = preg_replace('/-[^-]*$/', '', substr($base, 0, 75)); } if ($base === '') $base = sanitize_title($old_slug); $new_slug = wp_unique_post_slug($base, $pid, $post->post_status, $post->post_type, $post->post_parent); tn_admin_panel_backup($post); update_post_meta($pid, '_tn_admin_prev_headline', base64_encode(wp_json_encode(array( 'title' => $old_title, 'slug' => $old_slug, 'time' => current_time('mysql'), )))); $changed_slug = ($new_slug !== $old_slug); $r = wp_update_post(array('ID' => $pid, 'post_title' => $new_title, 'post_name' => $new_slug), true); if (is_wp_error($r)) wp_send_json_error(array('msg' => $r->get_error_message())); $msg = 'Titulo actualizado: ' . esc_html($new_title); if ($changed_slug && $old_slug !== '') $msg .= '. Slug nuevo: la URL anterior redirige 301 a la nueva.'; $msg .= ' Respaldo guardado.'; wp_send_json_success(array('msg' => $msg, 'reload' => true)); } wp_send_json_error(array('msg' => 'Operacion desconocida')); }); Francia registra temperaturas históricas con la mitad del país en alerta roja | elperiodico.com - Tenemos Noticias de Latinoamérica y el Mundo
Ir al contenido
Internacionales

Francia registra temperaturas históricas con la mitad del país en alerta roja | elperiodico.com

📅 🕐 hace un momento🔗 Fuente: elperiodico.com🕑 5 min de lectura
Los parisinos se lanzan al canal de San Martín para refrescarse durante la ola de calor
Compartir:

Desde hace días, Francia afronta una ola de calor histórica, en la que los termómetros parecen no tener techo. Se espera que este lunes, 49 departamentos entren en alerta máxima por altas temperaturas, es decir, la mitad del mapa francés se teñirá de color rojo, incluyendo París, mientras que otras 40 regiones pasarán a alerta naranja, según anunció este domingo Météo-France. 

Fuente de TenemosNoticias.com: www.elperiodico.com

En la sección: El Periódico – internacional

🔂 ¿Te gustó la noticia? Compártela:
Compartir:
🔗 Fuente original: elperiodico.com ·

También te puede interesar

¡Copiado al portapapeles!

Mi resumen de noticias

WhatsApp