Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-delete-all-forms.php
+++ b/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-delete-all-forms.php
@@ -0,0 +1,40 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-delete-all-forms.php
+// Status: NEW FILE
+
+namespace WPAICGAdminAjaxAIForms;
+
+use WP_Error;
+use WPAICGAIFormsAdminAIPKit_AI_Form_Ajax_Handler;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Handles the logic for deleting all AI forms.
+ * Called by AIPKit_AI_Form_Ajax_Handler::ajax_delete_all_ai_forms().
+ *
+ * @param AIPKit_AI_Form_Ajax_Handler $handler_instance
+ * @return void
+ */
+function do_ajax_delete_all_forms_logic(AIPKit_AI_Form_Ajax_Handler $handler_instance): void
+{
+ $form_storage = $handler_instance->get_form_storage();
+
+ if (!$form_storage) {
+ $handler_instance->send_wp_error(new WP_Error('storage_missing', __('Form storage component is not available.', 'gpt3-ai-content-generator')), 500);
+ return;
+ }
+
+ $deleted = $form_storage->delete_all_forms();
+ if (is_wp_error($deleted)) {
+ $handler_instance->send_wp_error($deleted);
+ } elseif ($deleted === false) {
+ $handler_instance->send_wp_error(new WP_Error('delete_all_failed', __('Failed to delete all forms.', 'gpt3-ai-content-generator')), 500);
+ } else {
+ /* translators: %d is the number of forms deleted */
+ wp_send_json_success(['message' => sprintf(__('%d forms deleted successfully.', 'gpt3-ai-content-generator'), $deleted)]);
+ }
+}
--- a/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-delete-form.php
+++ b/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-delete-form.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace WPAICGAdminAjaxAIForms;
+
+use WP_Error;
+use WPAICGAIFormsAdminAIPKit_AI_Form_Ajax_Handler;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Handles the logic for deleting an AI form.
+ * Called by AIPKit_AI_Form_Ajax_Handler::ajax_delete_ai_form().
+ *
+ * @param AIPKit_AI_Form_Ajax_Handler $handler_instance
+ * @return void
+ */
+function do_ajax_delete_form_logic(AIPKit_AI_Form_Ajax_Handler $handler_instance): void
+{
+ $form_storage = $handler_instance->get_form_storage();
+
+ if (!$form_storage) {
+ $handler_instance->send_wp_error(new WP_Error('storage_missing', __('Form storage component is not available.', 'gpt3-ai-content-generator')), 500);
+ return;
+ }
+
+ $form_id = isset($_POST['form_id']) ? absint($_POST['form_id']) : 0;
+ if (empty($form_id)) {
+ $handler_instance->send_wp_error(new WP_Error('id_required', __('Form ID is required for deletion.', 'gpt3-ai-content-generator')), 400);
+ return;
+ }
+
+ $deleted = $form_storage->delete_form($form_id);
+ if ($deleted) {
+ wp_send_json_success(['message' => __('Form deleted successfully.', 'gpt3-ai-content-generator')]);
+ } else {
+ $handler_instance->send_wp_error(new WP_Error('delete_failed', __('Failed to delete form.', 'gpt3-ai-content-generator')), 500);
+ }
+}
--- a/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-duplicate-form.php
+++ b/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-duplicate-form.php
@@ -0,0 +1,65 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-duplicate-form.php
+// Status: MODIFIED
+
+namespace WPAICGAdminAjaxAIForms;
+
+use WP_Error;
+use WPAICGAIFormsAdminAIPKit_AI_Form_Ajax_Handler;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Handles the logic for duplicating an AI form.
+ * Called by AIPKit_AI_Form_Ajax_Handler::ajax_duplicate_ai_form().
+ *
+ * @param AIPKit_AI_Form_Ajax_Handler $handler_instance
+ * @return void
+ */
+function do_ajax_duplicate_form_logic(AIPKit_AI_Form_Ajax_Handler $handler_instance): void
+{
+ $form_storage = $handler_instance->get_form_storage();
+
+ if (!$form_storage) {
+ $handler_instance->send_wp_error(new WP_Error('storage_missing', __('Form storage component is not available.', 'gpt3-ai-content-generator')), 500);
+ return;
+ }
+
+ $form_id_to_duplicate = isset($_POST['form_id']) ? absint($_POST['form_id']) : 0;
+ if (empty($form_id_to_duplicate)) {
+ $handler_instance->send_wp_error(new WP_Error('id_required', __('Form ID is required for duplication.', 'gpt3-ai-content-generator')), 400);
+ return;
+ }
+
+ // Get all data from the original form
+ $original_form_data = $form_storage->get_form_data($form_id_to_duplicate);
+ if (is_wp_error($original_form_data)) {
+ $handler_instance->send_wp_error($original_form_data);
+ return;
+ }
+
+ // --- FIX: Remap and re-encode the structure for saving ---
+ // The get_form_data() returns 'structure' as a PHP array, but the save function expects 'form_structure' as a JSON string.
+ if (isset($original_form_data['structure']) && is_array($original_form_data['structure'])) {
+ // Re-encode with flags to preserve Unicode characters, preventing them from becoming gibberish on some servers.
+ $original_form_data['form_structure'] = wp_json_encode($original_form_data['structure'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
+ unset($original_form_data['structure']); // Remove the old PHP array key
+ }
+ // --- END FIX ---
+
+ // Prepare data for the new form
+ $new_title = $original_form_data['title'] . ' (Copy)';
+
+ // The get_form_data() result is now compatible with the settings array needed by save_form_settings(),
+ // which is called by create_form().
+ $result = $form_storage->create_form($new_title, $original_form_data);
+
+ if (is_wp_error($result)) {
+ $handler_instance->send_wp_error($result);
+ } else {
+ wp_send_json_success(['message' => __('Form duplicated successfully.', 'gpt3-ai-content-generator'), 'new_form_id' => $result]);
+ }
+}
--- a/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-export-all-forms.php
+++ b/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-export-all-forms.php
@@ -0,0 +1,54 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-export-all-forms.php
+// Status: NEW FILE
+
+namespace WPAICGAdminAjaxAIForms;
+
+use WP_Error;
+use WPAICGAIFormsAdminAIPKit_AI_Form_Ajax_Handler;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Handles the logic for exporting all AI forms.
+ * Called by AIPKit_AI_Form_Ajax_Handler::ajax_export_all_ai_forms().
+ *
+ * @param AIPKit_AI_Form_Ajax_Handler $handler_instance
+ * @return void
+ */
+function do_ajax_export_all_forms_logic(AIPKit_AI_Form_Ajax_Handler $handler_instance): void
+{
+ $form_storage = $handler_instance->get_form_storage();
+
+ if (!$form_storage) {
+ $handler_instance->send_wp_error(new WP_Error('storage_missing', __('Form storage component is not available.', 'gpt3-ai-content-generator')), 500);
+ return;
+ }
+
+ // Get all forms without pagination
+ $all_forms_list = $form_storage->get_forms_list(['posts_per_page' => -1]);
+ $form_ids = wp_list_pluck($all_forms_list['forms'], 'id');
+
+ if (empty($form_ids)) {
+ wp_send_json_error(['message' => __('No forms found to export.', 'gpt3-ai-content-generator')], 404);
+ return;
+ }
+
+ $exported_forms = [];
+ foreach ($form_ids as $form_id) {
+ $form_data = $form_storage->get_form_data($form_id);
+ if (!is_wp_error($form_data)) {
+ // Remove keys that are not needed for export/import
+ unset($form_data['id']);
+ unset($form_data['status']);
+ $exported_forms[] = $form_data;
+ } else {
+ error_log("AI Forms Export All: Could not get data for form ID {$form_id}. Error: " . $form_data->get_error_message());
+ }
+ }
+
+ wp_send_json_success(['forms' => $exported_forms]);
+}
--- a/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-get-form.php
+++ b/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-get-form.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace WPAICGAdminAjaxAIForms;
+
+use WP_Error;
+use WPAICGAIFormsAdminAIPKit_AI_Form_Ajax_Handler;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Handles the logic for fetching a single AI form's data.
+ * Called by AIPKit_AI_Form_Ajax_Handler::ajax_get_ai_form().
+ *
+ * @param AIPKit_AI_Form_Ajax_Handler $handler_instance
+ * @return void
+ */
+function do_ajax_get_form_logic(AIPKit_AI_Form_Ajax_Handler $handler_instance): void
+{
+ $form_storage = $handler_instance->get_form_storage();
+
+ if (!$form_storage) {
+ $handler_instance->send_wp_error(new WP_Error('storage_missing', __('Form storage component is not available.', 'gpt3-ai-content-generator')), 500);
+ return;
+ }
+
+ $form_id = isset($_POST['form_id']) ? absint($_POST['form_id']) : 0;
+ if (empty($form_id)) {
+ $handler_instance->send_wp_error(new WP_Error('id_required', __('Form ID is required.', 'gpt3-ai-content-generator')), 400);
+ return;
+ }
+
+ $form_data = $form_storage->get_form_data($form_id);
+ if (is_wp_error($form_data)) {
+ $handler_instance->send_wp_error($form_data);
+ } else {
+ wp_send_json_success(['form' => $form_data]);
+ }
+}
--- a/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-import-forms.php
+++ b/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-import-forms.php
@@ -0,0 +1,90 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-import-forms.php
+// Status: MODIFIED
+
+namespace WPAICGAdminAjaxAIForms;
+
+use WP_Error;
+use WPAICGAIFormsAdminAIPKit_AI_Form_Ajax_Handler;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Handles the logic for importing AI forms from a JSON file.
+ * Called by AIPKit_AI_Form_Ajax_Handler::ajax_import_ai_forms().
+ *
+ * @param AIPKit_AI_Form_Ajax_Handler $handler_instance
+ * @return void
+ */
+function do_ajax_import_forms_logic(AIPKit_AI_Form_Ajax_Handler $handler_instance): void
+{
+ $form_storage = $handler_instance->get_form_storage();
+
+ if (!$form_storage) {
+ $handler_instance->send_wp_error(new WP_Error('storage_missing_import', __('Form storage component is not available.', 'gpt3-ai-content-generator')), 500);
+ return;
+ }
+
+ if (empty($_POST['forms_json'])) {
+ $handler_instance->send_wp_error(new WP_Error('no_data_import', __('No form data received for import.', 'gpt3-ai-content-generator')), 400);
+ return;
+ }
+
+ $forms_json = wp_unslash($_POST['forms_json']);
+ $forms_to_import = json_decode($forms_json, true);
+
+ if (json_last_error() !== JSON_ERROR_NONE || !is_array($forms_to_import)) {
+ $handler_instance->send_wp_error(new WP_Error('invalid_json_import', __('Invalid JSON data received for import.', 'gpt3-ai-content-generator')), 400);
+ return;
+ }
+
+ $imported_count = 0;
+ $failed_count = 0;
+ $errors = [];
+
+ foreach ($forms_to_import as $form_data) {
+ $title = isset($form_data['title']) ? sanitize_text_field($form_data['title']) : 'Imported Form';
+
+ // Sanitize settings before creating the form
+ // This is a minimal sanitization; a more robust one could be implemented.
+ $settings = $form_data;
+ unset($settings['title'], $settings['id'], $settings['status']); // Remove fields not used in creation
+
+ // --- FIX: Remap and re-encode the structure for saving ---
+ // The export file has 'structure' as an array, but the save function expects 'form_structure' as a JSON string.
+ if (isset($settings['structure']) && is_array($settings['structure'])) {
+ // Re-encode with flags to preserve Unicode characters, preventing them from becoming gibberish on some servers.
+ $settings['form_structure'] = wp_json_encode($settings['structure'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
+ unset($settings['structure']); // Remove the old PHP array key
+ }
+ // --- END FIX ---
+
+
+ // Append "(Imported)" to avoid direct title conflicts, making management easier.
+ $new_title = $title . ' (Imported)';
+
+ $result = $form_storage->create_form($new_title, $settings);
+
+ if (is_wp_error($result)) {
+ $failed_count++;
+ /* translators: 1: The form title. 2: The specific error message. */
+ $errors[] = sprintf(__('Failed to import form "%1$s": %2$s', 'gpt3-ai-content-generator'), esc_html($title), $result->get_error_message());
+ } else {
+ $imported_count++;
+ }
+ }
+ /* translators: %d is the number of forms imported */
+ $message = sprintf(_n('%d form was imported successfully.', '%d forms were imported successfully.', $imported_count, 'gpt3-ai-content-generator'), $imported_count);
+
+ if ($failed_count > 0) {
+ /* translators: %d is the number of forms that failed to import */
+ $message .= ' ' . sprintf(_n('%d form failed to import.','%d forms failed to import.',$failed_count,'gpt3-ai-content-generator'),$failed_count);
+ // Optionally log detailed errors for admin
+ error_log('AI Forms Import Errors: ' . print_r($errors, true));
+ }
+
+ wp_send_json_success(['message' => $message, 'imported_count' => $imported_count, 'failed_count' => $failed_count]);
+}
--- a/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-list-forms.php
+++ b/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-list-forms.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace WPAICGAdminAjaxAIForms;
+
+use WP_Error;
+use WPAICGAIFormsAdminAIPKit_AI_Form_Ajax_Handler;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Handles the logic for listing all AI forms with dynamic querying.
+ * Called by AIPKit_AI_Form_Ajax_Handler::ajax_list_ai_forms().
+ * UPDATED: Handles pagination, search, and sorting parameters.
+ *
+ * @param AIPKit_AI_Form_Ajax_Handler $handler_instance
+ * @return void
+ */
+function do_ajax_list_forms_logic(AIPKit_AI_Form_Ajax_Handler $handler_instance): void
+{
+ $form_storage = $handler_instance->get_form_storage();
+ if (!$form_storage) {
+ $handler_instance->send_wp_error(new WP_Error('storage_missing', __('Form storage component is not available.', 'gpt3-ai-content-generator')), 500);
+ return;
+ }
+
+ // --- NEW: Read and sanitize query parameters from POST ---
+ $paged = isset($_POST['page']) ? absint($_POST['page']) : 1;
+ $search = isset($_POST['search']) ? sanitize_text_field(wp_unslash($_POST['search'])) : '';
+ $sort_by = isset($_POST['sort_by']) ? sanitize_key($_POST['sort_by']) : 'title';
+ $sort_order = isset($_POST['sort_order']) && in_array(strtoupper($_POST['sort_order']), ['ASC', 'DESC']) ? strtoupper($_POST['sort_order']) : 'ASC';
+ $provider_filter = isset($_POST['filter_provider']) ? sanitize_text_field(wp_unslash($_POST['filter_provider'])) : 'all';
+
+ // Whitelist sortable columns
+ $allowed_sort_keys = ['id', 'title', 'provider', 'model', 'date'];
+ if (!in_array($sort_by, $allowed_sort_keys)) {
+ $sort_by = 'title';
+ }
+ // WP_Query uses 'ID' instead of 'id'
+ if ($sort_by === 'id') {
+ $sort_by = 'ID';
+ }
+
+
+ $args = [
+ 'paged' => $paged,
+ 'search' => $search,
+ 'orderby' => $sort_by,
+ 'order' => $sort_order,
+ 'filter_provider' => $provider_filter,
+ ];
+
+ $result = $form_storage->get_forms_list($args);
+
+ // The result from get_forms_list_logic is now an array with 'forms' and 'pagination' keys
+ wp_send_json_success($result);
+}
--- a/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-save-form.php
+++ b/gpt3-ai-content-generator/admin/ajax/ai-forms/ajax-save-form.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace WPAICGAdminAjaxAIForms;
+
+use WP_Error;
+use WPAICGAIFormsAdminAIPKit_AI_Form_Ajax_Handler;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Handles the logic for saving an AI form.
+ * Called by AIPKit_AI_Form_Ajax_Handler::ajax_save_ai_form().
+ *
+ * @param AIPKit_AI_Form_Ajax_Handler $handler_instance
+ * @return void
+ */
+function do_ajax_save_form_logic(AIPKit_AI_Form_Ajax_Handler $handler_instance): void
+{
+
+ $form_storage = $handler_instance->get_form_storage();
+
+ if (!$form_storage) {
+ $handler_instance->send_wp_error(new WP_Error('storage_missing', __('Form storage component is not available.', 'gpt3-ai-content-generator')), 500);
+ return;
+ }
+
+ $form_id = isset($_POST['form_id']) && !empty($_POST['form_id']) ? absint($_POST['form_id']) : null;
+ $title = isset($_POST['title']) ? sanitize_text_field(wp_unslash($_POST['title'])) : '';
+ $prompt_template = isset($_POST['prompt_template']) ? sanitize_textarea_field(wp_unslash($_POST['prompt_template'])) : '';
+ $form_structure_json = isset($_POST['form_structure']) ? wp_kses_post(wp_unslash($_POST['form_structure'])) : '[]';
+
+ // Process labels - now receiving as a JSON string from JavaScript
+ $default_labels = [
+ 'generate_button' => __('Generate', 'gpt3-ai-content-generator'),
+ 'stop_button' => __('Stop', 'gpt3-ai-content-generator'),
+ 'download_button' => __('Download', 'gpt3-ai-content-generator'),
+ 'save_button' => __('Save', 'gpt3-ai-content-generator'),
+ 'copy_button' => __('Copy', 'gpt3-ai-content-generator'),
+ 'provider_label' => __('AI Provider', 'gpt3-ai-content-generator'),
+ 'model_label' => __('AI Model', 'gpt3-ai-content-generator'),
+ ];
+
+ $submitted_labels = [];
+ if (isset($_POST['labels']) && !empty($_POST['labels'])) {
+ $labels_json = wp_unslash($_POST['labels']);
+ $decoded_labels = json_decode($labels_json, true);
+ if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_labels)) {
+ $submitted_labels = $decoded_labels;
+ }
+ }
+
+ // Merge defaults: Use submitted value if not empty, otherwise use default.
+ $final_labels = [];
+ foreach ($default_labels as $key => $default_value) {
+ $submitted_value = isset($submitted_labels[$key]) ? trim($submitted_labels[$key]) : '';
+ $final_labels[sanitize_key($key)] = sanitize_text_field(!empty($submitted_value) ? $submitted_value : $default_value);
+ }
+
+
+ // --- Get AI config fields from POST ---
+ $ai_provider = isset($_POST['ai_provider']) ? sanitize_text_field(wp_unslash($_POST['ai_provider'])) : null;
+ $ai_model = isset($_POST['ai_model']) ? sanitize_text_field(wp_unslash($_POST['ai_model'])) : null;
+ $temperature = isset($_POST['temperature']) ? sanitize_text_field(wp_unslash($_POST['temperature'])) : null;
+ $max_tokens = isset($_POST['max_tokens']) ? absint($_POST['max_tokens']) : null;
+ $top_p = isset($_POST['top_p']) ? sanitize_text_field(wp_unslash($_POST['top_p'])) : null;
+ $frequency_penalty = isset($_POST['frequency_penalty']) ? sanitize_text_field(wp_unslash($_POST['frequency_penalty'])) : null;
+ $presence_penalty = isset($_POST['presence_penalty']) ? sanitize_text_field(wp_unslash($_POST['presence_penalty'])) : null;
+
+ // --- Get Vector config fields from POST ---
+ $enable_vector_store = isset($_POST['enable_vector_store']) && $_POST['enable_vector_store'] === '1' ? '1' : '0';
+ $vector_store_provider = isset($_POST['vector_store_provider']) ? sanitize_key($_POST['vector_store_provider']) : 'openai';
+ $openai_vector_store_ids = isset($_POST['openai_vector_store_ids']) && is_array($_POST['openai_vector_store_ids']) ? array_map('sanitize_text_field', $_POST['openai_vector_store_ids']) : [];
+ $pinecone_index_name = isset($_POST['pinecone_index_name']) ? sanitize_text_field($_POST['pinecone_index_name']) : '';
+ $qdrant_collection_name = isset($_POST['qdrant_collection_name']) ? sanitize_text_field($_POST['qdrant_collection_name']) : '';
+ $vector_embedding_provider = isset($_POST['vector_embedding_provider']) ? sanitize_key($_POST['vector_embedding_provider']) : 'openai';
+ $vector_embedding_model = isset($_POST['vector_embedding_model']) ? sanitize_text_field($_POST['vector_embedding_model']) : '';
+ $vector_store_top_k = isset($_POST['vector_store_top_k']) ? absint($_POST['vector_store_top_k']) : 3;
+
+ $decoded_structure = json_decode($form_structure_json, true);
+ if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded_structure)) {
+ $handler_instance->send_wp_error(new WP_Error('invalid_structure_json', __('Invalid form structure data submitted.', 'gpt3-ai-content-generator')), 400);
+ return;
+ }
+
+ if (empty($title)) {
+ $handler_instance->send_wp_error(new WP_Error('title_required', __('Form title cannot be empty.', 'gpt3-ai-content-generator')), 400);
+ return;
+ }
+ if (empty($prompt_template)) {
+ $handler_instance->send_wp_error(new WP_Error('prompt_required', __('Prompt template is required.', 'gpt3-ai-content-generator')), 400);
+ return;
+ }
+
+ $settings = [
+ 'prompt_template' => $prompt_template,
+ 'form_structure' => $form_structure_json,
+ 'ai_provider' => $ai_provider,
+ 'ai_model' => $ai_model,
+ 'temperature' => $temperature,
+ 'max_tokens' => $max_tokens,
+ 'top_p' => $top_p,
+ 'frequency_penalty' => $frequency_penalty,
+ 'presence_penalty' => $presence_penalty,
+ // Vector settings
+ 'enable_vector_store' => $enable_vector_store,
+ 'vector_store_provider' => $vector_store_provider,
+ 'openai_vector_store_ids' => $openai_vector_store_ids,
+ 'pinecone_index_name' => $pinecone_index_name,
+ 'qdrant_collection_name' => $qdrant_collection_name,
+ 'vector_embedding_provider' => $vector_embedding_provider,
+ 'vector_embedding_model' => $vector_embedding_model,
+ 'vector_store_top_k' => $vector_store_top_k,
+ // Labels
+ 'labels' => $final_labels,
+ ];
+
+ if ($form_id) {
+ $updated_post_id = wp_update_post([
+ 'ID' => $form_id,
+ 'post_title' => $title,
+ ], true);
+
+ if (is_wp_error($updated_post_id)) {
+ $handler_instance->send_wp_error($updated_post_id);
+ return;
+ }
+ $form_storage->save_form_settings($form_id, $settings);
+ wp_send_json_success(['message' => __('Form updated successfully.', 'gpt3-ai-content-generator'), 'form_id' => $form_id]);
+ } else {
+ $result = $form_storage->create_form($title, $settings);
+ if (is_wp_error($result)) {
+ $handler_instance->send_wp_error($result);
+ } else {
+ wp_send_json_success(['message' => __('Form created successfully.', 'gpt3-ai-content-generator'), 'form_id' => $result]);
+ }
+ }
+}
--- a/gpt3-ai-content-generator/admin/ajax/migration/analysis/count-cpt-posts.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/analysis/count-cpt-posts.php
@@ -0,0 +1,51 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/analysis/count-cpt-posts.php
+// Status: MODIFIED
+
+namespace WPAICGAdminAjaxMigrationAnalysis;
+
+use WP_Query;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Counts posts for given CPTs and returns a summary.
+ *
+ * @param array $post_types An array of post type slugs.
+ * @return array ['count' => int, 'summary' => string, 'details' => array]
+ */
+function count_cpt_posts_logic(array $post_types): array
+{
+ $total_count = 0;
+ $details = [];
+
+ foreach ($post_types as $post_type) {
+
+ $query = new WP_Query([
+ 'post_type' => $post_type,
+ 'post_status' => 'any', // 'any' is important for finding all statuses
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ 'no_found_rows' => false, // Ensure found_posts is calculated
+ ]);
+ $count = $query->found_posts;
+ $total_count += $count;
+ if ($count > 0) {
+ $post_type_object = get_post_type_object($post_type);
+ $label = $post_type_object ? $post_type_object->labels->singular_name : $post_type; // Use singular_name for better label
+ // translators: %1$d is the number of posts, %2$s is the CPT name.
+ $details[] = sprintf(__('%1$d posts found in "%2$s" CPT.', 'gpt3-ai-content-generator'), $count, $label);
+ }
+ }
+
+ $summary = sprintf(
+ // translators: %d is the number of legacy posts found.
+ _n('%d legacy post found.', '%d legacy posts found.', $total_count, 'gpt3-ai-content-generator'),
+ $total_count
+ );
+
+ return ['count' => $total_count, 'summary' => $summary, 'details' => $details];
+}
--- a/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-custom-prompts.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-custom-prompts.php
@@ -0,0 +1,58 @@
+<?php
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-custom-prompts.php
+// Status: NEW FILE
+
+namespace WPAICGAdminAjaxMigrationAnalysis;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Gets details about old custom prompts from the options table.
+ *
+ * @return array ['count' => int, 'prompts' => array, 'summary' => string, 'details' => array]
+ */
+function get_old_custom_prompts_logic(): array
+{
+ $prompts = [];
+ $count = 0;
+
+ // AutoGPT / Bulk Content Writer Prompt
+ $autogpt_prompt = get_option('wpaicg_custom_prompt_auto', '');
+ if (!empty($autogpt_prompt)) {
+ $prompts['autogpt'] = [
+ 'label' => __('AutoGPT / Content Writer Prompt', 'gpt3-ai-content-generator'),
+ 'value' => $autogpt_prompt
+ ];
+ $count++;
+ }
+
+ // Custom Image Prompt
+ $image_prompt = get_option('wpaicg_custom_image_prompt', '');
+ if (!empty($image_prompt)) {
+ $prompts['image'] = [
+ 'label' => __('In-Content Image Prompt', 'gpt3-ai-content-generator'),
+ 'value' => $image_prompt
+ ];
+ $count++;
+ }
+
+ // Custom Featured Image Prompt
+ $featured_image_prompt = get_option('wpaicg_custom_featured_image_prompt', '');
+ if (!empty($featured_image_prompt)) {
+ $prompts['featured_image'] = [
+ 'label' => __('Featured Image Prompt', 'gpt3-ai-content-generator'),
+ 'value' => $featured_image_prompt
+ ];
+ $count++;
+ }
+
+ return [
+ 'count' => $count,
+ 'prompts' => $prompts,
+ /* translators: %d is the number of custom prompts found */
+ 'summary' => sprintf(_n('%d custom prompt found.', '%d custom prompts found.', $count, 'gpt3-ai-content-generator'), $count),
+ 'details' => array_keys($prompts) // just for logging maybe
+ ];
+}
No newline at end of file
--- a/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-integration-data.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-integration-data.php
@@ -0,0 +1,93 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-integration-data.php
+// Status: MODIFIED
+// I have updated the logic to correctly handle both raw JSON strings and serialized PHP arrays from the database for Google Sheets and RSS data.
+
+namespace WPAICGAdminAjaxMigrationAnalysis;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Gets details about old integration data (Google Sheets, RSS) from the options table.
+ *
+ * @return array ['count' => int, 'integrations' => array, 'summary' => string, 'details' => array]
+ */
+function get_old_integration_data_logic(): array
+{
+ $integrations = [];
+ $count = 0;
+ $details = [];
+
+ // Google Sheets URL
+ $g_sheets_url = get_option('wpaicg_google_sheets_url', '');
+ if (!empty($g_sheets_url)) {
+ $integrations['google_sheets_url'] = [
+ 'label' => __('Google Sheets URL', 'gpt3-ai-content-generator'),
+ 'value' => $g_sheets_url,
+ 'is_json' => false
+ ];
+ $count++;
+ $details[] = 'Google Sheets URL found.';
+ }
+
+ // Google Sheets Credentials
+ $g_sheets_creds = get_option('wpaicg_google_credentials_json', []);
+ if (!empty($g_sheets_creds)) {
+ $creds_value = '';
+ if (is_array($g_sheets_creds)) {
+ // If it's already an array, encode it for display.
+ $creds_value = wp_json_encode($g_sheets_creds, JSON_PRETTY_PRINT);
+ } elseif (is_string($g_sheets_creds)) {
+ // If it's a string, try to decode it to re-encode it prettily.
+ // This handles cases where a raw JSON string was saved.
+ $decoded = json_decode($g_sheets_creds, true);
+ if (json_last_error() === JSON_ERROR_NONE) {
+ $creds_value = wp_json_encode($decoded, JSON_PRETTY_PRINT);
+ } else {
+ $creds_value = $g_sheets_creds; // Fallback to the raw string if it's not valid JSON
+ }
+ }
+
+ if (!empty($creds_value)) {
+ $integrations['google_sheets_credentials'] = [
+ 'label' => __('Google Sheets Credentials', 'gpt3-ai-content-generator'),
+ 'value' => $creds_value,
+ 'is_json' => true
+ ];
+ $count++;
+ $details[] = 'Google Sheets Credentials found.';
+ }
+ }
+
+ // RSS Feeds
+ $rss_feeds = get_option('wpaicg_rss_feeds', []);
+ if (is_string($rss_feeds)) {
+ // Attempt to unserialize if it's a string (handles cases where DB clients might not auto-unserialize)
+ $rss_feeds = maybe_unserialize($rss_feeds);
+ }
+ if (!empty($rss_feeds) && is_array($rss_feeds)) {
+ $rss_urls = array_column($rss_feeds, 'url');
+ $filtered_urls = array_filter($rss_urls);
+
+ if (!empty($filtered_urls)) {
+ $integrations['rss_feeds'] = [
+ 'label' => __('RSS Feed URLs', 'gpt3-ai-content-generator'),
+ 'value' => implode("n", $filtered_urls),
+ 'is_json' => false
+ ];
+ $count++;
+ $details[] = count($filtered_urls) . ' RSS Feed(s) found.';
+ }
+ }
+
+ return [
+ 'count' => $count,
+ 'integrations' => $integrations,
+ /* translators: %d is the number of old integration settings found */
+ 'summary' => sprintf(_n('%d old integration setting found.', '%d old integration settings found.', $count, 'gpt3-ai-content-generator'), $count),
+ 'details' => $details
+ ];
+}
--- a/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-options-details.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-options-details.php
@@ -0,0 +1,59 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-options-details.php
+// Status: MODIFIED
+// I have added old WooCommerce settings options to the list of options to be analyzed and deleted.
+
+namespace WPAICGAdminAjaxMigrationAnalysis;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Gets details about old options and the legacy `wpaicg` table.
+ *
+ * @return array ['count' => int, 'details' => array]
+ */
+function get_old_options_details_logic(): array
+{
+ $count = 0;
+ $details = [];
+ $old_options = [
+ 'wpaicg_options', 'wpaicg_provider', 'wpaicg_chat_widget', 'wpaicg_module_settings',
+ 'wpaicg_version', 'wpaicg_openai_api_key', 'wpaicg_azure_api_key', 'wpaicg_azure_endpoint',
+ 'wpaicg_azure_deployment', 'wpaicg_google_model_api_key', 'wpaicg_google_default_model',
+ 'wpaicg_openrouter_api_key', 'wpaicg_openrouter_default_model', 'wpaicg_deepseek_api_key',
+ 'wpaicg_elevenlabs_api', 'wpaicg_pinecone_api', 'wpaicg_qdrant_api_key', 'wpaicg_qdrant_endpoint',
+ 'wpaicg_image_setting_provider', 'wpaicg_image_setting_openai_model', 'wpaicg_image_setting_openai_size',
+ 'wpaicg_image_setting_openai_quality', 'wpaicg_image_setting_openai_style', 'wpaicg_image_setting_openai_n',
+ 'wpaicg_image_setting_azure_model', 'wpaicg_image_setting_azure_size', 'wpaicg_image_setting_azure_n',
+ 'wpaicg_image_setting_google_model', 'wpaicg_image_setting_google_size', 'wpaicg_image_setting_google_n',
+ 'wpaicg_chat_shortcode_options', 'wpaicg_banned_words', 'wpaicg_banned_ips',
+ 'wpaicg_editor_button_menus', 'wpaicg_editor_change_action',
+ 'wpaicg_woo_generate_title', 'wpaicg_woo_generate_description', 'wpaicg_woo_generate_short',
+ 'wpaicg_woo_generate_tags', 'wpaicg_woo_meta_description', '_wpaicg_shorten_woo_url',
+ 'wpaicg_generate_woo_focus_keyword', 'wpaicg_enforce_woo_keyword_in_url', 'wpaicg_woo_custom_prompt',
+ 'wpaicg_order_status_token'
+ ];
+ foreach ($old_options as $option_name) {
+ if (get_option($option_name) !== false) {
+ $count++;
+ /* translators: %s is the option name */
+ $details[] = sprintf(__('Found option: %s', 'gpt3-ai-content-generator'), $option_name);
+ }
+ }
+
+ // Check for the old wpaicg table by passing its name in an array
+ $legacy_table_result = table_exists_and_has_rows_logic(['wpaicg']);
+ if ($legacy_table_result['count'] > 0) {
+ $count += $legacy_table_result['count'];
+ $details = array_merge($details, $legacy_table_result['details']);
+ }
+
+ return [
+ 'count' => $count,
+ 'summary' => sprintf('%d legacy settings found (options and/or database table entries).', $count),
+ 'details' => $details
+ ];
+}
--- a/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-woocommerce-prompts.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-woocommerce-prompts.php
@@ -0,0 +1,52 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/analysis/get-old-woocommerce-prompts.php
+// Status: NEW FILE
+
+namespace WPAICGAdminAjaxMigrationAnalysis;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Gets details about old WooCommerce custom prompts from the options table.
+ *
+ * @return array ['count' => int, 'prompts' => array, 'summary' => string, 'details' => array]
+ */
+function get_old_woocommerce_prompts_logic(): array
+{
+ $prompts = [];
+ $count = 0;
+ $details = [];
+
+ $woo_prompts_options = [
+ 'wpaicg_woo_custom_prompt_title' => __('WooCommerce Title Prompt', 'gpt3-ai-content-generator'),
+ 'wpaicg_woo_custom_prompt_short' => __('WooCommerce Short Description Prompt', 'gpt3-ai-content-generator'),
+ 'wpaicg_woo_custom_prompt_description' => __('WooCommerce Full Description Prompt', 'gpt3-ai-content-generator'),
+ 'wpaicg_woo_custom_prompt_meta' => __('WooCommerce Meta Description Prompt', 'gpt3-ai-content-generator'),
+ 'wpaicg_woo_custom_prompt_keywords' => __('WooCommerce Tags Prompt', 'gpt3-ai-content-generator'),
+ 'wpaicg_woo_custom_prompt_focus_keyword' => __('WooCommerce Focus Keyword Prompt', 'gpt3-ai-content-generator'),
+ ];
+
+ foreach ($woo_prompts_options as $option_name => $label) {
+ $prompt_value = get_option($option_name, '');
+ if (!empty($prompt_value)) {
+ $prompts[$option_name] = [
+ 'label' => $label,
+ 'value' => wp_unslash($prompt_value)
+ ];
+ $count++;
+ $details[] = 'WooCommerce Prompt found: ' . $label;
+ }
+ }
+ /* translators: %d is the number of custom WooCommerce prompts found */
+ $summary = sprintf(_n('%d custom WooCommerce prompt found.', '%d custom WooCommerce prompts found.', $count, 'gpt3-ai-content-generator'), $count);
+
+ return [
+ 'count' => $count,
+ 'prompts' => $prompts,
+ 'summary' => $summary,
+ 'details' => $details
+ ];
+}
--- a/gpt3-ai-content-generator/admin/ajax/migration/analysis/handle-analysis-request.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/analysis/handle-analysis-request.php
@@ -0,0 +1,112 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/analysis/handle-analysis-request.php
+// Status: MODIFIED
+// I have updated this file to include the new woocommerce prompts analysis step.
+
+namespace WPAICGAdminAjaxMigrationAnalysis;
+
+use WPAICGAdminAjaxMigrationAIPKit_Migration_Base_Ajax_Action;
+use WPAICGWP_AI_Content_Generator_Activator;
+use WP_Error;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+// Load other analysis files
+require_once __DIR__ . '/get-old-options-details.php';
+require_once __DIR__ . '/count-cpt-posts.php';
+require_once __DIR__ . '/table-exists-and-has-rows.php';
+require_once __DIR__ . '/get-old-custom-prompts.php';
+require_once __DIR__ . '/get-old-integration-data.php';
+require_once __DIR__ . '/get-old-woocommerce-prompts.php';
+
+/**
+ * Main logic function for handling the data analysis request.
+ * Called by AIPKit_Analyze_Old_Data_Action.
+ *
+ * @param AIPKit_Migration_Base_Ajax_Action $handlerInstance The instance of the action class.
+ * @return void Sends JSON response.
+ */
+function handle_analysis_request_logic(AIPKit_Migration_Base_Ajax_Action $handlerInstance): void
+{
+ $permission_check = $handlerInstance->check_module_access_permissions('settings', $handlerInstance::MIGRATION_NONCE_ACTION);
+ if (is_wp_error($permission_check)) {
+ $handlerInstance->send_wp_error($permission_check);
+ return;
+ }
+
+ $analysis_results = [];
+
+ try {
+ // --- 1. Global, Provider & API Settings ---
+ $analysis_results['global_settings'] = get_old_options_details_logic();
+
+ // --- 2. AI Forms & Other Data ---
+ // This category now covers legacy CPTs and associated tables.
+ // Migration will only handle AI Forms; deletion will handle everything else.
+ $cpt_data_cpts = ['wpaicg_mtemplate', 'wpaicg_file', 'wpaicg_finetune', 'wpaicg_form'];
+ $cpt_data_posts_result = count_cpt_posts_logic($cpt_data_cpts);
+ // Includes form tables and old image log tables for cleanup
+ $cpt_data_tables = ['wpaicg_form_logs', 'wpaicg_form_feedback', 'wpaicg_formtokens', 'wpaicg_image_logs', 'wpaicg_imagetokens'];
+ $cpt_data_tables_result = table_exists_and_has_rows_logic($cpt_data_tables);
+ $total_cpt_data_count = $cpt_data_posts_result['count'] + $cpt_data_tables_result['count'];
+ $analysis_results['cpt_data'] = [
+ 'count' => $total_cpt_data_count,
+ 'summary' => sprintf(
+ '%s, %s',
+ $cpt_data_posts_result['summary'],
+ $cpt_data_tables_result['summary']
+ ),
+ 'details' => array_merge($cpt_data_posts_result['details'], $cpt_data_tables_result['details'])
+ ];
+
+ // --- 3. Chatbots ---
+ $chatbot_cpt_result = count_cpt_posts_logic(['wpaicg_chatbot']);
+ $chatbot_tables_result = table_exists_and_has_rows_logic(['wpaicg_chatlogs', 'wpaicg_chattokens']);
+ $total_chatbot_count = $chatbot_cpt_result['count'] + $chatbot_tables_result['count'];
+ $analysis_results['chatbot_data'] = [
+ 'count' => $total_chatbot_count,
+ 'summary' => sprintf(
+ '%s, %s',
+ $chatbot_cpt_result['summary'],
+ $chatbot_tables_result['summary']
+ ),
+ 'details' => array_merge($chatbot_cpt_result['details'], $chatbot_tables_result['details'])
+ ];
+
+ // --- 4. Indexed Data (Knowledge Base) ---
+ $indexed_data_cpts = ['wpaicg_embeddings', 'wpaicg_pdfadmin', 'wpaicg_builder'];
+ $indexed_data_result = count_cpt_posts_logic($indexed_data_cpts);
+ $analysis_results['indexed_data'] = [
+ 'count' => $indexed_data_result['count'],
+ 'summary' => $indexed_data_result['summary'],
+ 'details' => $indexed_data_result['details']
+ ];
+
+ // --- 5. Custom Prompts ---
+ $analysis_results['custom_prompts'] = get_old_custom_prompts_logic();
+
+ // --- 6. Integration Data ---
+ $analysis_results['integration_data'] = get_old_integration_data_logic();
+
+ // --- 7. WooCommerce Custom Prompts ---
+ $analysis_results['woocommerce_prompts'] = get_old_woocommerce_prompts_logic();
+
+
+ // Update status and save results
+ update_option(WP_AI_Content_Generator_Activator::MIGRATION_ANALYSIS_RESULTS_OPTION, $analysis_results, 'no');
+ update_option(WP_AI_Content_Generator_Activator::MIGRATION_STATUS_OPTION, 'analysis_complete', 'no');
+
+ wp_send_json_success([
+ 'message' => __('Analysis complete.', 'gpt3-ai-content-generator'),
+ 'analysis_data' => $analysis_results,
+ ]);
+
+ } catch (Exception $e) {
+ $error_message = 'Analysis failed: ' . $e->getMessage();
+ update_option(WP_AI_Content_Generator_Activator::MIGRATION_LAST_ERROR_OPTION, $error_message, 'no');
+ $handlerInstance->send_wp_error(new WP_Error('analysis_exception', $error_message, ['status' => 500]));
+ }
+}
--- a/gpt3-ai-content-generator/admin/ajax/migration/analysis/table-exists-and-has-rows.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/analysis/table-exists-and-has-rows.php
@@ -0,0 +1,43 @@
+<?php
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/analysis/table-exists-and-has-rows.php
+// Status: MODIFIED
+
+namespace WPAICGAdminAjaxMigrationAnalysis;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+/**
+ * Checks if a list of database tables exist and if they have rows.
+ *
+ * @param array $table_names An array of table names (without prefix).
+ * @return array ['count' => int, 'summary' => string, 'details' => array] The count is the number of tables with rows.
+ */
+function table_exists_and_has_rows_logic(array $table_names): array
+{
+ global $wpdb;
+ $tables_with_rows_count = 0;
+ $details = [];
+
+ foreach ($table_names as $table_name) {
+ $full_table_name = $wpdb->prefix . $table_name;
+ if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $full_table_name)) === $full_table_name) {
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- This is a read-only analysis tool.
+ $row_count = $wpdb->get_var("SELECT COUNT(*) FROM " . esc_sql($full_table_name));
+ if ($row_count > 0) {
+ $tables_with_rows_count++;
+ // translators: %1$d is the number of rows, %2$s is the table name.
+ $details[] = sprintf(__('%1$d rows found in table "%2$s".', 'gpt3-ai-content-generator'), $row_count, $full_table_name);
+ }
+ }
+ }
+
+ $summary = sprintf(
+ // translators: %d is the number of legacy database tables with data found.
+ _n('%d legacy database table with data found.', '%d legacy database tables with data found.', $tables_with_rows_count, 'gpt3-ai-content-generator'),
+ $tables_with_rows_count
+ );
+
+ return ['count' => $tables_with_rows_count, 'summary' => $summary, 'details' => $details];
+}
No newline at end of file
--- a/gpt3-ai-content-generator/admin/ajax/migration/class-aipkit-analyze-old-data-action.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/class-aipkit-analyze-old-data-action.php
@@ -0,0 +1,28 @@
+<?php
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/class-aipkit-analyze-old-data-action.php
+// Status: MODIFIED
+
+namespace WPAICGAdminAjaxMigration;
+
+if (!defined('ABSPATH')) {
+ exit; // Exit if accessed directly
+}
+
+// Load the main logic for this action.
+require_once __DIR__ . '/analysis/handle-analysis-request.php';
+
+/**
+ * Handles the AJAX action for analyzing old data.
+ * This class now acts as a simple orchestrator, delegating the main logic.
+ */
+class AIPKit_Analyze_Old_Data_Action extends AIPKit_Migration_Base_Ajax_Action
+{
+ /**
+ * Handles the AJAX request by calling the externalized logic function.
+ */
+ public function handle_request()
+ {
+ // The namespaced function will handle permissions, logic, and response sending.
+ WPAICGAdminAjaxMigrationAnalysishandle_analysis_request_logic($this);
+ }
+}
No newline at end of file
--- a/gpt3-ai-content-generator/admin/ajax/migration/class-aipkit-migration-base-ajax-action.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/class-aipkit-migration-base-ajax-action.php
@@ -0,0 +1,61 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/class-aipkit-migration-base-ajax-action.php
+// Status: MODIFIED
+
+namespace WPAICGAdminAjaxMigration;
+
+use WPAICGDashboardAjaxBaseDashboardAjaxHandler;
+use WPAICGWP_AI_Content_Generator_Activator; // For MIGRATION_LAST_ERROR_OPTION
+use WP_Error;
+
+if (!defined('ABSPATH')) {
+ exit; // Exit if accessed directly
+}
+
+/**
+ * Base class for Migration Tool AJAX actions.
+ */
+abstract class AIPKit_Migration_Base_Ajax_Action extends BaseDashboardAjaxHandler
+{
+ public const MIGRATION_NONCE_ACTION = 'aipkit_migration_tool_action'; // Consistent nonce
+
+ public function __construct()
+ {
+ // Constructor can be empty or initialize common dependencies for migration actions.
+ // For now, BaseDashboardAjaxHandler handles nonce/permission checks based on this constant.
+ }
+
+ /**
+ * Abstract method to be implemented by child classes to handle the specific AJAX request.
+ */
+ abstract public function handle_request();
+
+ /**
+ * Updates the status of a specific migration category.
+ * @param string $category_key The key for the category (e.g., 'global_settings').
+ * @param string $status The new status (e.g., 'completed', 'deleted', 'failed').
+ */
+ protected function update_category_status(string $category_key, string $status): void
+ {
+ $category_statuses = get_option(WP_AI_Content_Generator_Activator::MIGRATION_CATEGORY_STATUS_OPTION, []);
+ $category_statuses[$category_key] = $status;
+ update_option(WP_AI_Content_Generator_Activator::MIGRATION_CATEGORY_STATUS_OPTION, $category_statuses, 'no');
+ }
+
+ /**
+ * A unified way to handle exceptions during migration/deletion actions.
+ * @param Exception $e The exception object.
+ * @param string $error_code The WP_Error code to use.
+ * @param string $category_key The key of the category that failed.
+ */
+ protected function handle_exception(Exception $e, string $error_code, string $category_key = ''): void
+ {
+ $error_message = 'Error during migration: ' . $e->getMessage();
+ update_option(WP_AI_Content_Generator_Activator::MIGRATION_LAST_ERROR_OPTION, $error_message, 'no');
+ if (!empty($category_key)) {
+ $this->update_category_status($category_key, 'failed');
+ }
+ $this->send_wp_error(new WP_Error($error_code, $error_message), 500);
+ }
+}
--- a/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-chatbot-data-action.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-chatbot-data-action.php
@@ -0,0 +1,76 @@
+<?php
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-chatbot-data-action.php
+// Status: NEW FILE
+
+namespace WPAICGAdminAjaxMigrationDelete;
+
+use WPAICGAdminAjaxMigrationAIPKit_Migration_Base_Ajax_Action;
+use WPAICGWP_AI_Content_Generator_Activator;
+use WP_Query;
+use WP_Error;
+
+if (!defined('ABSPATH')) {
+ exit; // Exit if accessed directly
+}
+
+/**
+ * Handles the AJAX action for deleting old Chatbot data.
+ */
+class AIPKit_Delete_Old_Chatbot_Data_Action extends AIPKit_Migration_Base_Ajax_Action
+{
+ public function handle_request()
+ {
+ $permission_check = $this->check_module_access_permissions('settings', self::MIGRATION_NONCE_ACTION);
+ if (is_wp_error($permission_check)) {
+ $this->send_wp_error($permission_check);
+ return;
+ }
+
+ global $wpdb;
+ $deleted_counts = ['cpt_posts' => 0, 'tables' => 0];
+
+ try {
+ // Delete CPT posts
+ $cpt_slug = 'wpaicg_chatbot';
+ $old_bots_query = new WP_Query([
+ 'post_type' => $cpt_slug,
+ 'post_status' => 'any',
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ 'no_found_rows' => true,
+ 'update_post_meta_cache' => false,
+ 'update_post_term_cache' => false,
+ ]);
+ if ($old_bots_query->have_posts()) {
+ foreach ($old_bots_query->posts as $post_id) {
+ if (wp_delete_post($post_id, true)) {
+ $deleted_counts['cpt_posts']++;
+ }
+ }
+ }
+
+ // Drop old tables
+ $old_tables = ['wpaicg_chatlogs', 'wpaicg_chattokens'];
+ foreach ($old_tables as $table_suffix) {
+ $table_name = $wpdb->prefix . $table_suffix;
+ if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) === $table_name) {
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+ $wpdb->query("DROP TABLE IF EXISTS " . esc_sql($table_name));
+ $deleted_counts['tables']++;
+ }
+ }
+
+ // Update category status
+ $this->update_category_status('chatbot_data', 'deleted');
+
+ wp_send_json_success([
+ /* translators: %1$d is the number of posts, %2$d is the number of tables */
+ 'message' => sprintf(__('Old chatbot data deleted: %1$d posts and %2$d database tables removed.', 'gpt3-ai-content-generator'), $deleted_counts['cpt_posts'], $deleted_counts['tables']),
+ 'category_status' => 'deleted'
+ ]);
+
+ } catch (Exception $e) {
+ $this->handle_exception($e, 'chatbot_data_deletion_failed', 'chatbot_data');
+ }
+ }
+}
No newline at end of file
--- a/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-cpt-data-action.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-cpt-data-action.php
@@ -0,0 +1,81 @@
+<?php
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-cpt-data-action.php
+// Status: MODIFIED
+
+namespace WPAICGAdminAjaxMigrationDelete;
+
+use WPAICGAdminAjaxMigrationAIPKit_Migration_Base_Ajax_Action;
+use WPAICGWP_AI_Content_Generator_Activator;
+use WP_Query;
+use WP_Error;
+
+if (!defined('ABSPATH')) {
+ exit; // Exit if accessed directly
+}
+
+/**
+ * Handles the AJAX action for deleting old CPT and AI Forms data.
+ */
+class AIPKit_Delete_Old_CPT_Data_Action extends AIPKit_Migration_Base_Ajax_Action
+{
+ public function handle_request()
+ {
+ $permission_check = $this->check_module_access_permissions('settings', self::MIGRATION_NONCE_ACTION);
+ if (is_wp_error($permission_check)) {
+ $this->send_wp_error($permission_check);
+ return;
+ }
+
+ global $wpdb;
+ $deleted_counts = ['cpt_posts' => 0, 'tables' => 0];
+ $old_cpts_to_delete = [
+ 'wpaicg_mtemplate', 'wpaicg_pdfadmin', 'wpaicg_file',
+ 'wpaicg_finetune', 'wpaicg_form'
+ ];
+
+ try {
+ // Delete CPT posts
+ foreach ($old_cpts_to_delete as $cpt_slug) {
+ $query = new WP_Query([
+ 'post_type' => $cpt_slug,
+ 'post_status' => 'any',
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ 'no_found_rows' => true,
+ 'update_post_meta_cache' => false,
+ 'update_post_term_cache' => false,
+ ]);
+ if ($query->have_posts()) {
+ foreach ($query->posts as $post_id) {
+ if (wp_delete_post($post_id, true)) {
+ $deleted_counts['cpt_posts']++;
+ }
+ }
+ }
+ }
+
+ // Drop old AI Forms tables
+ $old_tables = ['wpaicg_form_logs', 'wpaicg_form_feedback', 'wpaicg_formtokens'];
+ foreach ($old_tables as $table_suffix) {
+ $table_name = $wpdb->prefix . $table_suffix;
+ if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) === $table_name) {
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+ $wpdb->query("DROP TABLE IF EXISTS " . esc_sql($table_name));
+ $deleted_counts['tables']++;
+ }
+ }
+
+ // Update category status
+ $this->update_category_status('cpt_data', 'deleted');
+
+ wp_send_json_success([
+ /* translators: %1$d is the number of posts, %2$d is the number of tables */
+ 'message' => sprintf(__('Old AI Forms & associated legacy data deleted: %1$d posts and %2$d database tables removed.', 'gpt3-ai-content-generator'), $deleted_counts['cpt_posts'], $deleted_counts['tables']),
+ 'category_status' => 'deleted'
+ ]);
+
+ } catch (Exception $e) {
+ $this->handle_exception($e, 'cpt_data_deletion_failed', 'cpt_data');
+ }
+ }
+}
No newline at end of file
--- a/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-global-settings-action.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-global-settings-action.php
@@ -0,0 +1,85 @@
+<?php
+
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-global-settings-action.php
+// Status: MODIFIED
+// I have added the old WooCommerce settings options to the list of options to be deleted.
+
+namespace WPAICGAdminAjaxMigrationDelete;
+
+use WPAICGAdminAjaxMigrationAIPKit_Migration_Base_Ajax_Action;
+use WPAICGWP_AI_Content_Generator_Activator;
+use WP_Error;
+
+if (!defined('ABSPATH')) {
+ exit; // Exit if accessed directly
+}
+
+/**
+ * Handles the AJAX action for deleting old Global Settings data.
+ */
+class AIPKit_Delete_Old_Global_Settings_Action extends AIPKit_Migration_Base_Ajax_Action
+{
+ public function handle_request()
+ {
+ $permission_check = $this->check_module_access_permissions('settings', self::MIGRATION_NONCE_ACTION);
+ if (is_wp_error($permission_check)) {
+ $this->send_wp_error($permission_check);
+ return;
+ }
+
+ global $wpdb;
+ $deleted_counts = ['options' => 0, 'tables' => 0];
+
+ try {
+ // Delete old options
+ $old_options = [
+ 'wpaicg_options', 'wpaicg_provider', 'wpaicg_chat_widget', 'wpaicg_module_settings',
+ 'wpaicg_version', 'wpaicg_openai_api_key', 'wpaicg_azure_api_key', 'wpaicg_azure_endpoint',
+ 'wpaicg_azure_deployment', 'wpaicg_google_model_api_key', 'wpaicg_google_default_model',
+ 'wpaicg_openrouter_api_key', 'wpaicg_openrouter_default_model', 'wpaicg_deepseek_api_key',
+ 'wpaicg_elevenlabs_api', 'wpaicg_pinecone_api', 'wpaicg_qdrant_api_key', 'wpaicg_qdrant_endpoint',
+ 'wpaicg_image_setting_provider', 'wpaicg_image_setting_openai_model', 'wpaicg_image_setting_openai_size',
+ 'wpaicg_image_setting_openai_quality', 'wpaicg_image_setting_openai_style', 'wpaicg_image_setting_openai_n',
+ 'wpaicg_image_setting_azure_model', 'wpaicg_image_setting_azure_size', 'wpaicg_image_setting_azure_n',
+ 'wpaicg_image_setting_google_model', 'wpaicg_image_setting_google_size', 'wpaicg_image_setting_google_n',
+ 'wpaicg_chat_shortcode_options', 'wpaicg_banned_words', 'wpaicg_banned_ips',
+ 'wpaicg_ai_model', 'wpaicg_custom_models', 'wpaicg_google_safety_settings', 'wpaicg_sleep_time',
+ 'wpaicg_openai_model_list', 'wpaicg_openrouter_model_list', 'wpaicg_google_model_list',
+ 'wpaicg_limit_tokens_form', // Old AI Forms token settings
+ 'wpaicg_editor_button_menus',
+ 'wpaicg_editor_change_action',
+ 'wpaicg_woo_generate_title', 'wpaicg_woo_generate_description', 'wpaicg_woo_generate_short',
+ 'wpaicg_woo_generate_tags', 'wpaicg_woo_meta_description', '_wpaicg_shorten_woo_url',
+ 'wpaicg_generate_woo_focus_keyword', 'wpaicg_enforce_woo_keyword_in_url', 'wpaicg_woo_custom_prompt',
+ 'wpaicg_order_status_token'
+ ];
+ foreach ($old_options as $option_name) {
+ if (get_option($option_name) !== false) {
+ if (delete_option($option_name)) {
+ $deleted_counts['options']++;
+ }
+ }
+ }
+
+ // Drop old settings table
+ $old_table_name = $wpdb->prefix . 'wpaicg';
+ if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $old_table_name)) === $old_table_name) {
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+ $wpdb->query("DROP TABLE IF EXISTS " . esc_sql($old_table_name));
+ $deleted_counts['tables']++;
+ }
+
+ // Update category status
+ $this->update_category_status('global_settings', 'deleted');
+
+ wp_send_json_success([
+ /* translators: %1$d is the number of options, %2$d is the number of tables */
+ 'message' => sprintf(__('Old global settings deleted: %1$d options and %2$d database tables removed.', 'gpt3-ai-content-generator'), $deleted_counts['options'], $deleted_counts['tables']),
+ 'category_status' => 'deleted'
+ ]);
+
+ } catch (Exception $e) {
+ $this->handle_exception($e, 'global_settings_deletion_failed', 'global_settings');
+ }
+ }
+}
--- a/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-indexed-data-action.php
+++ b/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-indexed-data-action.php
@@ -0,0 +1,61 @@
+<?php
+// File: /Applications/MAMP/htdocs/wordpress/wp-content/plugins/gpt3-ai-content-generator/admin/ajax/migration/delete/class-aipkit-delete-old-indexed-data-action.php
+// Status: NEW FILE
+
+namespace WPAICGAdminAjaxMigrationDelete;