PluginProbe
BotWriter – AI Writer & SEO Content Generator / 3.4.9
BotWriter – AI Writer & SEO Content Generator v3.4.9
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 / posts.php

posts.php in BotWriter – AI Writer & SEO Content Generator 3.4.9, at includes/posts.php

1,019 lines 46.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit;
4 }
5
6
7
8 function botwriter_automatic_posts_page()
9 {
10 // Verify user has permission
11 if (!current_user_can('manage_options')) {
12 wp_die(esc_html__('You do not have permission to access this page.', 'botwriter'));
13 }
14
15 // Debug cron scheduling and status when viewing Tasks page
16 $next_cron = wp_next_scheduled('botwriter_scheduled_events_plugin_cron');
17 botwriter_log('Tasks page loaded', [
18 'cron_active' => get_option('botwriter_cron_active'),
19 'next_cron' => $next_cron ? gmdate('Y-m-d H:i:s', $next_cron) . ' UTC' : 'not scheduled',
20 ]);
21
22 $table = new botwriter_tasks_Table();
23 $table->prepare_items();
24 $message = '';
25 if ('delete_all' === $table->current_action()) {
26 $message = 'Items deleted';
27 }
28 if ('delete' === $table->current_action()) {
29 $message = 'Item deleted';
30 }
31
32 $message = esc_html($message);
33 ?>
34
35 <div class="wrap">
36 <div class="icon32 icon32-posts-post" id="icon-edit"><br></div>
37 <h2>
38 <?php esc_html_e('Tasks', 'botwriter'); ?>
39 <a class="add-new-h2"
40 href="<?php echo esc_url(get_admin_url(get_current_blog_id(), 'admin.php?page=botwriter_addnew_page')); ?>">
41 <?php esc_html_e('Add new', 'botwriter'); ?>
42 </a>
43 </h2>
44
45
46
47 <?php if (!empty($message)) : ?>
48 <div class="updated below-h2" id="message"><p><?php echo esc_html($message); ?></p></div>
49 <?php endif; ?>
50
51 <form id="contacts-table" method="POST">
52 <?php
53 wp_nonce_field('botwriter_tasks_nonce_action', 'botwriter_tasks_nonce');
54 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page slug echoed into hidden field; the form has its own nonce above.
55 $page_value = isset($_REQUEST['page']) ? sanitize_text_field(wp_unslash($_REQUEST['page'])) : '';
56 ?>
57 <input type="hidden" name="page" value="<?php echo esc_html($page_value); ?>"/>
58 <?php $table->display(); ?>
59 </form>
60 </div>
61
62 <?php
63 }
64
65
66 function botwriter_form_page_handler(){
67
68 // Verify user has permission
69 if (!current_user_can('manage_options')) {
70 wp_die(esc_html__('You do not have permission to access this page.', 'botwriter'));
71 }
72
73 global $wpdb;
74 $table_name = $wpdb->prefix . 'botwriter_tasks';
75
76 $message = '';
77 $notice = '';
78
79
80 $default = array(
81 'id' => 0,
82
83 /* translators: %d: next task number */
84 'task_name' => sprintf(__('Task %d', 'botwriter'), $wpdb->get_var("SELECT COUNT(*) FROM $table_name") + 1),
85 'writer' => 'orion',
86 'narration' => 'Descriptive',
87 'custom_style' => '',
88 'post_language' => '',
89 'post_length' => '800',
90 'custom_post_length' => '',
91 'post_status' => 'publish',
92 'days' => '',
93 'times_per_day' => 1,
94 'execution_count' => 0, // Initialize execution count to zero
95 'last_execution_date' => null, // Set last execution date to null initially
96 'website_type' => 'ai',
97
98 'website_name' => '',
99 'domain_name' => '',
100 'post_type' => 'post',
101 'category_id' => '',
102 'taxonomy_data' => '',
103 'website_category_id' => '',
104 'website_category_name' => '',
105 'aigenerated_title' => '',
106 'aigenerated_content' => '',
107 'aigenerated_tags' => '',
108 'aigenerated_image' => '',
109 'post_count' => '',
110 'post_order' => '',
111 'title_prompt' => '',
112 'content_prompt' => '',
113 'tags_prompt' => '',
114 'image_prompt' => '',
115 'image_generating_status' => '',
116 'author_selection' => '',
117
118 'news_keyword' => '',
119 'news_country' => '',
120 'news_language' => '',
121 'news_time_published' => '',
122 'news_source' => '',
123 'rss_source' => '',
124 'ai_keywords' => '',
125 'disable_ai_images' => 0,
126 'template_id' => null,
127
128
129
130
131 );
132
133
134 if ( isset($_REQUEST['nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['nonce'])), basename(__FILE__))) {
135
136 $days = isset($_POST['days']) ? implode(",", array_map('sanitize_text_field', wp_unslash($_POST['days']))) : "";
137 $times_per_day = isset($_POST['times_per_day']) ? intval(wp_unslash($_POST['times_per_day'])) : 1;
138
139
140 // Process only the specific values needed
141 $item = array(
142 'id' => isset($_POST['id']) ? intval(wp_unslash($_POST['id'])) : 0,
143
144 'task_name' => isset($_POST['task_name']) ? sanitize_text_field(wp_unslash($_POST['task_name'])) : '',
145 'post_status' => isset($_POST['post_status']) ? sanitize_text_field(wp_unslash($_POST['post_status'])) : '',
146 'days' => $days,
147 'times_per_day' => $times_per_day,
148 'writer' => isset($_POST['writer']) ? sanitize_text_field(wp_unslash($_POST['writer'])) : '',
149 'narration' => isset($_POST['narration']) ? sanitize_text_field(wp_unslash($_POST['narration'])) : '',
150 'post_length' => isset($_POST['post_length']) ? sanitize_text_field(wp_unslash($_POST['post_length'])) : '',
151 'custom_post_length'=> isset($_POST['custom_post_length']) ? sanitize_text_field(wp_unslash($_POST['custom_post_length'])) : '',
152 'custom_style' => isset($_POST['custom_style']) ? sanitize_text_field(wp_unslash($_POST['custom_style'])) : '',
153 'post_language' => isset($_POST['post_language']) ? sanitize_text_field(wp_unslash($_POST['post_language'])) : '',
154
155 'website_type' => isset($_POST['website_type']) ? sanitize_text_field(wp_unslash($_POST['website_type'])) : '',
156 'website_name' => '',
157 'domain_name' => isset($_POST['domain_name']) ? sanitize_url(wp_unslash($_POST['domain_name'])) : '',
158 'post_type' => isset($_POST['task_post_type']) ? sanitize_text_field(wp_unslash($_POST['task_post_type'])) : 'post',
159 'category_id' => isset($_POST['category_id']) ? array_map('intval', wp_unslash($_POST['category_id'])) : array(),
160 'taxonomy_data' => isset($_POST['taxonomy_data']) ? sanitize_text_field(wp_unslash($_POST['taxonomy_data'])) : '',
161 'website_category_id'=> isset($_POST['website_category_id']) ? array_map('intval', wp_unslash($_POST['website_category_id'])) : array(),
162 'website_category_name'=> isset($_POST['website_category_name']) ? sanitize_text_field(wp_unslash($_POST['website_category_name'])) : '',
163 'aigenerated_title' => '',
164 'aigenerated_content'=> '',
165 'aigenerated_tags' => '',
166 'aigenerated_image' => '',
167 'post_count' => 5,
168 'post_order' => '',
169 'title_prompt' => '',
170
171 'content_prompt' => '',
172
173 'tags_prompt' => '',
174 'image_prompt' => '',
175 'image_generating_status' => '',
176
177 'author_selection' => isset($_POST['author_selection']) ? sanitize_text_field(wp_unslash($_POST['author_selection'])) : '',
178
179 'news_keyword' => isset($_POST['news_keyword']) ? sanitize_text_field(wp_unslash($_POST['news_keyword'])) : '',
180 'news_country' => isset($_POST['news_country']) ? sanitize_text_field(wp_unslash($_POST['news_country'])) : '',
181 'news_language' => isset($_POST['news_language']) ? sanitize_text_field(wp_unslash($_POST['news_language'])) : '',
182 'news_time_published' => isset($_POST['news_time_published']) ? sanitize_text_field(wp_unslash($_POST['news_time_published'])) : '',
183 'news_source' => isset($_POST['news_source']) ? sanitize_text_field(wp_unslash($_POST['news_source'])) : '',
184
185 'rss_source' => isset($_POST['rss_source']) ? sanitize_text_field(wp_unslash($_POST['rss_source'])) : '',
186 'ai_keywords' => isset($_POST['ai_keywords']) ? sanitize_text_field(wp_unslash($_POST['ai_keywords'])) : '',
187 'disable_ai_images' => isset($_POST['disable_ai_images']) ? intval(wp_unslash($_POST['disable_ai_images'])) : 0,
188 'template_id' => isset($_POST['template_id']) && !empty($_POST['template_id']) ? intval(wp_unslash($_POST['template_id'])) : null
189
190 );
191
192 // Process taxonomy terms and build taxonomy_data JSON
193 $taxonomy_terms = isset($_POST['taxonomy_terms']) ? map_deep(wp_unslash($_POST['taxonomy_terms']), 'sanitize_text_field') : array();
194 $taxonomy_data = array();
195 foreach ($taxonomy_terms as $taxonomy_name => $term_ids) {
196 $taxonomy_name = sanitize_key($taxonomy_name);
197 $taxonomy_data[$taxonomy_name] = array_map('intval', (array)$term_ids);
198 }
199 $item['taxonomy_data'] = wp_json_encode($taxonomy_data);
200
201 // For backward compatibility, extract category IDs if 'category' taxonomy is present
202 if (isset($taxonomy_data['category'])) {
203 $item['category_id'] = implode(',', $taxonomy_data['category']);
204 } else {
205 // Convert legacy category_id array to text
206 $category_ids = implode(",", $item['category_id']);
207 $item['category_id'] = $category_ids;
208 }
209
210 //Convert website_category_id to text
211 $website_category_ids = implode(",", $item['website_category_id']);
212
213
214 $item['website_category_id'] = $website_category_ids;
215
216 botwriter_log('Task validation starting', [
217 'task_name' => $item['task_name'],
218 'post_type' => $item['post_type'] ?? 'not set',
219 'category_id' => $item['category_id'],
220 'taxonomy_data' => $item['taxonomy_data'] ?? 'not set',
221 ]);
222
223 $item_valid = botwriter_validate_website($item);
224 botwriter_log('Task validation result', ['valid' => $item_valid === true ? 'yes' : $item_valid]);
225
226 if ($item_valid === true) {
227
228
229 //$item = array_map('sanitize_text_field', $item); // Sanitize all inputs
230 if ($item['id'] == 0) {
231 // Do not insert primary key 'id' explicitly to allow AUTO_INCREMENT/SQLite AUTOINCREMENT to work
232 $insert_data = $item;
233 unset($insert_data['id']);
234 botwriter_log('Task INSERT data', ['fields' => array_keys($insert_data), 'post_type' => $insert_data['post_type'] ?? 'not set', 'taxonomy_data' => $insert_data['taxonomy_data'] ?? 'not set']);
235 $result = $wpdb->insert($table_name, $insert_data);
236
237 $item['id'] = $wpdb->insert_id;
238 if ($result) {
239 botwriter_log('Task created', [
240 'task_id' => $item['id'],
241 'website_type' => $item['website_type'],
242 'status' => $item['status'] ?? null,
243 'days' => $item['days'] ?? null,
244 'times_per_day' => $item['times_per_day'] ?? null,
245 ]);
246 $message = __('New task was successfully saved!', 'botwriter');
247 ?>
248 <div id="redirecion" data-url="<?php echo esc_url( admin_url('admin.php?page=botwriter_automatic_posts') ); ?>"></div>
249 <?php
250
251 } else {
252 botwriter_log('Task insert failed', [
253 'error' => $wpdb->last_error,
254 'last_query' => $wpdb->last_query,
255 ], 'error');
256 $notice = __('There was an error while saving item', 'botwriter') . ': ' . $wpdb->last_error;
257 }
258 } else {
259 // Do not try to update the primary key value itself
260 $update_data = $item;
261 $row_id = (int) $item['id'];
262 unset($update_data['id']);
263 $result = $wpdb->update($table_name, $update_data, array('id' => $row_id));
264
265
266 if ($result !== false) {
267 if ($result === 0) {
268 botwriter_log('Task update no changes', [
269 'task_id' => $row_id,
270 'website_type' => $item['website_type'],
271 ]);
272 $message = __('No changes were made, but the update was successful.', 'botwriter');
273 ?>
274 <div id="redirecion" data-url="<?php echo esc_url( admin_url('admin.php?page=botwriter_automatic_posts') ); ?>"></div>
275 <?php
276
277 } else {
278 botwriter_log('Task updated', [
279 'task_id' => $row_id,
280 'website_type' => $item['website_type'],
281 'status' => $item['status'] ?? null,
282 'days' => $item['days'] ?? null,
283 'times_per_day' => $item['times_per_day'] ?? null,
284 ]);
285 $message = __('New task was successfully updated!', 'botwriter');
286 ?>
287 <div id="redirecion" data-url="<?php echo esc_url( admin_url('admin.php?page=botwriter_automatic_posts') ); ?>"></div>
288 <?php
289 }
290 } else {
291 botwriter_log('Task update failed', [
292 'task_id' => $row_id,
293 'error' => $wpdb->last_error,
294 ], 'error');
295 $notice = __('There was an error while updating item: ', 'botwriter') . $wpdb->last_error;
296 }
297 }
298 } else {
299
300 $notice = $item_valid;
301 }
302 }
303 else {
304
305 $item = $default;
306 if (isset($_REQUEST['id'])) {
307 $sanitized_id = absint($_REQUEST['id']); // Sanitize as an integer
308 $item = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table_name} WHERE id = %d", $sanitized_id), ARRAY_A);
309 if (!$item) {
310 $item = $default;
311 $notice = __('Item not found', 'botwriter');
312 }
313 }
314
315 }
316
317
318 add_meta_box('botwriter_post_form_meta_box', __('Task Form', 'botwriter'), 'botwriter_post_form_meta_box_handler', 'botwriter_automatic_post_new', 'normal', 'default');
319
320 ?>
321 <div class="wrap">
322 <div class="icon32 icon32-posts-post" id="icon-edit"><br></div>
323
324 <?php
325 $is_editing = isset($_REQUEST['id']) && intval($_REQUEST['id']) > 0;
326 $title_text = $is_editing ? __('Edit Task', 'botwriter') : __('Add New', 'botwriter');
327 ?>
328 <h2><?php echo esc_html($title_text); ?> <a class="add-new-h2"
329 href="<?php echo esc_url(get_admin_url(get_current_blog_id(), 'admin.php?page=botwriter_automatic_posts')); ?>"><?php esc_html_e('Back to List', 'botwriter'); ?></a>
330 </h2>
331
332 <?php if (!empty($notice)): ?>
333 <div id="notice" class="error"><p><?php echo esc_attr($notice) ?></p></div>
334 <?php endif;?>
335 <?php if (!empty($message)): ?>
336 <div id="message" class="updated"><p><?php echo esc_attr($message) ?></p></div>
337 <?php endif;?>
338
339 <form id="form" method="POST">
340 <input type="hidden" name="nonce" value="<?php echo esc_attr(wp_create_nonce(basename(__FILE__))); ?>"/>
341
342 <input type="hidden" name="id" value="<?php echo esc_attr($item['id']) ?>"/>
343
344 <div class="metabox-holder" id="poststuff">
345 <div id="post-body">
346 <div id="post-body-content">
347
348 <?php do_meta_boxes('botwriter_automatic_post_new', 'normal', $item); ?>
349 <input type="submit" value="<?php esc_attr_e('Save', 'botwriter')?>" id="submit" class="button-primary" name="submit">
350 </div>
351 </div>
352 </div>
353 </form>
354 </div>
355
356 <?php
357 }
358
359
360
361 function botwriter_get_admin_email(){
362 $admin_email = get_option('admin_email', false);
363 if ($admin_email !== false) {
364
365
366 } else {
367 $admin_email = 'email@example.com';
368 }
369 return $admin_email;
370 }
371
372
373
374
375 function botwriter_post_form_meta_box_handler($item)
376 {
377 Global $botwriter_languages,$botwriter_countries;
378 // Get the selected days
379 $selected_days = isset($item["days"]) ? explode(",", $item["days"]) : array();
380 $times_per_day = isset($item["times_per_day"]) ? $item["times_per_day"] : 1;
381
382
383
384
385 $dir_images_writers = plugin_dir_url(dirname(__FILE__)) . '/assets/images/writers/';
386 $dir_images_icons = plugin_dir_url(dirname(__FILE__)) . '/assets/images/icons/';
387
388
389 ?>
390
391 <div id="loading">
392 <div class="loader">
393 <div class="inner one"></div>
394 <div class="inner two"></div>
395 <div class="inner three"></div>
396 </div>
397 </div>
398
399
400
401
402 <div class="container">
403 <form class="row g-3">
404 <?php
405 //Get admin domain name
406 $botwriter_admin_email = botwriter_get_admin_email();
407 $botwriter_domain_name = esc_url(get_site_url());
408 $is_empty = empty($item['domain_name']);
409 ?>
410
411 <input type="hidden" id="botwriter_admin_email" value="<?php echo esc_attr($botwriter_admin_email); ?>">
412 <input type="hidden" id="botwriter_domain_name" value="<?php echo esc_attr($botwriter_domain_name); ?>">
413
414 <div class="col-md-6">
415 <label class="form-label">Task Name:</label>
416 <input id="task_name" name="task_name" type="text" class="form-control" value="<?php echo esc_attr($item['task_name']); ?>" required>
417 </div>
418 <br>
419
420 <!-- Writers -->
421 <div class="col-md-6">
422 <label class="form-label"><?php echo esc_html__('Writer:', 'botwriter'); ?></label>
423 <div class="writer-options">
424
425 <!-- Orion, the Versatile Assistant -->
426 <label class="writer-option">
427 <input type="radio" name="writer" value="orion" required <?php if ($item['writer'] === 'orion') {
428 echo 'checked';
429 } ?>>
430 <img src="<?php echo esc_url($dir_images_writers . 'orion.jpeg'); ?>" alt="<?php echo esc_attr__('Orion', 'botwriter'); ?>" class="writer-photo">
431 <div class="writer-info">
432 <strong><?php echo esc_html__('Orion, the Versatile Assistant', 'botwriter'); ?></strong>
433 <p><?php echo esc_html__('Adaptable and insightful, perfect for a wide range of topics and styles.', 'botwriter'); ?></p>
434 </div>
435 </label>
436
437 <!-- Lucida, the Analytical Critic -->
438 <label class="writer-option">
439 <input type="radio" name="writer" value="lucida" required <?php if ($item['writer'] === 'lucida') {
440 echo 'checked';
441 } ?>>
442 <img src="<?php echo esc_url($dir_images_writers . 'lucida.jpeg'); ?>" alt="<?php echo esc_attr__('Lucida', 'botwriter'); ?>" class="writer-photo">
443 <div class="writer-info">
444 <strong><?php echo esc_html__('Lucida, the Analytical Critic', 'botwriter'); ?></strong>
445 <p><?php echo esc_html__('Precise and direct, perfect for deep analysis in complex topics.', 'botwriter'); ?></p>
446 </div>
447 </label>
448
449 <!-- Max, the Adventurous Narrator -->
450 <label class="writer-option">
451 <input type="radio" name="writer" value="max" required <?php echo $item['writer'] === 'max' ? 'checked' : ''; ?>>
452 <img src="<?php echo esc_url($dir_images_writers . 'max.jpeg'); ?>" alt="<?php echo esc_attr__('Max', 'botwriter'); ?>" class="writer-photo">
453 <div class="writer-info">
454 <strong><?php echo esc_html__('Max, the Adventurous Narrator', 'botwriter'); ?></strong>
455 <p><?php echo esc_html__('Passionate and descriptive, ideal for stories of travel and culture.', 'botwriter'); ?></p>
456 </div>
457 </label>
458
459 <!-- Cloe, the Ironic Cultural Critic -->
460 <label class="writer-option">
461 <input type="radio" name="writer" value="cloe" required <?php echo $item['writer'] === 'cloe' ? 'checked' : ''; ?>>
462 <img src="<?php echo esc_url($dir_images_writers . 'cloe.jpeg'); ?>" alt="<?php echo esc_attr__('Cloe', 'botwriter'); ?>" class="writer-photo">
463 <div class="writer-info">
464 <strong><?php echo esc_html__('Cloe, the Ironic Cultural Critic', 'botwriter'); ?></strong>
465 <p><?php echo esc_html__('Sarcastic and witty, perfect for cultural and social reviews.', 'botwriter'); ?></p>
466 </div>
467 </label>
468
469 <!-- Gael, the Reflective Poet -->
470 <label class="writer-option">
471 <input type="radio" name="writer" value="gael" required <?php echo $item['writer'] === 'gael' ? 'checked' : ''; ?>>
472 <img src="<?php echo esc_url($dir_images_writers . 'gael.jpeg'); ?>" alt="<?php echo esc_attr__('Gael', 'botwriter'); ?>" class="writer-photo">
473 <div class="writer-info">
474 <strong><?php echo esc_html__('Gael, the Reflective Poet', 'botwriter'); ?></strong>
475 <p><?php echo esc_html__('Introspective and poetic, ideal for philosophical and emotional themes.', 'botwriter'); ?></p>
476 </div>
477 </label>
478
479 <!-- Custom -->
480 <label class="writer-option">
481 <input type="radio" name="writer" value="custom" required <?php echo $item['writer'] === 'custom' ? 'checked' : ''; ?>>
482 <img src="<?php echo esc_url($dir_images_writers . 'custom.jpeg'); ?>" alt="<?php echo esc_attr__('Custom', 'botwriter'); ?>" class="writer-photo">
483 <div class="writer-info">
484 <strong><?php echo esc_html__('Custom, the User-Selected Style Bot', 'botwriter'); ?></strong>
485 <p><?php echo esc_html__('Allows the user to choose a specific narrative style..', 'botwriter'); ?></p>
486 <select class="form-select" id="narration" name="narration">
487 <?php
488 $styles = [
489 "Descriptive" => "Descriptive",
490 "Narrative" => "Narrative",
491 "Explanatory" => "Explanatory",
492 "Argumentative" => "Argumentative",
493 "Comparative" => "Comparative",
494 "Process Analysis" => "Process Analysis",
495 "Allegorical" => "Allegorical",
496 "Chronological" => "Chronological",
497 "Ironic" => "Ironic",
498 "ConsistencyAndRepetition" => "Consistency and Repetition",
499 "LanguagePlayAndPoeticExpression" => "Language Play and Poetic Expression",
500 "InternalMonologue" => "Internal Monologue",
501 "Dialogical" => "Dialogical",
502 "Custom" => "Custom"
503 ];
504 foreach ($styles as $value => $name) {
505 $selected = ($item["narration"] == $value) ? 'selected' : '';
506 echo "<option value='" . esc_attr($value) . "' " . esc_attr($selected) . ">" . esc_html($name) . "</option>";
507 }
508 ?>
509 </select>
510 <div id="customStyleInput" style="display: none; margin-top: 10px;">
511 <label for="customStyle" class="form-label">Specify Custom Style:</label>
512 <input type="text" class="form-control" id="custom_style" name="custom_style" placeholder="Enter your custom writing style" value="<?php echo esc_attr($item['custom_style']); ?>">
513 </div>
514
515 </div>
516
517 </label>
518
519 </div>
520
521 </div>
522 <br>
523
524 <div class="col-md-6">
525 <label class="form-label">Author Selection</label>
526 <select name="author_selection" class="form-select">
527 <?php
528 $authors = get_users();
529
530 foreach ($authors as $author) {
531 $author_id = $author->ID;
532 $author_name = $author->display_name;
533 $author_description = get_the_author_meta('description', $author_id); // Get the author's description
534
535 if ($item['author_selection'] === strval($author_id)) {
536 echo '<option value="' . esc_attr($author_id) . '" selected>' . esc_html($author_name) . '</option>';
537 continue;
538 }
539
540 echo '<option value="' . esc_attr($author_id) . '">' . esc_html($author_name) . '</option>';
541 }
542 ?>
543 </select>
544 <p class="form-text">Select an author from the list.</p>
545 </div>
546
547 <div class="col-md-6">
548 <label for="post_status" class="form-label">Post Status:</label>
549 <select id="post_status" name="post_status" class="form-select">
550 <option value="publish" <?php if ($item['post_status'] === 'publish') {
551 echo 'selected';
552 } ?>>Publish</option>
553 <option value="draft" <?php if ($item['post_status'] === 'draft') {
554 echo 'selected';
555 } ?>>Draft</option>
556 </select>
557 <p class="form-text">Select the status of the post. Choose 'Draft' if you want to revise it before publishing.</p>
558 </div>
559
560 <div class="col-md-6">
561 <label class="form-label"><?php esc_html_e('Days of the Week:', 'botwriter'); ?></label><br>
562 <?php
563 $days_of_week = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
564 foreach ($days_of_week as $day) {
565 $is_checked = in_array($day, $selected_days) ? 'checked' : '';
566 echo "<input type='checkbox' name='days[]' value='" . esc_attr($day) . "' " . esc_attr($is_checked) . "> " . esc_html($day) . "<br>";
567
568 }
569 ?>
570 <p class="form-text">Select the days on which you want to write and publish.</p>
571 </div>
572
573 <div class="col-md-6">
574 <label class="form-label"><?php esc_html_e('Post per Day:', 'botwriter'); ?></label>
575 <input type="number" name="times_per_day" min="1" value="<?php echo esc_attr($times_per_day); ?>" required>
576 </div>
577 <br>
578
579 <div class="col-md-6">
580 <label for="task_post_type" class="form-label"><?php esc_html_e('Post Type:', 'botwriter'); ?></label>
581 <select id="task_post_type" name="task_post_type" class="form-select">
582 <?php
583 $post_types = get_post_types(array('public' => true), 'objects');
584 $current_post_type = isset($item['post_type']) ? $item['post_type'] : 'post';
585 foreach ($post_types as $pt) {
586 // Skip attachment
587 if ($pt->name === 'attachment') {
588 continue;
589 }
590 $selected = ($current_post_type === $pt->name) ? 'selected' : '';
591 echo '<option value="' . esc_attr($pt->name) . '" ' . esc_attr($selected) . '>' . esc_html($pt->label) . '</option>';
592 }
593 ?>
594 </select>
595 <p class="form-text"><?php esc_html_e('Select the type of content to create.', 'botwriter'); ?></p>
596 </div>
597
598 <div class="col-md-12" id="taxonomy-container">
599 <?php
600 // Get saved taxonomy data
601 $saved_taxonomy_data = array();
602 if (isset($item['taxonomy_data']) && !empty($item['taxonomy_data'])) {
603 $saved_taxonomy_data = json_decode($item['taxonomy_data'], true);
604 if (!is_array($saved_taxonomy_data)) {
605 $saved_taxonomy_data = array();
606 }
607 }
608
609 // Fallback: if no taxonomy_data but has category_id, use it for 'category' taxonomy
610 if (empty($saved_taxonomy_data) && isset($item['category_id']) && !empty($item['category_id'])) {
611 $saved_taxonomy_data['category'] = array_filter(explode(',', $item['category_id']));
612 }
613
614 // Get taxonomies for the current post type
615 // Skip tag taxonomies since AI generates tags automatically
616 $skip_taxonomies = array('post_tag', 'product_tag');
617 $taxonomies = get_object_taxonomies($current_post_type, 'objects');
618 foreach ($taxonomies as $taxonomy) {
619 if (!$taxonomy->public) {
620 continue;
621 }
622
623 // Skip tag taxonomies (AI generates tags)
624 if (in_array($taxonomy->name, $skip_taxonomies, true)) {
625 continue;
626 }
627
628 $terms = get_terms(array(
629 'taxonomy' => $taxonomy->name,
630 'hide_empty' => false,
631 'orderby' => 'name',
632 'order' => 'ASC',
633 ));
634
635 if (is_wp_error($terms) || empty($terms)) {
636 continue;
637 }
638
639 $selected_terms = isset($saved_taxonomy_data[$taxonomy->name]) ? (array)$saved_taxonomy_data[$taxonomy->name] : array();
640
641 // For 'category' taxonomy, if nothing selected, select first one
642 if ($taxonomy->name === 'category' && empty($selected_terms) && !empty($terms)) {
643 $selected_terms = array($terms[0]->term_id);
644 }
645 ?>
646 <div class="taxonomy-field" data-taxonomy="<?php echo esc_attr($taxonomy->name); ?>">
647 <label for="taxonomy_<?php echo esc_attr($taxonomy->name); ?>" class="form-label">
648 <?php echo esc_html($taxonomy->label); ?>:
649 </label>
650 <select id="taxonomy_<?php echo esc_attr($taxonomy->name); ?>"
651 name="taxonomy_terms[<?php echo esc_attr($taxonomy->name); ?>][]"
652 class="form-select taxonomy-select"
653 multiple>
654 <?php
655 foreach ($terms as $term) {
656 $is_selected = in_array($term->term_id, array_map('intval', $selected_terms)) ? 'selected' : '';
657 echo '<option value="' . esc_attr($term->term_id) . '" ' . esc_attr($is_selected) . '>' . esc_html($term->name) . '</option>';
658 }
659 ?>
660 </select>
661 </div>
662 <?php
663 }
664 ?>
665 <input type="hidden" name="taxonomy_data" id="taxonomy_data" value="<?php echo esc_attr(isset($item['taxonomy_data']) ? $item['taxonomy_data'] : ''); ?>">
666 </div>
667
668 <?php
669 $default_language_code = substr(get_locale(), 0, 2); // Obtiene el idioma predeterminado desde la configuración
670
671 // Asignar el idioma predeterminado si no hay idioma guardado
672 if (empty($item['post_language']) || $item['post_language'] == '') {
673 $item['post_language'] = $default_language_code;
674 }
675 ?>
676
677 <div class="col-md-6">
678 <label for="post_language" class="form-label">Post Language:</label>
679 <select class="form-select" id="post_language" name="post_language">
680 <?php
681 foreach ($botwriter_languages as $code => $name) {
682 $selected = ($item['post_language'] == $code) ? 'selected' : '';
683 echo "<option value='" . esc_attr($code) . "' " . esc_attr($selected) . " >" . esc_html($name) . "</option>";
684 }
685 ?>
686 </select>
687 </div>
688
689
690
691
692
693 <br>
694
695 <div class="col-md-6">
696 <label class="form-label"><?php echo esc_html__('Source of Ideas:', 'botwriter'); ?></label>
697
698 <!-- Radio Button Options with Icons -->
699 <div class="source-options">
700 <!-- Articles from the Own Blog -->
701 <label class="source-option">
702 <input type="radio" name="website_type" value="ai" <?php if ($item['website_type'] === 'ai') {
703 echo 'checked';
704 } ?>>
705
706 <img src="<?php echo esc_url($dir_images_icons . 'sameblog100.png'); ?>" alt="Own Blog Articles" class="source-icon">
707 <div class="writer-info">
708 <strong><?php echo esc_html__('AI Articles: topics, keywords, or instructions', 'botwriter'); ?></strong>
709 <p><?php echo esc_html__('Generate articles from topics or keywords, or provide detailed instructions (prompt). Tip: specify audience, tone, key points, and constraints.', 'botwriter'); ?></p>
710 </div>
711 </label>
712
713 <!-- WordPress External -->
714 <label class="source-option">
715 <input type="radio" name="website_type" value="wordpress" required <?php if ($item['website_type'] === 'wordpress') {
716 echo 'checked';
717 } ?>>
718 <img src="<?php echo esc_url($dir_images_icons . 'externalwp100.png'); ?>" alt="WordPress External" class="source-icon">
719 <div class="writer-info">
720 <strong><?php echo esc_html__('WordPress External', 'botwriter'); ?></strong>
721 <p><?php echo esc_html__('Inspired by articles from an external WordPress, potentially in other languages. It rewrites the content and designs a completely new image.', 'botwriter'); ?></p>
722 </div>
723 </label>
724
725 <!-- Google News -->
726 <label class="source-option" style="display:none;">
727 <input type="radio" name="website_type" value="news" <?php if ($item['website_type'] === 'news') {
728 echo 'checked';
729 } ?>>
730 <img src="<?php echo esc_url($dir_images_icons . 'news100.png'); ?>" alt="Google News" class="source-icon">
731 <div class="writer-info">
732 <strong><?php echo esc_html__('Google News', 'botwriter'); ?></strong>
733 <p><?php echo esc_html__('Extracts trending news from Google News to rewrite and adapt to your blog’s audience. You can select the topic or keyword.', 'botwriter'); ?></p>
734 </div>
735 </label>
736
737 <!-- RSS (Web with RSS) -->
738 <label class="source-option">
739 <input type="radio" name="website_type" value="rss" <?php if ($item['website_type'] === 'rss') {
740 echo 'checked';
741 } ?>>
742
743 <img src="<?php echo esc_url($dir_images_icons . 'rss100.png'); ?>" alt="RSS" class="source-icon">
744 <div class="writer-info">
745 <strong><?php echo esc_html__('RSS (Web with RSS)', 'botwriter'); ?></strong>
746 <p><?php echo esc_html__('Extracts articles from a website with an RSS feed to rewrite and adapt to your blog’s audience. You can select the topic or keyword.', 'botwriter'); ?></p>
747 </div>
748 </label>
749
750
751
752 <!-- External Research -->
753 <!-- hidden
754 <label class="source-option">
755 <input type="radio" name="website_type" value="external_research" <?php if ($item['website_type'] === 'external_research') {
756 echo 'checked';
757 } ?>>
758 <img src="<?php echo esc_url($dir_images_icons . 'external100.png'); ?>" alt="External Research" class="source-icon">
759 <div class="writer-info">
760 <strong><?php echo esc_html__('External Research', 'botwriter'); ?></strong>
761 <p><?php echo esc_html__('Extracts information from external sources to rewrite and adapt to your blog’s audience. You can select the topic or keyword.', 'botwriter'); ?></p>
762 </div>
763 </label>
764 !-->
765
766 </div>
767 </div>
768 <br>
769 <!-- Url of Site of Extern Wordpress -->
770 <div class="col-md-6" id="div_website_domainname">
771 <label class="form-label">Url of Site of Extern Wordpress:</label>
772 <input id="domain_name" name="domain_name" type="text" class="form-control" value="<?php echo esc_attr($item['domain_name']); ?>" >
773 <p class="form-text">Enter the URL of the external WordPress site from which you want to get ideas. Later get the categories.</p>
774 </div>
775
776
777
778 <!-- website_category_id -->
779 <div class="col-md-6" id="div_website_category_id">
780 <label for="website_category_id" class="form-label">External Website Categories:</label><br>
781 <input type="hidden" name="website_category_name" value="<?php echo esc_attr($item['website_category_name']); ?>">
782
783 <?php
784 if (!$is_empty) {
785 echo esc_html__('Previously selected: ', 'botwriter') . esc_html($item['website_category_name']);
786 }
787 ?>
788
789
790
791
792 <select id="website_category_id" name="website_category_id[]" multiple class="form-select" <?php
793 if ($is_empty) {
794 echo 'style="display: none;"';
795 }
796 ?>>
797 <?php
798
799 //Get selected categories
800 $selected_website_categories = $item['website_category_id'];
801 //Turn categories to array list
802 $selected_website_categories = explode(',', $selected_website_categories);
803
804
805 ?>
806 </select>
807 <button type="button" class="btn btn-primary" id="refresh_website_categories_btn">
808 <i class="bi bi-arrow-clockwise"></i>
809 <?php
810 $category_button_name = 'Get Categories';
811
812 if (!$is_empty) {
813 $category_button_name = 'Refresh';
814 }
815
816 echo esc_html($category_button_name);
817 ?>
818 </button>
819 </div>
820 <br>
821
822
823 <!-- News -->
824 <div id="div_news">
825 <div class="col-md-6" >
826 <label class="form-label">Google News Keywords:</label>
827 <input id="news_keyword" name="news_keyword" type="text" class="form-control" value="<?php echo esc_attr($item['news_keyword']); ?>" >
828 <p class="form-text">Try on https://news.google.com to see the type of news that will appear</p>
829 </div>
830 <br>
831
832 <?php
833 $locale = get_locale();
834 $default_country_code = substr($locale, -2); // Obtiene el código del país ('ES')
835 $default_language_code = substr($locale, 0, 2); // Obtiene el código del idioma ('es')
836 ?>
837
838 <div class="col-md-6">
839 <label class="form-label" for="news_country">Google News Country:</label>
840 <select name="news_country" class="form-select">
841 <!-- ISO 3166-1 alpha-2 country codes -->
842 <!-- Replace with actual country codes and names -->
843 <?php
844 foreach ($botwriter_countries as $code => $name) {
845 $selected = ($item['news_country'] == $code) ? 'selected' : (($code == strtolower($default_country_code) && empty($item['news_country'])) ? 'selected' : '');
846 echo "<option value='" . esc_attr($code) . "' " . esc_attr($selected) . " >" . esc_html($name) . "</option>";
847 }
848 ?>
849 </select>
850 </div>
851 <br>
852
853 <div class="col-md-6">
854 <label class="form-label" for="news_language">Google News Language:</label>
855 <select name="news_language" class="form-select">
856 <!-- ISO 639-1 alpha-2 language codes -->
857 <!-- Replace with actual language codes and names -->
858 <?php
859
860 foreach ($botwriter_languages as $code => $name) {
861 $selected = ($item['news_language'] == $code) ? 'selected' : (($code == strtolower($default_language_code) && empty($item['news_language'])) ? 'selected' : '');
862 echo "<option value='" . esc_attr($code) . "' " . esc_attr($selected) . " >" . esc_html($name) . "</option>";
863
864 }
865
866 ?>
867 </select>
868 </div>
869 <br>
870
871 <div class="col-md-6">
872 <label class="form-label" for="news_time_published">Google News Time Published:</label>
873 <select name="news_time_published" class="form-select">
874 <?php
875 $time_options = [
876 'h' => 'Last Hour',
877 'd' => 'Last Day',
878 'w' => 'Last Week',
879 'y' => 'Last Year',
880 '' => 'Anytime',
881 ];
882
883 foreach ($time_options as $value => $label) {
884 $selected = ($item['news_time_published'] == $value) ? 'selected' : '';
885
886 echo "<option value='" . esc_attr($value) . "' " . esc_attr($selected) . " >" . esc_html($label) . "</option>";
887
888 }
889 ?>
890 </select>
891 </div>
892 <br>
893 <div class="col-md-6" >
894 <label class="form-label">Google News Source:</label>
895 <input id="news_source" name="news_source" type="text" class="form-control" value="<?php echo esc_attr($item['news_source']); ?>" >
896 <p class="form-text">Optional. Enter the source (website) of the news you want to get ideas from. For e.g. https://bbc.com or https://cnn.com</p>
897 </div>
898
899 </div>
900 <!-- End News -->
901
902 <!-- RSS -->
903 <div class="col-md-6" id="div_rss">
904 <label class="form-label">RSS Feed URL:</label>
905 <input id="rss_source" name="rss_source" type="text" class="form-control" value="<?php echo esc_attr($item['rss_source']); ?>" >
906 <p class="form-text">Enter the URL of the RSS feed. View <a href="https://github.com/plenaryapp/awesome-rss-feeds" target="_blank">awesome RSS feeds</a></p>
907 <button type="button" class="btn btn-primary" id="check_rss_feed_btn">Check RSS Feed</button>
908 <div id="rss_response"></div>
909 </div>
910 <!-- End RSS -->
911
912 <!-- AI -->
913 <div class="col-md-6" id="div_ai">
914 <label class="form-label">AI Keywords / Prompt:</label>
915 <textarea id="ai_keywords" name="ai_keywords" class="form-control" rows="6"><?php echo esc_textarea($item['ai_keywords']); ?></textarea>
916 <p class="form-text">Enter keywords or topics separated by commas, or write a prompt with instructions.</p>
917 <p class="form-text" style="background-color: #e7f3ff; padding: 8px 12px; border-radius: 4px; border-left: 3px solid #2271b1;"><strong>Tip:</strong> For full control over the prompt, select the "Custom Prompt (Empty)" template.</p>
918 </div>
919 <br>
920
921 <!-- Template Selection -->
922 <div class="col-md-6" id="div_template">
923 <label class="form-label"><?php esc_html_e('Prompt Template:', 'botwriter'); ?></label>
924 <select id="template_id" name="template_id" class="form-select">
925 <?php
926 $templates = botwriter_get_all_templates();
927 $selected_template_id = isset($item['template_id']) && !empty($item['template_id']) ? intval($item['template_id']) : null;
928
929 // If no template selected, find the default one
930 if ($selected_template_id === null) {
931 foreach ($templates as $tpl) {
932 if (!empty($tpl['is_default'])) {
933 $selected_template_id = intval($tpl['id']);
934 break;
935 }
936 }
937 }
938
939 foreach ($templates as $tpl) {
940 $label = (string) $tpl['name'];
941 if (!empty($tpl['is_default'])) {
942 $label .= ' �
943 ';
944 }
945 echo '<option value="' . esc_attr($tpl['id']) . '"';
946 selected($selected_template_id, $tpl['id']);
947 echo '>' . esc_html($label) . '</option>';
948 }
949 ?>
950 </select>
951 <p class="form-text"><?php esc_html_e('Select the prompt template to use for content generation. Templates marked with �
952 are the default.', 'botwriter'); ?></p>
953 </div>
954 <br>
955
956 <!-- End AI -->
957
958 <!-- Post Length -->
959 <div class="col-md-6">
960 <label class="form-label">Post Length:</label>
961 <select id="post_length" name="post_length" class="form-select">
962 <option value="400" <?php echo ($item['post_length'] == 400) ? 'selected' : ''; ?>>Short (400 words)</option>
963 <option value="800" <?php echo ($item['post_length'] == 800) ? 'selected' : ''; ?>>Medium (800 words)</option>
964 <option value="1600" <?php echo ($item['post_length'] == 1600) ? 'selected' : ''; ?>>Long (1600 words)</option>
965 <option value="custom" <?php echo (!in_array($item['post_length'], [400, 800, 1600])) ? 'selected' : ''; ?>>Custom</option>
966 </select>
967 <p class="form-text">Select the desired post length or choose custom to enter a specific number of words.</p>
968 </div>
969 <!-- End Post Length -->
970
971
972
973 <!-- Custom Length Input -->
974 <div class="col-md-6" id="customLengthInput" style="display: <?php echo (!in_array($item['post_length'], [400, 800, 1600])) ? 'block' : 'none'; ?>;">
975 <label class="form-label">Custom Length (max 4000):</label>
976 <input id="custom_post_length" name="custom_post_length" type="number" class="form-control" value="<?php echo esc_html($item['custom_post_length']) ?>">
977 <p class="form-text">Enter the number of words you want the post to have.</p>
978 </div>
979 <br>
980
981 <!-- Disable AI Images -->
982 <div class="col-md-6">
983 <?php
984 $image_provider = get_option('botwriter_image_provider', 'stockphoto');
985 $force_disable_images = ($image_provider === 'none');
986 ?>
987 <label class="form-label"><?php esc_html_e('AI Image Generation:', 'botwriter'); ?></label>
988 <input type="hidden" name="disable_ai_images" value="<?php echo $force_disable_images ? '1' : '0'; ?>">
989 <input type="checkbox" name="disable_ai_images" value="1" id="disable_ai_images_checkbox"
990 <?php checked($force_disable_images || (isset($item['disable_ai_images']) ? $item['disable_ai_images'] : 0), 1); ?>
991 <?php echo $force_disable_images ? 'disabled' : ''; ?>>
992 <span><?php esc_html_e('Disable AI image generation for this task', 'botwriter'); ?></span>
993 <?php if ($force_disable_images): ?>
994 <p class="form-text bw-text-error bw-disabled-images-warning"><span class="dashicons dashicons-info bw-icon-small"></span> <?php esc_html_e('Image generation is globally disabled. Change the Image Provider in Settings to enable it.', 'botwriter'); ?></p>
995 <?php else: ?>
996 <p class="form-text" id="disable_ai_images_description"><?php esc_html_e('Check this option to generate only text articles without AI-generated images for this specific task. This can speed up processing and reduce costs.', 'botwriter'); ?></p>
997 <?php endif; ?>
998 <?php
999 $is_checked = $force_disable_images || (isset($item['disable_ai_images']) ? $item['disable_ai_images'] : 0);
1000 $tip_style = $is_checked ? '' : 'display:none;';
1001 ?>
1002 <p class="form-text bw-asi-inline-tip" id="bw_asi_tip" style="<?php echo esc_attr($tip_style); ?>"><span class="dashicons dashicons-images-alt2"></span> <?php printf(
1003 /* translators: %s: link to All Sources Images plugin */
1004 esc_html__('Tip: Use %s to add free images from Pexels, Unsplash & Pixabay automatically.', 'botwriter'),
1005 '<a href="' . esc_url(admin_url('plugin-install.php?s=all+sources+images&tab=search&type=term')) . '" target="_blank">All Sources Images</a>'
1006 ); ?></p>
1007 </div>
1008 <br>
1009
1010
1011
1012
1013 </form>
1014 </div>
1015
1016 <?php
1017 }
1018
1019