} public static function cleanup() { if (!class_exists('ActionScheduler') || !class_exists('ActionScheduler_Store')) { self::save_result(0, 'Action Scheduler ist nicht verfügbar.'); return; } if (get_transient('nr_asc_cleanup_lock')) { return; } set_transient('nr_asc_cleanup_lock', 1, 10 * MINUTE_IN_SECONDS); $deleted = 0; $errors = 0; $started = microtime(true); $limited = false; try { $store = ActionScheduler::store(); // Process the less frequent statuses first to avoid starving them. $rules = array('failed' => 3, 'canceled' => 0, 'complete' => 0); foreach ($rules as $status => $days) { if ($deleted >= self::BATCH) { break; } if (microtime(true) - $started >= self::MAX_SECONDS) { $limited = true; break; } $query = array( 'status' => $status, 'orderby' => 'modified', 'order' => 'ASC', 'per_page' => self::BATCH - $deleted, ); // No retention period for completed/canceled actions. if ($days > 0) { $query['modified'] = new DateTime('-' . $days . ' days', new DateTimeZone('UTC')); $query['modified_compare'] = '<'; } $ids = $store->query_actions($query); foreach ($ids as $id) { if (microtime(true) - $started >= self::MAX_SECONDS) { $limited = true; break 2; } try { // An action may have been retried since the query. if ($store->get_status((int) $id) !== $status) { continue; } $store->delete_action((int) $id); ++$deleted; } catch (Throwable $e) { ++$errors; error_log('NR Action Scheduler Cleaner: ' . $e->getMessage()); } } } $message = $errors ? $errors . ' Fehler; siehe PHP-Fehlerprotokoll.' : 'OK'; if ($limited) { $message .= ' Zeitbudget erreicht; Fortsetzung beim nächsten Lauf.'; } elseif ($deleted >= self::BATCH) { $message .= ' Löschlimit erreicht; weitere alte Aktionen ggf. beim nächsten Lauf.'; } self::save_result($deleted, $message); } catch (Throwable $e) { self::save_result($deleted, 'Fehler; siehe PHP-Fehlerprotokoll.'); error_log('NR Action Scheduler Cleaner: ' . $e->getMessage()); } finally { delete_transient('nr_asc_cleanup_lock'); } } private static function save_result($count, $message) { update_option(self::OPTION, array('time' => current_time('mysql'), 'deleted' => (int) $count, 'message' => $message), false); } public static function widget() { if (current_user_can('manage_options')) { wp_add_dashboard_widget('nr_asc_dashboard', 'NR – Action Scheduler Cleaner', array(__CLASS__, 'render')); } } public static function render() { $last = get_option(self::OPTION, array()); echo '
Automatische Bereinigung: alle 12 Stunden (WP-Cron; abhängig vom Cron-Aufruf)
'; echo 'Abgeschlossen/Abgebrochen: keine Aufbewahrung · Fehlgeschlagen: 3 Tage
Maximal ' . esc_html(self::BATCH) . ' Aktionen je Durchlauf; Zeitbudget ' . esc_html(self::MAX_SECONDS) . ' Sekunden.
Letzter Lauf: ' . esc_html($last['time']) . '
';
echo 'Gelöscht: ' . esc_html($last['deleted']) . '
';
echo 'Status: ' . esc_html($last['message']) . '
Noch keine Bereinigung ausgeführt.
'; } $next = wp_next_scheduled(self::CRON); if ($next) { echo 'Nächster Lauf: ' . esc_html(wp_date('d.m.Y H:i', $next)) . '
'; } echo ''; if (isset($_GET['nr_asc_done'])) { echo 'Bereinigung ausgeführt. Ergebnis siehe oben.
'; } } public static function manual() { if (!current_user_can('manage_options')) { wp_die('Keine Berechtigung.'); } check_admin_referer(self::NONCE); self::cleanup(); wp_safe_redirect(add_query_arg('nr_asc_done', '1', admin_url('index.php'))); exit; } } NR_Action_Scheduler_Cleaner::init(); register_deactivation_hook(__FILE__, function () { wp_clear_scheduled_hook(NR_Action_Scheduler_Cleaner::CRON); });