最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use AJAX in a widget only on the admin backend? - Stack Overflow

programmeradmin1浏览0评论

I'm trying to create a simple WordPress widget for AI text generation. The widget should allow the user to type a prompt, set a word limit, and click a "Generate Text" button. I want to use the OpenAI API to generate text based on the prompt and word limit. However, I'm facing an issue with how the widget behaves depending on where it's loaded.

Problem:

The AI text generation works fine when all the options (form) are loaded on the WordPress front page (working example in CODE-1 below). But I want the widget to only be available in the backend (as part of the widget options).

So, my goal is to make the AI text generator available only in the backend for editing widgets, and have the API generate text and insert it into the widget's text area. The main issue is that AJAX doesn’t seem to work properly within widget options, and I’m struggling to find a solution.

What I want to achieve:

  • Create a widget in the WordPress backend.
  • Use the OpenAI API to generate text based on user input (prompt and word limit).
  • Insert the generated text into the widget text area.

Questions:

How can I make AJAX work properly inside the WordPress widget options in the backend? Is there a recommended approach for integrating OpenAI API text generation with a WordPress widget in the backend? I’m not sure what else to check or why API requests can't be handled through the backend widget options. I tested AI text generation on a backend page (not widgets), and it works fine there. The issue is only with the widgets, and I can’t figure out why it’s not working. There are no errors in the console, nothing—it just doesn’t work in the widget.

Any help would be greatly appreciated!

CODE-1 (works OK if loaded as front-end widget):

<?php
/*
Plugin Name: OpenAI Widget
Description: A simple WordPress widget to generate text using OpenAI GPT-3.5 API.
Version: 1.0
Author: WP
*/

class OpenAI_Widget extends WP_Widget {

    public function __construct() {
        parent::__construct(
            'openai_widget',
            __('OpenAI Text Generator', 'openai_widget_domain'),
            array('description' => __('Generate text using OpenAI GPT-3.5', 'openai_widget_domain'))
        );
    }

    public function widget($args, $instance) {
        echo $args['before_widget'];
        ?>
        <div class="openai-widget">
            <label for="openai_prompt">Prompt:</label>
            <textarea id="openai_prompt" rows="3"></textarea>

            <label for="openai_word_limit">Word Limit:</label>
            <input type="number" id="openai_word_limit" value="100" min="1" max="500"/>

            <button id="openai_generate">Generate Text</button>

            <label for="openai_output">Output:</label>
            <textarea id="openai_output" rows="5" readonly></textarea>
        </div>

        <script>
document.addEventListener("DOMContentLoaded", function() {
    document.getElementById("openai_generate").addEventListener("click", function() {
        let prompt = document.getElementById("openai_prompt").value.trim();
        let wordLimit = parseInt(document.getElementById("openai_word_limit").value);

        if (prompt.length === 0) {
            alert("Please enter a prompt.");
            return;
        }

        fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: `action=openai_generate_text&prompt=${encodeURIComponent(prompt)}&word_limit=${wordLimit}`
        })
        .then(response => response.json())
        .then(data => {
            console.log(data);  // Debugging: log the response data

            // Check if the response is as expected
            if (data.success) {
                const output = document.getElementById("openai_output");
                if (output) {
                    output.value = data.data.text || "No text generated.";  // Access text inside `data.data.text`
                } else {
                    console.error('Output textarea not found.');
                }
            } else {
                const output = document.getElementById("openai_output");
                if (output) {
                    output.value = "Error: " + (data.message || "Unknown error");
                } else {
                    console.error('Output textarea not found.');
                }
            }
        })
        .catch(error => {
            console.error("Error:", error);
            const output = document.getElementById("openai_output");
            if (output) {
                output.value = "Error: Unable to reach API or process request.";
            }
        });
    });
});


        </script>
        <?php
        echo $args['after_widget'];
    }

    public function form($instance) {
        echo '<p>No settings required.</p>';
    }

    public function update($new_instance, $old_instance) {
        return $new_instance;
    }
}

function register_openai_widget() {
    register_widget('OpenAI_Widget');
}
add_action('widgets_init', 'register_openai_widget');

function openai_generate_text() {
    if (!isset($_POST['prompt']) || !isset($_POST['word_limit'])) {
        wp_send_json_error(['message' => 'Invalid request']);
    }

    $api_key = 'MY-API-KEY'; // Replace with your OpenAI API key
    $prompt = sanitize_text_field($_POST['prompt']);
    $word_limit = max(1, min((int) $_POST['word_limit'], 500));

    // OpenAI tokens are ~4 per word, but we cap them to 2048 to prevent long responses
    $max_tokens = min($word_limit * 4, 2048);

    // Updated API endpoint for GPT-3.5 Turbo
    $response = wp_remote_post('', [
        'headers' => [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ],
        'body' => json_encode([
            'model'       => 'gpt-3.5-turbo',
            'messages'    => [
                ['role' => 'system', 'content' => 'You are a helpful assistant.'],
                ['role' => 'user', 'content' => $prompt],
            ],
            'max_tokens'  => $max_tokens,
            'temperature' => 0.7,
            'top_p'       => 1.0,
        ]),
    ]);

    if (is_wp_error($response)) {
        wp_send_json_error(['message' => 'API request failed: ' . $response->get_error_message()]);
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);

    // Debugging: Log the full response body to see the structure of the response
    error_log('OpenAI Response: ' . print_r($body, true));

    // Check if the response has the correct structure
    if (isset($body['choices'][0]['message']['content'])) {
        wp_send_json_success(['text' => trim($body['choices'][0]['message']['content'])]);
    } else {
        wp_send_json_error(['message' => 'Error from OpenAI: ' . json_encode($body)]);
    }
}
add_action('wp_ajax_openai_generate_text', 'openai_generate_text');
add_action('wp_ajax_nopriv_openai_generate_text', 'openai_generate_text');
?>

CODE-2 (loaded in widgets backend options - not working):

<?php
/*
Plugin Name: OpenAI Widget - backend options
Description: A simple WordPress widget to generate text using OpenAI GPT-3.5 API.
Version: 1.0
Author: WP
*/

class OpenAI_Widget extends WP_Widget {

    public function __construct() {
        parent::__construct(
            'openai_widget',
            __('OpenAI Text Generator', 'openai_widget_domain'),
            array('description' => __('Generate text using OpenAI GPT-3.5', 'openai_widget_domain'))
        );
    }

    public function widget($args, $instance) {
        // On the front-end, just show the widget without any fields or outputs.
        echo $args['before_widget'];
        echo $args['after_widget'];
    }

    public function form($instance) {
        // Backend form
        $prompt = !empty($instance['prompt']) ? $instance['prompt'] : '';
        $word_limit = !empty($instance['word_limit']) ? $instance['word_limit'] : 100;
        ?>
        <p>
            <label for="<?php echo $this->get_field_id('prompt'); ?>"><?php _e('Prompt:'); ?></label>
            <textarea class="widefat" id="<?php echo $this->get_field_id('prompt'); ?>" name="<?php echo $this->get_field_name('prompt'); ?>" rows="3"><?php echo esc_attr($prompt); ?></textarea>
        </p>
        <p>
            <label for="<?php echo $this->get_field_id('word_limit'); ?>"><?php _e('Word Limit:'); ?></label>
            <input class="widefat" id="<?php echo $this->get_field_id('word_limit'); ?>" name="<?php echo $this->get_field_name('word_limit'); ?>" type="number" value="<?php echo esc_attr($word_limit); ?>" min="1" max="500" />
        </p>
        <p>
            <!-- Button in backend -->
            <button class="widefat" type="button" id="generate_openai_backend_text">Generate Text (Backend Only)</button>
        </p>
        <div id="openai_backend_output_area" style="display:none;">
            <h4>Generated Text:</h4>
            <textarea id="openai_backend_output" rows="5" readonly></textarea>
        </div>

        <?php
    }

    public function update($new_instance, $old_instance) {
        // Save the widget options
        $instance = array();
        $instance['prompt'] = !empty($new_instance['prompt']) ? sanitize_text_field($new_instance['prompt']) : '';
        $instance['word_limit'] = !empty($new_instance['word_limit']) ? intval($new_instance['word_limit']) : 100;
        return $instance;
    }
}

function register_openai_widget() {
    register_widget('OpenAI_Widget');
}
add_action('widgets_init', 'register_openai_widget');

// Handle the AJAX request for text generation
function openai_generate_text() {
    if (!isset($_POST['prompt']) || !isset($_POST['word_limit'])) {
        wp_send_json_error(['message' => 'Invalid request']);
    }

    $api_key = 'MY-API-KEY'; // Replace with your OpenAI API key
    $prompt = sanitize_text_field($_POST['prompt']);
    $word_limit = max(1, min((int) $_POST['word_limit'], 500));

    // OpenAI tokens are ~4 per word, but we cap them to 2048 to prevent long responses
    $max_tokens = min($word_limit * 4, 2048);

    // Updated API endpoint for GPT-3.5 Turbo
    $response = wp_remote_post('', [
        'headers' => [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ],
        'body' => json_encode([
            'model'       => 'gpt-3.5-turbo',
            'messages'    => [
                ['role' => 'system', 'content' => 'You are a helpful assistant.'],
                ['role' => 'user', 'content' => $prompt],
            ],
            'max_tokens'  => $max_tokens,
            'temperature' => 0.7,
            'top_p'       => 1.0,
        ]),
    ]);

    if (is_wp_error($response)) {
        wp_send_json_error(['message' => 'API request failed: ' . $response->get_error_message()]);
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);

    // Check if the response has the correct structure
    if (isset($body['choices'][0]['message']['content'])) {
        wp_send_json_success(['text' => trim($body['choices'][0]['message']['content'])]);
    } else {
        wp_send_json_error(['message' => 'Error from OpenAI: ' . json_encode($body)]);
    }
}
add_action('wp_ajax_openai_generate_text', 'openai_generate_text');
add_action('wp_ajax_nopriv_openai_generate_text', 'openai_generate_text');
?>

I'm trying to create a simple WordPress widget for AI text generation. The widget should allow the user to type a prompt, set a word limit, and click a "Generate Text" button. I want to use the OpenAI API to generate text based on the prompt and word limit. However, I'm facing an issue with how the widget behaves depending on where it's loaded.

Problem:

The AI text generation works fine when all the options (form) are loaded on the WordPress front page (working example in CODE-1 below). But I want the widget to only be available in the backend (as part of the widget options).

So, my goal is to make the AI text generator available only in the backend for editing widgets, and have the API generate text and insert it into the widget's text area. The main issue is that AJAX doesn’t seem to work properly within widget options, and I’m struggling to find a solution.

What I want to achieve:

  • Create a widget in the WordPress backend.
  • Use the OpenAI API to generate text based on user input (prompt and word limit).
  • Insert the generated text into the widget text area.

Questions:

How can I make AJAX work properly inside the WordPress widget options in the backend? Is there a recommended approach for integrating OpenAI API text generation with a WordPress widget in the backend? I’m not sure what else to check or why API requests can't be handled through the backend widget options. I tested AI text generation on a backend page (not widgets), and it works fine there. The issue is only with the widgets, and I can’t figure out why it’s not working. There are no errors in the console, nothing—it just doesn’t work in the widget.

Any help would be greatly appreciated!

CODE-1 (works OK if loaded as front-end widget):

<?php
/*
Plugin Name: OpenAI Widget
Description: A simple WordPress widget to generate text using OpenAI GPT-3.5 API.
Version: 1.0
Author: WP
*/

class OpenAI_Widget extends WP_Widget {

    public function __construct() {
        parent::__construct(
            'openai_widget',
            __('OpenAI Text Generator', 'openai_widget_domain'),
            array('description' => __('Generate text using OpenAI GPT-3.5', 'openai_widget_domain'))
        );
    }

    public function widget($args, $instance) {
        echo $args['before_widget'];
        ?>
        <div class="openai-widget">
            <label for="openai_prompt">Prompt:</label>
            <textarea id="openai_prompt" rows="3"></textarea>

            <label for="openai_word_limit">Word Limit:</label>
            <input type="number" id="openai_word_limit" value="100" min="1" max="500"/>

            <button id="openai_generate">Generate Text</button>

            <label for="openai_output">Output:</label>
            <textarea id="openai_output" rows="5" readonly></textarea>
        </div>

        <script>
document.addEventListener("DOMContentLoaded", function() {
    document.getElementById("openai_generate").addEventListener("click", function() {
        let prompt = document.getElementById("openai_prompt").value.trim();
        let wordLimit = parseInt(document.getElementById("openai_word_limit").value);

        if (prompt.length === 0) {
            alert("Please enter a prompt.");
            return;
        }

        fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: `action=openai_generate_text&prompt=${encodeURIComponent(prompt)}&word_limit=${wordLimit}`
        })
        .then(response => response.json())
        .then(data => {
            console.log(data);  // Debugging: log the response data

            // Check if the response is as expected
            if (data.success) {
                const output = document.getElementById("openai_output");
                if (output) {
                    output.value = data.data.text || "No text generated.";  // Access text inside `data.data.text`
                } else {
                    console.error('Output textarea not found.');
                }
            } else {
                const output = document.getElementById("openai_output");
                if (output) {
                    output.value = "Error: " + (data.message || "Unknown error");
                } else {
                    console.error('Output textarea not found.');
                }
            }
        })
        .catch(error => {
            console.error("Error:", error);
            const output = document.getElementById("openai_output");
            if (output) {
                output.value = "Error: Unable to reach API or process request.";
            }
        });
    });
});


        </script>
        <?php
        echo $args['after_widget'];
    }

    public function form($instance) {
        echo '<p>No settings required.</p>';
    }

    public function update($new_instance, $old_instance) {
        return $new_instance;
    }
}

function register_openai_widget() {
    register_widget('OpenAI_Widget');
}
add_action('widgets_init', 'register_openai_widget');

function openai_generate_text() {
    if (!isset($_POST['prompt']) || !isset($_POST['word_limit'])) {
        wp_send_json_error(['message' => 'Invalid request']);
    }

    $api_key = 'MY-API-KEY'; // Replace with your OpenAI API key
    $prompt = sanitize_text_field($_POST['prompt']);
    $word_limit = max(1, min((int) $_POST['word_limit'], 500));

    // OpenAI tokens are ~4 per word, but we cap them to 2048 to prevent long responses
    $max_tokens = min($word_limit * 4, 2048);

    // Updated API endpoint for GPT-3.5 Turbo
    $response = wp_remote_post('https://api.openai/v1/chat/completions', [
        'headers' => [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ],
        'body' => json_encode([
            'model'       => 'gpt-3.5-turbo',
            'messages'    => [
                ['role' => 'system', 'content' => 'You are a helpful assistant.'],
                ['role' => 'user', 'content' => $prompt],
            ],
            'max_tokens'  => $max_tokens,
            'temperature' => 0.7,
            'top_p'       => 1.0,
        ]),
    ]);

    if (is_wp_error($response)) {
        wp_send_json_error(['message' => 'API request failed: ' . $response->get_error_message()]);
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);

    // Debugging: Log the full response body to see the structure of the response
    error_log('OpenAI Response: ' . print_r($body, true));

    // Check if the response has the correct structure
    if (isset($body['choices'][0]['message']['content'])) {
        wp_send_json_success(['text' => trim($body['choices'][0]['message']['content'])]);
    } else {
        wp_send_json_error(['message' => 'Error from OpenAI: ' . json_encode($body)]);
    }
}
add_action('wp_ajax_openai_generate_text', 'openai_generate_text');
add_action('wp_ajax_nopriv_openai_generate_text', 'openai_generate_text');
?>

CODE-2 (loaded in widgets backend options - not working):

<?php
/*
Plugin Name: OpenAI Widget - backend options
Description: A simple WordPress widget to generate text using OpenAI GPT-3.5 API.
Version: 1.0
Author: WP
*/

class OpenAI_Widget extends WP_Widget {

    public function __construct() {
        parent::__construct(
            'openai_widget',
            __('OpenAI Text Generator', 'openai_widget_domain'),
            array('description' => __('Generate text using OpenAI GPT-3.5', 'openai_widget_domain'))
        );
    }

    public function widget($args, $instance) {
        // On the front-end, just show the widget without any fields or outputs.
        echo $args['before_widget'];
        echo $args['after_widget'];
    }

    public function form($instance) {
        // Backend form
        $prompt = !empty($instance['prompt']) ? $instance['prompt'] : '';
        $word_limit = !empty($instance['word_limit']) ? $instance['word_limit'] : 100;
        ?>
        <p>
            <label for="<?php echo $this->get_field_id('prompt'); ?>"><?php _e('Prompt:'); ?></label>
            <textarea class="widefat" id="<?php echo $this->get_field_id('prompt'); ?>" name="<?php echo $this->get_field_name('prompt'); ?>" rows="3"><?php echo esc_attr($prompt); ?></textarea>
        </p>
        <p>
            <label for="<?php echo $this->get_field_id('word_limit'); ?>"><?php _e('Word Limit:'); ?></label>
            <input class="widefat" id="<?php echo $this->get_field_id('word_limit'); ?>" name="<?php echo $this->get_field_name('word_limit'); ?>" type="number" value="<?php echo esc_attr($word_limit); ?>" min="1" max="500" />
        </p>
        <p>
            <!-- Button in backend -->
            <button class="widefat" type="button" id="generate_openai_backend_text">Generate Text (Backend Only)</button>
        </p>
        <div id="openai_backend_output_area" style="display:none;">
            <h4>Generated Text:</h4>
            <textarea id="openai_backend_output" rows="5" readonly></textarea>
        </div>

        <?php
    }

    public function update($new_instance, $old_instance) {
        // Save the widget options
        $instance = array();
        $instance['prompt'] = !empty($new_instance['prompt']) ? sanitize_text_field($new_instance['prompt']) : '';
        $instance['word_limit'] = !empty($new_instance['word_limit']) ? intval($new_instance['word_limit']) : 100;
        return $instance;
    }
}

function register_openai_widget() {
    register_widget('OpenAI_Widget');
}
add_action('widgets_init', 'register_openai_widget');

// Handle the AJAX request for text generation
function openai_generate_text() {
    if (!isset($_POST['prompt']) || !isset($_POST['word_limit'])) {
        wp_send_json_error(['message' => 'Invalid request']);
    }

    $api_key = 'MY-API-KEY'; // Replace with your OpenAI API key
    $prompt = sanitize_text_field($_POST['prompt']);
    $word_limit = max(1, min((int) $_POST['word_limit'], 500));

    // OpenAI tokens are ~4 per word, but we cap them to 2048 to prevent long responses
    $max_tokens = min($word_limit * 4, 2048);

    // Updated API endpoint for GPT-3.5 Turbo
    $response = wp_remote_post('https://api.openai/v1/chat/completions', [
        'headers' => [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ],
        'body' => json_encode([
            'model'       => 'gpt-3.5-turbo',
            'messages'    => [
                ['role' => 'system', 'content' => 'You are a helpful assistant.'],
                ['role' => 'user', 'content' => $prompt],
            ],
            'max_tokens'  => $max_tokens,
            'temperature' => 0.7,
            'top_p'       => 1.0,
        ]),
    ]);

    if (is_wp_error($response)) {
        wp_send_json_error(['message' => 'API request failed: ' . $response->get_error_message()]);
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);

    // Check if the response has the correct structure
    if (isset($body['choices'][0]['message']['content'])) {
        wp_send_json_success(['text' => trim($body['choices'][0]['message']['content'])]);
    } else {
        wp_send_json_error(['message' => 'Error from OpenAI: ' . json_encode($body)]);
    }
}
add_action('wp_ajax_openai_generate_text', 'openai_generate_text');
add_action('wp_ajax_nopriv_openai_generate_text', 'openai_generate_text');
?>
Share Improve this question edited Feb 16 at 10:13 DarkBee 15.6k8 gold badges72 silver badges116 bronze badges asked Feb 15 at 16:00 DEV186516844DEV186516844 175 bronze badges 5
  • WordPress doesn't use widgets as much as it does blocks now. Widgets are kinda obsolete. – admcfajn Commented Feb 15 at 16:16
  • 1 Just writing "works not" is a bit thin, you should write what you expected and what happened instead. Additionally, look for errors (e.g. in logging etc.) and reveal the notices. Also make visible what you do not understand so that it is more clear what you are actually asking about. – hakre Commented Feb 15 at 16:18
  • Your first step is to debug your code and figure out why it’s not working. How to debug JS – James Commented Feb 15 at 17:22
  • @admcfajn the block themes are not completely usable yet then old style themes are still useful. – mmm Commented Feb 16 at 9:19
  • @mmm Good point. I meant that widgets for theme sidebars have been replaced with blocks. There's still dashboard widgets though. – admcfajn Commented Feb 19 at 2:58
Add a comment  | 

1 Answer 1

Reset to default 0

the new widget editor doesn't load the javascript code inserted in the html code. then you can load the code with a separate file in the constructor of OpenAI_Widget like that :

if (is_admin()) {
    
    wp_enqueue_script(
          "MY_PLUGIN__custom_js" // id of the javascript file
        , plugins_url("widget/custom.js", __FILE__)
        , [] // no dependencies
        , "3" // put your plugin version here
    );
    
    // for passing data to the javascript code
    wp_localize_script(
          "MY_PLUGIN__custom_js" // id of the javascript file
        , "my_plugin__data" // javascript variable name
        ,
        [
            "api_url" => rest_url("my_plugin/openai_generate_text"),
        ]
    );
    
}

this javascript code is loaded with the widgets editor and the button doesn't exist at this moment. then you can manage the click like that :

document.addEventListener("DOMContentLoaded", e => {
    
    // hook the event listener to a higher node
    document.getElementById("widgets-editor").addEventListener("click", e => {
        
        if ("generate_openai_backend_text" !== e["target"]["id"]) {
            // stop if it's not a click on the button
            return;
        }
                    
        
        console.log(my_plugin__data["api_url"]);
        
        
        // do the http request
        
        
    });
    
});

and to create urls that return data, I recommend to add a custom endpoint in the api system of wordpress

add_action("rest_api_init", function () {
    
    register_rest_route("my_plugin", "openai_generate_text",
        [
            "methods" => WP_REST_Server::EDITABLE, // POST PUT PATCH
            "callback" => function (\WP_REST_Request $request) {
                
                $params = $request->get_params();
                
                $r = [
                    "success" => TRUE,
                    "data" => [
                        "text" => "api | {$params["prompt"]}",
                    ],
                ];
                
                return rest_ensure_response($r);
                
            },
            "permission_callback" => "__return_true",
        ]
    );
    
    
});

be careful that the javascript code you write doesn't work if there is more than one of this widget.

发布评论

评论列表(0)

  1. 暂无评论