diff options
-rw-r--r-- | lib/AdminPanel/Module/Clock.pm | 336 | ||||
-rw-r--r-- | lib/AdminPanel/Shared/TimeZone.pm | 462 | ||||
-rwxr-xr-x | scripts/adminClock | 9 |
3 files changed, 807 insertions, 0 deletions
diff --git a/lib/AdminPanel/Module/Clock.pm b/lib/AdminPanel/Module/Clock.pm new file mode 100644 index 00000000..5525dda3 --- /dev/null +++ b/lib/AdminPanel/Module/Clock.pm @@ -0,0 +1,336 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Module::Clock; +#============================================================= -*-perl-*- + +=head1 NAME + +AdminPanel::Module::Clock - This module aims to configure system clock and time + +=head1 SYNOPSIS + + my $clockSettings = AdminPanel::Module::Clock->new(); + $clockSettings->start(); + +=head1 DESCRIPTION + +Long_description + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command: + +perldoc AdminPanel::Module::Clock + +=head1 SEE ALSO + +SEE_ALSO + +=head1 AUTHOR + +Angelo Naselli <anaselli@linux.it> + +=head1 COPYRIGHT and LICENSE + +Copyright (C) 2014, Angelo Naselli. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2, as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +=head1 FUNCTIONS + +=cut + +use Moose; + +use diagnostics; +use strict; + +use AdminPanel::Shared::GUI; +use AdminPanel::Shared::Locales; +use AdminPanel::Shared::TimeZone; +use AdminPanel::Shared::Services;# qw (services); + + +use Time::Piece; + +use yui; + +extends qw( AdminPanel::Module ); + +### TODO icon +has '+icon' => ( + default => "/usr/share/mcc/themes/default/time-mdk.png", +); + +has 'loc' => ( + is => 'rw', + init_arg => undef, + builder => '_localeInitialize' +); + +sub _localeInitialize { + my $self = shift; + + # TODO fix domain binding for translation + $self->loc(AdminPanel::Shared::Locales->new(domain_name => 'libDrakX-standalone') ); + # TODO if we want to give the opportunity to test locally add dir_name => 'path' +} + +has 'sh_gui' => ( + is => 'rw', + lazy => 1, + init_arg => undef, + builder => '_SharedGUIInitialize' +); + +sub _SharedGUIInitialize { + my $self = shift; + + $self->sh_gui(AdminPanel::Shared::GUI->new() ); +} + +has 'sh_tz' => ( + is => 'rw', + lazy => 1, + init_arg => undef, + builder => '_SharedTimeZoneInitialize' +); + +sub _SharedTimeZoneInitialize { + my $self = shift; + + $self->sh_tz(AdminPanel::Shared::TimeZone->new() ); +} + + +=head1 VERSION + +Version 1.0.0 + +=cut + +our $VERSION = '1.0.0'; + +#============================================================= + +=head2 BUILD + +=head3 INPUT + + $self: this object + +=head3 DESCRIPTION + + The BUILD method is called after a Moose object is created, + in this methods Services loads all the service information. + +=cut + +#============================================================= +sub BUILD { + my $self = shift; + + if (! $self->name) { + $self->name ($self->loc->N("Date, Clock & Time Zone Settings")); + } +} + +#============================================================= + +=head2 start + +=head3 INPUT + + $self: this object + +=head3 DESCRIPTION + + This method extends Module::start and is invoked to + start admin clock + +=cut + +#============================================================= +sub start { + my $self = shift; + + $self->_adminClockPanel(); +}; + + + + + +sub _adminClockPanel { + my $self = shift; + + my $appTitle = yui::YUI::app()->applicationTitle(); + + ## set new title to get it in dialog + yui::YUI::app()->setApplicationTitle($self->name); + ## set icon if not already set by external launcher + yui::YUI::app()->setApplicationIcon($self->icon); + + my $factory = yui::YUI::widgetFactory; + my $optFactory = yui::YUI::optionalWidgetFactory; + die "calendar widgets missing" if (!$optFactory->hasDateField() || !$optFactory->hasTimeField()); + + # Create Dialog + my $dialog = $factory->createMainDialog; + + # Start Dialog layout: + my $layout = $factory->createVBox( $dialog ); + my $align; + + my $hbox = $factory->createHBox($layout); + + my $dateField = $optFactory->createDateField($hbox, ""); + $factory->createHSpacing($hbox, 1.0); + my $timeField = $optFactory->createTimeField($hbox, ""); + my $t = localtime; + my $day = $t->strftime("%F"); + my $time = $t->strftime("%H:%M:%S"); + $dateField->setValue($day); + $timeField->setValue($time); + + $factory->createHSpacing($hbox, 1.0); + my $frame = $factory->createFrame ($hbox, $self->loc->N("TimeZone")); + my $vbox = $factory->createVBox( $frame ); + my $timezone = $self->sh_tz->readConfiguration(); + my $timeZoneLbl = $factory->createLabel($vbox, $self->loc->N("not defined")); + if (exists $timezone->{ZONE}) { + $timeZoneLbl->setValue($timezone->{ZONE}); + } + + my $changeTZButton = $factory->createPushButton($vbox, $self->loc->N("Change Time Zone")); + + $hbox = $factory->createHBox($layout); + my $ntpFrame = $factory->createCheckBoxFrame($hbox, $self->loc->N("Enable Network Time Protocol"), 0); +# $ntpFrame->setWeight($yui::YD_HORIZ, 1); + + $vbox = $factory->createVBox( $ntpFrame ); + $factory->createLabel($vbox, $self->loc->N("Your computer can synchronize its clock with a remote time server using NTP")); + my $hbox1 = $factory->createHBox($vbox); + $factory->createLabel($hbox1,$self->loc->N("Server:")); + my $ntpServers = $factory->createComboBox( $hbox1, "" ); + +# + + + + +####################### +################## + + # buttons on the last line + $align = $factory->createLeft($layout); + $hbox = $factory->createHBox($align); + my $aboutButton = $factory->createPushButton($hbox, $self->loc->N("About") ); + my $resetButton = $factory->createPushButton($hbox, $self->loc->N("Reset") ); + $align = $factory->createRight($hbox); + $hbox = $factory->createHBox($align); + my $cancelButton = $factory->createPushButton($hbox, $self->loc->N("Cancel")); + my $okButton = $factory->createPushButton($hbox, $self->loc->N("Ok")); + + ## no changes by default + $dialog->setDefaultButton($cancelButton); + + # End Dialof layout + + while(1) { + my $event = $dialog->waitForEvent(1000); + my $eventType = $event->eventType(); + + + #event type checking + if ($eventType == $yui::YEvent::CancelEvent) { + last; + } + elsif ($eventType == $yui::YEvent::TimeoutEvent) { + $t = Time::Piece->strptime($timeField->value(), "%H:%M:%S") + 1; + $timeField->setValue($t->strftime("%H:%M:%S")); + } + elsif ($eventType == $yui::YEvent::WidgetEvent) { + # widget selected + my $widget = $event->widget(); + if ($widget == $cancelButton) { + last; + } + elsif ($widget == $okButton) { + ### TODO manage OK pressed ### + last; + } + elsif ($widget == $changeTZButton) { + my $timezones = $self->sh_tz->getTimeZones(); + if (!$timezones || scalar (@{$timezones}) == 0) { + $self->sh_gui->warningMsgBox({title => $self->loc->N("Timezone - DrakClock"), + text => $self->loc->N("Failed to retrieve timezone list"), + }); + $changeTZButton->setDisabled(); + } + else { + my $item = $self->sh_gui->ask_fromTreeList({title => $self->loc->N("Timezone - DrakClock"), + header => $self->loc->N("Which is your timezone?"), + default_button => 1, + item_separator => '/', + default_item => $timezone->{ZONE}, + list => $timezones}); + if ($item) { + $timezone->{UTC} = $self->sh_gui->ask_YesOrNo({ + title => $self->loc->N("GMT - DrakClock"), + text => $self->loc->N("Is your hardware clock set to GMT?"), + default_button => $timezone->{UTC}, + }); + $timezone->{ZONE} = $item; + $timeZoneLbl->setValue($timezone->{ZONE}); + } + } + } + elsif ($widget == $resetButton) { + $t = localtime; + $day = $t->strftime("%F"); + $time = $t->strftime("%H:%M:%S"); + $dateField->setValue($day); + $timeField->setValue($time); + } + elsif($widget == $aboutButton) { + my $translators = $self->loc->N("_: Translator(s) name(s) & email(s)\n"); + $translators =~ s/\</\<\;/g; + $translators =~ s/\>/\>\;/g; + $self->sh_gui->AboutDialog({ name => $self->name, + version => $self->VERSION, + credits => $self->loc->N("Copyright (C) %s Mageia community", '2014'), + license => $self->loc->N("GPLv2"), + description => $self->loc->N("Date, Clock & Time Zone Settings allows to setup time zone and adjust date and time"), + authors => $self->loc->N("<h3>Developers</h3> + <ul><li>%s</li> + <li>%s</li> + </ul> + <h3>Translators</h3> + <ul><li>%s</li></ul>", + "Angelo Naselli <anaselli\@linux.it>", + $translators + ), + } + ); + } + } + } + $dialog->destroy(); + + #restore old application title + yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle; +} + + + diff --git a/lib/AdminPanel/Shared/TimeZone.pm b/lib/AdminPanel/Shared/TimeZone.pm new file mode 100644 index 00000000..8b1eb81c --- /dev/null +++ b/lib/AdminPanel/Shared/TimeZone.pm @@ -0,0 +1,462 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Shared::TimeZone; + +#============================================================= -*-perl-*- + +=head1 NAME + +AdminPanel::Shared::TimeZone - module to manage TimeZone settings + +=head1 SYNOPSIS + + my $tz = AdminPanel::Shared::TimeZone->new(); + + +=head1 DESCRIPTION + +This module allows to manage time zone settings. + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command: + +perldoc AdminPanel::Shared::TimeZone + + +=head1 AUTHOR + +Angelo Naselli <anaselli@linux.it> + +=head1 COPYRIGHT and LICENSE + +Copyright (C) 2014, Angelo Naselli. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2, as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +=head1 METHODS + +=cut + + +use diagnostics; +use strict; + +use Moose; + +use DateTime::TimeZone; +use Config::Auto; + +#============================================================= + +=head2 new - optional parameters + +=head3 timezone_prefix + + optional parameter to set the system timezone directory, + default value is /usr/share/zoneinfo + +=cut + +#============================================================= + +has 'timezone_prefix' => ( + is => 'rw', + isa => 'Str', + default => "/usr/share/zoneinfo", +); + +#============================================================= + +=head2 new - optional parameters + +=head3 clock_configuration_file + + optional parameter to set the clock system configuration file, + default value is /etc/sysconfig/clock + +=cut + +#============================================================= + +has 'clock_configuration_file' => ( + is => 'rw', + isa => 'Str', + default => "/etc/sysconfig/clock", +); + + + + +#============================================================= + +=head2 get_timezone_prefix + +=head3 OUTPUT + +timezone_prefix: directory in which time zone files are + +=head3 DESCRIPTION + +Return the timezone directory (defualt: /usr/share/zoneinfo) + +=cut + +#============================================================= + +sub get_timezone_prefix() { + my $self = shift; + + return $self->timezone_prefix; +} + +#============================================================= + +=head2 getTimeZones + +=head3 INPUT + + $from_system: if present and its value is not 0 checks into timezone_prefix + directory and gets the list from there + +=head3 OUTPUT + + @l: ARRAY containing sorted time zones + +=head3 DESCRIPTION + + This method returns the available timezones + +=cut + +#============================================================= +sub getTimeZones { + my ($self, $from_system) = @_; + + if ($from_system and $from_system != 0) { + require MDK::Common::DataStructure; + require MDK::Common::Various; + my $tz_prefix = $self->get_timezone_prefix(); + open(my $F, "cd $tz_prefix && find [A-Z]* -noleaf -type f |"); + my @l = MDK::Common::DataStructure::difference2([ MDK::Common::Various::chomp_(<$F>) ], [ 'ROC', 'PRC' ]); + close $F or die "cannot list the available zoneinfos"; + return sort @l; + } + + return DateTime::TimeZone->all_names; +} + +#============================================================= + +=head2 readConfiguration + +=head3 OUTPUT + + hash containing: + UTC => HW clock is set as UTC + ZONE => Time Zone set + +=head3 DESCRIPTION + + This method returns the time zone system settings as hash + reference + +=cut + +#============================================================= +sub readConfiguration { + my $self = shift; + + my $prefs = {}; + + if (-e $self->clock_configuration_file) { + $prefs = Config::Auto::parse($self->clock_configuration_file); + } + + return $prefs; +} + +no Moose; +__PACKAGE__->meta->make_immutable; + + +1; + +######################## + +=comment + + + + +# TODO fix +our $ntp = "ntp"; +my $servername_config_suffix = ""; +# unless (-f $::prefix . "/etc/" . $ntp . ".conf") { +# $ntp = "chrony"; +# $servername_config_suffix = " iburst"; +# } + +sub ntp_server() { + find { $_ ne '127.127.1.0' } map { if_(/^\s*server\s+(\S*)/, $1) } cat_($::prefix . "/etc/" . $ntp . ".conf"); +} + +sub set_ntp_server { + my ($server) = @_; + my $f = $::prefix . "/etc/" . $ntp . ".conf"; + -f $f or return; + + my $pool_match = qr/\.pool\.ntp\.org$/; + my @servers = $server =~ $pool_match ? (map { "$_.$server" } 0 .. 2) : $server; + + my $added = 0; + substInFile { + if (/^#?\s*server\s+(\S*)/ && $1 ne '127.127.1.0') { + $_ = $added ? $_ =~ $pool_match ? undef : "#server $1\n" : join('', map { "server $_$servername_config_suffix\n" } @servers); + $added = 1; + } + } $f; + if ($ntp eq "ntp") { + output_p("$::prefix/etc/ntp/step-tickers", join('', map { "$_\n" } @servers)); + } + + require services; + services::set_status($ntp . 'd', to_bool($server), $::isInstall); +} + +sub write { + my ($t) = @_; + + set_ntp_server($t->{ntp}); + + my $tz_prefix = get_timezone_prefix(); + eval { cp_af($tz_prefix . '/' . $t->{timezone}, "$::prefix/etc/localtime") }; + $@ and log::l("installing /etc/localtime failed"); + setVarsInSh("$::prefix/etc/sysconfig/clock", { + ZONE => $t->{timezone}, + UTC => bool2text($t->{UTC}), + ARC => "false", + }); + + my $adjtime_file = $::prefix . '/etc/adjtime'; + my @adjtime = cat_($adjtime_file); + @adjtime or @adjtime = ("0.0 0 0.0\n", "0\n"); + $adjtime[2] = $t->{UTC} ? "UTC\n" : "LOCAL\n"; + output_p($adjtime_file, @adjtime); +} + +sub reload_sys_clock { + my ($t) = @_; + require run_program; + any::disable_x_screensaver(); + run_program::run('hwclock', '--hctosys', ($t->{UTC} ? '--utc' : '--localtime')); + any::enable_x_screensaver(); +} + +#- best guesses for a given country +my %c2t = ( +'AM' => 'Asia/Yerevan', +'AR' => 'America/Buenos_Aires', +'AT' => 'Europe/Vienna', +'AU' => 'Australia/Sydney', +'BA' => 'Europe/Sarajevo', +'BE' => 'Europe/Brussels', +'BG' => 'Europe/Sofia', +'BR' => 'America/Sao_Paulo', #- most brazilians live on this time zone +'BY' => 'Europe/Minsk', +'CA' => 'Canada/Eastern', +'CH' => 'Europe/Zurich', +'CN' => 'Asia/Beijing', +'CZ' => 'Europe/Prague', +'DE' => 'Europe/Berlin', +'DK' => 'Europe/Copenhagen', +'EE' => 'Europe/Tallinn', +'ES' => 'Europe/Madrid', +'FI' => 'Europe/Helsinki', +'FR' => 'Europe/Paris', +'GB' => 'Europe/London', +'GE' => 'Asia/Yerevan', +'GL' => 'Arctic/Longyearbyen', +'GR' => 'Europe/Athens', +'HR' => 'Europe/Zagreb', +'HU' => 'Europe/Budapest', +'ID' => 'Asia/Jakarta', +'IE' => 'Europe/Dublin', +'IL' => 'Asia/Tel_Aviv', +'IN' => 'Asia/Kolkata', +'IR' => 'Asia/Tehran', +'IS' => 'Atlantic/Reykjavik', +'IT' => 'Europe/Rome', +'JP' => 'Asia/Tokyo', +'KR' => 'Asia/Seoul', +'LT' => 'Europe/Vilnius', +'LV' => 'Europe/Riga', +'MK' => 'Europe/Skopje', +'MT' => 'Europe/Malta', +'MX' => 'America/Mexico_City', +'MY' => 'Asia/Kuala_Lumpur', +'NL' => 'Europe/Amsterdam', +'NO' => 'Europe/Oslo', +'NZ' => 'Pacific/Auckland', +'PL' => 'Europe/Warsaw', +'PT' => 'Europe/Lisbon', +'RO' => 'Europe/Bucharest', +'RU' => 'Europe/Moscow', +'SE' => 'Europe/Stockholm', +'SI' => 'Europe/Ljubljana', +'SK' => 'Europe/Bratislava', +'TH' => 'Asia/Bangkok', +'TJ' => 'Asia/Dushanbe', +'TR' => 'Europe/Istanbul', +'TW' => 'Asia/Taipei', +'UA' => 'Europe/Kiev', +'US' => 'America/New_York', +'UZ' => 'Asia/Tashkent', +'VN' => 'Asia/Saigon', +'YU' => 'Europe/Belgrade', +'ZA' => 'Africa/Johannesburg', +); + +sub fuzzyChoice { + my ($b, $count) = bestMatchSentence($_[0], keys %c2t); + $count ? $b : ''; +} +sub bestTimezone { $c2t{fuzzyChoice($_[0])} || 'GMT' } + +our %ntp_servers; + +sub get_ntp_server_tree { + my ($zone) = @_; + map { + $ntp_servers{$zone}{$_} => ( + exists $ntp_servers{$_} ? + $zone ? + translate($_) . "|" . N("All servers") : + N("All servers") : + translate($zone) . "|" . translate($_) + ), + get_ntp_server_tree($_); + } keys %{$ntp_servers{$zone}}; +} + +sub ntp_servers() { + # FIXME: missing parameter: + +{ get_ntp_server_tree() }; +} + +sub dump_ntp_zone { + my ($zone) = @_; + map { if_(/\[\d+\](.+) -- (.+\.ntp\.org)/, $1 => $2) } `lynx -dump http://www.pool.ntp.org/zone/$zone`; +} +sub print_ntp_zone { + my ($zone, $name) = @_; + # FIXME: missing parameter: + my %servers = dump_ntp_zone($zone); + print qq(\$ntp_servers{"$name"} = {\n); + print join('', map { qq( N_("$_") => "$servers{$_}",\n) } sort(keys %servers)); + print "};\n"; + \%servers; +} +sub print_ntp_servers() { + print_ntp_zone(); + my $servers = print_ntp_zone('@', "Global"); + foreach my $name (sort(keys %$servers)) { + my ($zone) = $servers->{$name} =~ /^(.*?)\./; + print_ntp_zone($zone, $name); + } +} + +# perl -Mtimezone -e 'timezone::print_ntp_servers()' +$ntp_servers{""} = { + N_("Global") => "pool.ntp.org", +}; +$ntp_servers{Global} = { + N_("Africa") => "africa.pool.ntp.org", + N_("Asia") => "asia.pool.ntp.org", + N_("Europe") => "europe.pool.ntp.org", + N_("North America") => "north-america.pool.ntp.org", + N_("Oceania") => "oceania.pool.ntp.org", + N_("South America") => "south-america.pool.ntp.org", +}; +$ntp_servers{Africa} = { + N_("South Africa") => "za.pool.ntp.org", + N_("Tanzania") => "tz.pool.ntp.org", +}; +$ntp_servers{Asia} = { + N_("Bangladesh") => "bd.pool.ntp.org", + N_("China") => "cn.pool.ntp.org", + N_("Hong Kong") => "hk.pool.ntp.org", + N_("India") => "in.pool.ntp.org", + N_("Indonesia") => "id.pool.ntp.org", + N_("Iran") => "ir.pool.ntp.org", + N_("Israel") => "il.pool.ntp.org", + N_("Japan") => "jp.pool.ntp.org", + N_("Korea") => "kr.pool.ntp.org", + N_("Malaysia") => "my.pool.ntp.org", + N_("Philippines") => "ph.pool.ntp.org", + N_("Singapore") => "sg.pool.ntp.org", + N_("Taiwan") => "tw.pool.ntp.org", + N_("Thailand") => "th.pool.ntp.org", + N_("Turkey") => "tr.pool.ntp.org", + N_("United Arab Emirates") => "ae.pool.ntp.org", +}; +$ntp_servers{Europe} = { + N_("Austria") => "at.pool.ntp.org", + N_("Belarus") => "by.pool.ntp.org", + N_("Belgium") => "be.pool.ntp.org", + N_("Bulgaria") => "bg.pool.ntp.org", + N_("Czech Republic") => "cz.pool.ntp.org", + N_("Denmark") => "dk.pool.ntp.org", + N_("Estonia") => "ee.pool.ntp.org", + N_("Finland") => "fi.pool.ntp.org", + N_("France") => "fr.pool.ntp.org", + N_("Germany") => "de.pool.ntp.org", + N_("Greece") => "gr.pool.ntp.org", + N_("Hungary") => "hu.pool.ntp.org", + N_("Ireland") => "ie.pool.ntp.org", + N_("Italy") => "it.pool.ntp.org", + N_("Lithuania") => "lt.pool.ntp.org", + N_("Luxembourg") => "lu.pool.ntp.org", + N_("Netherlands") => "nl.pool.ntp.org", + N_("Norway") => "no.pool.ntp.org", + N_("Poland") => "pl.pool.ntp.org", + N_("Portugal") => "pt.pool.ntp.org", + N_("Romania") => "ro.pool.ntp.org", + N_("Russian Federation") => "ru.pool.ntp.org", + N_("Slovakia") => "sk.pool.ntp.org", + N_("Slovenia") => "si.pool.ntp.org", + N_("Spain") => "es.pool.ntp.org", + N_("Sweden") => "se.pool.ntp.org", + N_("Switzerland") => "ch.pool.ntp.org", + N_("Ukraine") => "ua.pool.ntp.org", + N_("United Kingdom") => "uk.pool.ntp.org", + N_("Yugoslavia") => "yu.pool.ntp.org", +}; +$ntp_servers{"North America"} = { + N_("Canada") => "ca.pool.ntp.org", + N_("Guatemala") => "gt.pool.ntp.org", + N_("Mexico") => "mx.pool.ntp.org", + N_("United States") => "us.pool.ntp.org", +}; +$ntp_servers{Oceania} = { + N_("Australia") => "au.pool.ntp.org", + N_("New Zealand") => "nz.pool.ntp.org", +}; +$ntp_servers{"South America"} = { + N_("Argentina") => "ar.pool.ntp.org", + N_("Brazil") => "br.pool.ntp.org", + N_("Chile") => "cl.pool.ntp.org", +}; + +1; +=cut diff --git a/scripts/adminClock b/scripts/adminClock new file mode 100755 index 00000000..37aadb68 --- /dev/null +++ b/scripts/adminClock @@ -0,0 +1,9 @@ +#!/usr/bin/perl + +use AdminPanel::Module::Clock; + +my $clockManager = AdminPanel::Module::Clock->new(); + +$clockManager->start(); + +1; |