esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), ) ); } $post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0; $post = $post_id ? get_post( $post_id ) : null; if ( ! $post || 'aicw_post' !== $post->post_type ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid post. Please try again.', 'ai-content-writer' ), ) ); } return $post; } /** * Approve a completed temp post: promote it to its final post type/status now. * * @since 2.3.0 * @return void */ public static function handle_approve_temp_post() { $post = self::get_temp_post_from_request(); $campaign_id = absint( get_post_meta( $post->ID, '_aicw_campaign_id', true ) ); if ( ! $campaign_id || ! aicw_get_campaign( $campaign_id ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Campaign not found. Please try again.', 'ai-content-writer' ), ) ); } if ( ! empty( aicw_temp_post_needs_update( $post, $campaign_id ) ) ) { wp_send_json_error( array( 'message' => esc_html__( 'This post is not finished yet — wait for content and thumbnail generation to complete.', 'ai-content-writer' ), ) ); } Cron::publish_posts( $post->ID, $campaign_id ); wp_send_json_success( array( 'message' => esc_html__( 'Post approved and published.', 'ai-content-writer' ), ) ); } /** * Regenerate a temp post: clear its content so the pipeline rewrites it. * * @since 2.3.0 * @return void */ public static function handle_regenerate_temp_post() { $post = self::get_temp_post_from_request(); $campaign_id = absint( get_post_meta( $post->ID, '_aicw_campaign_id', true ) ); if ( ! $campaign_id || ! aicw_get_campaign( $campaign_id ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Campaign not found. Please try again.', 'ai-content-writer' ), ) ); } // Clear the generated data so the next pipeline run rebuilds it. wp_update_post( array( 'ID' => $post->ID, 'post_content' => '', 'post_excerpt' => '', ) ); delete_post_meta( $post->ID, '_aicw_generate_content_attempts' ); delete_post_meta( $post->ID, '_aicw_seo' ); delete_post_meta( $post->ID, '_aicw_suggested_categories' ); delete_post_meta( $post->ID, '_aicw_suggested_tags' ); // Queue an immediate background run to regenerate. Cron::run_now( $campaign_id ); wp_send_json_success( array( 'message' => esc_html__( 'Regeneration queued — the content will be rewritten in the background.', 'ai-content-writer' ), ) ); } /** * Save general settings. * * @since 1.0.0 * @return void */ public static function save_general_settings() { check_admin_referer( 'aicw_save_general_settings' ); $referer = wp_get_referer(); if ( ! current_user_can( 'manage_options' ) ) { ai_content_writer()->flash_notice( esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } /* * Allow third-party plugins to modify the settings before saving. * * @param array $settings The settings array. * * @since 1.7.0 */ do_action( 'aicw_before_save_general_settings' ); $default_host = isset( $_POST['aicw_default_host'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_default_host'] ) ) : 'https://www.bing.com/'; $log_retention_period = isset( $_POST['aicw_log_retention_period'] ) ? absint( wp_unslash( $_POST['aicw_log_retention_period'] ) ) : ''; $output_meta_desc = isset( $_POST['aicw_output_meta_description'] ) ? 'yes' : 'no'; update_option( 'aicw_default_host', $default_host ); update_option( 'aicw_log_retention_period', $log_retention_period ); update_option( 'aicw_output_meta_description', $output_meta_desc ); ai_content_writer()->flash_notice( esc_html__( 'General settings saved successfully.', 'ai-content-writer' ), 'success' ); wp_safe_redirect( $referer ); exit(); } /** * Save API settings. * * @since 1.0.0 * @return void */ public static function save_api_settings() { check_admin_referer( 'aicw_save_api_settings' ); $referer = wp_get_referer(); if ( ! current_user_can( 'manage_options' ) ) { ai_content_writer()->flash_notice( esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } $api_model = isset( $_POST['aicw_api_model'] ) ? sanitize_key( wp_unslash( $_POST['aicw_api_model'] ) ) : ''; $gemini_api_key = isset( $_POST['aicw_gemini_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_gemini_api_key'] ) ) : ''; $gemini_ai_model = isset( $_POST['aicw_gemini_ai_model'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_gemini_ai_model'] ) ) : ''; $chatgpt_api_secret_key = isset( $_POST['aicw_chatgpt_api_secret_key'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_chatgpt_api_secret_key'] ) ) : ''; $chatgpt_ai_model = isset( $_POST['aicw_chatgpt_ai_model'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_chatgpt_ai_model'] ) ) : ''; $pexels_api_key = isset( $_POST['aicw_pexels_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_pexels_api_key'] ) ) : ''; $unsplash_api_key = isset( $_POST['aicw_unsplash_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_unsplash_api_key'] ) ) : ''; $pixabay_api_key = isset( $_POST['aicw_pixabay_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_pixabay_api_key'] ) ) : ''; $image_source = isset( $_POST['aicw_image_source'] ) ? sanitize_key( wp_unslash( $_POST['aicw_image_source'] ) ) : 'pexels'; // Keys are rendered masked (••••1234); an unchanged masked value means "keep the stored key". if ( $gemini_api_key === aicw_mask_api_key( get_option( 'aicw_gemini_api_key' ) ) ) { $gemini_api_key = ''; } if ( $chatgpt_api_secret_key === aicw_mask_api_key( get_option( 'aicw_chatgpt_api_secret_key' ) ) ) { $chatgpt_api_secret_key = ''; } if ( $pexels_api_key === aicw_mask_api_key( get_option( 'aicw_pexels_api_key' ) ) ) { $pexels_api_key = ''; } if ( $unsplash_api_key === aicw_mask_api_key( get_option( 'aicw_unsplash_api_key' ) ) ) { $unsplash_api_key = ''; } if ( $pixabay_api_key === aicw_mask_api_key( get_option( 'aicw_pixabay_api_key' ) ) ) { $pixabay_api_key = ''; } // Update common options. if ( array_key_exists( $api_model, aicw_get_ai_providers() ) ) { update_option( 'aicw_api_model', $api_model ); } update_option( 'aicw_gemini_ai_model', $gemini_ai_model ); update_option( 'aicw_chatgpt_ai_model', $chatgpt_ai_model ); update_option( 'aicw_image_source', array_key_exists( $image_source, aicw_get_image_sources() ) ? $image_source : 'pexels' ); if ( ! empty( $pexels_api_key ) ) { update_option( 'aicw_pexels_api_key', $pexels_api_key ); } if ( ! empty( $unsplash_api_key ) ) { update_option( 'aicw_unsplash_api_key', $unsplash_api_key ); } if ( ! empty( $pixabay_api_key ) ) { update_option( 'aicw_pixabay_api_key', $pixabay_api_key ); } // Check if the Gemini API key is valid or not. if ( ! empty( $gemini_api_key ) ) { // Create Gemini service instance. $service = new \AIContentWriter\Services\Gemini( $gemini_api_key ); // Call the service to get models. $models = $service->get_models(); if ( is_wp_error( $models ) ) { ai_content_writer()->flash_notice( esc_html( 'Gemini: ' . $models->get_error_message() ), 'error' ); update_option( 'aicw_gemini_api_key', '' ); update_option( 'aicw_gemini_models', array() ); } else { update_option( 'aicw_gemini_api_key', $gemini_api_key ); update_option( 'aicw_gemini_models', $models ); } } // Check if the ChatGPT API secret key is valid or not. if ( ! empty( $chatgpt_api_secret_key ) ) { // Create OpenAI service instance. $service = new \AIContentWriter\Services\OpenAI( $chatgpt_api_secret_key ); // Call the service to get models. $models = $service->get_models(); if ( is_wp_error( $models ) ) { ai_content_writer()->flash_notice( esc_html( 'OpenAI: ' . $models->get_error_message() ), 'error' ); update_option( 'aicw_chatgpt_api_secret_key', '' ); update_option( 'aicw_chatgpt_models', array() ); } else { update_option( 'aicw_chatgpt_api_secret_key', $chatgpt_api_secret_key ); update_option( 'aicw_chatgpt_models', $models ); } } /** * Fires while saving the API settings, after the built-in providers. * * Pro/add-on plugins hook here to persist their own provider keys. * The nonce and capability checks have already run. * * @since 2.4.0 */ do_action( 'aicw_save_api_settings' ); ai_content_writer()->flash_notice( esc_html__( 'API settings saved successfully.', 'ai-content-writer' ), 'success' ); wp_safe_redirect( $referer ); exit(); } /** * Save Image Settings. * * @since 2.2.2 * @return void */ public static function save_image_settings() { check_admin_referer( 'aicw_save_image_settings' ); $referer = wp_get_referer(); if ( ! current_user_can( 'manage_options' ) ) { ai_content_writer()->flash_notice( esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } $enabled_img_gen = isset( $_POST['aicw_enable_img_generation'] ) ? sanitize_text_field( wp_unslash( $_POST['aicw_enable_img_generation'] ) ) : ''; update_option( 'aicw_enable_img_generation', $enabled_img_gen ); ai_content_writer()->flash_notice( esc_html__( 'Image generation settings saved successfully.', 'ai-content-writer' ), 'success' ); wp_safe_redirect( $referer ); exit(); } /** * Add campaign. * * @since 1.0.0 * @return void */ public static function handle_add_campaign() { check_admin_referer( 'aicw_add_campaign' ); $referer = wp_get_referer(); if ( ! current_user_can( 'manage_options' ) ) { ai_content_writer()->flash_notice( esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } $type = isset( $_POST['campaign_type'] ) ? sanitize_text_field( wp_unslash( $_POST['campaign_type'] ) ) : 'articles'; $title = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : ''; $keywords = isset( $_POST['keywords'] ) ? wp_kses_post( wp_unslash( $_POST['keywords'] ) ) : ''; $status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : 'publish'; $rss_feed = isset( $_POST['rss_feed_link'] ) ? sanitize_text_field( wp_unslash( $_POST['rss_feed_link'] ) ) : ''; // Validate the input fields. if ( empty( $title ) ) { ai_content_writer()->flash_notice( esc_html__( 'Title is required. Please enter the campaign title.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $type ) ) { ai_content_writer()->flash_notice( esc_html__( 'Campaign type is required. Please select the campaign type.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $keywords ) && ! in_array( $type, array( 'rss-feed', 'autopilot' ), true ) ) { ai_content_writer()->flash_notice( esc_html__( 'Keywords are required. Please enter the keywords.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( 'rss-feed' === $type && empty( $rss_feed ) ) { ai_content_writer()->flash_notice( esc_html__( 'RSS feed link is required. Please enter the RSS feed link.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } // Create a new campaign. $campaign_id = wp_insert_post( array( 'post_title' => wp_strip_all_tags( $title ), 'post_content' => wp_kses_post( $keywords ), 'post_status' => $status, 'post_type' => 'aicw_campaign', ) ); if ( is_wp_error( $campaign_id ) ) { ai_content_writer()->flash_notice( $campaign_id->get_error_message(), 'error' ); wp_safe_redirect( $referer ); exit(); } // Update the campaign meta and schedule the cron event to run the campaign. $frequency = self::save_campaign_meta( $campaign_id, $type, $rss_feed ); $imported = self::import_campaign_titles( $campaign_id ); if ( 'publish' === $status ) { Cron::schedule_campaign( $campaign_id, $frequency ); } if ( $imported > 0 ) { ai_content_writer()->flash_notice( sprintf( /* translators: %d: Number of imported titles. */ esc_html__( '%d titles imported and queued for content generation.', 'ai-content-writer' ), $imported ), 'success' ); } ai_content_writer()->flash_notice( esc_html__( 'Campaign added successfully.', 'ai-content-writer' ), 'success' ); $referer = remove_query_arg( 'add', $referer ); wp_safe_redirect( add_query_arg( 'edit', $campaign_id, $referer ) ); exit(); } /** * Create pending temp posts from a pasted list of titles (one per line). * * Only applies to AI campaign types — scraped campaigns need a source link * per post. Duplicate titles (already existing on the site) are skipped. * * @param int $campaign_id The campaign ID. * * @since 2.3.0 * @return int Number of imported titles. */ private static function import_campaign_titles( $campaign_id ) { // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified by the calling handler. $raw = isset( $_POST['import_titles'] ) ? sanitize_textarea_field( wp_unslash( $_POST['import_titles'] ) ) : ''; // phpcs:enable WordPress.Security.NonceVerification.Missing if ( '' === trim( $raw ) ) { return 0; } $titles = array_unique( array_filter( array_map( 'trim', preg_split( '/\r?\n/', $raw ) ) ) ); $imported = 0; foreach ( $titles as $title ) { if ( aicw_get_post_by_title( $title ) ) { continue; } $post_id = wp_insert_post( array( 'post_type' => 'aicw_post', 'post_title' => wp_strip_all_tags( $title ), 'post_content' => '', 'post_status' => 'pending', 'post_author' => get_post_field( 'post_author', $campaign_id ), ) ); if ( is_wp_error( $post_id ) || ! $post_id ) { continue; } update_post_meta( $post_id, '_aicw_campaign_id', $campaign_id ); ++$imported; } return $imported; } /** * Persist the campaign meta shared by the add and edit handlers. * * Reads directly from $_POST — the callers are responsible for nonce and * capability checks before calling this. * * @param int $campaign_id The campaign ID. * @param string $type The (already sanitized) campaign type. * @param string $rss_feed The (already sanitized) RSS feed link. * * @since 2.3.0 * @return string The saved campaign frequency. */ private static function save_campaign_meta( $campaign_id, $type, $rss_feed ) { // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified by the calling handler. $host = isset( $_POST['campaign_host'] ) ? sanitize_text_field( wp_unslash( $_POST['campaign_host'] ) ) : 'https://bing.com'; $source = isset( $_POST['campaign_source'] ) ? sanitize_text_field( wp_unslash( $_POST['campaign_source'] ) ) : ''; $block_keywords = isset( $_POST['block_keywords'] ) ? sanitize_text_field( wp_unslash( $_POST['block_keywords'] ) ) : ''; $search_replaces = isset( $_POST['search_replaces'] ) ? map_deep( wp_unslash( $_POST['search_replaces'] ), 'sanitize_text_field' ) : array(); $html_cleaners = isset( $_POST['html_cleaners'] ) ? sanitize_text_field( wp_unslash( $_POST['html_cleaners'] ) ) : ''; $insert_content_position = isset( $_POST['insert_content_position'] ) ? sanitize_text_field( wp_unslash( $_POST['insert_content_position'] ) ) : 'none'; $insert_content = isset( $_POST['insert_content'] ) ? wp_kses_post( wp_unslash( $_POST['insert_content'] ) ) : ''; $target = isset( $_POST['target'] ) ? sanitize_text_field( wp_unslash( $_POST['target'] ) ) : ''; $frequency = isset( $_POST['frequency'] ) ? sanitize_text_field( wp_unslash( $_POST['frequency'] ) ) : 'hourly'; $generate_thumb = isset( $_POST['generate_thumbnail'] ) ? 'yes' : ''; $seo_enabled = isset( $_POST['seo_enabled'] ) ? 'yes' : 'no'; $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( wp_unslash( $_POST['post_type'] ) ) : 'post'; $completed_post_status = isset( $_POST['completed_post_status'] ) ? sanitize_text_field( wp_unslash( $_POST['completed_post_status'] ) ) : 'publish'; $language = isset( $_POST['language'] ) ? sanitize_text_field( wp_unslash( $_POST['language'] ) ) : ''; $tone = isset( $_POST['tone'] ) ? sanitize_key( wp_unslash( $_POST['tone'] ) ) : ''; $ai_provider = isset( $_POST['ai_provider'] ) ? sanitize_key( wp_unslash( $_POST['ai_provider'] ) ) : ''; $post_author = isset( $_POST['post_author'] ) ? absint( wp_unslash( $_POST['post_author'] ) ) : 0; $post_categories = isset( $_POST['post_categories'] ) ? array_filter( array_map( 'absint', (array) wp_unslash( $_POST['post_categories'] ) ) ) : array(); $post_tags = isset( $_POST['post_tags'] ) ? sanitize_text_field( wp_unslash( $_POST['post_tags'] ) ) : ''; // phpcs:enable WordPress.Security.NonceVerification.Missing // Only persist known languages/tones. if ( ! array_key_exists( $language, aicw_get_languages() ) ) { $language = 'English'; } if ( ! array_key_exists( $tone, aicw_get_content_tones() ) ) { $tone = ''; } // Only persist registered providers (Autopilot campaigns pick their own). if ( ! array_key_exists( $ai_provider, aicw_get_ai_providers() ) ) { $ai_provider = 'gemini'; } update_post_meta( $campaign_id, '_aicw_campaign_type', $type ); update_post_meta( $campaign_id, '_aicw_campaign_host', $host ); update_post_meta( $campaign_id, '_aicw_campaign_source', $source ); update_post_meta( $campaign_id, '_aicw_rss_feed_link', $rss_feed ); update_post_meta( $campaign_id, '_aicw_block_keywords', $block_keywords ); update_post_meta( $campaign_id, '_aicw_search_replaces', $search_replaces ); update_post_meta( $campaign_id, '_aicw_html_cleaners', $html_cleaners ); update_post_meta( $campaign_id, '_aicw_insert_content_position', $insert_content_position ); update_post_meta( $campaign_id, '_aicw_insert_content', $insert_content ); update_post_meta( $campaign_id, '_aicw_campaign_target', $target ); update_post_meta( $campaign_id, '_aicw_campaign_frequency', $frequency ); update_post_meta( $campaign_id, '_aicw_generate_thumbnail', $generate_thumb ); update_post_meta( $campaign_id, '_aicw_seo_enabled', $seo_enabled ); update_post_meta( $campaign_id, '_aicw_post_type', $post_type ); update_post_meta( $campaign_id, '_aicw_completed_post_status', $completed_post_status ); update_post_meta( $campaign_id, '_aicw_language', $language ); update_post_meta( $campaign_id, '_aicw_tone', $tone ); update_post_meta( $campaign_id, '_aicw_ai_provider', $ai_provider ); update_post_meta( $campaign_id, '_aicw_post_author', $post_author ); update_post_meta( $campaign_id, '_aicw_post_categories', $post_categories ); update_post_meta( $campaign_id, '_aicw_post_tags', $post_tags ); /** * Fires after the campaign meta has been saved. * * Add-ons persist their own campaign fields here. The nonce and * capability checks have already run in the calling handler. * * @param int $campaign_id The campaign ID. * * @since 2.4.0 */ do_action( 'aicw_save_campaign_meta', $campaign_id ); return $frequency; } /** * Edit campaign. * * @since 1.0.0 * @return void */ public static function handle_edit_campaign() { check_admin_referer( 'aicw_edit_campaign' ); $referer = wp_get_referer(); if ( ! current_user_can( 'manage_options' ) ) { ai_content_writer()->flash_notice( esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } $id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0; $type = isset( $_POST['campaign_type'] ) ? sanitize_text_field( wp_unslash( $_POST['campaign_type'] ) ) : 'articles'; $title = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : ''; $keywords = isset( $_POST['keywords'] ) ? wp_kses_post( wp_unslash( $_POST['keywords'] ) ) : ''; $status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : 'publish'; $rss_feed = isset( $_POST['rss_feed_link'] ) ? sanitize_text_field( wp_unslash( $_POST['rss_feed_link'] ) ) : ''; // Validate the input fields. if ( empty( $id ) ) { ai_content_writer()->flash_notice( esc_html__( 'Invalid campaign. Please try again.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $title ) ) { ai_content_writer()->flash_notice( esc_html__( 'Title is required. Please enter the campaign title.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $type ) ) { ai_content_writer()->flash_notice( esc_html__( 'Campaign type is required. Please select the campaign type.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $keywords ) && ! in_array( $type, array( 'rss-feed', 'autopilot' ), true ) ) { ai_content_writer()->flash_notice( esc_html__( 'Keywords are required. Please enter the keywords.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( 'rss-feed' === $type && empty( $rss_feed ) ) { ai_content_writer()->flash_notice( esc_html__( 'RSS feed link is required. Please enter the RSS feed link.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } // Update the campaign. $campaign_id = wp_update_post( array( 'ID' => $id, 'post_title' => wp_strip_all_tags( $title ), 'post_content' => wp_kses_post( $keywords ), 'post_status' => $status, ) ); if ( is_wp_error( $campaign_id ) ) { ai_content_writer()->flash_notice( $campaign_id->get_error_message(), 'error' ); wp_safe_redirect( $referer ); exit(); } // Update the campaign meta and schedule the cron event to run the campaign. $frequency = self::save_campaign_meta( $campaign_id, $type, $rss_feed ); $imported = self::import_campaign_titles( $campaign_id ); if ( 'publish' === $status ) { Cron::schedule_campaign( $campaign_id, $frequency ); } if ( $imported > 0 ) { ai_content_writer()->flash_notice( sprintf( /* translators: %d: Number of imported titles. */ esc_html__( '%d titles imported and queued for content generation.', 'ai-content-writer' ), $imported ), 'success' ); } ai_content_writer()->flash_notice( esc_html__( 'Campaign updated successfully.', 'ai-content-writer' ), 'success' ); wp_safe_redirect( $referer ); exit(); } /** * Generate content. * * @since 1.0.0 * @return void */ public static function handle_generate_content() { check_admin_referer( 'aicw_generate_content' ); $referer = wp_get_referer(); if ( ! current_user_can( 'manage_options' ) ) { ai_content_writer()->flash_notice( esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } $prompt_content = isset( $_POST['prompt'] ) ? sanitize_textarea_field( wp_unslash( $_POST['prompt'] ) ) : ''; $keywords = isset( $_POST['keywords'] ) ? sanitize_text_field( wp_unslash( $_POST['keywords'] ) ) : ''; $prompt_type = isset( $_POST['prompt_type'] ) ? sanitize_text_field( wp_unslash( $_POST['prompt_type'] ) ) : 'Prompt'; $language = isset( $_POST['language'] ) ? sanitize_text_field( wp_unslash( $_POST['language'] ) ) : esc_html__( 'English', 'ai-content-writer' ); $min_words = isset( $_POST['min_words'] ) ? absint( wp_unslash( $_POST['min_words'] ) ) : ''; $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( wp_unslash( $_POST['post_type'] ) ) : ''; $status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : 'draft'; // Remember the post-generation choices as the defaults for the next run. update_option( 'aicw_enable_img_generation', isset( $_POST['aicw_enable_img_generation'] ) ? 'yes' : '' ); update_option( 'aicw_enable_redirection', isset( $_POST['aicw_enable_redirection'] ) ? 'yes' : '' ); // For ChatGPT OpenAI API model. $system_tone = isset( $_POST['system_tone'] ) ? sanitize_textarea_field( wp_unslash( $_POST['system_tone'] ) ) : ''; if ( empty( $system_tone ) ) { $system_tone = esc_html__( 'You are an expert SEO content writer. Generate factually accurate, engaging, and well-structured articles optimized for readability and search engines.', 'ai-content-writer' ); } $max_tokens = isset( $_POST['max_tokens'] ) ? absint( wp_unslash( $_POST['max_tokens'] ) ) : ''; $temperature = isset( $_POST['temperature'] ) ? floatval( wp_unslash( $_POST['temperature'] ) ) : ''; // Validate the input fields. if ( empty( $prompt_content ) ) { ai_content_writer()->flash_notice( esc_html__( 'Title is required. Please, start with "Write an article on..."', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $prompt_type ) ) { ai_content_writer()->flash_notice( esc_html__( 'Prompt type is required. Please, select the prompt type.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $language ) ) { ai_content_writer()->flash_notice( esc_html__( 'Language is required. Please, select the language.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $post_type ) ) { ai_content_writer()->flash_notice( esc_html__( 'Post type is required. Please, select the post type.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } // Resolve the configured AI provider. $api_model = get_option( 'aicw_api_model' ); $provider = aicw_get_ai_provider( $api_model ); if ( ! $provider ) { ai_content_writer()->flash_notice( esc_html__( 'Invalid API model. Please configure the API settings.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( ! $provider->is_configured() ) { ai_content_writer()->flash_notice( sprintf( /* translators: %s: Provider label. */ esc_html__( 'Please configure the API settings. A valid %s API key is required to generate the content.', 'ai-content-writer' ), $provider->get_label() ), 'error' ); wp_safe_redirect( $referer ); exit(); } // Build the generation prompt. $prompt = sprintf( /* translators: 1: Prompt Content, 2: SEO/keyword instructions, 3: Language, 4: Min Words */ esc_html__( '%1$s with a professional tone. %2$s Ensure the content is engaging, easy to read, and formatted with proper headings (H2, H3), bullet points, and paragraphs. Use %3$s as the language for this article. Keep the word count minimum of %4$s words for the best readability and SEO ranking. The article should include: - A catchy introduction that hooks the reader - Informative body sections with subheadings - A compelling conclusion with key takeaways Avoid fluff, keep sentences concise, and ensure factual accuracy. Where applicable, use industry insights, statistics, and real-world examples. If necessary, cite reliable sources to back up claims. Ensure no AI or robotic tone—the content should feel human-written.', 'ai-content-writer' ), '"' . $prompt_type . ': ' . $prompt_content . '"', empty( $keywords ) ? esc_html__( 'The article should be optimized for SEO', 'ai-content-writer' ) : esc_html__( 'The article should be optimized for SEO, including relevant keywords: ', 'ai-content-writer' ) . '"' . $keywords . '".', $language, empty( $min_words ) ? 500 : $min_words ); // Request the structured JSON contract for deterministic post data. $prompt .= "\n\n" . aicw_get_json_prompt_instructions(); $raw = $provider->generate( $prompt, array( 'system' => $system_tone, 'max_tokens' => empty( $max_tokens ) ? 8000 : absint( $max_tokens ), 'temperature' => empty( $temperature ) ? 0.7 : floatval( $temperature ), 'json' => true, ) ); if ( is_wp_error( $raw ) ) { ai_content_writer()->flash_notice( $raw->get_error_message(), 'error' ); wp_safe_redirect( $referer ); exit(); } $parsed = aicw_parse_generated_post( $raw, $prompt_content ); if ( null !== $parsed ) { $content = $parsed['content']; $title = $parsed['title']; } else { // Fallback: treat the response as plain markdown. $converted = aicw_convert_openai_markdown_to_html( $raw, $prompt_content ); $content = isset( $converted['content'] ) ? $converted['content'] : ''; $title = ! empty( $converted['title'] ) ? $converted['title'] : $prompt_content; } // Create a new post. $postarr = array( 'post_title' => wp_strip_all_tags( $title ), 'post_content' => wp_kses_post( $content ), 'post_status' => $status, 'post_type' => $post_type, ); if ( null !== $parsed ) { if ( ! empty( $parsed['excerpt'] ) ) { $postarr['post_excerpt'] = $parsed['excerpt']; } if ( ! empty( $parsed['seo']['slug'] ) ) { $postarr['post_name'] = $parsed['seo']['slug']; } } $post_id = wp_insert_post( $postarr ); if ( is_wp_error( $post_id ) ) { ai_content_writer()->flash_notice( $post_id->get_error_message(), 'error' ); wp_safe_redirect( $referer ); exit(); } // Add post-meta for identification. _aicw_campaign_id is used to identify the campaign. But in this case, we are using the manual prompt to generate the content. update_post_meta( $post_id, '_aicw_campaign_id', 'manual' ); // Persist the structured extras (SEO fields, taxonomy suggestions). if ( null !== $parsed ) { update_post_meta( $post_id, '_aicw_seo', $parsed['seo'] ); // Write the SEO fields into the active SEO plugin, when present. aicw_apply_seo_meta( $post_id ); /** * Fires after an instant-generation post has been created with * structured AI data. * * @param int $post_id The post ID. * @param array $parsed The normalized post data. * * @since 2.4.0 */ do_action( 'aicw_instant_post_created', $post_id, $parsed ); if ( ! empty( $parsed['tags'] ) && is_object_in_taxonomy( $post_type, 'post_tag' ) ) { wp_set_post_terms( $post_id, $parsed['tags'], 'post_tag' ); } if ( ! empty( $parsed['categories'] ) && is_object_in_taxonomy( $post_type, 'category' ) ) { $term_ids = array(); foreach ( $parsed['categories'] as $category_name ) { $term = get_term_by( 'name', $category_name, 'category' ); if ( $term instanceof \WP_Term ) { $term_ids[] = (int) $term->term_id; } } if ( ! empty( $term_ids ) ) { wp_set_post_terms( $post_id, $term_ids, 'category' ); } } } if ( 'yes' === get_option( 'aicw_enable_img_generation', 'yes' ) ) { wp_safe_redirect( add_query_arg( array( 'post_id' => $post_id, ), admin_url( 'admin.php?page=aicw-generate-content&featured_image=1' ) ) ); exit(); } // Redirect to the post edit page. if ( 'yes' === get_option( 'aicw_enable_redirection', 'yes' ) ) { ai_content_writer()->flash_notice( esc_html__( 'Content generated successfully.', 'ai-content-writer' ), 'success' ); wp_safe_redirect( admin_url( 'post.php?post=' . $post_id . '&action=edit' ) ); exit(); } ai_content_writer()->flash_notice( esc_html__( 'Content generated successfully.', 'ai-content-writer' ), 'success' ); wp_safe_redirect( $referer ); exit(); } /** * Set featured image. * * @since 1.0.0 * @return void */ public static function handle_set_featured_image() { check_admin_referer( 'aicw_set_featured_image' ); $referer = wp_get_referer(); if ( ! current_user_can( 'manage_options' ) ) { ai_content_writer()->flash_notice( esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } $post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0; $media_url = isset( $_POST['featured_image'] ) ? sanitize_url( wp_unslash( $_POST['featured_image'] ) ) : ''; if ( empty( $post_id ) ) { ai_content_writer()->flash_notice( esc_html__( 'Invalid post. Please try again.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } if ( empty( $media_url ) ) { ai_content_writer()->flash_notice( esc_html__( 'Invalid image. Please select an image.', 'ai-content-writer' ), 'error' ); wp_safe_redirect( $referer ); exit(); } $media_id = media_sideload_image( $media_url, $post_id, get_the_title( $post_id ), 'id' ); if ( is_wp_error( $media_id ) ) { ai_content_writer()->flash_notice( sprintf( /* translators: %s: Error message. */ esc_html__( 'Failed to download the featured image. Error: %s', 'ai-content-writer' ), esc_html( $media_id->get_error_message() ) ), 'error' ); wp_safe_redirect( $referer ); exit(); } set_post_thumbnail( $post_id, $media_id ); // Image SEO: use the post title as alt text when none is set. if ( ! get_post_meta( $media_id, '_wp_attachment_image_alt', true ) ) { update_post_meta( $media_id, '_wp_attachment_image_alt', sanitize_text_field( get_the_title( $post_id ) ) ); } // Redirect to the post-edit page. if ( 'yes' === get_option( 'aicw_enable_redirection', 'yes' ) ) { wp_safe_redirect( admin_url( 'post.php?post=' . $post_id . '&action=edit' ) ); exit(); } ai_content_writer()->flash_notice( esc_html__( 'Content generated and featured image set successfully.', 'ai-content-writer' ), 'success' ); // Remove featured_image & post_id from the referer URL. wp_safe_redirect( remove_query_arg( array( 'featured_image', 'post_id' ), $referer ) ); exit(); } /** * Handle ajax request for deleting the campaigns temporarily post. * * @since 1.4.0 * @return void */ public static function handle_delete_temp_post() { check_ajax_referer( 'aicw_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), ) ); } $post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0; if ( empty( $post_id ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid post. Please try again.', 'ai-content-writer' ), ) ); } // Delete the post. This will permanently delete the post. if ( wp_delete_post( $post_id, true ) ) { wp_send_json_success( array( 'message' => esc_html__( 'Post deleted successfully.', 'ai-content-writer' ), ) ); } wp_send_json_error( array( 'message' => esc_html__( 'Failed to delete the post. Please try again.', 'ai-content-writer' ), ) ); } /** * Handle ajax request for running the campaign instantly. * * @since 1.4.0 * @return void */ public static function handle_run_campaign() { check_ajax_referer( 'aicw_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), ) ); } $campaign_id = isset( $_POST['campaign_id'] ) ? absint( wp_unslash( $_POST['campaign_id'] ) ) : 0; if ( empty( $campaign_id ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid campaign. Please try again.', 'ai-content-writer' ), ) ); } $campaign = get_post( $campaign_id ); if ( ! $campaign || 'aicw_campaign' !== $campaign->post_type ) { wp_send_json_error( array( 'message' => esc_html__( 'Campaign not found. Please try again.', 'ai-content-writer' ), ) ); } // Queue an immediate background run instead of processing inline — // title/content generation can take minutes and would time out the AJAX request. Cron::run_now( $campaign_id ); wp_send_json_success( array( 'message' => esc_html__( 'Campaign queued to run now. Each run advances the campaign one step (titles, content, featured image, publish), so check the Logs page in a few minutes for progress.', 'ai-content-writer' ), ) ); } /** * Handle ajax request for testing an API connection with the saved key. * * @since 2.3.0 * @return void */ public static function handle_test_connection() { check_ajax_referer( 'aicw_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'ai-content-writer' ), ) ); } $provider_slug = isset( $_POST['provider'] ) ? sanitize_key( wp_unslash( $_POST['provider'] ) ) : ''; // Image sources are tested with a tiny search against that specific source. if ( array_key_exists( $provider_slug, aicw_get_image_sources() ) ) { $images = ai_content_writer()->fetch_images( $provider_slug, 'nature', 3 ); if ( is_wp_error( $images ) ) { wp_send_json_error( array( 'message' => esc_html( $images->get_error_message() ) ) ); } wp_send_json_success( array( 'message' => sprintf( /* translators: %s: Image source name. */ esc_html__( '%s connection successful.', 'ai-content-writer' ), ucfirst( $provider_slug ) ), ) ); } $provider = aicw_get_ai_provider( $provider_slug ); if ( ! $provider ) { wp_send_json_error( array( 'message' => esc_html__( 'Unknown provider.', 'ai-content-writer' ), ) ); } if ( ! $provider->is_configured() ) { wp_send_json_error( array( 'message' => esc_html__( 'No API key saved yet. Save your key first, then test the connection.', 'ai-content-writer' ), ) ); } $models = $provider->get_models( true ); if ( is_wp_error( $models ) ) { wp_send_json_error( array( 'message' => esc_html( $models->get_error_message() ) ) ); } wp_send_json_success( array( 'message' => sprintf( /* translators: 1: Provider label, 2: Number of models. */ esc_html__( '%1$s connection successful. %2$d models available.', 'ai-content-writer' ), $provider->get_label(), count( $models ) ), ) ); } }