/* ========================== Admin Page Wrapper ========================== */ function rj360_admin_wrapper(){ $nonce = wp_create_nonce('rj360'); $opt = get_option(RJ360_OPT, rj360_defaults()); $cats = get_terms(['taxonomy' => 'category', 'hide_empty' => false, 'number' => 200]); rj360_output_ui($nonce, $opt, $cats); // Llama al UI puro con datos pre-fetchados } /* ========================== Enqueue CSS solo en nuestra pantalla ========================== */ add_action('admin_enqueue_scripts', function($hook){ if ($hook !== 'toplevel_page_rj360-admin') return; if (!wp_style_is('rj360-admin-style','registered')){ wp_register_style('rj360-admin-style', false, [], RJ360_VER); } wp_enqueue_style('rj360-admin-style'); $inline = '/* ===== Admin CSS (inline) ===== */ :root{ --gap:12px; --radius:12px; --border:#e3e6ea; --bg:#f6f7f9; --card:#fff; --primary:#0366d6; --primary-ink:#fff; --muted:#6b7280; --ok:#16a34a; --warn:#b45309; --danger:#dc2626; } #rj360{max-width:1400px} #rj360 h1{margin-bottom:14px} #rj360 .rj-grid{ display:grid; grid-template-columns: 1.1fr 1.8fr 1.1fr; gap: var(--gap); align-items:start; } #rj360 .card{background:var(--card); border:1px solid var(--border); border-radius:var(--radius); overflow:hidden} #rj360 .card .inner{padding:14px 14px 18px} #rj360 .row{display:flex; gap:10px; align-items:center; margin-bottom:10px} #rj360 .row.cols-2>*, #rj360 .row.cols-3>*{flex:1} #rj360 .row.cols-3>*{min-width:0} #rj360 input[type="text"], #rj360 input[type="number"], #rj360 input[type="datetime-local"], #rj360 textarea, #rj360 select{ width:100%; padding:10px 12px; border:1px solid var(--border); border-radius:10px; background:#fff; outline:none } #rj360 textarea{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace} #rj360 .button{padding:8px 12px; border:1px solid var(--border); background:#fff; border-radius:10px; cursor:pointer} #rj360 .button:hover{background:#f3f4f6} #rj360 .button.button-primary{border-color:var(--primary); background:var(--primary); color:var(--primary-ink)} #rj360 .chips{display:flex; flex-wrap:wrap; gap:8px} #rj360 .chip{display:inline-flex; align-items:center; gap:6px; padding:6px 10px; border:1px solid var(--border); border-radius:20px; background:#fff} #rj360 .panel{display:none} #rj360 .panel.active{display:block} #rj360 .list{display:flex; flex-direction:column; gap:8px; max-height:520px; overflow:auto; border:1px dashed #e5e7eb; padding:8px; border-radius:10px} #rj360 .item a{text-decoration:none} #rj360 .preview{margin-top:12px; border:1px solid var(--border); border-radius:12px; overflow:hidden} #rj360 .preview #preview-frame{width:100%; height:540px; border:0} #rj360 .days{display:flex; gap:8px; flex-wrap:wrap} #rj360 .days-row{align-items:flex-start} @media (max-width:1280px){ #rj360 .rj-grid{ grid-template-columns: 1fr } #rj360 .preview #preview-frame{height:480px} } @media (min-width:1281px) and (max-width:1600px){ #rj360 .rj-grid{ grid-template-columns: 1fr 1.4fr 1fr } }'; if (function_exists('wp_add_inline_style')) wp_add_inline_style('rj360-admin-style', $inline); wp_enqueue_style('theme-style', get_stylesheet_uri(), [], null, 'all'); }); /* ========================== Defaults & Setup ========================== */ function rj360_defaults(){ return [ 'feeds'=>[ ['url'=>'https://prensa.jujuy.gob.ar/feed/','active'=>true], ['url'=>'https://www.argentina.gob.ar/noticias/todas-las-noticias/rss.xml','active'=>true], ], 'days'=>['mon','tue','wed','thu','fri','sat','sun'], 'safe_image_domains'=>".gob.ar\nargentina.gob.ar\njujuy.gob.ar\nwikipedia.org\nwikimedia.org\nprensa.jujuy.gob.ar" ];} function rj360_norm_feeds($fs){ $o=[]; foreach((array)$fs as $f){ if(is_array($f)&&isset($f['url'])) $o[]=['url'=>trim($f['url']),'active'=>!empty($f['active'])]; elseif(is_string($f)&&$f) $o[]=['url'=>trim($f),'active'=>true]; } return $o; } add_action('admin_init',function(){ $opt=get_option(RJ360_OPT); if(!$opt) $opt=rj360_defaults(); $opt=array_merge(rj360_defaults(),$opt); $opt['feeds']=rj360_norm_feeds($opt['feeds']); update_option(RJ360_OPT,$opt); }); /* ========================== Admin Menu ========================== */ add_action('admin_menu',function(){ add_menu_page('Reportero 360','Reportero 360','edit_posts','rj360-admin','rj360_admin_wrapper','dashicons-media-document',3); }); /* ========================== AJAX ========================== */ add_action('wp_ajax_rj360_api', function(){ if(!current_user_can('edit_posts')) wp_send_json_error('forbidden'); check_ajax_referer('rj360'); $sub = sanitize_key($_POST['sub']??''); $payload = json_decode(stripslashes($_POST['payload']??'{}'), true) ?: []; try{ switch($sub){ case 'fetch_rss': $out = rj360_ajax_fetch_rss($payload); break; case 'fetch_url': $out = rj360_ajax_fetch_url($payload); break; case 'search_posts': $out = rj360_ajax_search_posts($payload); break; case 'read_post': $out = rj360_ajax_read_post($payload); break; case 'clean_html': $out = ['html'=>rj360_clean_html($payload['html']??'')]; break; case 'publish': $out = ['id'=>rj360_publish($payload)]; break; case 'save_opt': update_option(RJ360_OPT, rj360_merge_options($payload['opt']??[])); $out=['ok'=>true]; break; default: throw new Exception('acción no soportada'); } wp_send_json_success($out); }catch(Throwable $e){ wp_send_json_error($e->getMessage()); } }); /* ========================== Helpers ========================== */ function rj360_merge_options($in){ $base = get_option(RJ360_OPT, rj360_defaults()); $in['feeds'] = rj360_norm_feeds($in['feeds']??[]); $out = array_merge($base, $in); return $out; } function rj360_host_allows($url){ $opt = get_option(RJ360_OPT, rj360_defaults()); $list = array_filter(array_map('trim', explode("\n", (string)($opt['safe_image_domains']??'')))); if(!$list) return true; $host = parse_url($url, PHP_URL_HOST); if(!$host) return false; foreach($list as $rule){ $rule = ltrim($rule); if($rule && (str_ends_with($host,$rule) || $host===$rule || (strpos($rule,'.')===0 && str_ends_with($host,$rule)))){ return true; } } return false; } function rj360_clean_html($html){ $allowed = [ 'a'=>['href'=>true,'title'=>true,'target'=>true,'rel'=>true], 'p'=>['class'=>true],'br'=>[], 'strong'=>[], 'em'=>[], 'b'=>[], 'i'=>[], 'ul'=>[],'ol'=>[],'li'=>[], 'h1'=>[],'h2'=>[],'h3'=>[],'h4'=>[],'h5'=>[],'h6'=>[], 'img'=>['src'=>true,'alt'=>true,'title'=>true,'width'=>true,'height'=>true,'loading'=>true], 'figure'=>[],'figcaption'=>[], 'blockquote'=>['cite'=>true], 'code'=>[], 'pre'=>[] ]; $html = wp_kses((string)$html, $allowed); // Quitar atributos peligrosos $html = preg_replace('/\son\w+="[^"]*"/i','',$html); // Remover iframes/embeds $html = preg_replace('#<(iframe|embed|script)[\s\S]*?\1>#i','',$html); return $html; } /* ========================== RSS ========================== */ function rj360_ajax_fetch_rss($p){ $url = esc_url_raw($p['url']??''); $limit = max(1, min( (int)($p['limit']??12), 50 )); if(!$url) throw new Exception('feed vacío'); require_once ABSPATH.'wp-includes/feed.php'; $rss = fetch_feed($url); if(is_wp_error($rss)) throw new Exception('RSS inválido'); $max = $rss->get_item_quantity($limit); $items = []; for($i=0; $i<$max; $i++){ $it = $rss->get_item($i); $items[] = [ 'title' => (string)$it->get_title(), 'link' => (string)$it->get_link(), 'date' => (string)$it->get_date('c'), ]; } return ['items'=>$items]; } /* ========================== URL Fetch (simple) ========================== */ function rj360_ajax_fetch_url($p){ $url = esc_url_raw($p['url']??''); if(!$url) throw new Exception('URL vacía'); $res = wp_remote_get($url, ['timeout'=>12,'redirection'=>5,'user-agent'=>'RJ360/3.0']); if(is_wp_error($res)) throw new Exception('No se pudo descargar'); $html = wp_remote_retrieve_body($res); if(!$html) throw new Exception('Sin contenido'); // Title $title=''; if(preg_match('~
'.esc_html($subtitle).'
' : '').$content, 'post_status' => $status, 'post_author' => $author, 'post_type' => 'post', ]; if($slug) $postarr['post_name'] = $slug; if($excerpt)$postarr['post_excerpt'] = $excerpt; if($date) $postarr['post_date'] = date('Y-m-d H:i:s', strtotime($date)); $id = wp_insert_post($postarr, true); if(is_wp_error($id)) throw new Exception($id->get_error_message()); if($cats) wp_set_post_categories($id, $cats, false); if($tags) wp_set_post_tags($id, $tags, false); // Meta JSON $meta = (array)($p['meta']??[]); foreach($meta as $k=>$v){ if(is_scalar($v) || is_null($v)) update_post_meta($id, sanitize_key($k), maybe_serialize($v)); } // Imagen if($image){ if(!rj360_host_allows($image)) throw new Exception('Dominio de imagen no permitido'); require_once ABSPATH.'wp-admin/includes/media.php'; require_once ABSPATH.'wp-admin/includes/file.php'; require_once ABSPATH.'wp-admin/includes/image.php'; $att_id = media_sideload_image($image, $id, $title, 'id'); if(!is_wp_error($att_id)) set_post_thumbnail($id, $att_id); } return $id; } /* ========================== Media helpers (fallback) ========================== */ if(!function_exists('rj360_media_sideload')){ function rj360_media_sideload($url,$post=0,$desc=''){ if(!$url) return 0; require_once ABSPATH.'wp-admin/includes/file.php'; require_once ABSPATH.'wp-admin/includes/media.php'; require_once ABSPATH.'wp-admin/includes/image.php'; $tmp=download_url($url); if(is_wp_error($tmp)) return 0; $mime=function_exists('mime_content_type')?mime_content_type($tmp):(wp_check_filetype($url)['type']??'image/jpeg'); $name=basename(parse_url($url,PHP_URL_PATH))?:'media.jpg'; $file=['name'=>$name,'type'=>$mime,'tmp_name'=>$tmp,'size'=>@filesize($tmp),'error'=>0]; $id=media_handle_sideload($file,$post,$desc); if(is_wp_error($id)){ @unlink($tmp); return 0; } @unlink($tmp); $path=get_attached_file($id); if($path&&file_exists($path)){ $meta=wp_generate_attachment_metadata($id,$path); if($meta) wp_update_attachment_metadata($id,$meta); } return $id; } }