* Tue Jun 24 2025 Brian Read <brianr@koozali.org> 11.0.0-95.sme

- Add clock ticker to datetime panel [SME: 13054]
- Add Test Server button for ntp server [SME: 13048]
- Add checking that date is fully valid [SME: 13055]
This commit is contained in:
2025-06-25 10:35:16 +01:00
parent 0341d02608
commit 0f2e2b82aa
8 changed files with 210 additions and 62 deletions

View File

@@ -37,7 +37,7 @@ use esmith::NavigationDB; # no UTF8 raw is ok for ASCII only flat file
use SrvMngr_Auth qw(check_admin_access);
#this is overwrittrn with the "release" by the spec file - release can be "99.el8.sme"
our $VERSION = '93.el8.sme';
our $VERSION = '94.el8.sme';
#Extract the release value
if ($VERSION =~ /^(\d+)/) {
$VERSION = $1; # $1 contains the matched numeric digits
@@ -328,6 +328,7 @@ sub setup_routing {
$if_admin->get('/datetime')->to('datetime#main')->name('datetime');
$if_admin->post('/datetimeu')->to('datetime#do_update')->name('datetimeu');
$if_admin->get('/datetimed')->to('datetime#do_display')->name('datetimed');
$if_admin->post('/datetimet')->to('datetime#do_testntp')->name('datetimet');
$if_admin->get('/directory')->to('directory#main')->name('directory');

View File

@@ -13,6 +13,8 @@ use esmith::NetworksDB::UTF8;
use esmith::HostsDB;
use esmith::DomainsDB::UTF8;
use DateTime;
use constant FALSE => 0;
use constant TRUE => 1;
@@ -79,6 +81,7 @@ my $ddb;
my $now_sec = sprintf('%02d', $today_sec);
my $current_year = $today_year;
my $ntpserverurl = $cdb->get_prop('ntpd','NTPServer');
my $now = DateTime->now( time_zone => 'local' );
my %ret = (
# fields from Inputs
'time_mode'=>($ntpserverurl eq '' ? 'dat_manually_set' : 'dat_ntp_server'),
@@ -90,6 +93,8 @@ my $ddb;
'minute'=>"$now_min",
'second'=>"$now_sec",
'ntpstatus' => $cdb->get_prop('ntpd','status') || 'disabled',
# and the current time as a full format
'currentdatetime' => $now->strftime('%Y-%m-%dT%H:%M:%S')
);
return %ret;
@@ -224,38 +229,12 @@ sub validate_change_datetime {
$timezone = "US/Eastern";
}
my $month = $c->param('month');
if ($month =~ /^(\d{1,2})$/) {
$month = $1;
} else {
$month = "1";
}
if (($month < 1) || ($month > 12)) {
return $c->l('dat_INVALID_MONTH') . " $month. " . $c->l('dat_MONTH_BETWEEN_1_AND_12');
}
my $day = $c->param('day');
if ($day =~ /^(\d{1,2})$/) {
$day = $1;
} else {
$day = "1";
}
if (($day < 1) || ($day > 31)) {
return $c->l('dat_INVALID_DAY') . " $day. " . $c->l('dat_BETWEEN_1_AND_31');
}
my $year = $c->param('year');
if (!is_valid_date($year, $month, $day)){
return $c->l('dat_Invalid_date')
}
if ($year =~ /^(\d{4})$/) {
$year = $1;
} else {
$year = "2000";
}
if (($year < 1900) || ($year > 2200)) {
return $c->l('dat_INVALID_YEAR') . " $year. " . $c->l('dat_FOUR_DIGIT_YEAR');
}
my $hour = $c->param('hour');
if ($hour =~ /^(\d{1,2})$/) {
@@ -289,24 +268,6 @@ sub validate_change_datetime {
if (($second < 0) || ($second > 59)) {
return $c->l('dat_INVALID_SECOND') . " $second. " . $c->l('dat_BETWEEN_0_AND_59');
}
#my $ampm = $c->param('Ampm');
#Move to 24 hours clock - not using AM/PM.
#if ($ampm =~ /^(AM|PM)$/) {
#$ampm = $1;
#} else {
#$ampm = "AM";
#}
# force AM so that it actually works on 24hr clock.
#$ampm = "AM";
# convert to 24 hour time
#$hour = $hour % 12;
#if ($ampm eq "PM") {
# $hour = $hour + 12;
#}
#--------------------------------------------------
# Store time zone in configuration database
@@ -325,10 +286,27 @@ sub validate_change_datetime {
# and hardware clock
#--------------------------------------------------
my $newdate = sprintf "%02d%02d%02d%02d%04d.%02d", $month, $day, $hour, $minute, $year, $second;
esmith::util::backgroundCommand(2, "/sbin/e-smith/signal-event", "timezone-update", $newdate);
$c->app->log->info("Changing date manually to $newdate");
esmith::util::backgroundCommand(2, "/sbin/e-smith/signal-event", "timezone-update", $newdate); #TEMP!!!
return '';
} ## end sub validate_change_datetime
sub is_valid_date {
my ($year, $month, $day) = @_;
# Check if all parts are defined and integers
return 0 unless defined $year && defined $month && defined $day;
return 0 unless $year =~ /^\d+$/ && $month =~ /^\d+$/ && $day =~ /^\d+$/;
# Try to construct a DateTime object
eval {
DateTime->new(year => $year, month => $month, day => $day);
1;
} or return 0;
return 1;
}
sub update_ntpserver {
my $c = shift;
my $ntpserver = shift;
@@ -378,6 +356,4 @@ sub disable_ntp {
return '';
} ## end sub disable_ntp
1;

View File

@@ -8,6 +8,9 @@ package SrvMngr::Controller::Datetime;
# heading : System
# description : Date and time
# navigation : 4000 300
#
# ######name : datetimet, method : post, url : /datetimet, ctlact : datetime#testntp
#
# routes : end
#
# Documentation: https://wiki.contribs.org/Datetime
@@ -115,7 +118,7 @@ sub do_update {
my $c = shift;
$c->app->log->info($c->log_req);
$c->app->log->info($c->param('month'));
#$c->app->log->info($c->param('month'));
#The most common ones - you might want to delete some of these if they are not used.
@@ -173,7 +176,7 @@ sub do_update {
$c->render(template => "datetime");
return
} else {
if ($c->param('time_mode') eq 'data_manually_set') {
if ($c->param('time_mode') eq 'dat_manually_set') {
$c->stash( success => $c->l('dat_UPDATING_CLOCK'));
} else {
$c->stash( success => $c->l('dat_SETTINGS_CHANGED'));
@@ -279,5 +282,52 @@ sub do_display {
dat_data => \%dat_data
);
$c->render(template => "datetime");
}
}
sub do_testntp {
my $c = shift;
my $server = $c->req->json->{ntpserver} // '';
# Strict validation: hostname or IPv4
unless ($server =~ /^(?=.{1,253}$)([a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*|\d{1,3}(?:\.\d{1,3}){3})$/) {
return $c->render(json => { success => 0, error => 'Invalid server name or IP' });
}
my $timeout = 5;
my @cmd = ('timeout', $timeout, 'ntpdate', '-q', $server);
# Run ntpdate and capture output
my $output = qx{@cmd 2>&1};
$c->app->log->info($output);
my $exit_code = $? >> 8;
# Parse for known errors
if ($exit_code == 124) {
return $c->render(json => { success => 0, error => "Timeout: NTP server did not respond within $timeout seconds" });
}
if ($output =~ /no server suitable for synchronization found/i) {
return $c->render(json => { success => 0, error => "No suitable NTP server found or server unreachable" });
}
if ($output =~ /Name or service not known|Temporary failure in name resolution/i) {
return $c->render(json => { success => 0, error => "DNS resolution failed for $server" });
}
if ($output =~ /ntpdig: no eligible servers/i) {
return $c->render(json => { success => 0, error => "Not a an NTP server" });
}
if ($output =~ /permission denied/i) {
return $c->render(json => { success => 0, error => "Permission denied running ntpdate" });
}
if ($exit_code != 0) {
return $c->render(json => { success => 0, error => "ntpdate failed (exit code $exit_code): $output" });
}
# Extract date and time down to seconds from adjust line
my ($datetime) = $output =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/m;
if ($datetime) {
return $c->render(json => { success => 1, time => $datetime });
} else {
return $c->render(json => { success => 0, error => "Could not parse date/time from NTP server response." });}
}
1;

View File

@@ -1,5 +1,5 @@
'dat_FORM_TITLE' => 'Date and time configuration',
'dat_The_time_is_currently' => 'The time is currently:',
'dat_INITIAL_DESC' => 'This is where you configure the date and time of this server. You may use an existing network time server or
manually set the date and time for your time zone.',
'dat_SET_DATE_TITLE' => 'Set Date and Time',
@@ -58,4 +58,5 @@ clock, and <b>will not</b> try to synchronize from a time server.',
'dat_ntp_server' => 'NTP server',
'dat_manually_set' => 'Set manually',
'dat_NTP_Server_URL' =>'NTP Server URL:',
'dat_set_manually' =>'Set Date and Time:',
'dat_set_manually' =>'Set Date and Time:',
'dat_Invalid_date' => 'Invalid date',