getSubfeeds($feed_url); if (is_wp_error($subfeeds)) { wp_send_json_error($subfeeds->get_error_message()); wp_die(); } wp_send_json_success($subfeeds); wp_die(); } /** * Normaliza las URLs eliminando barras adicionales, * convirtiendo a minúsculas y quitando la barra final. * * @param string $url URL a normalizar. * @return string URL normalizada. */ function aife_normalize_feed_url($url) { return rtrim( strtolower( preg_replace('/([^:])\/+/', '$1/', $url) ), '/' ); } add_shortcode('aife_post_meta', function($atts){ global $post; // Aseguramos que solo aceptamos una clave válida $attribs = shortcode_atts([ 'key' => '', ], $atts); $meta_key = sanitize_key( $attribs['key'] ); // Si la clave está vacía, no se devuelve contenido if ( empty( $meta_key ) ) { return ''; } // Recuperamos el valor de la meta y lo saneamos antes de mostrarlo $value = get_post_meta( $post->ID, $meta_key, true ); if ( is_string( $value ) ) { // Permitir solo HTML seguro. Si prefieres eliminar todo HTML usa esc_html() en su lugar. $value = wp_kses_post( $value ); } return $value; }); /** * Verifica si un campo personalizado existe en el sistema de WordPress. * * Evalúa si el campo: * - Está registrado oficialmente con register_post_meta() * - Tiene al menos una entrada guardada en postmeta * - Está definido por ACF (si ACF está activo) * * @param string $meta_key * @return bool */ function aife_meta_key_existe( $meta_key ) { global $wp_meta_keys, $wpdb; // 1. ¿Está registrado oficialmente con register_post_meta? if ( isset( $wp_meta_keys['post']['post'] ) ) { if ( array_key_exists( $meta_key, $wp_meta_keys['post']['post'] ) ) { return true; } } // 2. ¿Existe al menos una entrada en postmeta con esa clave? // $meta_en_bbdd = $wpdb->get_var( $wpdb->prepare( // "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key = %s LIMIT 1", //$meta_key //) ); if ( ! empty( $meta_en_bbdd ) ) { return true; } // 3. ¿Está definido por ACF? if ( function_exists( 'get_field_object' ) ) { $acf_field = get_field_object( $meta_key, 'post' ); if ( ! empty( $acf_field ) ) { return true; } } return false; } function aife_comprobar_api_rest_activa() { $respuesta = wp_remote_get(home_url('/wp-json'), ['redirection' => 0]); if (is_wp_error($respuesta)) { return false; } $codigo_respuesta = wp_remote_retrieve_response_code($respuesta); if ($codigo_respuesta !== 200) { return false; } $cuerpo = wp_remote_retrieve_body($respuesta); // Intentar decodificar el JSON $json = json_decode($cuerpo, true); // Si no es un JSON válido, la API REST no está operativa if (json_last_error() !== JSON_ERROR_NONE) { return false; } // Opcionalmente verifica un campo típico del JSON de WP REST API if (empty($json['namespaces'])) { return false; } return true; } function aife_get_config_feed_y_general_from_aife_original_id($aife_original_post_id){ $config = array(); // Cargar config_general, ya suele estar en el formato correcto. $config_general = get_option( 'aife_settings', [] ); foreach ($config_general as $key => $value) { $config['config_general'][$key] = maybe_unserialize($value); } $_source_feed_id = get_post_meta( $aife_original_post_id , '_source_feed_id', true ); if($_source_feed_id){ $raw_config_feed = get_post_meta( $_source_feed_id ); foreach ($raw_config_feed as $key => $value) { if (is_array($value) && count($value) === 1) { $unserialized = maybe_unserialize($value[0]); $config['config_feed'][$key] = $unserialized; } else { // Si es un array con más de un valor, mantenerlo tal cual. $config['config_feed'][$key] = $value; } } }else{ $config['config_feed'] = $config['config_general']; } return $config; } /** * Obtiene los datos básicos de un post para enviarlos a OpenAI. * * @param int $post_id * @return array|WP_Error */ function aife_get_post_data($params) { if (empty($params['post_id'])) { return new WP_Error('falta_post_id', 'Falta post_id.'); } $post_id = absint($params['post_id']); $post = get_post($post_id); if (!$post) { return new WP_Error('post_no_encontrado', __('Post no encontrado.', 'ai-feeds')); } return [ 'title' => $post->post_title, 'excerpt' => wp_strip_all_tags(get_the_excerpt($post)), 'content' => trim(preg_replace('/\s+/', ' ', preg_replace("/[\r\n]+/", "\n", ($post->post_content)))), 'pub_date' => get_post_meta($post_id, '_pub_date', true), 'categories' => get_post_meta($post_id, '_categories', true), 'original_url' => get_post_meta($post_id, '_original_url', true), 'config' => aife_get_config_feed_y_general_from_aife_original_id($post_id), 'user' => wp_get_current_user()->display_name, ]; } /** * Devuelve claramente las categorías de WP con ruta completa e ID. * * @param int $limit * @return array */ function aife_get_categorias_con_ruta($limit = 200) { $categories = get_categories([ 'number' => $limit, 'orderby' => 'count', 'order' => 'DESC', 'hide_empty' => false, 'fields' => 'ids' ]); $categorias_con_ruta = []; foreach ($categories as $cat_id) { $categoria = get_category($cat_id); $ruta = [$categoria->name]; while ($categoria->parent) { $categoria = get_category($categoria->parent); array_unshift($ruta, $categoria->name); } $categorias_con_ruta[] = [ 'id' => $cat_id, 'ruta' => implode(' > ', $ruta) ]; } return $categorias_con_ruta; } if ( is_admin() ) { add_action('transition_post_status', 'aife_gestionar_eliminacion_post_original', 10, 3); } function aife_gestionar_eliminacion_post_original($new_status, $old_status, $post) { // Verificamos claramente si el post es del tipo estándar 'post' if ($post->post_type !== 'post') { return; } // Proceder claramente solo si el post pasa a la papelera o se elimina permanentemente if (in_array($new_status, ['trash', 'delete'], true)) { $feed_original_id = get_post_meta($post->ID, '_post_from_ai_feed', true); if (!empty($feed_original_id) && get_post_type($feed_original_id) === 'aife_original') { wp_delete_post($feed_original_id, true); // Elimina el original definitivamente } } } /** * Elimina claramente enlaces HTML de un texto. * * @param string $content Contenido HTML original. * @return string Contenido sin enlaces. */ function aife_remove_links(string $content): string { return preg_replace('/]*>(.*?)<\/a>/is', '$1', $content); } function aife_add_nofollow_links($content) { return preg_replace_callback('//i', function ($matches) { $anchorTag = $matches[0]; // Si ya tiene un rel, lo modificamos claramente if (stripos($anchorTag, 'rel=') !== false) { // Añadimos nofollow a rel existente if (preg_match('/rel=[\'"](.*?)[\'"]/i', $anchorTag, $relMatch)) { $relValues = array_map('trim', explode(' ', $relMatch[1])); if (!in_array('nofollow', $relValues)) { $relValues[] = 'nofollow'; } $newRel = 'rel="' . implode(' ', $relValues) . '"'; $anchorTag = preg_replace('/rel=[\'"].*?[\'"]/i', $newRel, $anchorTag); } } else { // Si no tiene rel, se lo añadimos claramente $anchorTag = preg_replace('/