Changeset 3398370
- Timestamp:
- 11/18/2025 09:21:04 PM (5 months ago)
- Location:
- aicontify
- Files:
-
- 26 added
- 1 deleted
- 7 edited
-
tags/5.0.0 (added)
-
tags/5.0.0/aicontify.php (added)
-
tags/5.0.0/contentPosts.php (added)
-
tags/5.0.0/css (added)
-
tags/5.0.0/css/style.css (added)
-
tags/5.0.0/dashboard.php (added)
-
tags/5.0.0/faqPosts.php (added)
-
tags/5.0.0/img (added)
-
tags/5.0.0/img/logo-aicontify.png (added)
-
tags/5.0.0/js (added)
-
tags/5.0.0/js/tabsPosts.js (added)
-
tags/5.0.0/languages (added)
-
tags/5.0.0/languages/aicontify-fa_IR.mo (added)
-
tags/5.0.0/languages/aicontify-fa_IR.po (added)
-
tags/5.0.0/languages/aicontify.pot (added)
-
tags/5.0.0/readme.txt (added)
-
tags/5.0.0/seoDescription.php (added)
-
tags/5.0.0/seoTitle.php (added)
-
tags/5.0.0/settings (added)
-
tags/5.0.0/settings.php (added)
-
tags/5.0.0/settings/tab-content.php (added)
-
tags/5.0.0/settings/tab-faq.php (added)
-
tags/5.0.0/settings/tab-main.php (added)
-
tags/5.0.0/settings/tab-seo-meta.php (added)
-
tags/5.0.0/settings/tab-seo-title.php (added)
-
tags/5.0.0/tabsPosts.php (added)
-
trunk/aicontify.php (modified) (2 diffs)
-
trunk/contentPosts.php (modified) (2 diffs)
-
trunk/faqPosts.php (modified) (1 diff)
-
trunk/js/tabsPosts.js (modified) (5 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/seoDescription.php (modified) (2 diffs)
-
trunk/seoTitle.php (modified) (1 diff)
-
trunk/test (deleted)
Legend:
- Unmodified
- Added
- Removed
-
aicontify/trunk/aicontify.php
r3397131 r3398370 4 4 Plugin URI: https://aicontify.com/ 5 5 Description: Your Smart Content Assistant – One Click, Full Content 6 Version: 4.0.26 Version: 5.0.0 7 7 Author: Hassan Solgi 8 8 Author URI: https://t.me/hassansolgi … … 126 126 true 127 127 ); 128 129 wp_localize_script( 130 'aicont-post-tabs', 131 'aicontApiSettings', 132 [ 133 'root' => esc_url_raw(rest_url()), 134 'nonce' => wp_create_nonce('wp_rest'), 135 'api_key' => esc_js(aicont_get_api_key()), 136 'site_language' => esc_js(sanitize_text_field(get_option('aicont_plugin_site_language', 'en_US'))), 137 'site_title' => esc_js(sanitize_text_field(get_option('aicont_plugin_site_title', ''))), 138 'api_endpoint' => esc_url_raw(rest_url('aicont/v1/')), 139 'content_prompt_custom' => esc_js(sanitize_textarea_field(get_option('aicont_plugin_content_prompt_custom', ''))), 140 'faq_prompt_custom' => esc_js(sanitize_textarea_field(get_option('aicont_plugin_faq_prompt_custom', ''))), 141 'seo_title_prompt_custom' => esc_js(sanitize_textarea_field(get_option('aicont_plugin_seo_title_prompt_custom', ''))), 142 'seo_meta_prompt_custom' => esc_js(sanitize_textarea_field(get_option('aicont_plugin_seo_meta_prompt_custom', ''))), 143 'faq_article_id' => esc_js(sanitize_text_field(get_option('aicont_plugin_faq_article_id', ''))), 144 'faq_question_id' => esc_js(sanitize_text_field(get_option('aicont_plugin_faq_question_id', ''))), 145 'faq_answer_id' => esc_js(sanitize_text_field(get_option('aicont_plugin_faq_answer_id', ''))), 146 'template_type' => esc_js(sanitize_text_field(get_option('aicont_plugin_template_type', 'ready'))), 147 ] 148 ); 128 129 wp_localize_script('aicont-post-tabs', 'aicontify_ajax', [ 130 'ajax_url' => admin_url('admin-ajax.php'), 131 'nonce' => wp_create_nonce('aicont_nonce') 132 ]); 149 133 } 150 134 } -
aicontify/trunk/contentPosts.php
r3397005 r3398370 2 2 if (!defined('ABSPATH')) exit; 3 3 4 // -------------------- REST API: Content Posts Generating (Client) -------------------- 5 add_action('rest_api_init', function() { 6 register_rest_route('aicont/v1', '/generate_contentPostss', [ 7 'methods' => 'POST', 8 'callback' => 'aicont_generate_content_posts_api', 9 'permission_callback' => function() { 10 return current_user_can('edit_posts'); 11 }, 12 'args' => [ 13 'keyword' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 14 'post_id' => ['required' => true, 'type' => 'integer', 'sanitize_callback' => 'absint'], 15 'api_key' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 16 'site_language' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 17 'site_title' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 18 'custom_prompt' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_textarea_field'], 19 ] 20 ]); 21 }); 4 add_action('wp_ajax_aicont_generate_and_save_content', 'aicont_generate_and_save_content_handler'); 22 5 23 function aicont_generate_ content_posts_api($request) {24 global $aicont_site_language, $aicont_site_title;6 function aicont_generate_and_save_content_handler() { 7 check_ajax_referer('aicont_nonce', 'nonce'); 25 8 26 $keyword = $request->get_param('keyword'); 27 $post_id = $request->get_param('post_id'); 28 $api_key = $request->get_param('api_key'); 29 $site_language = $request->get_param('site_language') ?: $aicont_site_language; 30 $site_title = $request->get_param('site_title') ?: $aicont_site_title; 31 $custom_prompt = $request->get_param('custom_prompt'); 9 $post_id = absint($_POST['post_id'] ?? 0); 10 $keyword = sanitize_text_field($_POST['keyword'] ?? ''); 11 $custom_prompt = sanitize_textarea_field($_POST['custom_prompt'] ?? ''); 32 12 33 if (empty($keyword)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_keyword_empty'], 400); 34 if (empty($post_id)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_id_invalid'], 400); 13 if (!$post_id || !get_post($post_id)) { 14 wp_send_json_error(['code' => 'err_post_id_invalid']); 15 } 35 16 36 $post = get_post($post_id); 37 if (!$post) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_not_found'], 404); 17 if (empty($keyword)) { 18 wp_send_json_error(['code' => 'err_keyword_empty']); 19 } 38 20 39 // Priority: custom_prompt from AJAX > post meta > global option 21 $post_title = get_the_title($post_id); 22 40 23 if (empty($custom_prompt)) { 41 24 $custom_prompt = get_post_meta($post_id, 'aicont_singlepost_content_prompt_custom', true); … … 45 28 } 46 29 30 $active_license = get_option('aicont_active_license', ''); 31 $active_license = $active_license ? trim(sanitize_text_field($active_license)) : ''; 32 33 $result = aicont_call_webtinus_server([ 34 'keyword' => $keyword, 35 'post_id' => $post_id, 36 'post_title' => $post_title, 37 'prompt' => $custom_prompt, 38 'api_key' => $active_license 39 ]); 40 41 if (is_wp_error($result) || empty($result['content'])) { 42 $error_code = $result['error_code'] ?? 'err_content_empty'; 43 wp_send_json_error(['code' => $error_code]); 44 } 45 46 $updated = wp_update_post([ 47 'ID' => $post_id, 48 'post_content' => wp_kses_post($result['content']) 49 ], true); 50 51 if (is_wp_error($updated)) { 52 wp_send_json_error(['code' => 'err_save_failed']); 53 } 54 55 wp_send_json_success([ 56 'message' => 'success', 57 'content_length' => strlen($result['content']) 58 ]); 59 } 60 61 function aicont_call_webtinus_server($args) { 47 62 $client_site_url = home_url('', 'https'); 48 if (empty($client_site_url)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_site_url'], 500); 49 50 $server_url = 'https://webtinus.com/wp-json/aicont/v1/generate_contentPostss'; 63 $server_url = 'https://webtinus.com/wp-json/aicont/v1/generate_contentPosts1'; 51 64 52 65 $body = wp_json_encode([ 53 'keyword' => $keyword, 54 'post_id' => $post_id, 55 'prompt' => $custom_prompt, 56 'api_key' => $api_key, 66 'keyword' => $args['keyword'], 67 'post_id' => $args['post_id'], 57 68 'client_site_url' => $client_site_url, 58 'site_title' => $site_title, 59 'site_language' => $site_language 69 'post_title' => $args['post_title'] ?? '', 70 'prompt' => $args['prompt'] ?? '', 71 'api_key' => $args['api_key'] ?? '', 72 'site_title' => get_bloginfo('name'), 73 'site_language' => get_locale() 60 74 ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); 61 75 62 76 $response = wp_remote_post($server_url, [ 63 'headers' => ['Content-Type' => 'application/json', 'X-Client-Site' => $client_site_url], 64 'body' => $body, 65 'timeout' => 120, 77 'headers' => [ 78 'Content-Type' => 'application/json', 79 'X-Client-Site' => $client_site_url, 80 'User-Agent' => 'AiContify-WordPress-Plugin/1.0' 81 ], 82 'body' => $body, 83 'timeout' => 180, 66 84 'sslverify' => true 67 85 ]); 68 86 69 87 if (is_wp_error($response)) { 70 return new WP_REST_Response(['success' => false, 'error_code' => 'err_connection_failed'], 500);88 return ['error_code' => 'err_connection_failed']; 71 89 } 72 90 73 $ status_code = wp_remote_retrieve_response_code($response);74 $ body = wp_remote_retrieve_body($response);75 $data = json_decode($ body, true);91 $response_code = wp_remote_retrieve_response_code($response); 92 $response_body = wp_remote_retrieve_body($response); 93 $data = json_decode($response_body, true); 76 94 77 if ($ status_code !== 200 || !isset($data['success'])) {78 $ code = $data['error_code'] ?? 'err_server_error';79 return new WP_REST_Response(['success' => false, 'error_code' => $code], $status_code);95 if ($response_code !== 200 || empty($data['success'])) { 96 $error_code = $data['error_code'] ?? 'err_server_error'; 97 return ['error_code' => $error_code]; 80 98 } 81 99 82 if ( !$data['success'] ||empty($data['content'])) {83 return new WP_REST_Response(['success' => false, 'error_code' => $data['error_code'] ?? 'err_content_empty'], 500);100 if (empty($data['content'])) { 101 return ['error_code' => 'err_content_empty']; 84 102 } 85 103 86 return new WP_REST_Response([ 87 'success' => true, 88 'message_code' => 'success_content_generated', 89 'content' => trim($data['content']) 90 ], 200); 104 return ['content' => trim($data['content'])]; 91 105 } 92 ?> -
aicontify/trunk/faqPosts.php
r3397005 r3398370 2 2 if (!defined('ABSPATH')) exit; 3 3 4 // -------------------- REST API: FAQ Generator (Client) -------------------- 5 add_action('rest_api_init', function() { 6 register_rest_route('aicont/v1', '/generate_faqq', [ 7 'methods' => 'POST', 8 'callback' => 'aicont_generate_faq_api', 9 'permission_callback' => function() { 10 return current_user_can('edit_posts'); 11 }, 12 'args' => [ 13 'keyword' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 14 'post_id' => ['required' => true, 'type' => 'integer', 'sanitize_callback' => 'absint'], 15 'post_title' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 16 'api_key' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 17 'site_language' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 18 'site_title' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 19 'prompt' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_textarea_field'], 20 ] 21 ]); 22 }); 4 add_action('wp_ajax_aicont_generate_and_save_faq', 'aicont_generate_and_save_faq_handler'); 23 5 24 function aicont_generate_ faq_api($request) {25 global $aicont_api_key, $aicont_site_language, $aicont_site_title;6 function aicont_generate_and_save_faq_handler() { 7 check_ajax_referer('aicont_nonce', 'nonce'); 26 8 27 $keyword = $request->get_param('keyword'); 28 $post_id = $request->get_param('post_id'); 29 $post_title = $request->get_param('post_title'); 30 $api_key = $request->get_param('api_key') ?: $aicont_api_key; 31 $site_language = $request->get_param('site_language') ?: $aicont_site_language; 32 $site_title = $request->get_param('site_title') ?: $aicont_site_title; 33 $prompt = $request->get_param('prompt'); 9 $post_id = absint($_POST['post_id'] ?? 0); 10 $keyword = sanitize_text_field($_POST['keyword'] ?? ''); 11 $custom_prompt = sanitize_textarea_field($_POST['custom_prompt'] ?? ''); 34 12 35 // Validation 36 if (empty($keyword)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_keyword_empty'], 400); 37 if (empty($post_id)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_id_invalid'], 400); 38 if (empty($post_title)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_title_missing'], 400); 39 40 $post = get_post($post_id); 41 if (!$post) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_not_found'], 404); 42 43 // Priority: custom_prompt from request > post meta > global option 44 if (empty($prompt)) { 45 $prompt = get_post_meta($post_id, 'aicont_singlepost_faq_prompt_custom', true); 13 if (!$post_id || !get_post($post_id)) { 14 wp_send_json_error(['code' => 'err_post_id_invalid']); 46 15 } 47 if (empty($ prompt)) {48 $prompt = get_option('aicont_plugin_faq_prompt_custom', '');16 if (empty($keyword)) { 17 wp_send_json_error(['code' => 'err_keyword_empty']); 49 18 } 50 19 20 $post_title = get_the_title($post_id); 21 if (empty($post_title)) { 22 wp_send_json_error(['code' => 'err_post_title_missing']); 23 } 24 25 if (empty($custom_prompt)) { 26 $custom_prompt = get_post_meta($post_id, 'aicont_singlepost_faq_prompt_custom', true); 27 } 28 if (empty($custom_prompt)) { 29 $custom_prompt = get_option('aicont_plugin_faq_prompt_custom', ''); 30 } 31 32 $active_license = get_option('aicont_active_license', ''); 33 34 $result = aicont_call_webtinus_faq([ 35 'keyword' => $keyword, 36 'post_id' => $post_id, 37 'post_title' => $post_title, 38 'prompt' => $custom_prompt, 39 'api_key' => $active_license 40 ]); 41 42 if (is_wp_error($result) || empty($result['faq_content'])) { 43 wp_send_json_error(['code' => $result['error_code'] ?? 'err_faq_empty']); 44 } 45 46 $saved = aicont_save_faq_to_post($post_id, $result['faq_content']); 47 if (!$saved) { 48 wp_send_json_error(['code' => 'err_save_failed']); 49 } 50 51 wp_send_json_success(['message' => 'success_faq_generated']); 52 } 53 54 function aicont_call_webtinus_faq($args) { 51 55 $client_site_url = home_url('', 'https'); 52 if (empty($client_site_url)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_site_url'], 500);56 $server_url = 'https://webtinus.com/wp-json/aicont/v1/generate_faq1'; 53 57 54 $server_url = 'https://webtinus.com/wp-json/aicont/v1/generate_faqq';55 58 $body = wp_json_encode([ 56 'keyword' => $ keyword,57 'post_id' => $ post_id,58 'post_title' => $ post_title,59 'prompt' => $ prompt,60 'api_key' => $a pi_key,59 'keyword' => $args['keyword'], 60 'post_id' => $args['post_id'], 61 'post_title' => $args['post_title'], 62 'prompt' => $args['prompt'], 63 'api_key' => $args['api_key'] ?? '', 61 64 'client_site_url' => $client_site_url, 62 'site_title' => $site_title,63 'site_language' => $site_language65 'site_title' => get_bloginfo('name'), 66 'site_language' => get_locale() 64 67 ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); 65 68 66 69 $response = wp_remote_post($server_url, [ 67 70 'headers' => ['Content-Type' => 'application/json', 'X-Client-Site' => $client_site_url], 68 'body' => $body, 69 'timeout' => 120, 70 'sslverify' => true 71 'body' => $body, 72 'timeout' => 180 71 73 ]); 72 74 73 if (is_wp_error($response)) { 74 return new WP_REST_Response(['success' => false, 'error_code' => 'err_connection_failed'], 500); 75 if (is_wp_error($response)) return ['error_code' => 'err_connection_failed']; 76 77 $data = json_decode(wp_remote_retrieve_body($response), true); 78 79 if (empty($data['success']) || empty($data['faq_content'])) { 80 return ['error_code' => $data['error_code'] ?? 'err_faq_empty']; 75 81 } 76 82 77 $status_code = wp_remote_retrieve_response_code($response); 78 $body = wp_remote_retrieve_body($response); 79 $data = json_decode($body, true); 83 return ['faq_content' => trim($data['faq_content'])]; 84 } 80 85 81 if ($status_code !== 200 || !isset($data['success'])) { 82 $code = $data['error_code'] ?? 'err_server_error'; 83 return new WP_REST_Response(['success' => false, 'error_code' => $code], $status_code); 84 } 85 86 if (!$data['success'] || empty($data['faq_content'])) { 87 return new WP_REST_Response(['success' => false, 'error_code' => $data['error_code'] ?? 'err_faq_empty'], 500); 88 } 89 90 $faq_content = trim($data['faq_content']); 86 function aicont_save_faq_to_post($post_id, $faq_content) { 91 87 $lines = array_filter(preg_split('/\r\n|\r|\n/', $faq_content), 'trim'); 92 93 88 $template_type = get_option('aicont_plugin_template_type', 'ready'); 94 $faq_html = '';95 89 96 90 if ($template_type === 'custom') { 97 $article_class = sanitize_html_class(get_option('aicont_plugin_faq_article_id', 'faq-article')); 98 $question_class = sanitize_html_class(get_option('aicont_plugin_faq_question_id', 'faq-question')); 99 $answer_class = sanitize_html_class(get_option('aicont_plugin_faq_answer_id', 'faq-answer')); 91 if (!function_exists('update_field')) { 92 return false; 93 } 94 $repeater = get_option('aicont_plugin_faq_article_id', 'faq_post_single'); 95 $q_field = get_option('aicont_plugin_faq_question_id', 'tit_faq_post_single'); 96 $a_field = get_option('aicont_plugin_faq_answer_id', 'desc_faq_post_single'); 100 97 101 $faq_html .= "<article class='" . esc_attr($article_class) . "'>"; 102 $faq_html .= "<h2>" . esc_html__('Frequently Asked Questions', 'aicontify') . "</h2>"; 103 for ($i = 0; $i < count($lines); $i += 2) { 104 $question = trim($lines[$i] ?? ''); 105 $answer = trim($lines[$i + 1] ?? ''); 106 if ($question && $answer) { 107 $faq_html .= "<h3 class='" . esc_attr($question_class) . "'>" . esc_html($question) . "</h3>"; 108 $faq_html .= "<p class='" . esc_attr($answer_class) . "'>" . wp_kses_post($answer) . "</p>"; 98 delete_field($repeater, $post_id); 99 foreach (array_chunk($lines, 2) as $pair) { 100 if (count($pair) === 2) { 101 add_row($repeater, [$q_field => $pair[0], $a_field => $pair[1]], $post_id); 109 102 } 110 103 } 111 $faq_html .= "</article>";112 104 } else { 113 $faq_html .= "<div class='aicont-faq-section'>"; 114 $faq_html .= "<h2>" . esc_html__('Frequently Asked Questions', 'aicontify') . "</h2>"; 115 for ($i = 0; $i < count($lines); $i += 2) { 116 $question = trim($lines[$i] ?? ''); 117 $answer = trim($lines[$i + 1] ?? ''); 118 if ($question && $answer) { 119 $faq_html .= "<h3>" . esc_html($question) . "</h3>"; 120 $faq_html .= "<p>" . wp_kses_post($answer) . "</p>"; 105 $faq_html = "<div class='aicont-faq-section'><h2>" . __('Frequently Asked Questions', 'aicontify') . "</h2>"; 106 foreach (array_chunk($lines, 2) as $pair) { 107 if (count($pair) === 2) { 108 $faq_html .= "<h3>" . esc_html($pair[0]) . "</h3>"; 109 $faq_html .= "<p>" . wp_kses_post($pair[1]) . "</p>"; 121 110 } 122 111 } 123 112 $faq_html .= "</div>"; 113 114 $post = get_post($post_id); 115 $new_content = $post->post_content . "\n" . $faq_html; 116 117 wp_update_post([ 118 'ID' => $post_id, 119 'post_content' => $new_content 120 ], true); 124 121 } 125 122 126 $saved = 0; 127 if ($template_type === 'custom') { 128 $acf_repeater_field = get_option('aicont_plugin_faq_article_id', 'faq_post_single'); 129 $acf_question_field = get_option('aicont_plugin_faq_question_id', 'tit_faq_post_single'); 130 $acf_answer_field = get_option('aicont_plugin_faq_answer_id', 'desc_faq_post_single'); 131 132 if (!function_exists('delete_field') || !function_exists('add_row')) { 133 return new WP_REST_Response(['success' => false, 'error_code' => 'err_acf_missing'], 400); 134 } 135 136 delete_field($acf_repeater_field, $post_id); 137 for ($i = 0; $i < count($lines); $i += 2) { 138 $question = trim($lines[$i] ?? ''); 139 $answer = trim($lines[$i + 1] ?? ''); 140 if ($question && $answer) { 141 $added = add_row($acf_repeater_field, [ 142 $acf_question_field => $question, 143 $acf_answer_field => $answer, 144 ], $post_id); 145 if ($added) $saved++; 146 } 147 } 148 149 if ($saved === 0) { 150 return new WP_REST_Response(['success' => false, 'error_code' => 'err_save_failed'], 500); 151 } 152 153 } else { 154 $current_content = $post->post_content; 155 $post_data = [ 156 'ID' => $post_id, 157 'post_content' => $current_content . "\n" . wp_kses_post($faq_html), 158 'post_modified' => current_time('mysql'), 159 'post_modified_gmt' => current_time('mysql', 1), 160 ]; 161 $post_updated = wp_update_post($post_data, true); 162 if (is_wp_error($post_updated)) { 163 return new WP_REST_Response(['success' => false, 'error_code' => 'err_save_failed'], 500); 164 } 165 } 166 167 return new WP_REST_Response([ 168 'success' => true, 169 'message_code' => 'success_faq_generated', 170 'faq_content' => $faq_content, 171 'faq_html' => $faq_html 172 ], 200); 123 return true; 173 124 } -
aicontify/trunk/js/tabsPosts.js
r3397005 r3398370 56 56 }); 57 57 58 // Varglobal58 // global 59 59 const keywordInput = document.getElementById("aicont_keyword"); 60 60 61 // AiContify Content Generator 62 const contentBtn = document.getElementById( 61 /** 62 * AiContify - Content Generator 63 */ 64 65 const generateBtn = document.getElementById( 63 66 "aicont-generate-contentPosts-btn" 64 67 ); 65 const contentResultBox = document.getElementById( 66 "aicont-contentPosts-result" 67 ); 68 const contentLoadingBox = document.getElementById("aicont-loading-box"); 69 70 // --- Message Translator --- 71 function getContentMessage(code) { 72 const messages = { 73 err_keyword_empty: 74 "Please enter a keyword in the field above the tabs before generating content.", 75 err_post_id_invalid: "Invalid post ID.", 76 err_wp_settings_missing: 77 "WordPress API settings are missing. Please check plugin configuration.", 68 const resultBox = document.getElementById("aicont-contentPosts-result"); 69 const loadingBox = document.getElementById("aicont-loading-box"); 70 71 if (!keywordInput || !generateBtn || !resultBox || !loadingBox) { 72 console.warn("AiContify: Required elements not found."); 73 return; 74 } 75 76 function toggleGenerateButton() { 77 const hasKeyword = keywordInput.value.trim().length > 0; 78 generateBtn.disabled = !hasKeyword; 79 generateBtn.style.opacity = hasKeyword ? "1" : "0.5"; 80 generateBtn.style.cursor = hasKeyword ? "pointer" : "not-allowed"; 81 } 82 83 keywordInput.addEventListener("input", toggleGenerateButton); 84 keywordInput.addEventListener("paste", () => 85 setTimeout(toggleGenerateButton, 100) 86 ); 87 toggleGenerateButton(); 88 89 function getMessage(key) { 90 const translations = { 91 generating: "Generating and saving content, please wait...", 92 success_generated: "Content generated successfully!", 93 saving_post: "Saving post...", 94 page_refreshing: "Page is refreshing to display changes...", 95 err_keyword_empty: "Please enter a keyword before generating content.", 78 96 err_connection_failed: 79 "Failed to connect to the server. Please check your internet.", 80 err_server_error: "Server error occurred.", 81 err_invalid_response: "Invalid response from server.", 97 "Connection failed. Please check your internet connection.", 98 err_server_error: "An unexpected server error occurred.", 82 99 err_content_empty: "Generated content is empty.", 83 err_save_timeout: "Save operation timed out.",84 100 err_save_failed: "Failed to save the post.", 85 err_invalid_site_url: "Client site URL is invalid.",86 101 err_daily_limit_exceeded: 87 "Daily AI post limit reached. Upgrade to Pro or try tomorrow.", 88 89 success_content_generated: "Content generated and inserted. Saving...", 90 success_content_saved: "Content saved successfully.", 102 "Daily AI generation limit reached. Try again tomorrow or upgrade.", 91 103 }; 92 return wp.i18n.__(messages[code] || "Unknown error occurred.", "aicontify"); 93 } 94 95 function getPostTitle() { 96 if (wp?.data?.select("core/editor")) { 97 const gutenbergTitle = wp.data 98 .select("core/editor") 99 .getEditedPostAttribute("title") 100 ?.trim(); 101 if (gutenbergTitle) { 102 return gutenbergTitle; 103 } 104 } 105 const classicTitleInput = document.getElementById("title"); 106 if (classicTitleInput && classicTitleInput.value?.trim()) { 107 return classicTitleInput.value.trim(); 108 } 109 const metaInput = document.querySelector('input[name="post_title"]'); 110 if (metaInput && metaInput.value?.trim()) { 111 return metaInput.value.trim(); 112 } 113 return null; 114 } 115 116 function insertContent(content) { 117 if (wp?.data?.dispatch("core/editor")) { 118 wp.data.dispatch("core/editor").editPost({ content: content }); 119 return true; 120 } 121 if (typeof tinyMCE !== "undefined" && tinyMCE.activeEditor) { 122 tinyMCE.activeEditor.setContent(content); 123 return true; 124 } 125 const contentTextarea = document.getElementById("content"); 126 if (contentTextarea) { 127 contentTextarea.value = content; 128 contentTextarea.dispatchEvent(new Event("change", { bubbles: true })); 129 return true; 130 } 131 return false; 132 } 133 134 async function savePost() { 135 if (wp?.data?.dispatch("core/editor")) { 136 try { 137 await wp.data.dispatch("core/editor").savePost(); 138 let isSaved = false; 139 for (let i = 0; i < 15; i++) { 140 await new Promise((r) => setTimeout(r, 500)); 141 if (!wp.data.select("core/editor").isSavingPost()) { 142 isSaved = true; 143 break; 144 } 145 } 146 if (!isSaved) throw new Error("err_save_timeout"); 147 return true; 148 } catch (err) { 149 throw new Error( 150 err.message.includes("timeout") 151 ? "err_save_timeout" 152 : "err_save_failed" 153 ); 154 } 155 } 156 157 const postForm = document.getElementById("post"); 158 if (postForm) { 159 try { 160 const publishButton = 161 document.getElementById("publish") || 162 document.querySelector('button[type="submit"]'); 163 if (publishButton) { 164 publishButton.click(); 165 await new Promise((r) => setTimeout(r, 2000)); 166 return true; 167 } 168 } catch (err) { 169 throw new Error("err_save_failed"); 170 } 171 } 172 throw new Error("err_save_failed"); 173 } 174 175 function toggleGenerateButton() { 176 const keyword = keywordInput?.value?.trim(); 177 if (keyword && keyword.length > 0) { 178 contentBtn.disabled = false; 179 contentBtn.style.opacity = "1"; 180 contentBtn.style.cursor = "pointer"; 181 } else { 182 contentBtn.disabled = true; 183 contentBtn.style.opacity = "0.5"; 184 contentBtn.style.cursor = "not-allowed"; 185 } 186 } 187 188 if (contentBtn && keywordInput) { 189 toggleGenerateButton(); 190 keywordInput.addEventListener("input", toggleGenerateButton); 191 keywordInput.addEventListener("paste", () => 192 setTimeout(toggleGenerateButton, 0) 193 ); 194 } 195 196 // Content Generation Handler 197 if (contentBtn) { 198 contentBtn.addEventListener("click", async function () { 199 const postId = this.getAttribute("data-postid"); 200 const keyword = keywordInput?.value?.trim(); 201 202 if (!postId || isNaN(postId)) { 203 contentResultBox.innerHTML = `<p class="aicont-error">${getContentMessage( 204 "err_post_id_invalid" 205 )}</p>`; 206 return; 207 } 208 209 if (!keyword || keyword.length === 0) { 210 contentResultBox.innerHTML = `<p class="aicont-error">${getContentMessage( 211 "err_keyword_empty" 212 )}</p>`; 213 return; 214 } 215 216 contentLoadingBox.style.display = "block"; 217 contentResultBox.innerHTML = ""; 218 219 try { 220 const singlePostPrompt = 221 document 222 .getElementById("aicont_singlepost_content_prompt_custom") 223 ?.value?.trim() || ""; 224 const globalPrompt = aicont?.contentPrompt || ""; 225 226 const customPrompt = singlePostPrompt || globalPrompt || ""; 227 228 const response = await fetch(aicont.ajaxUrl, { 229 method: "POST", 230 headers: { 231 "Content-Type": "application/x-www-form-urlencoded", 232 }, 233 body: new URLSearchParams({ 234 action: "aicont_generate_content", 235 nonce: aicont.nonce, 236 post_id: postId, 237 keyword: keyword, 238 custom_prompt: customPrompt, 239 }).toString(), 240 }); 241 242 const data = await response.json(); 243 244 if (!data.success) { 245 throw new Error(data.data?.message || "err_server_error"); 246 } 247 248 if (!data.data?.content || data.data.content.trim().length === 0) { 249 throw new Error("err_content_empty"); 250 } 251 252 const isInserted = insertContent(data.data.content); 253 if (!isInserted) { 254 throw new Error("err_invalid_response"); 255 } 256 257 contentResultBox.innerHTML = `<p class="aicont-success">${getContentMessage( 258 "success_content_generated" 259 )}</p>`; 260 261 await savePost(); 262 263 contentResultBox.innerHTML = `<p class="aicont-success">${getContentMessage( 264 "success_content_saved" 265 )}</p>`; 266 contentLoadingBox.style.display = "none"; 267 } catch (error) { 268 contentLoadingBox.style.display = "none"; 269 const errorCode = 270 error.message === "err_save_timeout" || 271 error.message === "err_save_failed" || 272 error.message === "err_invalid_response" || 273 error.message === "err_content_empty" || 274 error.message === "err_keyword_empty" || 275 error.message === "err_post_id_invalid" 276 ? error.message 277 : "err_server_error"; 278 279 contentResultBox.innerHTML = `<p class="aicont-error">${getContentMessage( 280 errorCode 281 )}</p>`; 282 283 console.error("AiContify Content Generation Error:", error); 284 } 285 }); 286 } 287 288 // --- Content Generator --- 289 if (contentBtn) { 290 contentBtn.addEventListener("click", async function () { 291 const keyword = keywordInput?.value?.trim(); 292 if (!keyword) { 293 contentResultBox.innerHTML = `<span class='error'>${getContentMessage( 294 "err_keyword_empty" 295 )}</span>`; 296 return; 297 } 298 299 const postId = contentBtn.dataset.postid; 300 if (!postId || isNaN(postId)) { 301 contentResultBox.innerHTML = `<span class='error'>${getContentMessage( 302 "err_post_id_invalid" 303 )}</span>`; 304 return; 305 } 306 307 if ( 308 !window.aicontApiSettings || 309 !aicontApiSettings.root || 310 !aicontApiSettings.nonce 311 ) { 312 contentResultBox.innerHTML = `<span class='error'>${getContentMessage( 313 "err_wp_settings_missing" 314 )}</span>`; 315 return; 316 } 317 318 contentBtn.disabled = true; 319 contentBtn.textContent = wp.i18n.__("Generating...", "aicontify"); 320 contentResultBox.innerHTML = ""; 321 contentLoadingBox.style.display = "block"; 322 323 try { 324 const requestData = { 325 keyword, 326 post_id: parseInt(postId), 327 api_key: aicontApiSettings.api_key || "", 328 site_language: aicontApiSettings.site_language, 329 site_title: aicontApiSettings.site_title, 330 prompt: aicontApiSettings.content_prompt_custom || "", 331 }; 332 333 const response = await fetch( 334 aicontApiSettings.root + "aicont/v1/generate_contentPostss", 335 { 336 method: "POST", 337 headers: { 338 "Content-Type": "application/json", 339 "X-WP-Nonce": aicontApiSettings.nonce, 340 }, 341 body: JSON.stringify(requestData), 342 } 343 ); 344 345 let data; 346 try { 347 data = await response.json(); 348 } catch (jsonErr) { 349 throw new Error("err_invalid_response"); 350 } 351 352 if (!response.ok) { 353 const code = data.error_code || "err_server_error"; 354 throw new Error(code); 355 } 356 357 if (data.success && data.content) { 358 if (!insertContent(data.content)) { 359 throw new Error("err_save_failed"); 360 } 361 362 contentResultBox.innerHTML = `<span class='success'>${getContentMessage( 363 "success_content_generated" 364 )}</span><br><span style='color: #0073aa;'>${wp.i18n.__( 365 "Saving post...", 366 "aicontify" 367 )}</span>`; 368 369 await savePost(); 370 371 contentResultBox.innerHTML = `<span class='success'>${getContentMessage( 372 "success_content_saved" 373 )}</span><br><span style='color: #0073aa;'>${wp.i18n.__( 374 "Page is refreshing to display changes...", 375 "aicontify" 376 )}</span>`; 377 378 setTimeout(() => location.reload(), 1500); 379 } else { 380 throw new Error(data.error_code || "err_invalid_response"); 381 } 382 } catch (err) { 383 const code = 384 typeof err.message === "string" && err.message.startsWith("err_") 385 ? err.message 386 : "err_server_error"; 387 contentResultBox.innerHTML = `<span class='error'>${getContentMessage( 388 code 389 )}</span>`; 390 } finally { 391 toggleGenerateButton(); 392 contentBtn.textContent = wp.i18n.__( 393 "Generate Main Content", 394 "aicontify" 395 ); 396 contentLoadingBox.style.display = "none"; 397 } 398 }); 399 } 400 401 // --- FAQ Generator --- 104 return wp.i18n.__(translations[key] || key, "aicontify"); 105 } 106 107 generateBtn.addEventListener("click", async function (e) { 108 e.preventDefault(); 109 110 const postId = generateBtn.getAttribute("data-postid"); 111 const keyword = keywordInput.value.trim(); 112 113 resultBox.innerHTML = ""; 114 loadingBox.style.display = "block"; 115 resultBox.innerHTML = `<span style='color: #0073aa;'>${getMessage( 116 "generating" 117 )}</span>`; 118 119 if (!keyword) { 120 resultBox.innerHTML = `<span class='error'>${getMessage( 121 "err_keyword_empty" 122 )}</span>`; 123 loadingBox.style.display = "none"; 124 return; 125 } 126 127 try { 128 const customPromptEl = document.getElementById( 129 "aicont_singlepost_content_prompt_custom" 130 ); 131 const customPrompt = customPromptEl ? customPromptEl.value.trim() : ""; 132 133 const formData = new URLSearchParams(); 134 formData.append("action", "aicont_generate_and_save_content"); 135 formData.append("nonce", aicontify_ajax.nonce); 136 formData.append("post_id", postId); 137 formData.append("keyword", keyword); 138 formData.append("custom_prompt", customPrompt); 139 140 const response = await fetch(aicontify_ajax.ajax_url, { 141 method: "POST", 142 credentials: "same-origin", 143 headers: { 144 "Content-Type": "application/x-www-form-urlencoded", 145 }, 146 body: formData, 147 }); 148 149 const data = await response.json(); 150 151 if (!response.ok || !data.success) { 152 const code = data.data?.code || "err_server_error"; 153 throw new Error(code); 154 } 155 156 resultBox.innerHTML = ` 157 <span class='success'>${getMessage("success_generated")}</span><br> 158 <span style='color: #0073aa;'>${getMessage("saving_post")}</span> 159 `; 160 161 setTimeout(() => { 162 resultBox.innerHTML = ` 163 <span class='success'>${getMessage("success_generated")}</span><br> 164 <span style='color: #0073aa;'>${getMessage("page_refreshing")}</span> 165 `; 166 setTimeout(() => location.reload(), 1200); 167 }, 800); 168 } catch (error) { 169 console.error("AiContify Error:", error); 170 171 const errorCode = error.message; 172 const errorMessages = { 173 err_keyword_empty: "err_keyword_empty", 174 err_connection_failed: "err_connection_failed", 175 err_server_error: "err_server_error", 176 err_content_empty: "err_content_empty", 177 err_save_failed: "err_save_failed", 178 err_daily_limit_exceeded: "err_daily_limit_exceeded", 179 }; 180 181 const msgKey = errorMessages[errorCode] || "err_server_error"; 182 resultBox.innerHTML = `<span class='error'>${getMessage(msgKey)}</span>`; 183 } finally { 184 loadingBox.style.display = "none"; 185 } 186 }); 187 188 // --- FAQ Generator (PHP-Only + AJAX) --- 402 189 const faqBtn = document.getElementById("aicont-generate-faq-btn"); 403 190 const faqResultBox = document.getElementById("aicont-faq-result"); 404 191 const faqLoadingBox = document.getElementById("aicont-faq-loading-box"); 405 192 406 // --- Message Translator for FAQ --- 407 function getFaqMessage(code) { 408 const messages = { 409 // Errors 410 err_keyword_empty: 411 "Please enter a keyword in the field above the tabs before generating FAQs.", 412 err_post_id_invalid: "Invalid post ID.", 413 err_wp_settings_missing: 414 "WordPress API settings are missing. Please check plugin configuration.", 415 err_connection_failed: 416 "Failed to connect to the server. Please check your internet.", 417 err_server_error: "Server error occurred.", 418 err_invalid_response: "Invalid response from server.", 419 err_faq_empty: "Generated FAQ content is empty.", 420 err_acf_missing: 421 "ACF functions are not available. Ensure ACF Pro is installed and active.", 422 err_save_failed: "Failed to save FAQ items.", 423 err_post_title_missing: "Post title is missing.", 424 err_daily_limit_exceeded: 425 "Daily AI post limit reached. Upgrade to Pro or try tomorrow.", 426 427 // Success 428 success_faq_generated: "FAQs generated and saved successfully!", 429 }; 430 431 return wp.i18n.__(messages[code] || "Unknown error occurred.", "aicontify"); 432 } 433 434 function getPostTitle() { 435 if (wp?.data?.select("core/editor")) { 436 const gutenbergTitle = wp.data 437 .select("core/editor") 438 .getEditedPostAttribute("title") 439 ?.trim(); 440 if (gutenbergTitle) { 441 return gutenbergTitle; 442 } 443 } 444 445 const classicTitleInput = document.getElementById("title"); 446 if (classicTitleInput && classicTitleInput.value?.trim()) { 447 return classicTitleInput.value.trim(); 448 } 449 450 const metaInput = document.querySelector('input[name="post_title"]'); 451 if (metaInput && metaInput.value?.trim()) { 452 return metaInput.value.trim(); 453 } 454 455 return null; 456 } 457 458 function toggleFaqButton() { 459 const keyword = keywordInput?.value?.trim(); 460 if (keyword && keyword.length > 0) { 461 faqBtn.disabled = false; 462 faqBtn.style.opacity = "1"; 463 faqBtn.style.cursor = "pointer"; 464 } else { 465 faqBtn.disabled = true; 466 faqBtn.style.opacity = "0.5"; 467 faqBtn.style.cursor = "not-allowed"; 468 } 469 } 470 471 if (faqBtn && keywordInput) { 472 toggleFaqButton(); 193 if (faqBtn && keywordInput && faqResultBox && faqLoadingBox) { 194 function toggleFaqButton() { 195 const hasKeyword = keywordInput.value.trim().length > 0; 196 faqBtn.disabled = !hasKeyword; 197 faqBtn.style.opacity = hasKeyword ? "1" : "0.5"; 198 faqBtn.style.cursor = hasKeyword ? "pointer" : "not-allowed"; 199 } 200 473 201 keywordInput.addEventListener("input", toggleFaqButton); 474 202 keywordInput.addEventListener("paste", () => 475 setTimeout(toggleFaqButton, 0)203 setTimeout(toggleFaqButton, 100) 476 204 ); 477 } 478 479 if (faqBtn) { 480 faqBtn.addEventListener("click", async function () { 481 const keyword = keywordInput?.value?.trim(); 205 toggleFaqButton(); 206 207 function getFaqMessage(key) { 208 const translations = { 209 generating: "Generating FAQs, please wait...", 210 success_generated: "FAQs generated and saved successfully!", 211 saving_post: "Saving FAQs to post...", 212 page_refreshing: "Page is refreshing to display changes...", 213 err_keyword_empty: "Please enter a keyword before generating FAQs.", 214 err_server_error: "Server error occurred while generating FAQs.", 215 err_save_failed: "Failed to save FAQ items.", 216 err_acf_missing: "ACF Pro is required for custom FAQ templates.", 217 err_daily_limit_exceeded: 218 "Daily AI generation limit reached. Upgrade or try tomorrow.", 219 }; 220 return wp.i18n.__(translations[key] || key, "aicontify"); 221 } 222 223 faqBtn.addEventListener("click", async function (e) { 224 e.preventDefault(); 225 226 const postId = faqBtn.getAttribute("data-postid"); 227 const keyword = keywordInput.value.trim(); 228 229 faqResultBox.innerHTML = ""; 230 faqLoadingBox.style.display = "block"; 231 faqResultBox.innerHTML = `<span style='color: #0073aa;'>${getFaqMessage( 232 "generating" 233 )}</span>`; 234 482 235 if (!keyword) { 483 236 faqResultBox.innerHTML = `<span class='error'>${getFaqMessage( 484 237 "err_keyword_empty" 485 238 )}</span>`; 239 faqLoadingBox.style.display = "none"; 486 240 return; 487 241 } 488 242 489 const postId = faqBtn.dataset.postid;490 if (!postId || isNaN(postId)) {491 faqResultBox.innerHTML = `<span class='error'>${getFaqMessage(492 "err_post_id_invalid"493 )}</span>`;494 return;495 }496 497 if (498 !window.aicontApiSettings ||499 !aicontApiSettings.root ||500 !aicontApiSettings.nonce501 ) {502 faqResultBox.innerHTML = `<span class='error'>${getFaqMessage(503 "err_wp_settings_missing"504 )}</span>`;505 return;506 }507 508 faqBtn.disabled = true;509 faqBtn.textContent = wp.i18n.__("Generating FAQs...", "aicontify");510 faqResultBox.innerHTML = "";511 faqLoadingBox.style.display = "block";512 513 243 try { 514 const postTitle = getPostTitle(); 515 if (!postTitle) throw new Error("err_post_title_missing"); 516 517 const singlePostPrompt = 518 document 519 .getElementById("aicont_singlepost_faq_prompt_custom") 520 ?.value?.trim() || ""; 521 const globalPrompt = aicontApiSettings.faq_prompt_custom || ""; 522 523 const customPrompt = singlePostPrompt || globalPrompt || ""; 524 525 const requestData = { 526 keyword, 527 post_id: parseInt(postId), 528 post_title: postTitle, 529 api_key: aicontApiSettings.api_key || "", 530 site_language: aicontApiSettings.site_language, 531 site_title: aicontApiSettings.site_title, 532 prompt: customPrompt, 533 }; 534 535 const response = await fetch( 536 aicontApiSettings.root + "aicont/v1/generate_faqq", 537 { 538 method: "POST", 539 headers: { 540 "Content-Type": "application/json", 541 "X-WP-Nonce": aicontApiSettings.nonce, 542 }, 543 body: JSON.stringify(requestData), 544 } 244 const customPromptEl = document.getElementById( 245 "aicont_singlepost_faq_prompt_custom" 545 246 ); 546 547 let data; 548 try { 549 data = await response.json(); 550 } catch (jsonErr) { 551 throw new Error("err_invalid_response"); 552 } 553 554 if (!response.ok) { 555 const code = data.error_code || "err_server_error"; 247 const customPrompt = customPromptEl ? customPromptEl.value.trim() : ""; 248 249 const formData = new URLSearchParams(); 250 formData.append("action", "aicont_generate_and_save_faq"); 251 formData.append("nonce", aicontify_ajax.nonce); 252 formData.append("post_id", postId); 253 formData.append("keyword", keyword); 254 formData.append("custom_prompt", customPrompt); 255 256 const response = await fetch(aicontify_ajax.ajax_url, { 257 method: "POST", 258 credentials: "same-origin", 259 headers: { "Content-Type": "application/x-www-form-urlencoded" }, 260 body: formData, 261 }); 262 263 const data = await response.json(); 264 265 if (!response.ok || !data.success) { 266 const code = data.data?.code || "err_server_error"; 556 267 throw new Error(code); 557 268 } 558 269 559 if (data.success && data.faq_content) { 560 faqResultBox.innerHTML = ` 561 <span class='success'>${getFaqMessage( 562 "success_faq_generated" 563 )}</span><br> 564 <span style='color: #0073aa;'>${wp.i18n.__( 565 "Page is refreshing to display changes...", 566 "aicontify" 567 )}</span> 270 faqResultBox.innerHTML = ` 271 <span class='success'>${getFaqMessage("success_generated")}</span><br> 272 <span style='color: #0073aa;'>${getFaqMessage("page_refreshing")}</span> 568 273 `; 569 setTimeout(() => location.reload(), 1500); 570 } else { 571 throw new Error(data.error_code || "err_invalid_response"); 572 } 573 } catch (err) { 574 const code = 575 typeof err.message === "string" && err.message.startsWith("err_") 576 ? err.message 577 : "err_server_error"; 274 275 setTimeout(() => location.reload(), 1500); 276 } catch (error) { 277 console.error("AiContify FAQ Error:", error); 278 const code = error.message; 279 const msg = 280 { 281 err_keyword_empty: "err_keyword_empty", 282 err_save_failed: "err_save_failed", 283 err_acf_missing: "err_acf_missing", 284 err_daily_limit_exceeded: "err_daily_limit_exceeded", 285 }[code] || "err_server_error"; 578 286 579 287 faqResultBox.innerHTML = `<span class='error'>${getFaqMessage( 580 code288 msg 581 289 )}</span>`; 582 290 } finally { 583 toggleFaqButton();584 faqBtn.textContent = wp.i18n.__("Generate FAQs", "aicontify");585 291 faqLoadingBox.style.display = "none"; 586 292 } … … 588 294 } 589 295 590 // --- SEO Title Generator ---296 // --- SEO Title Generator (PHP-Only + AJAX) --- 591 297 const seoTitleBtn = document.getElementById("aicont-generate-seo-title-btn"); 592 298 const seoTitleResultBox = document.getElementById("aicont-seo-title-result"); … … 595 301 ); 596 302 597 // --- Message Translator for SEO Title --- 598 function getSeoTitleMessage(code) { 599 const messages = { 600 // Errors 601 err_keyword_empty: 602 "Please enter a keyword in the field above the tabs before generating SEO title.", 603 err_post_id_invalid: "Invalid post ID.", 604 err_wp_settings_missing: 605 "WordPress API settings (API key or endpoint) are missing or incomplete. Please check plugin configuration.", 606 err_license_required: 607 "API key is required for non-free models. Please configure a valid API key in the plugin settings.", 608 err_connection_failed: 609 "Failed to connect to the server. Please check your internet.", 610 err_server_error: "Server error occurred.", 611 err_invalid_response: "Invalid response from server.", 612 err_title_empty: "Generated SEO title is empty.", 613 err_save_failed: "Failed to save SEO title.", 614 err_post_title_missing: "Post title is missing.", 615 err_daily_limit_exceeded: 616 "Daily AI post limit reached. Upgrade to Pro or try tomorrow.", 617 618 // Success 619 success_seo_title_generated: 620 "SEO title generated and saved successfully!", 621 }; 622 623 return wp.i18n.__(messages[code] || "Unknown error occurred.", "aicontify"); 624 } 625 626 function getPostTitle() { 627 // 1️⃣ Gutenberg 628 if (wp?.data?.select("core/editor")) { 629 const gutenbergTitle = wp.data 630 .select("core/editor") 631 .getEditedPostAttribute("title") 632 ?.trim(); 633 if (gutenbergTitle) { 634 return gutenbergTitle; 635 } 636 } 637 638 // 2️⃣ Classic Editor 639 const classicTitleInput = document.getElementById("title"); 640 if (classicTitleInput && classicTitleInput.value?.trim()) { 641 return classicTitleInput.value.trim(); 642 } 643 644 const metaInput = document.querySelector('input[name="post_title"]'); 645 if (metaInput && metaInput.value?.trim()) { 646 return metaInput.value.trim(); 647 } 648 649 return null; 650 } 651 652 function toggleSeoTitleButton() { 653 const keyword = keywordInput?.value?.trim(); 654 if (keyword && keyword.length > 0) { 655 seoTitleBtn.disabled = false; 656 seoTitleBtn.style.opacity = "1"; 657 seoTitleBtn.style.cursor = "pointer"; 658 } else { 659 seoTitleBtn.disabled = true; 660 seoTitleBtn.style.opacity = "0.5"; 661 seoTitleBtn.style.cursor = "not-allowed"; 662 } 663 } 664 665 if (seoTitleBtn && keywordInput) { 666 toggleSeoTitleButton(); 303 if (seoTitleBtn && keywordInput && seoTitleResultBox && seoTitleLoadingBox) { 304 function toggleSeoTitleButton() { 305 const hasKeyword = keywordInput.value.trim().length > 0; 306 seoTitleBtn.disabled = !hasKeyword; 307 seoTitleBtn.style.opacity = hasKeyword ? "1" : "0.5"; 308 seoTitleBtn.style.cursor = hasKeyword ? "pointer" : "not-allowed"; 309 } 310 667 311 keywordInput.addEventListener("input", toggleSeoTitleButton); 668 312 keywordInput.addEventListener("paste", () => 669 setTimeout(toggleSeoTitleButton, 0)313 setTimeout(toggleSeoTitleButton, 100) 670 314 ); 671 } 672 673 if (seoTitleBtn) { 674 seoTitleBtn.addEventListener("click", async function () { 675 const keyword = keywordInput?.value?.trim(); 315 toggleSeoTitleButton(); 316 317 function getSeoTitleMessage(key) { 318 const translations = { 319 generating: "Generating SEO title, please wait...", 320 success_generated: "SEO title generated and saved successfully!", 321 page_refreshing: "Page is refreshing to display changes...", 322 err_keyword_empty: 323 "Please enter a keyword before generating SEO title.", 324 err_server_error: "Server error occurred while generating SEO title.", 325 err_save_failed: "Failed to save SEO title to Yoast.", 326 err_yoast_missing: "Yoast SEO plugin is not active.", 327 err_daily_limit_exceeded: 328 "Daily AI generation limit reached. Upgrade or try tomorrow.", 329 }; 330 return wp.i18n.__(translations[key] || key, "aicontify"); 331 } 332 333 seoTitleBtn.addEventListener("click", async function (e) { 334 e.preventDefault(); 335 336 const postId = seoTitleBtn.getAttribute("data-postid"); 337 const keyword = keywordInput.value.trim(); 338 339 seoTitleResultBox.innerHTML = ""; 340 seoTitleLoadingBox.style.display = "block"; 341 seoTitleResultBox.innerHTML = `<span style='color: #0073aa;'>${getSeoTitleMessage( 342 "generating" 343 )}</span>`; 344 676 345 if (!keyword) { 677 346 seoTitleResultBox.innerHTML = `<span class='error'>${getSeoTitleMessage( 678 347 "err_keyword_empty" 679 348 )}</span>`; 349 seoTitleLoadingBox.style.display = "none"; 680 350 return; 681 351 } 682 352 683 const postId = seoTitleBtn.dataset.postid;684 if (!postId) {685 seoTitleResultBox.innerHTML = `<span class='error'>${getSeoTitleMessage(686 "err_post_id_invalid"687 )}</span>`;688 return;689 }690 691 if (692 !window.aicontApiSettings ||693 !aicontApiSettings.root ||694 !aicontApiSettings.nonce695 ) {696 seoTitleResultBox.innerHTML = `<span class='error'>${getSeoTitleMessage(697 "err_wp_settings_missing"698 )}</span>`;699 return;700 }701 702 seoTitleBtn.disabled = true;703 seoTitleBtn.textContent = wp.i18n.__(704 "Generating SEO title...",705 "aicontify"706 );707 seoTitleResultBox.innerHTML = "";708 seoTitleLoadingBox.style.display = "block";709 710 353 try { 711 const postTitle = getPostTitle(); 712 if (!postTitle) { 713 throw new Error("err_post_title_missing"); 714 } 715 716 const singlePostPrompt = 717 document 718 .getElementById("aicont_singlepost_seo_title_prompt_custom") 719 ?.value?.trim() || ""; 720 const globalPrompt = aicontApiSettings.seo_title_prompt_custom || ""; 721 722 const customPrompt = singlePostPrompt || globalPrompt || ""; 723 724 const requestData = { 725 keyword, 726 post_id: parseInt(postId), 727 post_title: postTitle, 728 api_key: aicontApiSettings.api_key || "", 729 site_language: aicontApiSettings.site_language, 730 site_title: aicontApiSettings.site_title, 731 prompt: customPrompt, 732 }; 733 734 const response = await fetch( 735 aicontApiSettings.root + "aicont/v1/generate_seo_titlee", 736 { 737 method: "POST", 738 headers: { 739 "Content-Type": "application/json", 740 "X-WP-Nonce": aicontApiSettings.nonce, 741 }, 742 body: JSON.stringify(requestData), 743 } 354 const customPromptEl = document.getElementById( 355 "aicont_singlepost_seo_title_prompt_custom" 744 356 ); 357 const customPrompt = customPromptEl ? customPromptEl.value.trim() : ""; 358 359 const formData = new URLSearchParams(); 360 formData.append("action", "aicont_generate_and_save_seo_title"); 361 formData.append("nonce", aicontify_ajax.nonce); 362 formData.append("post_id", postId); 363 formData.append("keyword", keyword); 364 formData.append("custom_prompt", customPrompt); 365 366 const response = await fetch(aicontify_ajax.ajax_url, { 367 method: "POST", 368 credentials: "same-origin", 369 headers: { "Content-Type": "application/x-www-form-urlencoded" }, 370 body: formData, 371 }); 745 372 746 373 const data = await response.json(); 747 374 748 if (!response.ok ) {749 const code = data. error_code || "err_server_error";375 if (!response.ok || !data.success) { 376 const code = data.data?.code || "err_server_error"; 750 377 throw new Error(code); 751 378 } 752 379 753 if (data.success && data.seo_title) { 754 seoTitleResultBox.innerHTML = ` 380 const title = data.seo_title 381 ? `: <strong>${data.seo_title}</strong>` 382 : ""; 383 seoTitleResultBox.innerHTML = ` 755 384 <span class='success'>${getSeoTitleMessage( 756 "success_seo_title_generated" 757 )}: <strong>${data.seo_title}</strong></span><br> 758 <span style='color: #0073aa;'>${wp.i18n.__( 759 "Page is refreshing to display changes...", 760 "aicontify" 385 "success_generated" 386 )}${title}</span><br> 387 <span style='color: #0073aa;'>${getSeoTitleMessage( 388 "page_refreshing" 761 389 )}</span> 762 390 `; 763 setTimeout(() => location.reload(), 1500); 764 } else { 765 throw new Error(data.error_code || "err_invalid_response"); 766 } 767 } catch (err) { 768 const code = err.message.startsWith("err_") 769 ? err.message 770 : "err_server_error"; 391 392 setTimeout(() => location.reload(), 1500); 393 } catch (error) { 394 console.error("AiContify SEO Title Error:", error); 395 const code = error.message; 396 const msg = 397 { 398 err_keyword_empty: "err_keyword_empty", 399 err_save_failed: "err_save_failed", 400 err_yoast_missing: "err_yoast_missing", 401 err_daily_limit_exceeded: "err_daily_limit_exceeded", 402 }[code] || "err_server_error"; 403 771 404 seoTitleResultBox.innerHTML = `<span class='error'>${getSeoTitleMessage( 772 code405 msg 773 406 )}</span>`; 774 407 } finally { 775 toggleSeoTitleButton();776 seoTitleBtn.textContent = wp.i18n.__("Generate SEO Title", "aicontify");777 408 seoTitleLoadingBox.style.display = "none"; 778 409 } … … 780 411 } 781 412 782 // --- Meta Description Generator ---413 // --- Meta Description Generator (PHP-Only + AJAX) --- 783 414 const metaDescBtn = document.getElementById( 784 415 "aicont-generate-meta-description-btn" … … 791 422 ); 792 423 793 // --- Message Translator for Meta Description --- 794 function getMetaDescMessage(code) { 795 const messages = { 796 // Errors 797 err_keyword_empty: 798 "Please enter a keyword in the field above the tabs before generating meta description.", 799 err_post_id_invalid: "Invalid post ID.", 800 err_wp_settings_missing: 801 "WordPress API settings are missing or incomplete. Please check plugin configuration.", 802 err_connection_failed: 803 "Failed to connect to the server. Please check your internet.", 804 err_server_error: "Server error occurred.", 805 err_invalid_response: "Invalid response from server.", 806 err_meta_empty: "Generated meta description is empty.", 807 err_save_failed: "Failed to save meta description.", 808 err_post_title_missing: "Post title is missing.", 809 err_daily_limit_exceeded: 810 "Daily AI post limit reached. Upgrade to Pro or try tomorrow.", 811 812 // Success 813 success_meta_generated: 814 "Meta description generated and saved successfully!", 815 }; 816 817 return wp.i18n.__(messages[code] || "Unknown error occurred.", "aicontify"); 818 } 819 820 function getPostTitle() { 821 if (wp?.data?.select("core/editor")) { 822 const gutenbergTitle = wp.data 823 .select("core/editor") 824 .getEditedPostAttribute("title") 825 ?.trim(); 826 if (gutenbergTitle) { 827 return gutenbergTitle; 828 } 829 } 830 831 const classicTitleInput = document.getElementById("title"); 832 if (classicTitleInput && classicTitleInput.value?.trim()) { 833 return classicTitleInput.value.trim(); 834 } 835 836 const metaInput = document.querySelector('input[name="post_title"]'); 837 if (metaInput && metaInput.value?.trim()) { 838 return metaInput.value.trim(); 839 } 840 841 return null; 842 } 843 844 function toggleMetaDescButton() { 845 const keyword = keywordInput?.value?.trim(); 846 if (keyword && keyword.length > 0) { 847 metaDescBtn.disabled = false; 848 metaDescBtn.style.opacity = "1"; 849 metaDescBtn.style.cursor = "pointer"; 850 } else { 851 metaDescBtn.disabled = true; 852 metaDescBtn.style.opacity = "0.5"; 853 metaDescBtn.style.cursor = "not-allowed"; 854 } 855 } 856 857 if (metaDescBtn && keywordInput) { 858 toggleMetaDescButton(); 424 if (metaDescBtn && keywordInput && metaDescResultBox && metaDescLoadingBox) { 425 function toggleMetaDescButton() { 426 const hasKeyword = keywordInput.value.trim().length > 0; 427 metaDescBtn.disabled = !hasKeyword; 428 metaDescBtn.style.opacity = hasKeyword ? "1" : "0.5"; 429 metaDescBtn.style.cursor = hasKeyword ? "pointer" : "not-allowed"; 430 } 431 859 432 keywordInput.addEventListener("input", toggleMetaDescButton); 860 433 keywordInput.addEventListener("paste", () => 861 setTimeout(toggleMetaDescButton, 0)434 setTimeout(toggleMetaDescButton, 100) 862 435 ); 863 } 864 865 if (metaDescBtn) { 866 metaDescBtn.addEventListener("click", async function () { 867 const keyword = keywordInput?.value?.trim(); 436 toggleMetaDescButton(); 437 438 function getMetaDescMessage(key) { 439 const translations = { 440 generating: "Generating meta description, please wait...", 441 success_generated: "Meta description generated and saved successfully!", 442 page_refreshing: "Page is refreshing to display changes...", 443 err_keyword_empty: 444 "Please enter a keyword before generating meta description.", 445 err_server_error: 446 "Server error occurred while generating meta description.", 447 err_save_failed: "Failed to save meta description to Yoast.", 448 err_yoast_missing: "Yoast SEO plugin is not active.", 449 err_daily_limit_exceeded: 450 "Daily AI generation limit reached. Upgrade or try tomorrow.", 451 }; 452 return wp.i18n.__(translations[key] || key, "aicontify"); 453 } 454 455 metaDescBtn.addEventListener("click", async function (e) { 456 e.preventDefault(); 457 458 const postId = metaDescBtn.getAttribute("data-postid"); 459 const keyword = keywordInput.value.trim(); 460 461 metaDescResultBox.innerHTML = ""; 462 metaDescLoadingBox.style.display = "block"; 463 metaDescResultBox.innerHTML = `<span style='color: #0073aa;'>${getMetaDescMessage( 464 "generating" 465 )}</span>`; 466 868 467 if (!keyword) { 869 468 metaDescResultBox.innerHTML = `<span class='error'>${getMetaDescMessage( 870 469 "err_keyword_empty" 871 470 )}</span>`; 471 metaDescLoadingBox.style.display = "none"; 872 472 return; 873 473 } 874 474 875 const postId = metaDescBtn.dataset.postid;876 if (!postId) {877 metaDescResultBox.innerHTML = `<span class='error'>${getMetaDescMessage(878 "err_post_id_invalid"879 )}</span>`;880 return;881 }882 883 if (884 !window.aicontApiSettings ||885 !aicontApiSettings.root ||886 !aicontApiSettings.nonce887 ) {888 metaDescResultBox.innerHTML = `<span class='error'>${getMetaDescMessage(889 "err_wp_settings_missing"890 )}</span>`;891 return;892 }893 894 metaDescBtn.disabled = true;895 metaDescBtn.textContent = wp.i18n.__(896 "Generating meta description...",897 "aicontify"898 );899 metaDescResultBox.innerHTML = "";900 metaDescLoadingBox.style.display = "block";901 902 475 try { 903 const postTitle = getPostTitle(); 904 if (!postTitle) throw new Error("err_post_title_missing"); 905 906 const singlePostPrompt = 907 document 908 .getElementById("aicont_singlepost_seo_description_prompt_custom") 909 ?.value?.trim() || ""; 910 const globalPrompt = aicontApiSettings.seo_meta_prompt_custom || ""; 911 912 const customPrompt = singlePostPrompt || globalPrompt || ""; 913 914 const requestData = { 915 keyword, 916 post_id: parseInt(postId), 917 post_title: postTitle, 918 api_key: aicontApiSettings.api_key || "", 919 site_language: aicontApiSettings.site_language, 920 site_title: aicontApiSettings.site_title, 921 prompt: customPrompt, 922 }; 923 924 const response = await fetch( 925 aicontApiSettings.root + "aicont/v1/generate_meta_descriptionn", 926 { 927 method: "POST", 928 headers: { 929 "Content-Type": "application/json", 930 "X-WP-Nonce": aicontApiSettings.nonce, 931 }, 932 body: JSON.stringify(requestData), 933 } 476 const customPromptEl = document.getElementById( 477 "aicont_singlepost_seo_description_prompt_custom" 934 478 ); 479 const customPrompt = customPromptEl ? customPromptEl.value.trim() : ""; 480 481 const formData = new URLSearchParams(); 482 formData.append("action", "aicont_generate_and_save_meta_description"); 483 formData.append("nonce", aicontify_ajax.nonce); 484 formData.append("post_id", postId); 485 formData.append("keyword", keyword); 486 formData.append("custom_prompt", customPrompt); 487 488 const response = await fetch(aicontify_ajax.ajax_url, { 489 method: "POST", 490 credentials: "same-origin", 491 headers: { "Content-Type": "application/x-www-form-urlencoded" }, 492 body: formData, 493 }); 935 494 936 495 const data = await response.json(); 937 496 938 if (!response.ok ) {939 const code = data. error_code || "err_server_error";497 if (!response.ok || !data.success) { 498 const code = data.data?.code || "err_server_error"; 940 499 throw new Error(code); 941 500 } 942 501 943 if (data.success && data.meta_description) { 944 metaDescResultBox.innerHTML = ` 502 const desc = data.meta_description 503 ? `: <strong>${data.meta_description}</strong>` 504 : ""; 505 metaDescResultBox.innerHTML = ` 945 506 <span class='success'>${getMetaDescMessage( 946 "success_meta_generated" 947 )}: <strong>${data.meta_description}</strong></span><br> 948 <span style='color: #0073aa;'>${wp.i18n.__( 949 "Page is refreshing to display changes...", 950 "aicontify" 507 "success_generated" 508 )}${desc}</span><br> 509 <span style='color: #0073aa;'>${getMetaDescMessage( 510 "page_refreshing" 951 511 )}</span> 952 512 `; 953 setTimeout(() => location.reload(), 1500); 954 } else { 955 throw new Error(data.error_code || "err_invalid_response"); 956 } 957 } catch (err) { 958 const code = err.message.startsWith("err_") 959 ? err.message 960 : "err_server_error"; 513 514 setTimeout(() => location.reload(), 1500); 515 } catch (error) { 516 console.error("AiContify Meta Description Error:", error); 517 const code = error.message; 518 const msg = 519 { 520 err_keyword_empty: "err_keyword_empty", 521 err_save_failed: "err_save_failed", 522 err_yoast_missing: "err_yoast_missing", 523 err_daily_limit_exceeded: "err_daily_limit_exceeded", 524 }[code] || "err_server_error"; 525 961 526 metaDescResultBox.innerHTML = `<span class='error'>${getMetaDescMessage( 962 code527 msg 963 528 )}</span>`; 964 529 } finally { 965 toggleMetaDescButton();966 metaDescBtn.textContent = wp.i18n.__(967 "Generate Meta Description",968 "aicontify"969 );970 530 metaDescLoadingBox.style.display = "none"; 971 531 } -
aicontify/trunk/readme.txt
r3397131 r3398370 4 4 Requires at least: 5.0 5 5 Tested up to: 6.8 6 Stable tag: 4.0.26 Stable tag: 5.0.0 7 7 Requires PHP: 7.4 8 8 License: GPLv2 or later … … 121 121 == Changelog == 122 122 123 = 5.0.0 = November 19, 2025 124 Rest API optimization 125 123 126 = 4.0.0 = November 17, 2025 124 127 Added a custom prompt field inside post editor for more flexible content generation -
aicontify/trunk/seoDescription.php
r3397005 r3398370 2 2 if (!defined('ABSPATH')) exit; 3 3 4 // -------------------- REST API: Meta Description Generator (Client) -------------------- 5 add_action('rest_api_init', function() { 6 register_rest_route('aicont/v1', '/generate_meta_descriptionn', [ 7 'methods' => 'POST', 8 'callback' => 'aicont_generate_meta_description_api_callback', 9 'permission_callback' => function() { 10 return current_user_can('edit_posts'); 11 }, 12 'args' => [ 13 'keyword' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 14 'post_id' => ['required' => true, 'type' => 'integer', 'sanitize_callback' => 'absint'], 15 'post_title' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 16 'api_key' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 17 'site_language' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 18 'site_title' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 19 'prompt' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_textarea_field'], 20 ] 21 ]); 22 }); 4 add_action('wp_ajax_aicont_generate_and_save_meta_description', 'aicont_generate_and_save_meta_description_handler'); 23 5 24 function aicont_generate_ meta_description_api_callback($request) {25 global $aicont_api_key, $aicont_site_language, $aicont_site_title;6 function aicont_generate_and_save_meta_description_handler() { 7 check_ajax_referer('aicont_nonce', 'nonce'); 26 8 27 $keyword = $request->get_param('keyword'); 28 $post_id = $request->get_param('post_id'); 29 $post_title = $request->get_param('post_title'); 30 $api_key = $request->get_param('api_key') ?: $aicont_api_key; 31 $site_language = $request->get_param('site_language') ?: $aicont_site_language; 32 $site_title = $request->get_param('site_title') ?: $aicont_site_title; 33 $prompt = $request->get_param('prompt'); 9 $post_id = absint($_POST['post_id'] ?? 0); 10 $keyword = sanitize_text_field($_POST['keyword'] ?? ''); 11 $custom_prompt = sanitize_textarea_field($_POST['custom_prompt'] ?? ''); 34 12 35 if (empty($keyword)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_keyword_empty'], 400); 36 if (empty($post_id)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_id_invalid'], 400); 37 if (empty($post_title)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_title_missing'], 400); 38 39 $post = get_post($post_id); 40 if (!$post) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_not_found'], 404); 41 42 // Priority: custom_prompt from request > post meta > global option 43 if (empty($prompt)) { 44 $prompt = get_post_meta($post_id, 'aicont_singlepost_seo_description_prompt_custom', true); 13 if (!$post_id || !get_post($post_id)) { 14 wp_send_json_error(['code' => 'err_post_id_invalid']); 45 15 } 46 if (empty($ prompt)) {47 $prompt = get_option('aicont_plugin_seo_description_prompt_custom', '');16 if (empty($keyword)) { 17 wp_send_json_error(['code' => 'err_keyword_empty']); 48 18 } 49 19 20 $post_title = get_the_title($post_id); 21 if (empty($post_title)) { 22 wp_send_json_error(['code' => 'err_post_title_missing']); 23 } 24 25 if (empty($custom_prompt)) { 26 $custom_prompt = get_post_meta($post_id, 'aicont_singlepost_seo_description_prompt_custom', true); 27 } 28 if (empty($custom_prompt)) { 29 $custom_prompt = get_option('aicont_plugin_seo_meta_prompt_custom', ''); 30 } 31 32 $active_license = get_option('aicont_active_license', ''); 33 34 $result = aicont_call_webtinus_meta_description([ 35 'keyword' => $keyword, 36 'post_id' => $post_id, 37 'post_title' => $post_title, 38 'prompt' => $custom_prompt, 39 'api_key' => $active_license 40 ]); 41 42 if (is_wp_error($result) || empty($result['meta_description'])) { 43 wp_send_json_error(['code' => $result['error_code'] ?? 'err_meta_empty']); 44 } 45 46 $meta_description = sanitize_textarea_field(trim($result['meta_description'])); 47 48 if (!class_exists('WPSEO_Meta')) { 49 wp_send_json_error(['code' => 'err_yoast_missing']); 50 } 51 52 $saved = update_post_meta($post_id, '_yoast_wpseo_metadesc', $meta_description); 53 if ($saved === false) { 54 wp_send_json_error(['code' => 'err_save_failed']); 55 } 56 57 wp_update_post([ 58 'ID' => $post_id, 59 'post_modified' => current_time('mysql'), 60 'post_modified_gmt' => current_time('mysql', 1) 61 ]); 62 63 wp_send_json_success([ 64 'message' => 'success_meta_generated', 65 'meta_description' => $meta_description 66 ]); 67 } 68 69 function aicont_call_webtinus_meta_description($args) { 50 70 $client_site_url = home_url('', 'https'); 51 if (empty($client_site_url)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_site_url'], 500);71 $server_url = 'https://webtinus.com/wp-json/aicont/v1/generate_meta_description1'; 52 72 53 $server_url = 'https://webtinus.com/wp-json/aicont/v1/generate_meta_descriptionn';54 73 $body = wp_json_encode([ 55 'keyword' => $keyword,56 'post_id' => $post_id,57 'post_title' => $post_title,58 'prompt' => $prompt,59 ' client_site_url'=> $client_site_url,60 ' site_title' => $site_title,61 'site_ language' => $site_language,62 ' api_key' => $api_key74 'keyword' => $args['keyword'], 75 'post_id' => $args['post_id'], 76 'post_title' => $args['post_title'], 77 'prompt' => $args['prompt'], 78 'api_key' => $args['api_key'] ?? '', 79 'client_site_url' => $client_site_url, 80 'site_title' => get_bloginfo('name'), 81 'site_language' => get_locale() 63 82 ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); 64 83 … … 66 85 'headers' => ['Content-Type' => 'application/json', 'X-Client-Site' => $client_site_url], 67 86 'body' => $body, 68 'timeout' => 60, 69 'sslverify' => true 87 'timeout' => 90 70 88 ]); 71 89 72 90 if (is_wp_error($response)) { 73 return new WP_REST_Response(['success' => false, 'error_code' => 'err_connection_failed'], 500);91 return ['error_code' => 'err_connection_failed']; 74 92 } 75 93 76 $status_code = wp_remote_retrieve_response_code($response); 77 $body = wp_remote_retrieve_body($response); 78 $data = json_decode($body, true); 94 $data = json_decode(wp_remote_retrieve_body($response), true); 79 95 80 if ( $status_code !== 200 || !isset($data['success'])) {81 return new WP_REST_Response(['success' => false, 'error_code' => $data['error_code'] ?? 'err_server_error'], $status_code);96 if (empty($data['success']) || empty($data['meta_description'])) { 97 return ['error_code' => $data['error_code'] ?? 'err_meta_empty']; 82 98 } 83 99 84 if (!$data['success'] || empty($data['meta_description'])) { 85 return new WP_REST_Response(['success' => false, 'error_code' => $data['error_code'] ?? 'err_meta_empty'], 500); 86 } 87 88 $meta_description = sanitize_textarea_field($data['meta_description']); 89 90 if (!class_exists('WPSEO_Meta')) { 91 return new WP_REST_Response(['success' => false, 'error_code' => 'err_yoast_missing'], 400); 92 } 93 94 $meta_updated = update_post_meta($post_id, '_yoast_wpseo_metadesc', $meta_description); 95 if ($meta_updated === false) { 96 return new WP_REST_Response(['success' => false, 'error_code' => 'err_save_failed'], 500); 97 } 98 99 $post_data = [ 100 'ID' => $post_id, 101 'post_modified' => current_time('mysql'), 102 'post_modified_gmt' => current_time('mysql', 1), 103 'post_status' => $post->post_status 104 ]; 105 106 $post_updated = wp_update_post($post_data, true); 107 if (is_wp_error($post_updated)) { 108 return new WP_REST_Response(['success' => false, 'error_code' => 'err_save_failed'], 500); 109 } 110 111 return new WP_REST_Response([ 112 'success' => true, 113 'message_code' => 'success_meta_generated', 114 'meta_description' => $meta_description 115 ], 200); 100 return ['meta_description' => trim($data['meta_description'])]; 116 101 } -
aicontify/trunk/seoTitle.php
r3397005 r3398370 2 2 if (!defined('ABSPATH')) exit; 3 3 4 // -------------------- REST API: SEO Title Generator (Client) -------------------- 5 add_action('rest_api_init', function() { 6 register_rest_route('aicont/v1', '/generate_seo_titlee', [ 7 'methods' => 'POST', 8 'callback' => 'aicont_generate_seo_title_api_callback', 9 'permission_callback' => function() { 10 return current_user_can('edit_posts'); 11 }, 12 'args' => [ 13 'keyword' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 14 'post_id' => ['required' => true, 'type' => 'integer', 'sanitize_callback' => 'absint'], 15 'post_title' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 16 'model_id' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 17 'api_key' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 18 'site_language' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 19 'site_title' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 20 'prompt' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_textarea_field'], 21 ] 22 ]); 23 }); 4 add_action('wp_ajax_aicont_generate_and_save_seo_title', 'aicont_generate_and_save_seo_title_handler'); 24 5 25 function aicont_generate_ seo_title_api_callback($request) {26 global $aicont_model_id, $aicont_api_key, $aicont_site_language, $aicont_site_title;6 function aicont_generate_and_save_seo_title_handler() { 7 check_ajax_referer('aicont_nonce', 'nonce'); 27 8 28 $keyword = $request->get_param('keyword'); 29 $post_id = $request->get_param('post_id'); 30 $post_title = $request->get_param('post_title'); 31 $model_id = $request->get_param('model_id') ?: $aicont_model_id; 32 $api_key = $request->get_param('api_key') ?: $aicont_api_key; 33 $site_language = $request->get_param('site_language') ?: $aicont_site_language; 34 $site_title = $request->get_param('site_title') ?: $aicont_site_title; 35 $prompt = $request->get_param('prompt'); 9 $post_id = absint($_POST['post_id'] ?? 0); 10 $keyword = sanitize_text_field($_POST['keyword'] ?? ''); 11 $custom_prompt = sanitize_textarea_field($_POST['custom_prompt'] ?? ''); 36 12 37 // Validation 38 if (empty($keyword)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_keyword_empty'], 400); 39 if (empty($post_id)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_id_invalid'], 400); 40 if (empty($post_title)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_title_missing'], 400); 41 42 $post = get_post($post_id); 43 if (!$post) return new WP_REST_Response(['success' => false, 'error_code' => 'err_post_not_found'], 404); 44 45 // Priority: custom_prompt from request > post meta > global option 46 if (empty($prompt)) { 47 $prompt = get_post_meta($post_id, 'aicont_singlepost_seo_title_prompt_custom', true); 13 if (!$post_id || !get_post($post_id)) { 14 wp_send_json_error(['code' => 'err_post_id_invalid']); 48 15 } 49 if (empty($ prompt)) {50 $prompt = get_option('aicont_plugin_seo_title_prompt_custom', '');16 if (empty($keyword)) { 17 wp_send_json_error(['code' => 'err_keyword_empty']); 51 18 } 52 19 20 $post_title = get_the_title($post_id); 21 if (empty($post_title)) { 22 wp_send_json_error(['code' => 'err_post_title_missing']); 23 } 24 25 if (empty($custom_prompt)) { 26 $custom_prompt = get_post_meta($post_id, 'aicont_singlepost_seo_title_prompt_custom', true); 27 } 28 if (empty($custom_prompt)) { 29 $custom_prompt = get_option('aicont_plugin_seo_title_prompt_custom', ''); 30 } 31 32 $active_license = get_option('aicont_active_license', ''); 33 34 $result = aicont_call_webtinus_seo_title([ 35 'keyword' => $keyword, 36 'post_id' => $post_id, 37 'post_title' => $post_title, 38 'prompt' => $custom_prompt, 39 'api_key' => $active_license 40 ]); 41 42 if (is_wp_error($result) || empty($result['seo_title'])) { 43 wp_send_json_error(['code' => $result['error_code'] ?? 'err_title_empty']); 44 } 45 46 $seo_title = wp_strip_all_tags(sanitize_text_field($result['seo_title'])); 47 48 if (!class_exists('WPSEO_Meta')) { 49 wp_send_json_error(['code' => 'err_yoast_missing']); 50 } 51 52 $saved = update_post_meta($post_id, '_yoast_wpseo_title', $seo_title); 53 if ($saved === false) { 54 wp_send_json_error(['code' => 'err_save_failed']); 55 } 56 57 wp_update_post([ 58 'ID' => $post_id, 59 'post_modified' => current_time('mysql'), 60 'post_modified_gmt' => current_time('mysql', 1) 61 ]); 62 63 wp_send_json_success([ 64 'message' => 'success_seo_title_generated', 65 'seo_title' => $seo_title 66 ]); 67 } 68 69 function aicont_call_webtinus_seo_title($args) { 53 70 $client_site_url = home_url('', 'https'); 54 if (empty($client_site_url)) return new WP_REST_Response(['success' => false, 'error_code' => 'err_site_url'], 500);71 $server_url = 'https://webtinus.com/wp-json/aicont/v1/generate_seo_title1'; 55 72 56 // Send request to central server57 $server_url = 'https://webtinus.com/wp-json/aicont/v1/generate_seo_titlee';58 73 $body = wp_json_encode([ 59 'keyword' => $keyword, 60 'post_id' => $post_id, 61 'post_title' => $post_title, 62 'prompt' => $prompt, 74 'keyword' => $args['keyword'], 75 'post_id' => $args['post_id'], 76 'post_title' => $args['post_title'], 77 'prompt' => $args['prompt'], 78 'api_key' => $args['api_key'] ?? '', 63 79 'client_site_url' => $client_site_url, 64 'site_title' => $site_title, 65 'site_language' => $site_language, 66 'model_id' => $model_id, 67 'api_key' => $api_key 80 'site_title' => get_bloginfo('name'), 81 'site_language' => get_locale() 68 82 ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); 69 83 70 84 $response = wp_remote_post($server_url, [ 71 'headers' => [ 72 'Content-Type' => 'application/json', 73 'X-Client-Site' => $client_site_url 74 ], 85 'headers' => ['Content-Type' => 'application/json', 'X-Client-Site' => $client_site_url], 75 86 'body' => $body, 76 'timeout' => 60, 77 'sslverify' => true 87 'timeout' => 90 78 88 ]); 79 89 80 90 if (is_wp_error($response)) { 81 return new WP_REST_Response(['success' => false, 'error_code' => 'err_connection_failed'], 500);91 return ['error_code' => 'err_connection_failed']; 82 92 } 83 93 84 $status_code = wp_remote_retrieve_response_code($response); 85 $body = wp_remote_retrieve_body($response); 86 $data = json_decode($body, true); 94 $data = json_decode(wp_remote_retrieve_body($response), true); 87 95 88 if ( $status_code !== 200 || !isset($data['success'])) {89 return new WP_REST_Response(['success' => false, 'error_code' => $data['error_code'] ?? 'err_server_error'], $status_code);96 if (empty($data['success']) || empty($data['seo_title'])) { 97 return ['error_code' => $data['error_code'] ?? 'err_title_empty']; 90 98 } 91 99 92 if (!$data['success'] || empty($data['seo_title'])) { 93 return new WP_REST_Response(['success' => false, 'error_code' => $data['error_code'] ?? 'err_title_empty'], 500); 94 } 95 96 $seo_title = wp_strip_all_tags(sanitize_text_field($data['seo_title'])); 97 98 if (!class_exists('WPSEO_Meta')) { 99 return new WP_REST_Response(['success' => false, 'error_code' => 'err_yoast_missing'], 400); 100 } 101 102 $meta_updated = update_post_meta($post_id, '_yoast_wpseo_title', $seo_title); 103 if ($meta_updated === false) { 104 return new WP_REST_Response(['success' => false, 'error_code' => 'err_save_failed'], 500); 105 } 106 107 $post_data = [ 108 'ID' => $post_id, 109 'post_modified' => current_time('mysql'), 110 'post_modified_gmt' => current_time('mysql', 1), 111 'post_status' => $post->post_status 112 ]; 113 114 $post_updated = wp_update_post($post_data, true); 115 if (is_wp_error($post_updated)) { 116 return new WP_REST_Response(['success' => false, 'error_code' => 'err_save_failed'], 500); 117 } 118 119 return new WP_REST_Response([ 120 'success' => true, 121 'message_code'=> 'success_seo_title_generated', 122 'seo_title' => $seo_title 123 ], 200); 100 return ['seo_title' => trim($data['seo_title'])]; 124 101 }
Note: See TracChangeset
for help on using the changeset viewer.