PluginProbe
BotWriter – AI Writer & SEO Content Generator / 3.2.8
BotWriter – AI Writer & SEO Content Generator v3.2.8
3.4.10 3.4.9 3.4.8 3.4.7 3.4.6 3.4.4 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 3.3.0 3.2.8 trunk 1.3.0 1.3.1 1.3.2 1.3.3 All 51 releases
botwriter / includes / quickpost.php

quickpost.php in BotWriter – AI Writer & SEO Content Generator 3.2.8, at includes/quickpost.php

359 lines 17.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) { exit; }
3
4 // AJAX: create task and enqueue immediately
5 add_action('wp_ajax_botwriter_quick_create', 'botwriter_quick_create');
6 function botwriter_quick_create() {
7 if (!current_user_can('manage_options')) { wp_send_json_error('forbidden'); }
8 check_ajax_referer('botwriter_quickpost_nonce', 'nonce');
9
10 global $wpdb;
11 $table_name = $wpdb->prefix . 'botwriter_tasks';
12
13 // Reuse the same schema/validation as normal form
14 // Force defaults for Write Now: today and 1 time
15 // Use gmdate to satisfy WPCS (day name in English); current_time('timestamp') already accounts for WP tz
16 $today = gmdate('l', current_time('timestamp'));
17 $days = $today;
18 $times_per_day = 1;
19
20 // Whitelist for post_status
21 $raw_status = isset($_POST['post_status']) ? sanitize_text_field(wp_unslash($_POST['post_status'])) : 'publish';
22 $allowed_status = array('publish','draft','pending','private','future');
23 $safe_status = in_array($raw_status, $allowed_status, true) ? $raw_status : 'publish';
24
25 $item = array(
26 'task_name' => isset($_POST['task_name']) ? sanitize_text_field(wp_unslash($_POST['task_name'])) : '',
27 'post_status' => $safe_status,
28 'days' => $days,
29 'times_per_day' => $times_per_day,
30 'writer' => isset($_POST['writer']) ? sanitize_text_field(wp_unslash($_POST['writer'])) : 'orion',
31 'narration' => isset($_POST['narration']) ? sanitize_text_field(wp_unslash($_POST['narration'])) : 'Descriptive',
32 'post_length' => isset($_POST['post_length']) ? sanitize_text_field(wp_unslash($_POST['post_length'])) : '800',
33 'custom_post_length'=> isset($_POST['custom_post_length']) ? sanitize_text_field(wp_unslash($_POST['custom_post_length'])) : '',
34 'custom_style' => isset($_POST['custom_style']) ? sanitize_text_field(wp_unslash($_POST['custom_style'])) : '',
35 'post_language' => isset($_POST['post_language']) ? sanitize_text_field(wp_unslash($_POST['post_language'])) : '',
36 'website_type' => isset($_POST['website_type']) ? sanitize_text_field(wp_unslash($_POST['website_type'])) : 'ai',
37 'website_name' => '',
38 'domain_name' => isset($_POST['domain_name']) ? esc_url_raw(wp_unslash($_POST['domain_name'])) : '',
39 'post_type' => isset($_POST['task_post_type']) ? sanitize_text_field(wp_unslash($_POST['task_post_type'])) : 'post',
40 'category_id' => isset($_POST['category_id']) ? array_map('intval', (array) wp_unslash($_POST['category_id'])) : array(),
41 'taxonomy_data' => isset($_POST['taxonomy_data']) ? sanitize_text_field(wp_unslash($_POST['taxonomy_data'])) : '',
42 'website_category_id'=> isset($_POST['website_category_id']) ? array_map('intval', (array) wp_unslash($_POST['website_category_id'])) : array(),
43 'website_category_name'=> isset($_POST['website_category_name']) ? sanitize_text_field(wp_unslash($_POST['website_category_name'])) : '',
44 'aigenerated_title' => '',
45 'aigenerated_content'=> '',
46 'aigenerated_tags' => '',
47 'aigenerated_image' => '',
48 'post_count' => 1,
49 'post_order' => '',
50 'title_prompt' => isset($_POST['title_prompt']) ? sanitize_text_field(wp_unslash($_POST['title_prompt'])) : '',
51 'content_prompt' => isset($_POST['content_prompt']) ? sanitize_text_field(wp_unslash($_POST['content_prompt'])) : '',
52 'tags_prompt' => isset($_POST['tags_prompt']) ? sanitize_text_field(wp_unslash($_POST['tags_prompt'])) : '',
53 'image_prompt' => isset($_POST['image_prompt']) ? sanitize_text_field(wp_unslash($_POST['image_prompt'])) : '',
54 'image_generating_status' => '',
55 'author_selection' => isset($_POST['author_selection']) ? sanitize_text_field(wp_unslash($_POST['author_selection'])) : '',
56 'task_type' => 'writenow',
57 'news_keyword' => isset($_POST['news_keyword']) ? sanitize_text_field(wp_unslash($_POST['news_keyword'])) : '',
58 'news_country' => isset($_POST['news_country']) ? sanitize_text_field(wp_unslash($_POST['news_country'])) : '',
59 'news_language' => isset($_POST['news_language']) ? sanitize_text_field(wp_unslash($_POST['news_language'])) : '',
60 'news_time_published'=> isset($_POST['news_time_published']) ? sanitize_text_field(wp_unslash($_POST['news_time_published'])) : '',
61 'news_source' => isset($_POST['news_source']) ? sanitize_text_field(wp_unslash($_POST['news_source'])) : '',
62 'rss_source' => isset($_POST['rss_source']) ? sanitize_text_field(wp_unslash($_POST['rss_source'])) : '',
63 'ai_keywords' => isset($_POST['ai_keywords']) ? sanitize_text_field(wp_unslash($_POST['ai_keywords'])) : '',
64 'disable_ai_images' => isset($_POST['disable_ai_images']) ? intval(wp_unslash($_POST['disable_ai_images'])) : 0,
65 'template_id' => isset($_POST['template_id']) && !empty($_POST['template_id']) ? intval(wp_unslash($_POST['template_id'])) : null,
66 );
67
68 // Convert arrays to comma strings
69 $item['category_id'] = implode(',', $item['category_id']);
70 $item['website_category_id'] = implode(',', $item['website_category_id']);
71
72 // Process taxonomy terms and build taxonomy_data JSON if not already provided
73 if (empty($item['taxonomy_data']) && isset($_POST['taxonomy_terms'])) {
74 $taxonomy_terms = wp_unslash($_POST['taxonomy_terms']);
75 $taxonomy_data = array();
76 foreach ($taxonomy_terms as $taxonomy_name => $term_ids) {
77 $taxonomy_name = sanitize_key($taxonomy_name);
78 $taxonomy_data[$taxonomy_name] = array_map('intval', (array)$term_ids);
79 }
80 $item['taxonomy_data'] = wp_json_encode($taxonomy_data);
81
82 // For backward compatibility, extract category IDs if 'category' taxonomy is present
83 if (isset($taxonomy_data['category'])) {
84 $item['category_id'] = implode(',', $taxonomy_data['category']);
85 }
86 }
87
88 // Validate
89 if (!function_exists('botwriter_validate_website')) { wp_send_json_error('validator-missing'); }
90 $validation = botwriter_validate_website($item, true);
91 if ($validation !== true) {
92 wp_send_json_error($validation);
93 }
94
95 // Insert task
96 $inserted = $wpdb->insert($table_name, $item);
97 if ($inserted === false) {
98 wp_send_json_error('db-error: ' . $wpdb->last_error);
99 }
100 $task_id = intval($wpdb->insert_id);
101
102 // Immediately mark it as executed today so cron won't pick it and create another log
103 $wpdb->update(
104 $table_name,
105 array(
106 'execution_count' => 1,
107 'last_execution_date'=> current_time('Y-m-d'),
108 'last_execution_time'=> current_time('Y-m-d H:i:s'),
109 ),
110 array('id' => $task_id),
111 array('%d','%s','%s'),
112 array('%d')
113 );
114
115 // Build initial event/log and kick phase 1
116 $event = $item;
117 $event['task_status'] = 'pending';
118 $event['id_task'] = $task_id;
119 $event['intentosfase1'] = 0;
120
121 if (!function_exists('botwriter_logs_register')) { wp_send_json_error('logs-missing'); }
122 $log_id = botwriter_logs_register($event);
123 $event['id'] = $log_id;
124
125 if (!function_exists('botwriter_send1_data_to_server')) { wp_send_json_error('send1-missing'); }
126 botwriter_send1_data_to_server($event);
127
128 wp_send_json_success(array('task_id' => $task_id, 'log_id' => $log_id));
129 }
130
131 // AJAX: poll status and try advancing phase 2
132 add_action('wp_ajax_botwriter_quick_poll', 'botwriter_quick_poll');
133 function botwriter_quick_poll() {
134 if (!current_user_can('manage_options')) { wp_send_json_error('forbidden'); }
135 check_ajax_referer('botwriter_quickpost_nonce', 'nonce');
136
137 $log_id = isset($_POST['log_id']) ? intval($_POST['log_id']) : 0;
138 if ($log_id <= 0) { wp_send_json_error('invalid-log'); }
139
140 if (!function_exists('botwriter_logs_get')) { wp_send_json_error('logs-missing'); }
141 $log = botwriter_logs_get($log_id);
142 if (!$log) { wp_send_json_error('log-not-found'); }
143
144 $status = $log['task_status'];
145
146 // Try to advance phase 2 when pending/inqueue
147 if (in_array($status, array('pending', 'inqueue'), true)) {
148 if (!function_exists('botwriter_send2_data_to_server')) { wp_send_json_error('send2-missing'); }
149 botwriter_send2_data_to_server($log);
150 // reload
151 $log = botwriter_logs_get($log_id);
152 $status = $log['task_status'];
153 }
154
155 $progress = 10; // default
156 if ($status === 'inqueue') { $progress = 60; }
157 if ($status === 'direct_queued') { $progress = 30; }
158 if ($status === 'processing') { $progress = 60; }
159 if ($status === 'completed') { $progress = 100; }
160 if ($status === 'error') { $progress = 100; }
161
162 $edit_link = '';
163 $view_link = '';
164 if (!empty($log['id_post_published'])) {
165 $pid = intval($log['id_post_published']);
166 $edit_link = get_edit_post_link($pid, '');
167 $view_link = get_permalink($pid);
168 }
169
170 $response = array(
171 'status' => $status,
172 'progress' => $progress,
173 'id_post_published' => isset($log['id_post_published']) ? intval($log['id_post_published']) : 0,
174 'error' => isset($log['error']) ? $log['error'] : '',
175 'edit_link' => $edit_link,
176 'view_link' => $view_link,
177 );
178
179 wp_send_json_success($response);
180 }
181
182 // AJAX: retry phase 1 for an errored quickpost log
183 add_action('wp_ajax_botwriter_quick_retry', 'botwriter_quick_retry');
184 function botwriter_quick_retry() {
185 if (!current_user_can('manage_options')) { wp_send_json_error('forbidden'); }
186 check_ajax_referer('botwriter_quickpost_nonce', 'nonce');
187
188 $log_id = isset($_POST['log_id']) ? intval($_POST['log_id']) : 0;
189 if ($log_id <= 0) { wp_send_json_error('invalid-log'); }
190
191 if (!function_exists('botwriter_logs_get')) { wp_send_json_error('logs-missing'); }
192 $log = botwriter_logs_get($log_id);
193 if (!$log) { wp_send_json_error('log-not-found'); }
194
195 // Ensure we resend full task data (prompts, ai_keywords, etc.) by merging with source task
196 global $wpdb;
197 $table_tasks = $wpdb->prefix . 'botwriter_tasks';
198 if (!empty($log['id_task'])) {
199 $task_row = $wpdb->get_row(
200 $wpdb->prepare(
201 'SELECT * FROM ' . $table_tasks . ' WHERE id = %d',
202 intval($log['id_task'])
203 ),
204 ARRAY_A
205 );
206 if (is_array($task_row)) {
207 // Avoid overriding log id with task id
208 unset($task_row['id']);
209 // Task fields should be present; let log engine fields override when colliding
210 $log = array_merge($task_row, $log);
211 }
212 }
213
214 // Prepare for a new phase 1 dispatch: clear error and mark pending
215 $log['task_status'] = 'pending';
216 $log['error'] = '';
217 // Optionally clear server id so a fresh queue id is assigned
218 $log['id_task_server'] = 0;
219
220 if (!function_exists('botwriter_logs_register')) { wp_send_json_error('logs-missing'); }
221 botwriter_logs_register($log, $log_id);
222
223 if (!function_exists('botwriter_send1_data_to_server')) { wp_send_json_error('send1-missing'); }
224 botwriter_send1_data_to_server($log);
225
226 wp_send_json_success(array('log_id' => $log_id));
227 }
228
229 // AJAX: cancel and delete the quickpost task and its log
230 add_action('wp_ajax_botwriter_quick_cancel', 'botwriter_quick_cancel');
231 function botwriter_quick_cancel() {
232 if (!current_user_can('manage_options')) { wp_send_json_error('forbidden'); }
233 check_ajax_referer('botwriter_quickpost_nonce', 'nonce');
234
235 $task_id = isset($_POST['task_id']) ? intval($_POST['task_id']) : 0;
236 $log_id = isset($_POST['log_id']) ? intval($_POST['log_id']) : 0;
237 if ($task_id <= 0 || $log_id <= 0) { wp_send_json_error('invalid-ids'); }
238
239 global $wpdb;
240 $table_tasks = $wpdb->prefix . 'botwriter_tasks';
241 $table_logs = $wpdb->prefix . 'botwriter_logs';
242
243 // Only allow cancel for Write now tasks for safety
244 $task = $wpdb->get_row(
245 $wpdb->prepare(
246 'SELECT id, task_type FROM ' . $table_tasks . ' WHERE id = %d',
247 $task_id
248 ),
249 ARRAY_A
250 );
251 if (!$task || (isset($task['task_type']) && $task['task_type'] !== 'writenow')) {
252 wp_send_json_error('not-writenow');
253 }
254
255 // Delete log and task
256 $wpdb->delete($table_logs, array('id' => $log_id), array('%d'));
257 $wpdb->delete($table_tasks, array('id' => $task_id), array('%d'));
258
259 wp_send_json_success(array('deleted' => true));
260 }
261
262 // Page handler rendering identical form and AJAX UI
263 function botwriter_quick_post_page_handler() {
264 if (!current_user_can('manage_options')) { return; }
265
266 global $wpdb;
267 $table_name = $wpdb->prefix . 'botwriter_tasks';
268
269 // Defaults similar to posts.php $default
270 $item = array(
271 'id' => 0,
272 /* translators: %d: next quick post number */
273 'task_name' => sprintf(__('Quick Post %d', 'botwriter'), $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE website_type = 'ai'") + 1),
274 'writer' => 'orion',
275 'narration' => 'Descriptive',
276 'custom_style' => '',
277 'post_language' => '',
278 'post_length' => '800',
279 'custom_post_length' => '',
280 'post_status' => 'publish',
281 'days' => '',
282 'times_per_day' => 1,
283 'execution_count' => 0,
284 'last_execution_date' => null,
285 'website_type' => 'ai',
286 'website_name' => '',
287 'domain_name' => '',
288 'post_type' => 'post',
289 'category_id' => '',
290 'taxonomy_data' => '',
291 'website_category_id' => '',
292 'website_category_name' => '',
293 'aigenerated_title' => '',
294 'aigenerated_content' => '',
295 'aigenerated_tags' => '',
296 'aigenerated_image' => '',
297 'post_count' => '',
298 'post_order' => '',
299 'title_prompt' => '',
300 'content_prompt' => '',
301 'tags_prompt' => '',
302 'image_prompt' => '',
303 'image_generating_status' => '',
304 'author_selection' => '',
305 'news_keyword' => '',
306 'news_country' => '',
307 'news_language' => '',
308 'news_time_published' => '',
309 'news_source' => '',
310 'rss_source' => '',
311 'ai_keywords' => '',
312 );
313
314 // Register the same meta box handler for this page render
315 add_meta_box('botwriter_post_form_meta_box', __('Task Form', 'botwriter'), 'botwriter_post_form_meta_box_handler', 'botwriter_automatic_post_new', 'normal', 'default');
316
317 echo '<div class="wrap">';
318 echo '<h2>' . esc_html__('Write now', 'botwriter') . '</h2>';
319 echo '<p class="description" style="max-width:800px;">' .
320 esc_html__('This page is for writing a single article immediately. For recurring schedules or multiple runs, use', 'botwriter') .
321 ' <a href="' . esc_url(admin_url('admin.php?page=botwriter_addnew_page')) . '">' . esc_html__('New task', 'botwriter') . '</a>.' .
322 '</p>';
323
324 echo '<form id="botwriter-quick-form" method="post">';
325 echo '<input type="hidden" name="nonce" value="' . esc_attr(wp_create_nonce('botwriter_quickpost_nonce')) . '">';
326 echo '<div class="metabox-holder" id="poststuff"><div id="post-body"><div id="post-body-content">';
327 do_meta_boxes('botwriter_automatic_post_new', 'normal', $item);
328 // Progress bar should appear above the buttons
329 $bw_video_url = esc_url( BOTWRITER_URL . 'assets/images/bot_working.mp4');
330 $bw_done_url = esc_url( BOTWRITER_URL . 'assets/images/robot_icon2.png');
331 echo '<div id="bwqp-progress" style="display:none;margin-top:10px;max-width:900px;">';
332 // Row: left media, right steps/messages
333 echo '<div id="bwqp-row" style="display:flex; align-items:flex-start; gap:16px;">';
334 // Left media (no borders, left aligned)
335 echo '<div id="bwqp-media" style="display:none; width:256px;">';
336 echo '<video id="bwqp-video" width="256" height="256" muted playsinline loop style="display:none;">';
337 echo '<source src="' . esc_url( $bw_video_url ) . '" type="video/mp4" />';
338 echo '</video>';
339 echo '<img id="bwqp-done" src="' . esc_url( $bw_done_url ) . '" width="256" height="256" alt="Done" style="display:none;" />';
340 echo '</div>';
341 // Right messages/steps container
342 echo '<div id="bwqp-steps" style="flex:1 1 auto; min-height:256px; padding-top:6px;">';
343 echo '</div>';
344 echo '</div>'; // close row
345 // Progress bar and status inside the progress container
346 echo '<div style="background:#e5e5e5;border-radius:4px;overflow:hidden;">';
347 echo '<div id="bwqp-bar" style="width:0%;background:#2271b1;color:#fff;padding:6px 10px;transition:width .3s;">0%</div>';
348 echo '</div>';
349 echo '<p id="bwqp-status" style="margin-top:6px;"></p>';
350 echo '</div>'; // close #bwqp-progress
351 echo '<p class="submit" style="margin-top:12px;">';
352 echo '<button type="button" id="botwriter-quick-create" class="button button-primary">' . esc_html__('Create Now', 'botwriter') . '</button> ';
353 echo '<a class="button" href="' . esc_url(admin_url('admin.php?page=botwriter_automatic_posts')) . '">' . esc_html__('Back to Tasks', 'botwriter') . '</a>';
354 echo '</p>';
355 echo '</div></div></div>';
356 echo '</form>';
357 echo '</div>';
358 }
359