id = $data; } return $this->id; } /** Accessor a la propiedad homónima */ public function fecha( $data = null ) { if ( ! is_null( $data ) ) { $this->fecha = $data; } return $this->fecha; } /** Accessor a la propiedad homónima */ public function plugin_version( $data = null ) { if ( ! is_null( $data ) ) { $this->plugin_version = $data; } return $this->plugin_version; } /** Accessor a la propiedad homónima */ public function generador( $data = null ) { if ( ! is_null( $data ) ) { $this->generador = $data; } return $this->generador; } /** Accessor a la propiedad homónima */ public function modelo( $data = null ) { if ( ! is_null( $data ) ) { $this->modelo = $data; } return $this->modelo; } /** Accessor a la propiedad homónima */ public function idioma( $data = null ) { if ( ! is_null( $data ) ) { $this->idioma = $data; } return $this->idioma; } /** Accessor a la propiedad homónima */ public function longitud_solicitada( $data = null ) { if ( ! is_null( $data ) ) { $this->longitud_solicitada = $data; } return $this->longitud_solicitada; } /** Accessor a la propiedad homónima */ public function tono( $data = null ) { if ( ! is_null( $data ) ) { $this->tono = $data; } return $this->tono; } /** Accessor a la propiedad homónima */ public function mensaje_sistema( $data = null ) { if ( ! is_null( $data ) ) { $this->mensaje_sistema = $data; } return $this->mensaje_sistema; } /** Accessor a la propiedad homónima */ public function contexto( $data = null ) { if ( ! is_null( $data ) ) { $this->contexto = $data; } return $this->contexto; } /** Accessor a la propiedad homónima */ public function titulo( $data = null ) { if ( ! is_null( $data ) ) { $this->titulo = $data; } return $this->titulo; } /** Accessor a la propiedad homónima */ public function directrices_adicionales( $data = null ) { if ( ! is_null( $data ) ) { $this->directrices_adicionales = $data; } return $this->directrices_adicionales; } /** Accessor a la propiedad homónima */ public function prompt( $data = null ) { if ( ! is_null( $data ) ) { $this->prompt = $data; } return $this->prompt; } /** Accessor a la propiedad homónima */ public function solicitud( $data = null ) { if ( ! is_null( $data ) ) { $this->solicitud = $data; } return $this->solicitud; } /** Accessor a la propiedad homónima */ public function respuesta( $data = null ) { if ( ! is_null( $data ) ) { $this->respuesta = $data; } return $this->respuesta; } /** Accessor a la propiedad homónima */ public function error( $data = null ) { if ( ! is_null( $data ) ) { $this->error = $data; } return $this->error; } /** Accessor a la propiedad homónima */ public function contenido( $data = null ) { if ( ! is_null( $data ) ) { $this->contenido = $data; } return $this->contenido; } /** Accessor a la propiedad homónima */ public function meta_description( $data = null ) { if ( ! is_null( $data ) ) { $this->meta_description = $data; } return $this->meta_description; } /** Accessor a la propiedad homónima */ public function meta_keywords( $data = null ) { if ( ! is_null( $data ) ) { $this->meta_keywords = $data; } return $this->meta_keywords; } /** Accessor a la propiedad homónima */ public function imagen_id( $data = null ) { if ( ! is_null( $data ) ) { $this->imagen_id = $data; } return $this->imagen_id; } /** Accessor a la propiedad homónima */ public function valida( $data = null ) { if ( ! is_null( $data ) ) { $this->valida = $data; } return $this->valida; } /** Accessor a la propiedad homónima */ public function validacion_errores( $data = null ) { if ( ! is_null( $data ) ) { $this->validacion_errores = $data; } return $this->validacion_errores; } /** Accessor a la propiedad homónima */ public function validacion_advertencias( $data = null ) { if ( ! is_null( $data ) ) { $this->validacion_advertencias = $data; } return $this->validacion_advertencias; } /** Accessor a la propiedad homónima */ public function validacion_notificaciones( $data = null ) { if ( ! is_null( $data ) ) { $this->validacion_notificaciones = $data; } return $this->validacion_notificaciones; } /** Accessor a la propiedad homónima */ public function validada_por( $data = null ) { if ( ! is_null( $data ) ) { $this->validada_por = $data; } return $this->validada_por; } /** Accessor a la propiedad homónima */ public function validada_fecha( $data = null ) { if ( ! is_null( $data ) ) { $this->validada_fecha = $data; } return $this->validada_fecha; } /** Accessor a la propiedad homónima */ public function autor( $data = null ) { if ( ! is_null( $data ) ) { $this->autor = $data; } return $this->autor; } /** Accessor a la propiedad homónima */ public function publicada_post_id( $data = null ) { if ( ! is_null( $data ) ) { $this->publicada_post_id = $data; } return $this->publicada_post_id; } /** Accessor a la propiedad homónima */ public function publicable_slug( $data = null ) { if ( ! is_null( $data ) ) { $this->publicable_slug = $data; } return $this->publicable_slug; } /** Accessor a la propiedad homónima */ public function publicada_fecha( $data = null ) { if ( ! is_null( $data ) ) { $this->publicada_fecha = $data; } return $this->publicada_fecha; } /** Constructor de la clase. * * Inicializa la instancia de la clase y limpia las propiedades internas. */ public function __construct() { $this->limpiar(); } /** Vacía las propiedades para reutilizar el objeto */ public function limpiar() { $this->id = null; $this->fecha = '0000-00-00 00:00:00'; $this->plugin_version = ''; $this->generador = ''; $this->modelo = ''; $this->idioma = ''; $this->longitud_solicitada = 0; $this->tono = ''; $this->mensaje_sistema = ''; $this->contexto = ''; $this->titulo = ''; $this->directrices_adicionales = ''; $this->prompt = ''; $this->solicitud = ''; $this->respuesta = ''; $this->error = 0; $this->contenido = ''; $this->meta_description = ''; $this->meta_keywords = ''; $this->imagen_id = ''; $this->valida = 0; $this->validacion_errores = ''; $this->validacion_advertencias = ''; $this->validacion_notificaciones = ''; $this->validada_por = ''; $this->validada_fecha = '0000-00-00 00:00:00'; $this->autor = ''; $this->publicada_post_id = 0; $this->publicable_slug = ''; $this->publicada_fecha = '0000-00-00 00:00:00'; } /** Método para grabar las propiedades a la base de datos. */ public function grabar() { global $wpdb; // Objeto global de WordPress para acceder a la base de datos. // Retira la clave de API de la solicitud por seguridad. $solicitud_anonimizada = $this->solicitud; $solicitud_anonimizada['headers']['Authorization'] = 'Bearer sk-my-hidden-api-key'; $datos = array( 'fecha' => $this->fecha, 'plugin_version' => $this->plugin_version, 'generador' => $this->generador, 'modelo' => $this->modelo, 'idioma' => $this->idioma, 'longitud_solicitada' => $this->longitud_solicitada, 'tono' => $this->tono, 'mensaje_sistema' => $this->mensaje_sistema, 'contexto' => $this->contexto, 'titulo' => $this->titulo, 'directrices_adicionales' => $this->directrices_adicionales, 'prompt' => $this->prompt, 'solicitud' => wp_json_encode( $solicitud_anonimizada ), 'respuesta' => wp_json_encode( $this->respuesta ), 'error' => $this->error, 'contenido' => $this->contenido, 'meta_description' => $this->meta_description, 'meta_keywords' => $this->meta_keywords, 'imagen_id' => $this->imagen_id, 'valida' => $this->valida, 'validacion_errores' => wp_json_encode( $this->validacion_errores ), 'validacion_advertencias' => wp_json_encode( $this->validacion_advertencias ), 'validacion_notificaciones' => wp_json_encode( $this->validacion_notificaciones ), 'validada_por' => $this->validada_por, 'validada_fecha' => $this->validada_fecha, 'autor' => $this->autor, 'publicada_post_id' => $this->publicada_post_id, 'publicable_slug' => $this->publicable_slug, 'publicada_fecha' => $this->publicada_fecha, ); $tabla = aicc_tabla_generaciones(); if ( $this->id ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $resultado = $wpdb->update( $tabla, $datos, array( 'id' => $this->id ) ); } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $resultado = $wpdb->insert( $tabla, $datos ); $this->id = $wpdb->insert_id; } return ( false !== $resultado ); } /** Método para cargar las propiedades desde la base de datos. * Cargar una fila dado el id. * * @param int|null $id ID de la generación de artículo a cargar (opcional). * Si no se proporciona, se utiliza el valor del atributo id de la instancia. * @return bool Retorna true si la carga fue exitosa, false en caso contrario. */ public function cargar( $id ) { global $wpdb; // Si no se indica id, se carga el de la instancia. if ( ! $id ) { $id = $this->id; } $tabla = aicc_tabla_generaciones(); // Consulta SQL para obtener las propiedades de la fila con el id correspondiente. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $fila = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$tabla} WHERE id = %d", $id ) ); // Si la consulta no devuelve resultados, devolvemos false. if ( is_null( $fila ) ) { return false; } // Asignamos los valores recuperados a las propiedades del objeto. // https://www.php.net/manual/es/function.json-decode.php Observar parámetros. $this->id = $fila->id; $this->fecha = $fila->fecha; $this->plugin_version = $fila->plugin_version; $this->generador = $fila->generador; $this->modelo = $fila->modelo; $this->idioma = $fila->idioma; $this->longitud_solicitada = $fila->longitud_solicitada; $this->tono = $fila->tono; $this->mensaje_sistema = $fila->mensaje_sistema; $this->contexto = $fila->contexto; $this->titulo = $fila->titulo; $this->directrices_adicionales = $fila->directrices_adicionales; $this->prompt = $fila->prompt; $this->solicitud = json_decode( $fila->solicitud, true ); $this->respuesta = json_decode( $fila->respuesta, true ); $this->error = $fila->error; $this->contenido = $fila->contenido; $this->meta_description = $fila->meta_description; $this->meta_keywords = $fila->meta_keywords; $this->imagen_id = $fila->imagen_id; $this->valida = $fila->valida; $this->validacion_errores = json_decode( $fila->validacion_errores, true ); $this->validacion_advertencias = json_decode( $fila->validacion_advertencias, true ); $this->validacion_notificaciones = json_decode( $fila->validacion_notificaciones, true ); $this->validada_por = $fila->validada_por; $this->validada_fecha = $fila->validada_fecha; $this->autor = $fila->autor; $this->publicada_post_id = $fila->publicada_post_id; $this->publicable_slug = $fila->publicable_slug; $this->publicada_fecha = $fila->publicada_fecha; return true; } /** URL de WP para editar el borrador. */ public function url_editar_post() { $url = ''; if ( $this->publicada_post_id ) { $url = get_edit_post_link( $this->publicada_post_id ); } return $url; } /** Crea un borrador con la generación. * * Esta función crea un borrador de un post en WordPress utilizando la información de la clase. * Incorpora el contenido de $description al excerpt del post, asigna categorías, etiquetas y * la imagen destacada si están disponibles. * * @return int $post_id ID del post creado, o 0 si falla la creación. */ public function crear_borrador() { // Probablemente no haya nada a automatizar con las categorías o haría $post_category = array( 'category1', 'category2' ). $post_category = array(); // Toma el posible valor de las metaetiquetas. $description = $this->meta_description; $keywords = $this->meta_keywords; // Toma las metakeywords como tags. $post_tags = $keywords; // Toma la imagen destacada. $imagen_id = $this->imagen_id; // Crea el slug a partir del título eliminando las stopwords. // Primero lo prepara del modo oficial. $slug = sanitize_title( $this->titulo ); // Divide el candidato a slug en un array de palabras utilizando los guiones como separador. $palabras = explode( '-', $slug ); // Quita las stopwords. $palabras = aicc_elimina_stopwords( $palabras, $this->idioma ); // Reconstruye el slug con las palabras restantes, si quedó algo. if ( ! empty( $palabras ) ) { $slug = implode( '-', $palabras ); } $new_post = array( 'post_title' => $this->titulo, 'post_name' => $slug, 'post_content' => wp_kses_post( $this->contenido ), 'post_status' => 'draft', 'post_category' => $post_category, 'tags_input' => $post_tags, 'post_excerpt' => $description, // Añade el contenido de $description al excerpt del post. ); $post_id = wp_insert_post( $new_post ); if ( $post_id > 0 ) { $this->publicada_post_id = $post_id; $this->publicada_fecha = current_time( 'Y-m-d H:i:s' ); $this->grabar(); // Metaetiquetas. $this->add_seo_meta_data( $post_id, $keywords, $description ); // ID de la imagen destacada si la hay. if ( $imagen_id ) { set_post_thumbnail( $post_id, $imagen_id ); } } return $post_id; } /** * Borrar una fila dado el id. * * @param int|null $id ID de la generación de artículo a eliminar (opcional). * Si no se proporciona, se utiliza el valor del atributo id de la instancia. * @return bool Retorna true si la eliminación fue exitosa, false en caso contrario. */ public function borrar( $id = null ) { global $wpdb; // Si no se indica id, se borra el de la instancia. if ( ! $id ) { $id = $this->id; } $tabla = aicc_tabla_generaciones(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $result = $wpdb->delete( $tabla, array( 'id' => $id ) ); if ( false !== $result ) { $this->id = null; return true; } else { return false; } } /** Ver el contenido de un artículo. */ public function ver_html() { /* translators: %1$s: Short date, %2$s: AI model. */ $html = esc_html( sprintf( __( 'Created: %1$s by %2$s', 'ai-content-creator' ), $this->fecha_hora_corta( $this->fecha ), $this->modelo ) ); if ( $this->generador ) { $html .= esc_html( ' (' . $this->generador . ')' ); } $html .= "
\n"; $html .= '

' . __( 'Title', 'ai-content-creator' ) . ': ' . esc_html( $this->titulo ) . "

\n"; $html .= $this->ver_errores_html(); if ( $this->imagen_id ) { $html .= '

' . __( 'Featured image', 'ai-content-creator' ) . ':

'; $html .= '

'; $html .= wp_get_attachment_image( $this->imagen_id, 'large', false, array( 'class' => 'aicc-responsive-img' ) ); $html .= "

\n"; } elseif ( aicc_modo_debug() ) { $html .= ' ' . __( 'No featured image selected', 'ai-content-creator' ) . ".
\n"; } if ( $this->contenido ) { $html .= '

' . __( 'Content', 'ai-content-creator' ) . ':

'; $html .= $this->iframe_contenido_html(); } elseif ( aicc_modo_debug() ) { $html .= ' ' . __( 'No content', 'ai-content-creator' ) . ".
\n"; } return $html; } /** Ver la lista de errores. */ public function ver_errores_html() { $html = ''; $errores_acumulados = array(); if ( $this->error_proceso_api() ) { $errores_acumulados[] = __( 'Error', 'ai-content-creator' ) . ' HTTP: ' . esc_html( $this->error_proceso_api() ); } if ( $this->error_respuesta_ai() ) { $errores_acumulados[] = __( 'Error', 'ai-content-creator' ) . ' IA: ' . esc_html( $this->error_respuesta_ai() ); } if ( $this->error_finalizacion_ai() ) { $errores_acumulados[] = __( 'Article termination reason', 'ai-content-creator' ) . ': ' . esc_html( $this->error_finalizacion_ai() ); } if ( ! empty( $this->validacion_errores ) ) { $errores_acumulados = array_merge( $errores_acumulados, $this->validacion_errores ); } // Escapa el html antes de agregar el icono, que sí ha de aplicarse como html. // Errores. $errores_acumulados_html = array(); if ( ! empty( $errores_acumulados ) ) { foreach ( $errores_acumulados as $error_acumulado ) { $errores_acumulados_html[] = esc_html( $error_acumulado ); } } // Advertencias. $advertencias_html = array(); if ( ! empty( $this->validacion_advertencias ) ) { foreach ( $this->validacion_advertencias as $advertencia ) { $advertencias_html[] = esc_html( $advertencia ); } } // Notificaciones. $notificaciones_html = array(); if ( ! empty( $this->validacion_notificaciones ) ) { foreach ( $this->validacion_notificaciones as $notificacion ) { $notificaciones_html[] = esc_html( $notificacion ); } } if ( ! empty( $errores_acumulados ) ) { $html .= $this->icono_aviso_html( 'error' ) . ' '; $html .= nl2br( join( "\n" . $this->icono_aviso_html( 'error' ) . ' ', $errores_acumulados_html ) ); $html .= "
\n"; } elseif ( aicc_modo_debug() ) { $html .= $this->icono_aviso_html( 'notificacion' ) . ' ' . __( 'No errors found', 'ai-content-creator' ) . ".
\n"; } if ( ! empty( $advertencias_html ) ) { $html .= $this->icono_aviso_html( 'advertencia' ) . ' '; $html .= nl2br( join( "\n" . $this->icono_aviso_html( 'advertencia' ) . ' ', $advertencias_html ) ); $html .= "
\n"; } elseif ( aicc_modo_debug() ) { $html .= $this->icono_aviso_html( 'notificacion' ) . ' ' . __( 'No warnings found', 'ai-content-creator' ) . ".
\n"; } if ( ! empty( $this->validacion_notificaciones ) ) { $html .= $this->icono_aviso_html( 'notificacion' ) . ' '; $html .= nl2br( join( "\n" . $this->icono_aviso_html( 'notificacion' ) . ' ', $notificaciones_html ) ); $html .= "
\n"; } elseif ( aicc_modo_debug() ) { $html .= $this->icono_aviso_html( 'notificacion' ) . ' ' . __( 'No notifications found', 'ai-content-creator' ) . ".
\n"; } // Agrega un salto de línea final si hay contenido. if ( $html ) { $html .= "
\n"; } return $html; } /** Ver la solicitud de generación. */ public function ver_solicitud() { // Solo se emplea en modo debug. $html = __( 'Request', 'ai-content-creator' ) . ': ' . nl2br( esc_html( var_export( $this->solicitud, true ) ) ) . "
\n"; return $html; } /** Ver la respuesta a la generación. */ public function ver_respuesta() { // Solo se emplea en modo debug. $html = __( 'Response', 'ai-content-creator' ) . ': ' . nl2br( esc_html( var_export( $this->respuesta, true ) ) ) . "
\n"; return $html; } /** Procesa la respuesta de la comunicación consultando a la API, tomando de ahí los posibles errores y el contenido de . */ public function procesar_respuesta_auto() { if ( $this->error_proceso_api() ) { // Errores HTTP. $this->error( 1 ); } else { // Errores indicados expresamente por la AI. if ( $this->error_respuesta_ai() ) { $this->error( 1 ); } else { // Contenido del artículo y posible error si se le detecta alguno. list( $article_text, $estado_error ) = aicc_procesar_respuesta_auto( $this ); $this->contenido( $article_text ); $this->error( $estado_error ); $this->extrae_metaetiquetas(); } } } /** Toma las metaetiqetas de la respuesta. */ public function extrae_metaetiquetas() { $modelo = $this->modelo(); $respuesta = $this->respuesta(); // La respuesta es un array, toma el cuerpo de la respuesta HTTP. $body = wp_remote_retrieve_body( $respuesta ); $response_body = json_decode( $body, true ); $respuesta_mensaje = aicc_contenido_mensaje_respuesta( $modelo, $response_body ); // Ya se puede considerar contenido el mensaje y empezar a procesarlo. $this->meta_description( aicc_extrae_meta_etiqueta( $respuesta_mensaje, 'description' ) ); $this->meta_keywords( aicc_extrae_meta_etiqueta( $respuesta_mensaje, 'keywords' ) ); } /** * Validar el contenido de un artículo automáticamente. * He visto respuestas que son solo repeticiones consecutivas de la pregunta, igual o cambiando el h1 por h2, quizás en temperaturas de 0.1 y 0.2 en el modelo davici de GPT-3 * en 0.5 baja a h3, h4, h5... y quitando los

* y hace diveros h2 con variaciones del título... sin cerrar el

*/ public function validar_contenido_auto() { $this->valida = aicc_validar_contenido_auto( $this->contenido ); // Toma la lista de errores de validación. $this->validacion_errores = aicc_errores_validacion_contenido( $this->contenido ); $this->validacion_advertencias = aicc_advertencias_validacion_contenido( $this->contenido ); $this->validacion_notificaciones = aicc_notificaciones_validacion_contenido( $this->contenido ); $this->validada_por = aicc_validador_auto_actual(); $this->validada_fecha = current_time( 'Y-m-d H:i:s' ); return $this->valida; } /** Texto del posible error en el proceso. * * @return string El texto con el error si lo hubo, vacío en caso contrario. */ public function error_proceso_api() { $error_proceso_api = ''; if ( is_wp_error( $this->respuesta ) ) { // Típicamente errores en la comunicación HTTP. $error_proceso_api = $this->respuesta->get_error_message(); } return $error_proceso_api; } /** Posible error indicado como respuesta de la IA. * * @return string El texto con el error si lo hubo, vacío en caso contrario. */ public function error_respuesta_ai() { $error_respuesta_ai = ''; if ( ! is_wp_error( $this->respuesta ) ) { $body = wp_remote_retrieve_body( $this->respuesta ); $respuesta_body = json_decode( $body, true ); if ( isset( $respuesta_body['error'] ) ) { $error_respuesta_ai = $respuesta_body['error']['message']; // Algunos errores de la AI, siendo estándar y conocidos, los puedo traducir como constantes. $error_respuesta_ai = aicc_traduce_error_externo( $error_respuesta_ai ); } } return $error_respuesta_ai; } /** Posible error indicado como causa de finalización de la IA. * * @return string El texto con el error si lo hubo, vacío en caso contrario. */ public function error_finalizacion_ai() { $error_finalizacion_ai = ''; $body = wp_remote_retrieve_body( $this->respuesta ); $response_body = json_decode( $body, true ); $error_finalizacion_ai = aicc_causa_fin_respuesta( $this->generador, $response_body ); if ( 'stop' === $error_finalizacion_ai ) { // stop indica la terminación normal. $error_finalizacion_ai = ''; } return $error_finalizacion_ai; } /** HTML con el iframe del artículo y el JS que lo aproxima a la altura requerida. * * @return string El HTML con el IFRAME y el JS. */ public function iframe_contenido_html() { // Uso de sandbox admitiendo tan solo allow-same-origin para poder redimensionar la altura del marco. $html = '' . "\n"; // Script para ajustar la altura del frame en el contenedor padre. $html .= "\n"; return $html; } /** HTML con la apertura y cabecera de la lista de generaciones de artículos. * * @return string El HTML con el inicio de tabla y el TR de los encabezados de la lista. */ public function cabecera_tabla_lista_html() { $html = '' . "\n"; $html .= "\n"; $html .= '\n"; $html .= '\n"; $html .= '\n"; $html .= '\n"; $html .= '\n"; $html .= '\n"; $html .= '\n"; $html .= '\n"; $html .= "\n"; return $html; } /** HTML con la fila de la lista que corresponde a la generación del artículo. * * @return string El HTML con el TR del artículo como elemento de la lista. */ public function elemento_tabla_lista_html() { $html = ''; $html .= "\n"; $html .= '\n"; $html .= '\n"; $html .= '\n"; $html .= '\n"; // Ver. $html .= '\n"; // Estado. $html .= '\n"; // Acción. En función del estado del artículo y del posprocesaror, indica la acción recomendada. $html .= '\n"; // Borrar. $html .= '\n"; $html .= "\n"; return $html; } /** Por simple coherencia, HTML del cierre de la tabla de la lista de artículos como pie. * * @return string El HTML del cierre de la tabla con la lista. */ public function pie_tabla_lista_html() { return "
' . __( 'ID', 'ai-content-creator' ) . "' . __( 'Date', 'ai-content-creator' ) . "' . __( 'Model', 'ai-content-creator' ) . "' . __( 'Title', 'ai-content-creator' ) . "' . __( 'View', 'ai-content-creator' ) . "' . __( 'Status', 'ai-content-creator' ) . "' . __( 'Action', 'ai-content-creator' ) . "' . __( 'Delete', 'ai-content-creator' ) . "
'; $html .= esc_html( $this->id ); $html .= "'; $html .= esc_html( $this->fecha_hora_corta( $this->fecha ) ); $html .= "'; $html .= esc_html( aicc_denominacion_modelo( $this->modelo ) ); $html .= "'; if ( $this->titulo ) { $html .= esc_html( $this->titulo ); } else { $html .= '[' . esc_html( __( 'Untitled', 'ai-content-creator' ) ) . ']'; } $html .= "'; $html .= ' '; $html .= $this->dashicon_html( 'dashicons-visibility', __( 'View', 'ai-content-creator' ), 'text-decoration:none;' ); $html .= ''; $html .= "'; $html .= $this->icono_estado_html(); $html .= "'; $html .= $this->icono_accion_html(); $html .= "'; if ( ! $this->publicada_post_id ) { $tooltip = __( 'Delete', 'ai-content-creator' ); $html .= ' '; $html .= $this->dashicon_html( 'dashicons-trash', $tooltip, 'text-decoration:none;' ); $html .= ''; } $html .= "
\n"; } /** HTML con los bloques de formularios de todas las operaciones aplicables. * * @return string El HTML del conjunto de bloques con todas la operaciones aplicables al artículo. */ public function bloque_operaciones_html() { $html = ''; // Reprocesar. if ( aicc_validador_auto_actual() !== $this->validada_por ) { $html_contenido = '

' . __( 'You have a different version of the articles post-production system than the one initially used.', 'ai-content-creator' ); $html_contenido .= ' ' . __( 'That system is responsible for the basic validation of the content received from the AI and a subsequent process to facilitate its publication.', 'ai-content-creator' ) . "

\n"; $html_contenido .= '

' . __( 'You can repeat post-production in search of a possible better result.', 'ai-content-creator' ) . "

\n"; if ( $this->publicada_post_id ) { $html_contenido .= '

' . __( 'The reprocessing does not affect the previously created draft.', 'ai-content-creator' ) . "

\n"; } $html_boton = $this->formulario_operacion_html( 'reprocesar', __( 'Reprocess', 'ai-content-creator' ), 'dashicons-update' ); $html .= $this->bloque_formulario_operacion_html( $html_contenido, $html_boton ); } // Regenerar. $html_contenido = '

' . __( 'You can go back to the beginning to create a new article based on the request used in this one.', 'ai-content-creator' ) . "

\n"; $html_contenido .= '

' . __( 'The new article will not overwrite the data of this one', 'ai-content-creator' ); if ( $this->publicada_post_id ) { $html_contenido .= ' '; $html_contenido .= __( 'nor will affect the previously created draft', 'ai-content-creator' ); } $html_contenido .= ".

\n"; // Alternativas: dashicons-welcome-add-page, dashicons-controls-repeat, dashicons-redo, dashicons-editor-break. $html_boton = $this->formulario_operacion_html( 'regenerar', __( 'Create another', 'ai-content-creator' ), 'dashicons-welcome-add-page' ); $html .= $this->bloque_formulario_operacion_html( $html_contenido, $html_boton ); // Editar post + borrar generación. if ( $this->publicada_post_id ) { $html_contenido = '

' . __( 'You can', 'ai-content-creator' ) . ' '; $html_contenido .= __( 'edit the article', 'ai-content-creator' ); $html_contenido .= ' '; $html_contenido .= __( "with your WordPress's usual system", 'ai-content-creator' ) . ".

\n"; // Enlace como falso botón. $html_boton = '
'; $html_boton .= $this->falso_boton_editar_articulo_html(); $html_boton .= '
'; $html .= $this->bloque_formulario_operacion_html( $html_contenido, $html_boton ); $html_contenido = '

' . __( 'If you are sure that you will not need any revision of how it was generated anymore, you can delete it. This change is not reversible. The draft or published article will not be affected.', 'ai-content-creator' ) . "

\n"; $html_boton = $this->formulario_operacion_html( 'borrar', __( 'Delete', 'ai-content-creator' ), 'dashicons-trash' ); $html .= $this->bloque_formulario_operacion_html( $html_contenido, $html_boton ); } else { if ( current_user_can( 'edit_posts' ) ) { if ( $this->valida ) { // Operaciones viables en un artículo corrrecto // Seleccionar o cambiar la imagen. $html .= $this->bloque_gestion_imagen(); // Crear borrador. $html_contenido = '

' . __( 'Create a draft using the generated content to edit and finalize its publishing.', 'ai-content-creator' ) . "

\n"; // Alternativa: dashicons-migrate. $html_boton = $this->formulario_operacion_html( 'borrador', __( 'Create draft', 'ai-content-creator' ), 'dashicons-external' ); $html .= $this->bloque_formulario_operacion_html( $html_contenido, $html_boton ); } else { if ( $this->contenido ) { // Operaciones viables en un artículo con defectos si pese a ello tiene contenido. // Seleccionar o cambiar la imagen. $html .= $this->bloque_gestion_imagen(); // Crear borrador aun con error. $html_contenido = '

' . __( 'Although errors have been detected in the generated content, if you want to take advantage of it, you can create a draft to edit and finalize its publishing.', 'ai-content-creator' ) . "

\n"; $texto_confirmacion = __( 'I understand that the content of the draft may be defective.', 'ai-content-creator' ); $html_contenido .= '

' . $texto_confirmacion . "

\n"; $html_boton = $this->formulario_operacion_html( 'borrador', __( 'Create draft', 'ai-content-creator' ), 'dashicons-external' ); $html .= $this->bloque_formulario_operacion_html( $html_contenido, $html_boton ); $html .= ''; // Llama inicialmente para deshabilitar, así uso el código estándar con el botón habilitado en origen. $html .= ''; // javascript que deshabilite el botón 'borrador' directamente. // javascript que habilite el botón 'borrador' al hacer click en la casilla onchange="isChecked(this, 'borrador')". } } } else { // No tiene permisos para crear ni editar borradores. // En principio no debería llegar aquí tampoco si no los tiene. $html .= '

' . __( "You don't have permission to create drafts.", 'ai-content-creator' ) . "

\n"; } // Borrar. $html_contenido = '

'; if ( $this->contenido ) { // No es necesario que sea válida para poder crear un borrador, basta con que tenga contenido. $html_contenido = __( 'If the content is not useful and you will not need any revision of how it was generated or create a draft with it, you can delete it. This change is not reversible.', 'ai-content-creator' ); } else { // Si no tiene contenido, no puede crear borrador, no mencionarlo entonces. $html_contenido = __( 'If the content is not useful and you will not need any revision of how it was generated, you can delete it. This change is not reversible.', 'ai-content-creator' ); } $html_contenido .= "

\n"; $html_boton = $this->formulario_operacion_html( 'borrar', __( 'Delete', 'ai-content-creator' ), 'dashicons-trash' ); $html .= $this->bloque_formulario_operacion_html( $html_contenido, $html_boton ); } // Otros dashicons útiles: dashicons-editor-help, dashicons-download, dashicons-upload. // Se puedne ver en https://developer.wordpress.org/resource/dashicons/ . return $html; } /** HTML para gestionar la selección de la imagen destacada * * @return string El HTML con el código para gestionar la selección de la imagen destacada. */ private function bloque_gestion_imagen() { // Seleccionar imagen. // Obtiene las claves para la búsqueda de imágenes. $metakeywords = $this->meta_keywords; if ( ! $metakeywords ) { $metakeywords = aicc_improvisa_meta_keywords( $this->titulo, $this->idioma ); } $pixabay_url = aicc_url_busqueda_pixabay( $this->meta_keywords, $this->idioma ); // Seleccionar o cambiar la imagen seleccionada. if ( $this->imagen_id ) { $html_contenido = '

'; $html_contenido .= esc_html( __( 'Change the selected featured image for the article.', 'ai-content-creator' ) ); $html_contenido .= ' ' . esc_html( __( 'This will remove the previously selected image from the media library.', 'ai-content-creator' ) ); $html_contenido .= "

\n"; $html_boton = $this->formulario_operacion_html( 'imagen', __( 'Change featured image', 'ai-content-creator' ), 'dashicons-format-image' ); } else { $html_contenido = '

' . esc_html( __( 'Select a featured image for the article.', 'ai-content-creator' ) ) . "

\n"; $html_boton = $this->formulario_operacion_html( 'imagen', __( 'Select featured image', 'ai-content-creator' ), 'dashicons-format-image' ); } $html = $this->bloque_formulario_operacion_html( $html_contenido, $html_boton ); return $html; } /** Genera HTML para mostrar el formulario de acciones sobre los artículos. * * @param string $html_contenido El contenido HTML del formulario. * @param string $html_boton El botón HTML del formulario. * @return string El HTML del formulario con los estilos aplicados. */ public function bloque_formulario_operacion_html( $html_contenido, $html_boton ) { $html = '
'; $html .= $html_contenido; $html .= $html_boton; $html .= '
'; return $html; } /** Genera el HTML para el formulario de acciones sobre los artículos. * * @param string $accion La acción que se realizará en el formulario. * @param string $texto El texto del botón del formulario. * @param string $dashicon (Opcional) El nombre de la clase Dashicon para mostrar junto al texto del botón. * @return string El HTML del formulario con los campos y botón correspondientes. */ public function formulario_operacion_html( $accion, $texto, $dashicon = '' ) { // Reprocesar ha de ir a la página de creación y con POST. // Los demás, a la de artículos y con GET. if ( 'regenerar' === $accion ) { $action = 'admin.php'; $pagina = 'aicc_menu'; $metodo = 'GET'; } else { $action = 'admin.php'; $pagina = 'aicc_articles'; $metodo = 'GET'; } $html = '
' . "\n"; if ( $pagina ) { $html .= ' ' . "\n"; } $html .= ' ' . "\n"; // Todos indican id menos reprocesar_todo. if ( 'reprocesar_todo' !== $accion ) { $html .= ' ' . "\n"; if ( 'borrar' === $accion ) { $html .= wp_nonce_field( 'borrado', 'campo_nonce' ); } } // Proporciona estilo centrar el botón. $html .= '
'; $html .= ' ' . $this->boton_formulario_operacion_html( $texto, $accion, $dashicon ) . "\n"; $html .= '
'; $html .= '
'; return $html; } /** Enlace como falso botón. */ public function falso_boton_editar_articulo_html() { // Icono y texto en funciçon de si es borrador o está publicado. if ( 'publish' === get_post_status( $this->publicada_post_id ) ) { $dashicon = 'dashicons-media-document'; $texto = __( 'Edit published article', 'ai-content-creator' ); } else { $dashicon = 'dashicons-edit-page'; $texto = __( 'Edit draft', 'ai-content-creator' ); } // Forma el enlace con dashicon y texto al estilo de botón. $html = '' . "\n"; if ( $dashicon ) { $html .= ' '; } $html .= $texto; $html .= "\n"; $html .= " \n"; return $html; } /** Convierte un formato datetime de MySQL a fecha/hora del usuario. * * @param string $fecha La fecha en formato datetime de MySQL (YYYY-MM-DD HH:MM:SS). * @return string La fecha y hora en el formato corto del usuario (según las configuraciones de WordPress). */ private function fecha_hora_corta( $fecha ) { $fecha_php = strtotime( $fecha ); $fecha_corta = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $fecha_php ); return $fecha_corta; } /** Indica en html estado de un artículo. */ private function icono_estado_html() { $html = ''; if ( $this->error ) { // Alternativo: dashicons-no-alt. $html .= $this->dashicon_html( 'dashicons-dismiss', __( 'An error occurred while trying to create the article and it is not valid.', 'ai-content-creator' ), 'color:red;' ); } elseif ( $this->publicada_post_id ) { if ( 'publish' === get_post_status( $this->publicada_post_id ) ) { $html .= $this->dashicon_html( 'dashicons-media-document', __( 'Article published', 'ai-content-creator' ), 'color:green;' ); } else { /* translators: %s: Short date. */ $html .= $this->dashicon_html( 'dashicons-media-document', sprintf( __( 'Draft prepared on %s', 'ai-content-creator' ), $this->fecha_hora_corta( $this->publicada_fecha ) ), 'color:orange;' ); } } elseif ( $this->valida ) { if ( $this->validada_por ) { $html .= $this->dashicon_html( 'dashicons-yes-alt', __( 'Validated', 'ai-content-creator' ) . $this->por_validador_fecha_html(), 'color:green;' ); } else { // Supuestamente ya no procede. $html .= $this->dashicon_html( 'dashicons-yes', __( 'Automatic preliminary validation', 'ai-content-creator' ), 'color:green;' ); } } else { if ( $this->validada_por ) { $html .= $this->dashicon_html( 'dashicons-warning', __( 'Did not pass validation', 'ai-content-creator' ) . $this->por_validador_fecha_html(), 'color:orange;' ); } else { $html .= $this->dashicon_html( 'dashicons-warning', __( 'Did not pass validation', 'ai-content-creator' ), 'color:orange;' ); } } return $html; } /** Icono indicando la acción sugerida en la lista de artículos. */ private function icono_accion_html() { $html = ''; if ( $this->publicada_post_id ) { // Editable o no. if ( current_user_can( 'edit_post', $this->publicada_post_id ) ) { $html .= ''; // Publicado o no. if ( 'publish' === get_post_status( $this->publicada_post_id ) ) { $html .= $this->dashicon_html( 'dashicons-media-document', __( 'Edit published article', 'ai-content-creator' ) ); } else { /* translators: %s: Short date. */ $html .= $this->dashicon_html( 'dashicons-edit-page', esc_attr( sprintf( __( 'Edit draft prepared on %s', 'ai-content-creator' ), $this->fecha_hora_corta( $this->publicada_fecha ) ) ) ); } $html .= ''; } else { // Publicado o no. if ( 'publish' === get_post_status( $this->publicada_post_id ) ) { $html .= $this->dashicon_html( 'dashicons-media-document', esc_attr( __( 'You don\'t have permission to edit the published article.', 'ai-content-creator' ) ), 'color:gray;' ); } else { $html .= $this->dashicon_html( 'dashicons-edit-page', esc_attr( __( 'You don\'t have permission to edit the published draft.', 'ai-content-creator' ) ), 'color:gray;' ); } } } elseif ( aicc_validador_auto_actual() !== $this->validada_por ) { $html .= ''; $html .= $this->dashicon_html( 'dashicons-update', __( 'Reprocess', 'ai-content-creator' ), 'text-decoration:none;' ); $html .= ''; } elseif ( '' === $this->imagen_id ) { // Seleccionar imagen destacada. $html .= ''; $html .= $this->dashicon_html( 'dashicons-format-image', __( 'Select featured image', 'ai-content-creator' ), 'text-decoration:none;' ); $html .= ''; } elseif ( $this->valida ) { if ( current_user_can( 'edit_posts' ) ) { $html .= ''; $html .= $this->dashicon_html( 'dashicons-external', __( 'Create draft', 'ai-content-creator' ), 'text-decoration:none;' ); $html .= ''; } else { $html .= $this->dashicon_html( 'dashicons-external', esc_attr( __( 'You don\'t have permission to create drafts.', 'ai-content-creator' ) ), 'color:gray;' ); } } else { $html .= ''; $html .= $this->dashicon_html( 'dashicons-welcome-add-page', __( 'Create another', 'ai-content-creator' ), 'text-decoration:none;' ); $html .= ''; } return $html; } /** Proporciona el dashicon correspondiente al tipo de aviso indicado. * * @param string $tipo_aviso Uno de 'notificacion', 'advertencia' u otros. Por efecto 'error'. */ private function icono_aviso_html( $tipo_aviso = 'error' ) { $titulo = ''; if ( 'notificacion' === $tipo_aviso ) { $dashicon = 'dashicons-info'; $color = 'blue'; } elseif ( 'advertencia' === $tipo_aviso ) { $dashicon = 'dashicons-warning'; $color = 'orange'; } else { $dashicon = 'dashicons-dismiss'; $color = 'red'; } $estilo = "color: $color;"; $html = $this->dashicon_html( $dashicon, $titulo, $estilo ); return $html; } /** Proporciona el texto de la validación u otras operaciones reliazadas * por el agente y fecha correspondientes. */ private function por_validador_fecha_html() { $html = $this->por_agente_fecha_html( $this->validada_por, $this->validada_fecha ); return $html; } /** Texto de la validación u otras operaciones realizadas por algún agente y fecha. * * @param string $agente Agente validador. * @param string $fecha Fecha de la validación en formato datetime de MySQL. * @return string HTML con el código. */ private function por_agente_fecha_html( $agente, $fecha ) { /* translators: %1$s: Agent, %2$s: Short date. */ $html = ' ' . sprintf( __( "by '%1\$s' at %2\$s", 'ai-content-creator' ), $agente, $this->fecha_hora_corta( $fecha ) ); return $html; } /** Código de un dashicon con el tooltip que pueda proceder. * * @param string $dashicon Código del dashicon. * @param string $titulo Atributo title para formar un tooltip. * @param string $estilo CSS opcional para el atributo style. * @return string HTML con el código. */ private function dashicon_html( $dashicon, $titulo, $estilo = '' ) { $html = '