diff --git a/api/coauthor.php b/api/coauthor.php
index ec3ff6f..eccf078 100644
--- a/api/coauthor.php
+++ b/api/coauthor.php
@@ -47,11 +47,8 @@ add_action('rest_api_init', function() {
'photo' => $photo,
'photo_sizes' => $photo_sizes,
- // Оставляем gravatar как запасной вариант, если фото нет
- 'gravatar' => get_avatar_url($coauthor->user_email, ['size' => 192]),
-
// Для обратной совместимости
- 'avatar' => $photo ?: get_avatar_url($coauthor->user_email, ['size' => 192]),
+ 'avatar' => $photo ?: '',
'url' => get_author_posts_url($coauthor->ID, $coauthor->user_nicename),
'type' => $coauthor->type ?? 'guest-author'
diff --git a/functions.php b/functions.php
index 746d2c5..2499b27 100644
--- a/functions.php
+++ b/functions.php
@@ -24,6 +24,7 @@ if ( defined( 'WP_CLI' ) && WP_CLI ) {
require "inc/action-scheduler-functions.php";
require "inc/wp-cli-scheduler-commands.php";
require "inc/adfox_on.php"; // управление adfox
+ require "inc/generate_coauthors_cache.php"; // генерим кеш списка авторов
}
include "inc/get_cached_alm.php";
diff --git a/inc/action-scheduler-functions.php b/inc/action-scheduler-functions.php
new file mode 100644
index 0000000..c4503de
--- /dev/null
+++ b/inc/action-scheduler-functions.php
@@ -0,0 +1,166 @@
+post_status ) {
+ return false;
+ }
+
+ $post_url = get_permalink( $post_id );
+ $post_title = $post->post_title;
+
+ $delay = 0;
+
+ //google news
+ as_schedule_single_action(
+ time() + $delay,
+ 'process_sitemap_submission',
+ [],
+ 'sitemap_generation'
+ );
+
+ //IndexNow
+ as_schedule_single_action(
+ time() + 5,
+ 'process_indexnow_submission',
+ [ $post_id, $post_url, $post_title ],
+ 'post_publish_processing'
+ );
+
+ return true;
+
+ } catch ( Exception $e ) {
+ // Логируем ошибку
+ error_log( "failed for post {$post_id}: " . $e->getMessage() );
+ log_scheduler_activity( "failed for post {$post_id}: " . $e->getMessage() );
+
+ // Пробрасываем исключение для перевода задачи в Failed
+ throw $e;
+ }
+
+}
+
+
+function log_scheduler_activity( $message, $post_id = null ) {
+
+ $log_file = ABSPATH . 'scheduler.log';
+ $log_file = '/var/www/profile/html/scheduler.log';
+
+ $log_entry = sprintf(
+ "[%s] %s%s\n",
+ current_time( 'Y-m-d H:i:s' ),
+ $post_id ? "Post {$post_id}: " : "",
+ $message
+ );
+
+ file_put_contents( $log_file, $log_entry, FILE_APPEND | LOCK_EX );
+}
+
+
+
+// generation google news php82 /usr/bin/wp eval-file test-theme-load.php --path=/var/www/profile/html
+add_action('process_sitemap_submission', 'handle_sitemap_submission');
+
+function handle_sitemap_submission() {
+
+ $generator = new AK_Sitemap_Generator();
+ $result = $generator->generate_news_sitemap([ 'profile_article', 'anew', 'yellow' ]);
+
+ if ($result) {
+ error_log('News Sitemap generated successfully');
+ } else {
+ error_log('News Sitemap generation failed');
+ }
+
+ return $result;
+
+}
+
+//index now
+function process_indexnow_submission( $post_id, $post_url, $post_title ) {
+
+
+ try {
+
+ // Проверяем флаг отправки
+ if ( get_post_meta( $post_id, '_indexnow_sent', true ) ) {
+ return;
+ }
+
+ // Ваша логика отправки в IndexNow
+ $api_key = 'b1a2g3d4i8f6g7h8i9juyg0k11l12';
+ //$domain = parse_url( home_url(), PHP_URL_HOST );
+ $domain = 'https://profile.ru';
+
+
+ $body = array(
+ 'host' => $domain,
+ 'key' => $api_key,
+ // 'keyLocation' => esc_url( home_url( '/' . $api_key . '.txt' ) ), // ❌ НЕ НУЖНО
+ 'urlList' => array( $post_url )
+ );
+
+ $response = wp_remote_post( 'https://api.indexnow.org/IndexNow', array(
+ 'body' => wp_json_encode( $body ),
+ 'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
+ 'timeout' => 30,
+ ));
+
+ // ПРАВИЛЬНАЯ ПРОВЕРКА ОТВЕТА
+ if ( is_wp_error( $response ) ) {
+ log_scheduler_activity( "IndexNow WP Error for post {$post_id}: " . $response->get_error_message(), $post_id );
+ return false;
+ }
+
+ $response_code = wp_remote_retrieve_response_code( $response );
+ $response_body = wp_remote_retrieve_body( $response );
+
+ // IndexNow возвращает 200/201 при успехе
+ if ( in_array( $response_code, array( 200, 201, 202 ) ) ) {
+ // Помечаем как успешно отправленный
+ update_post_meta( $post_id, '_indexnow_sent', current_time( 'mysql' ) );
+ log_scheduler_activity( "IndexNow success {$post_url}", $post_id );
+ return true;
+ } else {
+ log_scheduler_activity( "IndexNow failed {$post_url}. Status: {$response_code} | Response: {$response_body}", $post_id );
+ return false;
+ }
+
+ } catch ( Exception $e ) {
+ // Логируем ошибку
+ error_log( "IndexNow failed for post {$post_id}: " . $e->getMessage() );
+
+ echo $e->getMessage();
+
+ // Пробрасываем исключение для перевода задачи в Failed
+ throw $e;
+ }
+
+
+}
\ No newline at end of file
diff --git a/inc/action-scheduler-functions_15_10_25.php b/inc/action-scheduler-functions_15_10_25.php
new file mode 100644
index 0000000..615a4de
--- /dev/null
+++ b/inc/action-scheduler-functions_15_10_25.php
@@ -0,0 +1,159 @@
+post_status ) {
+ return false;
+ }
+
+ $post_url = get_permalink( $post_id );
+ $post_title = $post->post_title;
+
+ //google news
+ as_schedule_single_action(
+ time() + $delay,
+ 'process_sitemap_submission',
+ [],
+ 'sitemap_generation'
+ );
+
+ //IndexNow
+ as_schedule_single_action(
+ time() + 5,
+ 'process_indexnow_submission',
+ [ $post_id, $post_url, $post_title ],
+ 'post_publish_processing'
+ );
+
+ return true;
+
+ } catch ( Exception $e ) {
+ // Логируем ошибку
+ error_log( "failed for post {$post_id}: " . $e->getMessage() );
+ log_scheduler_activity( "failed for post {$post_id}: " . $e->getMessage() );
+
+ // Пробрасываем исключение для перевода задачи в Failed
+ throw $e;
+ }
+
+}
+
+
+function log_scheduler_activity( $message, $post_id = null ) {
+
+ $log_file = ABSPATH . 'scheduler.log';
+
+ $log_entry = sprintf(
+ "[%s] %s%s\n",
+ current_time( 'Y-m-d H:i:s' ),
+ $post_id ? "Post {$post_id}: " : "",
+ $message
+ );
+
+ file_put_contents( $log_file, $log_entry, FILE_APPEND | LOCK_EX );
+}
+
+
+
+// generation google news
+add_action('process_sitemap_submission', 'handle_sitemap_submission');
+
+function handle_sitemap_submission() {
+ $generator = new AK_Sitemap_Generator();
+ $result = $generator->generate_news_sitemap([ 'profile_article', 'anew', 'yellow' ]);
+
+ if ($result) {
+ error_log('News Sitemap generated successfully');
+ } else {
+ error_log('News Sitemap generation failed');
+ }
+
+ return $result;
+}
+
+//index now
+function process_indexnow_submission( $post_id, $post_url, $post_title ) {
+
+
+ try {
+
+ // Проверяем флаг отправки
+ if ( get_post_meta( $post_id, '_indexnow_sent', true ) ) {
+ return;
+ }
+
+ // Ваша логика отправки в IndexNow
+ $api_key = 'b1a2g3d4i8f6g7h8i9juyg0k11l12';
+ //$domain = parse_url( home_url(), PHP_URL_HOST );
+ $domain = 'https://profile.ru';
+
+
+ $body = array(
+ 'host' => $domain,
+ 'key' => $api_key,
+ // 'keyLocation' => esc_url( home_url( '/' . $api_key . '.txt' ) ), // ❌ НЕ НУЖНО
+ 'urlList' => array( $post_url )
+ );
+
+ $response = wp_remote_post( 'https://api.indexnow.org/IndexNow', array(
+ 'body' => wp_json_encode( $body ),
+ 'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
+ 'timeout' => 30,
+ ));
+
+ // ПРАВИЛЬНАЯ ПРОВЕРКА ОТВЕТА
+ if ( is_wp_error( $response ) ) {
+ log_scheduler_activity( "IndexNow WP Error for post {$post_id}: " . $response->get_error_message(), $post_id );
+ return false;
+ }
+
+ $response_code = wp_remote_retrieve_response_code( $response );
+ $response_body = wp_remote_retrieve_body( $response );
+
+ // IndexNow возвращает 200/201 при успехе
+ if ( in_array( $response_code, array( 200, 201, 202 ) ) ) {
+ // Помечаем как успешно отправленный
+ update_post_meta( $post_id, '_indexnow_sent', current_time( 'mysql' ) );
+ log_scheduler_activity( "IndexNow success {$post_url}", $post_id );
+ return true;
+ } else {
+ log_scheduler_activity( "IndexNow failed {$post_url}. Status: {$response_code} | Response: {$response_body}", $post_id );
+ return false;
+ }
+
+ } catch ( Exception $e ) {
+ // Логируем ошибку
+ error_log( "IndexNow failed for post {$post_id}: " . $e->getMessage() );
+
+ echo $e->getMessage();
+
+ // Пробрасываем исключение для перевода задачи в Failed
+ throw $e;
+ }
+
+
+}
\ No newline at end of file
diff --git a/inc/add_adv_checked.php b/inc/add_adv_checked.php
new file mode 100644
index 0000000..3fa4391
--- /dev/null
+++ b/inc/add_adv_checked.php
@@ -0,0 +1,124 @@
+ID, '_is_advertisement', true);
+ wp_nonce_field('advertisement_nonce', 'advertisement_nonce_field');
+ echo '';
+ },
+ ['anew', 'yellow', 'profile_article'],
+ 'side',
+ 'low'
+ );
+
+ // Метабокс для Аэрофлота (только для profile_article)
+ add_meta_box(
+ 'aeroflot_meta_box',
+ 'Лента Аэрофлота',
+ function($post) {
+ $no_aeroflot = get_post_meta($post->ID, '_no_aeroflot', true);
+ wp_nonce_field('aeroflot_nonce', 'aeroflot_nonce_field');
+ echo '';
+ },
+ ['profile_article'], // Только для этого типа записей
+ 'side',
+ 'low'
+ );
+});
+
+add_action('save_post', function($post_id) {
+ // Сохранение рекламного материала
+ if (isset($_POST['advertisement_nonce_field']) && wp_verify_nonce($_POST['advertisement_nonce_field'], 'advertisement_nonce')) {
+ if (!defined('DOING_AUTOSAVE') || !DOING_AUTOSAVE) {
+ if (current_user_can('edit_post', $post_id)) {
+ update_post_meta($post_id, '_is_advertisement', isset($_POST['is_advertisement']) ? '1' : '0');
+ }
+ }
+ }
+
+ // Сохранение настройки Аэрофлот (только для profile_article)
+ if (isset($_POST['aeroflot_nonce_field']) && wp_verify_nonce($_POST['aeroflot_nonce_field'], 'aeroflot_nonce')) {
+ if (!defined('DOING_AUTOSAVE') || !DOING_AUTOSAVE) {
+ if (current_user_can('edit_post', $post_id)) {
+ update_post_meta($post_id, '_no_aeroflot', isset($_POST['no_aeroflot']) ? '1' : '0');
+ }
+ }
+ }
+});
+
+add_action('admin_head', function() {
+ echo '';
+});
\ No newline at end of file
diff --git a/inc/add_adv_checked_advonly.php b/inc/add_adv_checked_advonly.php
new file mode 100644
index 0000000..d692177
--- /dev/null
+++ b/inc/add_adv_checked_advonly.php
@@ -0,0 +1,46 @@
+ID, '_is_advertisement', true);
+ wp_nonce_field('advertisement_nonce', 'advertisement_nonce_field');
+ echo '';
+ },
+ ['anew', 'yellow', 'profile_article'],
+ 'side',
+ 'low'
+ );
+});
+
+add_action('save_post', function($post_id) {
+ if (!isset($_POST['advertisement_nonce_field']) || !wp_verify_nonce($_POST['advertisement_nonce_field'], 'advertisement_nonce')) return;
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
+ if (!current_user_can('edit_post', $post_id)) return;
+
+ update_post_meta($post_id, '_is_advertisement', isset($_POST['is_advertisement']) ? '1' : '0');
+});
+
+add_action('admin_head', function() {
+ echo '';
+});
\ No newline at end of file
diff --git a/inc/adfox_on.php b/inc/adfox_on.php
new file mode 100644
index 0000000..114a70d
--- /dev/null
+++ b/inc/adfox_on.php
@@ -0,0 +1,74 @@
+show_status();
+ break;
+ case 'on':
+ $this->turn_on();
+ break;
+ case 'off':
+ $this->turn_off();
+ break;
+ case 'toggle':
+ $this->toggle();
+ break;
+ default:
+ WP_CLI::error("Неизвестная команда: {$action}. Используйте: status, on, off, toggle");
+ }
+ }
+
+ private function show_status() {
+ $show_ad = get_option('show_ad', 0);
+ $status = ((int)$show_ad === 1) ? '✅ включена' : '❌ выключена';
+
+ WP_CLI::line("Текущий статус рекламы: {$status}");
+ WP_CLI::line("Значение в базе: {$show_ad}");
+ }
+
+ private function turn_on() {
+ update_option('show_ad', 1);
+ WP_CLI::success('✅ Реклама включена');
+ $this->show_status();
+ }
+
+ private function turn_off() {
+ update_option('show_ad', 0);
+ WP_CLI::success('❌ Реклама выключена');
+ $this->show_status();
+ }
+
+ private function toggle() {
+ $current = get_option('show_ad', 0);
+ $new_value = ((int)$current === 1) ? 0 : 1;
+ $action = ($new_value === 1) ? '✅ включена' : '❌ выключена';
+
+ update_option('show_ad', $new_value);
+ WP_CLI::success("Реклама {$action}");
+ $this->show_status();
+ }
+}
+
+// Регистрируем команду
+if (defined('WP_CLI') && WP_CLI) {
+ WP_CLI::add_command('ad', 'Ad_Manager_Commands');
+}
\ No newline at end of file
diff --git a/inc/admin/auto_check.php b/inc/admin/auto_check.php
new file mode 100644
index 0000000..bb32667
--- /dev/null
+++ b/inc/admin/auto_check.php
@@ -0,0 +1,38 @@
+base === 'post') {
+ ?>
+
+ [
+ 'background' => '#9e5a63', // фон для banned
+ 'color' => '#ffffff', // белый цвет для рамки
+ 'placeholder_color' => '#ffffff', // цвет placeholder для banned
+ 'allow_new_tags' => false // запрещаем новые теги для banned
+ ],
+ 'keys' => [
+ 'background' => '#e8f2ff', // синий фон для keys
+ 'color' => '#2271b1', // синий цвет для рамки
+ 'placeholder_color' => '#2271b1', // цвет placeholder для keys
+ 'allow_new_tags' => true // разрешаем новые теги для keys
+ ],
+ 'post_tag' => [
+ 'background' => '#31708e', // светлый фон для post_tag
+ 'color' => '#ffffff', // серый цвет для рамки
+ 'placeholder_color' => '#ffffff', // цвет placeholder для post_tag
+ 'allow_new_tags' => false // запрещаем новые теги для post_tag
+ ]
+ ];
+ ?>
+
+
+
+
+ $taxonomy,
+ 'hide_empty' => false,
+ 'number' => 100,
+ ];
+
+ // Если есть поисковый запрос, добавляем поиск и сортировку по релевантности
+ if (!empty($search)) {
+ $args['search'] = $search;
+ $args['orderby'] = 'name';
+ $args['order'] = 'ASC';
+ }
+
+ $terms = get_terms($args);
+
+ $result = [];
+ foreach ($terms as $term) {
+ $result[] = [
+ 'id' => $term->term_id,
+ 'text' => $term->name
+ ];
+ }
+
+ // Сортируем по релевантности, если есть поисковый запрос
+ if (!empty($search)) {
+ usort($result, function($a, $b) use ($search) {
+ $a_text = $a['text'];
+ $b_text = $b['text'];
+ $search_lower = strtolower($search);
+
+ // Приоритет: точное совпадение
+ if (strtolower($a_text) === $search_lower && strtolower($b_text) !== $search_lower) {
+ return -1;
+ }
+ if (strtolower($b_text) === $search_lower && strtolower($a_text) !== $search_lower) {
+ return 1;
+ }
+
+ // Приоритет: начинается с поискового запроса
+ $a_starts = stripos($a_text, $search) === 0;
+ $b_starts = stripos($b_text, $search) === 0;
+
+ if ($a_starts && !$b_starts) {
+ return -1;
+ }
+ if ($b_starts && !$a_starts) {
+ return 1;
+ }
+
+ // Приоритет: содержит поисковый запрос
+ $a_contains = stripos($a_text, $search) !== false;
+ $b_contains = stripos($b_text, $search) !== false;
+
+ if ($a_contains && !$b_contains) {
+ return -1;
+ }
+ if ($b_contains && !$a_contains) {
+ return 1;
+ }
+
+ // Если одинаковый приоритет - сортируем по алфавиту
+ return strcasecmp($a_text, $b_text);
+ });
+ }
+
+ wp_send_json($result);
+});
+
+// AJAX обработчик для получения терминов поста
+add_action('wp_ajax_get_post_terms', function () {
+ $taxonomy = sanitize_key($_POST['taxonomy']);
+ $post_id = (int)$_POST['post_id'];
+
+ $terms = wp_get_post_terms($post_id, $taxonomy);
+
+ $result = [];
+ foreach ($terms as $term) {
+ $result[] = [
+ 'id' => $term->term_id,
+ 'text' => $term->name
+ ];
+ }
+
+ wp_send_json($result);
+});
+
+// AJAX обработчик для получения популярных тегов
+add_action('wp_ajax_get_popular_terms', function () {
+ $taxonomy = sanitize_key($_GET['taxonomy']);
+
+ // Получаем популярные теги (с наибольшим количеством постов)
+ $terms = get_terms([
+ 'taxonomy' => $taxonomy,
+ 'orderby' => 'count',
+ 'order' => 'DESC',
+ 'number' => 20,
+ 'hide_empty' => false,
+ ]);
+
+ $result = [];
+ foreach ($terms as $term) {
+ $result[] = [
+ 'id' => $term->term_id,
+ 'name' => $term->name,
+ 'count' => $term->count // Добавляем количество использований
+ ];
+ }
+
+ wp_send_json($result);
+});
+
+// Обработчик сохранения с исправленной логикой для новых тегов
+add_action('save_post', function($post_id) {
+ // Проверяем права пользователя
+ if (!current_user_can('edit_post', $post_id)) return;
+
+ // Убираем автосохранение
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
+
+ // Отладочная информация
+ error_log('Save post hook called for post: ' . $post_id);
+ error_log('POST data for keys: ' . ($_POST['keys_ids'] ?? 'not set'));
+ error_log('POST data for banned: ' . ($_POST['banned_ids'] ?? 'not set'));
+ error_log('POST data for post_tag: ' . ($_POST['post_tag_ids'] ?? 'not set'));
+
+ $taxonomies = ['banned', 'keys', 'post_tag'];
+
+ foreach ($taxonomies as $taxonomy) {
+ if (isset($_POST[$taxonomy . '_ids'])) {
+ $values = explode(',', $_POST[$taxonomy . '_ids']);
+ $term_ids = [];
+
+ foreach ($values as $value) {
+ $value = trim($value);
+ if (empty($value)) continue;
+
+ // Если значение НЕ число, это новый тег (только для keys)
+ if (!is_numeric($value) && $taxonomy === 'keys') {
+ // Это название нового тега
+ $term_name = $value;
+ if (!empty($term_name)) {
+ // Создаем новый терм
+ $new_term = wp_insert_term($term_name, $taxonomy);
+ if (!is_wp_error($new_term)) {
+ $term_ids[] = $new_term['term_id'];
+ error_log('Created new term: ' . $term_name . ' with ID: ' . $new_term['term_id']);
+ } else if ($new_term->get_error_code() === 'term_exists') {
+ // Если терм уже существует, получаем его ID
+ $existing_term = get_term_by('name', $term_name, $taxonomy);
+ if ($existing_term) {
+ $term_ids[] = $existing_term->term_id;
+ error_log('Term already exists: ' . $term_name . ' with ID: ' . $existing_term->term_id);
+ }
+ } else {
+ error_log('Error creating term: ' . $term_name . ' - ' . $new_term->get_error_message());
+ }
+ }
+ } else {
+ // Существующий ID термина
+ $term_ids[] = intval($value);
+ }
+ }
+
+ $term_ids = array_filter($term_ids);
+ if (!empty($term_ids)) {
+ wp_set_object_terms($post_id, $term_ids, $taxonomy, false);
+ error_log('Set terms for ' . $taxonomy . ': ' . implode(', ', $term_ids));
+ } else {
+ // Если нет терминов, очищаем
+ wp_set_object_terms($post_id, [], $taxonomy, false);
+ error_log('Cleared terms for ' . $taxonomy);
+ }
+ }
+}
+});
\ No newline at end of file
diff --git a/inc/article-rss-feed.php b/inc/article-rss-feed.php
new file mode 100644
index 0000000..f39d52d
--- /dev/null
+++ b/inc/article-rss-feed.php
@@ -0,0 +1,271 @@
+output_rss_feed($date);
+ exit;
+ }
+ }
+
+ /**
+ * Получаем URL иконки сайта
+ */
+private function get_site_icon_url() {
+ $site_icon_id = get_option('site_icon');
+ if ($site_icon_id) {
+ return wp_get_attachment_image_url($site_icon_id, 'full');
+ }
+
+ // Fallback на логотип или дефолтную иконку
+ $custom_logo_id = get_theme_mod('custom_logo');
+ if ($custom_logo_id) {
+ return wp_get_attachment_image_url($custom_logo_id, 'full');
+ }
+
+ return home_url('/wp-admin/images/wordpress-logo.png');
+}
+
+
+private function get_clean_post_content($post) {
+
+ $content = apply_filters('the_content', $post->post_content);
+
+ // Удаляем запрещенные для Турбо элементы
+ $content = preg_replace('/
+ ';
+}
+
+
+// Добавляем стили и скрипты
+add_action('wp_enqueue_scripts', 'add_sql_debug_styles');
+
+function add_sql_debug_styles() {
+ if (!current_user_can('administrator')) {
+ return;
+ }
+
+ wp_enqueue_style('sql-debug-style', false);
+ echo '
+
+ ';
+}
\ No newline at end of file
diff --git a/inc/test_action_scheduler.php b/inc/test_action_scheduler.php
new file mode 100644
index 0000000..a6ebb08
--- /dev/null
+++ b/inc/test_action_scheduler.php
@@ -0,0 +1,53 @@
+post_type, array( 'profile_article','anew','yellow' ), true ) ) {
+ return;
+ }
+
+ if ( 'publish' !== $post->post_status ) {
+ return;
+ }
+
+ // Планируем задачу
+ as_schedule_single_action(
+ time() + 60,
+ 'test_action_scheduler_task',
+ array( $post_id, $post->post_title ),
+ 'test'
+ );
+
+ //error_log( "Задача запланирована для поста ID: {$post_id}" );
+}
+
+add_action( 'save_post', 'test_schedule_action_on_save_post', 10, 3 );
+
diff --git a/inc/wp-cli-scheduler-commands.php b/inc/wp-cli-scheduler-commands.php
new file mode 100644
index 0000000..4090abc
--- /dev/null
+++ b/inc/wp-cli-scheduler-commands.php
@@ -0,0 +1,159 @@
+
+ * : ID задачи Action Scheduler
+ *
+ * [--retry]
+ * : Сбросить статус failed перед запуском
+ *
+ * ## EXAMPLES
+ *
+ * wp scheduler force-run-action 11843340
+ * wp scheduler force-run-action 456 --retry
+ *
+ * @param array $args
+ * @param array $assoc_args
+ */
+public function force_run_action( $args, $assoc_args ) {
+ list( $action_id ) = $args;
+ $retry = isset( $assoc_args['retry'] );
+
+ WP_CLI::log( "Принудительный запуск задачи ID: {$action_id}" );
+
+ global $wpdb;
+
+ // Получаем информацию о задаче
+ $action = $wpdb->get_row( $wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}actionscheduler_actions WHERE action_id = %d",
+ $action_id
+ ) );
+
+ if ( ! $action ) {
+ WP_CLI::error( "Задача с ID {$action_id} не найдена." );
+ return;
+ }
+
+ WP_CLI::log( "Статус: " . $action->status );
+ WP_CLI::log( "Хук: " . $action->hook );
+
+ // Если нужно сбросить статус failed
+ if ( $retry && 'failed' === $action->status ) {
+ $updated = $wpdb->update(
+ "{$wpdb->prefix}actionscheduler_actions",
+ array( 'status' => 'pending' ),
+ array( 'action_id' => $action_id ),
+ array( '%s' ),
+ array( '%d' )
+ );
+
+ if ( $updated ) {
+ WP_CLI::log( "Статус сброшен с failed на pending" );
+ }
+ }
+
+ // Запускаем задачу через Action Scheduler
+ try {
+ // Создаем экземпляр задачи
+ $store = ActionScheduler::store();
+ $action_obj = $store->fetch_action( $action_id );
+
+ if ( ! $action_obj ) {
+ WP_CLI::error( "Не удалось создать объект задачи." );
+ return;
+ }
+
+ WP_CLI::log( "Запуск задачи..." );
+
+ // Выполняем задачу
+ $start_time = microtime( true );
+ $action_obj->execute();
+ $execution_time = microtime( true ) - $start_time;
+
+ // Проверяем новый статус
+ $new_status = $wpdb->get_var( $wpdb->prepare(
+ "SELECT status FROM {$wpdb->prefix}actionscheduler_actions WHERE action_id = %d",
+ $action_id
+ ) );
+
+ WP_CLI::success( sprintf(
+ "Задача выполнена за %.2f секунд. Новый статус: %s",
+ $execution_time,
+ $new_status
+ ) );
+
+ } catch ( Exception $e ) {
+ WP_CLI::error( "Ошибка выполнения: " . $e->getMessage() );
+ }
+}
+
+
+
+
+
+
+
+ /**
+ * Сбросить статус failed задачи на pending
+ *
+ * ## OPTIONS
+ *
+ *
+ * : ID задачи Action Scheduler
+ *
+ * ## EXAMPLES
+ *
+ * wp scheduler reset-failed-action 123
+ */
+
+ public function reset_failed_action( $args, $assoc_args ) {
+
+ list( $action_id ) = $args;
+
+ global $wpdb;
+
+ $action = $wpdb->get_row( $wpdb->prepare(
+ "SELECT status FROM {$wpdb->prefix}actionscheduler_actions WHERE action_id = %d",
+ $action_id
+ ) );
+
+ if ( ! $action ) {
+ WP_CLI::error( "Задача с ID {$action_id} не найдена." );
+ return;
+ }
+
+ if ( 'failed' !== $action->status ) {
+ WP_CLI::warning( "Статус задачи не 'failed', а '{$action->status}'. Сброс не требуется." );
+ return;
+ }
+
+ $updated = $wpdb->update(
+ "{$wpdb->prefix}actionscheduler_actions",
+ array( 'status' => 'pending' ),
+ array( 'action_id' => $action_id ),
+ array( '%s' ),
+ array( '%d' )
+ );
+
+ if ( $updated ) {
+ WP_CLI::success( "Статус задачи сброшен с failed на pending." );
+ } else {
+ WP_CLI::error( "Не удалось сбросить статус задачи." );
+ }
+ }
+
+
+
+
+
+}
+
+// Регистрируем команды - ЭТО ОБЯЗАТЕЛЬНО!
+WP_CLI::add_command( 'scheduler', 'Scheduler_CLI_Commands' );
\ No newline at end of file