Plugin Directory

Changeset 3232882


Ignore:
Timestamp:
01/31/2025 07:34:25 PM (12 months ago)
Author:
nachomd
Message:

Release v1.06 - WooCommerce support, YouTube integration, and improvements

Location:
easy-gpt-for-wp
Files:
64 added
14 edited

Legend:

Unmodified
Added
Removed
  • easy-gpt-for-wp/trunk/admin/admin-functions.php

    r3197201 r3232882  
    4242        'easy-gpt-bulk-generate-new',  // Menu slug
    4343        'easy_gpt_bulk_generate_page'  // Function to display the menu content
     44    );
     45
     46    add_submenu_page(
     47        'easy-gpt-settings',
     48        'Cron Job Status',       // Page title
     49        'Cron Job Status',       // Menu title
     50        'manage_options',        // Capability
     51        'easy-gpt-cron-status',  // Menu slug
     52        'easy_gpt_status_page'   // Function to display the status page
    4453    );
    4554
  • easy-gpt-for-wp/trunk/easy-gpt-for-wp.php

    r3227131 r3232882  
    44 * Plugin URI: https://easygptforwp.com/
    55 * Description: Integrates OpenAI GPT functionalities to automatically generate content and manage SEO in WordPress.
    6  * Version: 1.05
     6 * Version: 1.06
    77 * Author: Ignacio Gil Alvarez
    88 * Author URI: https://morris34.com/
     
    1515    exit; // Exit if accessed directly.
    1616}
     17
     18define('EASY_GPT_PLUGIN_VERSION', '1.06');
    1719
    1820/**
     
    4143require_once plugin_dir_path(__FILE__) . 'includes/easy-gpt-bulk-article-generator.php';
    4244require_once plugin_dir_path(__FILE__) . 'includes/bulk-article-processing.php';
     45require_once plugin_dir_path(__FILE__) . 'includes/status.php';
     46
    4347
    4448
     
    4953    easy_gpt_create_tables();
    5054    easy_gpt_schedule_cron_jobs();
     55   
     56    update_option('easy_gpt_plugin_version', EASY_GPT_PLUGIN_VERSION);
    5157}
    5258register_activation_hook(__FILE__, 'easy_gpt_for_wordpress_activate');
     
    124130      start_time time DEFAULT NULL,
    125131      articles_per_interval int(11) DEFAULT 1,
     132      is_product tinyint(1) DEFAULT 0,  -- Nuevo campo
     133      generate_with_prices tinyint(1) DEFAULT 0, -- Nuevo campo
     134      price_list text DEFAULT NULL, -- Nuevo campo
     135      include_videos_bulk tinyint(1) DEFAULT 0, -- Nuevo campo
     136      video_position_bulk varchar(50) DEFAULT 'end', -- Nuevo campo
     137      video_paragraph_bulk int(11) DEFAULT 3, -- Nuevo campo
    126138      PRIMARY KEY  (id)
    127139    ) $charset_collate;";
     
    142154    dbDelta($sql);
    143155    dbDelta($sql2);
     156
     157    // **Verificar y agregar nuevas columnas si faltan**
     158    $existing_columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name", ARRAY_A);
     159    $columns = array_column($existing_columns, 'Field');
     160
     161    $alter_queries = [];
     162
     163    if (!in_array('content_type', $columns)) {
     164        $alter_queries[] = "ADD COLUMN content_type varchar(50) DEFAULT 'article'";
     165    }
     166    if (!in_array('enable_product_prices', $columns)) {
     167        $alter_queries[] = "ADD COLUMN enable_product_prices tinyint(1) DEFAULT 0";
     168    }
     169    if (!in_array('product_prices', $columns)) {
     170        $alter_queries[] = "ADD COLUMN product_prices text DEFAULT NULL";
     171    }
     172
     173    if (!in_array('include_videos_bulk', $columns)) {
     174        $alter_queries[] = "ADD COLUMN include_videos_bulk tinyint(1) DEFAULT 0";
     175    }
     176    if (!in_array('video_position_bulk', $columns)) {
     177        $alter_queries[] = "ADD COLUMN video_position_bulk varchar(50) DEFAULT 'end'";
     178    }
     179    if (!in_array('video_paragraph_bulk', $columns)) {
     180        $alter_queries[] = "ADD COLUMN video_paragraph_bulk int(11) DEFAULT 3";
     181    }
     182
     183    if (!empty($alter_queries)) {
     184        $wpdb->query("ALTER TABLE $table_name " . implode(", ", $alter_queries));
     185    }
    144186}
    145187
     
    159201    }
    160202}
     203
     204function easy_gpt_check_for_updates() {
     205    $installed_version = get_option('easy_gpt_plugin_version');
     206
     207    if ($installed_version !== EASY_GPT_PLUGIN_VERSION) {
     208        // Ejecutar actualizaciones si hay una nueva versión
     209        easy_gpt_create_tables(); // Asegura que las columnas nuevas se agreguen
     210        update_option('easy_gpt_plugin_version', EASY_GPT_PLUGIN_VERSION);
     211    }
     212}
     213add_action('admin_init', 'easy_gpt_check_for_updates');
    161214?>
  • easy-gpt-for-wp/trunk/includes/bulk-article-processing.php

    r3197201 r3232882  
    7777                    [ 'id' => intval( $next_pending_task->id ) ]
    7878                );
     79
     80                $wpdb->query( $wpdb->prepare(
     81                    "UPDATE {$table_generations} SET status = %s WHERE id = %d AND status = %s",
     82                    'processing',
     83                    intval( $generation->id ),
     84                    'pending'
     85                ) );
    7986
    8087                if ( easy_gpt_generate_complete_article( intval( $next_pending_task->id ), $article_params ) ) {
     
    153160    $article_params = easy_gpt_sanitize_article_params( $article_params );
    154161
     162    $content_type = isset($article_params['content_type']) ? $article_params['content_type'] : 'article';
     163    $is_product = ($content_type === 'product');
     164   
     165    error_log("Es un producto? $is_product");
     166   
    155167    // Generar todos los componentes necesarios antes de crear el artículo
    156168    $api_key = sanitize_text_field( get_option( 'easy_gpt_options' )['easy_gpt_api_key'] );
     
    186198    }
    187199
    188     // Convertir el nombre de la categoría en ID
    189     $category_id = get_cat_ID( $category_name );
    190     if ( ! $category_id ) {
    191         // Si la categoría no existe, crearla
    192         $new_category = wp_insert_term( $category_name, 'category' );
    193         if ( is_wp_error( $new_category ) ) {
    194             return false;
    195         }
    196         $category_id = intval( $new_category['term_id'] );
     200
     201    $category_taxonomy = $is_product ? 'product_cat' : 'category';
     202   
     203    // Buscar la categoría en la taxonomía correspondiente
     204    $category_term = get_term_by('name', $category_name, $category_taxonomy);
     205   
     206    if (!$category_term) {
     207        // Si la categoría no existe, crearla en la taxonomía correcta
     208        $new_category = wp_insert_term($category_name, $category_taxonomy);
     209        if (!is_wp_error($new_category) && isset($new_category['term_id'])) {
     210            $category_id = intval($new_category['term_id']);
     211        } else {
     212            $category_id = null; // Si hay error, no asignamos categoría
     213        }
     214    } else {
     215        $category_id = intval($category_term->term_id);
    197216    }
    198217
     
    236255        'post_category' => [ $category_id ], // Usar el ID numérico de la categoría
    237256        'tags_input'    => array_map( 'sanitize_text_field', explode( ', ', $tags ) ),
     257        'post_type'     => $is_product ? 'product' : 'post',
    238258        'post_author'   => $default_author,
    239259    ];
     260
    240261
    241262    // Verificar si debe publicarse inmediatamente o programarse
     
    259280    }
    260281
     282
     283    if ($is_product) {
     284        easy_gpt_generate_product_price($article_params, $post_id);
     285    }
     286   
     287   
     288    if ($is_product) {
     289        if ($category_id) {
     290            wp_set_object_terms($post_id, [$category_id], 'product_cat');
     291        }
     292   
     293        if (!empty($tags)) {
     294            $tag_names = array_map('sanitize_text_field', explode(',', $tags));
     295            $tag_ids = [];
     296   
     297            foreach ($tag_names as $tag_name) {
     298                $tag_term = get_term_by('name', trim($tag_name), 'product_tag');
     299                if (!$tag_term) {
     300                    $new_tag = wp_insert_term(trim($tag_name), 'product_tag');
     301                    if (!is_wp_error($new_tag)) {
     302                        $tag_ids[] = intval($new_tag['term_id']);
     303                    }
     304                } else {
     305                    $tag_ids[] = intval($tag_term->term_id);
     306                }
     307            }
     308   
     309            if (!empty($tag_ids)) {
     310                wp_set_object_terms($post_id, $tag_ids, 'product_tag');
     311            }
     312        }
     313    }
     314
    261315    // 10. Descargar y asignar las imágenes a la biblioteca de medios
    262316    if ( ! empty( $article_params['auto_images'] ) ) {
     
    281335            set_post_thumbnail( $post_id, $image_ids[0] );
    282336        }
    283 
     337       
     338        if ($is_product && count($image_ids) > 1) {
     339            // Excluir la imagen destacada y agregar las demás a la galería
     340            $gallery_images = array_slice($image_ids, 1);
     341            update_post_meta($post_id, '_product_image_gallery', implode(',', $gallery_images));
     342        }
     343   
    284344        // 12. Insertar imágenes en el contenido del artículo usando las URLs locales
    285345        $new_content = easy_gpt_insert_images_into_content( $article, $local_image_urls, $article_params['set_featured_image'] );
     
    357417    }
    358418    return $sanitized_params;
     419}
     420
     421function easy_gpt_generate_product_price($article_params, $post_id) {
     422    global $wpdb;
     423
     424    // Obtener la moneda configurada en WooCommerce
     425    $currency = get_option('woocommerce_currency', 'USD'); // Si no hay configuración, por defecto USD
     426
     427    // Verificar si el usuario ha proporcionado un precio manual
     428    if (!empty($article_params['product_prices'])) {
     429        $prices = explode("\n", trim($article_params['product_prices']));
     430        $price = floatval(trim($prices[0])); // Tomar el primer precio disponible
     431    } else {
     432        // Si no hay precio manual, generarlo con OpenAI
     433        $prompt = "Generate a reasonable price for a product titled '{$article_params['title']}' in {$currency}. The response must be only a numeric value, without any currency symbol, letters, or additional text.";
     434
     435        $response = easy_gpt_call_openai_api_no_session($prompt, $article_params['model'], $article_params['temperature'], get_option('easy_gpt_options')['easy_gpt_api_key']);
     436
     437        // Extraer solo números
     438        $price = isset($response['text']) ? floatval(preg_replace('/[^0-9.]/', '', $response['text'])) : 0;
     439
     440        // Si la IA no devuelve un número válido, asignar un precio por defecto
     441        if ($price <= 0) {
     442            $price = rand(10, 100); // Precio aleatorio entre 10 y 100 si OpenAI falla
     443        }
     444    }
     445
     446    // Guardar el precio en el meta de WooCommerce
     447    update_post_meta($post_id, '_regular_price', $price);
     448    update_post_meta($post_id, '_price', $price); // WooCommerce usa _price como campo principal
     449    update_post_meta($post_id, '_currency', $currency); // Opcional: almacenar la moneda usada
     450
     451    return $price;
    359452}
    360453
     
    371464    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    372465
    373     $full_prompt = "Title Generation: Using the prompt '$prompt', keywords '$keywords', and specific instructions '$specific_instructions', generate a title that fits the style '$writing_style' and tone '$writing_tone' in '$language' language. Only return the title without enclosing it in quotes.";
     466    // Determinar si es un artículo o un producto
     467    $entity_type = ($article_params['content_type'] === 'product') ? "product" : "article";
     468   
     469    $full_prompt = "Title Generation for a {$entity_type}: Using the prompt '$prompt', keywords '$keywords', and specific instructions '$specific_instructions', generate a title that fits the style '$writing_style' and tone '$writing_tone' in '$language' language. Only return the title without enclosing it in quotes.";
    374470
    375471    $response = easy_gpt_call_openai_api_no_session($full_prompt, $model, $temperature, $api_key);
     
    402498    }
    403499
    404     $full_prompt = "Generate an outline for an article titled '{$title}' with the following details (only return the headings, don't explain anything, do not include an introduction or conclusion):\n" .
     500    // Determinar si es un artículo o un producto
     501    $entity_type = ($article_params['content_type'] === 'product') ? "product" : "article";
     502
     503    $full_prompt = "Generate an outline for a {$entity_type} titled '{$title}' with the following details (only return the headings, don't explain anything, do not include an introduction or conclusion):\n" .
    405504                   "Prompt: {$prompt}\n" .
    406505                   "Keywords: {$keywords}\n" .
    407506                   "Specific instructions: {$specific_instructions}\n" .
    408507                   "Number of H2 Subheadings: {$num_subheadings} (create only {$num_subheadings} h2 subheadings)\n" .
    409                    "Total Words for the article: Minimum {$min_words} words, Maximum {$max_words} words.\n" .
    410                    "Ensure the outline and the number of sections are appropriate for the total word count of the article.\n" .
     508                   "Total Words for the {$entity_type}: Minimum {$min_words} words, Maximum {$max_words} words.\n" .
     509                   "Ensure the outline and the number of sections are appropriate for the total word count of the {$entity_type}.\n" .
    411510                   "Type of Additional Subheadings you can use (only if necessary): {$type_subheadings} (Place each subheading on a new line. Mark 'H2' subheadings with a '# ' prefix. {$subheading_instructions})\n" .
    412511                   "Language: {$language}\n" .
     
    414513                   "Tone: {$writing_tone}\n" .
    415514                   "Incorporate the keyword '{$keywords}' naturally in about half of the headings. Ensure the headings are varied and do not repeat the keyword in each heading.";
     515
    416516
    417517    $response = easy_gpt_call_openai_api_no_session($full_prompt, $model, $temperature, $api_key);
     
    433533    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    434534
    435     $full_prompt = "Generate an excerpt for an article titled '{$title}' with the following details:\n" .
     535    $entity_type = ($article_params['content_type'] === 'product') ? "product" : "article";
     536   
     537    $full_prompt = "Generate an excerpt for a {$entity_type} titled '{$title}' with the following details:\n" .
    436538                   "Prompt: {$prompt}\n" .
    437539                   "Keywords: {$keywords}\n" .
     
    440542                   "Language: {$language}\n" .
    441543                   "Writing Style: {$writing_style}\n" .
    442                    "Tone: {$writing_tone}\n".
     544                   "Tone: {$writing_tone}\n" .
    443545                   "Maximum Words for the excerpt: 60 words.";
    444546
     
    460562    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    461563
    462     $prompt_title = "Generate an SEO-friendly metatitle for an article with the following details, ensuring the metatitle does not exceed 60 characters (only return the metatitle without enclosing it in quotes):\nTitle: {$title}\nPrompt: {$prompt}\nKeywords: {$keywords}\nOutline: {$outline}\nLanguage: {$language}\nWriting Style: {$writing_style}\nTone: {$writing_tone}";
    463 
    464     $prompt_description = "Generate an SEO-friendly metadescription for an article with the following details, ensuring the metadescription does not exceed 155 characters (only return the metadescription without enclosing it in quotes):\nTitle: {$title}\nPrompt: {$prompt}\nKeywords: {$keywords}\nOutline: {$outline}\nLanguage: {$language}\nWriting Style: {$writing_style}\nTone: {$writing_tone}";
     564    // Determinar si es un artículo o un producto
     565    $entity_type = ($article_params['content_type'] === 'product') ? "product" : "article";
     566   
     567    // Generar el título SEO
     568    $prompt_title = "Generate an SEO-friendly metatitle for a {$entity_type} with the following details, ensuring the metatitle does not exceed 60 characters (only return the metatitle without enclosing it in quotes):\n" .
     569                    "Title: {$title}\n" .
     570                    "Prompt: {$prompt}\n" .
     571                    "Keywords: {$keywords}\n" .
     572                    "Outline: {$outline}\n" .
     573                    "Language: {$language}\n" .
     574                    "Writing Style: {$writing_style}\n" .
     575                    "Tone: {$writing_tone}";
     576   
     577    // Generar la meta descripción SEO
     578    $prompt_description = "Generate an SEO-friendly metadescription for a {$entity_type} with the following details, ensuring the metadescription does not exceed 155 characters (only return the metadescription without enclosing it in quotes):\n" .
     579                          "Title: {$title}\n" .
     580                          "Prompt: {$prompt}\n" .
     581                          "Keywords: {$keywords}\n" .
     582                          "Outline: {$outline}\n" .
     583                          "Language: {$language}\n" .
     584                          "Writing Style: {$writing_style}\n" .
     585                          "Tone: {$writing_tone}";
    465586
    466587    $title_response = easy_gpt_call_openai_api_no_session($prompt_title, $model, $temperature, $api_key);
     
    484605    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    485606
    486     $categories = get_terms(['taxonomy' => 'category', 'hide_empty' => false]);
     607    // Determinar si es un artículo o un producto
     608    $is_product = ($article_params['content_type'] === 'product');
     609    $taxonomy = $is_product ? 'product_cat' : 'category';
     610
     611    // Obtener las categorías disponibles en la taxonomía correspondiente
     612    $categories = get_terms(['taxonomy' => $taxonomy, 'hide_empty' => false]);
    487613    $category_names = array_map(function($cat) {
    488614        return $cat->name;
    489615    }, $categories);
    490     $category_list = implode(", ", $category_names);
    491 
    492     $full_prompt = "Given the existing categories: {$category_list}. Suggest only 1 category for an article titled '{$title}' with details:\n" .
     616    $category_list = !empty($category_names) ? implode(", ", $category_names) : "None available";
     617
     618    // Construir el prompt basado en si es un artículo o un producto
     619    $entity_type = $is_product ? "product" : "article";
     620    $full_prompt = "Given the existing {$entity_type} categories: {$category_list}. Suggest only 1 category for a {$entity_type} titled '{$title}' with details:\n" .
    493621                   "Prompt: {$prompt}\n" .
    494622                   "Outline: {$outline}\n" .
    495623                   "Keywords: {$keywords}\n" .
    496                    "Language: {$language}\n".
     624                   "Language: {$language}\n" .
    497625                   "Only return the name of the category";
    498626
     627    // Llamada a OpenAI para obtener la categoría sugerida
    499628    $response = easy_gpt_call_openai_api_no_session($full_prompt, $model, $temperature, $api_key);
    500629
    501630    return isset($response['text']) ? sanitize_text_field($response['text']) : false;
    502631}
     632
    503633
    504634function easy_gpt_generate_tags_via_cron($article_params) {
     
    512642    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    513643
    514     $full_prompt = "Suggest 3 tags for an article titled '{$title}' with details:\n" .
     644    // Determinar si es un artículo o un producto
     645    $entity_type = ($article_params['content_type'] === 'product') ? "product" : "article";
     646   
     647    $full_prompt = "Suggest 3 tags for a {$entity_type} titled '{$title}' with details:\n" .
    515648                   "Prompt: {$prompt}\n" .
    516649                   "Outline: {$outline}\n" .
    517650                   "Keywords: {$keywords}\n" .
    518                    "Language: {$language}\n".
     651                   "Language: {$language}\n" .
    519652                   "Only return the 3 tags separated by comma without # symbol";
     653
    520654
    521655    $response = easy_gpt_call_openai_api_no_session($full_prompt, $model, $temperature, $api_key);
     
    562696    $format_instructions = $format_article ? " and use <i>italic</i> tags for scientific terms or foreign words, and <b>bold</b> tags for significant phrases or parts of sentences that emphasize major findings, novel ideas, or critical points, characteristics, facts, or also to make the lecture easy." : "";
    563697
     698    // Determinar si es un artículo o un producto
     699    $entity_type = ($article_params['content_type'] === 'product') ? "product" : "article";
     700
     701    // **Campos de Video Añadidos**
     702    $include_video = isset($article_params['include_videos_bulk']) ? boolval($article_params['include_videos_bulk']) : false;
     703    $video_position = isset($article_params['video_position_bulk']) ? sanitize_text_field($article_params['video_position_bulk']) : 'end';
     704    $after_paragraph = isset($article_params['video_paragraph_bulk']) ? max(1, intval($article_params['video_paragraph_bulk'])) : 3;
     705
     706error_log("DEBUG: include_videos_bulk = " . ($include_video ? 'true' : 'false'));
     707error_log("DEBUG: video_position_bulk = " . $video_position);
     708error_log("DEBUG: video_paragraph_bulk = " . $after_paragraph);
     709
     710    $include_video_flag = $include_video ? '1' : '0';
     711    error_log("DEBUG: video_flag = " . $include_video_flag);
     712    $video_result = easy_gpt_get_youtube_video_url($title, $keywords, $language, $include_video_flag);
     713
     714
     715    $video_url = isset($video_result['video_url']) ? $video_result['video_url'] : '';
     716
    564717    // Generar la introducción
    565     $intro_prompt = "Generate an introduction for an article titled '{$title}' with the following details, formatted with <p> tags for each paragraph $format_instructions (only return the introduction):\n" .
     718    $intro_prompt = "Generate an introduction for a {$entity_type} titled '{$title}' with the following details, formatted with <p> tags for each paragraph {$format_instructions} (only return the introduction):\n" .
    566719                    "Prompt: {$prompt}\n" .
    567720                    "Keywords: {$keywords}\n" .
     
    571724                    "Maximum Words for the introduction: 200 words.\n";
    572725
     726
    573727    $intro_response = easy_gpt_call_openai_api_no_session($intro_prompt, $model, $temperature, $api_key);
    574728
     
    578732
    579733    $responses = [$intro_response['text']];
     734
     735    if (!empty($video_url) && $include_video) {
     736        if ($video_position === 'start') {
     737            $responses[0] .= "$video_url"; // Después de la introducción
     738        }
     739    }
    580740
    581741    // Dividir el outline en secciones H2, H3, H4
     
    604764    $max_words_section = $max_words / max(count($h2_sections), 1);
    605765
    606     foreach ($h2_sections as $h2_section) {
     766    foreach ($h2_sections as $index => $h2_section) {
    607767        $h2_title = array_shift($h2_section);
    608768        $h3_h4 = implode("\n", $h2_section);
     
    611771        }
    612772
    613         $section_prompt = "Generate a detailed section for the article titled '{$title}'. Include the following details:\n" .
    614                           "Section Title: {$h2_title}\n" .
    615                           "Subheadings:\n{$h3_h4}\n" .
    616                           "Format the article using tags like <h2> for section title, <p> for paragraphs, <h3> <h4> for subheadings, <ul><li> for lists... $format_instructions. Do not write <html>.\n" .
    617                           "Prompt: {$prompt}\n" .
    618                           "Keywords: {$keywords}\n" .
    619                           "Specific Instructions: {$specific_instructions_content}\n" .
    620                           "Language: {$language}\n" .
    621                           "Style: {$writing_style}\n" .
    622                           "Tone: {$writing_tone}\n" .
    623                           "Note: This is a section of a larger article. Begin with an introduction (is an introduction for the section, not a subheading), follow with detailed content under each subheading and do not include conclusions or summaries.\n" .
    624                           "Word Limit: This section should contain at least {$min_words_section} words and no more than {$max_words_section} words. Do not use the symbol #.\n" .
    625                           $links_instructions;
     773
     774    // Generar la sección
     775    $section_prompt = "Generate a detailed section for the {$entity_type} titled '{$title}'. Include the following details:\n" .
     776                      "Section Title: {$h2_title}\n" .
     777                      "Subheadings:\n{$h3_h4}\n" .
     778                      "Format the {$entity_type} using tags like <h2> for section title, <p> for paragraphs, <h3> <h4> for subheadings, <ul><li> for lists... {$format_instructions}. Do not write <html>.\n" .
     779                      "Prompt: {$prompt}\n" .
     780                      "Keywords: {$keywords}\n" .
     781                      "Specific Instructions: {$specific_instructions_content}\n" .
     782                      "Language: {$language}\n" .
     783                      "Style: {$writing_style}\n" .
     784                      "Tone: {$writing_tone}\n" .
     785                      "Note: This is a section of a larger {$entity_type}. Begin with an introduction (it is an introduction for the section, not a subheading), follow with detailed content under each subheading and do not include conclusions or summaries.\n" .
     786                      "Word Limit: This section should contain at least {$min_words_section} words and no more than {$max_words_section} words. Do not use the symbol #.\n" .
     787                      $links_instructions;
     788
    626789
    627790        $response = easy_gpt_call_openai_api_no_session($section_prompt, $model, $temperature, $api_key);
     
    632795
    633796        $responses[] = $response['text'];
     797       
     798        if (!empty($video_url) && $include_video && $video_position === 'random' && $index === $insert_video_after_section) {
     799            $responses[] .= "$video_url";
     800        }
     801       
     802    }
     803   
     804
     805    if (!empty($video_url) && $include_video && $video_position === 'end') {
     806        $responses[] .= "$video_url";
    634807    }
    635808
    636809    // Generar conclusión
    637     $conclusion_prompt = "Generate a conclusion for an article titled '{$title}' with the following details, formatted with <p> tags for each paragraph $format_instructions, give it a <h2> heading:\n" .
     810    $conclusion_prompt = "Generate a conclusion for a {$entity_type} titled '{$title}' with the following details, formatted with <p> tags for each paragraph {$format_instructions}, give it a <h2> heading:\n" .
    638811                         "Prompt: {$prompt}\n" .
    639812                         "Keywords: {$keywords}\n" .
     
    643816                         "Maximum Words for the conclusion: 200 words.\n" .
    644817                         $bibliography_instructions;
    645 
     818                     
    646819    $conclusion_response = easy_gpt_call_openai_api_no_session($conclusion_prompt, $model, $temperature, $api_key);
    647820
     
    652825    $responses[] = $conclusion_response['text'];
    653826
     827    // Manejar la opción 'after_paragraph'
     828    if (!empty($video_url) && $include_video && $video_position === 'after_paragraph') {
     829        $full_article = implode("", $responses);
     830        // Usar preg_split para mantener las etiquetas <p>
     831        $paragraphs = preg_split('/(<p\b[^>]*>.*?<\/p>)/i', $full_article, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
     832
     833
     834        error_log("Total paragraphs found: " . count($paragraphs));
     835        error_log("After paragraph number: " . $after_paragraph);
     836
     837        // Validar el número de párrafos
     838        if ($after_paragraph > 0 && $after_paragraph <= count($paragraphs)) {
     839            // Insertar el video después del párrafo especificado
     840            // $after_paragraph es 1-based, ajustar a 0-based
     841            $insert_index = $after_paragraph; // Como cada párrafo está en un índice separado
     842            if (isset($paragraphs[$insert_index])) {
     843                array_splice($paragraphs, $insert_index, 0, "\n<p>{$video_url}</p>\n");
     844                error_log("Inserted video after paragraph {$after_paragraph}.");
     845            } else {
     846                // Fallback en caso de que no exista el índice
     847                $paragraphs[] = "\n<p>{$video_url}</p>\n";
     848                error_log("Paragraph index {$insert_index} not found. Inserted video at the end.");
     849            }
     850        } else {
     851            // Si el número de párrafos es mayor o igual, insertar al final antes de la conclusión
     852            $paragraphs[] = "\n<p>{$video_url}</p>\n";
     853            error_log("After paragraph number {$after_paragraph} exceeds paragraph count. Inserted video at the end.");
     854        }
     855
     856        $full_article = implode("", $paragraphs);
     857    } else {
     858        $full_article = implode("", $responses);
     859    }
     860
    654861    // Combinar todas las secciones del artículo en una sola variable
    655     $generated_article = implode("\n", $responses);
     862    $generated_article = $full_article;
    656863
    657864    return wp_kses_post($generated_article);
     
    669876    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    670877
    671     $full_prompt = "Generate an image for an article titled '{$title}' with the following details, do not include text in the image:\n" .
     878    // Determinar si es un artículo o un producto
     879    $entity_type = ($article_params['content_type'] === 'product') ? "product" : "article";
     880   
     881    // Generar el prompt de imagen
     882    $full_prompt = "Generate an image for a {$entity_type} titled '{$title}' with the following details. Do not include text in the image:\n" .
    672883                   "Prompt: {$prompt}\n" .
    673884                   "Keywords: {$keywords}\n" .
     
    677888                   "Only return the image URL.";
    678889
    679     $response = easy_gpt_call_openai_image_api_no_session($full_prompt, $model, $api_key);
     890
     891    $response = easy_gpt_call_openai_image_api_no_session($full_prompt, $model, $image_size, $image_quality, $api_key);
    680892
    681893    return isset($response['image_url']) ? ['image_url' => esc_url_raw($response['image_url'])] : false;
     
    689901    $output_cost_per_token = floatval($easy_gpt_options['model_costs'][$model]['output'] ?? 0);
    690902
     903    if(strpos($model, 'o1') !== false)
     904    {
     905        $temperature = 1;
     906    }
     907   
    691908    // Determinar el endpoint correcto basado en el modelo
    692     if (strpos($model, 'gpt-3.5-turbo') !== false || strpos($model, 'gpt-4') !== false) {
     909    if (strpos($model, 'gpt-4o') !== false || strpos($model, 'gpt-3.5-turbo') !== false || strpos($model, 'gpt-4') !== false || strpos($model, 'o1') !== false) {
    693910        $url = 'https://api.openai.com/v1/chat/completions';
    694911        $postData = [
     
    760977}
    761978
    762 function easy_gpt_call_openai_image_api_no_session($prompt, $model, $api_key) {
     979function easy_gpt_call_openai_image_api_no_session($prompt, $model, $image_size, $image_quality, $api_key) {
    763980    global $easy_gpt_options;
    764981    global $easy_gpt_total_cost;
     
    7901007    }
    7911008
    792     $output_cost_per_image = floatval($easy_gpt_options['model_costs'][$model]['output'] ?? 0); // Costo por imagen
    793     $image_cost = $output_cost_per_image;
     1009    $image_cost = 0.0;
     1010   
     1011    // Verificar si el modelo seleccionado es un modelo de generación de imágenes
     1012    if (in_array($model, ['dall-e-3', 'dall-e-2'])) {
     1013        if (isset($easy_gpt_options['model_costs'][$model]['image'])) {
     1014            if ($model === 'dall-e-3') {
     1015                if (isset($easy_gpt_options['model_costs'][$model]['image'][$image_quality][$image_size])) {
     1016                    $image_cost = floatval($easy_gpt_options['model_costs'][$model]['image'][$image_quality][$image_size]);
     1017                } else {
     1018                    // Tamaño o calidad no definidos, ya se manejó arriba
     1019                    $image_cost = 0.0;
     1020                }
     1021            } elseif ($model === 'dall-e-2') {
     1022                if (isset($easy_gpt_options['model_costs'][$model]['image'][$image_size])) {
     1023                    $image_cost = floatval($easy_gpt_options['model_costs'][$model]['image'][$image_size]);
     1024                } else {
     1025                    // Tamaño no definido, ya se manejó arriba
     1026                    $image_cost = 0.0;
     1027                }
     1028            }
     1029        }
     1030    } else {
     1031        // Manejar modelos de texto
     1032        $output_cost_per_token = isset($easy_gpt_options['model_costs'][$model]['output']) ? floatval($easy_gpt_options['model_costs'][$model]['output']) : 0.0;
     1033        $image_cost = $output_cost_per_token;
     1034    }
    7941035
    7951036    // Sumar al costo total global
  • easy-gpt-for-wp/trunk/includes/bulk-generation.php

    r3222181 r3232882  
    100100$include_bibliography = isset( $options['easy_gpt_include_bibliography'] ) ? $options['easy_gpt_include_bibliography'] : '0';
    101101
     102$include_videos_bulk = isset($options['easy_gpt_include_videos']) ? $options['easy_gpt_include_videos'] : '0';
     103$video_position_bulk = isset($options['easy_gpt_video_position']) ? $options['easy_gpt_video_position'] : 'end';
     104$video_paragraph_bulk = isset($options['easy_gpt_video_paragraph']) ? $options['easy_gpt_video_paragraph'] : 3;
     105
    102106
    103107    ?>
     
    116120                    <tr>
    117121                        <td class="easy-gpt-column" style="max-width: 300px;">
    118                             <div class="easy-gpt-field">
    119                                 <label for="easy_gpt_number_of_articles"><?php esc_html_e( 'Number of Articles:', 'easy-gpt-for-wp' ); ?></label>
     122                            <!-- Toggle para elegir entre Articles y Products -->
     123                            <?php if ( class_exists( 'WooCommerce' ) ) : ?>
     124                                <div class="easy-gpt-field">
     125                                    <label for="content_type"><?php esc_html_e('Content Type:', 'easy-gpt-for-wp'); ?></label>
     126                                    <div class="toggle-container">
     127                                        <span class="toggle-label articles-label active">Articles</span>
     128                                        <label class="switch">
     129                                            <input type="checkbox" id="content_type_toggle">
     130                                            <span class="slider round"></span>
     131                                        </label>
     132                                        <span class="toggle-label products-label">Products</span>
     133                                        <input type="hidden" id="content_type" name="content_type" value="article">
     134                                    </div>
     135                                </div>
     136                            <?php endif; ?>
     137
     138
     139                           
     140                            <!-- Número de artículos/productos -->
     141                            <div class="easy-gpt-field">
     142                                <label for="easy_gpt_number_of_articles"><?php esc_html_e('Number of Items:', 'easy-gpt-for-wp'); ?></label>
    120143                                <input type="number" id="easy_gpt_number_of_articles" name="easy_gpt_number_of_articles" min="1" max="50" step="1" value="1">
    121144                            </div>
     145                           
     146                            <!-- Checkbox para habilitar la generación de precios (visible solo si se eligen productos) -->
     147                            <div class="easy-gpt-field" id="price-option-container" style="display: none;">
     148                                <label for="enable_product_prices"><?php esc_html_e('Generate with Prices?', 'easy-gpt-for-wp'); ?></label>
     149                                <label class="switch">
     150                                    <input type="checkbox" id="enable_product_prices" name="enable_product_prices">
     151                                    <span class="slider round"></span>
     152                                </label>
     153                            </div>
     154                           
     155                            <!-- Campo para especificar precios (visible solo si se activa la opción de precios) -->
     156                            <div class="easy-gpt-field" id="price-input-container" style="display: none;">
     157                                <label for="easy_gpt_product_prices"><?php esc_html_e('Specify Prices (one per line):', 'easy-gpt-for-wp'); ?></label>
     158                                <textarea id="easy_gpt_product_prices" name="easy_gpt_product_prices" rows="2" cols="50"></textarea>
     159                                <p class="description"><?php esc_html_e('Leave blank to let the AI generate the price automatically.', 'easy-gpt-for-wp'); ?></p>
     160                            </div>
     161
    122162                            <div class="easy-gpt-field">
    123163                                <label for="easy_gpt_prompt"><?php esc_html_e( 'Prompt (one line per article):', 'easy-gpt-for-wp' ); ?></label>
     
    333373                    <label for="easy_gpt_include_excerpt" class="black-label"><?php esc_html_e( 'Include Excerpt:', 'easy-gpt-for-wp' ); ?></label>
    334374                    <input type="checkbox" id="easy_gpt_include_excerpt" name="easy_gpt_include_excerpt" <?php checked( $include_excerpt ); ?> class="adjusted-checkbox">
     375                </p>
     376                <h3><?php esc_html_e( 'Video Settings', 'easy-gpt-for-wp' ); ?></h3>
     377               
     378                <p class="easy-gpt-field easy-gpt-inline-bold">
     379                    <label for="easy_gpt_include_videos_bulk" class="black-label"><?php esc_html_e( 'Include Videos:', 'easy-gpt-for-wp' ); ?></label>
     380                    <input type="checkbox" id="easy_gpt_include_videos_bulk" name="easy_gpt_include_videos_bulk" <?php checked( $include_videos_bulk, '1' ); ?> class="adjusted-checkbox">
     381                    <small><?php esc_html_e( 'Check this box to include a YouTube video in each article.', 'easy-gpt-for-wp' ); ?></small>
     382                </p>
     383               
     384                <p class="easy-gpt-field">
     385                    <label for="easy_gpt_video_position_bulk"><?php esc_html_e( 'Video Position:', 'easy-gpt-for-wp' ); ?></label>
     386                    <select id="easy_gpt_video_position_bulk" name="easy_gpt_video_position_bulk">
     387                        <option value="start" <?php selected( $video_position_bulk, 'start' ); ?>><?php esc_html_e( 'Start', 'easy-gpt-for-wp' ); ?></option>
     388                        <option value="end" <?php selected( $video_position_bulk, 'end' ); ?>><?php esc_html_e( 'End', 'easy-gpt-for-wp' ); ?></option>
     389                        <option value="random" <?php selected( $video_position_bulk, 'random' ); ?>><?php esc_html_e( 'Random', 'easy-gpt-for-wp' ); ?></option>
     390                        <option value="after_paragraph" <?php selected( $video_position_bulk, 'after_paragraph' ); ?>><?php esc_html_e( 'After X Paragraphs', 'easy-gpt-for-wp' ); ?></option>
     391                    </select>
     392                    <p><small><?php esc_html_e( 'Choose where to insert the YouTube video in each article.', 'easy-gpt-for-wp' ); ?></small></p>
     393                </p>
     394               
     395                <p class="easy-gpt-field" id="easy_gpt_video_paragraph_container_bulk" style="display: <?php echo ( $video_position_bulk === 'after_paragraph' ) ? 'block' : 'none'; ?>;">
     396                    <label for="easy_gpt_video_paragraph_bulk"><?php esc_html_e( 'Number of Paragraphs:', 'easy-gpt-for-wp' ); ?></label>
     397                    <input type="number" id="easy_gpt_video_paragraph_bulk" name="easy_gpt_video_paragraph_bulk" min="1" value="<?php echo esc_attr( $video_paragraph_bulk ); ?>"></br>
     398                    <small><?php esc_html_e( 'Insert the video after this number of paragraphs.', 'easy-gpt-for-wp' ); ?></small>
    335399                </p>
    336400            </div>
     
    449513    $data['specific_instructions_excerpt'] = isset($_POST['specific_instructions_excerpt']) ? sanitize_textarea_field(wp_unslash($_POST['specific_instructions_excerpt'])) : '';
    450514    $data['use_same_specific_excerpt_instructions'] = isset($_POST['use_same_specific_excerpt_instructions']) ? intval($_POST['use_same_specific_excerpt_instructions']) : 0;
     515   
     516
    451517
    452518    // Procesar 'image_prompts' (array)
     
    477543        $data['use_same_image_prompts'] = '';
    478544    }
    479 
     545   
     546    error_log("articulo o producto:");
     547    error_log(isset($_POST['content_type']) ? sanitize_text_field($_POST['content_type']) : 'article');
     548    $data['content_type'] = isset($_POST['content_type']) ? sanitize_text_field($_POST['content_type']) : 'article';
     549    $data['enable_product_prices'] = isset($_POST['enable_product_prices']) ? intval($_POST['enable_product_prices']) : 0;
     550    $data['product_prices'] = isset($_POST['product_prices']) ? sanitize_textarea_field(wp_unslash($_POST['product_prices'])) : '';
     551
     552    $data['include_videos_bulk'] = isset($_POST['easy_gpt_include_videos_bulk']) ? intval($_POST['easy_gpt_include_videos_bulk']) : 0;
     553    $data['video_position_bulk'] = isset($_POST['easy_gpt_video_position_bulk']) ? sanitize_text_field(wp_unslash($_POST['easy_gpt_video_position_bulk'])) : 'end';
     554    $data['video_paragraph_bulk'] = isset($_POST['easy_gpt_video_paragraph_bulk']) ? intval($_POST['easy_gpt_video_paragraph_bulk']) : 3;
     555   
    480556    // Inserción en la base de datos
    481557    $table_name = $wpdb->prefix . 'bulk_generations';
     
    525601        '%s', // image_prompts
    526602        '%s', // use_same_image_prompts
     603        '%s', // content_type
     604        '%d', // enable_product_prices
     605        '%s', // product_prices
     606        '%d', // include_videos_bulk
     607        '%s', // video_position_bulk
     608        '%d', // video_paragraph_bulk
    527609    );
    528610
  • easy-gpt-for-wp/trunk/includes/easy-gpt-bulk-article-generator.php

    r3197201 r3232882  
    9191        $content_instruction = !empty($other_params['use_same_specific_content_instructions']) ? implode("\n", $contents) : ($contents[$i] ?? '');
    9292        $excerpt_instruction = !empty($other_params['use_same_specific_excerpt_instructions']) ? implode("\n", $excerpts) : ($excerpts[$i] ?? '');
     93       
     94        $product_prices_str = isset($other_params['product_prices']) ? sanitize_textarea_field($other_params['product_prices']) : '';
     95        $product_prices = array_map('sanitize_text_field', explode("\n", trim($product_prices_str)));
     96        $product_price = !empty($other_params['use_same_product_price']) ? implode("\n", $product_prices) : ($product_prices[$i] ?? '');
     97        $product_price = floatval(str_replace(',', '.', $product_price));
    9398
    9499        // Sanitizar instrucciones específicas
     
    152157            'start_time' => sanitize_text_field($other_params['start_time']),
    153158            'articles_per_interval' => intval($other_params['articles_per_interval']),
    154             'scheduled_publish_date' => ($publish_action === 'schedule' && isset($scheduled_dates[$i])) ? sanitize_text_field($scheduled_dates[$i]) : null
     159            'scheduled_publish_date' => ($publish_action === 'schedule' && isset($scheduled_dates[$i])) ? sanitize_text_field($scheduled_dates[$i]) : null,
     160            'content_type' => isset($other_params['content_type']) ? sanitize_text_field($other_params['content_type']) : 'article',
     161            'enable_product_prices' => isset($other_params['enable_product_prices']) ? intval($other_params['enable_product_prices']) : 0,
     162            'product_prices' => $product_price,
     163            'include_videos_bulk' => !empty($other_params['include_videos_bulk']),
     164            'video_position_bulk' => sanitize_text_field($other_params['video_position_bulk']),
     165            'video_paragraph_bulk' => intval($other_params['video_paragraph_bulk'])
    155166        ];
    156167
  • easy-gpt-for-wp/trunk/includes/easy-gpt-options.php

    r3197201 r3232882  
    22if ( ! defined( 'ABSPATH' ) ) exit;
    33$easy_gpt_options = [
    4     'models' => ['gpt-4o', 'gpt-4', 'gpt-4-0125-preview', 'gpt-4-turbo', 'gpt-4-turbo-preview', 'gpt-4-1106-preview', 'gpt-3.5-turbo'],
     4    'models' => ['o1-preview', 'o1-mini', 'gpt-4o', 'gpt-4o-mini', 'gpt-4', 'gpt-4-0125-preview', 'gpt-4-turbo', 'gpt-4-turbo-preview', 'gpt-4-1106-preview', 'gpt-3.5-turbo'],
    55    'writing_styles' => [
    66        'Formal',
     
    6666    ],
    6767    'model_costs' => [
    68         'gpt-4o' => ['input' => 0.005, 'output' => 0.015],
     68        'o1-preview' => ['input' => 0.015, 'output' => 0.06],
     69        'o1-mini' => ['input' => 0.003, 'output' => 0.012],
     70        'gpt-4o' => ['input' => 0.0025, 'output' => 0.01],
     71        'gpt-4o-mini' => ['input' => 0.00015, 'output' => 0.0006],
    6972        'gpt-4' => ['input' => 0.03, 'output' => 0.06],
    7073        'gpt-4-0125-preview' => ['input' => 0.01, 'output' => 0.03],
     
    7376        'gpt-4-1106-preview' => ['input' => 0.01, 'output' => 0.03],
    7477        'gpt-3.5-turbo' => ['input' => 0.0005, 'output' => 0.0015],
    75         'dall-e-3' => ['input' => 0.0, 'output' => 0.040],   // Costo por 1000 tokens para DALL-E 3
    76         'dall-e-2' => ['input' => 0.0, 'output' => 0.020]    // Costo por 1000 tokens para DALL-E 2
     78        'dall-e-3' => [
     79            'input' => 0.0,
     80            'output' => 0.0,
     81            'image' => [
     82                'standard' => [
     83                    '1024x1024' => 0.04,
     84                    '1024x1792' => 0.08,
     85                    '1792x1024' => 0.08,
     86                ],
     87                'hd' => [
     88                    '1024x1024' => 0.08,
     89                    '1024x1792' => 0.12,
     90                    '1792x1024' => 0.12,
     91                ],
     92            ],
     93        ],
     94        'dall-e-2' => [
     95            'input' => 0.0,
     96            'output' => 0.0,
     97            'image' => [
     98                '1024x1024' => 0.02,
     99                '1024x1792' => 0.02,
     100                '1792x1024' => 0.02,
     101            ],
     102        ],
    77103    ]
    78104];
  • easy-gpt-for-wp/trunk/includes/how-to-use.php

    r3197201 r3232882  
    1515                    <li><a href="#tab-generate-post">Generating a New Post/Page</a></li>
    1616                    <li><a href="#tab-bulk-generate">Bulk Post Generation</a></li>
     17                    <li><a href="#tab-youtube-api">YouTube API Configuration</a></li>
    1718                </ul>
    1819            </div>
     
    8384                    <p>The articles that are generated can be found in the 'Posts' section of your WordPress dashboard.</p>
    8485                </div>
     86                <div id="tab-youtube-api">
     87                    <h2>YouTube API Configuration</h2>
     88                    <p>To integrate YouTube functionalities with the Easy GPT plugin, you need to configure the YouTube API. Follow the steps below to set it up correctly.</p>
     89                   
     90                    <h3>Step 1: Create a Google Cloud Project</h3>
     91                    <ol>
     92                        <li>Go to the <a href="https://console.cloud.google.com/" target="_blank">Google Cloud Console</a>.</li>
     93                        <li>Click on the project dropdown at the top and select <strong>New Project</strong>.</li>
     94                        <li>Enter your project name, select your organization, and click <strong>Create</strong>.</li>
     95                    </ol>
     96
     97                    <h3>Step 2: Enable YouTube Data API</h3>
     98                    <ol>
     99                        <li>Within your new project, navigate to the <strong>APIs & Services</strong> > <strong>Library</strong>.</li>
     100                        <li>Search for <strong>YouTube Data API v3</strong> and click on it.</li>
     101                        <li>Click the <strong>Enable</strong> button.</li>
     102                    </ol>
     103
     104                    <h3>Step 3: Create API Credentials</h3>
     105                    <ol>
     106                        <li>Go to <strong>APIs & Services</strong> > <strong>Credentials</strong>.</li>
     107                        <li>Click on <strong>Create Credentials</strong> and select <strong>API Key</strong>.</li>
     108                        <li>Your new API key will be generated. Copy this key.</li>
     109                    </ol>
     110
     111                    <h3>Step 4: Restrict Your API Key</h3>
     112                    <p>For security purposes, it's recommended to restrict your API key.</p>
     113                    <ol>
     114                        <li>In the <strong>Credentials</strong> page, click on the <strong>Edit</strong> icon next to your API key.</li>
     115                        <li>Under <strong>Application restrictions</strong>, select <strong>HTTP referrers (web sites)</strong> and add your website URL.</li>
     116                        <li>Under <strong>API restrictions</strong>, select <strong>Restrict key</strong> and choose <strong>YouTube Data API v3</strong>.</li>
     117                        <li>Click <strong>Save</strong>.</li>
     118                    </ol>
     119
     120                    <h3>Step 5: Enter the API Key in Easy GPT Settings</h3>
     121                    <p>Now that you have your YouTube API key, enter it into the Easy GPT plugin settings:</p>
     122                    <ol>
     123                        <li>Navigate to <strong>Easy GPT > Settings</strong> in your WordPress dashboard.</li>
     124                        <li>Find the <strong>YouTube API Key</strong> field.</li>
     125                        <li>Paste your API key into this field and click <strong>Save Changes</strong>.</li>
     126                    </ol>
     127
     128                    <p>Your YouTube API is now configured and ready to use with the Easy GPT plugin.</p>
     129                </div>
    85130            </div>
    86131        </div>
  • easy-gpt-for-wp/trunk/includes/metaboxes.php

    r3227131 r3232882  
    44include 'easy-gpt-options.php';
    55
     6function easy_gpt_is_woocommerce_active() {
     7    return class_exists('WooCommerce');
     8}
     9
    610// Add metaboxes
    711add_action('add_meta_boxes', 'easy_gpt_add_metaboxes');
    812
    913function easy_gpt_add_metaboxes() {
     14   
     15    $post_types = ['post', 'page'];
     16
     17    if (class_exists('WooCommerce')) {
     18        $post_types[] = 'product';
     19    }
     20     
    1021    add_meta_box(
    1122        'easy_gpt_metabox',            // ID de la metabox
    1223        'Easy GPT Content Generator',  // Título de la metabox
    1324        'easy_gpt_metabox_callback',   // Función de callback
    14         ['post', 'page'],              // Pantallas donde se añadirá (entradas y páginas)
     25        $post_types,                    // Pantallas donde se añadirá (entradas y páginas)
    1526        'normal',                      // Contexto (ubicación)
    1627        'high'                         // Prioridad
     
    272283if ($include_bibliography === '') {
    273284    $include_bibliography = isset($options['easy_gpt_include_bibliography']) ? $options['easy_gpt_include_bibliography'] : '0';
     285}
     286
     287$include_videos = get_post_meta($post->ID, '_easy_gpt_include_videos_per_post', true);
     288if ($include_videos === '') {
     289    $include_videos = isset($options['easy_gpt_include_videos']) ? $options['easy_gpt_include_videos'] : '0';
     290}
     291
     292$video_position = get_post_meta($post->ID, '_easy_gpt_video_position_per_post', true);
     293if ($video_position === '') {
     294    $video_position = isset($options['easy_gpt_video_position']) ? $options['easy_gpt_video_position'] : 'end';
     295}
     296
     297$video_paragraph = get_post_meta($post->ID, '_easy_gpt_video_paragraph_per_post', true);
     298if ($video_paragraph === '') {
     299    $video_paragraph = isset($options['easy_gpt_video_paragraph']) ? $options['easy_gpt_video_paragraph'] : 3;
    274300}
    275301
     
    512538                <input type="checkbox" id="easy_gpt_include_excerpt" name="easy_gpt_include_excerpt" <?php checked( $include_excerpt ); ?> class="adjusted-checkbox">
    513539            </p>
     540            <h3><?php esc_html_e( 'Video Settings', 'easy-gpt-for-wp' ); ?></h3>
     541       
     542            <p class="easy-gpt-field easy-gpt-inline-bold">
     543                <label for="easy_gpt_include_videos_per_post" class="black-label"><?php esc_html_e( 'Include Videos:', 'easy-gpt-for-wp' ); ?></label>
     544                <input type="checkbox" id="easy_gpt_include_videos_per_post" name="easy_gpt_include_videos_per_post" <?php checked( $include_videos, '1' ); ?> class="adjusted-checkbox">
     545                <small><?php esc_html_e( 'Check this box to include a YouTube video in this article.', 'easy-gpt-for-wp' ); ?></small>
     546            </p>
     547           
     548            <p class="easy-gpt-field">
     549                <label for="easy_gpt_video_position_per_post"><?php esc_html_e( 'Video Position:', 'easy-gpt-for-wp' ); ?></label>
     550                <select id="easy_gpt_video_position_per_post" name="easy_gpt_video_position_per_post">
     551                    <option value="start" <?php selected( $video_position, 'start' ); ?>><?php esc_html_e( 'Start', 'easy-gpt-for-wp' ); ?></option>
     552                    <option value="end" <?php selected( $video_position, 'end' ); ?>><?php esc_html_e( 'End', 'easy-gpt-for-wp' ); ?></option>
     553                    <option value="random" <?php selected( $video_position, 'random' ); ?>><?php esc_html_e( 'Random', 'easy-gpt-for-wp' ); ?></option>
     554                    <option value="after_paragraph" <?php selected( $video_position, 'after_paragraph' ); ?>><?php esc_html_e( 'After X Paragraphs', 'easy-gpt-for-wp' ); ?></option>
     555                </select>
     556                <p><small><?php esc_html_e( 'Choose where to insert the YouTube video in the article.', 'easy-gpt-for-wp' ); ?></small></p>
     557            </p>
     558           
     559            <p class="easy-gpt-field" id="easy_gpt_video_paragraph_container" style="display: <?php echo ( $video_position === 'after_paragraph' ) ? 'block' : 'none'; ?>;">
     560                <label for="easy_gpt_video_paragraph"><?php esc_html_e( 'Number of Paragraphs:', 'easy-gpt-for-wp' ); ?></label>
     561                <input type="number" id="easy_gpt_video_paragraph" name="easy_gpt_video_paragraph_per_post" min="1" value="<?php echo esc_attr( $video_paragraph ); ?>"></br>
     562                <small><?php esc_html_e( 'Insert the video after this number of paragraphs.', 'easy-gpt-for-wp' ); ?></small>
     563            </p>
    514564            </div>
    515565            <div id="tab-3">
     
    631681    $new_include_links = isset($_POST['easy_gpt_include_links']) ? '1' : '0';
    632682    $new_include_bibliography = isset($_POST['easy_gpt_include_bibliography']) ? '1' : '0';
     683    $new_include_videos_per_post = isset($_POST['easy_gpt_include_videos_per_post']) ? '1' : '0';
     684    $new_video_position_per_post = isset($_POST['easy_gpt_video_position_per_post']) ? sanitize_text_field(wp_unslash($_POST['easy_gpt_video_position_per_post'])) : 'end';
     685    $new_video_paragraph_per_post = isset($_POST['easy_gpt_video_paragraph_per_post']) ? intval($_POST['easy_gpt_video_paragraph_per_post']) : 3;
     686
    633687
    634688    // Actualizar los campos meta en la base de datos.
     
    659713    update_post_meta($post_id, '_easy_gpt_include_links', $new_include_links);
    660714    update_post_meta($post_id, '_easy_gpt_include_bibliography', $new_include_bibliography);
     715    update_post_meta($post_id, '_easy_gpt_include_videos_per_post', $new_include_videos_per_post);
     716    update_post_meta($post_id, '_easy_gpt_video_position_per_post', $new_video_position_per_post);
     717    update_post_meta($post_id, '_easy_gpt_video_paragraph_per_post', $new_video_paragraph_per_post);
    661718}
    662719
     
    668725
    669726    try {
    670         if (isset($_POST['use_specific_instructions']) && $_POST['use_specific_instructions'] === 'true' && !empty($_POST['specific_instructions'])) {
     727        $use_specific_instructions = isset($_POST['use_specific_instructions']) && $_POST['use_specific_instructions'] === 'true';
     728
     729
     730        if ($use_specific_instructions && !empty($_POST['specific_instructions'])) {
    671731            $specific_instructions = sanitize_textarea_field(wp_unslash($_POST['specific_instructions']));
    672732            wp_send_json_success(array(
     
    690750            $writing_style = isset($_POST['writing_style']) ? sanitize_text_field(wp_unslash($_POST['writing_style'])) : '';
    691751            $writing_tone = isset($_POST['writing_tone']) ? sanitize_text_field(wp_unslash($_POST['writing_tone'])) : '';
    692 
    693             $full_prompt = "Title Generation: Using the prompt '$prompt', keywords '$keywords', and specific instructions '$specific_instructions', generate a title that fits the style '$writing_style' and tone '$writing_tone' in '$language' language. Only return the title without enclosing it in quotes.";
     752           
     753            $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     754            $is_product = ($post_type === 'product');
     755           
     756            $entity_type = $is_product ? "product" : "article";
     757           
     758            $full_prompt = "Title Generation for a {$entity_type}: Using the prompt '$prompt', keywords '$keywords', and specific instructions '$specific_instructions', generate a title that fits the style '$writing_style' and tone '$writing_tone' in '$language' language. Only return the title without enclosing it in quotes.";
    694759
    695760            $response = easy_gpt_call_openai_api($full_prompt, $model, $temperature, $api_key, $post_id);
     
    740805        }
    741806
    742         $full_prompt = "Generate an outline for an article titled '{$title}' with the following details (only return the headings, don't explain anything, do not include an introduction or conclusion:\n" .
     807        $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     808        $is_product = ($post_type === 'product');
     809        $entity_type = $is_product ? "product" : "article";
     810       
     811        $full_prompt = "Generate an outline for a {$entity_type} titled '{$title}' with the following details (only return the headings, don't explain anything, do not include an introduction or conclusion):\n" .
    743812                       "Prompt: {$prompt}\n" .
    744813                       "Keywords: {$keywords}\n" .
    745814                       "Specific instructions: {$specific_instructions}\n" .
    746815                       "Number of H2 Subheadings: {$num_subheadings} (create only {$num_subheadings} h2 subheadings) \n" .
    747                        "Total Words for the article: Minimum {$min_words} words, Maximum {$max_words} words.\n" .
    748                        "Ensure the outline and the number of sections are appropriate for the total word count of the article.\n" .
     816                       "Total Words for the {$entity_type}: Minimum {$min_words} words, Maximum {$max_words} words.\n" .
     817                       "Ensure the outline and the number of sections are appropriate for the total word count of the {$entity_type}.\n" .
    749818                       "Type of Additional Subheadings you can use (only if necessary): {$type_subheadings} (Place each subheading on a new line. Mark 'H2' subheadings with a '# ' prefix. {$subheading_instructions})\n" .
    750819                       "Language: {$language}\n" .
     
    752821                       "Tone: {$writing_tone}\n" .
    753822                       "Incorporate the keyword '{$keywords}' naturally in about half of the headings. Ensure the headings are varied and do not repeat the keyword in each heading.";
     823
    754824
    755825        $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
     
    789859    $includeLinks = isset($_POST['easy_gpt_includeLinks']) && $_POST['easy_gpt_includeLinks'] === 'true';
    790860
     861    $include_video = isset($_POST['easy_gpt_include_videos_per_post']) ? '1' : '0';
     862    $video_position = isset($_POST['easy_gpt_video_position_per_post']) ? sanitize_text_field(wp_unslash($_POST['easy_gpt_video_position_per_post'])) : 'end';
     863    $after_paragraph = isset($_POST['easy_gpt_video_paragraph_per_post']) ? max(1, intval($_POST['easy_gpt_video_paragraph_per_post'])) : 3;
     864
     865
    791866    // Construir instrucciones adicionales
    792867    $links_instructions = $includeLinks
     
    801876        ? " and use <i>italic</i> tags for scientific terms or foreign words, and <b>bold</b> tags for significant phrases or parts of sentences that emphasize major findings, novel ideas, or critical points, characteristics, facts, or also to make the lecture easy."
    802877        : "";
     878       
     879
     880    // Llamar a la función para obtener el video de YouTube
     881    $video_result = easy_gpt_get_youtube_video_url($title, $keywords, $language, $include_video);
     882   
     883    $video_url = isset($video_result['video_url']) ? $video_result['video_url'] : '';
     884
    803885
    804886    // Generar la introducción
    805     $intro_prompt = "Generate an introduction for an article titled '{$title}' with the following details, formatted with <p> tags for each paragraph{$format_instructions} (only return the introduction):\n" .
     887    $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     888    $is_product = ($post_type === 'product');
     889    $entity_type = $is_product ? "product" : "article";
     890   
     891    $intro_prompt = "Generate an introduction for a {$entity_type} titled '{$title}' with the following details, formatted with <p> tags for each paragraph{$format_instructions} (only return the introduction):\n" .
    806892                    "Prompt: {$prompt}\n" .
    807893                    "Keywords: {$keywords}\n" .
     
    811897                    "Maximum Words for the introduction: 200 words.\n";
    812898
     899
    813900    $intro_response = easy_gpt_call_openai_api($intro_prompt, $model, $temperature, $api_key, $post_id);
    814901
     
    819906
    820907    $responses = [$intro_response['text']];
     908   
     909    if (!empty($video_url) && $include_video) {
     910        if ($video_position === 'start') {
     911            $responses[0] .= "$video_url"; // Después de la introducción
     912        }
     913    }
    821914
    822915    // Procesar el esquema para obtener las secciones H2
     
    845938    if ($num_sections === 0) {
    846939        wp_send_json_error(['message' => "No sections found in outline"]);
    847         wp_die();
    848     }
     940        //wp_die();
     941    }
     942   
     943    $insert_video_after_section = ($video_position === 'random' && $num_sections > 0) ? rand(0, $num_sections - 1) : null;
    849944
    850945    $min_words_per_section = $num_sections > 0 ? $min_words / $num_sections : $min_words;
    851946    $max_words_per_section = $num_sections > 0 ? $max_words / $num_sections : $max_words;
    852947
    853     foreach ($h2_sections as $h2_section) {
     948    foreach ($h2_sections as $index => $h2_section) {
     949        error_log("Generando...");
     950       
    854951        $h2_title = array_shift($h2_section);
     952        error_log($h2_title);
    855953        $h3_h4 = implode("\n", $h2_section);
    856954        if (empty($h3_h4)) {
     
    858956        }
    859957
    860         $section_prompt = "Generate a detailed section for the article titled '{$title}'. Include the following details:\n" .
    861                           "Section Title: {$h2_title}\n" .
    862                           "Subheadings:\n{$h3_h4}\n" .
    863                           "Format the article using tags, like <h2> for section title, <p> for paragraphs, <h3>, <h4> for subheadings, <ul><li> for lists... {$format_instructions}, do not write <html>.\n" .
    864                           "Prompt: {$prompt}\n" .
    865                           "Keywords: {$keywords}\n" .
    866                           "Specific Instructions: {$specific_instructions_content}\n" .
    867                           "Language: {$language}\n" .
    868                           "Style: {$writing_style}\n" .
    869                           "Tone: {$writing_tone}\n" .
    870                           "Note: This is a section of a larger article. Begin with an introduction (an introduction for the section, not a subheading), follow with detailed content under each subheading, and do not include conclusions or summaries.\n" .
    871                           "Word Limit: This section should contain at least {$min_words_per_section} words and no more than {$max_words_per_section} words. Do not use this symbol #.\n" .
    872                           $links_instructions;
     958        $section_prompt = "Generate a detailed section for the {$entity_type} titled '{$title}'. Include the following details:\n" .
     959                  "Section Title: {$h2_title}\n" .
     960                  "Subheadings:\n{$h3_h4}\n" .
     961                  "Format the {$entity_type} using tags, like <h2> for section title, <p> for paragraphs, <h3>, <h4> for subheadings, <ul><li> for lists... {$format_instructions}, do not write <html>.\n" .
     962                  "Prompt: {$prompt}\n" .
     963                  "Keywords: {$keywords}\n" .
     964                  "Specific Instructions: {$specific_instructions_content}\n" .
     965                  "Language: {$language}\n" .
     966                  "Style: {$writing_style}\n" .
     967                  "Tone: {$writing_tone}\n" .
     968                  "Note: This is a section of a larger {$entity_type}. Begin with an introduction (an introduction for the section, not a subheading), follow with detailed content under each subheading, and do not include conclusions or summaries.\n" .
     969                  "Word Limit: This section should contain at least {$min_words_per_section} words and no more than {$max_words_per_section} words. Do not use this symbol #.\n" .
     970                  $links_instructions;
    873971
    874972        $response = easy_gpt_call_openai_api($section_prompt, $model, $temperature, $api_key, $post_id);
     
    880978
    881979        $responses[] = $response['text'];
     980
     981        // Insertar video si la opción es 'random' y esta es la sección seleccionada
     982        if (!empty($video_url) && $include_video && $video_position === 'random' && $index === $insert_video_after_section) {
     983            $responses[] .= "$video_url";
     984        }
    882985    }
    883986
    884987    // Generar la conclusión
    885     $conclusion_prompt = "Generate a conclusion for an article titled '{$title}' with the following details, formatted with <p> tags for each paragraph{$format_instructions}, give it a <h2> heading:\n" .
    886                          "Prompt: {$prompt}\n" .
    887                          "Keywords: {$keywords}\n" .
    888                          "Language: {$language}\n" .
    889                          "Writing Style: {$writing_style}\n" .
    890                          "Tone: {$writing_tone}\n" .
    891                          "Maximum Words for the conclusion: 200 words.\n" .
    892                          $bibliography_instructions;
     988
     989    if (!empty($video_url) && $include_video && $video_position === 'end') {
     990        $responses[] .= "$video_url";
     991    }
     992
     993    $conclusion_prompt = "Generate a conclusion for a {$entity_type} titled '{$title}' with the following details, formatted with <p> tags for each paragraph{$format_instructions}, give it a <h2> heading:\n" .
     994                     "Prompt: {$prompt}\n" .
     995                     "Keywords: {$keywords}\n" .
     996                     "Language: {$language}\n" .
     997                     "Writing Style: {$writing_style}\n" .
     998                     "Tone: {$writing_tone}\n" .
     999                     "Maximum Words for the conclusion: 200 words.\n" .
     1000                     $bibliography_instructions;
     1001
    8931002
    8941003    $conclusion_response = easy_gpt_call_openai_api($conclusion_prompt, $model, $temperature, $api_key, $post_id);
     
    9011010    $responses[] = $conclusion_response['text'];
    9021011
     1012
     1013    // Manejar la opción 'after_paragraph'
     1014    if (!empty($video_url) && $include_video && $video_position === 'after_paragraph') {
     1015        $full_article = implode("", $responses);
     1016        // Usar preg_split para mantener las etiquetas <p>
     1017        $paragraphs = preg_split('/(<p\b[^>]*>.*?<\/p>)/i', $full_article, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
     1018
     1019
     1020        error_log("Total paragraphs found: " . count($paragraphs));
     1021        error_log("After paragraph number: " . $after_paragraph);
     1022
     1023        // Validar el número de párrafos
     1024        if ($after_paragraph > 0 && $after_paragraph <= count($paragraphs)) {
     1025            // Insertar el video después del párrafo especificado
     1026            // $after_paragraph es 1-based, ajustar a 0-based
     1027            $insert_index = $after_paragraph; // Como cada párrafo está en un índice separado
     1028            if (isset($paragraphs[$insert_index])) {
     1029                array_splice($paragraphs, $insert_index, 0, "\n<p>{$video_url}</p>\n");
     1030                error_log("Inserted video after paragraph {$after_paragraph}.");
     1031            } else {
     1032                // Fallback en caso de que no exista el índice
     1033                $paragraphs[] = "\n<p>{$video_url}</p>\n";
     1034                error_log("Paragraph index {$insert_index} not found. Inserted video at the end.");
     1035            }
     1036        } else {
     1037            // Si el número de párrafos es mayor o igual, insertar al final antes de la conclusión
     1038            $paragraphs[] = "\n<p>{$video_url}</p>\n";
     1039            error_log("After paragraph number {$after_paragraph} exceeds paragraph count. Inserted video at the end.");
     1040        }
     1041
     1042        $full_article = implode("", $paragraphs);
     1043    } else {
     1044        $full_article = implode("", $responses);
     1045    }
     1046
    9031047    wp_send_json_success([
    904         'article' => implode("", $responses),
     1048        'article' => $full_article,
    9051049        'prompt_tokens' => $conclusion_response['prompt_tokens'],
    9061050        'completion_tokens' => $conclusion_response['completion_tokens'],
     
    9321076    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    9331077
    934     $full_prompt = "Generate an excerpt for an article titled '{$title}' with the following details:\n" .
     1078    $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     1079    $is_product = ($post_type === 'product');
     1080    $entity_type = $is_product ? "product" : "article";
     1081   
     1082    $full_prompt = "Generate an excerpt for a {$entity_type} titled '{$title}' with the following details:\n" .
    9351083                   "Prompt: {$prompt}\n" .
    9361084                   "Keywords: {$keywords}\n" .
     
    9411089                   "Tone: {$writing_tone}\n" .
    9421090                   "Maximum Words for the excerpt: 60 words.";
     1091   
    9431092
    9441093    $response = easy_gpt_call_openai_api($full_prompt, $model, $temperature, $api_key, $post_id);
     
    9721121    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    9731122
    974     $prompt_title = "Generate an SEO-friendly metatitle for an article with the following details, ensuring the metatitle does not exceed 60 characters (only return the metatitle without enclosing it in quotes):\n" .
     1123    $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     1124    $is_product = ($post_type === 'product');
     1125    $entity_type = $is_product ? "product" : "article";
     1126
     1127    $prompt_title = "Generate an SEO-friendly metatitle for a {$entity_type} with the following details, ensuring the metatitle does not exceed 60 characters (only return the metatitle without enclosing it in quotes):\n" .
    9751128                    "Title: {$title}\n" .
    9761129                    "Prompt: {$prompt}\n" .
     
    9801133                    "Writing Style: {$writing_style}\n" .
    9811134                    "Tone: {$writing_tone}";
    982 
    983     $prompt_description = "Generate an SEO-friendly metadescription for an article with the following details, ensuring the metadescription does not exceed 155 characters (only return the metadescription without enclosing it in quotes):\n" .
     1135   
     1136    $prompt_description = "Generate an SEO-friendly metadescription for a {$entity_type} with the following details, ensuring the metadescription does not exceed 155 characters (only return the metadescription without enclosing it in quotes):\n" .
    9841137                          "Title: {$title}\n" .
    9851138                          "Prompt: {$prompt}\n" .
     
    9891142                          "Writing Style: {$writing_style}\n" .
    9901143                          "Tone: {$writing_tone}";
     1144
    9911145
    9921146    $title_response = easy_gpt_call_openai_api($prompt_title, $model, $temperature, $api_key, $post_id);
     
    10231177    $keywords = isset($_POST['keywords']) ? sanitize_text_field(wp_unslash($_POST['keywords'])) : '';
    10241178
     1179
     1180
    10251181    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    10261182
    1027     // Obtener los nombres de las categorías existentes
    1028     $categories = get_terms(['taxonomy' => 'category', 'hide_empty' => false]);
     1183    $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     1184
     1185    // Determinar la taxonomía correcta
     1186    $taxonomy = ($post_type === 'product') ? 'product_cat' : 'category';
     1187
     1188    error_log("Post Type recibido: " . $post_type);
     1189    error_log("Taxonomía utilizada: " . $taxonomy);
     1190
     1191    // Obtener categorías de la taxonomía correcta
     1192    $categories = get_terms(['taxonomy' => $taxonomy, 'hide_empty' => false]);
     1193
     1194   
    10291195    $category_names = array_map(function($cat) {
    10301196        return $cat->name;
     
    10321198    $category_list = implode(", ", $category_names);
    10331199
    1034     $full_prompt = "Given the existing categories: {$category_list}. Suggest only 1 category for an article titled '{$title}' with details:\n" .
     1200    $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     1201    $is_product = ($post_type === 'product');
     1202   
     1203    $entity_type = $is_product ? "product" : "article";
     1204
     1205    $full_prompt = "Given the existing categories: {$category_list}. Suggest only 1 category for a {$entity_type} titled '{$title}' with details:\n" .
    10351206                   "Prompt: {$prompt}\n" .
    10361207                   "Outline: {$outline}\n" .
     
    10381209                   "Language: {$language}\n" .
    10391210                   "Only return the name of the category";
     1211                   
     1212    error_log($full_prompt);
    10401213
    10411214    $response = easy_gpt_call_openai_api($full_prompt, $model, $temperature, $api_key, $post_id);
     
    10671240    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    10681241
    1069     $full_prompt = "Suggest 3 tags for an article titled '{$title}' with details:\n" .
     1242    $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     1243    $is_product = ($post_type === 'product');
     1244    $entity_type = $is_product ? "product" : "article";
     1245   
     1246    $full_prompt = "Suggest 3 tags for a {$entity_type} titled '{$title}' with details:\n" .
    10701247                   "Prompt: {$prompt}\n" .
    10711248                   "Outline: {$outline}\n" .
    10721249                   "Keywords: {$keywords}\n" .
    10731250                   "Language: {$language}\n" .
    1074                    "Only return the 3 tags separated by commas without the # symbol";
     1251                   "Only return the 3 tags separated by commas without the # symbol.";
     1252
    10751253
    10761254    $response = easy_gpt_call_openai_api($full_prompt, $model, $temperature, $api_key, $post_id);
     
    11031281    $api_key = get_option('easy_gpt_options')['easy_gpt_api_key'];
    11041282
    1105     $full_prompt = "Generate an image for an article titled '{$title}' with the following details, do not include text in the image:\n" .
     1283    $post_type = isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : 'post';
     1284    $is_product = ($post_type === 'product');
     1285    $entity_type = $is_product ? "product" : "article";
     1286   
     1287    $full_prompt = "Generate an image for a {$entity_type} titled '{$title}' with the following details, do not include text in the image:\n" .
    11061288                   "Prompt: {$prompt}\n" .
    11071289                   "Keywords: {$keywords}\n" .
     
    11111293                   "Only return the image URL.";
    11121294
    1113     $response = easy_gpt_call_openai_image_api($full_prompt, $model, $api_key, $post_id);
     1295
     1296    $response = easy_gpt_call_openai_image_api($full_prompt, $model, $image_size, $image_quality, $api_key, $post_id);
    11141297
    11151298    if (!$response) {
     
    11231306
    11241307
    1125 function easy_gpt_call_openai_image_api($prompt, $model, $api_key, $post_id) {
     1308function easy_gpt_call_openai_image_api($prompt, $model, $image_size, $image_quality, $api_key, $post_id) {
    11261309    global $easy_gpt_options;
    11271310
     
    11761359    $new_total_tokens = 0;
    11771360
    1178     $output_cost_per_image = isset($easy_gpt_options['model_costs'][$model]['output']) ? floatval($easy_gpt_options['model_costs'][$model]['output']) : 0.0;
    1179     $image_cost = $output_cost_per_image;
     1361    $image_cost = 0.0;
     1362   
     1363    // Verificar si el modelo seleccionado es un modelo de generación de imágenes
     1364    if (in_array($model, ['dall-e-3', 'dall-e-2'])) {
     1365        if (isset($easy_gpt_options['model_costs'][$model]['image'])) {
     1366            if ($model === 'dall-e-3') {
     1367                if (isset($easy_gpt_options['model_costs'][$model]['image'][$image_quality][$image_size])) {
     1368                    $image_cost = floatval($easy_gpt_options['model_costs'][$model]['image'][$image_quality][$image_size]);
     1369                } else {
     1370                    // Tamaño o calidad no definidos, ya se manejó arriba
     1371                    $image_cost = 0.0;
     1372                }
     1373            } elseif ($model === 'dall-e-2') {
     1374                if (isset($easy_gpt_options['model_costs'][$model]['image'][$image_size])) {
     1375                    $image_cost = floatval($easy_gpt_options['model_costs'][$model]['image'][$image_size]);
     1376                } else {
     1377                    // Tamaño no definido, ya se manejó arriba
     1378                    $image_cost = 0.0;
     1379                }
     1380            }
     1381        }
     1382    } else {
     1383        // Manejar modelos de texto
     1384        $output_cost_per_token = isset($easy_gpt_options['model_costs'][$model]['output']) ? floatval($easy_gpt_options['model_costs'][$model]['output']) : 0.0;
     1385        $image_cost = $output_cost_per_token;
     1386    }
    11801387
    11811388    $total_cost = 0.0;
     
    12591466    $input_cost_per_token = isset($easy_gpt_options['model_costs'][$model]['input']) ? floatval($easy_gpt_options['model_costs'][$model]['input']) : 0.0;
    12601467    $output_cost_per_token = isset($easy_gpt_options['model_costs'][$model]['output']) ? floatval($easy_gpt_options['model_costs'][$model]['output']) : 0.0;
     1468   
     1469    if(strpos($model, 'o1') !== false)
     1470    {
     1471        $temperature = 1;
     1472    }
    12611473
    12621474    // Determinar el endpoint correcto basado en el modelo
    1263     if (strpos($model, 'gpt-4o') !== false || strpos($model, 'gpt-3.5-turbo') !== false || strpos($model, 'gpt-4') !== false) {
     1475    if (strpos($model, 'gpt-4o') !== false || strpos($model, 'gpt-3.5-turbo') !== false || strpos($model, 'gpt-4') !== false || strpos($model, 'o1') !== false) {
    12641476        $url = "https://api.openai.com/v1/chat/completions";
    12651477        $request_body = [
     
    12941506
    12951507$response_code = wp_remote_retrieve_response_code($response);
     1508$response_body = wp_remote_retrieve_body($response);
     1509$decodedResponse = json_decode($response_body, true);
    12961510if ($response_code != 200) {
    12971511    if ($response_code == 401) {
     
    13001514        );
    13011515    }
    1302     throw new Exception('Request error: Response code ' . intval($response_code));
     1516    $error_message = isset($decodedResponse['error']['message']) ? $decodedResponse['error']['message'] : 'Unknown error';
     1517    throw new Exception('Request error: Response code ' . intval($response_code) . ' - ' . esc_html($error_message));
     1518   
    13031519}
    13041520
     
    14721688}
    14731689
     1690function easy_gpt_get_youtube_video_url($title, $keywords, $language, $include_video) {
     1691    // Obtener opciones del plugin
     1692    $options = get_option('easy_gpt_options');
     1693    $api_key = $options['easy_gpt_youtube_api_key'] ?? '';
     1694
     1695
     1696    // Verificar si la API Key está configurada y si el usuario ha habilitado la opción de incluir videos
     1697    if (empty($api_key)) {
     1698        error_log("YouTube API Key is missing.");
     1699        return ['video_url' => ''];
     1700    }
     1701
     1702    if ($include_video !== '1') {
     1703        error_log("YouTube video inclusion is disabled.");
     1704        return ['video_url' => ''];
     1705    }
     1706
     1707    // Refinar palabras clave para mejorar la búsqueda
     1708    $search_query = $keywords;
     1709    $encoded_query = urlencode("\"{$search_query}\"");
     1710
     1711    // Construir la URL con parámetros mejorados
     1712    $api_url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q={$encoded_query}&type=video&maxResults=3&order=relevance&regionCode=" . strtoupper($language) . "&relevanceLanguage=" . strtolower($language) . "&key={$api_key}";
     1713
     1714    error_log("YouTube API Request: " . urldecode($api_url)); // Log para depuración
     1715
     1716    // Hacer la solicitud a la API de YouTube
     1717    $response = wp_remote_get($api_url);
     1718
     1719    if (is_wp_error($response)) {
     1720        error_log("YouTube API Request failed: " . $response->get_error_message());
     1721        return ['error' => 'YouTube API Request failed.'];
     1722    }
     1723
     1724    $body = wp_remote_retrieve_body($response);
     1725    $data = json_decode($body, true);
     1726
     1727    if (isset($data['error'])) {
     1728        error_log("YouTube API Error: " . json_encode($data['error']));
     1729        return ['error' => 'YouTube API Error: ' . $data['error']['message']];
     1730    }
     1731
     1732    // Comprobar si hay resultados
     1733    if (!isset($data['items']) || empty($data['items'])) {
     1734        return ['error' => 'No videos found.'];
     1735    }
     1736
     1737    // Iterar sobre los resultados y verificar si el video es embebible
     1738    foreach ($data['items'] as $item) {
     1739        $video_id = $item['id']['videoId'] ?? '';
     1740        if (!$video_id) {
     1741            continue;
     1742        }
     1743
     1744        // Consultar los detalles del video para verificar si se puede embeber
     1745        $video_details_url = "https://www.googleapis.com/youtube/v3/videos?part=status&id={$video_id}&key={$api_key}";
     1746        $video_response = wp_remote_get($video_details_url);
     1747
     1748        if (is_wp_error($video_response)) {
     1749            error_log("Failed to retrieve video details: " . $video_response->get_error_message());
     1750            continue;
     1751        }
     1752
     1753        $video_body = wp_remote_retrieve_body($video_response);
     1754        $video_data = json_decode($video_body, true);
     1755
     1756        if (isset($video_data['items'][0]['status']['embeddable']) && $video_data['items'][0]['status']['embeddable']) {
     1757            $video_url = "<p>\nhttps://www.youtube.com/watch?v={$video_id}\n</p>";
     1758            error_log("Selected Video: {$video_url}");
     1759            return ['video_url' => $video_url];
     1760        }
     1761    }
     1762
     1763    return ['video_url' => ''];
     1764}
     1765
     1766add_action('admin_footer', 'easy_gpt_video_position_per_post_script');
     1767function easy_gpt_video_position_per_post_script() {
     1768    ?>
     1769    <script>
     1770        jQuery(document).ready(function($) {
     1771            // Seleccionar los elementos
     1772            var select = $('#easy_gpt_video_position_per_post');
     1773            var paragraphContainer = $('#easy_gpt_video_paragraph_container');
     1774
     1775            // Función para mostrar u ocultar el campo de párrafos
     1776            function toggleParagraphInput() {
     1777                if (select.val() === 'after_paragraph') {
     1778                    paragraphContainer.show();
     1779                } else {
     1780                    paragraphContainer.hide();
     1781                }
     1782                console.log("Video Position Changed to:", select.val());
     1783            }
     1784
     1785            // Inicializar el estado al cargar la página
     1786            toggleParagraphInput();
     1787
     1788            // Escuchar el cambio en el select
     1789            select.on('change', toggleParagraphInput);
     1790        });
     1791    </script>
     1792    <?php
     1793}
     1794
    14741795?>
  • easy-gpt-for-wp/trunk/includes/settings-functions.php

    r3227131 r3232882  
    2626
    2727    add_settings_section(
     28        'easy_gpt_youtube_settings_section',
     29        'YouTube Settings',
     30        'easy_gpt_youtube_settings_section_callback',
     31        'easy-gpt-settings'
     32    );
     33   
     34    // Agregar campo para la API Key de YouTube
     35    add_settings_field(
     36        'easy_gpt_youtube_api_key',
     37        'YouTube API Key',
     38        'easy_gpt_youtube_api_key_render',
     39        'easy-gpt-settings',
     40        'easy_gpt_youtube_settings_section'
     41    );
     42   
     43    // Agregar checkbox para habilitar la inclusión de videos en los artículos
     44    add_settings_field(
     45        'easy_gpt_include_videos',
     46        'Include Videos in Articles',
     47        'easy_gpt_include_videos_render',
     48        'easy-gpt-settings',
     49        'easy_gpt_youtube_settings_section'
     50    );
     51
     52    add_settings_field(
     53        'easy_gpt_video_position',
     54        'Video Position in Article',
     55        'easy_gpt_video_position_render',
     56        'easy-gpt-settings',
     57        'easy_gpt_youtube_settings_section'
     58    );
     59
     60    add_settings_section(
    2861        'easy_gpt_article_settings_section',
    2962        'Article Generation Settings',
     
    232265}
    233266
     267function easy_gpt_youtube_settings_section_callback() {
     268    echo 'Configure YouTube API settings for embedding videos in generated articles.';
     269}
     270
     271function easy_gpt_youtube_api_key_render() {
     272    $options = get_option('easy_gpt_options');
     273    $api_key = $options['easy_gpt_youtube_api_key'] ?? '';
     274
     275    // Aplicar máscara, dejando visibles solo los últimos 4 caracteres
     276    if (strlen($api_key) >= 4) {
     277        $masked_key = str_repeat('*', strlen($api_key) - 4) . substr($api_key, -4);
     278    } else {
     279        $masked_key = str_repeat('*', strlen($api_key));
     280    }
     281
     282    echo "<input type='text' id='easy_gpt_youtube_api_key' name='easy_gpt_options[easy_gpt_youtube_api_key]' value='" . esc_attr($masked_key) . "'>";
     283    echo "<p><small>Find your YouTube API Key <a href='https://console.cloud.google.com/apis/library/youtube.googleapis.com' target='_blank'>here</a>.</small></p>";
     284
     285    // Mostrar estado de validación
     286    echo "<div id='easy_gpt_youtube_api_key_validation' style='margin-top: 5px; font-weight: bold;'>";
     287
     288    if (isset($options['youtube_api_key_valid'])) {
     289        switch ($options['youtube_api_key_valid']) {
     290            case 'valid':
     291                echo "<span style='color: green;'>YouTube API Key is valid.</span>";
     292                break;
     293            case 'invalid':
     294                echo "<span style='color: red;'>YouTube API Key is invalid.</span>";
     295                break;
     296            case 'error':
     297                echo "<span style='color: red;'>Error validating the YouTube API Key.</span>";
     298                break;
     299            case 'empty':
     300                echo "<span style='color: blue;'>YouTube API Key is empty.</span>";
     301                break;
     302        }
     303    }
     304
     305    echo "</div>";
     306}
     307
     308function easy_gpt_include_videos_render() {
     309    $options = get_option('easy_gpt_options');
     310    $checked = isset($options['easy_gpt_include_videos']) ? checked($options['easy_gpt_include_videos'], '1', false) : '';
     311
     312    echo "<input type='checkbox' name='easy_gpt_options[easy_gpt_include_videos]' value='1' {$checked}>";
     313    echo "<small>Check this box to automatically include a relevant YouTube video in articles.</small>";
     314}
     315
     316function easy_gpt_video_position_render() {
     317    $options = get_option('easy_gpt_options');
     318    $position = $options['easy_gpt_video_position'] ?? 'end';
     319
     320    echo "<select name='easy_gpt_options[easy_gpt_video_position]' id='easy_gpt_video_position'>";
     321    echo "<option value='start' " . selected($position, 'start', false) . ">Start</option>";
     322    echo "<option value='end' " . selected($position, 'end', false) . ">End</option>";
     323    echo "<option value='random' " . selected($position, 'random', false) . ">Random</option>";
     324    echo "<option value='after_paragraph' " . selected($position, 'after_paragraph', false) . ">After X Paragraphs</option>";
     325    echo "</select>";
     326
     327    echo "<p><small>Choose where to insert the YouTube video in the article.</small></p>";
     328
     329    // Si se selecciona "after_paragraph", mostrar campo para ingresar el número de párrafos
     330    $paragraph_position = $options['easy_gpt_video_paragraph'] ?? 3;
     331    echo "<input type='number' name='easy_gpt_options[easy_gpt_video_paragraph]' id='easy_gpt_video_paragraph' min='1' value='" . esc_attr($paragraph_position) . "' style='display:" . ($position === 'after_paragraph' ? 'inline' : 'none') . ";'>";
     332}
    234333
    235334function easy_gpt_api_key_render() {
     
    246345    echo "<div id='easy_gpt_api_key_validation' style='margin-top: 5px; font-weight: bold;'>";
    247346
    248     // Mostrar el estado de validaci¨®n
     347    // Mostrar el estado de validacin
    249348    if (isset($options['api_key_valid'])) {
    250349        switch ($options['api_key_valid']) {
     
    269368
    270369function easy_gpt_model_render() {
    271     global $easy_gpt_options;;
    272     $options = get_option('easy_gpt_options');
     370    global $easy_gpt_options;
     371    $options = get_option('easy_gpt_options');
     372    $default_model = 'gpt-4o'; // Modelo por defecto
     373
    273374    echo "<select name='easy_gpt_options[easy_gpt_model]'>";
    274375    foreach ($easy_gpt_options['models'] as $model) {
    275376        $selected = (isset($options['easy_gpt_model']) && $options['easy_gpt_model'] == $model) ? 'selected' : '';
     377       
     378        // Si no hay un modelo guardado, usa gpt-4o por defecto
     379        if (!isset($options['easy_gpt_model']) && $model === $default_model) {
     380            $selected = 'selected';
     381        }
     382
    276383        echo "<option value='" . esc_attr($model) . "' " . esc_attr($selected) . ">" . esc_html($model) . "</option>";
    277 
    278     }
    279     echo "</select>";
     384    }
     385    echo "</select>";
     386
     387    // Nota sobre los modelos más lentos y más caros
     388    echo "<p style='font-size: 12px; color: #777; margin-top: 5px;'>
     389        <strong>Note:</strong> Models <code>o1-mini</code> and <code>o1-preview</code> are slower and more expensive compared to others.
     390    </p>";
    280391}
    281392
     
    491602
    492603 
    493     // Si la clave API est¨¢ enmascarada, mantener la clave original
     604    // Si la clave API est enmascarada, mantener la clave original
    494605    if (isset($input['easy_gpt_api_key']) && strpos($input['easy_gpt_api_key'], '****') !== false) {
    495606        $input['easy_gpt_api_key'] = $options['easy_gpt_api_key'];
     
    499610        $input['easy_gpt_api_key'] = sanitize_text_field($input['easy_gpt_api_key']);
    500611    }
    501         // Validar la API Key si no est¨¢ vac¨ªa
     612        // Validar la API Key si no est芍 vac赤a
    502613    if (!empty($input['easy_gpt_api_key'])) {
    503614
     
    539650    $input['easy_gpt_image_style'] = sanitize_text_field($input['easy_gpt_image_style']);
    540651
    541     // Manejar las casillas de verificaci¨®n
     652    // Manejar las casillas de verificacin
    542653    $checkbox_fields = [
    543654        'easy_gpt_subheading_type_h3',
     
    558669    }
    559670
     671    // Si la clave de YouTube está enmascarada, mantener la clave original
     672    if (isset($input['easy_gpt_youtube_api_key']) && strpos($input['easy_gpt_youtube_api_key'], '****') !== false) {
     673        $input['easy_gpt_youtube_api_key'] = $options['easy_gpt_youtube_api_key'];
     674    } else {
     675        $input['easy_gpt_youtube_api_key'] = sanitize_text_field($input['easy_gpt_youtube_api_key']);
     676    }
     677
     678    // Validar la API Key de YouTube si no está vacía
     679    if (!empty($input['easy_gpt_youtube_api_key'])) {
     680        $api_key = $input['easy_gpt_youtube_api_key'];
     681        $test_url = "https://www.googleapis.com/youtube/v3/videos?id=Ks-_Mh1QhMc&key={$api_key}&part=id";
     682        $response = wp_remote_get($test_url);
     683
     684        if (is_wp_error($response)) {
     685            $input['youtube_api_key_valid'] = 'error';
     686        } else {
     687            $status_code = wp_remote_retrieve_response_code($response);
     688            $input['youtube_api_key_valid'] = ($status_code === 200) ? 'valid' : 'invalid';
     689        }
     690    } else {
     691        $input['youtube_api_key_valid'] = 'empty';
     692    }
     693
     694
     695    // Manejar la casilla de verificación de inclusión de videos
     696    $input['easy_gpt_include_videos'] = isset($input['easy_gpt_include_videos']) ? '1' : '0';
     697
     698    // Sanitizar la opción de posición del video
     699    $valid_positions = ['start', 'end', 'random', 'after_paragraph'];
     700    $input['easy_gpt_video_position'] = in_array($input['easy_gpt_video_position'], $valid_positions) ? $input['easy_gpt_video_position'] : 'end';
     701
     702    // Sanitizar el número de párrafos si se elige "after_paragraph"
     703    $input['easy_gpt_video_paragraph'] = isset($input['easy_gpt_video_paragraph']) ? max(1, intval($input['easy_gpt_video_paragraph'])) : 3;
     704
     705
    560706    return $input;
    561707}
     
    586732    }
    587733}
     734
     735
     736add_action('admin_footer', 'easy_gpt_video_position_script');
     737function easy_gpt_video_position_script() {
     738    ?>
     739    <script>
     740        document.addEventListener("DOMContentLoaded", function() {
     741            let select = document.getElementById("easy_gpt_video_position");
     742            let paragraphInput = document.getElementById("easy_gpt_video_paragraph");
     743
     744            function toggleParagraphInput() {
     745                paragraphInput.style.display = (select.value === "after_paragraph") ? "inline" : "none";
     746            }
     747
     748            select.addEventListener("change", toggleParagraphInput);
     749            toggleParagraphInput();
     750        });
     751    </script>
     752    <?php
     753}
     754
     755add_action('wp_ajax_validate_youtube_api_key', 'easy_gpt_validate_youtube_api_key');
     756
     757function easy_gpt_validate_youtube_api_key() {
     758    check_ajax_referer('validate_api_key_nonce', 'security');
     759
     760    $api_key = sanitize_text_field($_POST['api_key']);
     761
     762    $test_url = "https://www.googleapis.com/youtube/v3/videos?id=Ks-_Mh1QhMc&key={$api_key}&part=id";
     763    $response = wp_remote_get($test_url);
     764
     765    if (is_wp_error($response)) {
     766        wp_send_json_error(['message' => 'Error validating the YouTube API Key.']);
     767    }
     768
     769    $status_code = wp_remote_retrieve_response_code($response);
     770
     771    if ($status_code === 200) {
     772        wp_send_json_success(['message' => 'YouTube API Key is valid.']);
     773    } else {
     774        wp_send_json_error(['message' => 'YouTube API Key is invalid.']);
     775    }
     776}
     777
     778add_action('admin_enqueue_scripts', 'easy_gpt_admin_youtube_styles');
     779
     780function easy_gpt_admin_youtube_styles($hook) {
     781    if ($hook != 'toplevel_page_easy-gpt-settings' && $hook != 'easy-gpt-settings_page_easy-gpt-settings') {
     782        return;
     783    }
     784
     785    $plugin_url = plugin_dir_url(dirname(__FILE__));
     786
     787    wp_enqueue_script('easy_gpt_admin_youtube_js', $plugin_url . 'js/admin-youtube.js', ['jquery'], null, true);
     788
     789    wp_localize_script('easy_gpt_admin_youtube_js', 'easyGptYoutubeAjax', [
     790        'ajax_url' => admin_url('admin-ajax.php'),
     791        'nonce'    => wp_create_nonce('validate_api_key_nonce'),
     792    ]);
     793}
  • easy-gpt-for-wp/trunk/js/bulk-generation.js

    r3197201 r3232882  
    11jQuery(document).ready(function($) {
    22
     3    // Inicializar las pestañas
    34    $("#easy-gpt-tabs").tabs();
    45    $("#easy-gpt-image-tabs").tabs();
    56
    6     // Function to refresh image tabs
     7    // Función para refrescar las pestañas de imágenes
    78    function refreshImageTabs() {
    89        var numImages = $('#easy_gpt_num_images').val();
     
    1011        var imageTabsContent = $('#easy-gpt-image-tabs');
    1112
    12         // Clear existing tabs
     13        // Limpiar pestañas existentes
    1314        imageTabsList.empty();
    1415        imageTabsContent.children('div[id^="image-tab-"]').remove();
    1516
    16         // Generate new tabs
     17        // Generar nuevas pestañas
    1718        for (var i = 1; i <= numImages; i++) {
    1819            imageTabsList.append('<li><a href="#image-tab-' + i + '">Image ' + i + '</a></li>');
     
    2425                    '</div>' +
    2526                    '<div class="easy-gpt-inline">' +
    26                         '<input type="checkbox" id="use_same_easy_gpt_image_prompt_' + i + '" name="use_same_easy_gpt_image_prompt_' + i + '" class="adjusted-checkbox">' +
     27                        '<input type="checkbox" id="use_same_easy_gpt_image_prompt_' + i + '" name="use_same_image_prompts[]" class="adjusted-checkbox">' +
    2728                        '<label for="use_same_easy_gpt_image_prompt_' + i + '" class="normal-label">Use same Image ' + i + ' instructions for all articles</label>' +
    2829                    '</div>' +
     
    3132        }
    3233
    33         // Refresh the tabs
     34        // Refrescar las pestañas
    3435        imageTabsContent.tabs("refresh");
    3536    }
    3637
    37     // Event listener for changes in the number of images
     38    // Evento para cambios en el número de imágenes
    3839    $('#easy_gpt_num_images').on('change input', function() {
    3940        refreshImageTabs();
    4041    });
    4142
    42     // Initial refresh of image tabs
     43    // Refrescar las pestañas de imágenes al cargar
    4344    refreshImageTabs();
    4445
     46    // Función para manejar la lógica de categorías y etiquetas
    4547    function toggleCategoryTagSettings() {
    4648        if ($('#easy_gpt_auto_categories').is(':checked')) {
    4749            console.log('Categories checkbox is checked');
     50            // Aquí puedes agregar lógica adicional si es necesario
    4851        } else {
    4952            console.log('Categories checkbox is not checked');
     53            // Aquí puedes agregar lógica adicional si es necesario
    5054        }
    5155        if ($('#easy_gpt_auto_tags').is(':checked')) {
    5256            console.log('Tags checkbox is checked');
     57            // Aquí puedes agregar lógica adicional si es necesario
    5358        } else {
    5459            console.log('Tags checkbox is not checked');
    55         }
    56     }
    57 
    58     // Toggle on page load
    59     toggleCategoryTagSettings();
    60 
    61     // Toggle on change
     60            // Aquí puedes agregar lógica adicional si es necesario
     61        }
     62    }
     63
     64    // Evento para cambios en categorías y etiquetas
    6265    $('#easy_gpt_auto_categories, #easy_gpt_auto_tags').change(function() {
    6366        toggleCategoryTagSettings();
    6467    });
    6568
     69    // Ejecutar en carga inicial
     70    toggleCategoryTagSettings();
     71
    6672    var isRequesting = false;
    6773
     74    // Evento para el botón "Generate Articles"
    6875    $('#generate-all').off('click').on('click', function(e) {
    6976        e.preventDefault();
    7077
    71         // Get the values from the form fields and log them
     78        // Obtener y validar valores del formulario
    7279        var prompt = $('#easy_gpt_prompt').val().trim();
    7380        var keywords = $('#easy_gpt_keywords').val().trim();
     
    7885        console.log('Number of Articles:', numArticles);
    7986
    80         // Validate that 'prompt' and 'keywords' are not empty
    8187        if (prompt === '') {
    8288            console.error('Validation Error: Prompt field is empty.');
    8389            alert('Please enter a Prompt.');
    84             return; // Exit the function if validation fails
     90            return;
    8591        }
    8692
     
    8894            console.error('Validation Error: Keywords field is empty.');
    8995            alert('Please enter Keywords.');
    90             return; // Exit the function if validation fails
     96            return;
    9197        }
    9298
     
    94100            console.error('Validation Error: Number of articles is invalid.');
    95101            alert('The number of articles must be at least 1.');
    96             return; // Exit the function if validation fails
    97         }
    98 
    99         // If all validations pass, proceed with the AJAX request
     102            return;
     103        }
     104
    100105        console.log('All validations passed. Preparing to send AJAX request.');
    101        
     106
    102107        if (isRequesting) return;
    103    
     108
    104109        isRequesting = true;
    105110        var $button = $(this);
    106111        $button.text('Requesting...');
    107        
     112
     113        console.log("Content Type:", $('#content_type').val());
     114
    108115        var data = {
    109116            'action': 'save_bulk_generation',
     
    112119            'keywords': $('#easy_gpt_keywords').val(),
    113120            'num_articles': $('#easy_gpt_number_of_articles').val(),
     121            'content_type': $('#content_type').val(),
     122            'enable_product_prices': $('#enable_product_prices').is(':checked') ? 1 : 0,
     123            'product_prices': $('#easy_gpt_product_prices').val(),
    114124            'model': $('#easy_gpt_model').val(),
    115125            'temperature': $('#easy_gpt_temperature').val(),
     
    148158            'start_date': $('#start_date').val(),
    149159            'start_time': $('#start_time').val(),
    150             'articles_per_interval': $('#articles_per_interval').val()
     160            'articles_per_interval': $('#articles_per_interval').val(),
     161            'easy_gpt_include_videos_bulk': $('#easy_gpt_include_videos_bulk').is(':checked') ? 1 : 0,
     162            'easy_gpt_video_position_bulk': $('#easy_gpt_video_position_bulk').val(),
     163            'easy_gpt_video_paragraph_bulk': $('#easy_gpt_video_paragraph_bulk').val(),
    151164        };
    152        
    153          console.log(data);
    154    
    155         // Collecting image prompts
     165
     166        console.log(data);
     167
     168        // Colección de image_prompts y use_same_image_prompts
    156169        var image_prompts = [];
    157170        var use_same_image_prompts = [];
     
    164177        data['image_prompts'] = JSON.stringify(image_prompts);
    165178        data['use_same_image_prompts'] = JSON.stringify(use_same_image_prompts);
    166    
     179
    167180        $.post(ajaxurl, data, function(response) {
    168             console.log(response); // Añadir un registro para depuración
     181            console.log(response); // Registro para depuración
    169182            if (response.success) {
    170183                alert('Generation saved! ID: ' + response.data.id);
     
    178191        });
    179192    });
    180    
     193
     194    // Función para iniciar la generación de artículos
    181195    function startArticleGeneration(generationId, numArticles) {
    182         // Lógica para iniciar la generación de artículos
    183196        console.log("Starting generation for ID: " + generationId + " with " + numArticles + " articles.");
    184    
     197
    185198        var data = {
    186199            'action': 'easy_gpt_start_generation',
     
    189202            'num_articles': numArticles
    190203        };
    191    
     204
    192205        $.post(ajaxurl, data, function(response) {
    193             console.log(response); // Añadir un registro para depuración
     206            console.log(response); // Registro para depuración
    194207            if (response.success) {
    195208                console.log("Generation started successfully.");
     
    200213        });
    201214    }
    202    
     215
     216    // Función para obtener tipos de subencabezados
    203217    function getSubheadingTypes() {
    204218        var types = [];
     
    207221        return types.join(',');
    208222    }
     223
     224    // Manejar la lógica de mostrar/ocultar para Video Position
     225    $('#easy_gpt_video_position_bulk').on('change', function() {
     226        console.log("Video Position changed to:", $(this).val()); // Depuración
     227        if ($(this).val() === 'after_paragraph') {
     228            $('#easy_gpt_video_paragraph_container_bulk').slideDown();
     229        } else {
     230            $('#easy_gpt_video_paragraph_container_bulk').slideUp();
     231        }
     232    });
     233
     234    // Inicializar el estado al cargar la página para Video Position
     235    if ($('#easy_gpt_video_position_bulk').val() === 'after_paragraph') {
     236        $('#easy_gpt_video_paragraph_container_bulk').show();
     237    } else {
     238        $('#easy_gpt_video_paragraph_container_bulk').hide();
     239    }
     240
     241    // Manejar la lógica de mostrar/ocultar para Content Type (Articles/Products)
     242    $('#content_type_toggle').on('change', function() {
     243        if ($(this).is(':checked')) {
     244            $('#content_type').val('product');
     245            $('.products-label').addClass('active');
     246            $('.articles-label').removeClass('active');
     247            $('#price-option-container').slideDown();
     248        } else {
     249            $('#content_type').val('article');
     250            $('.articles-label').addClass('active');
     251            $('.products-label').removeClass('active');
     252            $('#price-option-container').slideUp();
     253            $('#enable_product_prices').prop('checked', false);
     254            $('#price-input-container').slideUp();
     255        }
     256        console.log("Content Type Updated:", $('#content_type').val()); // Depuración
     257    });
     258
     259    // Manejar la lógica de mostrar/ocultar para Precios de Productos
     260    $('#enable_product_prices').on('change', function() {
     261        if ($(this).is(':checked')) {
     262            $('#price-input-container').slideDown();
     263        } else {
     264            $('#price-input-container').slideUp();
     265        }
     266    });
     267
     268    // Inicializar el estado al cargar la página para Precios de Productos
     269    if ($('#enable_product_prices').is(':checked')) {
     270        $('#price-input-container').show();
     271    } else {
     272        $('#price-input-container').hide();
     273    }
     274
    209275});
  • easy-gpt-for-wp/trunk/js/easy-gpt-custom.js

    r3227131 r3232882  
    11jQuery(document).ready(function($) {
    22
     3    var postType = isWooCommerceProductPage() ? 'product' : 'post';
     4   
     5    function isWooCommerceProductPage() {
     6        return $('body').hasClass('post-type-product');
     7    }
     8
     9    console.log("Detected post type:", postType);
    310
    411    tinymce.init({
     
    205212        var $button = $('#generate-image-' + imageIndex);
    206213        var originalText = $button.text();
     214        var postType = isWooCommerceProductPage() ? 'product' : 'post';
    207215        $button.text('Generating...');
    208216   
     
    211219            nonce: $('#easy_gpt_nonce').val(),
    212220            prompt: $('#easy_gpt_image_prompt_' + imageIndex).val() || $('#easy_gpt_prompt').val(),
     221            'post_type': postType,
    213222            keywords: $('#easy_gpt_keywords').val(),
    214223            title: $('#easy_gpt_generated_title_preview').val(),
     
    273282    var originalText = 'Generate Title';
    274283    var postID = getURLParameter('post');
     284    var postType = isWooCommerceProductPage() ? 'product' : 'post';
     285
    275286   
    276287    $preview.val('');
     
    284295        'nonce': $('#easy_gpt_nonce').val(),
    285296        'post_id': postID,
     297        'post_type': postType,
    286298        'prompt': $('#easy_gpt_prompt').val(),
    287299        'keywords': $('#easy_gpt_keywords').val(),
     300        'use_specific_instructions': $('#use_specific_title_instructions').is(':checked') ? 'true' : 'false',
    288301        'specific_instructions': $('#easy_gpt_specific_instructions_title').val(),
    289302        'model': $('#easy_gpt_model').val(),
     
    348361    var originalText = $button.text();
    349362    var postID = getURLParameter('post');
     363    var postType = isWooCommerceProductPage() ? 'product' : 'post';
    350364    $button.text('Generating...');
    351365    var fillBar = showLoadingBar()
     
    357371        prompt: $('#easy_gpt_prompt').val(),
    358372        post_id: postID,
     373        'post_type': postType,
    359374        keywords: $('#easy_gpt_keywords').val(),
    360375        title: $('#easy_gpt_generated_title_preview').val(),
     
    432447    var includeBibliography = $('#easy_gpt_include_bibliography').is(':checked');
    433448    var includeLinks = $('#easy_gpt_include_links').is(':checked');
    434     var includeYouTubeVideo = $('#easy_gpt_include_youtube_video').is(':checked');
     449    var include_videos_per_post = $('#easy_gpt_include_videos_per_post').is(':checked') ? '1' : '0';
     450    var video_position_per_post = $('#easy_gpt_video_position_per_post').val();
     451    var video_paragraph_per_post = $('#easy_gpt_video_paragraph').val();
    435452    var autoCategories = $('#easy_gpt_auto_categories').is(':checked');
    436453    var autoTags = $('#easy_gpt_auto_tags').is(':checked');
    437454    var postID = getURLParameter('post');
     455    var postType = isWooCommerceProductPage() ? 'product' : 'post';
    438456    $button.text('Generating...');
    439457    var fillBar = showLoadingBar()
     
    444462        nonce: $('#easy_gpt_nonce').val(),
    445463        post_id: postID,
     464        post_type: postType,
    446465        title: $('#easy_gpt_generated_title_preview').val(),
    447466        prompt: $('#easy_gpt_prompt').val(),
     
    459478        'easy_gpt_includeBibliography': includeBibliography,
    460479        'easy_gpt_includeLinks': includeLinks,
    461         'easy_gpt_include_youtube_video': includeYouTubeVideo
     480        easy_gpt_include_videos_per_post: include_videos_per_post,
     481        easy_gpt_video_position_per_post: video_position_per_post,
     482        easy_gpt_video_paragraph_per_post: video_paragraph_per_post
    462483    };
    463484
     
    513534    var originalText = $button.text();
    514535    var postID = getURLParameter('post');
     536    var postType = isWooCommerceProductPage() ? 'product' : 'post';
    515537    $button.text('Generating...'); // Texto de carga
    516538    var fillBar = showLoadingBar()
     
    521543        nonce: $('#easy_gpt_nonce').val(),
    522544        post_id: postID,
     545        'post_type': postType,
    523546        title: $('#easy_gpt_generated_title_preview').val(),
    524547        prompt: $('#easy_gpt_prompt').val(),
     
    575598    var originalText = $button.text();
    576599    var postID = getURLParameter('post');
     600    var postType = isWooCommerceProductPage() ? 'product' : 'post';
    577601    $button.text('Generating...'); // Texto de carga
    578602    var fillBar = showLoadingBar()
     
    583607        nonce: $('#easy_gpt_nonce').val(),
    584608        post_id: postID,
     609        'post_type': postType,
    585610        title: $('#easy_gpt_generated_title_preview').val(),
    586611        prompt: $('#easy_gpt_prompt').val(),
     
    847872
    848873        // Actualizar el excerpt
    849          if (isGutenbergActive()) {
     874        if (isGutenbergActive() && !isWooCommerceProductPage()) {
    850875            if (wp.data) {
    851876                wp.data.dispatch('core/editor').editPost({ excerpt: excerpt });
     
    853878                console.error('No se puede actualizar el extracto porque wp.data no est¨¢ disponible.');
    854879            }
    855          }
    856          else
    857          {
    858              $('#excerpt').val(excerpt);
    859          }
     880        } else {
     881            // Verificar si es un producto de WooCommerce
     882            if (isWooCommerceProductPage()) {
     883                // WooCommerce usa el excerpt como "Product short description"
     884                $('#excerpt').val(excerpt);
     885       
     886                // Si el editor TinyMCE est¨¢ activo, actualizarlo tambi¨¦n
     887                if (tinymce.get('excerpt')) {
     888                    tinymce.get('excerpt').setContent(excerpt);
     889                }
     890       
     891                // Asegurar que el valor tambi¨¦n se refleje en el iframe del editor visual
     892                var excerptIframe = document.getElementById('excerpt_ifr');
     893                if (excerptIframe) {
     894                    var iframeContent = excerptIframe.contentDocument || excerptIframe.contentWindow.document;
     895                    var iframeBody = iframeContent.querySelector('body');
     896                    if (iframeBody) {
     897                        iframeBody.innerHTML = excerpt;
     898                    }
     899                }
     900       
     901                console.log("Extracto actualizado en WooCommerce:", excerpt);
     902            } else {
     903                // Editor Cl¨¢sico est¨¢ndar (para posts normales)
     904                $('#excerpt').val(excerpt);
     905       
     906                // Tambi¨¦n actualizar TinyMCE si est¨¢ habilitado
     907                if (tinymce.get('excerpt')) {
     908                    tinymce.get('excerpt').setContent(excerpt);
     909                }
     910       
     911                console.log("Extracto actualizado en el Editor Cl¨¢sico:", excerpt);
     912            }
     913        }
     914
    860915
    861916    // Actualizar el t¨ªtulo SEO y la descripci¨®n SEO
     
    893948
    894949    var tags = $('#easy_gpt_generated_tag_preview').val();
    895     if (isGutenbergActive()) {
     950    if (isGutenbergActive() && !isWooCommerceProductPage()) {
    896951        var postId = wp.data.select('core/editor').getCurrentPostId();
    897952    }
    898953   
    899954             // Actualizar el t¨ªtulo y contenido
    900         if (isGutenbergActive()) {
     955        if (isGutenbergActive() && !isWooCommerceProductPage()) {
    901956            if (window.wp && wp.data && wp.data.dispatch && wp.blocks) {
    902957                wp.data.dispatch('core/editor').editPost({ title: title });
     
    944999    // Si est¨¢ marcado "Set first generated image as featured", establecer la primera imagen como destacada
    9451000    if (setFeaturedImage && imageIds.length > 0) {
    946         if (isGutenbergActive()) {
     1001        if (isGutenbergActive() && !isWooCommerceProductPage()) {
    9471002           
    9481003            wp.data.dispatch('core/editor').editPost({ featured_media: imageIds[0] });
     
    9531008        }
    9541009    }
    955    
     1010
     1011
     1012    if (isWooCommerceProductPage() && imageIds.length > 1) {
     1013        console.log('A0Š9adiendo im¨¢genes a la galer¨ªa de WooCommerce...');
     1014       
     1015        var galleryField = $('#product_image_gallery');
     1016        var galleryContainer = $('.product_images');
     1017       
     1018        // Obtener IDs actuales de la galer¨ªa
     1019        var currentGallery = galleryField.val();
     1020        var newGalleryIds = imageIds.slice(1).join(','); // Omitir la imagen destacada
     1021       
     1022        // Actualizar el campo oculto con los nuevos IDs
     1023        if (currentGallery) {
     1024            galleryField.val(currentGallery + ',' + newGalleryIds);
     1025        } else {
     1026            galleryField.val(newGalleryIds);
     1027        }
     1028       
     1029        console.log('Galer¨ªa actualizada:', galleryField.val());
     1030   
     1031        // A0Š9adir las im¨¢genes visualmente a la lista
     1032        imageData.slice(1).forEach(data => {
     1033            var imgElement = `
     1034                <li class="image" data-attachment_id="${data.attachment_id}">
     1035                    <img src="${data.url}" alt="" />
     1036                    <ul class="actions">
     1037                        <li><a href="#" class="delete tips" data-tip="Delete image">7¾0</a></li>
     1038                    </ul>
     1039                </li>`;
     1040            galleryContainer.append(imgElement);
     1041        });
     1042   
     1043        console.log('Im¨¢genes a0Š9adidas a la interfaz.');
     1044    }
     1045
     1046
    9561047    // Insertar etiquetas <img> en el contenido HTML, excluyendo la imagen destacada si aplica
    9571048    var newContent = await insertImagesIntoContent(content, savedImageUrls, setFeaturedImage);
     
    9691060       
    9701061             // Actualizar el t¨ªtulo y contenido
    971         if (isGutenbergActive()) {
     1062        if (isGutenbergActive() && !isWooCommerceProductPage()) {
    9721063            if (window.wp && wp.data && wp.data.dispatch && wp.blocks) {
    9731064                wp.data.dispatch('core/editor').resetBlocks(wp.blocks.parse(newContent));
     
    9821073       
    9831074        // Intentar actualizar las categor¨ªas y etiquetas
    984         if (isGutenbergActive()) {
     1075        if (isGutenbergActive() && !isWooCommerceProductPage()) {
    9851076            await handleCategoryUpdate();
    9861077            await addTagsToPost(postId, tags);
    9871078        } else {
    988             // Actualizar categor¨ªas en el Editor Cl¨¢sico
     1079            // Obtener las categor¨ªas y etiquetas generadas por Easy GPT
    9891080            var categoriesText = $('#easy_gpt_generated_category_preview').val();
    9901081            var tagsText = $('#easy_gpt_generated_tag_preview').val();
    991            
    992             console.log("Categories to be marked:", categoriesText); // Muestra las categor¨ªas que se intentan marcar
    993        
    994             if (categoriesText) {
    995                 var categoryArray = categoriesText.split(',');
    996                 $('#categorychecklist input:checked, #categorychecklist-pop input:checked').prop('checked', false);
    997        
    998                 console.log("All categories in checklist:");
    999                 $('#categorychecklist input[type=checkbox], #categorychecklist-pop input[type=checkbox]').each(function() {
    1000                     var checkboxLabel = $(this).parent().text().trim();
    1001                     console.log("Label text:", checkboxLabel); // Esto te mostrar¨¢ todos los textos de etiqueta en la consola
    1002                 });
    1003        
    1004                 categoryArray.forEach(function(categoryName) {
    1005                     categoryName = categoryName.trim();
    1006                     var found = false;
     1082       
     1083            console.log("Categor¨ªas a marcar:", categoriesText);
     1084            console.log("Etiquetas a a0Š9adir:", tagsText);
     1085       
     1086            // Detectar si estamos en un producto de WooCommerce
     1087            if (isWooCommerceProductPage()) {
     1088                // Actualizar categor¨ªas en WooCommerce
     1089                if (categoriesText) {
     1090                    var categoryArray = categoriesText.split(',');
     1091                   
     1092                    // Desmarcar todas las categor¨ªas previamente seleccionadas en WooCommerce
     1093                    $('#product_catchecklist input:checked').prop('checked', false);
     1094                   
     1095                    categoryArray.forEach(function(categoryName) {
     1096                        categoryName = categoryName.trim();
     1097                        var found = false;
     1098                       
     1099                        $('#product_catchecklist input[type=checkbox]').each(function() {
     1100                            var checkboxLabel = $(this).parent().text().trim();
     1101                            if (checkboxLabel.toLowerCase() === categoryName.toLowerCase()) {
     1102                                $(this).prop('checked', true);
     1103                                found = true;
     1104                                console.log("Categor¨ªa marcada en WooCommerce:", categoryName);
     1105                            }
     1106                        });
     1107                       
     1108                        if (!found) {
     1109                            console.error("Categor¨ªa no encontrada en WooCommerce:", categoryName);
     1110                        }
     1111                    });
     1112                } else {
     1113                    console.error('No se encontraron categor¨ªas generadas para WooCommerce.');
     1114                }
     1115       
     1116                // Actualizar etiquetas en WooCommerce
     1117                if (tagsText) {
     1118                    var tagsArray = tagsText.split(',');
     1119                   
     1120                    tagsArray.forEach(function(tag) {
     1121                        tag = tag.trim();
     1122                        $('#new-tag-product_tag').val(tag);
     1123       
     1124                        // Simular que el usuario presiona "Enter" para agregar la etiqueta
     1125                        $('#new-tag-product_tag').trigger($.Event('keypress', { which: 13, keyCode: 13 }));
     1126       
     1127                        console.log("Etiqueta a0Š9adida en WooCommerce:", tag);
     1128                    });
     1129                } else {
     1130                    console.error('No se encontraron etiquetas generadas para WooCommerce.');
     1131                }
     1132       
     1133            } else {
     1134                // Si no es un producto, actualizar categor¨ªas normales en el Editor Cl¨¢sico
     1135                if (categoriesText) {
     1136                    var categoryArray = categoriesText.split(',');
     1137                    $('#categorychecklist input:checked, #categorychecklist-pop input:checked').prop('checked', false);
     1138       
     1139                    console.log("Todas las categor¨ªas en la lista:");
    10071140                    $('#categorychecklist input[type=checkbox], #categorychecklist-pop input[type=checkbox]').each(function() {
    10081141                        var checkboxLabel = $(this).parent().text().trim();
    1009                         if (checkboxLabel.toLowerCase() === categoryName.toLowerCase()) {
    1010                             $(this).prop('checked', true);
    1011                             found = true;
    1012                             console.log("Category marked:", categoryName);
     1142                        console.log("Texto de la categor¨ªa:", checkboxLabel);
     1143                    });
     1144       
     1145                    categoryArray.forEach(function(categoryName) {
     1146                        categoryName = categoryName.trim();
     1147                        var found = false;
     1148                        $('#categorychecklist input[type=checkbox], #categorychecklist-pop input[type=checkbox]').each(function() {
     1149                            var checkboxLabel = $(this).parent().text().trim();
     1150                            if (checkboxLabel.toLowerCase() === categoryName.toLowerCase()) {
     1151                                $(this).prop('checked', true);
     1152                                found = true;
     1153                                console.log("Categor¨ªa marcada:", categoryName);
     1154                            }
     1155                        });
     1156                        if (!found) {
     1157                            console.error("Categor¨ªa no encontrada:", categoryName);
    10131158                        }
    10141159                    });
    1015                     if (!found) {
    1016                         console.error("Category not found in checklist:", categoryName);
    1017                     }
    1018                 });
    1019             } else {
    1020                 console.error('No categories text found.');
    1021             }
    1022 
    1023             // Actualizar etiquetas en el Editor Cl¨¢sico
    1024             $('#new-tag-post_tag').val(tagsText);
    1025         }
     1160                } else {
     1161                    console.error('No se encontraron categor¨ªas generadas.');
     1162                }
     1163       
     1164                // Actualizar etiquetas en el Editor Cl¨¢sico (para posts normales)
     1165                $('#new-tag-post_tag').val(tagsText);
     1166            }
     1167        }
     1168
    10261169
    10271170        // Intentar nuevamente despu¨¦s de 1 segundo si no se logr¨® en el primer intento
     
    10331176
    10341177        try {
    1035             if (isGutenbergActive()) {
     1178            if (isGutenbergActive() && !isWooCommerceProductPage()) {
    10361179                // Suponiendo que el servidor expone un endpoint que actualiza los tokens y costos
    10371180                const postId = wp.data.select('core/editor').getCurrentPostId();
     
    11021245    async function handleCategoryUpdate() {
    11031246        try {
    1104             if (isGutenbergActive()) {
     1247            if (isGutenbergActive() && !isWooCommerceProductPage()) {
    11051248                const categories = await wp.data.select('core').getEntityRecords('taxonomy', 'category', { per_page: -1 });
    11061249                if (!categories || categories.length === 0) {
    1107                     console.error('No se pudieron cargar las categor¨ªas.');
    1108                     return;
     1250                    //console.error('No se pudieron cargar las categor¨ªas.');
     1251                    //return;
    11091252                }
    11101253   
     
    11121255                const category = categories.find(cat => cat.name.toLowerCase() === categoryName.toLowerCase());
    11131256                if (!category) {
    1114                     console.error('Categor¨ªa no encontrada:', categoryName);
    1115                     return;
     1257                    //console.error('Categor¨ªa no encontrada:', categoryName);
     1258                    //return;
    11161259                }
    11171260   
     
    11281271   
    11291272    async function addTagsToPost(postId, tags) {
    1130         if (isGutenbergActive()) {
    1131             const tagsArray = tags.split(',').map(tag => tag.trim());
     1273        if (isGutenbergActive() && !isWooCommerceProductPage()) {
     1274            const tagsArray = tags.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0);
     1275           
     1276            if (tagsArray.length === 0) {
     1277                console.error('No se proporcionaron etiquetas v¨¢lidas.');
     1278                return;
     1279            }
     1280   
    11321281            const existingTags = await wp.data.select('core').getEntityRecords('taxonomy', 'post_tag', { per_page: -1 }) || [];
    11331282   
     1283            // Crear un mapa para b¨²squeda r¨¢pida
     1284            const existingTagsMap = new Map(existingTags.map(tag => [tag.name.toLowerCase(), tag.id]));
     1285   
    11341286            let tagIds = await Promise.all(tagsArray.map(async (tag) => {
    1135                 let foundTag = existingTags.find(t => t.name.toLowerCase() === tag.toLowerCase());
    1136                 if (foundTag) {
    1137                     return foundTag.id;
     1287                const tagLower = tag.toLowerCase();
     1288                if (existingTagsMap.has(tagLower)) {
     1289                    return existingTagsMap.get(tagLower);
    11381290                } else {
    11391291                    try {
    1140                         // Create new tag if it doesn't exist
    1141                         const newTag = await wp.data.dispatch('core').saveEntityRecord('taxonomy', 'post_tag', { name: tag });
    1142                         return newTag.id;
     1292                        // Generar un slug sanitizado
     1293                        const slug = sanitizeSlug(tag);
     1294                       
     1295                        // Crear una nueva etiqueta
     1296                        const newTag = await wp.data.dispatch('core').saveEntityRecord('taxonomy', 'post_tag', {
     1297                            name: tag,
     1298                            slug: slug
     1299                        });
     1300                       
     1301                        if (newTag && newTag.id) {
     1302                            // A0Š9adir al mapa para evitar duplicados
     1303                            existingTagsMap.set(tagLower, newTag.id);
     1304                            return newTag.id;
     1305                        } else {
     1306                            console.error(`La etiqueta "${tag}" se cre¨® pero no se pudo obtener su ID.`);
     1307                            return null;
     1308                        }
    11431309                    } catch (error) {
    1144                         console.error(`Error creating tag "${tag}":`, error);
     1310                        console.error(`Error al crear la etiqueta "${tag}":`, error);
     1311                       
     1312                        // Intentar extraer detalles del error
     1313                        if (error && error.message) {
     1314                            console.error(`Detalles del error: ${error.message}`);
     1315                        }
    11451316                        return null;
    11461317                    }
     
    11481319            }));
    11491320   
    1150             // Filtrar los IDs nulos
    1151             tagIds = tagIds.filter(id => id !== null);
    1152    
    1153             // A0Š9adir etiquetas al post
     1321            // Filtrar valores falsy (null, undefined, etc.)
     1322            tagIds = tagIds.filter(id => id);
     1323   
    11541324            if (tagIds.length > 0) {
    1155                 await wp.data.dispatch('core/editor').editPost({ tags: tagIds });
     1325                try {
     1326                    await wp.data.dispatch('core/editor').editPost({ tags: tagIds });
     1327                    console.log(`Etiquetas actualizadas correctamente para el post ID ${postId}.`);
     1328                } catch (error) {
     1329                    console.error('Error al actualizar las etiquetas del post:', error);
     1330                }
    11561331            } else {
    1157                 console.error('No tags were added because no valid tags were found or provided.');
    1158             }
    1159         }
    1160     }
     1332                console.error('No se a0Š9adieron etiquetas porque no se encontraron o proporcionaron etiquetas v¨¢lidas.');
     1333            }
     1334        } else {
     1335            console.warn('Gutenberg no est¨¢ activo o est¨¢s en una p¨¢gina de producto de WooCommerce.');
     1336        }
     1337    }
     1338   
     1339    // Funci¨®n de sanitizaci¨®n de slugs
     1340    function sanitizeSlug(text) {
     1341        return text
     1342            .toString()
     1343            .normalize('NFD') // Descompone caracteres compuestos
     1344            .replace(/[\u0300-\u036f]/g, '') // Elimina diacr¨ªticos
     1345            .toLowerCase()
     1346            .trim()
     1347            .replace(/\s+/g, '-') // Reemplaza espacios por guiones
     1348            .replace(/[^a-z0-9\-]/g, '') // Elimina caracteres no alfanum¨¦ricos y guiones
     1349            .replace(/\-\-+/g, '-'); // Reemplaza m¨²ltiples guiones por uno solo
     1350    }
     1351
    11611352   
    11621353       
     
    11981389
    11991390function generateCategories(postID) {
     1391
     1392    var postType = isWooCommerceProductPage() ? 'product' : 'post';
     1393
    12001394    $.ajax({
    12011395        url: ajaxurl,
     
    12041398            action: 'generate_categories',
    12051399            post_id: postID,
     1400            post_type: postType,
    12061401            nonce: $('#easy_gpt_nonce').val(),
    12071402            title: $('#easy_gpt_generated_title_preview').val(),
  • easy-gpt-for-wp/trunk/readme.txt

    r3227131 r3232882  
    11=== Easy GPT for WP | AI Content Generator===
    22Contributors: Ignacio Gil Alvarez
    3 Tags: Easy GPT for WP, gpt-4o, ai content generator, AI Writer, DALL-E 3
     3Tags: ai content generator, Ai content writer, gpt o1, AI Writer, DALL-E 3
    44Requires at least: WordPress 5.3
    55Tested up to: 6.7.1
    66Requires PHP: 7.3
    7 Stable tag: 1.05
     7Stable tag: 1.06
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010
    11 Generate rich, SEO-friendly content for your WordPress site using the power of OpenAI's GPT models. Fully compatible with Yoast SEO.
     11Generate rich, SEO-friendly content for your WordPress site using the power of OpenAI's GPT models. Fully compatible with Yoast SEO and WooCommerce.
    1212
    1313== Description ==
    14 Easy GPT for WP - AI Content Generator is a cutting-edge WordPress plugin that leverages OpenAI's GPT technology to automate the creation of engaging and original content. This plugin integrates seamlessly with WordPress, enabling you to create posts and pages filled with AI-powered content. Every element, from SEO optimization to image insertion, is carefully crafted to enhance your digital presence, ensuring compatibility with Yoast SEO for optimal search engine performance.
     14Easy GPT for WP - AI Content Generator is a cutting-edge WordPress plugin that leverages OpenAI's GPT technology to automate the creation of engaging and original content. This plugin integrates seamlessly with WordPress, enabling you to create posts, pages, and WooCommerce products filled with AI-powered content. Every element, from SEO optimization to image insertion, is carefully crafted to enhance your digital presence, ensuring compatibility with Yoast SEO for optimal search engine performance. Additionally, Easy GPT for WP can automatically embed relevant YouTube videos into your generated articles, providing a richer multimedia experience.
    1515
    1616== Key Features ==
     
    1919- Intelligent Image Generation: Enhance the visual appeal of your posts with visually appealing images generated by DALL-E.
    2020- Efficient Bulk Operations: Manage content at scale with bulk post creation, allowing for scheduled and consistent updates that maintain your site's freshness and relevance.
     21- WooCommerce Product Generation: Automatically create WooCommerce product listings, including titles, descriptions, short descriptions, tags, images, and, in batch mode, prices.
     22- YouTube Video Integration: Automatically embed relevant YouTube videos in generated articles for enhanced engagement (requires a Google API key).
    2123- SEO & Copywriting: Automatically optimize your content for search engines with integrated support for Yoast SEO, boosting your site's visibility and organic search rankings.
    2224- Advanced Customization: Adjust the AI’s output by customizing settings like writing style, tone, and content structure to match your specific audience and content goals.
     
    4547    - If you do not have an API key, you can generate it on the [OpenAI platform](https://platform.openai.com/api-keys). OpenAI charges based on usage, so ensure you have funds in your account.
    4648    - Navigate to `Easy GPT > Settings` and enter your API key in the 'API Key' field, then save the changes.
    47 5. **SEO Compatibility**: Easy GPT for WP is compatible with Yoast SEO. Make sure you have Yoast SEO installed and activated to utilize SEO features seamlessly.
     495. **YouTube API Configuration (Optional)**: If you want Easy GPT to automatically include YouTube videos in generated content, you'll need to configure the YouTube API.
     50    - Go to the [Google Cloud Console](https://console.cloud.google.com/).
     51    - Create a new project and enable the **YouTube Data API v3**.
     52    - Generate an API key from **APIs & Services > Credentials**.
     53    - Restrict the API key to your website for security.
     54    - Copy the API key and enter it in **Easy GPT > Settings > YouTube API Key**.
     556. **SEO Compatibility**: Easy GPT for WP is compatible with Yoast SEO. Make sure you have Yoast SEO installed and activated to utilize SEO features seamlessly.
    4856
    4957For more information and additional resources, visit the official website: [Easy GPT for WP](https://easygptforwp.com/).
     
    7684
    7785= Is Easy GPT for WP compatible with WooCommerce? =
    78 While current versions do not support WooCommerce, we are actively developing this capability. Stay tuned for updates!
     86Yes, Easy GPT for WP now supports WooCommerce. You can generate product titles, descriptions, short descriptions, tags, and images. Additionally, batch product generation allows you to create multiple products at once, including optional price generation.
    7987
    8088= Will Easy GPT for WP support article translation in the future? =
     
    115123== Changelog ==
    116124
     125= 1.06 =
     126* Added support for generating WooCommerce products, including title, description, short description, tags, images, and prices.
     127* Enabled batch product generation for WooCommerce, including optional price generation.
     128* Fixed minor bugs.
     129* Adjusted cost calculation for better accuracy.
     130* Added support for `o1-preview` and `o1-mini` models.
     131* Users can now pause and resume batch processing jobs.
     132* Users can adjust the execution interval for batch processing jobs.
     133* Added an option to automatically include YouTube videos and products in generated articles (requires a Google API key).
     134
    117135= 1.05 =
    118136* Add API Key Validation to handle authentication errors more effectively.
  • easy-gpt-for-wp/trunk/styles/bulk-generation.css

    r3197201 r3232882  
    326326    font-weight: bold;
    327327}
     328
     329/* Toggle Switch */
     330.toggle-container {
     331    display: flex;
     332    align-items: center;
     333    gap: 10px;
     334}
     335
     336/* Estilo de las etiquetas (Articles / Products) */
     337.toggle-label {
     338    font-size: 14px;
     339    font-weight: normal;
     340    transition: font-weight 0.3s ease;
     341}
     342
     343.toggle-label.active {
     344    font-weight: bold;
     345    color: #000;
     346}
     347
     348.switch {
     349    position: relative;
     350    display: inline-block;
     351    width: 50px;
     352    height: 24px;
     353}
     354
     355.switch input {
     356    opacity: 0;
     357    width: 0;
     358    height: 0;
     359}
     360
     361.slider {
     362    position: absolute;
     363    cursor: pointer;
     364    top: 0;
     365    left: 0;
     366    right: 0;
     367    bottom: 0;
     368    background-color: #ccc;
     369    transition: .4s;
     370    border-radius: 24px;
     371}
     372
     373.slider:before {
     374    position: absolute;
     375    content: "";
     376    height: 16px;
     377    width: 16px;
     378    left: 4px;
     379    bottom: 4px;
     380    background-color: white;
     381    transition: .4s;
     382    border-radius: 50%;
     383}
     384
     385input:checked + .slider {
     386    background-color: #2196F3;
     387}
     388
     389input:checked + .slider:before {
     390    transform: translateX(26px);
     391}
  • easy-gpt-for-wp/trunk/styles/how-to-use.css

    r3197201 r3232882  
    33    margin-top: 20px;
    44    font-family: Arial, sans-serif;
     5    align-items: flex-start; /* Ensures all tabs align at the top */
    56}
     7
    68#easy-gpt-how-to-use-menu {
    79    width: 200px;
     10    min-width: 200px;
     11    max-width: 200px;
    812    margin-right: 20px;
    913    background-color: #f9f9f9;
     
    1115    border: 1px solid #ddd;
    1216    border-radius: 4px;
     17    box-sizing: border-box;
    1318}
     19
    1420#easy-gpt-how-to-use-menu ul {
    1521    list-style: none;
     
    1723    margin: 0;
    1824}
     25
    1926#easy-gpt-how-to-use-menu ul li {
    2027    margin-bottom: 10px;
    2128}
     29
    2230#easy-gpt-how-to-use-menu ul li a {
    2331    display: block;
     
    2937    border: 1px solid #ddd;
    3038    font-weight: bold;
     39    word-wrap: break-word; /* Ensures long text wraps within the menu */
    3140}
     41
    3242#easy-gpt-how-to-use-menu ul li a:hover,
    3343#easy-gpt-how-to-use-menu ul li a.active {
     
    3545    color: #fff;
    3646}
     47
    3748#easy-gpt-how-to-use-content {
    3849    flex-grow: 1;
     
    4152    border: 1px solid #ddd;
    4253    border-radius: 4px;
     54    box-sizing: border-box;
    4355}
     56
    4457#easy-gpt-how-to-use-content > div {
    4558    display: none;
    4659}
     60
    4761#easy-gpt-how-to-use-content > div.active {
    4862    display: block;
    4963}
     64
    5065.wrap h1 {
    5166    font-size: 24px;
     
    5368    margin-bottom: 20px;
    5469}
     70
    5571.wrap p {
    5672    font-size: 16px;
     
    5874    color: #555;
    5975}
     76
     77/* Responsive Images */
     78#easy-gpt-how-to-use-content img {
     79    max-width: 100%;
     80    height: auto;
     81    display: block;
     82    margin: 10px 0;
     83}
Note: See TracChangeset for help on using the changeset viewer.