mirror of
https://github.com/wavelog/wavelog.git
synced 2026-03-22 10:24:14 +00:00
Merge branch 'dev' into menu_sorted
This commit is contained in:
@@ -22,7 +22,7 @@ $config['migration_enabled'] = TRUE;
|
||||
|
|
||||
*/
|
||||
|
||||
$config['migration_version'] = 195;
|
||||
$config['migration_version'] = 196;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -25,6 +25,10 @@ class Clublog extends CI_Controller {
|
||||
public function upload() {
|
||||
$this->load->model('clublog_model');
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$users = $this->clublog_model->get_clublog_users();
|
||||
|
||||
foreach ($users as $user) {
|
||||
|
||||
262
application/controllers/Cron.php
Normal file
262
application/controllers/Cron.php
Normal file
@@ -0,0 +1,262 @@
|
||||
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
class cron extends CI_Controller {
|
||||
function __construct() {
|
||||
|
||||
parent::__construct();
|
||||
|
||||
if (ENVIRONMENT == 'maintenance' && $this->session->userdata('user_id') == '') {
|
||||
echo "Maintenance Mode is active. Try again later.\n";
|
||||
redirect('user/login');
|
||||
}
|
||||
|
||||
$this->load->model('cron_model');
|
||||
}
|
||||
|
||||
public function index() {
|
||||
|
||||
$this->load->model('user_model');
|
||||
if (!$this->user_model->authorize(99)) {
|
||||
$this->session->set_flashdata('notice', 'You\'re not allowed to do that!');
|
||||
redirect('dashboard');
|
||||
}
|
||||
|
||||
$this->load->helper('file');
|
||||
|
||||
$footerData = [];
|
||||
$footerData['scripts'] = [
|
||||
'assets/js/cronstrue.min.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/cronstrue.min.js")),
|
||||
'assets/js/sections/cron.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/cron.js"))
|
||||
];
|
||||
|
||||
$data['page_title'] = "Cron Manager";
|
||||
$data['crons'] = $this->cron_model->get_crons();
|
||||
|
||||
$mastercron = array();
|
||||
$mastercron = $this->get_mastercron_status();
|
||||
$data['mastercron'] = $mastercron;
|
||||
|
||||
$this->load->view('interface_assets/header', $data);
|
||||
$this->load->view('cron/index');
|
||||
$this->load->view('interface_assets/footer', $footerData);
|
||||
}
|
||||
|
||||
public function run() {
|
||||
|
||||
// This is the main function, which handles all crons, runs them if enabled and writes the 'next run' timestamp to the database
|
||||
|
||||
// TODO Add an API Key to the cronjob to improve security?
|
||||
|
||||
$crons = $this->cron_model->get_crons();
|
||||
|
||||
$status = 'pending';
|
||||
|
||||
foreach ($crons as $cron) {
|
||||
if ($cron->enabled == 1) {
|
||||
|
||||
// calculate the crons expression
|
||||
$data = array(
|
||||
'expression' => $cron->expression,
|
||||
'timeZone' => null
|
||||
);
|
||||
$this->load->library('CronExpression', $data);
|
||||
|
||||
$cronjob = $this->cronexpression;
|
||||
$dt = new DateTime();
|
||||
$isdue = $cronjob->isMatching($dt);
|
||||
|
||||
$next_run = $cronjob->getNext();
|
||||
$next_run_date = date('Y-m-d H:i:s', $next_run);
|
||||
$this->cron_model->set_next_run($cron->id, $next_run_date);
|
||||
|
||||
if ($isdue == true) {
|
||||
$isdue_result = 'true';
|
||||
|
||||
// TODO Add log_message level debug here to have logging for the cron manager
|
||||
|
||||
echo "CRON: " . $cron->id . " -> is due: " . $isdue_result . "\n";
|
||||
echo "CRON: " . $cron->id . " -> RUNNING...\n";
|
||||
|
||||
$url = base_url() . $cron->function;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Wavelog Updater');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$crun = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($crun !== false) {
|
||||
echo "CRON: " . $cron->id . " -> CURL Result: " . $crun . "\n";
|
||||
$status = 'healthy';
|
||||
} else {
|
||||
echo "ERROR: Something went wrong with " . $cron->id . "\n";
|
||||
$status = 'failed';
|
||||
}
|
||||
} else {
|
||||
$isdue_result = 'false';
|
||||
echo "CRON: " . $cron->id . " -> is due: " . $isdue_result . " -> Next Run: " . $next_run_date . "\n";
|
||||
$status = 'healthy';
|
||||
}
|
||||
} else {
|
||||
echo 'CRON: ' . $cron->id . " is disabled. skipped..\n";
|
||||
$status = 'disabled';
|
||||
|
||||
// Set the next_run timestamp to null to indicate in the view/database that this cron is disabled
|
||||
$this->cron_model->set_next_run($cron->id, null);
|
||||
}
|
||||
$this->cron_model->set_status($cron->id, $status);
|
||||
$this->cronexpression = null;
|
||||
}
|
||||
|
||||
$datetime = new DateTime("now", new DateTimeZone('UTC'));
|
||||
$datetime = $datetime->format('Ymd H:i:s');
|
||||
$this->optionslib->update('mastercron_last_run', $datetime , 'no');
|
||||
}
|
||||
|
||||
public function editDialog() {
|
||||
|
||||
$cron_query = $this->cron_model->cron(xss_clean($this->input->post('id', true)));
|
||||
|
||||
$data['cron'] = $cron_query->row();
|
||||
$data['page_title'] = "Edit Cronjob";
|
||||
|
||||
$this->load->view('cron/edit', $data);
|
||||
}
|
||||
|
||||
public function edit() {
|
||||
$this->load->model('user_model');
|
||||
if (!$this->user_model->authorize(99)) {
|
||||
$this->session->set_flashdata('notice', 'You\'re not allowed to do that!');
|
||||
redirect('dashboard');
|
||||
}
|
||||
|
||||
$id = xss_clean($this->input->post('cron_id', true));
|
||||
$description = xss_clean($this->input->post('cron_description', true));
|
||||
$expression = xss_clean($this->input->post('cron_expression', true));
|
||||
$enabled = xss_clean($this->input->post('cron_enabled', true));
|
||||
|
||||
$data = array(
|
||||
'expression' => $expression,
|
||||
'timeZone' => null
|
||||
);
|
||||
$this->load->library('CronExpression', $data);
|
||||
$cron = $this->cronexpression;
|
||||
|
||||
if ($cron->isValid()) {
|
||||
$this->cron_model->edit_cron($id, $description, $expression, $enabled);
|
||||
$this->cronexpression = null;
|
||||
|
||||
header("Content-type: application/json");
|
||||
echo json_encode(['success' => true, 'messagecategory' => 'success', 'message' => 'Changes saved for Cronjob "' . $id . '"']);
|
||||
} else {
|
||||
$this->session->set_flashdata('error', 'The Cron Expression you entered is not valid');
|
||||
$this->cronexpression = null;
|
||||
|
||||
header("Content-type: application/json");
|
||||
echo json_encode(['success' => false, 'messagecategory' => 'error', 'message' => 'The expression "' . $expression . '" is not valid. Please try again.']);
|
||||
}
|
||||
}
|
||||
|
||||
public function toogleEnableCronSwitch() {
|
||||
|
||||
$id = xss_clean($this->input->post('id', true));
|
||||
$cron_enabled = xss_clean($this->input->post('checked', true));
|
||||
|
||||
if ($id ?? '' != '') {
|
||||
$this->cron_model->set_cron_enabled($id, $cron_enabled);
|
||||
$data['success'] = 1;
|
||||
} else {
|
||||
$data['success'] = 0;
|
||||
$data['flashdata'] = 'Not allowed';
|
||||
}
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function fetchCrons() {
|
||||
$hres = [];
|
||||
$result = $this->cron_model->get_crons();
|
||||
|
||||
foreach ($result as $cron) {
|
||||
$single = (object) [];
|
||||
$single->cron_id = $cron->id;
|
||||
$single->cron_description = $cron->description;
|
||||
$single->cron_status = $this->cronStatus2html($cron->enabled, $cron->status);
|
||||
$single->cron_expression = $this->cronExpression2html($cron->expression);
|
||||
$single->cron_last_run = $cron->last_run ?? 'never';
|
||||
$single->cron_next_run = ($cron->enabled == '1') ? ($cron->next_run ?? 'calculating..') : 'never';
|
||||
$single->cron_edit = $this->cronEdit2html($cron->id);
|
||||
$single->cron_enabled = $this->cronEnabled2html($cron->id, $cron->enabled);
|
||||
array_push($hres, $single);
|
||||
}
|
||||
echo json_encode($hres);
|
||||
}
|
||||
|
||||
private function cronStatus2html($enabled, $status) {
|
||||
if ($enabled == '1') {
|
||||
if ($status == 'healthy') {
|
||||
$htmlret = '<span class="badge text-bg-success">healthy</span>';
|
||||
} else {
|
||||
$htmlret = '<span class="badge text-bg-warning">' . $status . '</span>';
|
||||
}
|
||||
} else {
|
||||
$htmlret = '<span class="badge text-bg-secondary">disabled</span>';
|
||||
}
|
||||
return $htmlret;
|
||||
}
|
||||
|
||||
private function cronExpression2html($expression) {
|
||||
$htmlret = '<code id="humanreadable_tooltip" data-bs-toggle="tooltip">' . $expression . '</code>';
|
||||
return $htmlret;
|
||||
}
|
||||
|
||||
private function cronEdit2html($id) {
|
||||
$htmlret = '<button id="' . $id . '" class="editCron btn btn-outline-primary btn-sm"><i class="fas fa-edit"></i></button>';
|
||||
return $htmlret;
|
||||
}
|
||||
|
||||
private function cronEnabled2html($id, $enabled) {
|
||||
if ($enabled == '1') {
|
||||
$checked = 'checked';
|
||||
} else {
|
||||
$checked = '';
|
||||
}
|
||||
$htmlret = '<div class="form-check form-switch"><input name="cron_enable_switch" class="form-check-input enableCronSwitch" type="checkbox" role="switch" id="' . $id . '" ' . $checked . '></div>';
|
||||
return $htmlret;
|
||||
}
|
||||
|
||||
private function get_mastercron_status() {
|
||||
$warning_timelimit_seconds = 120; // yellow - warning please check
|
||||
$error_timelimit_seconds = 600; // red - "not running"
|
||||
|
||||
$result = array();
|
||||
|
||||
$last_run = $this->optionslib->get_option('mastercron_last_run') ?? null;
|
||||
|
||||
if ($last_run != null) {
|
||||
$timestamp_last_run = DateTime::createFromFormat('Ymd H:i:s', $last_run, new DateTimeZone('UTC'));
|
||||
$now = new DateTime();
|
||||
$diff = $now->getTimestamp() - $timestamp_last_run->getTimestamp();
|
||||
|
||||
if ($diff >= 0 && $diff <= $warning_timelimit_seconds) {
|
||||
$result['status'] = 'OK';
|
||||
$result['status_class'] = 'success';
|
||||
} else {
|
||||
if ($diff <= $error_timelimit_seconds) {
|
||||
$result['status'] = 'Last run occurred more than ' . $warning_timelimit_seconds . ' seconds ago.<br>Please check your master cron! It should run every minute (* * * * *).';
|
||||
$result['status_class'] = 'warning';
|
||||
} else {
|
||||
$result['status'] = 'Last run occurred more than ' . ($error_timelimit_seconds / 60) . ' minutes ago.<br>Seems like your Mastercron isn\'t running!<br>It should run every minute (* * * * *).';
|
||||
$result['status_class'] = 'danger';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result['status'] = 'Not running';
|
||||
$result['status_class'] = 'danger';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ class Debug extends CI_Controller
|
||||
$this->load->model('Logbook_model');
|
||||
$this->load->model('Debug_model');
|
||||
$this->load->model('Stations');
|
||||
$this->load->model('cron_model');
|
||||
|
||||
$footerData = [];
|
||||
$footerData['scripts'] = ['assets/js/sections/debug.js'];
|
||||
@@ -62,6 +63,14 @@ class Debug extends CI_Controller
|
||||
$data['userdata_status'] = $userdata_status;
|
||||
}
|
||||
|
||||
$data['dxcc_update'] = $this->cron_model->cron('update_dxcc')->row();
|
||||
$data['dok_update'] = $this->cron_model->cron('update_update_dok')->row();
|
||||
$data['lotw_user_update'] = $this->cron_model->cron('update_lotw_users')->row();
|
||||
$data['pota_update'] = $this->cron_model->cron('update_update_pota')->row();
|
||||
$data['scp_update'] = $this->cron_model->cron('update_update_clublog_scp')->row();
|
||||
$data['sota_update'] = $this->cron_model->cron('update_update_sota')->row();
|
||||
$data['wwff_update'] = $this->cron_model->cron('update_update_wwff')->row();
|
||||
|
||||
$data['page_title'] = "Debug";
|
||||
|
||||
$this->load->view('interface_assets/header', $data);
|
||||
|
||||
@@ -713,6 +713,11 @@ class eqsl extends CI_Controller {
|
||||
* Used for CRON job
|
||||
*/
|
||||
public function sync() {
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
ini_set('memory_limit', '-1');
|
||||
set_time_limit(0);
|
||||
$this->load->model('eqslmethods_model');
|
||||
|
||||
@@ -25,6 +25,10 @@ class Hrdlog extends CI_Controller {
|
||||
public function upload() {
|
||||
$this->setOptions();
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$this->load->model('logbook_model');
|
||||
|
||||
$station_ids = $this->logbook_model->get_station_id_with_hrdlog_code();
|
||||
|
||||
@@ -198,6 +198,10 @@ class Lotw extends CI_Controller {
|
||||
echo "You must install php OpenSSL for LoTW functions to work";
|
||||
}
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
// Get Station Profile Data
|
||||
$this->load->model('Stations');
|
||||
|
||||
|
||||
@@ -69,6 +69,10 @@ class Qrz extends CI_Controller {
|
||||
public function upload() {
|
||||
$this->setOptions();
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$this->load->model('logbook_model');
|
||||
|
||||
$station_ids = $this->logbook_model->get_station_id_with_qrz_api();
|
||||
@@ -260,6 +264,8 @@ class Qrz extends CI_Controller {
|
||||
$this->load->model('user_model');
|
||||
$this->load->model('logbook_model');
|
||||
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$api_keys = $this->logbook_model->get_qrz_apikeys();
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ class Update extends CI_Controller {
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->load->model('user_model');
|
||||
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
|
||||
|
||||
$data['page_title'] = "Updates";
|
||||
$this->load->view('interface_assets/header', $data);
|
||||
$this->load->view('update/index');
|
||||
@@ -176,6 +179,11 @@ class Update extends CI_Controller {
|
||||
|
||||
// Updates the DXCC & Exceptions from the Club Log Cty.xml file.
|
||||
public function dxcc() {
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$this->update_status("Downloading file");
|
||||
|
||||
// give it 10 minutes...
|
||||
@@ -236,9 +244,6 @@ class Update extends CI_Controller {
|
||||
$html .= "Dxcc Prefixes: ".$this->db->count_all('dxcc_prefixes')."<br/>";
|
||||
} else {
|
||||
$html = $done."....<br/>";
|
||||
$datetime = new DateTime("now", new DateTimeZone('UTC'));
|
||||
$datetime = $datetime->format('Ymd h:i');
|
||||
$this->optionslib->update('dxcc_clublog_update', $datetime , 'no');
|
||||
}
|
||||
|
||||
file_put_contents($this->make_update_path("status.html"), $html);
|
||||
@@ -302,6 +307,11 @@ class Update extends CI_Controller {
|
||||
}
|
||||
|
||||
public function update_clublog_scp() {
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$strFile = $this->make_update_path("clublog_scp.txt");
|
||||
$url = "https://cdn.clublog.org/clublog.scp.gz";
|
||||
set_time_limit(300);
|
||||
@@ -320,9 +330,6 @@ class Update extends CI_Controller {
|
||||
if ($nCount > 0)
|
||||
{
|
||||
echo "DONE: " . number_format($nCount) . " callsigns loaded";
|
||||
$datetime = new DateTime("now", new DateTimeZone('UTC'));
|
||||
$datetime = $datetime->format('Ymd h:i');
|
||||
$this->optionslib->update('scp_update', $datetime , 'no');
|
||||
} else {
|
||||
echo "FAILED: Empty file";
|
||||
}
|
||||
@@ -352,6 +359,11 @@ class Update extends CI_Controller {
|
||||
}
|
||||
|
||||
public function lotw_users() {
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$mtime = microtime();
|
||||
$mtime = explode(" ",$mtime);
|
||||
$mtime = $mtime[1] + $mtime[0];
|
||||
@@ -390,9 +402,6 @@ class Update extends CI_Controller {
|
||||
$totaltime = ($endtime - $starttime);
|
||||
echo "This page was created in ".$totaltime." seconds <br />";
|
||||
echo "Records inserted: " . $i . " <br/>";
|
||||
$datetime = new DateTime("now", new DateTimeZone('UTC'));
|
||||
$datetime = $datetime->format('Ymd h:i');
|
||||
$this->optionslib->update('lotw_users_update', $datetime , 'no');
|
||||
}
|
||||
|
||||
public function lotw_check() {
|
||||
@@ -412,6 +421,11 @@ class Update extends CI_Controller {
|
||||
* Used for autoupdating the DOK file which is used in the QSO entry dialog for autocompletion.
|
||||
*/
|
||||
public function update_dok() {
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$contents = file_get_contents('https://www.df2et.de/cqrlog/dok_and_sdok.txt', true);
|
||||
|
||||
if($contents === FALSE) {
|
||||
@@ -424,9 +438,6 @@ class Update extends CI_Controller {
|
||||
if ($nCount > 0)
|
||||
{
|
||||
echo "DONE: " . number_format($nCount) . " DOKs and SDOKs saved";
|
||||
$datetime = new DateTime("now", new DateTimeZone('UTC'));
|
||||
$datetime = $datetime->format('Ymd h:i');
|
||||
$this->optionslib->update('dok_file_update', $datetime , 'no');
|
||||
} else {
|
||||
echo"FAILED: Empty file";
|
||||
}
|
||||
@@ -440,6 +451,11 @@ class Update extends CI_Controller {
|
||||
* Used for autoupdating the SOTA file which is used in the QSO entry dialog for autocompletion.
|
||||
*/
|
||||
public function update_sota() {
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$csvfile = 'https://www.sotadata.org.uk/summitslist.csv';
|
||||
|
||||
$sotafile = './assets/json/sota.txt';
|
||||
@@ -474,9 +490,6 @@ class Update extends CI_Controller {
|
||||
if ($nCount > 0)
|
||||
{
|
||||
echo "DONE: " . number_format($nCount) . " SOTA's saved";
|
||||
$datetime = new DateTime("now", new DateTimeZone('UTC'));
|
||||
$datetime = $datetime->format('Ymd h:i');
|
||||
$this->optionslib->update('sota_file_update', $datetime , 'no');
|
||||
} else {
|
||||
echo"FAILED: Empty file";
|
||||
}
|
||||
@@ -486,6 +499,11 @@ class Update extends CI_Controller {
|
||||
* Pulls the WWFF directory for autocompletion in QSO dialogs
|
||||
*/
|
||||
public function update_wwff() {
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$csvfile = 'https://wwff.co/wwff-data/wwff_directory.csv';
|
||||
|
||||
$wwfffile = './assets/json/wwff.txt';
|
||||
@@ -524,15 +542,17 @@ class Update extends CI_Controller {
|
||||
if ($nCount > 0)
|
||||
{
|
||||
echo "DONE: " . number_format($nCount) . " WWFF's saved";
|
||||
$datetime = new DateTime("now", new DateTimeZone('UTC'));
|
||||
$datetime = $datetime->format('Ymd h:i');
|
||||
$this->optionslib->update('wwff_file_update', $datetime , 'no');
|
||||
} else {
|
||||
echo"FAILED: Empty file";
|
||||
}
|
||||
}
|
||||
|
||||
public function update_pota() {
|
||||
|
||||
// set the last run in cron table for the correct cron id
|
||||
$this->load->model('cron_model');
|
||||
$this->cron_model->set_last_run($this->router->class.'_'.$this->router->method);
|
||||
|
||||
$csvfile = 'https://pota.app/all_parks.csv';
|
||||
|
||||
$potafile = './assets/json/pota.txt';
|
||||
@@ -570,9 +590,6 @@ class Update extends CI_Controller {
|
||||
if ($nCount > 0)
|
||||
{
|
||||
echo "DONE: " . number_format($nCount) . " POTA's saved";
|
||||
$datetime = new DateTime("now", new DateTimeZone('UTC'));
|
||||
$datetime = $datetime->format('Ymd h:i');
|
||||
$this->optionslib->update('pota_file_update', $datetime , 'no');
|
||||
} else {
|
||||
echo"FAILED: Empty file";
|
||||
}
|
||||
|
||||
424
application/libraries/CronExpression.php
Normal file
424
application/libraries/CronExpression.php
Normal file
@@ -0,0 +1,424 @@
|
||||
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
* Cron expression parser and validator
|
||||
*
|
||||
* @author René Pollesch
|
||||
* edited by HB9HIL 04/2024
|
||||
*/
|
||||
class CronExpression {
|
||||
/**
|
||||
* Weekday name look-up table
|
||||
*/
|
||||
private const WEEKDAY_NAMES = [
|
||||
'sun' => 0,
|
||||
'mon' => 1,
|
||||
'tue' => 2,
|
||||
'wed' => 3,
|
||||
'thu' => 4,
|
||||
'fri' => 5,
|
||||
'sat' => 6
|
||||
];
|
||||
|
||||
/**
|
||||
* Month name look-up table
|
||||
*/
|
||||
private const MONTH_NAMES = [
|
||||
'jan' => 1,
|
||||
'feb' => 2,
|
||||
'mar' => 3,
|
||||
'apr' => 4,
|
||||
'may' => 5,
|
||||
'jun' => 6,
|
||||
'jul' => 7,
|
||||
'aug' => 8,
|
||||
'sep' => 9,
|
||||
'oct' => 10,
|
||||
'nov' => 11,
|
||||
'dec' => 12
|
||||
];
|
||||
|
||||
/**
|
||||
* Value boundaries
|
||||
*/
|
||||
private const VALUE_BOUNDARIES = [
|
||||
0 => [
|
||||
'min' => 0,
|
||||
'max' => 59,
|
||||
'mod' => 1
|
||||
],
|
||||
1 => [
|
||||
'min' => 0,
|
||||
'max' => 23,
|
||||
'mod' => 1
|
||||
],
|
||||
2 => [
|
||||
'min' => 1,
|
||||
'max' => 31,
|
||||
'mod' => 1
|
||||
],
|
||||
3 => [
|
||||
'min' => 1,
|
||||
'max' => 12,
|
||||
'mod' => 1
|
||||
],
|
||||
4 => [
|
||||
'min' => 0,
|
||||
'max' => 7,
|
||||
'mod' => 0
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var DateTimeZone|null
|
||||
*/
|
||||
protected readonly ?DateTimeZone $timeZone;
|
||||
|
||||
/**
|
||||
* @var array|null
|
||||
*/
|
||||
protected readonly ?array $registers;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected readonly string $expression;
|
||||
|
||||
/**
|
||||
* @param string $expression a cron expression, e.g. "* * * * *"
|
||||
* @param DateTimeZone|null $timeZone time zone objectstring $expression, DateTimeZone $timeZone = null
|
||||
*/
|
||||
public function __construct($data) {
|
||||
$this->timeZone = $data['timeZone'];
|
||||
$this->expression = $data['expression'];
|
||||
|
||||
try {
|
||||
$this->registers = $this->parse($data['expression']);
|
||||
} catch (Exception $e) {
|
||||
$this->registers = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether current cron expression has been parsed successfully
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(): bool {
|
||||
return null !== $this->registers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match either "now", a given date/time object or a timestamp against current cron expression
|
||||
*
|
||||
* @param mixed $when a DateTime object, a timestamp (int), or "now" if not set
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function isMatching($when = null): bool {
|
||||
if (false === ($when instanceof DateTimeInterface)) {
|
||||
$when = (new DateTime())->setTimestamp($when === null ? time() : $when);
|
||||
}
|
||||
|
||||
if ($this->timeZone !== null) {
|
||||
$when->setTimezone($this->timeZone);
|
||||
}
|
||||
|
||||
return $this->isValid() && $this->match(sscanf($when->format('i G j n w'), '%d %d %d %d %d'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate next matching timestamp
|
||||
*
|
||||
* @param mixed $start a DateTime object, a timestamp (int) or "now" if not set
|
||||
* @return int|bool next matching timestamp, or false on error
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getNext($start = null) {
|
||||
if ($this->isValid()) {
|
||||
$next = $this->toDateTime($start);
|
||||
|
||||
do {
|
||||
$pos = sscanf($next->format('i G j n Y w'), '%d %d %d %d %d %d');
|
||||
} while ($this->increase($next, $pos));
|
||||
|
||||
return $next->getTimestamp();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $start a DateTime object, a timestamp (int) or "now" if not set
|
||||
* @return DateTime
|
||||
*/
|
||||
private function toDateTime($start): DateTime {
|
||||
if ($start instanceof DateTimeInterface) {
|
||||
$next = $start;
|
||||
} elseif ((int)$start > 0) {
|
||||
$next = new DateTime('@' . $start);
|
||||
} else {
|
||||
$next = new DateTime('@' . time());
|
||||
}
|
||||
|
||||
$next->setTimestamp($next->getTimeStamp() - $next->getTimeStamp() % 60);
|
||||
$next->setTimezone($this->timeZone ?: new DateTimeZone(date_default_timezone_get()));
|
||||
|
||||
if ($this->isMatching($next)) {
|
||||
$next->modify('+1 minute');
|
||||
}
|
||||
|
||||
return $next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the timestamp in step sizes depending on which segment(s) of the cron pattern are matching.
|
||||
* Returns FALSE if the cron pattern is matching and thus no further cycle is required.
|
||||
*
|
||||
* @param DateTimeInterface $next
|
||||
* @param array $pos
|
||||
* @return bool
|
||||
*/
|
||||
private function increase(DateTimeInterface $next, array $pos): bool {
|
||||
switch (true) {
|
||||
case false === isset($this->registers[3][$pos[3]]):
|
||||
// next month, reset day/hour/minute
|
||||
$next->setTime(0, 0);
|
||||
$next->setDate($pos[4], $pos[3], 1);
|
||||
$next->modify('+1 month');
|
||||
return true;
|
||||
|
||||
case false === (isset($this->registers[2][$pos[2]]) && isset($this->registers[4][$pos[5]])):
|
||||
// next day, reset hour/minute
|
||||
$next->setTime(0, 0);
|
||||
$next->modify('+1 day');
|
||||
return true;
|
||||
|
||||
case false === isset($this->registers[1][$pos[1]]):
|
||||
// next hour, reset minute
|
||||
$next->setTime($pos[1], 0);
|
||||
$next->modify('+1 hour');
|
||||
return true;
|
||||
|
||||
case false === isset($this->registers[0][$pos[0]]):
|
||||
// next minute
|
||||
$next->modify('+1 minute');
|
||||
return true;
|
||||
|
||||
default:
|
||||
// all segments are matching
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $segments
|
||||
* @return bool
|
||||
*/
|
||||
private function match(array $segments): bool {
|
||||
foreach ($this->registers as $i => $item) {
|
||||
if (isset($item[(int)$segments[$i]]) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse whole cron expression
|
||||
*
|
||||
* @param string $expression
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parse(string $expression): array {
|
||||
$segments = preg_split('/\s+/', trim($expression));
|
||||
|
||||
if (is_array($segments) && sizeof($segments) === 5) {
|
||||
$registers = array_fill(0, 5, []);
|
||||
|
||||
foreach ($segments as $index => $segment) {
|
||||
$this->parseSegment($registers[$index], $index, $segment);
|
||||
}
|
||||
|
||||
$this->validateDate($registers);
|
||||
|
||||
if (isset($registers[4][7])) {
|
||||
$registers[4][0] = true;
|
||||
}
|
||||
|
||||
return $registers;
|
||||
}
|
||||
|
||||
throw new Exception('invalid number of segments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse one segment of a cron expression
|
||||
*
|
||||
* @param array $register
|
||||
* @param int $index
|
||||
* @param string $segment
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parseSegment(array &$register, int $index, string $segment): void {
|
||||
$allowed = [false, false, false, self::MONTH_NAMES, self::WEEKDAY_NAMES];
|
||||
|
||||
// month names, weekdays
|
||||
if ($allowed[$index] !== false && isset($allowed[$index][strtolower($segment)])) {
|
||||
// cannot be used together with lists or ranges
|
||||
$register[$allowed[$index][strtolower($segment)]] = true;
|
||||
} else {
|
||||
// split up current segment into single elements, e.g. "1,5-7,*/2" => [ "1", "5-7", "*/2" ]
|
||||
foreach (explode(',', $segment) as $element) {
|
||||
$this->parseElement($register, $index, $element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $register
|
||||
* @param int $index
|
||||
* @param string $element
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parseElement(array &$register, int $index, string $element): void {
|
||||
$step = 1;
|
||||
$segments = explode('/', $element);
|
||||
|
||||
if (sizeof($segments) > 1) {
|
||||
$this->validateStepping($segments, $index);
|
||||
|
||||
$element = (string)$segments[0];
|
||||
$step = (int)$segments[1];
|
||||
}
|
||||
|
||||
if (is_numeric($element)) {
|
||||
$this->validateValue($element, $index, $step);
|
||||
$register[intval($element)] = true;
|
||||
} else {
|
||||
$this->parseRange($register, $index, $element, $step);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse range of values, e.g. "5-10"
|
||||
*
|
||||
* @param array $register
|
||||
* @param int $index
|
||||
* @param string $range
|
||||
* @param int $stepping
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parseRange(array &$register, int $index, string $range, int $stepping): void {
|
||||
if ($range === '*') {
|
||||
$rangeArr = [self::VALUE_BOUNDARIES[$index]['min'], self::VALUE_BOUNDARIES[$index]['max']];
|
||||
} else {
|
||||
$rangeArr = explode('-', $range);
|
||||
}
|
||||
|
||||
$this->validateRange($rangeArr, $index);
|
||||
$this->fillRange($register, $index, $rangeArr, $stepping);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $register
|
||||
* @param int $index
|
||||
* @param array $range
|
||||
* @param int $stepping
|
||||
*/
|
||||
private function fillRange(array &$register, int $index, array $range, int $stepping): void {
|
||||
$boundary = self::VALUE_BOUNDARIES[$index]['max'] + self::VALUE_BOUNDARIES[$index]['mod'];
|
||||
$length = $range[1] - $range[0];
|
||||
|
||||
for ($i = 0; $i <= $length; $i += $stepping) {
|
||||
$register[($range[0] + $i) % $boundary] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate whether a given range of values exceeds allowed value boundaries
|
||||
*
|
||||
* @param array $range
|
||||
* @param int $index
|
||||
* @throws Exception
|
||||
*/
|
||||
private function validateRange(array $range, int $index): void {
|
||||
if (sizeof($range) !== 2) {
|
||||
throw new Exception('invalid range notation');
|
||||
}
|
||||
|
||||
foreach ($range as $value) {
|
||||
$this->validateValue($value, $index);
|
||||
}
|
||||
|
||||
if ($range[0] > $range[1]) {
|
||||
throw new Exception('lower value in range is larger than upper value');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param int $index
|
||||
* @param int $step
|
||||
* @throws Exception
|
||||
*/
|
||||
private function validateValue(string $value, int $index, int $step = 1): void {
|
||||
if (false === ctype_digit($value)) {
|
||||
throw new Exception('non-integer value');
|
||||
}
|
||||
|
||||
if (
|
||||
intval($value) < self::VALUE_BOUNDARIES[$index]['min'] ||
|
||||
intval($value) > self::VALUE_BOUNDARIES[$index]['max']
|
||||
) {
|
||||
throw new Exception('value out of boundary');
|
||||
}
|
||||
|
||||
if ($step !== 1) {
|
||||
throw new Exception('invalid combination of value and stepping notation');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $segments
|
||||
* @param int $index
|
||||
* @throws Exception
|
||||
*/
|
||||
private function validateStepping(array $segments, int $index): void {
|
||||
if (sizeof($segments) !== 2) {
|
||||
throw new Exception('invalid stepping notation');
|
||||
}
|
||||
|
||||
if ((int)$segments[1] < 1 || (int)$segments[1] > self::VALUE_BOUNDARIES[$index]['max']) {
|
||||
throw new Exception('stepping out of allowed range');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $segments
|
||||
* @throws Exception
|
||||
*/
|
||||
private function validateDate(array $segments): void {
|
||||
$year = date('Y');
|
||||
|
||||
for ($y = 0; $y < 27; $y++) {
|
||||
foreach (array_keys($segments[3]) as $month) {
|
||||
foreach (array_keys($segments[2]) as $day) {
|
||||
if (false === checkdate($month, $day, $year + $y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false === isset($segments[date('w', strtotime(sprintf('%d-%d-%d', $year + $y, $month, $day)))])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('no date ever can match the given combination of day/month/weekday');
|
||||
}
|
||||
}
|
||||
216
application/migrations/196_cron_table.php
Normal file
216
application/migrations/196_cron_table.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
class Migration_cron_table extends CI_Migration {
|
||||
|
||||
public function up() {
|
||||
if (!$this->db->table_exists('cron')) {
|
||||
|
||||
// define the structure of the new cron table
|
||||
$this->dbforge->add_field(array(
|
||||
'id' => array(
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => '255',
|
||||
'null' => FALSE,
|
||||
),
|
||||
'enabled' => array(
|
||||
'type' => 'TINYINT',
|
||||
'constraint' => '1',
|
||||
'null' => FALSE,
|
||||
),
|
||||
'status' => array(
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => '255',
|
||||
'null' => TRUE,
|
||||
),
|
||||
'description' => array(
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => '255',
|
||||
'null' => TRUE,
|
||||
),
|
||||
'function' => array(
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => '255',
|
||||
'null' => FALSE,
|
||||
),
|
||||
'expression' => array(
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => '100',
|
||||
'null' => TRUE,
|
||||
),
|
||||
'last_run' => array(
|
||||
'type' => 'TIMESTAMP',
|
||||
'null' => TRUE,
|
||||
),
|
||||
'next_run' => array(
|
||||
'type' => 'TIMESTAMP',
|
||||
'null' => TRUE,
|
||||
),
|
||||
'modified' => array(
|
||||
'type' => 'TIMESTAMP',
|
||||
'null' => TRUE,
|
||||
),
|
||||
));
|
||||
|
||||
// we set the key for the id, in this case the id is not numerical
|
||||
$this->dbforge->add_key('id', TRUE);
|
||||
|
||||
// now we can create the new table
|
||||
$this->dbforge->create_table('cron');
|
||||
|
||||
// to transfer data for the file updates we load the optionslib library
|
||||
$this->load->library('OptionsLib');
|
||||
|
||||
// and we fill the table with the cronjobs
|
||||
$data = array(
|
||||
array(
|
||||
'id' => 'clublog_upload',
|
||||
'enabled' => '0',
|
||||
'status' => 'pending',
|
||||
'description' => 'Upload QSOs to Clublog',
|
||||
'function' => 'index.php/clublog/upload',
|
||||
'expression' => '3 */6 * * *',
|
||||
'last_run' => null,
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'lotw_lotw_upload',
|
||||
'enabled' => '0',
|
||||
'status' => 'pending',
|
||||
'description' => 'Upload QSOs to LoTW',
|
||||
'function' => 'index.php/lotw/lotw_upload',
|
||||
'expression' => '0 */1 * * *',
|
||||
'last_run' => null,
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'qrz_upload',
|
||||
'enabled' => '0',
|
||||
'status' => 'pending',
|
||||
'description' => 'Upload QSOs to QRZ',
|
||||
'function' => 'index.php/qrz/upload',
|
||||
'expression' => '6 */6 * * *',
|
||||
'last_run' => null,
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'qrz_download',
|
||||
'enabled' => '0',
|
||||
'status' => 'pending',
|
||||
'description' => 'Download QSOs from QRZ',
|
||||
'function' => 'index.php/qrz/download',
|
||||
'expression' => '18 */6 * * *',
|
||||
'last_run' => null,
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'hrdlog_upload',
|
||||
'enabled' => '0',
|
||||
'status' => 'pending',
|
||||
'description' => 'Upload QSOs to HRD',
|
||||
'function' => 'index.php/hrdlog/upload',
|
||||
'expression' => '12 */6 * * *',
|
||||
'last_run' => null,
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'eqsl_sync',
|
||||
'enabled' => '0',
|
||||
'status' => 'pending',
|
||||
'description' => 'Upload/download QSOs to/from Eqsl',
|
||||
'function' => 'index.php/eqsl/sync',
|
||||
'expression' => '9 */6 * * *',
|
||||
'last_run' => null,
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'update_lotw_users',
|
||||
'enabled' => '1',
|
||||
'status' => 'pending',
|
||||
'description' => 'Update LOTW Users Activity',
|
||||
'function' => 'index.php/update/lotw_users',
|
||||
'expression' => '10 1 * * 1',
|
||||
'last_run' => ($this->optionslib->get_option('lotw_users_update') ? date("Y-m-d H:i", strtotime($this->optionslib->get_option('lotw_users_update'))) : null),
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'update_update_clublog_scp',
|
||||
'enabled' => '1',
|
||||
'status' => 'pending',
|
||||
'description' => 'Update Clublog SCP Database File',
|
||||
'function' => 'index.php/update/update_clublog_scp',
|
||||
'expression' => '0 0 * * 0',
|
||||
'last_run' => ($this->optionslib->get_option('scp_update') ? date("Y-m-d H:i", strtotime($this->optionslib->get_option('scp_update'))) : null),
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'update_update_dok',
|
||||
'enabled' => '1',
|
||||
'status' => 'pending',
|
||||
'description' => 'Update DOK File',
|
||||
'function' => 'index.php/update/update_dok',
|
||||
'expression' => '0 0 1 * *',
|
||||
'last_run' => ($this->optionslib->get_option('dok_file_update') ? date("Y-m-d H:i", strtotime($this->optionslib->get_option('dok_file_update'))) : null),
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'update_update_sota',
|
||||
'enabled' => '1',
|
||||
'status' => 'pending',
|
||||
'description' => 'Update SOTA File',
|
||||
'function' => 'index.php/update/update_sota',
|
||||
'expression' => '5 0 1 * *',
|
||||
'last_run' => ($this->optionslib->get_option('sota_file_update') ? date("Y-m-d H:i", strtotime($this->optionslib->get_option('sota_file_update'))) : null),
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'update_update_wwff',
|
||||
'enabled' => '1',
|
||||
'status' => 'pending',
|
||||
'description' => 'Update WWFF File',
|
||||
'function' => 'index.php/update/update_wwff',
|
||||
'expression' => '10 0 1 * *',
|
||||
'last_run' => ($this->optionslib->get_option('wwff_file_update') ? date("Y-m-d H:i", strtotime($this->optionslib->get_option('wwff_file_update'))) : null),
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'update_update_pota',
|
||||
'enabled' => '1',
|
||||
'status' => 'pending',
|
||||
'description' => 'Update POTA File',
|
||||
'function' => 'index.php/update/update_pota',
|
||||
'expression' => '15 0 1 * *',
|
||||
'last_run' => ($this->optionslib->get_option('pota_file_update') ? date("Y-m-d H:i", strtotime($this->optionslib->get_option('pota_file_update'))) : null),
|
||||
'next_run' => null
|
||||
),
|
||||
array(
|
||||
'id' => 'update_dxcc',
|
||||
'enabled' => '1',
|
||||
'status' => 'pending',
|
||||
'description' => 'Update DXCC data',
|
||||
'function' => 'index.php/update/dxcc',
|
||||
'expression' => '20 0 1 */2 *',
|
||||
'last_run' => ($this->optionslib->get_option('dxcc_clublog_update') ? date("Y-m-d H:i", strtotime($this->optionslib->get_option('dxcc_clublog_update'))) : null),
|
||||
'next_run' => null
|
||||
),
|
||||
);
|
||||
$this->db->insert_batch('cron', $data);
|
||||
|
||||
// since we transfered the source for the file update timestamps we don't need this options anymore
|
||||
$this->db->delete('options', array('option_name' => 'lotw_users_update'));
|
||||
$this->db->delete('options', array('option_name' => 'scp_update'));
|
||||
$this->db->delete('options', array('option_name' => 'dok_file_update'));
|
||||
$this->db->delete('options', array('option_name' => 'sota_file_update'));
|
||||
$this->db->delete('options', array('option_name' => 'wwff_file_update'));
|
||||
$this->db->delete('options', array('option_name' => 'pota_file_update'));
|
||||
$this->db->delete('options', array('option_name' => 'dxcc_clublog_update'));
|
||||
}
|
||||
}
|
||||
|
||||
public function down() {
|
||||
|
||||
$this->dbforge->drop_table('cron');
|
||||
|
||||
}
|
||||
}
|
||||
93
application/models/Cron_model.php
Normal file
93
application/models/Cron_model.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
class Cron_model extends CI_Model
|
||||
{
|
||||
// get all crons from the database
|
||||
function get_crons() {
|
||||
$this->db->from('cron');
|
||||
|
||||
$results = array();
|
||||
|
||||
$results = $this->db->get()->result();
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
// get details for a specific cron
|
||||
function cron($id) {
|
||||
|
||||
$clean_id = $this->security->xss_clean($id);
|
||||
|
||||
$this->db->where('id', $clean_id);
|
||||
|
||||
return $this->db->get('cron');
|
||||
}
|
||||
|
||||
// set the modified timestamp
|
||||
function set_modified($cron) {
|
||||
$data = array(
|
||||
'modified' => date('Y-m-d H:i:s')
|
||||
);
|
||||
|
||||
$this->db->where('id', $cron);
|
||||
$this->db->update('cron', $data);
|
||||
}
|
||||
|
||||
// set a new status for the cron
|
||||
function set_status($cron, $status) {
|
||||
$data = array(
|
||||
'status' => $status
|
||||
);
|
||||
|
||||
$this->db->where('id', $cron);
|
||||
$this->db->update('cron', $data);
|
||||
}
|
||||
|
||||
// set the last run
|
||||
function set_last_run($cron) {
|
||||
$data = array(
|
||||
'last_run' => date('Y-m-d H:i:s')
|
||||
);
|
||||
|
||||
$this->db->where('id', $cron);
|
||||
$this->db->update('cron', $data);
|
||||
}
|
||||
|
||||
// set the calculated next run
|
||||
function set_next_run($cron,$timestamp) {
|
||||
$data = array(
|
||||
'next_run' => $timestamp
|
||||
);
|
||||
|
||||
$this->db->where('id', $cron);
|
||||
$this->db->update('cron', $data);
|
||||
}
|
||||
|
||||
// set the cron enabled flag
|
||||
function set_cron_enabled($cron, $cron_enabled) {
|
||||
$data = array (
|
||||
'enabled' => ($cron_enabled === 'true' ? 1 : 0),
|
||||
'status' => ($cron_enabled === 'true' ? 'pending' : 'disabled'),
|
||||
);
|
||||
|
||||
$this->db->where('id', $cron);
|
||||
$this->db->update('cron', $data);
|
||||
|
||||
$this->set_modified($cron);
|
||||
}
|
||||
|
||||
// set the edited details for a cron
|
||||
function edit_cron($id, $description, $expression, $enabled) {
|
||||
|
||||
$data = array (
|
||||
'description' => $description,
|
||||
'expression' => $expression,
|
||||
'enabled' => ($enabled === 'true' ? 1 : 0)
|
||||
);
|
||||
|
||||
$this->db->where('id', $id);
|
||||
$this->db->update('cron', $data);
|
||||
|
||||
$this->set_modified($id);
|
||||
}
|
||||
}
|
||||
78
application/views/cron/edit.php
Normal file
78
application/views/cron/edit.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<div class="modal fade" id="editCronModal" tabindex="-1" aria-labelledby="editCronModal" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="cronEditLabel">Edit Cronjob</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Identifier</th>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" disabled style="font-family: Courier New;" name="edit_cron_id" id="edit_cron_id" value="<?php echo $cron->id; ?>">
|
||||
<span class="input-group-text" data-bs-toggle="tooltip" data-bs-placement="right" title="ID's can't be changed">
|
||||
<i class="fas fa-info"></i>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Enabled</th>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
<input name="edit_cron_enable_switch" class="form-check-input" type="checkbox" role="switch" id="edit_<?php echo $cron->id; ?>" <?php if ($cron->enabled ?? '0') { echo 'checked'; } ?>>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Description</th>
|
||||
<td>
|
||||
<textarea class="form-control" name="edit_cron_description" id="edit_cron_description" maxlength="240" rows="2" style="width:100%;"><?php echo $cron->description; ?></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Intervall</th>
|
||||
<td>
|
||||
<p>Choose a preset from the dropdown</p>
|
||||
<?php
|
||||
// List of available Presets
|
||||
$presets = array(
|
||||
'' => 'Custom',
|
||||
'*/5 * * * *' => 'Every 5 Minutes',
|
||||
'*/15 * * * *' => 'Every 15 Minutes',
|
||||
'0 * * * *' => 'Every Hour',
|
||||
'0 */2 * * *' => 'Every 2 Hours',
|
||||
'0 0 * * *' => 'Every Day at Midnight',
|
||||
'0 3 * * 1' => 'Every Monday at 03:00',
|
||||
'0 0 1 * *' => 'First Day of Every Month at midnight',
|
||||
'0 2 1 */2 *' => 'Every 2 Months at 02:00'
|
||||
);
|
||||
?>
|
||||
|
||||
<select class="form-select mb-4" id="edit_cron_expression_dropdown" name="edit_cron_expression_dropdown">
|
||||
<?php foreach ($presets as $cron_preset => $label) : ?>
|
||||
<option value="<?php echo $cron_preset; ?>" <?php if ($cron->expression == $cron_preset) {
|
||||
echo " selected=\"selected\"";
|
||||
} ?>><?php echo $label; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<p class="text-center"> - OR -</p>
|
||||
<p>Enter your own Cron Expression</p>
|
||||
<input type="text" class="form-control mb-1" style="font-family: Courier New;" name="edit_cron_expression_custom" id="edit_cron_expression_custom" value="<?php echo htmlspecialchars($cron->expression); ?>">
|
||||
<em id="exp_humanreadable" style="display: none;"></em>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="editCron()" ><?php echo lang('admin_save'); ?></button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo lang('general_word_cancel'); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
97
application/views/cron/index.php
Normal file
97
application/views/cron/index.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<div class="container mb-4 mt-2">
|
||||
<br>
|
||||
<h2><?php echo $page_title; ?></h2>
|
||||
|
||||
<div style="display: none;" id="cron_message_area" role="alert"></div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
How it works
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<p class="card-text">
|
||||
The Cron Manager assists the administrator in managing cron jobs without requiring CLI access.
|
||||
</p>
|
||||
<p class="card-text">
|
||||
To execute cron jobs based on the data below, remove all old cron jobs and create a new one:
|
||||
</p>
|
||||
<div class="main_cronjob">
|
||||
<pre><code id="main_cronjob">* * * * * curl --silent <?php echo base_url(); ?>index.php/cron/run &>/dev/null</code><span data-bs-toggle="tooltip" title="<?php echo lang('copy_to_clipboard'); ?>" onclick='copyCron("main_cronjob")'><i class="copy-icon fas fa-copy"></i></span></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col text-end" id="alert_status">
|
||||
<div class="alert alert-<?php echo $mastercron['status_class'] ?? 'danger'; ?> d-inline-block">
|
||||
Status Master-Cron: <?php echo $mastercron['status'] ?? 'Not running'; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Cron List
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if ($mastercron['status_class'] != 'danger') { ?>
|
||||
<div class="table-responsive">
|
||||
<table id="cron_table" style="width:100%" class="crontable table table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Description</th>
|
||||
<th>Status</th>
|
||||
<th>Intervall</th>
|
||||
<th>Last Run</th>
|
||||
<th>Next Run</th>
|
||||
<th>Edit</th>
|
||||
<th>I/O</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($crons as $cron) { ?>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;" class='cron_<?php echo $cron->id; ?>'><?php echo $cron->id; ?></td>
|
||||
<td style="vertical-align: middle;"><?php echo $cron->description; ?></td>
|
||||
<td style="vertical-align: middle;"><?php
|
||||
if ($cron->enabled == '1') {
|
||||
if ($cron->status == 'healthy') { ?>
|
||||
<span class="badge text-bg-success">healthy</span>
|
||||
<?php } else if ($cron->status == 'failed') { ?>
|
||||
<span class="badge text-bg-danger">failed</span>
|
||||
<?php } else { ?>
|
||||
<span class="badge text-bg-warning"><?php echo $cron->status; ?></span>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<span class="badge text-bg-secondary">disabled</span>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td style="vertical-align: middle;"><?php echo '<code id="humanreadable_tooltip" data-bs-toggle="tooltip">' . $cron->expression . '</code>'; ?></td>
|
||||
<td style="vertical-align: middle;"><?php echo $cron->last_run ?? 'never'; ?></td>
|
||||
<td style="vertical-align: middle;"><?php if ($cron->enabled == '1') {
|
||||
echo $cron->next_run ?? 'never';
|
||||
} else {
|
||||
echo 'never';
|
||||
} ?></td>
|
||||
<td style="vertical-align: middle;"><button id="<?php echo $cron->id; ?>" class="editCron btn btn-outline-primary btn-sm"><i class="fas fa-edit"></i></button></td>
|
||||
<td style="vertical-align: middle;">
|
||||
<div class="form-check form-switch"><input name="cron_enable_switch" class="form-check-input enableCronSwitch" type="checkbox" role="switch" id="<?php echo $cron->id; ?>" <?php if ($cron->enabled ?? '0') {
|
||||
echo 'checked';
|
||||
} ?>></div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<div class="text-center">
|
||||
<h4>Your Mastercron isn't running.<br>Copy the cron above to a external cron service or into your server's cron to use this cron manager.</h4>
|
||||
<p>On a basic linux server with shell access use this command to edit your crons:<pre><code>crontab -e</code></pre></p>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -462,38 +462,38 @@
|
||||
</thead>
|
||||
<tr>
|
||||
<td>DXCC update from Club Log</td>
|
||||
<td><?php echo (($this->optionslib->get_option('dxcc_clublog_update') ?? '') == '' ? '' : date($custom_date_format, strtotime($this->optionslib->get_option('dxcc_clublog_update') ?? '')) . ' ' . date("h:i", strtotime($this->optionslib->get_option('dxcc_clublog_update') ?? ''))) ?></td>
|
||||
<td><?php echo $dxcc_update->last_run ?? 'never'; ?></td>
|
||||
<td><a class="btn btn-sm btn-primary" href="<?php echo site_url('update'); ?>">Update</a></td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DOK file download</td>
|
||||
<td><?php echo (($this->optionslib->get_option('dok_file_update') ?? '') == '' ? '' : date($custom_date_format, strtotime($this->optionslib->get_option('dok_file_update') ?? '')) . ' ' . date("h:i", strtotime($this->optionslib->get_option('dok_file_update') ?? ''))) ?></td>
|
||||
<td><?php echo $dok_update->last_run ?? 'never'; ?></td>
|
||||
<td><a class="btn btn-sm btn-primary" href="<?php echo site_url('update/update_dok'); ?>">Update</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>LoTW users download</td>
|
||||
<td><?php echo (($this->optionslib->get_option('lotw_users_update') ?? '') == '' ? '' : date($custom_date_format, strtotime($this->optionslib->get_option('lotw_users_update') ?? '')) . ' ' . date("h:i", strtotime($this->optionslib->get_option('lotw_users_update') ?? ''))) ?></td>
|
||||
<td><?php echo $lotw_user_update->last_run ?? 'never'; ?></td>
|
||||
<td><a class="btn btn-sm btn-primary" href="<?php echo site_url('update/lotw_users'); ?>">Update</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POTA file download</td>
|
||||
<td><?php echo (($this->optionslib->get_option('pota_file_update') ?? '') == '' ? '' : date($custom_date_format, strtotime($this->optionslib->get_option('pota_file_update') ?? '')) . ' ' . date("h:i", strtotime($this->optionslib->get_option('pota_file_update') ?? ''))) ?></td>
|
||||
<td><?php echo $pota_update->last_run ?? 'never'; ?></td>
|
||||
<td><a class="btn btn-sm btn-primary" href="<?php echo site_url('update/update_pota'); ?>">Update</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SCP file download</td>
|
||||
<td><?php echo (($this->optionslib->get_option('scp_update') ?? '') == '' ? '' : date($custom_date_format, strtotime($this->optionslib->get_option('scp_update') ?? '')) . ' ' . date("h:i", strtotime($this->optionslib->get_option('scp_update') ?? ''))) ?></td>
|
||||
<td><?php echo $scp_update->last_run ?? 'never'; ?></td>
|
||||
<td><a class="btn btn-sm btn-primary" href="<?php echo site_url('update/update_clublog_scp'); ?>">Update</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SOTA file download</td>
|
||||
<td><?php echo (($this->optionslib->get_option('sota_file_update') ?? '') == '' ? '' : date($custom_date_format, strtotime($this->optionslib->get_option('sota_file_update') ?? '')) . ' ' . date("h:i", strtotime($this->optionslib->get_option('sota_file_update') ?? ''))) ?></td>
|
||||
<td><?php echo $sota_update->last_run ?? 'never'; ?></td>
|
||||
<td><a class="btn btn-sm btn-primary" href="<?php echo site_url('update/update_sota'); ?>">Update</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WWFF file download</td>
|
||||
<td><?php echo (($this->optionslib->get_option('wwff_file_update') ?? '') == '' ? '' : date($custom_date_format, strtotime($this->optionslib->get_option('wwff_file_update') ?? '')) . ' ' . date("h:i", strtotime($this->optionslib->get_option('wwff_file_update') ?? ''))) ?></td>
|
||||
<td><?php echo $wwff_update->last_run ?? 'never'; ?></td>
|
||||
<td><a class="btn btn-sm btn-primary" href="<?php echo site_url('update/update_wwff'); ?>">Update</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -106,6 +106,11 @@ if($this->session->userdata('user_id') != null) {
|
||||
<script src="<?php echo base_url() ;?>assets/js/sections/oqrs.js"></script>
|
||||
<?php } ?>
|
||||
|
||||
<!-- JS library to convert cron format to human readable -->
|
||||
<?php if ($this->uri->segment(1) == "cron") { ?>
|
||||
<script src="<?php echo base_url() ;?>assets/js/cronstrue.min.js"async></script>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($this->uri->segment(1) == "options") { ?>
|
||||
<script>
|
||||
$('#sendTestMailButton').click(function() {
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="<?php echo site_url('update'); ?>" title="Update Country Files"><i class="fas fa-sync"></i> <?php echo lang('menu_update_country_files'); ?></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="<?php echo site_url('cron'); ?>" title="Cron Manager"><i class="fas fa-clock"></i> Cron Manager</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="<?php echo site_url('debug'); ?>" title="Debug Information"><i class="fas fa-tools"></i> <?php echo lang('menu_debug_information'); ?></a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
1
assets/js/cronstrue.min.js
vendored
Normal file
1
assets/js/cronstrue.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
245
assets/js/sections/cron.js
Normal file
245
assets/js/sections/cron.js
Normal file
@@ -0,0 +1,245 @@
|
||||
$(document).ready(function () {
|
||||
init_datatable();
|
||||
|
||||
$(document).on('click', '.editCron', async function (e) { // Dynamic binding, since element doesn't exists when loading this JS
|
||||
editCronDialog(e);
|
||||
});
|
||||
|
||||
$(document).on('click', '.enableCronSwitch', async function (e) { // Dynamic binding, since element doesn't exists when loading this JS
|
||||
toggleEnableCronSwitch(e.currentTarget.id, this);
|
||||
});
|
||||
});
|
||||
|
||||
function copyCron(id) {
|
||||
var content = $('#' + id).text();
|
||||
|
||||
navigator.clipboard.writeText(content).then(function () { });
|
||||
|
||||
$('#' + id).addClass('flash-copy').delay('1000').queue(function () {
|
||||
$('#' + id).removeClass('flash-copy').dequeue();
|
||||
});
|
||||
}
|
||||
|
||||
function init_expression_tooltips() {
|
||||
$('.crontable tbody tr').each(function () {
|
||||
var expression = $(this).find('td:eq(3)').text().trim();
|
||||
|
||||
var humanReadable = cronstrue.toString(expression);
|
||||
|
||||
$(this).find('#humanreadable_tooltip').attr('data-bs-original-title', humanReadable).tooltip();
|
||||
});
|
||||
}
|
||||
|
||||
function init_datatable() {
|
||||
$('.crontable').DataTable({
|
||||
"pageLength": 25,
|
||||
responsive: true,
|
||||
ordering: true,
|
||||
"scrollY": "600px",
|
||||
"scrollCollapse": true,
|
||||
"paging": false,
|
||||
"scrollX": true,
|
||||
"autoWidth": false,
|
||||
"language": {
|
||||
url: getDataTablesLanguageUrl(),
|
||||
},
|
||||
dom: 'Bfrtip',
|
||||
buttons: [
|
||||
{
|
||||
text: 'Refresh',
|
||||
action: function (e, dt, node, config) {
|
||||
reloadCrons();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
init_expression_tooltips();
|
||||
}
|
||||
|
||||
function modalEventListener() {
|
||||
$('#edit_cron_expression_custom').on('input change', function (e) {
|
||||
humanReadableInEditDialog()
|
||||
});
|
||||
|
||||
$('#edit_cron_expression_dropdown').change(function () {
|
||||
humanReadableInEditDialog()
|
||||
});
|
||||
}
|
||||
|
||||
function displayMessages(category, message) {
|
||||
var html_class;
|
||||
var message_area = $('#cron_message_area');
|
||||
|
||||
if (category == 'success') {
|
||||
html_class = 'alert alert-success';
|
||||
} else if (category == 'warning') {
|
||||
html_class = 'alert alert-warning';
|
||||
} else if (category == 'error') {
|
||||
html_class = 'alert alert-danger';
|
||||
} else {
|
||||
html_class = 'alert alert-info';
|
||||
}
|
||||
|
||||
message_area.show();
|
||||
message_area.addClass(html_class);
|
||||
message_area.text(message);
|
||||
|
||||
setTimeout(function () {
|
||||
message_area.fadeOut();
|
||||
}, 7000);
|
||||
}
|
||||
|
||||
function editCronDialog(e) {
|
||||
$('#editCronModal').remove();
|
||||
|
||||
$.ajax({
|
||||
url: base_url + 'index.php/cron/editDialog',
|
||||
type: 'post',
|
||||
data: {
|
||||
id: e.currentTarget.id,
|
||||
},
|
||||
success: function (data) {
|
||||
$('body').append(data);
|
||||
|
||||
var editCronModal = new bootstrap.Modal(document.getElementById('editCronModal'));
|
||||
editCronModal.show();
|
||||
modalEventListener();
|
||||
$('[data-bs-toggle="tooltip"]').tooltip();
|
||||
},
|
||||
error: function (data) {
|
||||
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function editCron() {
|
||||
var $cron_id = $('#edit_cron_id').val();
|
||||
var $cron_description = $('#edit_cron_description').val();
|
||||
var $cron_expression = $('#edit_cron_expression_custom').val();
|
||||
var $cron_enabled = $('#edit_' + $cron_id).is(':checked') ? 'true' : 'false';
|
||||
|
||||
$.ajax({
|
||||
url: base_url + 'index.php/cron/edit',
|
||||
method: 'POST',
|
||||
data: {
|
||||
cron_id: $cron_id,
|
||||
cron_description: $cron_description,
|
||||
cron_expression: $cron_expression,
|
||||
cron_enabled: $cron_enabled
|
||||
},
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
reloadCrons();
|
||||
displayMessages(response.messagecategory, response.message);
|
||||
} else {
|
||||
displayMessages(response.messagecategory, response.message);
|
||||
}
|
||||
},
|
||||
error: function (response) {
|
||||
displayMessages('error', 'The query failed for a unknown reason');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function humanReadableInEditDialog() {
|
||||
var exp_inputID = $('#edit_cron_expression_custom');
|
||||
var exp_dropdownID = $('#edit_cron_expression_dropdown');
|
||||
var exp_humanreadableID = $('#exp_humanreadable');
|
||||
var humanReadable = '';
|
||||
|
||||
exp_inputID.on('input', function () {
|
||||
exp_dropdownID.val('');
|
||||
});
|
||||
|
||||
if (exp_dropdownID.val() == '') {
|
||||
exp_humanreadableID.show();
|
||||
|
||||
try {
|
||||
humanReadable = cronstrue.toString(exp_inputID.val());
|
||||
} catch (error) {
|
||||
humanReadable = 'waiting for complete expression...';
|
||||
}
|
||||
|
||||
exp_humanreadableID.text(humanReadable);
|
||||
} else {
|
||||
exp_humanreadableID.hide();
|
||||
|
||||
exp_inputID.val(exp_dropdownID.val());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toggleEnableCronSwitch(id, thisvar) {
|
||||
$.ajax({
|
||||
url: base_url + 'index.php/cron/toogleEnableCronSwitch',
|
||||
type: 'post',
|
||||
data: {
|
||||
id: id,
|
||||
checked: $(thisvar).is(':checked')
|
||||
},
|
||||
success: function (data) {
|
||||
reloadCrons();
|
||||
},
|
||||
error: function (data) {
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function reloadCrons() {
|
||||
$.ajax({
|
||||
url: base_url + 'index.php/cron/fetchCrons',
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
loadCronTable(data);
|
||||
},
|
||||
error: function (data) {
|
||||
BootstrapDialog.alert({
|
||||
title: 'ERROR',
|
||||
message: 'An error ocurred while making the request',
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
closable: false,
|
||||
draggable: false,
|
||||
callback: function (result) {
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadCronTable(rows) {
|
||||
var uninitialized = $('.crontable').filter(function () {
|
||||
return !$.fn.DataTable.fnIsDataTable(this);
|
||||
});
|
||||
|
||||
uninitialized.each(function () {
|
||||
init_datatable();
|
||||
});
|
||||
|
||||
var table = $('.crontable').DataTable();
|
||||
|
||||
table.clear();
|
||||
|
||||
for (i = 0; i < rows.length; i++) {
|
||||
let cron = rows[i];
|
||||
|
||||
var data = [];
|
||||
data.push(cron.cron_id);
|
||||
data.push(cron.cron_description);
|
||||
data.push(cron.cron_status);
|
||||
data.push(cron.cron_expression);
|
||||
data.push(cron.cron_last_run);
|
||||
data.push(cron.cron_next_run);
|
||||
data.push(cron.cron_edit);
|
||||
data.push(cron.cron_enabled);
|
||||
|
||||
let createdRow = table.row.add(data).index();
|
||||
}
|
||||
table.draw();
|
||||
init_expression_tooltips();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user