coreString('backgroundjobs_mode', 'ajax'); $lastRun = (int)$this->coreString('lastcron', '0'); $stopped = $mode === 'cron' && $now - $lastRun > self::STOPPED_AFTER; return [ 'mode' => $mode, 'lastRun' => $lastRun, 'ok' => $mode === 'cron' && !$stopped, 'stopped' => $stopped, 'docs' => $this->urls->linkToDocs('admin-background-jobs'), ]; } /** * Seconds since a running backup took its last step, when that is long enough to say it waits * for background jobs; null while it moves (or when it is not running). */ public static function waitingFor(Run $run, ?int $now = null): ?int { $idle = ($now ?? time()) - $run->getUpdatedAt(); return $run->getStatus() === Run::RUNNING && $idle >= self::WAITING_AFTER ? $idle : null; } /** One line for occ and the log; null when system cron runs. */ public static function describe(array $health, ?int $now = null): ?string { if ($health['ok']) { return null; } $last = $health['lastRun'] > 0 ? 'last ran ' . self::ago(($now ?? time()) - $health['lastRun']) : 'never ran'; $what = $health['stopped'] ? 'Background jobs are set to system cron, but cron has not run for over an hour (' . $last . ').' : 'Background jobs do not run on a system cron: they are set to "' . ($health['mode'] === 'webcron' ? 'Webcron' : 'AJAX') . '" and ' . $last . '.'; return $what . ' Backups, also the scheduled ones, need a system cron that runs every 5 minutes: ' . $health['docs']; } /** "12 min ago" style, for durations from a minute to days */ public static function ago(int $seconds): string { if ($seconds < 3600) { return max(1, intdiv($seconds, 60)) . ' min ago'; } if ($seconds < 172800) { return intdiv($seconds, 3600) . ' h ago'; } return intdiv($seconds, 86400) . ' days ago'; } /** Core stores these with different value types across versions: read whatever is there. */ private function coreString(string $key, string $default): string { try { return $this->appConfig->getValueString('core', $key, $default); } catch (AppConfigTypeConflictException) { return (string)$this->appConfig->getValueInt('core', $key, (int)$default); } } }