Unverified Commit c930b0a0 authored by Francesc Guasch's avatar Francesc Guasch Committed by GitHub
Browse files

Feature maintenance mode (#1331)

feature(fontend): maintenance mode

closes issue #1331
parent 1d0f07f3
......@@ -31,6 +31,7 @@ WriteMakefile(
,'Net::DNS' => 0
,'Net::SSH2' => 0
,'File::Rsync' => 0
,'DateTime::Format::DateParse'=> 0
},
BUILD_REQUIRES => {
'Test::Perl::Critic' => 0
......
......@@ -4,7 +4,7 @@ Architecture: all
Section: utils
Priority: optional
Maintainer: Francesc Guasch <frankie@telecos.upc.edu>
Depends: perl (>=5.18),libmojolicious-perl,mysql-common,libauthen-passphrase-perl, libdatetime-perl, libdbd-mysql-perl,libdbi-perl,libdbix-connector-perl,libipc-run3-perl,libio-stringy-perl,libnet-ldap-perl,libproc-pid-file-perl,libvirt-daemon-system,libsys-virt-perl,libxml-libxml-perl,libconfig-yaml-perl,libmoose-perl,libjson-xs-perl,qemu-utils,perlmagick,libmoosex-types-netaddr-ip-perl,libio-interface-perl,libiptables-chainmgr-perl,libnet-dns-perl,wget,liblocale-maketext-lexicon-perl,libmojolicious-plugin-i18n-perl,libdbd-sqlite3-perl, debconf (>= 0.2.26), adduser, libdigest-sha-perl, qemu-kvm, net-tools, libfile-rsync-perl, libnet-ssh2-perl, bridge-utils, libencode-locale-perl, libpbkdf2-tiny-perl
Depends: perl (>=5.18),libmojolicious-perl,mysql-common,libauthen-passphrase-perl, libdatetime-perl, libdbd-mysql-perl,libdbi-perl,libdbix-connector-perl,libipc-run3-perl,libio-stringy-perl,libnet-ldap-perl,libproc-pid-file-perl,libvirt-daemon-system,libsys-virt-perl,libxml-libxml-perl,libconfig-yaml-perl,libmoose-perl,libjson-xs-perl,qemu-utils,perlmagick,libmoosex-types-netaddr-ip-perl,libio-interface-perl,libiptables-chainmgr-perl,libnet-dns-perl,wget,liblocale-maketext-lexicon-perl,libmojolicious-plugin-i18n-perl,libdbd-sqlite3-perl, debconf (>= 0.2.26), adduser, libdigest-sha-perl, qemu-kvm, net-tools, libfile-rsync-perl, libnet-ssh2-perl, bridge-utils, libencode-locale-perl, libpbkdf2-tiny-perl, libdatetime-format-dateparse-perl
Description: Remote Virtual Desktops Manager
Ravada is a software that allows the user to connect to a
remote virtual desktop.
......@@ -4,7 +4,7 @@ Architecture: all
Section: utils
Priority: optional
Maintainer: Francesc Guasch <frankie@telecos.upc.edu>
Depends: perl (>=5.18),libmojolicious-perl,mysql-common,libauthen-passphrase-perl, libdatetime-perl, libdbd-mysql-perl,libdbi-perl,libdbix-connector-perl,libipc-run3-perl,libio-stringy-perl,libnet-ldap-perl,libproc-pid-file-perl,libvirt-bin,libvirt-daemon-system,libsys-virt-perl,libxml-libxml-perl,libconfig-yaml-perl,libmoose-perl,libjson-xs-perl,qemu-utils,perlmagick,libmoosex-types-netaddr-ip-perl,libio-interface-perl,libiptables-chainmgr-perl,libnet-dns-perl,wget,liblocale-maketext-lexicon-perl,libmojolicious-plugin-i18n-perl,libdbd-sqlite3-perl, debconf (>= 0.2.26), adduser, libdigest-sha-perl, qemu-kvm, net-tools, libfile-rsync-perl, libnet-ssh2-perl, bridge-utils, libencode-locale-perl, libpbkdf2-tiny-perl
Depends: perl (>=5.18),libmojolicious-perl,mysql-common,libauthen-passphrase-perl, libdatetime-perl, libdbd-mysql-perl,libdbi-perl,libdbix-connector-perl,libipc-run3-perl,libio-stringy-perl,libnet-ldap-perl,libproc-pid-file-perl,libvirt-bin,libvirt-daemon-system,libsys-virt-perl,libxml-libxml-perl,libconfig-yaml-perl,libmoose-perl,libjson-xs-perl,qemu-utils,perlmagick,libmoosex-types-netaddr-ip-perl,libio-interface-perl,libiptables-chainmgr-perl,libnet-dns-perl,wget,liblocale-maketext-lexicon-perl,libmojolicious-plugin-i18n-perl,libdbd-sqlite3-perl, debconf (>= 0.2.26), adduser, libdigest-sha-perl, qemu-kvm, net-tools, libfile-rsync-perl, libnet-ssh2-perl, bridge-utils, libencode-locale-perl, libpbkdf2-tiny-perl, libdatetime-format-dateparse-perl
Description: Remote Virtual Desktops Manager
Ravada is a software that allows the user to connect to a
remote virtual desktop.
......@@ -4,7 +4,7 @@ Architecture: all
Section: utils
Priority: optional
Maintainer: Francesc Guasch <frankie@telecos.upc.edu>
Depends: perl (>=5.18),libmojolicious-perl,mysql-common,libauthen-passphrase-perl, libdatetime-perl, libdbd-mysql-perl,libdbi-perl,libdbix-connector-perl,libipc-run3-perl,libio-stringy-perl,libnet-ldap-perl,libproc-pid-file-perl,libvirt-daemon-system,libsys-virt-perl,libxml-libxml-perl,libconfig-yaml-perl,libmoose-perl,libjson-xs-perl,qemu-utils,perlmagick,libmoosex-types-netaddr-ip-perl,libio-interface-perl,libiptables-chainmgr-perl,libnet-dns-perl,wget,liblocale-maketext-lexicon-perl,libmojolicious-plugin-i18n-perl,libdbd-sqlite3-perl, debconf (>= 0.2.26), adduser, libdigest-sha-perl, qemu-kvm, net-tools, libfile-rsync-perl, libnet-ssh2-perl, bridge-utils, libpbkdf2-tiny-perl
Depends: perl (>=5.18),libmojolicious-perl,mysql-common,libauthen-passphrase-perl, libdatetime-perl, libdbd-mysql-perl,libdbi-perl,libdbix-connector-perl,libipc-run3-perl,libio-stringy-perl,libnet-ldap-perl,libproc-pid-file-perl,libvirt-daemon-system,libsys-virt-perl,libxml-libxml-perl,libconfig-yaml-perl,libmoose-perl,libjson-xs-perl,qemu-utils,perlmagick,libmoosex-types-netaddr-ip-perl,libio-interface-perl,libiptables-chainmgr-perl,libnet-dns-perl,wget,liblocale-maketext-lexicon-perl,libmojolicious-plugin-i18n-perl,libdbd-sqlite3-perl, debconf (>= 0.2.26), adduser, libdigest-sha-perl, qemu-kvm, net-tools, libfile-rsync-perl, libnet-ssh2-perl, bridge-utils, libpbkdf2-tiny-perl, libdatetime-format-dateparse-perl
Description: Remote Virtual Desktops Manager
Ravada is a software that allows the user to connect to a
remote virtual desktop.
......@@ -4,7 +4,7 @@ Architecture: all
Section: utils
Priority: optional
Maintainer: Francesc Guasch <frankie@telecos.upc.edu>
Depends: perl (>=5.18),libmojolicious-perl,mysql-common,libauthen-passphrase-perl, libdatetime-perl, libdbd-mysql-perl,libdbi-perl,libdbix-connector-perl,libipc-run3-perl,libio-stringy-perl,libnet-ldap-perl,libproc-pid-file-perl,libvirt-daemon-system,libsys-virt-perl,libxml-libxml-perl,libconfig-yaml-perl,libmoose-perl,libjson-xs-perl,qemu-utils,perlmagick,libmoosex-types-netaddr-ip-perl,libio-interface-perl,libiptables-chainmgr-perl,libnet-dns-perl,wget,liblocale-maketext-lexicon-perl,libmojolicious-plugin-i18n-perl,libdbd-sqlite3-perl, debconf (>= 0.2.26), adduser, libdigest-sha-perl, qemu-kvm, net-tools, libfile-rsync-perl, libnet-ssh2-perl, bridge-utils, libpbkdf2-tiny-perl
Depends: perl (>=5.18),libmojolicious-perl,mysql-common,libauthen-passphrase-perl, libdatetime-perl, libdbd-mysql-perl,libdbi-perl,libdbix-connector-perl,libipc-run3-perl,libio-stringy-perl,libnet-ldap-perl,libproc-pid-file-perl,libvirt-daemon-system,libsys-virt-perl,libxml-libxml-perl,libconfig-yaml-perl,libmoose-perl,libjson-xs-perl,qemu-utils,perlmagick,libmoosex-types-netaddr-ip-perl,libio-interface-perl,libiptables-chainmgr-perl,libnet-dns-perl,wget,liblocale-maketext-lexicon-perl,libmojolicious-plugin-i18n-perl,libdbd-sqlite3-perl, debconf (>= 0.2.26), adduser, libdigest-sha-perl, qemu-kvm, net-tools, libfile-rsync-perl, libnet-ssh2-perl, bridge-utils, libpbkdf2-tiny-perl, libdatetime-format-dateparse-perl
Description: Remote Virtual Desktops Manager
Ravada is a software that allows the user to connect to a
remote virtual desktop.
......@@ -138,11 +138,13 @@ sub BUILD {
}
sub _install($self) {
$self->_sql_create_tables();
$self->_create_tables();
$self->_upgrade_tables();
$self->_upgrade_timestamps();
$self->_update_data();
$self->_init_user_daemon();
$self->_sql_insert_defaults();
}
sub _init_user_daemon {
......@@ -897,6 +899,9 @@ sub _add_indexes_generic($self) {
"index(id_request,date_send)"
,"index(date_changed)"
]
,settings => [
"index(id_parent,name)"
]
);
for my $table ( keys %index ) {
my $known = $self->_get_indexes($table);
......@@ -906,7 +911,7 @@ sub _add_indexes_generic($self) {
$name =~ s/,/_/g;
next if $known->{$name};
my $sql = "ALTER TABLE $table add $type $name ($fields)";
warn "INFO: Adding index to vms: $name";
warn "INFO: Adding index to $table: $name";
my $sth = $CONNECTOR->dbh->prepare($sql);
$sth->execute();
}
......@@ -1286,6 +1291,124 @@ sub _create_tables {
closedir $ls;
}
sub _sql_create_tables($self) {
my $driver = lc($CONNECTOR->dbh->{Driver}{Name});
my %tables = (
settings => {
id => 'integer NOT NULL PRIMARY KEY AUTO_INCREMENT'
, id_parent => 'INT NOT NULL'
, name => 'varchar(64) NOT NULL'
, value => 'varchar(128) DEFAULT NULL'
}
);
for my $table ( keys %tables ) {
my $sth = $CONNECTOR->dbh->table_info('%',undef,$table,'TABLE');
my $info = $sth->fetchrow_hashref();
$sth->finish;
next if keys %$info;
warn "INFO: creating table $table\n" if $0 !~ /\.t$/;
my $sql_fields;
for my $field (sort keys %{$tables{$table}} ) {
my $definition = _port_definition($driver, $tables{$table}->{$field});
$sql_fields .= ", " if $sql_fields;
$sql_fields .= " $field $definition";
}
my $sql = "CREATE TABLE $table ( $sql_fields )";
$CONNECTOR->dbh->do($sql);
}
}
sub _sql_insert_defaults($self){
my $cont = 1;
require Mojolicious::Plugin::Config;
my $plugin = Mojolicious::Plugin::Config->new();
my $conf = $plugin->load("/etc/rvd_front.conf");
my %values = (
settings => [
{
id => $cont++
,id_parent => 0
,name => 'frontend'
}
,{ id => $cont++
,id_parent => 1
,name => 'fallback'
,value => $conf->{fallback}
}
,{ id => $cont++
,id_parent => 1
,name => 'maintenance'
,value => 0
}
,{ id => $cont++
,id_parent => 1
,name => 'maintenance_start'
,value => ''
}
,{ id => $cont++
,id_parent => 1
,name => 'maintenance_end'
,value => ''
}
,{ id => $cont++
,id_parent => 1
,name => 'session_timeout'
,value => $conf->{session_timeout}
}
,{ id => $cont++
,id_parent => 1
,name => 'session_timeout_admin'
,value => $conf->{session_timeout_admin}
}
,{ id => $cont++
,id_parent => 1
,name => 'auto_view'
,value => $conf->{auto_view}
}
]
);
for my $table (sort keys %values) {
my $sth = $CONNECTOR->dbh->prepare("SELECT id FROM $table "
." WHERE id = ? "
);
for my $entry (@{$values{$table}}) {
$sth->execute($entry->{id});
my ($found) = $sth->fetchrow;
next if $found;
warn "INFO adding default $table ".Dumper($entry) if $0 !~ /t$/;
$self->_sql_insert_values($table, $entry);
}
}
}
sub _sql_insert_values($self, $table, $entry) {
my $sql = "INSERT INTO $table "
."( "
.join(" , ",sort keys %$entry)
.") "
." VALUES ( "
.join(" , ", map { '? ' } keys %$entry)
." ) ";
my $sth = $CONNECTOR->dbh->prepare($sql);
$sth->execute(map { $entry->{$_} } sort keys %$entry);
}
sub _port_definition($driver, $definition0){
return $definition0 if $driver eq 'mysql';
if ($driver eq 'sqlite') {
$definition0 =~ s/(.*) AUTO_INCREMENT$/$1 AUTOINCREMENT/i;
return $definition0 if $definition0 =~ /^(int|integer|char|varchar) /i;
}
}
sub _clean_iso_mini {
my $sth = $CONNECTOR->dbh->prepare("DELETE FROM iso_images WHERE device like ?");
$sth->execute('%/mini.iso');
......
......@@ -11,6 +11,7 @@ Ravada::Front - Web Frontend library for Ravada
use Carp qw(carp);
use DateTime;
use DateTime::Format::DateParse;
use Hash::Util qw(lock_hash);
use IPC::Run3 qw(run3);
use JSON::XS;
......@@ -1204,6 +1205,51 @@ sub _dbh {
return $CONNECTOR->dbh;
}
sub _get_settings($self, $id_parent=0) {
my $sth = $CONNECTOR->dbh->prepare(
"SELECT id,name,value"
." FROM settings "
." WHERE id_parent= ? "
);
$sth->execute($id_parent);
my $ret;
while ( my ( $id, $name, $value) = $sth->fetchrow) {
$value = 0+$value if defined $value && $value =~ /^\d+$/;
my $setting_sons = $self->_get_settings($id);
if ($setting_sons) {
$ret->{$name} = $setting_sons;
} else {
$ret->{$name} = { id => $id, value => $value};
}
}
return $ret;
}
sub settings_global($self) {
return $self->_get_settings();
}
sub is_in_maintenance($self) {
my $settings = $self->settings_global();
return 0 if ! $settings->{frontend}->{maintenance}->{value};
my $start = DateTime::Format::DateParse->parse_datetime(
$settings->{frontend}->{maintenance_start}->{value});
my $end= DateTime::Format::DateParse->parse_datetime(
$settings->{frontend}->{maintenance_end}->{value});
my $now = DateTime->now();
if ( $now >= $start && $now <= $end ) {
return 1;
}
my $sth = $self->_dbh->prepare("UPDATE settings set value = 0 "
." WHERE id=? "
);
$sth->execute($settings->{frontend}->{maintenance}->{id});
return 0;
}
=head2 version
Returns the version of the main module
......
......@@ -8,6 +8,7 @@ ravadaApp.directive("solShowMachine", swMach)
.controller("messagesPage", messagesPageC)
.controller("manage_nodes",manage_nodes)
.controller("new_node", newNodeCtrl)
.controller("settings_global", settings_global_ctrl)
;
ravadaApp.filter('orderObjectBy', function() {
......@@ -510,4 +511,41 @@ ravadaApp.directive("solShowMachine", swMach)
});
};
};
function settings_global_ctrl($scope, $http) {
$scope.init = function() {
$http.get('/settings_global.json').then(function(response) {
$scope.settings = response.data;
var now = new Date();
if ($scope.settings.frontend.maintenance.value == 0 ) {
console.log("default");
$scope.settings.frontend.maintenance_start.value
= new Date(now.getFullYear(), now.getMonth(), now.getDate()
, now.getHours(), now.getMinutes());
$scope.settings.frontend.maintenance_end.value
= new Date(now.getFullYear(), now.getMonth(), now.getDate()
, now.getHours(), now.getMinutes() + 15);
} else {
$scope.settings.frontend.maintenance_start.value
=new Date($scope.settings.frontend.maintenance_start.value);
$scope.settings.frontend.maintenance_end.value
=new Date($scope.settings.frontend.maintenance_end.value);
}
});
};
$scope.load_settings = function() {
$scope.init();
$scope.formSettings.$setPristine();
};
$scope.update_settings = function() {
$scope.formSettings.$setPristine();
console.log($scope.settings);
$http.post('/settings_global'
,JSON.stringify($scope.settings)
).then(function(response) {
});
};
};
}());
......@@ -27,6 +27,7 @@
.controller("users", usersCrtl)
.controller("bases", mainpageCrtl)
.controller("singleMachinePage", singleMachinePageC)
.controller("maintenance",maintenanceCtrl)
.controller("notifCrtl", notifCrtl)
.controller("run_domain",run_domain_ctrl)
.controller("run_domain_req",run_domain_req_ctrl)
......@@ -976,6 +977,12 @@
};
function maintenanceCtrl($scope, $interval, $http, request){
$scope.init = function(end) {
$scope.maintenance_end = new Date(end);
};
};
/*
function requestsCrtlSingle($scope, $interval, $http, request){
$scope.getReqs= function() {
......
......@@ -99,7 +99,6 @@ setlocale(LC_CTYPE, $old_locale);
#####
#####
plugin I18N => {namespace => 'Ravada::I18N', default => 'en'};
plugin 'RenderFile';
my %config;
%config = (config => $CONFIG_FRONT->{config}) if $CONFIG_FRONT->{config};
......@@ -147,12 +146,20 @@ hook before_routes => sub {
,host => $host
);
return if _logged_in($c);
return if $url =~ m{^/(anonymous|login|logout|requirements|robots.txt)}
|| $url =~ m{^/(css|font|img|js)}
$USER = _logged_in($c);
return if $url =~ m{^/(css|font|img|js)}
|| $url =~ m{^/fallback/.*\.(css|js|map)$};
# anonymous URLs
return if $url =~ m{^/(login|logout|requirements|robots.txt)};
if ( $RAVADA->is_in_maintenance ) {
return login($c) if !$USER && $url =~ m{^/$};
return maintenance($c) if !$USER || !$USER->is_operator;
}
return if $url =~ m{^/anonymous};
if (($url =~ m{^/machine/(clone|display|info|view)/}
|| $url =~ m{^/(list_bases_anonymous|request/)}i
|| $url =~ m{^/ws/subscribe}
......@@ -244,6 +251,26 @@ get '/anonymous/(#base_id).html' => sub {
return quick_start_domain($c,$base->id, $USER->name);
};
get '/settings_global.json' => sub($c) {
$RAVADA->is_in_maintenance();
return $c->render(json => $RAVADA->settings_global );
};
post '/settings_global' => sub($c) {
my $arg = decode_json($c->req->body);
my $orig_settings;
my $sth = $RAVADA->_dbh->prepare("SELECT id,value FROM settings");
$sth->execute();
while (my ($id, $value) = $sth->fetchrow) {
$orig_settings->{$id} = $value;
}
_update_settings($arg, $orig_settings);
return $c->render(json => { ok => 1 });
};
any '/admin/#type' => sub {
my $c = shift;
......@@ -1375,6 +1402,7 @@ get '/iso/download/(#id).json' => sub {
websocket '/ws/subscribe' => sub {
my $c = shift;
my $expiration = $SESSION_TIMEOUT;
return if !$USER;
$expiration = $SESSION_TIMEOUT_ADMIN if $USER->is_admin;
$c->inactivity_timeout( $expiration );
$c->on(message => sub {
......@@ -1493,7 +1521,13 @@ sub login {
);
$auth_ok = Ravada::Auth::SQL->new(name => $auth_ok->name);
if ( $RAVADA->is_in_maintenance() ) {
return maintenance($c) unless $auth_ok->is_operator;
$auth_ok->send_message('Warning: Server under maintenance. <a href="/admin/settings">Settings</a>');
}
my $machines = $RAVADA->list_machines_user($auth_ok);
$url = "/machine/clone/". $machines->[0]->{id}.".html" if scalar(@$machines) == 1 && !($auth_ok->is_admin);
my $auto_view = 1;
......@@ -1650,6 +1684,11 @@ sub admin {
if ($page eq 'nodes') {
Ravada::Request->refresh_vms();
}
if ($page eq 'settings') {
my $url = $c->req->url->to_abs->path;
my $host = $c->req->url->to_abs->host;
$c->stash(url_login => "/login");
}
$c->render( template => 'main/admin_'.$page);
};
......@@ -2462,6 +2501,41 @@ sub _new_anonymous_user {
return $name;
}
sub _update_settings($arg, $orig_settings) {
for my $field (keys %$arg) {
if ( !exists $arg->{$field}->{id} ) {
_update_settings($arg->{$field}, $orig_settings);
next;
}
confess "Error: invalid field $field" if $field !~ /^\w+$/;
my ( $value, $id )
= ($arg->{$field}->{value}
, $arg->{$field}->{id}
);
next if $orig_settings->{$id} eq $value;
my $sth = $RAVADA->_dbh->prepare(
"UPDATE settings set value=?"
." WHERE id=? "
);
$sth->execute($value, $id);
$USER->send_message("Setting $field to $value");
}
};
sub maintenance($c) {
my $maintenance_end = $RAVADA->settings_global->{frontend}->{maintenance_end}
->{value};
my $localtime_end = DateTime::Format::DateParse->parse_datetime($maintenance_end
);
# return $c->render(text => "Maintenance until ".$localtime_end->strftime('%Y-%m-%d %H:%M [%Z]'));
return $c->render(
maintenance_end => $maintenance_end
,template => '/main/maintenance'
);
}
my $routes = app->routes->children;
for my $route (@$routes){
$route->pattern->quote_start('(');
......
......@@ -32,6 +32,9 @@ navbar-dark bg-dark fixed-top navbar-expand-lg navbar-inverse">
<a class="dropdown-item" href="/admin/messages"><i class="fa fa-envelope" aria-hidden="true"></i>&nbsp;<%=l 'messages' %></a>
% if ($monitoring) {
<a class="dropdown-item" href="/admin/monitoring"><i class="fas fa-tachometer-alt" aria-hidden="true"></i>&nbsp<%=l 'monitoring' %></a>
% }
% if ($_user->is_admin) {
<a class="dropdown-item" href="/admin/settings"><i class="fas fa-tachometer-alt" aria-hidden="true"></i>&nbsp<%=l 'settings' %></a>
% }
</div>
</li>
......
<!DOCTYPE html>
<html ng-app="ravada.app">
%= include 'bootstrap/header'
<body id="page-top" data-spy="scroll" data-target="fixed-top" role="document">
<div id="wrapper">
%= include 'bootstrap/navigation'
<div id="page-wrapper"
ng-controller="settings_global"
ng-init="init('<%= url_for('ws_subscribe')->to_abs %>')"
>
<div class="page-header">
<div class="card">
<div class="card-header">
<h2>Global Settings</h2>
</div>
</div>
</div>
<div>
<form name="formSettings">
<div class="row" ng-hide="true">
<div class="col-md-2">
<label for="falback">Fallback</label>
</div>
<div class="col-md-2">
<input name="fallback" ng-model="settings.frontend.fallback.value" type="checkbox"
ng-true-value="1" ng-false-value="0"
>
</div>
</div>
<div class="row" ng-hide="true">
<div class="col-md-2">
<label for="session timeout">Session timeout</label>
</div>
<div class="col-md-2">
<input name="fallback" ng-model="settings.frontend.session_timeout.value"
type="text">
</div>
</div>
<div class="row">
<div class="col-md-2">
<label for="maintenance">Maintenance</label>
</div>
<div class="col-md-2">
<input name="maintenance" ng-model="settings.frontend.maintenance.value"
ng-true-value="1" ng-false-value="0"
type="checkbox">
</div>
<div>
<%= l 'Admin users can still log in from' %>
<a href="<%= $url_login %>"><%= $url_login %></a>
</div>
</div>
<div class="row" ng-show="settings.frontend.maintenance.value == 1">
<div class="col-md-2">
<label for="maintenance_start">Maintenance Start</label>
</div>
<div class="col-md-4">
<input name="maintenance_start" ng-model="settings.frontend.maintenance_start.value"
type="datetime-local">
</div>
</div>
<div class="row" ng-show="settings.frontend.maintenance.value == 1">
<div class="col-md-2">
<label for="maintenance_end">Maintenance End</label>
</div>
<div class="col-md-4">
<input name="maintenance_end" ng-model="settings.frontend.maintenance_end.value"
type="datetime-local">
</div>
</div>
<div class="row">
<div class="col-md-2">
<button ng-click="update_settings()"
ng-disabled="!formSettings.$valid || formSettings.$pristine">
save
</button>
<button ng-click="load_settings()"
ng-disabled="formSettings.$pristine">cancel</button>
</div>
</div>
</form>
</div>
</div>
</div>
%= include 'bootstrap/scripts'
%= include 'bootstrap/messages'
</body>
</html>
<!DOCTYPE html>
<html ng-app="ravada.app">
<body>
<div id="wrapper">
<div ng-controller="maintenance"
ng-init="init('<%= $maintenance_end %>')"
>
<div ng-cloak="1">
<%= l 'Sorry'%>.
<%= l 'Server in maintenance until' %> {{maintenance_end | date:'medium'}}
</div>
</div>
%= include 'bootstrap/scripts'
</div><!-- wrapper -->
</body>
</html>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment