Changeset 3232882
- Timestamp:
- 01/31/2025 07:34:25 PM (12 months ago)
- Location:
- easy-gpt-for-wp
- Files:
-
- 64 added
- 14 edited
-
tags/1.06 (added)
-
tags/1.06/admin (added)
-
tags/1.06/admin/admin-functions.php (added)
-
tags/1.06/admin/index.php (added)
-
tags/1.06/assets (added)
-
tags/1.06/assets/banner-1544x500.png.png (added)
-
tags/1.06/assets/banner-772x250.png (added)
-
tags/1.06/assets/icon-128x128.png (added)
-
tags/1.06/assets/icon-256x256.png (added)
-
tags/1.06/assets/screenshot-1.png (added)
-
tags/1.06/assets/screenshot-2.png (added)
-
tags/1.06/assets/screenshot-3.png (added)
-
tags/1.06/assets/screenshot-4.png (added)
-
tags/1.06/assets/screenshot-5.png (added)
-
tags/1.06/assets/screenshot-6.png (added)
-
tags/1.06/assets/screenshot-7.png (added)
-
tags/1.06/easy-gpt-for-wp.php (added)
-
tags/1.06/images (added)
-
tags/1.06/images/Logo-Pequeno-admin.png (added)
-
tags/1.06/images/accept-and-insert-article.png (added)
-
tags/1.06/images/generate-new-post.png (added)
-
tags/1.06/images/image-api.png (added)
-
tags/1.06/images/image-license.png (added)
-
tags/1.06/images/index.php (added)
-
tags/1.06/images/new-bulk-generation.png (added)
-
tags/1.06/images/tutorial-settings-post.png (added)
-
tags/1.06/images/tutorial-settings.png (added)
-
tags/1.06/images/view-bulk-generation.png (added)
-
tags/1.06/includes (added)
-
tags/1.06/includes/bulk-article-processing.php (added)
-
tags/1.06/includes/bulk-generation.php (added)
-
tags/1.06/includes/bulk-list.php (added)
-
tags/1.06/includes/cost.php (added)
-
tags/1.06/includes/easy-gpt-bulk-article-generator.php (added)
-
tags/1.06/includes/easy-gpt-options.php (added)
-
tags/1.06/includes/how-to-use.php (added)
-
tags/1.06/includes/index.php (added)
-
tags/1.06/includes/license.php (added)
-
tags/1.06/includes/metaboxes.php (added)
-
tags/1.06/includes/settings-functions.php (added)
-
tags/1.06/includes/status.php (added)
-
tags/1.06/includes/support.php (added)
-
tags/1.06/index.php (added)
-
tags/1.06/js (added)
-
tags/1.06/js/admin-script.js (added)
-
tags/1.06/js/admin-youtube.js (added)
-
tags/1.06/js/bulk-generation-another.js (added)
-
tags/1.06/js/bulk-generation.js (added)
-
tags/1.06/js/easy-gpt-bulk-list.js (added)
-
tags/1.06/js/easy-gpt-custom.js (added)
-
tags/1.06/js/how-to-use.js (added)
-
tags/1.06/public (added)
-
tags/1.06/public/index.php (added)
-
tags/1.06/public/public-functions.php (added)
-
tags/1.06/readme.txt (added)
-
tags/1.06/styles (added)
-
tags/1.06/styles/admin-style.css (added)
-
tags/1.06/styles/bulk-generation.css (added)
-
tags/1.06/styles/bulk-list.css (added)
-
tags/1.06/styles/how-to-use.css (added)
-
tags/1.06/styles/license.css (added)
-
tags/1.06/styles/metabox-style.css (added)
-
trunk/admin/admin-functions.php (modified) (1 diff)
-
trunk/easy-gpt-for-wp.php (modified) (7 diffs)
-
trunk/includes/bulk-article-processing.php (modified) (28 diffs)
-
trunk/includes/bulk-generation.php (modified) (6 diffs)
-
trunk/includes/easy-gpt-bulk-article-generator.php (modified) (2 diffs)
-
trunk/includes/easy-gpt-options.php (modified) (3 diffs)
-
trunk/includes/how-to-use.php (modified) (2 diffs)
-
trunk/includes/metaboxes.php (modified) (34 diffs)
-
trunk/includes/settings-functions.php (modified) (9 diffs)
-
trunk/includes/status.php (added)
-
trunk/js/admin-youtube.js (added)
-
trunk/js/bulk-generation.js (modified) (14 diffs)
-
trunk/js/easy-gpt-custom.js (modified) (28 diffs)
-
trunk/readme.txt (modified) (5 diffs)
-
trunk/styles/bulk-generation.css (modified) (1 diff)
-
trunk/styles/how-to-use.css (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
easy-gpt-for-wp/trunk/admin/admin-functions.php
r3197201 r3232882 42 42 'easy-gpt-bulk-generate-new', // Menu slug 43 43 '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 44 53 ); 45 54 -
easy-gpt-for-wp/trunk/easy-gpt-for-wp.php
r3227131 r3232882 4 4 * Plugin URI: https://easygptforwp.com/ 5 5 * Description: Integrates OpenAI GPT functionalities to automatically generate content and manage SEO in WordPress. 6 * Version: 1.0 56 * Version: 1.06 7 7 * Author: Ignacio Gil Alvarez 8 8 * Author URI: https://morris34.com/ … … 15 15 exit; // Exit if accessed directly. 16 16 } 17 18 define('EASY_GPT_PLUGIN_VERSION', '1.06'); 17 19 18 20 /** … … 41 43 require_once plugin_dir_path(__FILE__) . 'includes/easy-gpt-bulk-article-generator.php'; 42 44 require_once plugin_dir_path(__FILE__) . 'includes/bulk-article-processing.php'; 45 require_once plugin_dir_path(__FILE__) . 'includes/status.php'; 46 43 47 44 48 … … 49 53 easy_gpt_create_tables(); 50 54 easy_gpt_schedule_cron_jobs(); 55 56 update_option('easy_gpt_plugin_version', EASY_GPT_PLUGIN_VERSION); 51 57 } 52 58 register_activation_hook(__FILE__, 'easy_gpt_for_wordpress_activate'); … … 124 130 start_time time DEFAULT NULL, 125 131 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 126 138 PRIMARY KEY (id) 127 139 ) $charset_collate;"; … … 142 154 dbDelta($sql); 143 155 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 } 144 186 } 145 187 … … 159 201 } 160 202 } 203 204 function 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 } 213 add_action('admin_init', 'easy_gpt_check_for_updates'); 161 214 ?> -
easy-gpt-for-wp/trunk/includes/bulk-article-processing.php
r3197201 r3232882 77 77 [ 'id' => intval( $next_pending_task->id ) ] 78 78 ); 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 ) ); 79 86 80 87 if ( easy_gpt_generate_complete_article( intval( $next_pending_task->id ), $article_params ) ) { … … 153 160 $article_params = easy_gpt_sanitize_article_params( $article_params ); 154 161 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 155 167 // Generar todos los componentes necesarios antes de crear el artículo 156 168 $api_key = sanitize_text_field( get_option( 'easy_gpt_options' )['easy_gpt_api_key'] ); … … 186 198 } 187 199 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); 197 216 } 198 217 … … 236 255 'post_category' => [ $category_id ], // Usar el ID numérico de la categoría 237 256 'tags_input' => array_map( 'sanitize_text_field', explode( ', ', $tags ) ), 257 'post_type' => $is_product ? 'product' : 'post', 238 258 'post_author' => $default_author, 239 259 ]; 260 240 261 241 262 // Verificar si debe publicarse inmediatamente o programarse … … 259 280 } 260 281 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 261 315 // 10. Descargar y asignar las imágenes a la biblioteca de medios 262 316 if ( ! empty( $article_params['auto_images'] ) ) { … … 281 335 set_post_thumbnail( $post_id, $image_ids[0] ); 282 336 } 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 284 344 // 12. Insertar imágenes en el contenido del artículo usando las URLs locales 285 345 $new_content = easy_gpt_insert_images_into_content( $article, $local_image_urls, $article_params['set_featured_image'] ); … … 357 417 } 358 418 return $sanitized_params; 419 } 420 421 function 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; 359 452 } 360 453 … … 371 464 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 372 465 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."; 374 470 375 471 $response = easy_gpt_call_openai_api_no_session($full_prompt, $model, $temperature, $api_key); … … 402 498 } 403 499 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" . 405 504 "Prompt: {$prompt}\n" . 406 505 "Keywords: {$keywords}\n" . 407 506 "Specific instructions: {$specific_instructions}\n" . 408 507 "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" . 411 510 "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" . 412 511 "Language: {$language}\n" . … … 414 513 "Tone: {$writing_tone}\n" . 415 514 "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 416 516 417 517 $response = easy_gpt_call_openai_api_no_session($full_prompt, $model, $temperature, $api_key); … … 433 533 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 434 534 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" . 436 538 "Prompt: {$prompt}\n" . 437 539 "Keywords: {$keywords}\n" . … … 440 542 "Language: {$language}\n" . 441 543 "Writing Style: {$writing_style}\n" . 442 "Tone: {$writing_tone}\n" .544 "Tone: {$writing_tone}\n" . 443 545 "Maximum Words for the excerpt: 60 words."; 444 546 … … 460 562 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 461 563 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}"; 465 586 466 587 $title_response = easy_gpt_call_openai_api_no_session($prompt_title, $model, $temperature, $api_key); … … 484 605 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 485 606 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]); 487 613 $category_names = array_map(function($cat) { 488 614 return $cat->name; 489 615 }, $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" . 493 621 "Prompt: {$prompt}\n" . 494 622 "Outline: {$outline}\n" . 495 623 "Keywords: {$keywords}\n" . 496 "Language: {$language}\n" .624 "Language: {$language}\n" . 497 625 "Only return the name of the category"; 498 626 627 // Llamada a OpenAI para obtener la categoría sugerida 499 628 $response = easy_gpt_call_openai_api_no_session($full_prompt, $model, $temperature, $api_key); 500 629 501 630 return isset($response['text']) ? sanitize_text_field($response['text']) : false; 502 631 } 632 503 633 504 634 function easy_gpt_generate_tags_via_cron($article_params) { … … 512 642 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 513 643 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" . 515 648 "Prompt: {$prompt}\n" . 516 649 "Outline: {$outline}\n" . 517 650 "Keywords: {$keywords}\n" . 518 "Language: {$language}\n" .651 "Language: {$language}\n" . 519 652 "Only return the 3 tags separated by comma without # symbol"; 653 520 654 521 655 $response = easy_gpt_call_openai_api_no_session($full_prompt, $model, $temperature, $api_key); … … 562 696 $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." : ""; 563 697 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 706 error_log("DEBUG: include_videos_bulk = " . ($include_video ? 'true' : 'false')); 707 error_log("DEBUG: video_position_bulk = " . $video_position); 708 error_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 564 717 // Generar la introducción 565 $intro_prompt = "Generate an introduction for a n 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" . 566 719 "Prompt: {$prompt}\n" . 567 720 "Keywords: {$keywords}\n" . … … 571 724 "Maximum Words for the introduction: 200 words.\n"; 572 725 726 573 727 $intro_response = easy_gpt_call_openai_api_no_session($intro_prompt, $model, $temperature, $api_key); 574 728 … … 578 732 579 733 $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 } 580 740 581 741 // Dividir el outline en secciones H2, H3, H4 … … 604 764 $max_words_section = $max_words / max(count($h2_sections), 1); 605 765 606 foreach ($h2_sections as $ h2_section) {766 foreach ($h2_sections as $index => $h2_section) { 607 767 $h2_title = array_shift($h2_section); 608 768 $h3_h4 = implode("\n", $h2_section); … … 611 771 } 612 772 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 626 789 627 790 $response = easy_gpt_call_openai_api_no_session($section_prompt, $model, $temperature, $api_key); … … 632 795 633 796 $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"; 634 807 } 635 808 636 809 // Generar conclusión 637 $conclusion_prompt = "Generate a conclusion for a n 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" . 638 811 "Prompt: {$prompt}\n" . 639 812 "Keywords: {$keywords}\n" . … … 643 816 "Maximum Words for the conclusion: 200 words.\n" . 644 817 $bibliography_instructions; 645 818 646 819 $conclusion_response = easy_gpt_call_openai_api_no_session($conclusion_prompt, $model, $temperature, $api_key); 647 820 … … 652 825 $responses[] = $conclusion_response['text']; 653 826 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 654 861 // Combinar todas las secciones del artículo en una sola variable 655 $generated_article = implode("\n", $responses);862 $generated_article = $full_article; 656 863 657 864 return wp_kses_post($generated_article); … … 669 876 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 670 877 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" . 672 883 "Prompt: {$prompt}\n" . 673 884 "Keywords: {$keywords}\n" . … … 677 888 "Only return the image URL."; 678 889 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); 680 892 681 893 return isset($response['image_url']) ? ['image_url' => esc_url_raw($response['image_url'])] : false; … … 689 901 $output_cost_per_token = floatval($easy_gpt_options['model_costs'][$model]['output'] ?? 0); 690 902 903 if(strpos($model, 'o1') !== false) 904 { 905 $temperature = 1; 906 } 907 691 908 // 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) { 693 910 $url = 'https://api.openai.com/v1/chat/completions'; 694 911 $postData = [ … … 760 977 } 761 978 762 function easy_gpt_call_openai_image_api_no_session($prompt, $model, $ api_key) {979 function easy_gpt_call_openai_image_api_no_session($prompt, $model, $image_size, $image_quality, $api_key) { 763 980 global $easy_gpt_options; 764 981 global $easy_gpt_total_cost; … … 790 1007 } 791 1008 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 } 794 1035 795 1036 // Sumar al costo total global -
easy-gpt-for-wp/trunk/includes/bulk-generation.php
r3222181 r3232882 100 100 $include_bibliography = isset( $options['easy_gpt_include_bibliography'] ) ? $options['easy_gpt_include_bibliography'] : '0'; 101 101 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 102 106 103 107 ?> … … 116 120 <tr> 117 121 <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> 120 143 <input type="number" id="easy_gpt_number_of_articles" name="easy_gpt_number_of_articles" min="1" max="50" step="1" value="1"> 121 144 </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 122 162 <div class="easy-gpt-field"> 123 163 <label for="easy_gpt_prompt"><?php esc_html_e( 'Prompt (one line per article):', 'easy-gpt-for-wp' ); ?></label> … … 333 373 <label for="easy_gpt_include_excerpt" class="black-label"><?php esc_html_e( 'Include Excerpt:', 'easy-gpt-for-wp' ); ?></label> 334 374 <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> 335 399 </p> 336 400 </div> … … 449 513 $data['specific_instructions_excerpt'] = isset($_POST['specific_instructions_excerpt']) ? sanitize_textarea_field(wp_unslash($_POST['specific_instructions_excerpt'])) : ''; 450 514 $data['use_same_specific_excerpt_instructions'] = isset($_POST['use_same_specific_excerpt_instructions']) ? intval($_POST['use_same_specific_excerpt_instructions']) : 0; 515 516 451 517 452 518 // Procesar 'image_prompts' (array) … … 477 543 $data['use_same_image_prompts'] = ''; 478 544 } 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 480 556 // Inserción en la base de datos 481 557 $table_name = $wpdb->prefix . 'bulk_generations'; … … 525 601 '%s', // image_prompts 526 602 '%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 527 609 ); 528 610 -
easy-gpt-for-wp/trunk/includes/easy-gpt-bulk-article-generator.php
r3197201 r3232882 91 91 $content_instruction = !empty($other_params['use_same_specific_content_instructions']) ? implode("\n", $contents) : ($contents[$i] ?? ''); 92 92 $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)); 93 98 94 99 // Sanitizar instrucciones específicas … … 152 157 'start_time' => sanitize_text_field($other_params['start_time']), 153 158 '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']) 155 166 ]; 156 167 -
easy-gpt-for-wp/trunk/includes/easy-gpt-options.php
r3197201 r3232882 2 2 if ( ! defined( 'ABSPATH' ) ) exit; 3 3 $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'], 5 5 'writing_styles' => [ 6 6 'Formal', … … 66 66 ], 67 67 '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], 69 72 'gpt-4' => ['input' => 0.03, 'output' => 0.06], 70 73 'gpt-4-0125-preview' => ['input' => 0.01, 'output' => 0.03], … … 73 76 'gpt-4-1106-preview' => ['input' => 0.01, 'output' => 0.03], 74 77 '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 ], 77 103 ] 78 104 ]; -
easy-gpt-for-wp/trunk/includes/how-to-use.php
r3197201 r3232882 15 15 <li><a href="#tab-generate-post">Generating a New Post/Page</a></li> 16 16 <li><a href="#tab-bulk-generate">Bulk Post Generation</a></li> 17 <li><a href="#tab-youtube-api">YouTube API Configuration</a></li> 17 18 </ul> 18 19 </div> … … 83 84 <p>The articles that are generated can be found in the 'Posts' section of your WordPress dashboard.</p> 84 85 </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> 85 130 </div> 86 131 </div> -
easy-gpt-for-wp/trunk/includes/metaboxes.php
r3227131 r3232882 4 4 include 'easy-gpt-options.php'; 5 5 6 function easy_gpt_is_woocommerce_active() { 7 return class_exists('WooCommerce'); 8 } 9 6 10 // Add metaboxes 7 11 add_action('add_meta_boxes', 'easy_gpt_add_metaboxes'); 8 12 9 13 function easy_gpt_add_metaboxes() { 14 15 $post_types = ['post', 'page']; 16 17 if (class_exists('WooCommerce')) { 18 $post_types[] = 'product'; 19 } 20 10 21 add_meta_box( 11 22 'easy_gpt_metabox', // ID de la metabox 12 23 'Easy GPT Content Generator', // Título de la metabox 13 24 '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) 15 26 'normal', // Contexto (ubicación) 16 27 'high' // Prioridad … … 272 283 if ($include_bibliography === '') { 273 284 $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); 288 if ($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); 293 if ($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); 298 if ($video_paragraph === '') { 299 $video_paragraph = isset($options['easy_gpt_video_paragraph']) ? $options['easy_gpt_video_paragraph'] : 3; 274 300 } 275 301 … … 512 538 <input type="checkbox" id="easy_gpt_include_excerpt" name="easy_gpt_include_excerpt" <?php checked( $include_excerpt ); ?> class="adjusted-checkbox"> 513 539 </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> 514 564 </div> 515 565 <div id="tab-3"> … … 631 681 $new_include_links = isset($_POST['easy_gpt_include_links']) ? '1' : '0'; 632 682 $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 633 687 634 688 // Actualizar los campos meta en la base de datos. … … 659 713 update_post_meta($post_id, '_easy_gpt_include_links', $new_include_links); 660 714 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); 661 718 } 662 719 … … 668 725 669 726 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'])) { 671 731 $specific_instructions = sanitize_textarea_field(wp_unslash($_POST['specific_instructions'])); 672 732 wp_send_json_success(array( … … 690 750 $writing_style = isset($_POST['writing_style']) ? sanitize_text_field(wp_unslash($_POST['writing_style'])) : ''; 691 751 $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."; 694 759 695 760 $response = easy_gpt_call_openai_api($full_prompt, $model, $temperature, $api_key, $post_id); … … 740 805 } 741 806 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" . 743 812 "Prompt: {$prompt}\n" . 744 813 "Keywords: {$keywords}\n" . 745 814 "Specific instructions: {$specific_instructions}\n" . 746 815 "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" . 749 818 "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" . 750 819 "Language: {$language}\n" . … … 752 821 "Tone: {$writing_tone}\n" . 753 822 "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 754 824 755 825 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; … … 789 859 $includeLinks = isset($_POST['easy_gpt_includeLinks']) && $_POST['easy_gpt_includeLinks'] === 'true'; 790 860 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 791 866 // Construir instrucciones adicionales 792 867 $links_instructions = $includeLinks … … 801 876 ? " 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." 802 877 : ""; 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 803 885 804 886 // 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" . 806 892 "Prompt: {$prompt}\n" . 807 893 "Keywords: {$keywords}\n" . … … 811 897 "Maximum Words for the introduction: 200 words.\n"; 812 898 899 813 900 $intro_response = easy_gpt_call_openai_api($intro_prompt, $model, $temperature, $api_key, $post_id); 814 901 … … 819 906 820 907 $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 } 821 914 822 915 // Procesar el esquema para obtener las secciones H2 … … 845 938 if ($num_sections === 0) { 846 939 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; 849 944 850 945 $min_words_per_section = $num_sections > 0 ? $min_words / $num_sections : $min_words; 851 946 $max_words_per_section = $num_sections > 0 ? $max_words / $num_sections : $max_words; 852 947 853 foreach ($h2_sections as $h2_section) { 948 foreach ($h2_sections as $index => $h2_section) { 949 error_log("Generando..."); 950 854 951 $h2_title = array_shift($h2_section); 952 error_log($h2_title); 855 953 $h3_h4 = implode("\n", $h2_section); 856 954 if (empty($h3_h4)) { … … 858 956 } 859 957 860 $section_prompt = "Generate a detailed section for the articletitled '{$title}'. Include the following details:\n" .861 "Section Title: {$h2_title}\n" .862 "Subheadings:\n{$h3_h4}\n" .863 "Format the articleusing 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; 873 971 874 972 $response = easy_gpt_call_openai_api($section_prompt, $model, $temperature, $api_key, $post_id); … … 880 978 881 979 $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 } 882 985 } 883 986 884 987 // 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 893 1002 894 1003 $conclusion_response = easy_gpt_call_openai_api($conclusion_prompt, $model, $temperature, $api_key, $post_id); … … 901 1010 $responses[] = $conclusion_response['text']; 902 1011 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 903 1047 wp_send_json_success([ 904 'article' => implode("", $responses),1048 'article' => $full_article, 905 1049 'prompt_tokens' => $conclusion_response['prompt_tokens'], 906 1050 'completion_tokens' => $conclusion_response['completion_tokens'], … … 932 1076 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 933 1077 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" . 935 1083 "Prompt: {$prompt}\n" . 936 1084 "Keywords: {$keywords}\n" . … … 941 1089 "Tone: {$writing_tone}\n" . 942 1090 "Maximum Words for the excerpt: 60 words."; 1091 943 1092 944 1093 $response = easy_gpt_call_openai_api($full_prompt, $model, $temperature, $api_key, $post_id); … … 972 1121 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 973 1122 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" . 975 1128 "Title: {$title}\n" . 976 1129 "Prompt: {$prompt}\n" . … … 980 1133 "Writing Style: {$writing_style}\n" . 981 1134 "Tone: {$writing_tone}"; 982 983 $prompt_description = "Generate an SEO-friendly metadescription for a n articlewith 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" . 984 1137 "Title: {$title}\n" . 985 1138 "Prompt: {$prompt}\n" . … … 989 1142 "Writing Style: {$writing_style}\n" . 990 1143 "Tone: {$writing_tone}"; 1144 991 1145 992 1146 $title_response = easy_gpt_call_openai_api($prompt_title, $model, $temperature, $api_key, $post_id); … … 1023 1177 $keywords = isset($_POST['keywords']) ? sanitize_text_field(wp_unslash($_POST['keywords'])) : ''; 1024 1178 1179 1180 1025 1181 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 1026 1182 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 1029 1195 $category_names = array_map(function($cat) { 1030 1196 return $cat->name; … … 1032 1198 $category_list = implode(", ", $category_names); 1033 1199 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" . 1035 1206 "Prompt: {$prompt}\n" . 1036 1207 "Outline: {$outline}\n" . … … 1038 1209 "Language: {$language}\n" . 1039 1210 "Only return the name of the category"; 1211 1212 error_log($full_prompt); 1040 1213 1041 1214 $response = easy_gpt_call_openai_api($full_prompt, $model, $temperature, $api_key, $post_id); … … 1067 1240 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 1068 1241 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" . 1070 1247 "Prompt: {$prompt}\n" . 1071 1248 "Outline: {$outline}\n" . 1072 1249 "Keywords: {$keywords}\n" . 1073 1250 "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 1075 1253 1076 1254 $response = easy_gpt_call_openai_api($full_prompt, $model, $temperature, $api_key, $post_id); … … 1103 1281 $api_key = get_option('easy_gpt_options')['easy_gpt_api_key']; 1104 1282 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" . 1106 1288 "Prompt: {$prompt}\n" . 1107 1289 "Keywords: {$keywords}\n" . … … 1111 1293 "Only return the image URL."; 1112 1294 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); 1114 1297 1115 1298 if (!$response) { … … 1123 1306 1124 1307 1125 function easy_gpt_call_openai_image_api($prompt, $model, $ api_key, $post_id) {1308 function easy_gpt_call_openai_image_api($prompt, $model, $image_size, $image_quality, $api_key, $post_id) { 1126 1309 global $easy_gpt_options; 1127 1310 … … 1176 1359 $new_total_tokens = 0; 1177 1360 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 } 1180 1387 1181 1388 $total_cost = 0.0; … … 1259 1466 $input_cost_per_token = isset($easy_gpt_options['model_costs'][$model]['input']) ? floatval($easy_gpt_options['model_costs'][$model]['input']) : 0.0; 1260 1467 $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 } 1261 1473 1262 1474 // 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) { 1264 1476 $url = "https://api.openai.com/v1/chat/completions"; 1265 1477 $request_body = [ … … 1294 1506 1295 1507 $response_code = wp_remote_retrieve_response_code($response); 1508 $response_body = wp_remote_retrieve_body($response); 1509 $decodedResponse = json_decode($response_body, true); 1296 1510 if ($response_code != 200) { 1297 1511 if ($response_code == 401) { … … 1300 1514 ); 1301 1515 } 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 1303 1519 } 1304 1520 … … 1472 1688 } 1473 1689 1690 function 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®ionCode=" . 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 1766 add_action('admin_footer', 'easy_gpt_video_position_per_post_script'); 1767 function 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 1474 1795 ?> -
easy-gpt-for-wp/trunk/includes/settings-functions.php
r3227131 r3232882 26 26 27 27 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( 28 61 'easy_gpt_article_settings_section', 29 62 'Article Generation Settings', … … 232 265 } 233 266 267 function easy_gpt_youtube_settings_section_callback() { 268 echo 'Configure YouTube API settings for embedding videos in generated articles.'; 269 } 270 271 function 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 308 function 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 316 function 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 } 234 333 235 334 function easy_gpt_api_key_render() { … … 246 345 echo "<div id='easy_gpt_api_key_validation' style='margin-top: 5px; font-weight: bold;'>"; 247 346 248 // Mostrar el estado de validaci ¨®n347 // Mostrar el estado de validaci車n 249 348 if (isset($options['api_key_valid'])) { 250 349 switch ($options['api_key_valid']) { … … 269 368 270 369 function 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 273 374 echo "<select name='easy_gpt_options[easy_gpt_model]'>"; 274 375 foreach ($easy_gpt_options['models'] as $model) { 275 376 $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 276 383 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>"; 280 391 } 281 392 … … 491 602 492 603 493 // Si la clave API est ¨¢enmascarada, mantener la clave original604 // Si la clave API est芍 enmascarada, mantener la clave original 494 605 if (isset($input['easy_gpt_api_key']) && strpos($input['easy_gpt_api_key'], '****') !== false) { 495 606 $input['easy_gpt_api_key'] = $options['easy_gpt_api_key']; … … 499 610 $input['easy_gpt_api_key'] = sanitize_text_field($input['easy_gpt_api_key']); 500 611 } 501 // Validar la API Key si no est ¨¢ vac¨ªa612 // Validar la API Key si no est芍 vac赤a 502 613 if (!empty($input['easy_gpt_api_key'])) { 503 614 … … 539 650 $input['easy_gpt_image_style'] = sanitize_text_field($input['easy_gpt_image_style']); 540 651 541 // Manejar las casillas de verificaci ¨®n652 // Manejar las casillas de verificaci車n 542 653 $checkbox_fields = [ 543 654 'easy_gpt_subheading_type_h3', … … 558 669 } 559 670 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 560 706 return $input; 561 707 } … … 586 732 } 587 733 } 734 735 736 add_action('admin_footer', 'easy_gpt_video_position_script'); 737 function 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 755 add_action('wp_ajax_validate_youtube_api_key', 'easy_gpt_validate_youtube_api_key'); 756 757 function 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 778 add_action('admin_enqueue_scripts', 'easy_gpt_admin_youtube_styles'); 779 780 function 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 1 1 jQuery(document).ready(function($) { 2 2 3 // Inicializar las pestañas 3 4 $("#easy-gpt-tabs").tabs(); 4 5 $("#easy-gpt-image-tabs").tabs(); 5 6 6 // Func tion to refresh image tabs7 // Función para refrescar las pestañas de imágenes 7 8 function refreshImageTabs() { 8 9 var numImages = $('#easy_gpt_num_images').val(); … … 10 11 var imageTabsContent = $('#easy-gpt-image-tabs'); 11 12 12 // Clear existing tabs13 // Limpiar pestañas existentes 13 14 imageTabsList.empty(); 14 15 imageTabsContent.children('div[id^="image-tab-"]').remove(); 15 16 16 // Genera te new tabs17 // Generar nuevas pestañas 17 18 for (var i = 1; i <= numImages; i++) { 18 19 imageTabsList.append('<li><a href="#image-tab-' + i + '">Image ' + i + '</a></li>'); … … 24 25 '</div>' + 25 26 '<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">' + 27 28 '<label for="use_same_easy_gpt_image_prompt_' + i + '" class="normal-label">Use same Image ' + i + ' instructions for all articles</label>' + 28 29 '</div>' + … … 31 32 } 32 33 33 // Refres h the tabs34 // Refrescar las pestañas 34 35 imageTabsContent.tabs("refresh"); 35 36 } 36 37 37 // Event listener for changes in the number of images38 // Evento para cambios en el número de imágenes 38 39 $('#easy_gpt_num_images').on('change input', function() { 39 40 refreshImageTabs(); 40 41 }); 41 42 42 // Initial refresh of image tabs43 // Refrescar las pestañas de imágenes al cargar 43 44 refreshImageTabs(); 44 45 46 // Función para manejar la lógica de categorías y etiquetas 45 47 function toggleCategoryTagSettings() { 46 48 if ($('#easy_gpt_auto_categories').is(':checked')) { 47 49 console.log('Categories checkbox is checked'); 50 // Aquí puedes agregar lógica adicional si es necesario 48 51 } else { 49 52 console.log('Categories checkbox is not checked'); 53 // Aquí puedes agregar lógica adicional si es necesario 50 54 } 51 55 if ($('#easy_gpt_auto_tags').is(':checked')) { 52 56 console.log('Tags checkbox is checked'); 57 // Aquí puedes agregar lógica adicional si es necesario 53 58 } else { 54 59 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 62 65 $('#easy_gpt_auto_categories, #easy_gpt_auto_tags').change(function() { 63 66 toggleCategoryTagSettings(); 64 67 }); 65 68 69 // Ejecutar en carga inicial 70 toggleCategoryTagSettings(); 71 66 72 var isRequesting = false; 67 73 74 // Evento para el botón "Generate Articles" 68 75 $('#generate-all').off('click').on('click', function(e) { 69 76 e.preventDefault(); 70 77 71 // Get the values from the form fields and log them78 // Obtener y validar valores del formulario 72 79 var prompt = $('#easy_gpt_prompt').val().trim(); 73 80 var keywords = $('#easy_gpt_keywords').val().trim(); … … 78 85 console.log('Number of Articles:', numArticles); 79 86 80 // Validate that 'prompt' and 'keywords' are not empty81 87 if (prompt === '') { 82 88 console.error('Validation Error: Prompt field is empty.'); 83 89 alert('Please enter a Prompt.'); 84 return; // Exit the function if validation fails90 return; 85 91 } 86 92 … … 88 94 console.error('Validation Error: Keywords field is empty.'); 89 95 alert('Please enter Keywords.'); 90 return; // Exit the function if validation fails96 return; 91 97 } 92 98 … … 94 100 console.error('Validation Error: Number of articles is invalid.'); 95 101 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 100 105 console.log('All validations passed. Preparing to send AJAX request.'); 101 106 102 107 if (isRequesting) return; 103 108 104 109 isRequesting = true; 105 110 var $button = $(this); 106 111 $button.text('Requesting...'); 107 112 113 console.log("Content Type:", $('#content_type').val()); 114 108 115 var data = { 109 116 'action': 'save_bulk_generation', … … 112 119 'keywords': $('#easy_gpt_keywords').val(), 113 120 '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(), 114 124 'model': $('#easy_gpt_model').val(), 115 125 'temperature': $('#easy_gpt_temperature').val(), … … 148 158 'start_date': $('#start_date').val(), 149 159 '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(), 151 164 }; 152 153 console.log(data);154 155 // Col lecting imageprompts165 166 console.log(data); 167 168 // Colección de image_prompts y use_same_image_prompts 156 169 var image_prompts = []; 157 170 var use_same_image_prompts = []; … … 164 177 data['image_prompts'] = JSON.stringify(image_prompts); 165 178 data['use_same_image_prompts'] = JSON.stringify(use_same_image_prompts); 166 179 167 180 $.post(ajaxurl, data, function(response) { 168 console.log(response); // Añadir un registro para depuración181 console.log(response); // Registro para depuración 169 182 if (response.success) { 170 183 alert('Generation saved! ID: ' + response.data.id); … … 178 191 }); 179 192 }); 180 193 194 // Función para iniciar la generación de artículos 181 195 function startArticleGeneration(generationId, numArticles) { 182 // Lógica para iniciar la generación de artículos183 196 console.log("Starting generation for ID: " + generationId + " with " + numArticles + " articles."); 184 197 185 198 var data = { 186 199 'action': 'easy_gpt_start_generation', … … 189 202 'num_articles': numArticles 190 203 }; 191 204 192 205 $.post(ajaxurl, data, function(response) { 193 console.log(response); // Añadir un registro para depuración206 console.log(response); // Registro para depuración 194 207 if (response.success) { 195 208 console.log("Generation started successfully."); … … 200 213 }); 201 214 } 202 215 216 // Función para obtener tipos de subencabezados 203 217 function getSubheadingTypes() { 204 218 var types = []; … … 207 221 return types.join(','); 208 222 } 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 209 275 }); -
easy-gpt-for-wp/trunk/js/easy-gpt-custom.js
r3227131 r3232882 1 1 jQuery(document).ready(function($) { 2 2 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); 3 10 4 11 tinymce.init({ … … 205 212 var $button = $('#generate-image-' + imageIndex); 206 213 var originalText = $button.text(); 214 var postType = isWooCommerceProductPage() ? 'product' : 'post'; 207 215 $button.text('Generating...'); 208 216 … … 211 219 nonce: $('#easy_gpt_nonce').val(), 212 220 prompt: $('#easy_gpt_image_prompt_' + imageIndex).val() || $('#easy_gpt_prompt').val(), 221 'post_type': postType, 213 222 keywords: $('#easy_gpt_keywords').val(), 214 223 title: $('#easy_gpt_generated_title_preview').val(), … … 273 282 var originalText = 'Generate Title'; 274 283 var postID = getURLParameter('post'); 284 var postType = isWooCommerceProductPage() ? 'product' : 'post'; 285 275 286 276 287 $preview.val(''); … … 284 295 'nonce': $('#easy_gpt_nonce').val(), 285 296 'post_id': postID, 297 'post_type': postType, 286 298 'prompt': $('#easy_gpt_prompt').val(), 287 299 'keywords': $('#easy_gpt_keywords').val(), 300 'use_specific_instructions': $('#use_specific_title_instructions').is(':checked') ? 'true' : 'false', 288 301 'specific_instructions': $('#easy_gpt_specific_instructions_title').val(), 289 302 'model': $('#easy_gpt_model').val(), … … 348 361 var originalText = $button.text(); 349 362 var postID = getURLParameter('post'); 363 var postType = isWooCommerceProductPage() ? 'product' : 'post'; 350 364 $button.text('Generating...'); 351 365 var fillBar = showLoadingBar() … … 357 371 prompt: $('#easy_gpt_prompt').val(), 358 372 post_id: postID, 373 'post_type': postType, 359 374 keywords: $('#easy_gpt_keywords').val(), 360 375 title: $('#easy_gpt_generated_title_preview').val(), … … 432 447 var includeBibliography = $('#easy_gpt_include_bibliography').is(':checked'); 433 448 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(); 435 452 var autoCategories = $('#easy_gpt_auto_categories').is(':checked'); 436 453 var autoTags = $('#easy_gpt_auto_tags').is(':checked'); 437 454 var postID = getURLParameter('post'); 455 var postType = isWooCommerceProductPage() ? 'product' : 'post'; 438 456 $button.text('Generating...'); 439 457 var fillBar = showLoadingBar() … … 444 462 nonce: $('#easy_gpt_nonce').val(), 445 463 post_id: postID, 464 post_type: postType, 446 465 title: $('#easy_gpt_generated_title_preview').val(), 447 466 prompt: $('#easy_gpt_prompt').val(), … … 459 478 'easy_gpt_includeBibliography': includeBibliography, 460 479 '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 462 483 }; 463 484 … … 513 534 var originalText = $button.text(); 514 535 var postID = getURLParameter('post'); 536 var postType = isWooCommerceProductPage() ? 'product' : 'post'; 515 537 $button.text('Generating...'); // Texto de carga 516 538 var fillBar = showLoadingBar() … … 521 543 nonce: $('#easy_gpt_nonce').val(), 522 544 post_id: postID, 545 'post_type': postType, 523 546 title: $('#easy_gpt_generated_title_preview').val(), 524 547 prompt: $('#easy_gpt_prompt').val(), … … 575 598 var originalText = $button.text(); 576 599 var postID = getURLParameter('post'); 600 var postType = isWooCommerceProductPage() ? 'product' : 'post'; 577 601 $button.text('Generating...'); // Texto de carga 578 602 var fillBar = showLoadingBar() … … 583 607 nonce: $('#easy_gpt_nonce').val(), 584 608 post_id: postID, 609 'post_type': postType, 585 610 title: $('#easy_gpt_generated_title_preview').val(), 586 611 prompt: $('#easy_gpt_prompt').val(), … … 847 872 848 873 // Actualizar el excerpt 849 if (isGutenbergActive()) {874 if (isGutenbergActive() && !isWooCommerceProductPage()) { 850 875 if (wp.data) { 851 876 wp.data.dispatch('core/editor').editPost({ excerpt: excerpt }); … … 853 878 console.error('No se puede actualizar el extracto porque wp.data no est¨¢ disponible.'); 854 879 } 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 860 915 861 916 // Actualizar el t¨ªtulo SEO y la descripci¨®n SEO … … 893 948 894 949 var tags = $('#easy_gpt_generated_tag_preview').val(); 895 if (isGutenbergActive() ) {950 if (isGutenbergActive() && !isWooCommerceProductPage()) { 896 951 var postId = wp.data.select('core/editor').getCurrentPostId(); 897 952 } 898 953 899 954 // Actualizar el t¨ªtulo y contenido 900 if (isGutenbergActive() ) {955 if (isGutenbergActive() && !isWooCommerceProductPage()) { 901 956 if (window.wp && wp.data && wp.data.dispatch && wp.blocks) { 902 957 wp.data.dispatch('core/editor').editPost({ title: title }); … … 944 999 // Si est¨¢ marcado "Set first generated image as featured", establecer la primera imagen como destacada 945 1000 if (setFeaturedImage && imageIds.length > 0) { 946 if (isGutenbergActive() ) {1001 if (isGutenbergActive() && !isWooCommerceProductPage()) { 947 1002 948 1003 wp.data.dispatch('core/editor').editPost({ featured_media: imageIds[0] }); … … 953 1008 } 954 1009 } 955 1010 1011 1012 if (isWooCommerceProductPage() && imageIds.length > 1) { 1013 console.log('A09adiendo 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 // A09adir 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 a09adidas a la interfaz.'); 1044 } 1045 1046 956 1047 // Insertar etiquetas <img> en el contenido HTML, excluyendo la imagen destacada si aplica 957 1048 var newContent = await insertImagesIntoContent(content, savedImageUrls, setFeaturedImage); … … 969 1060 970 1061 // Actualizar el t¨ªtulo y contenido 971 if (isGutenbergActive() ) {1062 if (isGutenbergActive() && !isWooCommerceProductPage()) { 972 1063 if (window.wp && wp.data && wp.data.dispatch && wp.blocks) { 973 1064 wp.data.dispatch('core/editor').resetBlocks(wp.blocks.parse(newContent)); … … 982 1073 983 1074 // Intentar actualizar las categor¨ªas y etiquetas 984 if (isGutenbergActive() ) {1075 if (isGutenbergActive() && !isWooCommerceProductPage()) { 985 1076 await handleCategoryUpdate(); 986 1077 await addTagsToPost(postId, tags); 987 1078 } else { 988 // Actualizar categor¨ªas en el Editor Cl¨¢sico1079 // Obtener las categor¨ªas y etiquetas generadas por Easy GPT 989 1080 var categoriesText = $('#easy_gpt_generated_category_preview').val(); 990 1081 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 a09adir:", 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 a09adida 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:"); 1007 1140 $('#categorychecklist input[type=checkbox], #categorychecklist-pop input[type=checkbox]').each(function() { 1008 1141 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); 1013 1158 } 1014 1159 }); 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 1026 1169 1027 1170 // Intentar nuevamente despu¨¦s de 1 segundo si no se logr¨® en el primer intento … … 1033 1176 1034 1177 try { 1035 if (isGutenbergActive() ) {1178 if (isGutenbergActive() && !isWooCommerceProductPage()) { 1036 1179 // Suponiendo que el servidor expone un endpoint que actualiza los tokens y costos 1037 1180 const postId = wp.data.select('core/editor').getCurrentPostId(); … … 1102 1245 async function handleCategoryUpdate() { 1103 1246 try { 1104 if (isGutenbergActive() ) {1247 if (isGutenbergActive() && !isWooCommerceProductPage()) { 1105 1248 const categories = await wp.data.select('core').getEntityRecords('taxonomy', 'category', { per_page: -1 }); 1106 1249 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; 1109 1252 } 1110 1253 … … 1112 1255 const category = categories.find(cat => cat.name.toLowerCase() === categoryName.toLowerCase()); 1113 1256 if (!category) { 1114 console.error('Categor¨ªa no encontrada:', categoryName);1115 return;1257 //console.error('Categor¨ªa no encontrada:', categoryName); 1258 //return; 1116 1259 } 1117 1260 … … 1128 1271 1129 1272 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 1132 1281 const existingTags = await wp.data.select('core').getEntityRecords('taxonomy', 'post_tag', { per_page: -1 }) || []; 1133 1282 1283 // Crear un mapa para b¨²squeda r¨¢pida 1284 const existingTagsMap = new Map(existingTags.map(tag => [tag.name.toLowerCase(), tag.id])); 1285 1134 1286 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); 1138 1290 } else { 1139 1291 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 // A09adir 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 } 1143 1309 } 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 } 1145 1316 return null; 1146 1317 } … … 1148 1319 })); 1149 1320 1150 // Filtrar los IDs nulos 1151 tagIds = tagIds.filter(id => id !== null); 1152 1153 // A09adir etiquetas al post 1321 // Filtrar valores falsy (null, undefined, etc.) 1322 tagIds = tagIds.filter(id => id); 1323 1154 1324 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 } 1156 1331 } else { 1157 console.error('No tags were added because no valid tags were found or provided.'); 1158 } 1159 } 1160 } 1332 console.error('No se a09adieron 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 1161 1352 1162 1353 … … 1198 1389 1199 1390 function generateCategories(postID) { 1391 1392 var postType = isWooCommerceProductPage() ? 'product' : 'post'; 1393 1200 1394 $.ajax({ 1201 1395 url: ajaxurl, … … 1204 1398 action: 'generate_categories', 1205 1399 post_id: postID, 1400 post_type: postType, 1206 1401 nonce: $('#easy_gpt_nonce').val(), 1207 1402 title: $('#easy_gpt_generated_title_preview').val(), -
easy-gpt-for-wp/trunk/readme.txt
r3227131 r3232882 1 1 === Easy GPT for WP | AI Content Generator=== 2 2 Contributors: Ignacio Gil Alvarez 3 Tags: Easy GPT for WP, gpt-4o, ai content generator, AI Writer, DALL-E 33 Tags: ai content generator, Ai content writer, gpt o1, AI Writer, DALL-E 3 4 4 Requires at least: WordPress 5.3 5 5 Tested up to: 6.7.1 6 6 Requires PHP: 7.3 7 Stable tag: 1.0 57 Stable tag: 1.06 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Generate rich, SEO-friendly content for your WordPress site using the power of OpenAI's GPT models. Fully compatible with Yoast SEO .11 Generate rich, SEO-friendly content for your WordPress site using the power of OpenAI's GPT models. Fully compatible with Yoast SEO and WooCommerce. 12 12 13 13 == 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.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, 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. 15 15 16 16 == Key Features == … … 19 19 - Intelligent Image Generation: Enhance the visual appeal of your posts with visually appealing images generated by DALL-E. 20 20 - 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). 21 23 - SEO & Copywriting: Automatically optimize your content for search engines with integrated support for Yoast SEO, boosting your site's visibility and organic search rankings. 22 24 - 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. … … 45 47 - 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. 46 48 - 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. 49 5. **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**. 55 6. **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. 48 56 49 57 For more information and additional resources, visit the official website: [Easy GPT for WP](https://easygptforwp.com/). … … 76 84 77 85 = 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! 86 Yes, 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. 79 87 80 88 = Will Easy GPT for WP support article translation in the future? = … … 115 123 == Changelog == 116 124 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 117 135 = 1.05 = 118 136 * Add API Key Validation to handle authentication errors more effectively. -
easy-gpt-for-wp/trunk/styles/bulk-generation.css
r3197201 r3232882 326 326 font-weight: bold; 327 327 } 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 385 input:checked + .slider { 386 background-color: #2196F3; 387 } 388 389 input:checked + .slider:before { 390 transform: translateX(26px); 391 } -
easy-gpt-for-wp/trunk/styles/how-to-use.css
r3197201 r3232882 3 3 margin-top: 20px; 4 4 font-family: Arial, sans-serif; 5 align-items: flex-start; /* Ensures all tabs align at the top */ 5 6 } 7 6 8 #easy-gpt-how-to-use-menu { 7 9 width: 200px; 10 min-width: 200px; 11 max-width: 200px; 8 12 margin-right: 20px; 9 13 background-color: #f9f9f9; … … 11 15 border: 1px solid #ddd; 12 16 border-radius: 4px; 17 box-sizing: border-box; 13 18 } 19 14 20 #easy-gpt-how-to-use-menu ul { 15 21 list-style: none; … … 17 23 margin: 0; 18 24 } 25 19 26 #easy-gpt-how-to-use-menu ul li { 20 27 margin-bottom: 10px; 21 28 } 29 22 30 #easy-gpt-how-to-use-menu ul li a { 23 31 display: block; … … 29 37 border: 1px solid #ddd; 30 38 font-weight: bold; 39 word-wrap: break-word; /* Ensures long text wraps within the menu */ 31 40 } 41 32 42 #easy-gpt-how-to-use-menu ul li a:hover, 33 43 #easy-gpt-how-to-use-menu ul li a.active { … … 35 45 color: #fff; 36 46 } 47 37 48 #easy-gpt-how-to-use-content { 38 49 flex-grow: 1; … … 41 52 border: 1px solid #ddd; 42 53 border-radius: 4px; 54 box-sizing: border-box; 43 55 } 56 44 57 #easy-gpt-how-to-use-content > div { 45 58 display: none; 46 59 } 60 47 61 #easy-gpt-how-to-use-content > div.active { 48 62 display: block; 49 63 } 64 50 65 .wrap h1 { 51 66 font-size: 24px; … … 53 68 margin-bottom: 20px; 54 69 } 70 55 71 .wrap p { 56 72 font-size: 16px; … … 58 74 color: #555; 59 75 } 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.