From dbc0d9614421e064cc62aacf116ebb783c83f2f3 Mon Sep 17 00:00:00 2001 From: Jonathan Bauer Date: Fri, 1 Apr 2016 16:50:13 +0200 Subject: [merge] merging c3sl / fr - initial commit --- modules/adduser.inc.php | 60 - modules/adduser/config.json | 4 + modules/adduser/module.inc.php | 60 + modules/adduser/templates/page-adduser.html | 28 + modules/backup.inc.php | 134 - modules/backup/config.json | 4 + modules/backup/module.inc.php | 164 + modules/backup/templates/_page.html | 41 + modules/backup/templates/restore.html | 62 + modules/baseconfig.inc.php | 127 - modules/baseconfig/config.json | 4 + modules/baseconfig/module.inc.php | 127 + modules/baseconfig/templates/_page.html | 186 + modules/citymanagement/config.json | 5 + modules/citymanagement/module.inc.php | 81 + .../templates/page-citymanagement.html | 77 + modules/dozmod.inc.php | 252 - modules/dozmod/config.json | 4 + modules/dozmod/module.inc.php | 252 + modules/dozmod/templates/images-delete.html | 57 + modules/dozmod/templates/mailconfig.html | 91 + modules/dozmod/templates/orglist.html | 51 + modules/dozmod/templates/userlist.html | 62 + modules/eventlog.inc.php | 70 - modules/eventlog/config.json | 4 + modules/eventlog/module.inc.php | 70 + modules/eventlog/templates/_page.html | 41 + modules/imgmanagement/config.json | 5 + modules/imgmanagement/module.inc.php | 76 + .../templates/page-imgmanagement.html | 46 + modules/internetaccess.inc.php | 51 - modules/internetaccess/config.json | 4 + modules/internetaccess/module.inc.php | 51 + modules/internetaccess/templates/_page.html | 40 + modules/internetaccess/templates/restart.html | 22 + modules/locations.inc.php | 348 - modules/locations/config.json | 4 + modules/locations/module.inc.php | 348 + modules/locations/templates/location-subnets.html | 73 + modules/locations/templates/locations.html | 96 + modules/locations/templates/subnets.html | 35 + modules/main.inc.php | 54 - modules/main/config.json | 4 + modules/main/module.inc.php | 64 + modules/main/templates/dialog-generic.html | 13 + modules/main/templates/footer.html | 2 + modules/main/templates/main-menu.html | 51 + modules/main/templates/main-menu.html.topnavbar | 92 + modules/main/templates/messagebox-error.html | 1 + modules/main/templates/messagebox-info.html | 1 + modules/main/templates/messagebox-success.html | 1 + modules/main/templates/messagebox-warning.html | 1 + modules/main/templates/page-login.html | 11 + modules/main/templates/page-main-guest.html | 15 + modules/main/templates/page-main.html | 36 + modules/main/templates/page-minilinux.html | 14 + modules/main/templates/page-news.html | 57 + modules/main/templates/page-syslog.html | 58 + modules/main/templates/page-vmstore.html | 111 + modules/main/templates/pagenav.html | 16 + modules/main/templates/tm-callback-trigger.html | 15 + modules/minilinux.inc.php | 128 - modules/minilinux/config.json | 4 + modules/minilinux/module.inc.php | 128 + modules/minilinux/templates/download.html | 1 + modules/minilinux/templates/filelist.html | 77 + modules/minilinux/templates/page-minilinux.html | 28 + modules/news.inc.php | 167 - modules/news/config.json | 4 + modules/news/module.inc.php | 167 + modules/news/templates/page-news.html | 57 + modules/serversetup.inc.php | 141 - modules/serversetup/config.json | 4 + modules/serversetup/module.inc.php | 186 + modules/serversetup/templates/ipaddress.html | 34 + modules/serversetup/templates/ipxe.html | 149 + modules/serversetup/templates/ipxe_update.html | 20 + modules/session.inc.php | 38 - modules/session/config.json | 4 + modules/session/module.inc.php | 36 + modules/session/templates/page-login.html | 11 + modules/statistics.inc.php | 785 - modules/statistics/config.json | 5 + modules/statistics/module.inc.php | 792 + modules/statistics/templates/clientlist.html | 45 + modules/statistics/templates/cpumodels.html | 51 + modules/statistics/templates/id44.html | 48 + modules/statistics/templates/kvmstate.html | 47 + modules/statistics/templates/machine-hdds.html | 67 + modules/statistics/templates/machine-main.html | 124 + modules/statistics/templates/machine-notes.html | 17 + modules/statistics/templates/machine-usage.html | 51 + modules/statistics/templates/memory.html | 47 + modules/statistics/templates/newclients.html | 44 + modules/statistics/templates/summary.html | 33 + modules/statistics/templates/syslog.html | 43 + modules/support/config.json | 4 + modules/support/module.inc.php | 77 + modules/support/templates/go-pear.phar | 99492 +++++++++++++++++++ modules/support/templates/page-faq.html | 58 + modules/support/templates/page-support.html | 67 + modules/sysconfig.inc.php | 391 - modules/sysconfig/addconfig.inc.php | 4 +- modules/sysconfig/addmodule.inc.php | 31 +- modules/sysconfig/addmodule_adauth.inc.php | 108 +- modules/sysconfig/addmodule_branding.inc.php | 4 +- modules/sysconfig/addmodule_custommodule.inc.php | 4 +- modules/sysconfig/addmodule_ldapauth.inc.php | 92 +- modules/sysconfig/addmodule_sshconfig.inc.php | 2 +- modules/sysconfig/config.json | 4 + modules/sysconfig/module.inc.php | 396 + modules/sysconfig/templates/_page.html | 227 + modules/sysconfig/templates/ad-finish.html | 29 + modules/sysconfig/templates/ad-selfsearch.html | 112 + modules/sysconfig/templates/ad-start.html | 121 + .../templates/ad_ldap-checkconnection.html | 91 + .../templates/ad_ldap-checkcredentials.html | 67 + modules/sysconfig/templates/branding-check.html | 26 + modules/sysconfig/templates/branding-start.html | 25 + modules/sysconfig/templates/cfg-finish.html | 12 + modules/sysconfig/templates/cfg-start.html | 39 + .../sysconfig/templates/config-module-list.html | 17 + modules/sysconfig/templates/custom-filelist.html | 16 + modules/sysconfig/templates/custom-fileselect.html | 31 + modules/sysconfig/templates/custom-upload.html | 18 + modules/sysconfig/templates/ldap-finish.html | 29 + modules/sysconfig/templates/ldap-start.html | 101 + modules/sysconfig/templates/sshconfig-start.html | 27 + modules/sysconfig/templates/start.html | 12 + modules/sysconfignew/config.json | 4 + modules/sysconfignew/module.inc.php | 113 + modules/sysconfignew/templates/_pagenew.html | 190 + modules/sysconfignew/templates/module-editor.html | 269 + modules/syslog.inc.php | 94 - modules/syslog/config.json | 4 + modules/syslog/module.inc.php | 94 + modules/syslog/templates/page-syslog.html | 58 + modules/systemstatus.inc.php | 361 - modules/systemstatus/config.json | 4 + modules/systemstatus/module.inc.php | 361 + modules/systemstatus/templates/_page.html | 129 + modules/systemstatus/templates/addresses.html | 8 + modules/systemstatus/templates/diskstat.html | 63 + modules/systemstatus/templates/services.html | 6 + modules/systemstatus/templates/systeminfo.html | 115 + modules/translation.inc.php | 491 - modules/translation/config.json | 4 + modules/translation/module.inc.php | 597 + modules/translation/templates/_page.html | 17 + modules/translation/templates/edit.html | 71 + modules/translation/templates/module-list.html | 32 + modules/translation/templates/template-list.html | 32 + modules/usermanagement/config.json | 5 + modules/usermanagement/module.inc.php | 109 + .../usermanagement/templates/user-management.html | 127 + modules/vmstore.inc.php | 63 - modules/vmstore/config.json | 4 + modules/vmstore/module.inc.php | 63 + modules/vmstore/templates/mount.html | 25 + modules/vmstore/templates/page-vmstore.html | 111 + modules/webinterface.inc.php | 85 - modules/webinterface/config.json | 4 + modules/webinterface/module.inc.php | 85 + modules/webinterface/templates/httpd-restart.html | 6 + modules/webinterface/templates/https.html | 60 + modules/webinterface/templates/passwords.html | 25 + 166 files changed, 109095 insertions(+), 4045 deletions(-) delete mode 100644 modules/adduser.inc.php create mode 100644 modules/adduser/config.json create mode 100644 modules/adduser/module.inc.php create mode 100644 modules/adduser/templates/page-adduser.html delete mode 100644 modules/backup.inc.php create mode 100644 modules/backup/config.json create mode 100644 modules/backup/module.inc.php create mode 100644 modules/backup/templates/_page.html create mode 100644 modules/backup/templates/restore.html delete mode 100644 modules/baseconfig.inc.php create mode 100644 modules/baseconfig/config.json create mode 100644 modules/baseconfig/module.inc.php create mode 100644 modules/baseconfig/templates/_page.html create mode 100644 modules/citymanagement/config.json create mode 100644 modules/citymanagement/module.inc.php create mode 100644 modules/citymanagement/templates/page-citymanagement.html delete mode 100644 modules/dozmod.inc.php create mode 100644 modules/dozmod/config.json create mode 100644 modules/dozmod/module.inc.php create mode 100644 modules/dozmod/templates/images-delete.html create mode 100644 modules/dozmod/templates/mailconfig.html create mode 100644 modules/dozmod/templates/orglist.html create mode 100644 modules/dozmod/templates/userlist.html delete mode 100644 modules/eventlog.inc.php create mode 100644 modules/eventlog/config.json create mode 100644 modules/eventlog/module.inc.php create mode 100644 modules/eventlog/templates/_page.html create mode 100644 modules/imgmanagement/config.json create mode 100644 modules/imgmanagement/module.inc.php create mode 100644 modules/imgmanagement/templates/page-imgmanagement.html delete mode 100644 modules/internetaccess.inc.php create mode 100644 modules/internetaccess/config.json create mode 100644 modules/internetaccess/module.inc.php create mode 100644 modules/internetaccess/templates/_page.html create mode 100644 modules/internetaccess/templates/restart.html delete mode 100644 modules/locations.inc.php create mode 100644 modules/locations/config.json create mode 100644 modules/locations/module.inc.php create mode 100644 modules/locations/templates/location-subnets.html create mode 100644 modules/locations/templates/locations.html create mode 100644 modules/locations/templates/subnets.html delete mode 100644 modules/main.inc.php create mode 100644 modules/main/config.json create mode 100644 modules/main/module.inc.php create mode 100644 modules/main/templates/dialog-generic.html create mode 100644 modules/main/templates/footer.html create mode 100644 modules/main/templates/main-menu.html create mode 100644 modules/main/templates/main-menu.html.topnavbar create mode 100644 modules/main/templates/messagebox-error.html create mode 100644 modules/main/templates/messagebox-info.html create mode 100644 modules/main/templates/messagebox-success.html create mode 100644 modules/main/templates/messagebox-warning.html create mode 100644 modules/main/templates/page-login.html create mode 100644 modules/main/templates/page-main-guest.html create mode 100644 modules/main/templates/page-main.html create mode 100644 modules/main/templates/page-minilinux.html create mode 100644 modules/main/templates/page-news.html create mode 100644 modules/main/templates/page-syslog.html create mode 100644 modules/main/templates/page-vmstore.html create mode 100644 modules/main/templates/pagenav.html create mode 100644 modules/main/templates/tm-callback-trigger.html delete mode 100644 modules/minilinux.inc.php create mode 100644 modules/minilinux/config.json create mode 100644 modules/minilinux/module.inc.php create mode 100644 modules/minilinux/templates/download.html create mode 100644 modules/minilinux/templates/filelist.html create mode 100644 modules/minilinux/templates/page-minilinux.html delete mode 100644 modules/news.inc.php create mode 100644 modules/news/config.json create mode 100644 modules/news/module.inc.php create mode 100644 modules/news/templates/page-news.html delete mode 100644 modules/serversetup.inc.php create mode 100644 modules/serversetup/config.json create mode 100644 modules/serversetup/module.inc.php create mode 100644 modules/serversetup/templates/ipaddress.html create mode 100644 modules/serversetup/templates/ipxe.html create mode 100644 modules/serversetup/templates/ipxe_update.html delete mode 100644 modules/session.inc.php create mode 100644 modules/session/config.json create mode 100644 modules/session/module.inc.php create mode 100644 modules/session/templates/page-login.html delete mode 100644 modules/statistics.inc.php create mode 100644 modules/statistics/config.json create mode 100644 modules/statistics/module.inc.php create mode 100644 modules/statistics/templates/clientlist.html create mode 100644 modules/statistics/templates/cpumodels.html create mode 100644 modules/statistics/templates/id44.html create mode 100644 modules/statistics/templates/kvmstate.html create mode 100644 modules/statistics/templates/machine-hdds.html create mode 100644 modules/statistics/templates/machine-main.html create mode 100644 modules/statistics/templates/machine-notes.html create mode 100644 modules/statistics/templates/machine-usage.html create mode 100644 modules/statistics/templates/memory.html create mode 100644 modules/statistics/templates/newclients.html create mode 100644 modules/statistics/templates/summary.html create mode 100644 modules/statistics/templates/syslog.html create mode 100644 modules/support/config.json create mode 100644 modules/support/module.inc.php create mode 100644 modules/support/templates/go-pear.phar create mode 100644 modules/support/templates/page-faq.html create mode 100644 modules/support/templates/page-support.html delete mode 100644 modules/sysconfig.inc.php create mode 100644 modules/sysconfig/config.json create mode 100644 modules/sysconfig/module.inc.php create mode 100644 modules/sysconfig/templates/_page.html create mode 100644 modules/sysconfig/templates/ad-finish.html create mode 100644 modules/sysconfig/templates/ad-selfsearch.html create mode 100644 modules/sysconfig/templates/ad-start.html create mode 100644 modules/sysconfig/templates/ad_ldap-checkconnection.html create mode 100644 modules/sysconfig/templates/ad_ldap-checkcredentials.html create mode 100644 modules/sysconfig/templates/branding-check.html create mode 100644 modules/sysconfig/templates/branding-start.html create mode 100644 modules/sysconfig/templates/cfg-finish.html create mode 100644 modules/sysconfig/templates/cfg-start.html create mode 100644 modules/sysconfig/templates/config-module-list.html create mode 100644 modules/sysconfig/templates/custom-filelist.html create mode 100644 modules/sysconfig/templates/custom-fileselect.html create mode 100644 modules/sysconfig/templates/custom-upload.html create mode 100644 modules/sysconfig/templates/ldap-finish.html create mode 100644 modules/sysconfig/templates/ldap-start.html create mode 100644 modules/sysconfig/templates/sshconfig-start.html create mode 100644 modules/sysconfig/templates/start.html create mode 100644 modules/sysconfignew/config.json create mode 100644 modules/sysconfignew/module.inc.php create mode 100644 modules/sysconfignew/templates/_pagenew.html create mode 100644 modules/sysconfignew/templates/module-editor.html delete mode 100644 modules/syslog.inc.php create mode 100644 modules/syslog/config.json create mode 100644 modules/syslog/module.inc.php create mode 100644 modules/syslog/templates/page-syslog.html delete mode 100644 modules/systemstatus.inc.php create mode 100644 modules/systemstatus/config.json create mode 100644 modules/systemstatus/module.inc.php create mode 100644 modules/systemstatus/templates/_page.html create mode 100644 modules/systemstatus/templates/addresses.html create mode 100644 modules/systemstatus/templates/diskstat.html create mode 100644 modules/systemstatus/templates/services.html create mode 100644 modules/systemstatus/templates/systeminfo.html delete mode 100644 modules/translation.inc.php create mode 100644 modules/translation/config.json create mode 100644 modules/translation/module.inc.php create mode 100644 modules/translation/templates/_page.html create mode 100644 modules/translation/templates/edit.html create mode 100644 modules/translation/templates/module-list.html create mode 100644 modules/translation/templates/template-list.html create mode 100644 modules/usermanagement/config.json create mode 100644 modules/usermanagement/module.inc.php create mode 100644 modules/usermanagement/templates/user-management.html delete mode 100644 modules/vmstore.inc.php create mode 100644 modules/vmstore/config.json create mode 100644 modules/vmstore/module.inc.php create mode 100644 modules/vmstore/templates/mount.html create mode 100644 modules/vmstore/templates/page-vmstore.html delete mode 100644 modules/webinterface.inc.php create mode 100644 modules/webinterface/config.json create mode 100644 modules/webinterface/module.inc.php create mode 100644 modules/webinterface/templates/httpd-restart.html create mode 100644 modules/webinterface/templates/https.html create mode 100644 modules/webinterface/templates/passwords.html (limited to 'modules') diff --git a/modules/adduser.inc.php b/modules/adduser.inc.php deleted file mode 100644 index c236cb6f..00000000 --- a/modules/adduser.inc.php +++ /dev/null @@ -1,60 +0,0 @@ - $_POST['user'], - 'pass' => Crypto::hash6($_POST['pass1']), - 'fullname' => $_POST['fullname'], - 'phone' => $_POST['phone'], - 'email' => $_POST['email'], - ); - if (Database::exec('INSERT INTO user SET login = :user, passwd = :pass, fullname = :fullname, phone = :phone, email = :email', $data) != 1) { - Util::traceError('Could not create new user in DB'); - } - // Make it superadmin if first user. This method sucks as it's a race condition but hey... - $ret = Database::queryFirst('SELECT Count(*) AS num FROM user'); - if ($ret !== false && $ret['num'] == 1) { - Database::exec('UPDATE user SET permissions = 1'); - EventLog::clear(); - EventLog::info('Created first user ' . $_POST['user']); - } else { - EventLog::info(User::getName() . ' created user ' . $_POST['user']); - } - Message::addInfo('adduser-success'); - Util::redirect('?do=Session&action=login'); - } - } - } - - protected function doRender() - { - // No user was added, check if current user is allowed to add a new user - // Currently you can only add users if there is no user yet. :) - if (!User::hasPermission('superadmin') && Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { - Message::addError('adduser-disabled'); - } else { - - Render::setTitle(Dictionary::translate('lang_createUser')); - Render::addTemplate('page-adduser', $_POST); - } - } - -} diff --git a/modules/adduser/config.json b/modules/adduser/config.json new file mode 100644 index 00000000..d5da4cc8 --- /dev/null +++ b/modules/adduser/config.json @@ -0,0 +1,4 @@ +{ + "category":"hidden", + "enabled":"true" +} diff --git a/modules/adduser/module.inc.php b/modules/adduser/module.inc.php new file mode 100644 index 00000000..c236cb6f --- /dev/null +++ b/modules/adduser/module.inc.php @@ -0,0 +1,60 @@ + $_POST['user'], + 'pass' => Crypto::hash6($_POST['pass1']), + 'fullname' => $_POST['fullname'], + 'phone' => $_POST['phone'], + 'email' => $_POST['email'], + ); + if (Database::exec('INSERT INTO user SET login = :user, passwd = :pass, fullname = :fullname, phone = :phone, email = :email', $data) != 1) { + Util::traceError('Could not create new user in DB'); + } + // Make it superadmin if first user. This method sucks as it's a race condition but hey... + $ret = Database::queryFirst('SELECT Count(*) AS num FROM user'); + if ($ret !== false && $ret['num'] == 1) { + Database::exec('UPDATE user SET permissions = 1'); + EventLog::clear(); + EventLog::info('Created first user ' . $_POST['user']); + } else { + EventLog::info(User::getName() . ' created user ' . $_POST['user']); + } + Message::addInfo('adduser-success'); + Util::redirect('?do=Session&action=login'); + } + } + } + + protected function doRender() + { + // No user was added, check if current user is allowed to add a new user + // Currently you can only add users if there is no user yet. :) + if (!User::hasPermission('superadmin') && Database::queryFirst('SELECT userid FROM user LIMIT 1') !== false) { + Message::addError('adduser-disabled'); + } else { + + Render::setTitle(Dictionary::translate('lang_createUser')); + Render::addTemplate('page-adduser', $_POST); + } + } + +} diff --git a/modules/adduser/templates/page-adduser.html b/modules/adduser/templates/page-adduser.html new file mode 100644 index 00000000..0b097890 --- /dev/null +++ b/modules/adduser/templates/page-adduser.html @@ -0,0 +1,28 @@ +
+ + +

{{lang_createUser}}

+
+
{{lang_username}} *
+
+
+
+
{{lang_password}} *
+
+
+
+
+
{{lang_fullName}} *
+
+
+
+
{{lang_telephone}}
+
+
+
+
E-Mail
+
+
+ + +
\ No newline at end of file diff --git a/modules/backup.inc.php b/modules/backup.inc.php deleted file mode 100644 index 24352a9a..00000000 --- a/modules/backup.inc.php +++ /dev/null @@ -1,134 +0,0 @@ -action = Request::post('action'); - if ($this->action === 'backup') { - $this->backup(); - } elseif ($this->action === 'restore') { - $this->restore(); - } - } - - protected function doRender() - { - Render::setTitle(Dictionary::translate('lang_titleBackup')); - if ($this->action === 'restore') { - Render::addTemplate('backup/restore', $this->templateData); - } else { - Render::addScriptBottom('fileselect'); - Render::addTemplate('backup/_page'); - } - } - - private function backup() - { - $task = Taskmanager::submit('BackupRestore', array('mode' => 'backup')); - if (!isset($task['id'])) { - Message::addError('backup-failed'); - Util::redirect('?do=Backup'); - } - $task = Taskmanager::waitComplete($task, 30000); - if (!Taskmanager::isFinished($task) || !isset($task['data']['backupFile'])) { - Taskmanager::addErrorMessage($task); - Util::redirect('?do=Backup'); - } - while ((@ob_get_level()) > 0) - @ob_end_clean(); - $fh = @fopen($task['data']['backupFile'], 'rb'); - if ($fh === false) { - Message::addError('error-read', $task['data']['backupFile']); - Util::redirect('?do=Backup'); - } - Header('Content-Type: application/octet-stream', true); - Header('Content-Disposition: attachment; filename=' . 'satellite-backup_v' . Database::getExpectedSchemaVersion() . '_' . date('Y.m.d-H.i.s') . '.tgz'); - Header('Content-Length: ' . @filesize($task['data']['backupFile'])); - while (!feof($fh)) { - $data = fread($fh, 16000); - if ($data === false) { - EventLog::failure('Could not stream system backup to browser - backup corrupted!'); - die("\r\n\nDOWNLOAD INTERRUPTED!\n"); - } - echo $data; - @ob_flush(); - @flush(); - } - @fclose($fh); - @unlink($task['data']['backupFile']); - die(); - } - - private function restore() - { - if (!isset($_FILES['backupfile'])) { - Message::addError('missing-file'); - Util::redirect('?do=Backup'); - } - if ($_FILES['backupfile']['error'] != UPLOAD_ERR_OK) { - Message::addError('upload-failed', Util::uploadErrorString($_FILES['backupfile']['error'])); - Util::redirect('?do=Backup'); - } - $tempfile = '/tmp/bwlp-' . mt_rand(1, 100000) . '-' . crc32($_SERVER['REMOTE_ADDR']) . '.tgz'; - if (!move_uploaded_file($_FILES['backupfile']['tmp_name'], $tempfile)) { - Message::addError('error-write', $tempfile); - Util::redirect('?do=Backup'); - } - // Got uploaded file, now shut down all the daemons etc. - $parent = Trigger::stopDaemons(null, $this->templateData); - // Unmount store - $task = Taskmanager::submit('MountVmStore', array( - 'address' => 'null', - 'type' => 'images', - 'parentTask' => $parent, - 'failOnParentFail' => false - )); - if (isset($task['id'])) { - $this->templateData['mountid'] = $task['id']; - $parent = $task['id']; - } - EventLog::info('Creating backup, v' . Database::getExpectedSchemaVersion() . ' on ' . Property::getServerIp()); - // Finally run restore - $task = Taskmanager::submit('BackupRestore', array( - 'mode' => 'restore', - 'backupFile' => $tempfile, - 'parentTask' => $parent, - 'failOnParentFail' => false, - 'restoreOpenslx' => Request::post('restore_openslx', 'off') === 'on', - 'restoreDozmod' => Request::post('restore_dozmod', 'off') === 'on', - )); - if (isset($task['id'])) { - $this->templateData['restoreid'] = $task['id']; - $parent = $task['id']; - TaskmanagerCallback::addCallback($task, 'dbRestored'); - } - // Wait a bit - $task = Taskmanager::submit('SleepTask', array( - 'seconds' => 3, - 'parentTask' => $parent, - 'failOnParentFail' => false - )); - if (isset($task['id'])) - $parent = $task['id']; - // Reboot - $task = Taskmanager::submit('Reboot', array( - 'parentTask' => $parent, - 'failOnParentFail' => false - )); - // Leave this comment so the i18n scanner finds it: - // Message::addSuccess('restore-done'); - if (isset($task['id'])) - $this->templateData['rebootid'] = $task['id']; - } - -} diff --git a/modules/backup/config.json b/modules/backup/config.json new file mode 100644 index 00000000..4e7fa5fb --- /dev/null +++ b/modules/backup/config.json @@ -0,0 +1,4 @@ +{ + "category":"content", + "enabled":"true" +} diff --git a/modules/backup/module.inc.php b/modules/backup/module.inc.php new file mode 100644 index 00000000..4095f875 --- /dev/null +++ b/modules/backup/module.inc.php @@ -0,0 +1,164 @@ +action = Request::post('action'); + if ($this->action === 'backup') { + $this->backup(); + } elseif ($this->action === 'restore') { + $this->restore(); + } + } + + protected function doRender() + { + Render::setTitle(Dictionary::translate('lang_titleBackup')); + if ($this->action === 'restore') { + Render::addTemplate('restore', $this->templateData); + } else { + Render::addScriptBottom('fileselect'); + Render::addTemplate('_page'); + } + } + + private function backup() + { + $task = Taskmanager::submit('BackupRestore', array('mode' => 'backup')); + if (!isset($task['id'])) { + Message::addError('backup-failed'); + Util::redirect('?do=Backup'); + } + $task = Taskmanager::waitComplete($task, 30000); + if (!Taskmanager::isFinished($task) || !isset($task['data']['backupFile'])) { + Taskmanager::addErrorMessage($task); + Util::redirect('?do=Backup'); + } + while ((@ob_get_level()) > 0) + @ob_end_clean(); + $fh = @fopen($task['data']['backupFile'], 'rb'); + if ($fh === false) { + Message::addError('error-read', $task['data']['backupFile']); + Util::redirect('?do=Backup'); + } + Header('Content-Type: application/octet-stream', true); + Header('Content-Disposition: attachment; filename=' . 'satellite-backup_v' . Database::getExpectedSchemaVersion() . '_' . date('Y.m.d-H.i.s') . '.tgz'); + Header('Content-Length: ' . @filesize($task['data']['backupFile'])); + while (!feof($fh)) { + $data = fread($fh, 16000); + if ($data === false) { + EventLog::failure('Could not stream system backup to browser - backup corrupted!'); + die("\r\n\nDOWNLOAD INTERRUPTED!\n"); + } + echo $data; + @ob_flush(); + @flush(); + } + @fclose($fh); + @unlink($task['data']['backupFile']); + die(); + } + + private function restore() + { + if (!isset($_FILES['backupfile'])) { + Message::addError('missing-file'); + Util::redirect('?do=Backup'); + } + if ($_FILES['backupfile']['error'] != UPLOAD_ERR_OK) { + Message::addError('upload-failed', Util::uploadErrorString($_FILES['backupfile']['error'])); + Util::redirect('?do=Backup'); + } + $tempfile = '/tmp/bwlp-' . mt_rand(1, 100000) . '-' . crc32($_SERVER['REMOTE_ADDR']) . '.tgz'; + if (!move_uploaded_file($_FILES['backupfile']['tmp_name'], $tempfile)) { + Message::addError('error-write', $tempfile); + Util::redirect('?do=Backup'); + } + // Got uploaded file, now shut down all the daemons etc. + $parent = Trigger::stopDaemons(null, $this->templateData); + // Unmount store + $task = Taskmanager::submit('MountVmStore', array( + 'address' => 'null', + 'type' => 'images', + 'parentTask' => $parent, + 'failOnParentFail' => false + )); + if (isset($task['id'])) { + $this->templateData['mountid'] = $task['id']; + $parent = $task['id']; + } + EventLog::info('Creating backup, v' . Database::getExpectedSchemaVersion() . ' on ' . Property::getServerIp()); + // Finally run restore + $task = Taskmanager::submit('BackupRestore', array( + 'mode' => 'restore', + 'backupFile' => $tempfile, + 'parentTask' => $parent, + 'failOnParentFail' => false, + 'restoreOpenslx' => Request::post('restore_openslx', 'off') === 'on', + 'restoreDozmod' => Request::post('restore_dozmod', 'off') === 'on', + )); + if (isset($task['id'])) { + $this->templateData['restoreid'] = $task['id']; + $parent = $task['id']; + TaskmanagerCallback::addCallback($task, 'dbRestored'); + } + // Wait a bit + $task = Taskmanager::submit('SleepTask', array( + 'seconds' => 3, + 'parentTask' => $parent, + 'failOnParentFail' => false + )); + if (isset($task['id'])) + $parent = $task['id']; + // Reboot + $task = Taskmanager::submit('Reboot', array( + 'parentTask' => $parent, + 'failOnParentFail' => false + )); + // Leave this comment so the i18n scanner finds it: + // Message::addSuccess('restore-done'); + if (isset($task['id'])) + $this->templateData['rebootid'] = $task['id']; + } + private function stopDaemons($parent) + { + $task = Taskmanager::submit('SyncdaemonLauncher', array( + 'operation' => 'stop', + 'parentTask' => $parent, + 'failOnParentFail' => false + )); + if (isset($task['id'])) { + $this->templateData['syncid'] = $task['id']; + $parent = $task['id']; + } + $task = Taskmanager::submit('DozmodLauncher', array( + 'operation' => 'stop', + 'parentTask' => $parent, + 'failOnParentFail' => false + )); + if (isset($task['id'])) { + $this->templateData['dmsdid'] = $task['id']; + $parent = $task['id']; + } + $task = Taskmanager::submit('LdadpLauncher', array( + 'ids' => array(), + 'parentTask' => $parent, + 'failOnParentFail' => false + )); + if (isset($task['id'])) { + $this->templateData['ldadpid'] = $task['id']; + $parent = $task['id']; + } + return $parent; + } +} diff --git a/modules/backup/templates/_page.html b/modules/backup/templates/_page.html new file mode 100644 index 00000000..47b5a174 --- /dev/null +++ b/modules/backup/templates/_page.html @@ -0,0 +1,41 @@ +

{{lang_backupRestore}}

+ +
+ + +
+
{{lang_backup}}
+
+

{{lang_backupDescription}}

+ +
+
+
+ +
+ + +
+
{{lang_restore}}
+
+

{{lang_restoreDescription}}

+
+ + + + {{lang_browseForFile}}… + + +
+
+ +

{{lang_systemExplanation}}

+
+
+ +

{{lang_dozmodExplanation}}

+
+ +
+
+
\ No newline at end of file diff --git a/modules/backup/templates/restore.html b/modules/backup/templates/restore.html new file mode 100644 index 00000000..4494a993 --- /dev/null +++ b/modules/backup/templates/restore.html @@ -0,0 +1,62 @@ +
+
{{lang_backup}}
+
+
+
{{lang_stopping}} syncdaemon
+
{{lang_stopping}} dmsd
+
{{lang_stopping}} ldadp
+
{{lang_stopping}} vmstore
+
{{lang_restoreConfig}}
+
{{lang_reboot}}
+
+ + +
+
+ + diff --git a/modules/baseconfig.inc.php b/modules/baseconfig.inc.php deleted file mode 100644 index 152e3372..00000000 --- a/modules/baseconfig.inc.php +++ /dev/null @@ -1,127 +0,0 @@ -qry_extra[] = array( - 'name' => 'distroid', - 'value' => (int)$_REQUEST['distroid'], - 'table' => 'setting_distro', - ); - if (isset($_REQUEST['poolid'])) { - $this->qry_extra[] = array( - 'name' => 'poolid', - 'value' => (int)$_REQUEST['poolid'], - 'table' => 'setting_pool', - ); - } - } - - if (isset($_POST['setting']) && is_array($_POST['setting'])) { - if (User::hasPermission('superadmin')) { - // Build variables for specific sub-settings - $qry_insert = ''; - $qry_values = ''; - foreach ($this->qry_extra as $item) { - $qry_insert = ', ' . $item['name']; - $qry_values = ', :' . $item['name']; - } - // Load all existing config options to validate input - $res = Database::simpleQuery('SELECT setting, validator FROM setting'); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $key = $row['setting']; - $validator = $row['validator']; - $displayValue = (isset($_POST['setting'][$key]) ? $_POST['setting'][$key] : ''); - // Validate data first! - $mangledValue = Validator::validate($validator, $displayValue); - if ($mangledValue === false) { - Message::addWarning('value-invalid', $key, $displayValue); - continue; - } - // Now put into DB - Database::exec("INSERT INTO setting_global (setting, value, displayvalue $qry_insert) - VALUES (:key, :value, :displayvalue $qry_values) - ON DUPLICATE KEY UPDATE value = :value, displayvalue = :displayvalue", - $this->qry_extra + array( - 'key' => $key, - 'value' => $mangledValue, - 'displayvalue' => $displayValue - ) - ); - } - Message::addSuccess('settings-updated'); - Util::redirect('?do=BaseConfig'); - } - } - } - - protected function doRender() - { - if (!User::hasPermission('superadmin')) { - Message::addError('no-permission'); - Util::redirect('?do=Main'); - } - // Build left joins for specific settings - $joins = ''; - foreach ($this->qry_extra as $item) { - $joins .= " LEFT JOIN {$item['table']} "; - } - // List global config option - $settings = array(); - $res = Database::simpleQuery('SELECT cat_setting.catid, setting.setting, setting.defaultvalue, setting.permissions, setting.validator, tbl.displayvalue - FROM setting - INNER JOIN cat_setting USING (catid) - LEFT JOIN setting_global AS tbl USING (setting) - ORDER BY cat_setting.sortval ASC, setting.setting ASC'); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $row['description'] = Util::markup(Dictionary::translate('settings/setting', $row['setting'])); - if (is_null($row['displayvalue'])) $row['displayvalue'] = $row['defaultvalue']; - $row['item'] = $this->makeInput($row['validator'], $row['setting'], $row['displayvalue']); - $settings[$row['catid']]['settings'][] = $row; - $settings[$row['catid']]['category_name'] = Dictionary::translate('settings/cat_setting', 'cat_' . $row['catid']); - } - - Render::addTemplate('baseconfig/_page', array( - 'categories' => array_values($settings) - )); - } - - /** - * Create html snippet for setting, based on given validator - * @param type $validator - * @return boolean - */ - private function makeInput($validator, $setting, $current) - { - $parts = explode(':', $validator, 2); - if ($parts[0] === 'list') { - $items = explode('|', $parts[1]); - $ret = ''; - } - // Password field guessing - if (stripos($validator, 'password') !== false) { - $type = Property::getPasswordFieldType(); - } else { - $type = 'text'; - } - // Fallback: single line input - return ''; - } - -} diff --git a/modules/baseconfig/config.json b/modules/baseconfig/config.json new file mode 100644 index 00000000..93209f62 --- /dev/null +++ b/modules/baseconfig/config.json @@ -0,0 +1,4 @@ +{ + "category":"settings", + "enabled":"true" +} diff --git a/modules/baseconfig/module.inc.php b/modules/baseconfig/module.inc.php new file mode 100644 index 00000000..3d187d30 --- /dev/null +++ b/modules/baseconfig/module.inc.php @@ -0,0 +1,127 @@ +qry_extra[] = array( + 'name' => 'distroid', + 'value' => (int)$_REQUEST['distroid'], + 'table' => 'setting_distro', + ); + if (isset($_REQUEST['poolid'])) { + $this->qry_extra[] = array( + 'name' => 'poolid', + 'value' => (int)$_REQUEST['poolid'], + 'table' => 'setting_pool', + ); + } + } + + if (isset($_POST['setting']) && is_array($_POST['setting'])) { + if (User::hasPermission('superadmin')) { + // Build variables for specific sub-settings + $qry_insert = ''; + $qry_values = ''; + foreach ($this->qry_extra as $item) { + $qry_insert = ', ' . $item['name']; + $qry_values = ', :' . $item['name']; + } + // Load all existing config options to validate input + $res = Database::simpleQuery('SELECT setting, validator FROM setting'); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $key = $row['setting']; + $validator = $row['validator']; + $displayValue = (isset($_POST['setting'][$key]) ? $_POST['setting'][$key] : ''); + // Validate data first! + $mangledValue = Validator::validate($validator, $displayValue); + if ($mangledValue === false) { + Message::addWarning('value-invalid', $key, $displayValue); + continue; + } + // Now put into DB + Database::exec("INSERT INTO setting_global (setting, value, displayvalue $qry_insert) + VALUES (:key, :value, :displayvalue $qry_values) + ON DUPLICATE KEY UPDATE value = :value, displayvalue = :displayvalue", + $this->qry_extra + array( + 'key' => $key, + 'value' => $mangledValue, + 'displayvalue' => $displayValue + ) + ); + } + Message::addSuccess('settings-updated'); + Util::redirect('?do=BaseConfig'); + } + } + } + + protected function doRender() + { + if (!User::hasPermission('superadmin')) { + Message::addError('no-permission'); + Util::redirect('?do=Main'); + } + // Build left joins for specific settings + $joins = ''; + foreach ($this->qry_extra as $item) { + $joins .= " LEFT JOIN {$item['table']} "; + } + // List global config option + $settings = array(); + $res = Database::simpleQuery('SELECT cat_setting.catid, setting.setting, setting.defaultvalue, setting.permissions, setting.validator, tbl.displayvalue + FROM setting + INNER JOIN cat_setting USING (catid) + LEFT JOIN setting_global AS tbl USING (setting) + ORDER BY cat_setting.sortval ASC, setting.setting ASC'); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['description'] = Util::markup(Dictionary::translate('settings/setting', $row['setting'])); + if (is_null($row['displayvalue'])) $row['displayvalue'] = $row['defaultvalue']; + $row['item'] = $this->makeInput($row['validator'], $row['setting'], $row['displayvalue']); + $settings[$row['catid']]['settings'][] = $row; + $settings[$row['catid']]['category_name'] = Dictionary::translate('settings/cat_setting', 'cat_' . $row['catid']); + $settings[$row['catid']]['category_id'] = $row['catid']; + } + Render::addTemplate('_page', array( + 'categories' => array_values($settings) + )); + } + + /** + * Create html snippet for setting, based on given validator + * @param type $validator + * @return boolean + */ + private function makeInput($validator, $setting, $current) + { + $parts = explode(':', $validator, 2); + if ($parts[0] === 'list') { + $items = explode('|', $parts[1]); + $ret = ''; + } + // Password field guessing + if (stripos($validator, 'password') !== false) { + $type = Property::getPasswordFieldType(); + } else { + $type = 'text'; + } + // Fallback: single line input + return ''; + } + +} diff --git a/modules/baseconfig/templates/_page.html b/modules/baseconfig/templates/_page.html new file mode 100644 index 00000000..c72f0a5e --- /dev/null +++ b/modules/baseconfig/templates/_page.html @@ -0,0 +1,186 @@ +

{{lang_basicConfiguration}}

+

{{lang_clientRelatedConfig}}

+
+
+ + + {{#categories}} +
+ +
+
+
+ {{#settings}} +
+
+
+ +
+
+ {{setting}} +
{{defaultvalue}}
+
+
+ {{{item}}} +
+
+ +
+
+
+ + {{/settings}} +
+
+ +
+
+
+
+ {{/categories}} + + + + +
+ +
+
+
+ {{#users}} +
+
+
+ +
+
+ +
+ +
+
+ {{/users}} +
+
+
+
+
+ + + + Download +
+

+

+ + + +
+

+ +
+ + + +
+ +
+ + + +
+ diff --git a/modules/citymanagement/config.json b/modules/citymanagement/config.json new file mode 100644 index 00000000..3cd4afd3 --- /dev/null +++ b/modules/citymanagement/config.json @@ -0,0 +1,5 @@ +{ + "category":"hidden", + "enabled":"false", + "permission":"0" +} diff --git a/modules/citymanagement/module.inc.php b/modules/citymanagement/module.inc.php new file mode 100644 index 00000000..acc30bf9 --- /dev/null +++ b/modules/citymanagement/module.inc.php @@ -0,0 +1,81 @@ +page = $p; + else + $this->page = 1; + switch(Request::post('action')){ + case "edit": + $this->edit(Request::post('cityid'),Request::post('name')); + break; + case "create": + $this->create(Request::post('name')); + break; + case "delete": + $this->delete(Request::post('cityid')); + break; + } + + + if (!User::hasPermission('superadmin')) { + Message::addError('no-permission'); + Util::redirect('?do=Main'); + } + + } + + protected function doRender() + { + // load every city + $cities = array(); + $res = Database::simpleQuery("SELECT cityid, name FROM cities ORDER BY cityid DESC"); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $cities[] = array( + 'id' => $row['cityid'], + 'name' => $row['name'], + ); + } + + $pag = new Pagination($cities,$this->page); + + Render::addTemplate('page-citymanagement', array( + 'cities' => $pag->getItems(), + 'pages' => $pag->getPagination() + )); + } + + private function edit($cityid, $newname){ + $data = array ( + 'cityid' => $cityid, + 'name' => $newname, + ); + Database::exec ( 'UPDATE cities SET name = :name WHERE cityid = :cityid', $data ); + Message::addSuccess('update-city'); + } + + private function create($name){ + $data = array ( + 'name' => $name, + ); + Database::exec('INSERT INTO cities(name) VALUES( :name )',$data); + Message::addSuccess('add-city'); + } + + private function delete($cityid){ + $data = array ( + 'cityid' => $cityid + ); + Database::exec ( 'DELETE FROM cities WHERE cityid = :cityid', $data ); + Message::addSuccess('delete-city'); + } +} diff --git a/modules/citymanagement/templates/page-citymanagement.html b/modules/citymanagement/templates/page-citymanagement.html new file mode 100644 index 00000000..6d7750cf --- /dev/null +++ b/modules/citymanagement/templates/page-citymanagement.html @@ -0,0 +1,77 @@ +
+
+
{{lang_cityPage}}
+
+
+

+ {{lang_cityInfo}} +

+ +
+ + + + + + + + + + + + + + + {{#cities}} + + + + + + {{/cities}} + + + +
ID{{lang_cityname}}{{lang_operations}}
+ +
+ + + +
+
    + {{#pages}} +
  • {{page}}
  • + {{/pages}} +
+
+
+{{#cities}} + +{{/cities}} diff --git a/modules/dozmod.inc.php b/modules/dozmod.inc.php deleted file mode 100644 index 98cacd59..00000000 --- a/modules/dozmod.inc.php +++ /dev/null @@ -1,252 +0,0 @@ -mailHandler(); - } - if ($action === 'delimages') { - $result = $this->handleDeleteImages(); - if (!empty($result)) { - Message::addInfo('delete-images', $result); - } - Util::redirect('?do=DozMod'); - } - } - - protected function doRender() - { - $this->listDeletePendingImages(); - // Mail config - $conf = Database::queryFirst('SELECT value FROM sat.configuration WHERE parameter = :param', array('param' => 'mailconfig')); - if ($conf != null) { - $conf = @json_decode($conf['value'], true); - if (is_array($conf)) { - $conf['set_' . $conf['ssl']] = 'selected="selected"'; - } - } - Render::addTemplate('dozmod/mailconfig', $conf); - // User list for making people admin - $this->listUsers(); - $this->listOrganizations(); - } - - private function listDeletePendingImages() - { - $res = Database::simpleQuery("SELECT b.displayname," - . " own.firstname, own.lastname, own.email," - . " v.imageversionid, v.createtime, v.filesize, v.deletestate," - . " lat.expiretime AS latexptime, lat.deletestate AS latdelstate" - . " FROM sat.imageversion v" - . " INNER JOIN sat.imagebase b ON (b.imagebaseid = v.imagebaseid)" - . " INNER JOIN sat.user own ON (b.ownerid = own.userid)" - . " LEFT JOIN sat.imageversion lat ON (b.latestversionid = lat.imageversionid)" - . " WHERE v.deletestate <> 'KEEP'" - . " ORDER BY b.displayname ASC, v.createtime ASC"); - $NOW = time(); - $rows = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - if ($row['latexptime'] > $NOW && $row['latdelstate'] === 'KEEP') { - $row['hasNewerClass'] = 'glyphicon-ok green'; - } else { - $row['hasNewerClass'] = 'glyphicon-remove red'; - } - if ($row['deletestate'] === 'WANT_DELETE') { - $row['name_extra_class'] = 'slx-strike'; - } - $row['version'] = date('d.m.Y H:i:s', $row['createtime']); - $row['filesize'] = Util::readableFileSize($row['filesize']); - $rows[] = $row; - } - if (empty($rows)) - return; - Render::addTemplate('dozmod/images-delete', array('images' => $rows)); - } - - private function cleanMailArray() - { - $keys = array('host', 'port', 'ssl', 'senderAddress', 'replyTo', 'username', 'password', 'serverName'); - $data = array(); - foreach ($keys as $key) { - $data[$key] = Request::post($key, ''); - settype($data[$key], 'string'); - if (is_numeric($data[$key])) { - settype($data[$key], 'int'); - } - } - return $data; - } - - protected function doAjax() - { - if (!User::hasPermission('superadmin')) - return; - - $action = Request::post('action'); - if ($action === 'mail') { - $this->handleTestMail(); - } elseif ($action === 'setmail' || $action === 'setsu' || $action == 'setlogin') { - $this->setUserOption($action); - } elseif ($action === 'setorglogin') { - $this->setOrgOption($action); - } elseif ($action === 'delimages') { - die($this->handleDeleteImages()); - } - } - - private function handleDeleteImages() - { - $images = Request::post('images', false); - if (is_array($images)) { - foreach ($images as $image => $val) { - if (strtolower($val) !== 'on') - continue; - Database::exec("UPDATE sat.imageversion SET deletestate = 'WANT_DELETE'" - . " WHERE deletestate = 'SHOULD_DELETE' AND imageversionid = :imageversionid", array( - 'imageversionid' => $image - )); - } - if (!empty($images)) { - $ret = Download::asStringPost('http://127.0.0.1:9080/do/delete-images', false, 2, $code); - if ($code == 999) { - $ret .= "\nConnection to DMSD failed."; - } - return $ret; - } - } - return false; - } - - private function handleTestMail() - { - $do = Request::post('button'); - if ($do === 'test') { - // Prepare array - $data = $this->cleanMailArray(); - Header('Content-Type: text/plain; charset=utf-8'); - $data['recipient'] = Request::post('recipient', ''); - if (!preg_match('/.+@.+\..+/', $data['recipient'])) { - $result = 'No recipient given!'; - } else { - $result = Download::asStringPost('http://127.0.0.1:9080/do/mailtest', $data, 2, $code); - } - die($result); - } - } - - private function mailHandler() - { - // Check action - $do = Request::post('button'); - if ($do === 'save') { - // Prepare array - $data = $this->cleanMailArray(); - $data = json_encode($data); - Database::exec('INSERT INTO sat.configuration (parameter, value)' - . ' VALUES (:param, :value)' - . ' ON DUPLICATE KEY UPDATE value = VALUES(value)', array( - 'param' => 'mailconfig', - 'value' => $data - )); - Message::addSuccess('mail-config-saved'); - } - Util::redirect('?do=DozMod'); - } - - private function listUsers() - { - $res = Database::simpleQuery('SELECT userid, firstname, lastname, email, lastlogin, user.canlogin, issuperuser, emailnotifications,' - . ' organization.displayname AS orgname FROM sat.user' - . ' LEFT JOIN sat.organization USING (organizationid)' - . ' ORDER BY lastname ASC, firstname ASC'); - $rows = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $row['canlogin'] = $this->checked($row['canlogin']); - $row['issuperuser'] = $this->checked($row['issuperuser']); - $row['emailnotifications'] = $this->checked($row['emailnotifications']); - $row['lastlogin'] = date('d.m.Y', $row['lastlogin']); - $rows[] = $row; - } - Render::addTemplate('dozmod/userlist', array('users' => $rows)); - } - - private function listOrganizations() - { - $res = Database::simpleQuery('SELECT organizationid, displayname, canlogin FROM sat.organization' - . ' ORDER BY displayname ASC'); - $rows = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $row['canlogin'] = $this->checked($row['canlogin']); - $rows[] = $row; - } - Render::addTemplate('dozmod/orglist', array('organizations' => $rows)); - } - - private function checked($val) - { - if ($val) - return 'checked="checked"'; - return ''; - } - - private function setUserOption($option) - { - $val = (string) Request::post('value', '-'); - if ($val !== '1' && $val !== '0') - die('Nein'); - if ($option === 'setmail') { - $field = 'emailnotifications'; - } elseif ($option === 'setsu') { - $field = 'issuperuser'; - } elseif ($option === 'setlogin') { - $field = 'canlogin'; - } else { - die('Unknown'); - } - $user = (string) Request::post('userid', '?'); - $ret = Database::exec("UPDATE sat.user SET $field = :onoff WHERE userid = :userid", array( - 'userid' => $user, - 'onoff' => $val - )); - error_log("Setting $field to $val for $user - affected: $ret"); - if ($ret === false) - die('Error'); - if ($ret == 0) - die(1 - $val); - die($val); - } - - private function setOrgOption($option) - { - $val = (string) Request::post('value', '-'); - if ($val !== '1' && $val !== '0') - die('Nein'); - if ($option === 'setorglogin') { - $field = 'canlogin'; - } else { - die('Unknown'); - } - $ret = Database::exec("UPDATE sat.organization SET $field = :onoff WHERE organizationid = :organizationid", array( - 'organizationid' => (string) Request::post('organizationid', ''), - 'onoff' => $val - )); - if ($ret === false) - die('Error'); - if ($ret === 0) - die(1 - $val); - die($val); - } - -} diff --git a/modules/dozmod/config.json b/modules/dozmod/config.json new file mode 100644 index 00000000..4e7fa5fb --- /dev/null +++ b/modules/dozmod/config.json @@ -0,0 +1,4 @@ +{ + "category":"content", + "enabled":"true" +} diff --git a/modules/dozmod/module.inc.php b/modules/dozmod/module.inc.php new file mode 100644 index 00000000..f98d8952 --- /dev/null +++ b/modules/dozmod/module.inc.php @@ -0,0 +1,252 @@ +mailHandler(); + } + if ($action === 'delimages') { + $result = $this->handleDeleteImages(); + if (!empty($result)) { + Message::addInfo('delete-images', $result); + } + Util::redirect('?do=DozMod'); + } + } + + protected function doRender() + { + $this->listDeletePendingImages(); + // Mail config + $conf = Database::queryFirst('SELECT value FROM sat.configuration WHERE parameter = :param', array('param' => 'mailconfig')); + if ($conf != null) { + $conf = @json_decode($conf['value'], true); + if (is_array($conf)) { + $conf['set_' . $conf['ssl']] = 'selected="selected"'; + } + } + Render::addTemplate('mailconfig', $conf); + // User list for making people admin + $this->listUsers(); + $this->listOrganizations(); + } + + private function listDeletePendingImages() + { + $res = Database::simpleQuery("SELECT b.displayname," + . " own.firstname, own.lastname, own.email," + . " v.imageversionid, v.createtime, v.filesize, v.deletestate," + . " lat.expiretime AS latexptime, lat.deletestate AS latdelstate" + . " FROM sat.imageversion v" + . " INNER JOIN sat.imagebase b ON (b.imagebaseid = v.imagebaseid)" + . " INNER JOIN sat.user own ON (b.ownerid = own.userid)" + . " LEFT JOIN sat.imageversion lat ON (b.latestversionid = lat.imageversionid)" + . " WHERE v.deletestate <> 'KEEP'" + . " ORDER BY b.displayname ASC, v.createtime ASC"); + $NOW = time(); + $rows = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if ($row['latexptime'] > $NOW && $row['latdelstate'] === 'KEEP') { + $row['hasNewerClass'] = 'glyphicon-ok green'; + } else { + $row['hasNewerClass'] = 'glyphicon-remove red'; + } + if ($row['deletestate'] === 'WANT_DELETE') { + $row['name_extra_class'] = 'slx-strike'; + } + $row['version'] = date('d.m.Y H:i:s', $row['createtime']); + $row['filesize'] = Util::readableFileSize($row['filesize']); + $rows[] = $row; + } + if (empty($rows)) + return; + Render::addTemplate('images-delete', array('images' => $rows)); + } + + private function cleanMailArray() + { + $keys = array('host', 'port', 'ssl', 'senderAddress', 'replyTo', 'username', 'password', 'serverName'); + $data = array(); + foreach ($keys as $key) { + $data[$key] = Request::post($key, ''); + settype($data[$key], 'string'); + if (is_numeric($data[$key])) { + settype($data[$key], 'int'); + } + } + return $data; + } + + protected function doAjax() + { + if (!User::hasPermission('superadmin')) + return; + + $action = Request::post('action'); + if ($action === 'mail') { + $this->handleTestMail(); + } elseif ($action === 'setmail' || $action === 'setsu' || $action == 'setlogin') { + $this->setUserOption($action); + } elseif ($action === 'setorglogin') { + $this->setOrgOption($action); + } elseif ($action === 'delimages') { + die($this->handleDeleteImages()); + } + } + + private function handleDeleteImages() + { + $images = Request::post('images', false); + if (is_array($images)) { + foreach ($images as $image => $val) { + if (strtolower($val) !== 'on') + continue; + Database::exec("UPDATE sat.imageversion SET deletestate = 'WANT_DELETE'" + . " WHERE deletestate = 'SHOULD_DELETE' AND imageversionid = :imageversionid", array( + 'imageversionid' => $image + )); + } + if (!empty($images)) { + $ret = Download::asStringPost('http://127.0.0.1:9080/do/delete-images', false, 2, $code); + if ($code == 999) { + $ret .= "\nConnection to DMSD failed."; + } + return $ret; + } + } + return false; + } + + private function handleTestMail() + { + $do = Request::post('button'); + if ($do === 'test') { + // Prepare array + $data = $this->cleanMailArray(); + Header('Content-Type: text/plain; charset=utf-8'); + $data['recipient'] = Request::post('recipient', ''); + if (!preg_match('/.+@.+\..+/', $data['recipient'])) { + $result = 'No recipient given!'; + } else { + $result = Download::asStringPost('http://127.0.0.1:9080/do/mailtest', $data, 2, $code); + } + die($result); + } + } + + private function mailHandler() + { + // Check action + $do = Request::post('button'); + if ($do === 'save') { + // Prepare array + $data = $this->cleanMailArray(); + $data = json_encode($data); + Database::exec('INSERT INTO sat.configuration (parameter, value)' + . ' VALUES (:param, :value)' + . ' ON DUPLICATE KEY UPDATE value = VALUES(value)', array( + 'param' => 'mailconfig', + 'value' => $data + )); + Message::addSuccess('mail-config-saved'); + } + Util::redirect('?do=DozMod'); + } + + private function listUsers() + { + $res = Database::simpleQuery('SELECT userid, firstname, lastname, email, lastlogin, user.canlogin, issuperuser, emailnotifications,' + . ' organization.displayname AS orgname FROM sat.user' + . ' LEFT JOIN sat.organization USING (organizationid)' + . ' ORDER BY lastname ASC, firstname ASC'); + $rows = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['canlogin'] = $this->checked($row['canlogin']); + $row['issuperuser'] = $this->checked($row['issuperuser']); + $row['emailnotifications'] = $this->checked($row['emailnotifications']); + $row['lastlogin'] = date('d.m.Y', $row['lastlogin']); + $rows[] = $row; + } + Render::addTemplate('userlist', array('users' => $rows)); + } + + private function listOrganizations() + { + $res = Database::simpleQuery('SELECT organizationid, displayname, canlogin FROM sat.organization' + . ' ORDER BY displayname ASC'); + $rows = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['canlogin'] = $this->checked($row['canlogin']); + $rows[] = $row; + } + Render::addTemplate('orglist', array('organizations' => $rows)); + } + + private function checked($val) + { + if ($val) + return 'checked="checked"'; + return ''; + } + + private function setUserOption($option) + { + $val = (string) Request::post('value', '-'); + if ($val !== '1' && $val !== '0') + die('Nein'); + if ($option === 'setmail') { + $field = 'emailnotifications'; + } elseif ($option === 'setsu') { + $field = 'issuperuser'; + } elseif ($option === 'setlogin') { + $field = 'canlogin'; + } else { + die('Unknown'); + } + $user = (string) Request::post('userid', '?'); + $ret = Database::exec("UPDATE sat.user SET $field = :onoff WHERE userid = :userid", array( + 'userid' => $user, + 'onoff' => $val + )); + error_log("Setting $field to $val for $user - affected: $ret"); + if ($ret === false) + die('Error'); + if ($ret == 0) + die(1 - $val); + die($val); + } + + private function setOrgOption($option) + { + $val = (string) Request::post('value', '-'); + if ($val !== '1' && $val !== '0') + die('Nein'); + if ($option === 'setorglogin') { + $field = 'canlogin'; + } else { + die('Unknown'); + } + $ret = Database::exec("UPDATE sat.organization SET $field = :onoff WHERE organizationid = :organizationid", array( + 'organizationid' => (string) Request::post('organizationid', ''), + 'onoff' => $val + )); + if ($ret === false) + die('Error'); + if ($ret === 0) + die(1 - $val); + die($val); + } + +} diff --git a/modules/dozmod/templates/images-delete.html b/modules/dozmod/templates/images-delete.html new file mode 100644 index 00000000..c4cbfd34 --- /dev/null +++ b/modules/dozmod/templates/images-delete.html @@ -0,0 +1,57 @@ +

{{lang_heading}}

+ +
+
+ {{lang_subHeading}} +
+
+

{{lang_description}}

+
+
+ + + + + + + + + + + + + + + {{#images}} + + + + + + + + + {{/images}} + +
{{lang_image}}{{lang_version}}{{lang_owner}}{{lang_size}}
{{displayname}}
{{imageversionid}}
{{version}}{{lastname}}, {{firstname}}{{filesize}}
+ +
+ +
+
+
+ + \ No newline at end of file diff --git a/modules/dozmod/templates/mailconfig.html b/modules/dozmod/templates/mailconfig.html new file mode 100644 index 00000000..b19776c0 --- /dev/null +++ b/modules/dozmod/templates/mailconfig.html @@ -0,0 +1,91 @@ +

{{lang_mailConfigHeadline}}

+ +
+
+ {{lang_mailConfig}} +
+
+

{{lang_mailDescription}}

+

[BETA] Diese Funktionalität ist neu. Wir bitten um Nachsicht, falls es Situationen gibt, in denen zu viele + oder zu wenige Nachrichten verschickt werden.

+
+ + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+

{{lang_asteriskRequired}}

+
+

{{lang_testConfiguration}}

+
+ + +
+
+ + + + +
+ + +
+
+
+ + \ No newline at end of file diff --git a/modules/dozmod/templates/orglist.html b/modules/dozmod/templates/orglist.html new file mode 100644 index 00000000..d325cc4d --- /dev/null +++ b/modules/dozmod/templates/orglist.html @@ -0,0 +1,51 @@ +

{{lang_organizationList}}

+ +
+
+ {{lang_organizationListHeader}} +
+
+
+ + + + + + + + + {{#organizations}} + + + + + {{/organizations}} + +
{{lang_organization}}
{{displayname}}
+
+
+
+ + \ No newline at end of file diff --git a/modules/dozmod/templates/userlist.html b/modules/dozmod/templates/userlist.html new file mode 100644 index 00000000..a76eae5e --- /dev/null +++ b/modules/dozmod/templates/userlist.html @@ -0,0 +1,62 @@ +

{{lang_userList}}

+ +
+
+ {{lang_userListHeader}} +
+
+

{{lang_userListDescription}}

+
+ + + + + + + + + + + + + + {{#users}} + + + + + + + + + + {{/users}} + +
{{lang_user}}{{lang_organization}}{{lang_lastLogin}}{{lang_email}}
{{lastname}}, {{firstname}}{{orgname}}{{lastlogin}}{{email}}
+
+
+
+ + \ No newline at end of file diff --git a/modules/eventlog.inc.php b/modules/eventlog.inc.php deleted file mode 100644 index a070f5a2..00000000 --- a/modules/eventlog.inc.php +++ /dev/null @@ -1,70 +0,0 @@ -exec(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $day = date('d.m.Y', $row['dateline']); - if ($day === $today) { - $day = Dictionary::translate('today'); - } elseif ($day === $yesterday) { - $day = Dictionary::translate('yesterday'); - } - $row['date'] = $day . date(' H:i', $row['dateline']); - $row['icon'] = $this->typeToIcon($row['logtypeid']); - $row['color'] = $this->typeToColor($row['logtypeid']); - $lines[] = $row; - } - - $paginate->render('eventlog/_page', array( - 'list' => $lines - )); - } - - private function typeToIcon($type) - { - switch ($type) { - case 'info': - return 'ok'; - case 'warning': - return 'exclamation-sign'; - case 'failure': - return 'remove'; - default: - return 'question-sign'; - } - } - - private function typeToColor($type) - { - switch ($type) { - case 'info': - return ''; - case 'warning': - return 'orange'; - case 'failure': - return 'red'; - default: - return ''; - } - } - -} diff --git a/modules/eventlog/config.json b/modules/eventlog/config.json new file mode 100644 index 00000000..d42dc3e3 --- /dev/null +++ b/modules/eventlog/config.json @@ -0,0 +1,4 @@ +{ + "category":"status", + "enabled":"true" +} diff --git a/modules/eventlog/module.inc.php b/modules/eventlog/module.inc.php new file mode 100644 index 00000000..7cfc8a55 --- /dev/null +++ b/modules/eventlog/module.inc.php @@ -0,0 +1,70 @@ +exec(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $day = date('d.m.Y', $row['dateline']); + if ($day === $today) { + $day = Dictionary::translate('today'); + } elseif ($day === $yesterday) { + $day = Dictionary::translate('yesterday'); + } + $row['date'] = $day . date(' H:i', $row['dateline']); + $row['icon'] = $this->typeToIcon($row['logtypeid']); + $row['color'] = $this->typeToColor($row['logtypeid']); + $lines[] = $row; + } + + $paginate->render('_page', array( + 'list' => $lines + )); + } + + private function typeToIcon($type) + { + switch ($type) { + case 'info': + return 'ok'; + case 'warning': + return 'exclamation-sign'; + case 'failure': + return 'remove'; + default: + return 'question-sign'; + } + } + + private function typeToColor($type) + { + switch ($type) { + case 'info': + return ''; + case 'warning': + return 'orange'; + case 'failure': + return 'red'; + default: + return ''; + } + } + +} diff --git a/modules/eventlog/templates/_page.html b/modules/eventlog/templates/_page.html new file mode 100644 index 00000000..2e657805 --- /dev/null +++ b/modules/eventlog/templates/_page.html @@ -0,0 +1,41 @@ +

{{lang_eventLog}}

+{{{pagenav}}} + + + + + + + + + {{#list}} + + + + + + + {{/list}} + +
{{lang_when}}{{lang_event}}{{lang_details}}
{{date}}{{description}}{{#extra}} + » + + {{/extra}}
+{{{pagenav}}} + + diff --git a/modules/imgmanagement/config.json b/modules/imgmanagement/config.json new file mode 100644 index 00000000..49daa91a --- /dev/null +++ b/modules/imgmanagement/config.json @@ -0,0 +1,5 @@ +{ + "category":"hidden", + "enabled":"false", + "permission":"1" +} diff --git a/modules/imgmanagement/module.inc.php b/modules/imgmanagement/module.inc.php new file mode 100644 index 00000000..27844e23 --- /dev/null +++ b/modules/imgmanagement/module.inc.php @@ -0,0 +1,76 @@ +baselocation = '/var/images/'; + $this->images = array(); + + error_reporting(E_ALL); + ini_set('display_errors','on'); + + Session::get('token'); + + } + + protected function doRender() + { + /*get city of user !!!!NOT TESTED!!!! + + $data=array( 'id'= User.getId()); + $res = Database::exec("SELECT cityid FROM user WHERE userid=:id",$data); + $cityid = $res->fetch(PDO::FETCH_ASSOC); + $res = Database::exec("SELECT name FROM cities WHERE cityid=:cityid",$cityid); + $city = $res->fetch(PDO::FETCH_ASSOC); + $location = $baselocation . $city; + + + verify type of vars (string concatenation and more) + !!!!NOT TESTED!!!! + */ + + error_reporting(E_ALL); + ini_set('display_errors','on'); + //Search images on location specified + $location = $this->baselocation . 'curitiba/*'; + //Gets the configuration of each image + $config = substr($location,0,-1).'config.json'; + $imgsactive = json_decode(file_get_contents($config),true); + $images = glob($location, GLOB_ONLYDIR); + $actives = array(); + $deactives= array(); + foreach($images as &$imgname){ + $imgname= substr($imgname, strlen($location)-1); + //fill associative array (img->active[true/false]) + $this->images[$imgname] = isset($imgsactive[$imgname])?$imgsactive[$imgname] : false; + if($this->images[$imgname]){ + array_push($actives, array('name' => $imgname)); + }else{ + array_push($deactives, array('name'=>$imgname)); + } + } + + //Save eventually new images to config.json + $fp = fopen($config,'w'); + fwrite($fp,json_encode($this->images)); + fclose($fp); + Render::addTemplate('page-imgmanagement', array( + 'deactives' => $deactives, + 'actives' => $actives)); + } +} diff --git a/modules/imgmanagement/templates/page-imgmanagement.html b/modules/imgmanagement/templates/page-imgmanagement.html new file mode 100644 index 00000000..795ae39f --- /dev/null +++ b/modules/imgmanagement/templates/page-imgmanagement.html @@ -0,0 +1,46 @@ +
+
+ Gerenciamento de Imagens +
+ +
+
+ Imagens em azul indicam imagens criadas pelo usuário +
+ +
+
+
    + {{#deactives}} +
  • {{name}}
  • + {{/deactives}} +
+
+
+ +
+
+
    + + {{#actives}} +
  • {{name}}
  • + {{/actives}} +
+
+
+ + Upload de Nova Imagem + Gerar IPXE + +
+
+ + + diff --git a/modules/internetaccess.inc.php b/modules/internetaccess.inc.php deleted file mode 100644 index c142285d..00000000 --- a/modules/internetaccess.inc.php +++ /dev/null @@ -1,51 +0,0 @@ -{{lang_internetAccess}} + +
+ + + +
+
{{lang_internetAccess}}
+
+

{{lang_description}}

+
+ {{lang_proxyType}} + +
+
+

{{lang_manualProxyConfig}}

+
+ {{lang_proxyAddress}} * + +
+
+ {{lang_proxyPort}} * + +
+
+ {{lang_proxyUsername}} + +
+
+ {{lang_proxyPassword}} + +
+ +
+
+
diff --git a/modules/internetaccess/templates/restart.html b/modules/internetaccess/templates/restart.html new file mode 100644 index 00000000..effe1feb --- /dev/null +++ b/modules/internetaccess/templates/restart.html @@ -0,0 +1,22 @@ +
+
{{lang_serviceRestart}}
+
+
{{lang_restarting}} syncdaemon
+
{{lang_restarting}} dmsd
+
{{lang_restarting}} ldadp
+ +
+
+ + diff --git a/modules/locations.inc.php b/modules/locations.inc.php deleted file mode 100644 index d4d0c85f..00000000 --- a/modules/locations.inc.php +++ /dev/null @@ -1,348 +0,0 @@ -action = Request::post('action'); - if ($this->action === 'updatelocation') { - $this->updateLocation(); - } elseif ($this->action === 'addlocations') { - $this->addLocations(); - } - } - - private function addLocations() - { - $names = Request::post('newlocation', false); - $parents = Request::post('newparent', false); - if (!is_array($names) || !is_array($parents)) { - Message::addError('empty-field'); - Util::redirect('?do=Locations'); - } - $locs = Location::getLocations(); - $count = 0; - foreach ($names as $idx => $name) { - $name = trim($name); - if (empty($name)) continue; - $parent = isset($parents[$idx]) ? (int)$parents[$idx] : 0; - if ($parent !== 0) { - $ok = false; - foreach ($locs as $loc) { - if ($loc['locationid'] == $parent) { - $ok = true; - } - } - if (!$ok) { - Message::addWarning('value-invalid', 'parentlocationid', $parent); - continue; - } - } - Database::exec("INSERT INTO location (parentlocationid, locationname)" - . " VALUES (:parent, :name)", array( - 'parent' => $parent, - 'name' => $name - )); - $count++; - } - Message::addSuccess('added-x-entries', $count); - Util::redirect('?do=Locations'); - } - - private function updateLocation() - { - $locationId = Request::post('locationid', false, 'integer'); - $del = Request::post('deletelocation', false, 'integer'); - if ($locationId === false) { - Message::addError('parameter-missing', 'locationid'); - Util::redirect('?do=Locations'); - } - $location = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location' - . ' WHERE locationid = :lid', array('lid' => $locationId)); - if ($location === false) { - Message::addError('value-invalid', 'locationid', $locationId); - Util::redirect('?do=Locations'); - } - // Delete location? - if ($locationId === $del) { - $this->deleteLocation($location); - } - // Update subnets - $this->updateLocationSubnets($location); - // Insert subnets - $this->addNewLocationSubnets($location); // TODO - // Update location! - $this->updateLocationData($location); - Util::redirect('?do=Locations'); - } - - private function deleteLocation($location) - { - $locationId = (int)$location['locationid']; - $ids = $locationId; - if (Request::post('recursive', false) === 'on') { - $rows = Location::queryLocations(); - $rows = Location::buildTree($rows, $locationId); - $rows = Location::extractIds($rows); - if (!empty($rows)) { - $ids .= ',' . implode(',', $rows); - } - } - $subs = Database::exec("DELETE FROM subnet WHERE locationid IN ($ids)"); - $locs = Database::exec("DELETE FROM location WHERE locationid IN ($ids)"); - Database::exec('UPDATE location SET parentlocationid = :newparent WHERE parentlocationid = :oldparent', array( - 'newparent' => $location['parentlocationid'], - 'oldparent' => $location['locationid'] - )); - Message::addSuccess('location-deleted', $locs, $subs); - Util::redirect('?do=Locations'); - } - - private function updateLocationData($location) - { - $locationId = (int)$location['locationid']; - $newParent = Request::post('parentlocationid', false, 'integer'); - $newName = Request::post('locationname', false, 'string'); - if ($newName === false || preg_match('/^\s*$/', $newName)) { - if ($newName !== false) { - Message::addWarning('value-invalid', 'location name', $newName); - } - $newName = $location['locationname']; - } - if ($newParent === false) { - $newParent = $location['parentlocationid']; - } else if ($newParent !== 0) { - $rows = Location::queryLocations(); - $all = Location::extractIds(Location::buildTree($rows)); - if (!in_array($newParent, $all) || $newParent === $locationId) { - Message::addWarning('value-invalid', 'parent', $newParent); - $newParent = $location['parentlocationid']; - } else { - $rows = Location::extractIds(Location::buildTree($rows, $locationId)); - if (in_array($newParent, $rows)) { - Message::addWarning('value-invalid', 'parent', $newParent); - $newParent = $location['parentlocationid']; - } - } - } - $ret = Database::exec('UPDATE location SET parentlocationid = :parent, locationname = :name' - . ' WHERE locationid = :lid', array( - 'lid' => $locationId, - 'parent' => $newParent, - 'name' => $newName - )); - if ($ret > 0) { - Message::addSuccess('location-updated', $newName); - } - } - - private function updateLocationSubnets($location) - { - $locationId = (int)$location['locationid']; - // Deletion first - $dels = Request::post('deletesubnet', false); - if (is_array($dels)) { - $count = 0; - $stmt = Database::prepare('DELETE FROM subnet WHERE subnetid = :id'); - foreach ($dels as $key => $value) { - if (!is_numeric($key) || $value !== 'on') continue; - if ($stmt->execute(array('id' => $key))) { - $count += $stmt->rowCount(); - } - } - if ($count > 0) { - Message::addInfo('subnets-deleted', $count); - } - } - // Now actual updates - // TODO: Warn on mismatch/overlap (should lie entirely in parent's subnet, not overlap with others) - $starts = Request::post('startaddr', false); - $ends = Request::post('endaddr', false); - if (!is_array($starts) || !is_array($ends)) { - return; - } - $count = 0; - $stmt = Database::prepare('UPDATE subnet SET startaddr = :start, endaddr = :end' - . ' WHERE subnetid = :id'); - foreach ($starts as $key => $start) { - if (!isset($ends[$key]) || !is_numeric($key)) continue; - $end = $ends[$key]; - list($startLong, $endLong) = $this->rangeToLong($start, $end); - if ($startLong === false) { - Message::addWarning('value-invalid', 'start addr', $start); - } - if ($endLong === false) { - Message::addWarning('value-invalid', 'end addr', $start); - } - if ($startLong === false || $endLong === false) continue; - if ($startLong > $endLong) { - Message::addWarning('value-invalid', 'range', $start . ' - ' . $end); - continue; - } - if ($stmt->execute(array('id' => $key, 'start' => $startLong, 'end' => $endLong))) { - $count += $stmt->rowCount(); - } - } - if ($count > 0) { - Message::addInfo('subnets-updated', $count); - } - } - - private function addNewLocationSubnets($location) - { - $locationId = (int)$location['locationid']; - $starts = Request::post('newstartaddr', false); - $ends = Request::post('newendaddr', false); - if (!is_array($starts) || !is_array($ends)) { - return; - } - $count = 0; - $stmt = Database::prepare('INSERT INTO subnet SET startaddr = :start, endaddr = :end, locationid = :location'); - foreach ($starts as $key => $start) { - if (!isset($ends[$key]) || !is_numeric($key)) continue; - $end = $ends[$key]; - list($startLong, $endLong) = $this->rangeToLong($start, $end); - if ($startLong === false) { - Message::addWarning('value-invalid', 'new start addr', $start); - } - if ($endLong === false) { - Message::addWarning('value-invalid', 'new end addr', $start); - } - if ($startLong === false || $endLong === false) continue; - if ($startLong > $endLong) { - Message::addWarning('value-invalid', 'range', $start . ' - ' . $end); - continue; - } - if ($stmt->execute(array('location' => $locationId, 'start' => $startLong, 'end' => $endLong))) { - $count += $stmt->rowCount(); - } - } - if ($count > 0) { - Message::addInfo('subnets-created', $count); - } - } - - /* - * Rendering normal pages - */ - - protected function doRender() - { - //Render::setTitle(Dictionary::translate('lang_titleBackup')); - $getAction = Request::get('action'); - if (empty($getAction)) { - // Until we have a main landing page? - Util::redirect('?do=Locations&action=showlocations'); - } - if ($getAction === 'showsubnets') { - $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr, locationid FROM subnet"); - $rows = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $row['startaddr'] = long2ip($row['startaddr']); - $row['endaddr'] = long2ip($row['endaddr']); - $row['locations'] = Location::getLocations($row['locationid']); - $rows[] = $row; - } - Render::addTemplate('locations/subnets', array('list' => $rows)); - } elseif ($getAction === 'showlocations') { - $locs = Location::getLocations(); - Render::addTemplate('locations/locations', array('list' => $locs)); - } - } - - /* - * Ajax - */ - - protected function doAjax() - { - User::load(); - if (!User::isLoggedIn()) { - die('Unauthorized'); - } - $action = Request::any('action'); - if ($action === 'showlocation') { - $this->ajaxShowLocation(); - } - } - - private function ajaxShowLocation() - { - $locationId = Request::any('locationid', 0, 'integer'); - $loc = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location WHERE locationid = :lid', - array('lid' => $locationId)); - if ($loc === false) { - die('Unknown locationid'); - } - $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr FROM subnet WHERE locationid = :lid", - array('lid' => $locationId)); - $rows = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $row['startaddr'] = long2ip($row['startaddr']); - $row['endaddr'] = long2ip($row['endaddr']); - $rows[] = $row; - } - $data = array( - 'locationid' => $loc['locationid'], - 'locationname' => $loc['locationname'], - 'list' => $rows, - 'parents' => Location::getLocations($loc['parentlocationid'], $locationId, true) - ); - // if (moduleEnabled(DOZMOD) { - $lectures = Database::queryFirst('SELECT Count(*) AS cnt FROM sat.lecture l ' - . ' INNER JOIN sat.lecture_x_location ll ON (l.lectureid = ll.lectureid AND ll.locationid = :lid)', - array('lid' => $locationId)); - $data['lectures'] = $lectures['cnt']; - // } - // Get clients matching this location's subnet(s) - $mres = Database::simpleQuery("SELECT lastseen, logintime FROM machine" - . " INNER JOIN subnet ON (INET_ATON(machine.clientip) BETWEEN startaddr AND endaddr)" - . " WHERE subnet.locationid = :lid OR machine.locationid = :lid", array('lid' => $locationId)); - $count = $online = $used = 0; - $DL = time() - 605; - while ($row = $mres->fetch(PDO::FETCH_ASSOC)) { - $count++; - if ($row['lastseen'] > $DL) { - $online++; - if ($row['logintime'] != 0) { - $used++; - } - } - } - $data['machines'] = $count; - $data['machines_online'] = $online; - $data['machines_used'] = $used; - $data['used_percent'] = round(100 * $used / $online); - echo Render::parse('locations/location-subnets', $data); - } - - /* - * Helpers - */ - - private function rangeToLong($start, $end) - { - $startLong = ip2long($start); - $endLong = ip2long($end); - if ($startLong !== false) { - $startLong = sprintf("%u", $startLong); - } - if ($endLong !== false) { - $endLong = sprintf("%u", $endLong); - } - return array($startLong, $endLong); - } - -} diff --git a/modules/locations/config.json b/modules/locations/config.json new file mode 100644 index 00000000..4e7fa5fb --- /dev/null +++ b/modules/locations/config.json @@ -0,0 +1,4 @@ +{ + "category":"content", + "enabled":"true" +} diff --git a/modules/locations/module.inc.php b/modules/locations/module.inc.php new file mode 100644 index 00000000..60af719b --- /dev/null +++ b/modules/locations/module.inc.php @@ -0,0 +1,348 @@ +action = Request::post('action'); + if ($this->action === 'updatelocation') { + $this->updateLocation(); + } elseif ($this->action === 'addlocations') { + $this->addLocations(); + } + } + + private function addLocations() + { + $names = Request::post('newlocation', false); + $parents = Request::post('newparent', false); + if (!is_array($names) || !is_array($parents)) { + Message::addError('empty-field'); + Util::redirect('?do=Locations'); + } + $locs = Location::getLocations(); + $count = 0; + foreach ($names as $idx => $name) { + $name = trim($name); + if (empty($name)) continue; + $parent = isset($parents[$idx]) ? (int)$parents[$idx] : 0; + if ($parent !== 0) { + $ok = false; + foreach ($locs as $loc) { + if ($loc['locationid'] == $parent) { + $ok = true; + } + } + if (!$ok) { + Message::addWarning('value-invalid', 'parentlocationid', $parent); + continue; + } + } + Database::exec("INSERT INTO location (parentlocationid, locationname)" + . " VALUES (:parent, :name)", array( + 'parent' => $parent, + 'name' => $name + )); + $count++; + } + Message::addSuccess('added-x-entries', $count); + Util::redirect('?do=Locations'); + } + + private function updateLocation() + { + $locationId = Request::post('locationid', false, 'integer'); + $del = Request::post('deletelocation', false, 'integer'); + if ($locationId === false) { + Message::addError('parameter-missing', 'locationid'); + Util::redirect('?do=Locations'); + } + $location = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location' + . ' WHERE locationid = :lid', array('lid' => $locationId)); + if ($location === false) { + Message::addError('value-invalid', 'locationid', $locationId); + Util::redirect('?do=Locations'); + } + // Delete location? + if ($locationId === $del) { + $this->deleteLocation($location); + } + // Update subnets + $this->updateLocationSubnets($location); + // Insert subnets + $this->addNewLocationSubnets($location); // TODO + // Update location! + $this->updateLocationData($location); + Util::redirect('?do=Locations'); + } + + private function deleteLocation($location) + { + $locationId = (int)$location['locationid']; + $ids = $locationId; + if (Request::post('recursive', false) === 'on') { + $rows = Location::queryLocations(); + $rows = Location::buildTree($rows, $locationId); + $rows = Location::extractIds($rows); + if (!empty($rows)) { + $ids .= ',' . implode(',', $rows); + } + } + $subs = Database::exec("DELETE FROM subnet WHERE locationid IN ($ids)"); + $locs = Database::exec("DELETE FROM location WHERE locationid IN ($ids)"); + Database::exec('UPDATE location SET parentlocationid = :newparent WHERE parentlocationid = :oldparent', array( + 'newparent' => $location['parentlocationid'], + 'oldparent' => $location['locationid'] + )); + Message::addSuccess('location-deleted', $locs, $subs); + Util::redirect('?do=Locations'); + } + + private function updateLocationData($location) + { + $locationId = (int)$location['locationid']; + $newParent = Request::post('parentlocationid', false, 'integer'); + $newName = Request::post('locationname', false, 'string'); + if ($newName === false || preg_match('/^\s*$/', $newName)) { + if ($newName !== false) { + Message::addWarning('value-invalid', 'location name', $newName); + } + $newName = $location['locationname']; + } + if ($newParent === false) { + $newParent = $location['parentlocationid']; + } else if ($newParent !== 0) { + $rows = Location::queryLocations(); + $all = Location::extractIds(Location::buildTree($rows)); + if (!in_array($newParent, $all) || $newParent === $locationId) { + Message::addWarning('value-invalid', 'parent', $newParent); + $newParent = $location['parentlocationid']; + } else { + $rows = Location::extractIds(Location::buildTree($rows, $locationId)); + if (in_array($newParent, $rows)) { + Message::addWarning('value-invalid', 'parent', $newParent); + $newParent = $location['parentlocationid']; + } + } + } + $ret = Database::exec('UPDATE location SET parentlocationid = :parent, locationname = :name' + . ' WHERE locationid = :lid', array( + 'lid' => $locationId, + 'parent' => $newParent, + 'name' => $newName + )); + if ($ret > 0) { + Message::addSuccess('location-updated', $newName); + } + } + + private function updateLocationSubnets($location) + { + $locationId = (int)$location['locationid']; + // Deletion first + $dels = Request::post('deletesubnet', false); + if (is_array($dels)) { + $count = 0; + $stmt = Database::prepare('DELETE FROM subnet WHERE subnetid = :id'); + foreach ($dels as $key => $value) { + if (!is_numeric($key) || $value !== 'on') continue; + if ($stmt->execute(array('id' => $key))) { + $count += $stmt->rowCount(); + } + } + if ($count > 0) { + Message::addInfo('subnets-deleted', $count); + } + } + // Now actual updates + // TODO: Warn on mismatch/overlap (should lie entirely in parent's subnet, not overlap with others) + $starts = Request::post('startaddr', false); + $ends = Request::post('endaddr', false); + if (!is_array($starts) || !is_array($ends)) { + return; + } + $count = 0; + $stmt = Database::prepare('UPDATE subnet SET startaddr = :start, endaddr = :end' + . ' WHERE subnetid = :id'); + foreach ($starts as $key => $start) { + if (!isset($ends[$key]) || !is_numeric($key)) continue; + $end = $ends[$key]; + list($startLong, $endLong) = $this->rangeToLong($start, $end); + if ($startLong === false) { + Message::addWarning('value-invalid', 'start addr', $start); + } + if ($endLong === false) { + Message::addWarning('value-invalid', 'end addr', $start); + } + if ($startLong === false || $endLong === false) continue; + if ($startLong > $endLong) { + Message::addWarning('value-invalid', 'range', $start . ' - ' . $end); + continue; + } + if ($stmt->execute(array('id' => $key, 'start' => $startLong, 'end' => $endLong))) { + $count += $stmt->rowCount(); + } + } + if ($count > 0) { + Message::addInfo('subnets-updated', $count); + } + } + + private function addNewLocationSubnets($location) + { + $locationId = (int)$location['locationid']; + $starts = Request::post('newstartaddr', false); + $ends = Request::post('newendaddr', false); + if (!is_array($starts) || !is_array($ends)) { + return; + } + $count = 0; + $stmt = Database::prepare('INSERT INTO subnet SET startaddr = :start, endaddr = :end, locationid = :location'); + foreach ($starts as $key => $start) { + if (!isset($ends[$key]) || !is_numeric($key)) continue; + $end = $ends[$key]; + list($startLong, $endLong) = $this->rangeToLong($start, $end); + if ($startLong === false) { + Message::addWarning('value-invalid', 'new start addr', $start); + } + if ($endLong === false) { + Message::addWarning('value-invalid', 'new end addr', $start); + } + if ($startLong === false || $endLong === false) continue; + if ($startLong > $endLong) { + Message::addWarning('value-invalid', 'range', $start . ' - ' . $end); + continue; + } + if ($stmt->execute(array('location' => $locationId, 'start' => $startLong, 'end' => $endLong))) { + $count += $stmt->rowCount(); + } + } + if ($count > 0) { + Message::addInfo('subnets-created', $count); + } + } + + /* + * Rendering normal pages + */ + + protected function doRender() + { + //Render::setTitle(Dictionary::translate('lang_titleBackup')); + $getAction = Request::get('action'); + if (empty($getAction)) { + // Until we have a main landing page? + Util::redirect('?do=Locations&action=showlocations'); + } + if ($getAction === 'showsubnets') { + $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr, locationid FROM subnet"); + $rows = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['startaddr'] = long2ip($row['startaddr']); + $row['endaddr'] = long2ip($row['endaddr']); + $row['locations'] = Location::getLocations($row['locationid']); + $rows[] = $row; + } + Render::addTemplate('subnets', array('list' => $rows)); + } elseif ($getAction === 'showlocations') { + $locs = Location::getLocations(); + Render::addTemplate('locations', array('list' => $locs)); + } + } + + /* + * Ajax + */ + + protected function doAjax() + { + User::load(); + if (!User::isLoggedIn()) { + die('Unauthorized'); + } + $action = Request::any('action'); + if ($action === 'showlocation') { + $this->ajaxShowLocation(); + } + } + + private function ajaxShowLocation() + { + $locationId = Request::any('locationid', 0, 'integer'); + $loc = Database::queryFirst('SELECT locationid, parentlocationid, locationname FROM location WHERE locationid = :lid', + array('lid' => $locationId)); + if ($loc === false) { + die('Unknown locationid'); + } + $res = Database::simpleQuery("SELECT subnetid, startaddr, endaddr FROM subnet WHERE locationid = :lid", + array('lid' => $locationId)); + $rows = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['startaddr'] = long2ip($row['startaddr']); + $row['endaddr'] = long2ip($row['endaddr']); + $rows[] = $row; + } + $data = array( + 'locationid' => $loc['locationid'], + 'locationname' => $loc['locationname'], + 'list' => $rows, + 'parents' => Location::getLocations($loc['parentlocationid'], $locationId, true) + ); + // if (moduleEnabled(DOZMOD) { + $lectures = Database::queryFirst('SELECT Count(*) AS cnt FROM sat.lecture l ' + . ' INNER JOIN sat.lecture_x_location ll ON (l.lectureid = ll.lectureid AND ll.locationid = :lid)', + array('lid' => $locationId)); + $data['lectures'] = $lectures['cnt']; + // } + // Get clients matching this location's subnet(s) + $mres = Database::simpleQuery("SELECT lastseen, logintime FROM machine" + . " INNER JOIN subnet ON (INET_ATON(machine.clientip) BETWEEN startaddr AND endaddr)" + . " WHERE subnet.locationid = :lid OR machine.locationid = :lid", array('lid' => $locationId)); + $count = $online = $used = 0; + $DL = time() - 605; + while ($row = $mres->fetch(PDO::FETCH_ASSOC)) { + $count++; + if ($row['lastseen'] > $DL) { + $online++; + if ($row['logintime'] != 0) { + $used++; + } + } + } + $data['machines'] = $count; + $data['machines_online'] = $online; + $data['machines_used'] = $used; + $data['used_percent'] = round(100 * $used / $online); + echo Render::parse('location-subnets', $data); + } + + /* + * Helpers + */ + + private function rangeToLong($start, $end) + { + $startLong = ip2long($start); + $endLong = ip2long($end); + if ($startLong !== false) { + $startLong = sprintf("%u", $startLong); + } + if ($endLong !== false) { + $endLong = sprintf("%u", $endLong); + } + return array($startLong, $endLong); + } + +} diff --git a/modules/locations/templates/location-subnets.html b/modules/locations/templates/location-subnets.html new file mode 100644 index 00000000..76b7442a --- /dev/null +++ b/modules/locations/templates/location-subnets.html @@ -0,0 +1,73 @@ +
+
{{lang_locationSettings}}
+
+ + + +
+ +
+
+
+
+ {{lang_name}} + +
+
+
+
+ {{lang_parentLocation}} + +
+
+
+
+
+ + +
+
+
+
+
{{lang_assignedSubnets}}
+
{{lang_assignSubnetExplanation}}
+ + + + + + + + {{#list}} + + + + + + + {{/list}} + + + +
#{{lang_startAddress}}{{lang_endAddress}}
{{subnetid}}
+ + + + +
+
+
+
{{lang_locationInfo}}
+
+ {{lang_referencingLectures}}: {{lectures}} +
+
+ {{lang_matchingMachines}}: {{machines}} / {{machines_online}} / {{machines_used}} ({{used_percent}}%) +
+
\ No newline at end of file diff --git a/modules/locations/templates/locations.html b/modules/locations/templates/locations.html new file mode 100644 index 00000000..76c8f97c --- /dev/null +++ b/modules/locations/templates/locations.html @@ -0,0 +1,96 @@ +
+
+ {{lang_thisListBySubnet}} +
+

{{lang_locationsMainHeading}}

+ + + + + + + {{#list}} + + + + + + {{/list}} +
#{{lang_locationName}}
{{locationid}}
{{locationname}}
+ {{lang_edit}} +
+
+ + + + + + + + +
+ + + +
+
+
+ diff --git a/modules/locations/templates/subnets.html b/modules/locations/templates/subnets.html new file mode 100644 index 00000000..2294f42b --- /dev/null +++ b/modules/locations/templates/subnets.html @@ -0,0 +1,35 @@ +
+
+ {{lang_thisListByLocation}} +
+

{{lang_listOfSubnets}}

+
+ + + + + + + + + + {{#list}} + + + + + + + {{/list}} +
#{{lang_startAddress}}{{lang_endAddress}}{{lang_location}}
{{subnetid}} + +
+
+ +
+
+
diff --git a/modules/main.inc.php b/modules/main.inc.php deleted file mode 100644 index 9af1afef..00000000 --- a/modules/main.inc.php +++ /dev/null @@ -1,54 +0,0 @@ -sysconfig = !file_exists(CONFIG_HTTP_DIR . '/default/config.tgz'); - $this->minilinux = !file_exists(CONFIG_HTTP_DIR . '/default/kernel') || !file_exists(CONFIG_HTTP_DIR . '/default/initramfs-stage31') || !file_exists(CONFIG_HTTP_DIR . '/default/stage32.sqfs'); - $this->vmstore = !is_array(Property::getVmStoreConfig()); - $this->ipxe = !preg_match('/^\d+\.\d+\.\d+\.\d+$/', Property::getServerIp()); - Property::setNeedsSetup(($this->sysconfig || $this->minilinux || $this->vmstore || $this->ipxe) ? 1 : 0); - $res = Database::queryFirst("SELECT Count(*) AS cnt FROM sat.imageversion WHERE deletestate = 'SHOULD_DELETE'"); - $this->delPending = isset($res['cnt']) ? $res['cnt'] : 0; - } - } - - protected function doRender() - { - // Render::setTitle('abc'); - - if (!User::isLoggedIn()) { - Render::addTemplate('page-main-guest', array( - 'register' => (Database::queryFirst('SELECT userid FROM user LIMIT 1') === false) - )); - return; - } - // Logged in here - - Render::addTemplate('page-main', array( - 'user' => User::getName(), - 'sysconfig' => $this->sysconfig, - 'minilinux' => $this->minilinux, - 'vmstore' => $this->vmstore, - 'ipxe' => $this->ipxe, - 'delpending' => $this->delPending - )); - } - - protected function doAjax() - { - User::isLoggedIn(); - die('Status: DB running'); - } - -} diff --git a/modules/main/config.json b/modules/main/config.json new file mode 100644 index 00000000..d5da4cc8 --- /dev/null +++ b/modules/main/config.json @@ -0,0 +1,4 @@ +{ + "category":"hidden", + "enabled":"true" +} diff --git a/modules/main/module.inc.php b/modules/main/module.inc.php new file mode 100644 index 00000000..369d4b54 --- /dev/null +++ b/modules/main/module.inc.php @@ -0,0 +1,64 @@ +sysconfig = !file_exists(CONFIG_HTTP_DIR . '/default/config.tgz'); + $this->minilinux = !file_exists(CONFIG_HTTP_DIR . '/default/kernel') || !file_exists(CONFIG_HTTP_DIR . '/default/initramfs-stage31') || !file_exists(CONFIG_HTTP_DIR . '/default/stage32.sqfs'); + $this->vmstore = !is_array(Property::getVmStoreConfig()); + $this->ipxe = !preg_match('/^\d+\.\d+\.\d+\.\d+$/', Property::getServerIp()); + Property::setNeedsSetup(($this->sysconfig || $this->minilinux || $this->vmstore || $this->ipxe) ? 1 : 0); + $res = Database::queryFirst("SELECT Count(*) AS cnt FROM sat.imageversion WHERE deletestate = 'SHOULD_DELETE'", array(), true); + $this->delPending = isset($res['cnt']) ? $res['cnt'] : 0; + } + } + + protected function doRender() + { + // Render::setTitle('abc'); + + if (!User::isLoggedIn()) { + Render::addTemplate('page-main-guest', array( + 'register' => (Database::queryFirst('SELECT userid FROM user LIMIT 1') === false) + )); + return; + } + // Logged in here + + // Load news + $lines = array(); + $paginate = new Paginate("SELECT newsid, dateline, title, content FROM news ORDER BY dateline DESC", 10); + $res = $paginate->exec(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if(count($lines) >= 3) break; + $lines[] = $row; + } + + Render::addTemplate('page-main', array( + 'user' => User::getName(), + 'sysconfig' => $this->sysconfig, + 'minilinux' => $this->minilinux, + 'vmstore' => $this->vmstore, + 'ipxe' => $this->ipxe, + 'delpending' => $this->delPending, + 'news' => $lines + )); + } + + protected function doAjax() + { + User::isLoggedIn(); + die('Status: DB running'); + } + +} diff --git a/modules/main/templates/dialog-generic.html b/modules/main/templates/dialog-generic.html new file mode 100644 index 00000000..5face8ce --- /dev/null +++ b/modules/main/templates/dialog-generic.html @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/modules/main/templates/footer.html b/modules/main/templates/footer.html new file mode 100644 index 00000000..8cf71a5c --- /dev/null +++ b/modules/main/templates/footer.html @@ -0,0 +1,2 @@ +
+ \ No newline at end of file diff --git a/modules/main/templates/main-menu.html b/modules/main/templates/main-menu.html new file mode 100644 index 00000000..c4534c2f --- /dev/null +++ b/modules/main/templates/main-menu.html @@ -0,0 +1,51 @@ + + + diff --git a/modules/main/templates/main-menu.html.topnavbar b/modules/main/templates/main-menu.html.topnavbar new file mode 100644 index 00000000..8da8edd0 --- /dev/null +++ b/modules/main/templates/main-menu.html.topnavbar @@ -0,0 +1,92 @@ + + + diff --git a/modules/main/templates/messagebox-error.html b/modules/main/templates/messagebox-error.html new file mode 100644 index 00000000..873716c9 --- /dev/null +++ b/modules/main/templates/messagebox-error.html @@ -0,0 +1 @@ +
{{{message}}}
diff --git a/modules/main/templates/messagebox-info.html b/modules/main/templates/messagebox-info.html new file mode 100644 index 00000000..eb9d518a --- /dev/null +++ b/modules/main/templates/messagebox-info.html @@ -0,0 +1 @@ +
{{{message}}}
diff --git a/modules/main/templates/messagebox-success.html b/modules/main/templates/messagebox-success.html new file mode 100644 index 00000000..93674d69 --- /dev/null +++ b/modules/main/templates/messagebox-success.html @@ -0,0 +1 @@ +
{{{message}}}
diff --git a/modules/main/templates/messagebox-warning.html b/modules/main/templates/messagebox-warning.html new file mode 100644 index 00000000..b02e2e8a --- /dev/null +++ b/modules/main/templates/messagebox-warning.html @@ -0,0 +1 @@ +
{{{message}}}
diff --git a/modules/main/templates/page-login.html b/modules/main/templates/page-login.html new file mode 100644 index 00000000..247e9a55 --- /dev/null +++ b/modules/main/templates/page-login.html @@ -0,0 +1,11 @@ +
+

{{lang_enter}}

+ + + + + {{lang_register}} + +
\ No newline at end of file diff --git a/modules/main/templates/page-main-guest.html b/modules/main/templates/page-main-guest.html new file mode 100644 index 00000000..28b0d04c --- /dev/null +++ b/modules/main/templates/page-main-guest.html @@ -0,0 +1,15 @@ +
+
+

{{lang_welcome}}

+

{{lang_introGuest}}

+ {{#register}} + + {{/register}} +

{{lang_login}} »

+
+
diff --git a/modules/main/templates/page-main.html b/modules/main/templates/page-main.html new file mode 100644 index 00000000..39e4e74e --- /dev/null +++ b/modules/main/templates/page-main.html @@ -0,0 +1,36 @@ +
+

{{lang_welcome}}, {{user}}

+

{{lang_intro}}

+ +
+ diff --git a/modules/main/templates/page-minilinux.html b/modules/main/templates/page-minilinux.html new file mode 100644 index 00000000..dc13e6b0 --- /dev/null +++ b/modules/main/templates/page-minilinux.html @@ -0,0 +1,14 @@ +
+
{{lang_listObtained}}
+
+ + \ No newline at end of file diff --git a/modules/main/templates/page-news.html b/modules/main/templates/page-news.html new file mode 100644 index 00000000..8e400498 --- /dev/null +++ b/modules/main/templates/page-news.html @@ -0,0 +1,57 @@ +
+
+ {{lang_editNews}} +
+
+

{{lang_newsIntro}}

+
+
+ + +
+
+ + +
+

{{lang_latestUpdate}}: {{latestDate}}

+ + +
+
+
+ +
+
+ {{lang_newsOld}} +
+
+
+
+ + + + + + + + + + + + {{#list}} + + + + + + + {{/list}} + +
{{lang_date}}{{lang_title}}{{lang_content}}
{{date}}{{title}}{{content}} + {{lang_show}} + +
+
+
+
+
diff --git a/modules/main/templates/page-syslog.html b/modules/main/templates/page-syslog.html new file mode 100644 index 00000000..98e94291 --- /dev/null +++ b/modules/main/templates/page-syslog.html @@ -0,0 +1,58 @@ +

{{lang_clientLog}}

+
+ +
+ {{lang_filter}} + + + {{lang_not}} + + + + +
+
+{{{pagenav}}} + + + + + + + + + + {{#list}} + + + + + + + + {{/list}} + +
{{lang_when}}{{lang_client}}{{lang_event}}{{lang_details}}
{{date}}{{clientip}}{{description}}{{#extra}} + » + + {{/extra}}
+{{{pagenav}}} + + + + diff --git a/modules/main/templates/page-vmstore.html b/modules/main/templates/page-vmstore.html new file mode 100644 index 00000000..fe2c5225 --- /dev/null +++ b/modules/main/templates/page-vmstore.html @@ -0,0 +1,111 @@ +
+ + + + +
+
+ {{lang_vmLocation}} +
+
+

{{lang_vmLocationChoose}}

+
+
+ {{lang_intern}} +
+
+ {{lang_noAdditionalInformation}} +
+
+
+
+ NFS + +
+
+ + +
+
+
+
+ CIFS +
+
+ + +
+ +
+ + {{lang_username}} + + + + {{lang_password}} + + +
+
+ +
+ + {{lang_username}} + + + + {{lang_password}} + + +
+
+
+ +
+
+
+ + + + diff --git a/modules/main/templates/pagenav.html b/modules/main/templates/pagenav.html new file mode 100644 index 00000000..93194999 --- /dev/null +++ b/modules/main/templates/pagenav.html @@ -0,0 +1,16 @@ + +
\ No newline at end of file diff --git a/modules/main/templates/tm-callback-trigger.html b/modules/main/templates/tm-callback-trigger.html new file mode 100644 index 00000000..cd03a1fe --- /dev/null +++ b/modules/main/templates/tm-callback-trigger.html @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/modules/minilinux.inc.php b/modules/minilinux.inc.php deleted file mode 100644 index 0853c0db..00000000 --- a/modules/minilinux.inc.php +++ /dev/null @@ -1,128 +0,0 @@ - '?do=MiniLinux&async=true&action=list' - )); - Render::addFooter(''); - } - - protected function doAjax() - { - $data = Property::getVersionCheckInformation(); - if (!is_array($data) || !isset($data['systems'])) { - echo Render::parse('messagebox-error', array( - 'message' => 'Failed to retrieve the list: ' . print_r($data, true) - )); - return; - } - $action = Request::any('action'); - $selectedVersion = (int)Request::any('version', 0); - switch ($action) { - case 'list': - $count = 0; - foreach ($data['systems'] as &$system) { - // Get latest version, build simple array of all version numbers - $versionNumbers = array(); - $selected = false; - foreach ($system['versions'] as $version) { - if (!is_numeric($version['version']) || $version['version'] < 1) - continue; - if ($selectedVersion === 0 && ($selected === false || $selected['version'] < $version['version'])) - $selected = $version; - elseif ($version['version'] == $selectedVersion) - $selected = $version; - $versionNumbers[(int)$version['version']] = array( - 'version' => $version['version'] - ); - } - if ($selected === false) continue; // No versions for this system!? - ksort($versionNumbers); - // Mark latest version as selected - $versionNumbers[(int)$selected['version']]['selected'] = true; - // Add status information to system and its files - foreach ($selected['files'] as &$file) { - $file['uid'] = 'dlid' . $count++; - $local = CONFIG_HTTP_DIR . '/' . $system['id'] . '/' . $file['name']; - if (!file_exists($local) || filesize($local) !== $file['size'] || filemtime($local) < $file['mtime']) { - $file['fileChanged'] = true; - $system['systemChanged'] = true; - } - $taskId = Property::getDownloadTask($file['md5']); - if ($taskId !== false) { - $task = Taskmanager::status($taskId); - if (isset($task['data']['progress'])) { - $file['download'] = Render::parse('minilinux/download', array( - 'task' => $taskId, - 'name' => $file['name'] - )); - } - } - } - unset($system['versions']); - $system['files'] = $selected['files']; - $system['version'] = $selected['version']; - } - $data['versions'] = array_values($versionNumbers); - echo Render::parse('minilinux/filelist', $data); - return; - case 'download': - $id = Request::post('id'); - $name = Request::post('name'); - if (!$id || !$name || strpos("$id$name", '/') !== false) { - echo "Invalid download request"; - return; - } - $file = false; - $gpg = 'missing'; - foreach ($data['systems'] as &$system) { - if ($system['id'] !== $id) continue; - foreach ($system['versions'] as &$version) { - if ($version['version'] != $selectedVersion) continue; - foreach ($version['files'] as &$f) { - if ($f['name'] !== $name) continue; - $file = $f; - if (!empty($f['gpg'])) $gpg = $f['gpg']; - break; - } - } - } - if ($file === false) { - echo "Nonexistent system/file: $id / $name"; - return; - } - $task = Taskmanager::submit('DownloadFile', array( - 'url' => CONFIG_REMOTE_ML . '/' . $id . '/' . $selectedVersion . '/' . $name, - 'destination' => CONFIG_HTTP_DIR . '/' . $id . '/' . $name, - 'gpg' => $gpg - )); - if (!isset($task['id'])) { - echo 'Error launching download task: ' . $task['statusCode']; - return; - } - Property::setDownloadTask($file['md5'], $task['id']); - echo Render::parse('minilinux/download', array( - 'name' => $name, - 'task' => $task['id'] - )); - return; - } - } - -} diff --git a/modules/minilinux/config.json b/modules/minilinux/config.json new file mode 100644 index 00000000..93209f62 --- /dev/null +++ b/modules/minilinux/config.json @@ -0,0 +1,4 @@ +{ + "category":"settings", + "enabled":"true" +} diff --git a/modules/minilinux/module.inc.php b/modules/minilinux/module.inc.php new file mode 100644 index 00000000..91be456e --- /dev/null +++ b/modules/minilinux/module.inc.php @@ -0,0 +1,128 @@ + '?do=MiniLinux&async=true&action=list' + )); + Render::addFooter(''); + } + + protected function doAjax() + { + $data = Property::getVersionCheckInformation(); + if (!is_array($data) || !isset($data['systems'])) { + echo Render::parse('messagebox-error', array( + 'message' => 'Failed to retrieve the list: ' . print_r($data, true) + ),'main'); + return; + } + $action = Request::any('action'); + $selectedVersion = (int)Request::any('version', 0); + switch ($action) { + case 'list': + $count = 0; + foreach ($data['systems'] as &$system) { + // Get latest version, build simple array of all version numbers + $versionNumbers = array(); + $selected = false; + foreach ($system['versions'] as $version) { + if (!is_numeric($version['version']) || $version['version'] < 1) + continue; + if ($selectedVersion === 0 && ($selected === false || $selected['version'] < $version['version'])) + $selected = $version; + elseif ($version['version'] == $selectedVersion) + $selected = $version; + $versionNumbers[(int)$version['version']] = array( + 'version' => $version['version'] + ); + } + if ($selected === false) continue; // No versions for this system!? + ksort($versionNumbers); + // Mark latest version as selected + $versionNumbers[(int)$selected['version']]['selected'] = true; + // Add status information to system and its files + foreach ($selected['files'] as &$file) { + $file['uid'] = 'dlid' . $count++; + $local = CONFIG_HTTP_DIR . '/' . $system['id'] . '/' . $file['name']; + if (!file_exists($local) || filesize($local) !== $file['size'] || filemtime($local) < $file['mtime']) { + $file['fileChanged'] = true; + $system['systemChanged'] = true; + } + $taskId = Property::getDownloadTask($file['md5']); + if ($taskId !== false) { + $task = Taskmanager::status($taskId); + if (isset($task['data']['progress'])) { + $file['download'] = Render::parse('download', array( + 'task' => $taskId, + 'name' => $file['name'] + )); + } + } + } + unset($system['versions']); + $system['files'] = $selected['files']; + $system['version'] = $selected['version']; + } + $data['versions'] = array_values($versionNumbers); + echo Render::parse('filelist', $data); + return; + case 'download': + $id = Request::post('id'); + $name = Request::post('name'); + if (!$id || !$name || strpos("$id$name", '/') !== false) { + echo "Invalid download request"; + return; + } + $file = false; + $gpg = 'missing'; + foreach ($data['systems'] as &$system) { + if ($system['id'] !== $id) continue; + foreach ($system['versions'] as &$version) { + if ($version['version'] != $selectedVersion) continue; + foreach ($version['files'] as &$f) { + if ($f['name'] !== $name) continue; + $file = $f; + if (!empty($f['gpg'])) $gpg = $f['gpg']; + break; + } + } + } + if ($file === false) { + echo "Nonexistent system/file: $id / $name"; + return; + } + $task = Taskmanager::submit('DownloadFile', array( + 'url' => CONFIG_REMOTE_ML . '/' . $id . '/' . $selectedVersion . '/' . $name, + 'destination' => CONFIG_HTTP_DIR . '/' . $id . '/' . $name, + 'gpg' => $gpg + )); + if (!isset($task['id'])) { + echo 'Error launching download task: ' . $task['statusCode']; + return; + } + Property::setDownloadTask($file['md5'], $task['id']); + echo Render::parse('download', array( + 'name' => $name, + 'task' => $task['id'] + )); + return; + } + } + +} diff --git a/modules/minilinux/templates/download.html b/modules/minilinux/templates/download.html new file mode 100644 index 00000000..2e32df5a --- /dev/null +++ b/modules/minilinux/templates/download.html @@ -0,0 +1 @@ +
{{name}}
\ No newline at end of file diff --git a/modules/minilinux/templates/filelist.html b/modules/minilinux/templates/filelist.html new file mode 100644 index 00000000..ca94f4d0 --- /dev/null +++ b/modules/minilinux/templates/filelist.html @@ -0,0 +1,77 @@ + {{#systems}} +
+
+

{{title}}

+
+
+
+ {{lang_desiredVersion}} + +
+ {{#systemChanged}} +

+ {{lang_canUpdate1}} {{title}} {{lang_canUpdate2}} +

+

{{lang_update}}

+ {{/systemChanged}} + {{^systemChanged}} +

{{lang_systemUpdated}}

+ {{/systemChanged}} +
+

{{lang_filesInVersion}} {{version}}

+ +
+
+ {{/systems}} + {{^systems}} +
{{lang_configurationPackageNotFound}}
+ {{/systems}} + + diff --git a/modules/minilinux/templates/page-minilinux.html b/modules/minilinux/templates/page-minilinux.html new file mode 100644 index 00000000..007e1e1b --- /dev/null +++ b/modules/minilinux/templates/page-minilinux.html @@ -0,0 +1,28 @@ +
+
{{lang_listObtained}}
+ +
+ + \ No newline at end of file diff --git a/modules/news.inc.php b/modules/news.inc.php deleted file mode 100644 index d0aa32ea..00000000 --- a/modules/news.inc.php +++ /dev/null @@ -1,167 +0,0 @@ -newsId = false; - $this->newsTitle = false; - $this->newsContent = false; - $this->newsDate = false; - } elseif ($action === 'show') { - // show news - if (!$this->loadNews(Request::any('newsid'))) { - Message::addError('news-empty'); - } - } elseif ($action === 'save') { - // save to DB - if (!$this->saveNews()) { - // re-set the fields we got - Request::post('news-title') ? $this->newsTitle = Request::post('news-title') : $this->newsTitle = false; - Request::post('news-content') ? $this->newsContent = Request::post('news-content') : $this->newsContent = false; - } else { - Message::addSuccess('news-save-success'); - Util::redirect('?do=News'); - } - } elseif ($action === 'delete') { - // delete it - $this->delNews(Request::post('newsid')); - } else { - // unknown action, redirect user - Message::addError('invalid-action', $action); - Util::redirect('?do=News'); - } - } - - /** - * Implementation of the abstract doRender function - * - * Fetch the list of news from the database and paginate it. - * - */ - protected function doRender() - { - // fetch the list of the older news - $lines = array(); - $paginate = new Paginate("SELECT newsid, dateline, title, content FROM news ORDER BY dateline DESC", 10); - $res = $paginate->exec(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $row['date'] = date('d.m.Y H:i', $row['dateline']); - - if ($row['newsid'] == $this->newsId) $row['active'] = "active"; - $lines[] = $row; - } - $paginate->render('page-news', array( - 'token' => Session::get('token'), - 'latestDate' => ($this->newsDate ? date('d.m.Y H:i', $this->newsDate) : '--'), - 'latestContent' => $this->newsContent, - 'latestTitle' => $this->newsTitle, - 'list' => $lines )); - - } - /** - * Loads the news with the given ID into the form. - * - * @param int $newsId ID of the news to be shown. - * @return boolean true if loading that news worked - * - */ - private function loadNews($newsId) - { - // check to see if we need to request a specific newsid - if ($newsId !== false) { - $row = Database::queryFirst("SELECT newsid, title, content, dateline FROM news WHERE newsid = :newsid LIMIT 1", array( - 'newsid' => $newsId - )); - } else { - $row = Database::queryFirst("SELECT newsid, title, content, dateline FROM news ORDER BY dateline DESC LIMIT 1"); - } - - // fetch the news to be shown - if ($row !== false) { - $this->newsId = $row['newsid']; - $this->newsTitle = $row['title']; - $this->newsContent = $row['content']; - $this->newsDate = $row['dateline']; - } - return $row !== false; - } - - /** - * Save the given $newsTitle and $newsContent as POST'ed into the database. - * - */ - private function saveNews() - { - // check if news content were set by the user - $newsTitle = Request::post('news-title'); - $newsContent = Request::post('news-content'); - if ($newsContent !== '' && $newsTitle !== '') { - // we got title and content, save it to DB - Database::exec("INSERT INTO news (dateline, title, content) VALUES (:dateline, :title, :content)", array( - 'dateline' => time(), - 'title' => $newsTitle, - 'content' => $newsContent - )); - return true; - } else { - Message::addError('empty-field'); - return false; - } - } - - /** - * Delete the news entry with ID $newsId - * - * @param int $newsId ID of the entry to be deleted. - */ - private function delNews($newsId) - { - // sanity check: is newsId even numeric? - if (!is_numeric($newsId)) { - Message::addError('value-invalid', 'newsid', $newsId); - } else { - // check passed - do delete - Database::exec("DELETE FROM news WHERE newsid = :newsid LIMIT 1", array( - 'newsid' => $newsId - )); - Message::addSuccess('news-del-success'); - } - Util::redirect('?do=News'); - } - -} diff --git a/modules/news/config.json b/modules/news/config.json new file mode 100644 index 00000000..4e7fa5fb --- /dev/null +++ b/modules/news/config.json @@ -0,0 +1,4 @@ +{ + "category":"content", + "enabled":"true" +} diff --git a/modules/news/module.inc.php b/modules/news/module.inc.php new file mode 100644 index 00000000..9bbadc4f --- /dev/null +++ b/modules/news/module.inc.php @@ -0,0 +1,167 @@ +newsId = false; + $this->newsTitle = false; + $this->newsContent = false; + $this->newsDate = false; + } elseif ($action === 'show') { + // show news + if (!$this->loadNews(Request::any('newsid'))) { + Message::addError('news-empty'); + } + } elseif ($action === 'save') { + // save to DB + if (!$this->saveNews()) { + // re-set the fields we got + Request::post('news-title') ? $this->newsTitle = Request::post('news-title') : $this->newsTitle = false; + Request::post('news-content') ? $this->newsContent = Request::post('news-content') : $this->newsContent = false; + } else { + Message::addSuccess('news-save-success'); + Util::redirect('?do=News'); + } + } elseif ($action === 'delete') { + // delete it + $this->delNews(Request::post('newsid')); + } else { + // unknown action, redirect user + Message::addError('invalid-action', $action); + Util::redirect('?do=News'); + } + } + + /** + * Implementation of the abstract doRender function + * + * Fetch the list of news from the database and paginate it. + * + */ + protected function doRender() + { + // fetch the list of the older news + $lines = array(); + $paginate = new Paginate("SELECT newsid, dateline, title, content FROM news ORDER BY dateline DESC", 10); + $res = $paginate->exec(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['date'] = date('d.m.Y H:i', $row['dateline']); + + if ($row['newsid'] == $this->newsId) $row['active'] = "active"; + $lines[] = $row; + } + $paginate->render('page-news', array( + 'token' => Session::get('token'), + 'latestDate' => ($this->newsDate ? date('d.m.Y H:i', $this->newsDate) : '--'), + 'latestContent' => $this->newsContent, + 'latestTitle' => $this->newsTitle, + 'list' => $lines )); + + } + /** + * Loads the news with the given ID into the form. + * + * @param int $newsId ID of the news to be shown. + * @return boolean true if loading that news worked + * + */ + private function loadNews($newsId) + { + // check to see if we need to request a specific newsid + if ($newsId !== false) { + $row = Database::queryFirst("SELECT newsid, title, content, dateline FROM news WHERE newsid = :newsid LIMIT 1", array( + 'newsid' => $newsId + )); + } else { + $row = Database::queryFirst("SELECT newsid, title, content, dateline FROM news ORDER BY dateline DESC LIMIT 1"); + } + + // fetch the news to be shown + if ($row !== false) { + $this->newsId = $row['newsid']; + $this->newsTitle = $row['title']; + $this->newsContent = $row['content']; + $this->newsDate = $row['dateline']; + } + return $row !== false; + } + + /** + * Save the given $newsTitle and $newsContent as POST'ed into the database. + * + */ + private function saveNews() + { + // check if news content were set by the user + $newsTitle = Request::post('news-title'); + $newsContent = Request::post('news-content'); + if ($newsContent !== '' && $newsTitle !== '') { + // we got title and content, save it to DB + Database::exec("INSERT INTO news (dateline, title, content) VALUES (:dateline, :title, :content)", array( + 'dateline' => time(), + 'title' => $newsTitle, + 'content' => $newsContent + )); + return true; + } else { + Message::addError('empty-field'); + return false; + } + } + + /** + * Delete the news entry with ID $newsId + * + * @param int $newsId ID of the entry to be deleted. + */ + private function delNews($newsId) + { + // sanity check: is newsId even numeric? + if (!is_numeric($newsId)) { + Message::addError('value-invalid', 'newsid', $newsId); + } else { + // check passed - do delete + Database::exec("DELETE FROM news WHERE newsid = :newsid LIMIT 1", array( + 'newsid' => $newsId + )); + Message::addSuccess('news-del-success'); + } + Util::redirect('?do=News'); + } + +} \ No newline at end of file diff --git a/modules/news/templates/page-news.html b/modules/news/templates/page-news.html new file mode 100644 index 00000000..8e400498 --- /dev/null +++ b/modules/news/templates/page-news.html @@ -0,0 +1,57 @@ +
+
+ {{lang_editNews}} +
+
+

{{lang_newsIntro}}

+
+
+ + +
+
+ + +
+

{{lang_latestUpdate}}: {{latestDate}}

+ + +
+
+
+ +
+
+ {{lang_newsOld}} +
+
+
+
+ + + + + + + + + + + + {{#list}} + + + + + + + {{/list}} + +
{{lang_date}}{{lang_title}}{{lang_content}}
{{date}}{{title}}{{content}} + {{lang_show}} + +
+
+
+
+
diff --git a/modules/serversetup.inc.php b/modules/serversetup.inc.php deleted file mode 100644 index 87ebfdad..00000000 --- a/modules/serversetup.inc.php +++ /dev/null @@ -1,141 +0,0 @@ -currentMenu = Property::getBootMenu(); - - $action = Request::post('action'); - - if ($action === false) { - $this->currentAddress = Property::getServerIp(); - $this->getLocalAddresses(); - } - - if ($action === 'ip') { - // New address is to be set - $this->getLocalAddresses(); - $this->updateLocalAddress(); - } - - if ($action === 'ipxe') { - // iPXE stuff changes - $this->updatePxeMenu(); - } - } - - protected function doRender() - { - Render::setTitle(Dictionary::translate('lang_serverConfiguration')); - - $taskid = Request::any('taskid'); - if ($taskid !== false && Taskmanager::isTask($taskid)) { - Render::addTemplate('serversetup/ipxe_update', array('taskid' => $taskid)); - } - - Render::addTemplate('serversetup/ipaddress', array( - 'ips' => $this->taskStatus['data']['addresses'], - 'chooseHintClass' => $this->hasIpSet ? '' : 'alert alert-danger' - )); - $data = $this->currentMenu; - if (!isset($data['defaultentry'])) - $data['defaultentry'] = 'net'; - if ($data['defaultentry'] === 'net') - $data['active-net'] = 'checked'; - if ($data['defaultentry'] === 'hdd') - $data['active-hdd'] = 'checked'; - if ($data['defaultentry'] === 'custom') - $data['active-custom'] = 'checked'; - Render::addTemplate('serversetup/ipxe', $data); - } - - // ----------------------------------------------------------------------------------------------- - - private function getLocalAddresses() - { - $this->taskStatus = Taskmanager::submit('LocalAddressesList', array()); - - if ($this->taskStatus === false) { - $this->taskStatus['data']['addresses'] = false; - return false; - } - - if ($this->taskStatus['statusCode'] === TASK_WAITING) { // TODO: Async if just displaying - $this->taskStatus = Taskmanager::waitComplete($this->taskStatus['id']); - } - - $sortIp = array(); - foreach (array_keys($this->taskStatus['data']['addresses']) as $key) { - $item = & $this->taskStatus['data']['addresses'][$key]; - if (!isset($item['ip']) || !preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $item['ip']) || substr($item['ip'], 0, 4) === '127.') { - unset($this->taskStatus['data']['addresses'][$key]); - continue; - } - if ($this->currentAddress === $item['ip']) { - $item['default'] = true; - $this->hasIpSet = true; - } - $sortIp[] = $item['ip']; - } - unset($item); - array_multisort($sortIp, SORT_STRING, $this->taskStatus['data']['addresses']); - return true; - } - - private function updateLocalAddress() - { - $newAddress = Request::post('ip', 'none'); - $valid = false; - foreach ($this->taskStatus['data']['addresses'] as $item) { - if ($item['ip'] !== $newAddress) - continue; - $valid = true; - break; - } - if ($valid) { - Property::setServerIp($newAddress); - global $tidIpxe; - if (isset($tidIpxe) && $tidIpxe !== false) - Util::redirect('?do=ServerSetup&taskid=' . $tidIpxe); - } else { - Message::addError('invalid-ip', $newAddress); - } - Util::redirect(); - } - - private function updatePxeMenu() - { - $timeout = Request::post('timeout', 10); - if ($timeout === '') - $timeout = 0; - if (!is_numeric($timeout) || $timeout < 0) { - Message::addError('value-invalid', 'timeout', $timeout); - } - $this->currentMenu['defaultentry'] = Request::post('defaultentry', 'net'); - $this->currentMenu['timeout'] = $timeout; - $this->currentMenu['custom'] = Request::post('custom', ''); - $this->currentMenu['masterpasswordclear'] = Request::post('masterpassword', ''); - if (empty($this->currentMenu['masterpasswordclear'])) - $this->currentMenu['masterpassword'] = 'invalid'; - else - $this->currentMenu['masterpassword'] = Crypto::hash6($this->currentMenu['masterpasswordclear']); - Property::setBootMenu($this->currentMenu); - $id = Trigger::ipxe(); - Util::redirect('?do=ServerSetup&taskid=' . $id); - } - -} diff --git a/modules/serversetup/config.json b/modules/serversetup/config.json new file mode 100644 index 00000000..93209f62 --- /dev/null +++ b/modules/serversetup/config.json @@ -0,0 +1,4 @@ +{ + "category":"settings", + "enabled":"true" +} diff --git a/modules/serversetup/module.inc.php b/modules/serversetup/module.inc.php new file mode 100644 index 00000000..e37d7d40 --- /dev/null +++ b/modules/serversetup/module.inc.php @@ -0,0 +1,186 @@ +currentMenu = Property::getBootMenu(); + + if(Request::get('download') !== false){ + $this->downloadIpxe(Request::get('download')); + } + + if(Request::get('defaultIpxe') !== false){ + $this->defaultIpxe(Request::get('defaultIpxe')); + } + + $action = Request::post('action'); + + if ($action === false) { + $this->currentAddress = Property::getServerIp(); + $this->getLocalAddresses(); + } + + if ($action === 'ip') { + // New address is to be set + $this->getLocalAddresses(); + $this->updateLocalAddress(); + } + + if ($action === 'ipxe') { + // iPXE stuff changes + $this->updatePxeMenu(); + } + + if($action === 'save-script') { + // Save new iPXE script + $this->updateIpxeScript(); + } + + if($action === 'default-script') { + // Restore iPXE script to default + $this->defaultIpxe(); + } + } + + protected function doRender() + { + Render::setTitle(Dictionary::translate('lang_serverConfiguration')); + + $taskid = Request::any('taskid'); + if ($taskid !== false && Taskmanager::isTask($taskid)) { + Render::addTemplate('ipxe_update', array('taskid' => $taskid)); + } + + Render::addTemplate('ipaddress', array( + 'ips' => $this->taskStatus['data']['addresses'] + )); + $data = $this->currentMenu; + if (!isset($data['defaultentry'])) + $data['defaultentry'] = 'net'; + if ($data['defaultentry'] === 'net') + $data['active-net'] = 'checked'; + if ($data['defaultentry'] === 'hdd') + $data['active-hdd'] = 'checked'; + if ($data['defaultentry'] === 'custom') + $data['active-custom'] = 'checked'; + Render::addTemplate('ipxe', $data); + } + + // ----------------------------------------------------------------------------------------------- + + private function getLocalAddresses() + { + $this->taskStatus = Taskmanager::submit('LocalAddressesList', array()); + + if ($this->taskStatus === false) { + $this->taskStatus['data']['addresses'] = false; + return false; + } + + if ($this->taskStatus['statusCode'] === TASK_WAITING) { // TODO: Async if just displaying + $this->taskStatus = Taskmanager::waitComplete($this->taskStatus['id']); + } + + $sortIp = array(); + foreach (array_keys($this->taskStatus['data']['addresses']) as $key) { + $item = & $this->taskStatus['data']['addresses'][$key]; + if (!isset($item['ip']) || !preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $item['ip']) || substr($item['ip'], 0, 4) === '127.') { + unset($this->taskStatus['data']['addresses'][$key]); + continue; + } + if ($this->currentAddress === $item['ip']) { + $item['default'] = true; + } + $sortIp[] = $item['ip']; + } + unset($item); + array_multisort($sortIp, SORT_STRING, $this->taskStatus['data']['addresses']); + return true; + } + + private function updateLocalAddress() + { + $newAddress = Request::post('ip', 'none'); + $valid = false; + foreach ($this->taskStatus['data']['addresses'] as $item) { + if ($item['ip'] !== $newAddress) + continue; + $valid = true; + break; + } + if ($valid) { + Property::setServerIp($newAddress); + global $tidIpxe; + if (isset($tidIpxe) && $tidIpxe !== false) + Util::redirect('?do=ServerSetup&taskid=' . $tidIpxe); + } else { + Message::addError('invalid-ip', $newAddress); + } + Util::redirect(); + } + + private function updatePxeMenu() + { + $timeout = Request::post('timeout', 10); + if ($timeout === '') + $timeout = 0; + if (!is_numeric($timeout) || $timeout < 0) { + Message::addError('value-invalid', 'timeout', $timeout); + } + $this->currentMenu['defaultentry'] = Request::post('defaultentry', 'net'); + $this->currentMenu['timeout'] = $timeout; + $this->currentMenu['custom'] = Request::post('custom', ''); + $this->currentMenu['masterpasswordclear'] = Request::post('masterpassword', ''); + if (empty($this->currentMenu['masterpasswordclear'])) + $this->currentMenu['masterpassword'] = 'invalid'; + else + $this->currentMenu['masterpassword'] = Crypto::hash6($this->currentMenu['masterpasswordclear']); + Property::setBootMenu($this->currentMenu); + $id = Trigger::ipxe(); + Util::redirect('?do=ServerSetup&taskid=' . $id); + } + + private function downloadIpxe($ipxe){ + $file = '/opt/taskmanager/data/ipxe/src/bin/ipxe.' . $ipxe; + if (file_exists($file)) { + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename='.basename($file)); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + header('Content-Length: ' . filesize($file)); + ob_clean(); + flush(); + readfile($file); + exit(); + } + } + + private function updateIpxeScript(){ + $newScript = Request::post('custom-script'); + file_put_contents("/opt/taskmanager/data/pxe.embed",$newScript); + Util::redirect('?do=ServerSetup'); + } + + private function defaultIpxe(){ + $default = file_get_contents("/opt/taskmanager/data/pxe_default.embed"); + $default = str_replace("{{ip}}", "http://" . Property::getServerIp(), $default); + file_put_contents("/opt/taskmanager/data/pxe.embed",$default); + Util::redirect('?do=ServerSetup'); + } +} diff --git a/modules/serversetup/templates/ipaddress.html b/modules/serversetup/templates/ipaddress.html new file mode 100644 index 00000000..e4967703 --- /dev/null +++ b/modules/serversetup/templates/ipaddress.html @@ -0,0 +1,34 @@ +
+
+ {{lang_bootAddress}} +
+
+

+ {{lang_chooseIP}} +

+
+ + + + {{#ips}} + + + {{#default}} + + {{/default}} + {{^default}} + + {{/default}} + + {{/ips}} +
{{ip}} + {{lang_active}} + + +
+

+ {{lang_bootHint}} +

+
+
+
\ No newline at end of file diff --git a/modules/serversetup/templates/ipxe.html b/modules/serversetup/templates/ipxe.html new file mode 100644 index 00000000..54d7db16 --- /dev/null +++ b/modules/serversetup/templates/ipxe.html @@ -0,0 +1,149 @@ +
+
+ {{lang_mountIpxe}} +
+
+

{{lang_ipxeInfo}}

+ + +
+
+ + + + + +
+ +
+
+ + + +
+
+ +
+ +
+ + + + +
+
+ {{lang_bootMenu}} +
+
+

+ {{lang_bootInfo}} +

+
+ +
+ {{lang_bootBehavior}} +
+
+
+
+ +
+ {{lang_menuDisplayTime}} +
+ + {{lang_seconds}} +
+
+ +
+ {{lang_masterPassword}} +
+ +
+ {{lang_masterPasswordHelp}} +
+ +
+ {{lang_menuCustom}} + +
+
+ + +
+
+ + + + + + diff --git a/modules/serversetup/templates/ipxe_update.html b/modules/serversetup/templates/ipxe_update.html new file mode 100644 index 00000000..9c598667 --- /dev/null +++ b/modules/serversetup/templates/ipxe_update.html @@ -0,0 +1,20 @@ +
+
{{lang_menuGeneration}}
+
+
{{lang_menuGeneration}}
+ +
+
+ + diff --git a/modules/session.inc.php b/modules/session.inc.php deleted file mode 100644 index 5b9bc7fc..00000000 --- a/modules/session.inc.php +++ /dev/null @@ -1,38 +0,0 @@ - +

{{lang_enter}}

+ + + + + {{lang_register}} + + \ No newline at end of file diff --git a/modules/statistics.inc.php b/modules/statistics.inc.php deleted file mode 100644 index dbac4b75..00000000 --- a/modules/statistics.inc.php +++ /dev/null @@ -1,785 +0,0 @@ - $uuid, - 'text' => $text - )); - Message::addSuccess('notes-saved'); - Util::redirect('?do=Statistics&uuid=' . $uuid); - } - } - - protected function doRender() - { - Render::setTitle(Dictionary::translate('lang_titleClientStatistics')); - $uuid = Request::get('uuid', false, 'string'); - if ($uuid !== false) { - $this->showMachine($uuid); - return; - } - $filter = Request::get('filter', false, 'string'); - if ($filter !== false) { - $argument = Request::get('argument', false, 'string'); - $this->showMachineList($filter, $argument); - return; - } - Render::addScriptBottom('chart.min'); - Render::openTag('div', array('class' => 'row')); - $this->showSummary(); - $this->showMemory(); - $this->showId44(); - $this->showKvmState(); - $this->showLatestMachines(); - $this->showSystemModels(); - Render::closeTag('div'); - } - - private function capChart(&$json, $cutoff, $minSlice = 0.015) - { - $total = 0; - foreach ($json as $entry) { - $total += $entry['value']; - } - $cap = ceil($total * $cutoff); - $accounted = 0; - $id = 0; - foreach ($json as $entry) { - if (($accounted >= $cap || $entry['value'] / $total < $minSlice) && $id >= 3) break; - $id++; - $accounted += $entry['value']; - } - $json = array_slice($json, 0, $id); - if ($accounted / $total < 0.99) { - $json[] = array( - 'color' => '#eee', - 'label' => 'invalid', - 'value' => ($total - $accounted) - ); - } - } - - private function showSummary() - { - $cutoff = time() - 86400 * 30; - $online = time() - 610; - $known = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $cutoff"); - $on = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online"); - $used = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online AND logintime <> 0"); - $hdd = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE badsectors > 10 AND lastseen > $cutoff"); - $data = array( - 'known' => $known['val'], - 'online' => $on['val'], - 'used' => $used['val'], - 'usedpercent' => round($used['val'] / $on['val'] * 100), - 'badhdd' => $hdd['val'] - ); - // Graph - $cutoff = time() - 2*86400; - $res = Database::simpleQuery("SELECT dateline, data FROM statistic WHERE typeid = '~stats' AND dateline > $cutoff ORDER BY dateline ASC"); - $labels = array(); - $points1 = array('data' => array(), 'label' => 'Online', 'fillColor' => '#efe', 'strokeColor' => '#aea', 'pointColor' => '#7e7', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#7e7'); - $points2 = array('data' => array(), 'label' => 'In use', 'fillColor' => '#fee', 'strokeColor' => '#eaa', 'pointColor' => '#e77', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#e77'); - $sum = 0; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $x = explode('#', $row['data']); - if ($sum === 0) { - $labels[] = date('H:i', $row['dateline']); - } else { - $x[1] = max($x[1], array_pop($points1['data'])); - $x[2] = max($x[2], array_pop($points2['data'])); - } - $points1['data'][] = $x[1]; - $points2['data'][] = $x[2]; - $sum++; - if ($sum === 12) { - $sum = 0; - } - } - $data['json'] = json_encode(array('labels' => $labels, 'datasets' => array($points1, $points2))); - // Draw - Render::addTemplate('statistics/summary', $data); - } - - private function showSystemModels() - { - global $STATS_COLORS; - $res = Database::simpleQuery("SELECT systemmodel, Round(AVG(realcores)) AS cores, Count(*) AS `count` FROM machine" - . " GROUP BY systemmodel ORDER BY `count` DESC, systemmodel ASC"); - $lines = array(); - $json = array(); - $id = 0; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - if (empty($row['systemmodel'])) continue; - settype($row['count'], 'integer'); - $row['id'] = 'systemid' . $id; - $row['urlsystemmodel'] = urlencode($row['systemmodel']); - $lines[] = $row; - $json[] = array( - 'color' => $STATS_COLORS[$id % count($STATS_COLORS)], - 'label' => 'systemid' . $id, - 'value' => $row['count'] - ); - ++$id; - } - $this->capChart($json, 0.92); - Render::addTemplate('statistics/cpumodels', array('rows' => $lines, 'json' => json_encode($json))); - } - - private function showMemory() - { - global $STATS_COLORS, $SIZE_RAM; - $res = Database::simpleQuery("SELECT mbram, Count(*) AS `count` FROM machine GROUP BY mbram"); - $lines = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $gb = ceil($row['mbram'] / 1024); - for ($i = 1; $i < count($SIZE_RAM); ++$i) { - if ($SIZE_RAM[$i] < $gb) continue; - if ($SIZE_RAM[$i] - $gb >= $gb - $SIZE_RAM[$i-1]) --$i; - $gb = $SIZE_RAM[$i]; - break; - } - if (isset($lines[$gb])) { - $lines[$gb] += $row['count']; - } else { - $lines[$gb] = $row['count']; - } - } - asort($lines); - $data = array('rows' => array()); - $json = array(); - $id = 0; - foreach (array_reverse($lines, true) as $k => $v) { - $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => $this->ramColorClass($k * 1024)); - $json[] = array( - 'color' => $STATS_COLORS[$id % count($STATS_COLORS)], - 'label' => (string)$k, - 'value' => $v - ); - ++$id; - } - $this->capChart($json, 0.92); - $data['json'] = json_encode($json); - Render::addTemplate('statistics/memory', $data); - } - - private function showKvmState() - { - $colors = array('UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#6d6'); - $res = Database::simpleQuery("SELECT kvmstate, Count(*) AS `count` FROM machine GROUP BY kvmstate ORDER BY `count` DESC"); - $lines = array(); - $json = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $lines[] = $row; - $json[] = array( - 'color' => isset($colors[$row['kvmstate']]) ? $colors[$row['kvmstate']] : '#000', - 'label' => $row['kvmstate'], - 'value' => $row['count'] - ); - } - Render::addTemplate('statistics/kvmstate', array('rows' => $lines, 'json' => json_encode($json))); - } - - private function showId44() - { - global $STATS_COLORS, $SIZE_ID44; - $res = Database::simpleQuery("SELECT id44mb, Count(*) AS `count` FROM machine GROUP BY id44mb"); - $lines = array(); - $total = 0; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $total += $row['count']; - $gb = ceil($row['id44mb'] / 1024); - for ($i = 1; $i < count($SIZE_ID44); ++$i) { - if ($SIZE_ID44[$i] < $gb) continue; - if ($SIZE_ID44[$i] - $gb >= $gb - $SIZE_ID44[$i-1]) --$i; - $gb = $SIZE_ID44[$i]; - break; - } - if (isset($lines[$gb])) { - $lines[$gb] += $row['count']; - } else { - $lines[$gb] = $row['count']; - } - } - asort($lines); - $data = array('rows' => array()); - $json = array(); - $id = 0; - foreach (array_reverse($lines, true) as $k => $v) { - $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => $this->hddColorClass($k)); - if ($k === 0) { - $color = '#e55'; - } else { - $color = $STATS_COLORS[$id++ % count($STATS_COLORS)]; - } - $json[] = array( - 'color' => $color, - 'label' => (string)$k, - 'value' => $v - ); - } - $this->capChart($json, 0.95); - $data['json'] = json_encode($json); - Render::addTemplate('statistics/id44', $data); - } - - private function showLatestMachines() - { - $data = array('cutoff' => ceil(time() / 3600) * 3600 - 86400 * 7); - $res = Database::simpleQuery("SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine" - . " WHERE firstseen > :cutoff ORDER BY firstseen DESC LIMIT 32", $data); - $rows = array(); - $count = 0; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - if (empty($row['hostname'])) { - $row['hostname'] = $row['clientip']; - } - $row['firstseen'] = date('d.m. H:i', $row['firstseen']); - $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding.. - $row['gbtmp'] = round($row['id44mb'] / 1024); - $row['ramclass'] = $this->ramColorClass($row['mbram']); - $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']); - $row['hddclass'] = $this->hddColorClass($row['gbtmp']); - $row['kvmicon'] = $row['kvmstate'] === 'ENABLED' ? 'âś“' : 'âś—'; - if (++$count > 5) { - $row['style'] = 'display:none'; - } - $rows[] = $row; - } - Render::addTemplate('statistics/newclients', array('rows' => $rows, 'openbutton' => $count > 5)); - } - - private function showMachineList($filter, $argument) - { - global $SIZE_RAM, $SIZE_ID44; - $join = ''; - $filters = array('cpumodel', 'realcores', 'kvmstate', 'clientip', 'macaddr', 'machineuuid', 'systemmodel'); - if (in_array($filter, $filters)) { - // Simple filters mapping into db - $where = " $filter = :argument"; - $args = array('argument' => $argument); - } elseif ($filter === 'gbram') { - // Memory by rounded GB - $lower = floor($this->findBestValue($SIZE_RAM, $argument, false) * 1024 - 100); - $upper = ceil($this->findBestValue($SIZE_RAM, $argument, true) * 1024 + 100); - $where = " mbram BETWEEN $lower AND $upper"; - $args = array(); - } elseif ($filter === 'hddgb') { - // HDD by rounded GB - $lower = floor($this->findBestValue($SIZE_ID44, $argument, false) * 1024 - 100); - $upper = ceil($this->findBestValue($SIZE_ID44, $argument, true) * 1024 + 100); - $where = " id44mb BETWEEN $lower AND $upper"; - $args = array(); - } elseif ($filter === 'subnet') { - $argument = preg_replace('/[^0-9\.:]/', '', $argument); - $where = " clientip LIKE '$argument%'"; - $args = array(); - } elseif ($filter === 'badsectors') { - $where = " badsectors >= :argument "; - $args = array('argument' => $argument); - } elseif ($filter === 'state') { - if ( $argument === 'on') { - $where = " lastseen + 600 > UNIX_TIMESTAMP() "; - } elseif ($argument === 'off') { - $where = " lastseen + 600 < UNIX_TIMESTAMP() "; - } elseif ($argument === 'idle') { - $where = " lastseen + 600 > UNIX_TIMESTAMP() AND logintime = 0 "; - } elseif ($argument === 'occupied') { - $where = " lastseen + 600 > UNIX_TIMESTAMP() AND logintime <> 0 "; - } else { - Message::addError('invalid-filter'); - return; - } - } elseif ($filter === 'location') { - $where = "subnet.locationid = :lid OR machine.locationid = :lid"; - $join = " INNER JOIN subnet ON (INET_ATON(clientip) BETWEEN startaddr AND endaddr) "; - $args = array('lid' => (int)$argument); - } else { - Message::addError('invalid-filter'); - return; - } - $res = Database::simpleQuery("SELECT machineuuid, macaddr, clientip, firstseen, lastseen," - . " logintime, lastboot, realcores, mbram, kvmstate, cpumodel, id44mb, hostname, notes IS NOT NULL AS hasnotes, badsectors FROM machine" - . " $join WHERE $where ORDER BY lastseen DESC, clientip ASC", $args); - $rows = array(); - $NOW = time(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - if ($NOW - $row['lastseen'] > 610) { - $row['state_off'] = true; - } elseif ($row['logintime'] == 0) { - $row['state_idle'] = true; - } else { - $row['state_occupied'] = true; - } - //$row['firstseen'] = date('d.m.Y H:i', $row['firstseen']); - $row['lastseen'] = date('d.m. H:i', $row['lastseen']); - //$row['lastboot'] = date('d.m. H:i', $row['lastboot']); - $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding.. - $row['gbtmp'] = round($row['id44mb'] / 1024); - $octets = explode('.', $row['clientip']); - if (count($octets) === 4) { - $row['subnet'] = "$octets[0].$octets[1].$octets[2]."; - $row['lastoctet'] = $octets[3]; - } - $row['ramclass'] = $this->ramColorClass($row['mbram']); - $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']); - $row['hddclass'] = $this->hddColorClass($row['gbtmp']); - if (empty($row['hostname'])) $row['hostname'] = $row['clientip']; - $rows[] = $row; - } - Render::addTemplate('statistics/clientlist', array('rows' => $rows, 'filter' => $filter, 'argument' => $argument)); - } - - private function ramColorClass($mb) - { - if ($mb < 1500) - return 'danger'; - if ($mb < 2500) - return 'warning'; - return ''; - } - - private function kvmColorClass($state) - { - if ($state === 'DISABLED') - return 'danger'; - if ($state === 'UNKNOWN' || $state === 'UNSUPPORTED') - return 'warning'; - return ''; - } - - private function hddColorClass($gb) - { - if ($gb < 7) - return 'danger'; - if ($gb < 25) - return 'warning'; - return ''; - } - - private function findBestValue($array, $value, $up) - { - $best = 0; - for ($i = 0; $i < count($array); ++$i) { - if (abs($array[$i] - $value) < abs($array[$best] - $value)) { - $best = $i; - } - } - if (!$up && $best === 0) { - return $array[0]; - } - if ($up && $best + 1 === count($array)) { - return $array[$best]; - } - if ($up) { - return ($array[$best] + $array[$best + 1]) / 2; - } - return ($array[$best] + $array[$best - 1]) / 2; - } - - private function fillSessionInfo(&$row) - { - $res = Database::simpleQuery("SELECT dateline, username, data FROM statistic" - . " WHERE clientip = :ip AND typeid = '.vmchooser-session-name'" - . " AND dateline BETWEEN :start AND :end", array( - 'ip' => $row['clientip'], - 'start' => $row['logintime'] - 60, - 'end' => $row['logintime'] + 300 - )); - $session = false; - while ($r = $res->fetch(PDO::FETCH_ASSOC)) { - if ($session === false || abs($session['dateline'] - $row['logintime']) > abs($r['dateline'] - $row['logintime'])) { - $session = $r; - } - } - if ($session !== false) { - $row['session'] = $session['data']; - $row['username'] = $session['username']; - } - } - - private function showMachine($uuid) - { - $client = Database::queryFirst("SELECT machineuuid, macaddr, clientip, firstseen, lastseen, logintime, lastboot," - . " mbram, kvmstate, cpumodel, id44mb, data, hostname, notes FROM machine WHERE machineuuid = :uuid", - array('uuid' => $uuid)); - // Mangle fields - $NOW = time(); - if ($NOW - $client['lastseen'] > 610) { - $client['state_off'] = true; - } elseif ($client['logintime'] == 0) { - $client['state_idle'] = true; - } else { - $client['state_occupied'] = true; - $this->fillSessionInfo($client); - } - $client['firstseen_s'] = date('d.m.Y H:i', $client['firstseen']); - $client['lastseen_s'] = date('d.m.Y H:i', $client['lastseen']); - $uptime = $NOW - $client['lastboot']; - $client['lastboot_s'] = date('d.m.Y H:i', $client['lastboot']) . ' (Up ' . floor($uptime / 86400) . 'd ' . gmdate('H:i', $uptime) . ')'; - $client['logintime_s'] = date('d.m.Y H:i', $client['logintime']); - $client['gbram'] = round(round($client['mbram'] / 500) / 2, 1); - $client['gbtmp'] = round($client['id44mb'] / 1024); - $client['ramclass'] = $this->ramColorClass($client['mbram']); - $client['kvmclass'] = $this->kvmColorClass($client['kvmstate']); - $client['hddclass'] = $this->hddColorClass($client['gbtmp']); - // Parse the giant blob of data - $hdds = array(); - if (preg_match_all('/##### ([^#]+) #+$(.*?)^#####/ims', $client['data'] . '########', $out, PREG_SET_ORDER)) { - foreach ($out as $section) { - if ($section[1] === 'CPU') { - $this->parseCpu($client, $section[2]); - } - if ($section[1] === 'dmidecode') { - $this->parseDmiDecode($client, $section[2]); - } - if ($section[1] === 'Partition tables') { - $this->parseHdd($hdds, $section[2]); - } - if (isset($hdds['hdds']) && $section[1] === 'smartctl') { - // This currently required that the partition table section comes first... - $this->parseSmartctl($hdds['hdds'], $section[2]); - } - } - } - unset($client['data']); - // Throw output at user - Render::addTemplate('statistics/machine-main', $client); - // Sessions - $NOW = time(); - $cutoff = $NOW - 86400 * 7; - //if ($cutoff < $client['firstseen']) $cutoff = $client['firstseen']; - $scale = 100 / ($NOW - $cutoff); - $res = Database::simpleQuery("SELECT dateline, typeid, data FROM statistic" - . " WHERE dateline > :cutoff AND typeid IN ('~session-length', '~offline-length') AND machineuuid = :uuid ORDER BY dateline ASC", array( - 'cutoff' => $cutoff - 86400 * 14, - 'uuid' => $uuid - )); - $spans['rows'] = array(); - $spans['graph'] = ''; - $last = false; - $first = true; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - if ($first && $row['dateline'] > $cutoff && $client['lastboot'] > $cutoff) { - // Special case: offline before - $spans['graph'] .= '
 
'; - } - $first = false; - if ($row['dateline'] + $row['data'] < $cutoff || $row['data'] > 864000) continue; - if ($last !== false && abs($last['dateline'] - $row['dateline']) < 30 - && abs($last['data'] - $row['data']) < 30) continue; - if ($last !== false && $last['dateline'] + $last['data'] > $row['dateline']) { - $point = $last['dateline'] + $last['data']; - $row['data'] -= ($point - $row['dateline']); - $row['dateline'] = $point; - } - if ($row['dateline'] < $cutoff) { - $row['data'] -= ($cutoff - $row['dateline']); - $row['dateline'] = $cutoff; - } - $row['from'] = date('d.m. H:i', $row['dateline']); - $row['duration'] = floor($row['data'] / 86400) . 'd ' . gmdate('H:i', $row['data']); - if ($row['typeid'] === '~offline-length') { - $row['glyph'] = 'off'; - $color = '#444'; - } else { - $row['glyph'] = 'user'; - $color = '#e77'; - } - $spans['graph'] .= '
 
'; - $spans['rows'][] = $row; - $last = $row; - } - if ($first && $client['lastboot'] > $cutoff) { - // Special case: offline before - $spans['graph'] .= '
 
'; - } - if (isset($client['state_occupied'])) { - $spans['graph'] .= '
 
'; - } elseif (isset($client['state_off'])) { - $spans['graph'] .= '
 
'; - } - $t = explode('-', date('Y-n-j-G', $cutoff)); - if ($t[3] >= 8 && $t[3] <= 22) { - $start = mktime(22, 0, 0, $t[1], $t[2], $t[0]); - } else { - $start = mktime(22, 0, 0, $t[1], $t[2] - 1, $t[0]); - } - for ($i = $start; $i < $NOW; $i += 86400) { - $spans['graph'] .= '
 
'; - } - if (count($spans['rows']) > 10) { - $spans['hasrows2'] = true; - $spans['rows2'] = array_slice($spans['rows'], ceil(count($spans['rows']) / 2)); - $spans['rows'] = array_slice($spans['rows'], 0, ceil(count($spans['rows']) / 2)); - } - Render::addTemplate('statistics/machine-usage', $spans); - // Any hdds? - if (!empty($hdds['hdds'])) { - Render::addScriptBottom('chart.min'); - Render::addTemplate('statistics/machine-hdds', $hdds); - } - // Client log - $lres = Database::simpleQuery("SELECT logid, dateline, logtypeid, clientip, description, extra FROM clientlog" - . " WHERE clientip = :clientip ORDER BY logid DESC LIMIT 25", array('clientip' => $client['clientip'])); - $today = date('d.m.Y'); - $yesterday = date('d.m.Y', time() - 86400); - $count = 0; - $log = array(); - while ($row = $lres->fetch(PDO::FETCH_ASSOC)) { - if (substr($row['description'], -5) === 'on :0' && strpos($row['description'], 'root logged') === false) continue; - $day = date('d.m.Y', $row['dateline']); - if ($day === $today) { - $day = Dictionary::translate('today'); - } elseif ($day === $yesterday) { - $day = Dictionary::translate('yesterday'); - } - $row['date'] = $day . date(' H:i', $row['dateline']); - $row['icon'] = $this->eventToIconName($row['logtypeid']); - $log[] = $row; - if (++$count === 10) break; - } - Render::addTemplate('statistics/syslog', array( - 'clientip' => $client['clientip'], - 'list' => $log - )); - // Notes - Render::addTemplate('statistics/machine-notes', $client); - } - - private function eventToIconName($event) - { - switch ($event) { - case 'session-open': - return 'glyphicon-log-in'; - case 'session-close': - return 'glyphicon-log-out'; - case 'partition-swap': - return 'glyphicon-info-sign'; - case 'partition-temp': - case 'smartctl-realloc': - return 'glyphicon-exclamation-sign'; - default: - return 'glyphicon-minus'; - } - } - - private function parseCpu(&$row, $data) - { - if (0 >= preg_match_all('/^(.+):\s+(\d+)$/im', $data, $out, PREG_SET_ORDER)) return; - foreach ($out as $entry) { - $row[str_replace(' ', '', $entry[1])] = $entry[2]; - } - } - - private function parseDmiDecode(&$row, $data) - { - $lines = preg_split("/[\r\n]+/", $data); - $section = false; - $ramOk = false; - $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false; - foreach ($lines as $line) { - if (empty($line)) continue; - if ($line{0} !== "\t" && $line{0} !== ' ') { - $section = $line; - $ramOk = false; - if (($ramForm || $ramType) && ($ramSpeed || $ramClockSpeed)) { - if (isset($row['ramtype']) && !$ramClockSpeed) continue; - $row['ramtype'] = $ramType . ' ' . $ramForm; - if ($ramClockSpeed) $row['ramtype'] .= ', ' . $ramClockSpeed; - elseif ($ramSpeed) $row['ramtype'] .= ', ' . $ramSpeed; - $ramForm = false; - $ramType = false; - $ramClockSpeed = false; - } - continue; - } - if ($section === 'System Information' || $section === 'Base Board Information') { - if (empty($row['pcmodel']) && preg_match('/^\s*Product Name: +(\S.+?) *$/i', $line, $out)) { - $row['pcmodel'] = $out[1]; - } - if (empty($row['manufacturer']) && preg_match('/^\s*Manufacturer: +(\S.+?) *$/i', $line, $out)) { - $row['manufacturer'] = $out[1]; - } - } - else if ($section === 'Physical Memory Array') { - if (!$ramOk && preg_match('/Use: System Memory/i', $line)) { - $ramOk = true; - } - if ($ramOk && preg_match('/^\s*Number Of Devices: +(\S.+?) *$/i', $line, $out)) { - $row['ramslotcount'] = $out[1]; - } - if ($ramOk && preg_match('/^\s*Maximum Capacity: +(\S.+?)\s*$/i', $line, $out)) { - $row['maxram'] = preg_replace('/([MGT])B/', '$1iB', $out[1]); - } - } - else if ($section === 'Memory Device') { - if (preg_match('/^\s*Size:\s*(.*?)\s*$/i', $line, $out)) { - $row['extram'] = true; - if (preg_match('/(\d+)\s*(\w)i?B/i', $out[1], $out)) { - $out[2] = strtoupper($out[2]); - if ($out[2] === 'K' || ($out[2] === 'M' && $out[1] < 500)) { - $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false; - continue; - } - if ($out[2] === 'M' && $out[1] >= 1024) { - $out[2] = 'G'; - $out[1] = floor(($out[1] + 100) / 1024); - } - $row['ramslot'][]['size'] = $out[1] . ' ' . strtoupper($out[2]) . 'iB'; - } else if (!isset($row['ramslot']) || (count($row['ramslot']) < 8 && (!isset($row['ramslotcount']) || $row['ramslotcount'] <= 8))) { - $row['ramslot'][]['size'] = '_____'; - } - } - if (preg_match('/^\s*Form Factor:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') { - $ramForm = $out[1]; - } - if (preg_match('/^\s*Type:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') { - $ramType = $out[1]; - } - if (preg_match('/^\s*Speed:\s*(\d.*?)\s*$/i', $line, $out)) { - $ramSpeed = $out[1]; - } - if (preg_match('/^\s*Configured Clock Speed:\s*(\d.*?)\s*$/i', $line, $out)) { - $ramClockSpeed = $out[1]; - } - } - } - if (empty($row['ramslotcount'])) $row['ramslotcount'] = count($row['ramslot']); - } - - private function parseHdd(&$row, $data) - { - $hdds = array(); - // Could have more than one disk - linear scan - $lines = preg_split("/[\r\n]+/", $data); - $dev = false; - $i = 0; - foreach ($lines as $line) { - if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) { - // disk total size and name - unset($hdd); - $unit = 0; - $hdd = array( - 'devid' => 'devid-' . ++$i, - 'dev' => $out[1], - 'size' => round($out[2] / (1024 * 1024 * 1024)), - 'used' => 0, - 'partitions' => array(), - 'json' => array(), - ); - $hdds[] = &$hdd; - } elseif (preg_match('/^Units =.*= (\d+) bytes/i', $line, $out)) { - // Unit for start and end - $unit = $out[1] / (1024 * 1024); // Convert so that multiplying by unit yields MiB - } else if (isset($hdd) && $unit !== 0 && preg_match(',^/dev/(\S+)\s+.*\s(\d+)[\+\-]?\s+(\d+)[\+\-]?\s+\d+[\+\-]?\s+([0-9a-f]+)\s+(.*)$,i', $line, $out)) { - // Some partition - $type = strtolower($out[4]); - if ($type === '5' || $type === 'f' || $type === '85') continue; - $partsize = round(($out[3] - $out[2]) * $unit); - $hdd['partitions'][] = array( - 'id' => $out[1], - 'name' => $out[1], - 'size' => round($partsize / 1024, $partsize < 1024 ? 1 : 0), - 'type' => ($type === '44' ? 'OpenSLX' : $out[5]), - ); - $hdd['json'][] = array( - 'label' => $out[1], - 'value' => $partsize, - 'color' => ($type === '44' ? '#4d4' : ($type === '82' ? '#48f' : '#e55')), - ); - $hdd['used'] += $partsize; - } - } - unset($hdd); - $i = 0; - foreach ($hdds as &$hdd) { - $hdd['used'] = round($hdd['used'] / 1024); - $free = $hdd['size'] - $hdd['used']; - if ($free > 5) { - $hdd['partitions'][] = array( - 'id' => 'free-id-' . $i, - 'name' => Dictionary::translate('unused'), - 'size' => $free, - 'type' => '-', - ); - $hdd['json'][] = array( - 'label' => 'free-id-' . $i, - 'value' => $free * 1024, - 'color' => '#aaa', - ); - ++$i; - } - $hdd['json'] = json_encode($hdd['json']); - } - unset($hdd); - $row['hdds'] = &$hdds; - } - - private function parseSmartctl(&$hdds, $data) - { - $lines = preg_split("/[\r\n]+/", $data); - $i = 0; - foreach ($lines as $line) { - if (preg_match('/^NEXTHDD=(.+)$/', $line, $out)) { - unset($dev); - foreach ($hdds as &$hdd) { - if ($hdd['dev'] === $out[1]) $dev =& $hdd; - } - continue; - } - if (!isset($dev)) continue; - if (preg_match('/^([A-Z][^:]+):\s*(.*)$/', $line, $out)) { - $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2]; - } elseif (preg_match('/^\s*\d+\s+(\S+)\s+\S+\s+\d+\s+\d+\s+\d+\s+\S+\s+(\d+)(\s|$)/', $line, $out)) { - $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2]; - } - } - // Format strings - foreach ($hdds as &$hdd) { - if (isset($hdd['s_PowerOnHours'])) { - $hdd['PowerOnTime'] = ''; - $val = (int)$hdd['s_PowerOnHours']; - if ($val > 8760) { - $hdd['PowerOnTime'] .= floor($val / 8760) . 'Y, '; - $val %= 8760; - } - if ($val > 720) { - $hdd['PowerOnTime'] .= floor($val / 720) . 'M, '; - $val %= 720; - } - if ($val > 24) { - $hdd['PowerOnTime'] .= floor($val / 24) . 'd, '; - $val %= 24; - } - $hdd['PowerOnTime'] .= $val . 'h'; - } - } - } - -} diff --git a/modules/statistics/config.json b/modules/statistics/config.json new file mode 100644 index 00000000..17acab76 --- /dev/null +++ b/modules/statistics/config.json @@ -0,0 +1,5 @@ +{ + "category":"status", + "enabled":"true", + "permission":"0" +} diff --git a/modules/statistics/module.inc.php b/modules/statistics/module.inc.php new file mode 100644 index 00000000..faf88521 --- /dev/null +++ b/modules/statistics/module.inc.php @@ -0,0 +1,792 @@ + $uuid, + 'text' => $text + )); + Message::addSuccess('notes-saved'); + Util::redirect('?do=Statistics&uuid=' . $uuid); + } + } + + protected function doRender() + { + Render::setTitle(Dictionary::translate('lang_titleClientStatistics')); + $uuid = Request::get('uuid', false, 'string'); + if ($uuid !== false) { + $this->showMachine($uuid); + return; + } + $filter = Request::get('filter', false, 'string'); + if ($filter !== false) { + $argument = Request::get('argument', false, 'string'); + $this->showMachineList($filter, $argument); + return; + } + Render::addScriptBottom('chart.min'); + Render::openTag('div', array('class' => 'row')); + $this->showSummary(); + $this->showMemory(); + $this->showId44(); + $this->showKvmState(); + $this->showLatestMachines(); + $this->showSystemModels(); + Render::closeTag('div'); + } + + private function capChart(&$json, $cutoff, $minSlice = 0.015) + { + $total = 0; + foreach ($json as $entry) { + $total += $entry['value']; + } + if ($total === 0) + return; + $cap = ceil($total * $cutoff); + $accounted = 0; + $id = 0; + foreach ($json as $entry) { + if (($accounted >= $cap || $entry['value'] / $total < $minSlice) && $id >= 3) break; + $id++; + $accounted += $entry['value']; + } + $json = array_slice($json, 0, $id); + if ($accounted / $total < 0.99) { + $json[] = array( + 'color' => '#eee', + 'label' => 'invalid', + 'value' => ($total - $accounted) + ); + } + } + + private function showSummary() + { + $cutoff = time() - 86400 * 30; + $online = time() - 610; + $known = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $cutoff"); + $on = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online"); + $used = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online AND logintime <> 0"); + $hdd = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE badsectors > 10 AND lastseen > $cutoff"); + if ($on['val'] != 0) { + $usedpercent = round($used['val'] / $on['val'] * 100); + } else { + $usedpercent = 0; + } + $data = array( + 'known' => $known['val'], + 'online' => $on['val'], + 'used' => $used['val'], + 'usedpercent' => $usedpercent, + 'badhdd' => $hdd['val'] + ); + // Graph + $cutoff = time() - 2*86400; + $res = Database::simpleQuery("SELECT dateline, data FROM statistic WHERE typeid = '~stats' AND dateline > $cutoff ORDER BY dateline ASC"); + $labels = array(); + $points1 = array('data' => array(), 'label' => 'Online', 'fillColor' => '#efe', 'strokeColor' => '#aea', 'pointColor' => '#7e7', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#7e7'); + $points2 = array('data' => array(), 'label' => 'In use', 'fillColor' => '#fee', 'strokeColor' => '#eaa', 'pointColor' => '#e77', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#e77'); + $sum = 0; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $x = explode('#', $row['data']); + if ($sum === 0) { + $labels[] = date('H:i', $row['dateline']); + } else { + $x[1] = max($x[1], array_pop($points1['data'])); + $x[2] = max($x[2], array_pop($points2['data'])); + } + $points1['data'][] = $x[1]; + $points2['data'][] = $x[2]; + $sum++; + if ($sum === 12) { + $sum = 0; + } + } + $data['json'] = json_encode(array('labels' => $labels, 'datasets' => array($points1, $points2))); + // Draw + Render::addTemplate('summary', $data); + } + + private function showSystemModels() + { + global $STATS_COLORS; + $res = Database::simpleQuery("SELECT systemmodel, Round(AVG(realcores)) AS cores, Count(*) AS `count` FROM machine" + . " GROUP BY systemmodel ORDER BY `count` DESC, systemmodel ASC"); + $lines = array(); + $json = array(); + $id = 0; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if (empty($row['systemmodel'])) continue; + settype($row['count'], 'integer'); + $row['id'] = 'systemid' . $id; + $row['urlsystemmodel'] = urlencode($row['systemmodel']); + $lines[] = $row; + $json[] = array( + 'color' => $STATS_COLORS[$id % count($STATS_COLORS)], + 'label' => 'systemid' . $id, + 'value' => $row['count'] + ); + ++$id; + } + $this->capChart($json, 0.92); + Render::addTemplate('cpumodels', array('rows' => $lines, 'json' => json_encode($json))); + } + + private function showMemory() + { + global $STATS_COLORS, $SIZE_RAM; + $res = Database::simpleQuery("SELECT mbram, Count(*) AS `count` FROM machine GROUP BY mbram"); + $lines = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $gb = ceil($row['mbram'] / 1024); + for ($i = 1; $i < count($SIZE_RAM); ++$i) { + if ($SIZE_RAM[$i] < $gb) continue; + if ($SIZE_RAM[$i] - $gb >= $gb - $SIZE_RAM[$i-1]) --$i; + $gb = $SIZE_RAM[$i]; + break; + } + if (isset($lines[$gb])) { + $lines[$gb] += $row['count']; + } else { + $lines[$gb] = $row['count']; + } + } + asort($lines); + $data = array('rows' => array()); + $json = array(); + $id = 0; + foreach (array_reverse($lines, true) as $k => $v) { + $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => $this->ramColorClass($k * 1024)); + $json[] = array( + 'color' => $STATS_COLORS[$id % count($STATS_COLORS)], + 'label' => (string)$k, + 'value' => $v + ); + ++$id; + } + $this->capChart($json, 0.92); + $data['json'] = json_encode($json); + Render::addTemplate('memory', $data); + } + + private function showKvmState() + { + $colors = array('UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#6d6'); + $res = Database::simpleQuery("SELECT kvmstate, Count(*) AS `count` FROM machine GROUP BY kvmstate ORDER BY `count` DESC"); + $lines = array(); + $json = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $lines[] = $row; + $json[] = array( + 'color' => isset($colors[$row['kvmstate']]) ? $colors[$row['kvmstate']] : '#000', + 'label' => $row['kvmstate'], + 'value' => $row['count'] + ); + } + Render::addTemplate('kvmstate', array('rows' => $lines, 'json' => json_encode($json))); + } + + private function showId44() + { + global $STATS_COLORS, $SIZE_ID44; + $res = Database::simpleQuery("SELECT id44mb, Count(*) AS `count` FROM machine GROUP BY id44mb"); + $lines = array(); + $total = 0; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $total += $row['count']; + $gb = ceil($row['id44mb'] / 1024); + for ($i = 1; $i < count($SIZE_ID44); ++$i) { + if ($SIZE_ID44[$i] < $gb) continue; + if ($SIZE_ID44[$i] - $gb >= $gb - $SIZE_ID44[$i-1]) --$i; + $gb = $SIZE_ID44[$i]; + break; + } + if (isset($lines[$gb])) { + $lines[$gb] += $row['count']; + } else { + $lines[$gb] = $row['count']; + } + } + asort($lines); + $data = array('rows' => array()); + $json = array(); + $id = 0; + foreach (array_reverse($lines, true) as $k => $v) { + $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => $this->hddColorClass($k)); + if ($k === 0) { + $color = '#e55'; + } else { + $color = $STATS_COLORS[$id++ % count($STATS_COLORS)]; + } + $json[] = array( + 'color' => $color, + 'label' => (string)$k, + 'value' => $v + ); + } + $this->capChart($json, 0.95); + $data['json'] = json_encode($json); + Render::addTemplate('id44', $data); + } + + private function showLatestMachines() + { + $data = array('cutoff' => ceil(time() / 3600) * 3600 - 86400 * 7); + $res = Database::simpleQuery("SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine" + . " WHERE firstseen > :cutoff ORDER BY firstseen DESC LIMIT 32", $data); + $rows = array(); + $count = 0; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if (empty($row['hostname'])) { + $row['hostname'] = $row['clientip']; + } + $row['firstseen'] = date('d.m. H:i', $row['firstseen']); + $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding.. + $row['gbtmp'] = round($row['id44mb'] / 1024); + $row['ramclass'] = $this->ramColorClass($row['mbram']); + $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']); + $row['hddclass'] = $this->hddColorClass($row['gbtmp']); + $row['kvmicon'] = $row['kvmstate'] === 'ENABLED' ? 'âś“' : 'âś—'; + if (++$count > 5) { + $row['style'] = 'display:none'; + } + $rows[] = $row; + } + Render::addTemplate('newclients', array('rows' => $rows, 'openbutton' => $count > 5)); + } + + private function showMachineList($filter, $argument) + { + global $SIZE_RAM, $SIZE_ID44; + $join = ''; + $filters = array('cpumodel', 'realcores', 'kvmstate', 'clientip', 'macaddr', 'machineuuid', 'systemmodel'); + if (in_array($filter, $filters)) { + // Simple filters mapping into db + $where = " $filter = :argument"; + $args = array('argument' => $argument); + } elseif ($filter === 'gbram') { + // Memory by rounded GB + $lower = floor($this->findBestValue($SIZE_RAM, $argument, false) * 1024 - 100); + $upper = ceil($this->findBestValue($SIZE_RAM, $argument, true) * 1024 + 100); + $where = " mbram BETWEEN $lower AND $upper"; + $args = array(); + } elseif ($filter === 'hddgb') { + // HDD by rounded GB + $lower = floor($this->findBestValue($SIZE_ID44, $argument, false) * 1024 - 100); + $upper = ceil($this->findBestValue($SIZE_ID44, $argument, true) * 1024 + 100); + $where = " id44mb BETWEEN $lower AND $upper"; + $args = array(); + } elseif ($filter === 'subnet') { + $argument = preg_replace('/[^0-9\.:]/', '', $argument); + $where = " clientip LIKE '$argument%'"; + $args = array(); + } elseif ($filter === 'badsectors') { + $where = " badsectors >= :argument "; + $args = array('argument' => $argument); + } elseif ($filter === 'state') { + if ( $argument === 'on') { + $where = " lastseen + 600 > UNIX_TIMESTAMP() "; + } elseif ($argument === 'off') { + $where = " lastseen + 600 < UNIX_TIMESTAMP() "; + } elseif ($argument === 'idle') { + $where = " lastseen + 600 > UNIX_TIMESTAMP() AND logintime = 0 "; + } elseif ($argument === 'occupied') { + $where = " lastseen + 600 > UNIX_TIMESTAMP() AND logintime <> 0 "; + } else { + Message::addError('invalid-filter'); + return; + } + } elseif ($filter === 'location') { + $where = "subnet.locationid = :lid OR machine.locationid = :lid"; + $join = " INNER JOIN subnet ON (INET_ATON(clientip) BETWEEN startaddr AND endaddr) "; + $args = array('lid' => (int)$argument); + } else { + Message::addError('invalid-filter'); + return; + } + $res = Database::simpleQuery("SELECT machineuuid, macaddr, clientip, firstseen, lastseen," + . " logintime, lastboot, realcores, mbram, kvmstate, cpumodel, id44mb, hostname, notes IS NOT NULL AS hasnotes, badsectors FROM machine" + . " $join WHERE $where ORDER BY lastseen DESC, clientip ASC", $args); + $rows = array(); + $NOW = time(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if ($NOW - $row['lastseen'] > 610) { + $row['state_off'] = true; + } elseif ($row['logintime'] == 0) { + $row['state_idle'] = true; + } else { + $row['state_occupied'] = true; + } + //$row['firstseen'] = date('d.m.Y H:i', $row['firstseen']); + $row['lastseen'] = date('d.m. H:i', $row['lastseen']); + //$row['lastboot'] = date('d.m. H:i', $row['lastboot']); + $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding.. + $row['gbtmp'] = round($row['id44mb'] / 1024); + $octets = explode('.', $row['clientip']); + if (count($octets) === 4) { + $row['subnet'] = "$octets[0].$octets[1].$octets[2]."; + $row['lastoctet'] = $octets[3]; + } + $row['ramclass'] = $this->ramColorClass($row['mbram']); + $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']); + $row['hddclass'] = $this->hddColorClass($row['gbtmp']); + if (empty($row['hostname'])) $row['hostname'] = $row['clientip']; + $rows[] = $row; + } + Render::addTemplate('clientlist', array('rows' => $rows, 'filter' => $filter, 'argument' => $argument)); + } + + private function ramColorClass($mb) + { + if ($mb < 1500) + return 'danger'; + if ($mb < 2500) + return 'warning'; + return ''; + } + + private function kvmColorClass($state) + { + if ($state === 'DISABLED') + return 'danger'; + if ($state === 'UNKNOWN' || $state === 'UNSUPPORTED') + return 'warning'; + return ''; + } + + private function hddColorClass($gb) + { + if ($gb < 7) + return 'danger'; + if ($gb < 25) + return 'warning'; + return ''; + } + + private function findBestValue($array, $value, $up) + { + $best = 0; + for ($i = 0; $i < count($array); ++$i) { + if (abs($array[$i] - $value) < abs($array[$best] - $value)) { + $best = $i; + } + } + if (!$up && $best === 0) { + return $array[0]; + } + if ($up && $best + 1 === count($array)) { + return $array[$best]; + } + if ($up) { + return ($array[$best] + $array[$best + 1]) / 2; + } + return ($array[$best] + $array[$best - 1]) / 2; + } + + private function fillSessionInfo(&$row) + { + $res = Database::simpleQuery("SELECT dateline, username, data FROM statistic" + . " WHERE clientip = :ip AND typeid = '.vmchooser-session-name'" + . " AND dateline BETWEEN :start AND :end", array( + 'ip' => $row['clientip'], + 'start' => $row['logintime'] - 60, + 'end' => $row['logintime'] + 300 + )); + $session = false; + while ($r = $res->fetch(PDO::FETCH_ASSOC)) { + if ($session === false || abs($session['dateline'] - $row['logintime']) > abs($r['dateline'] - $row['logintime'])) { + $session = $r; + } + } + if ($session !== false) { + $row['session'] = $session['data']; + $row['username'] = $session['username']; + } + } + + private function showMachine($uuid) + { + $client = Database::queryFirst("SELECT machineuuid, macaddr, clientip, firstseen, lastseen, logintime, lastboot," + . " mbram, kvmstate, cpumodel, id44mb, data, hostname, notes FROM machine WHERE machineuuid = :uuid", + array('uuid' => $uuid)); + // Mangle fields + $NOW = time(); + if ($NOW - $client['lastseen'] > 610) { + $client['state_off'] = true; + } elseif ($client['logintime'] == 0) { + $client['state_idle'] = true; + } else { + $client['state_occupied'] = true; + $this->fillSessionInfo($client); + } + $client['firstseen_s'] = date('d.m.Y H:i', $client['firstseen']); + $client['lastseen_s'] = date('d.m.Y H:i', $client['lastseen']); + $uptime = $NOW - $client['lastboot']; + $client['lastboot_s'] = date('d.m.Y H:i', $client['lastboot']) . ' (Up ' . floor($uptime / 86400) . 'd ' . gmdate('H:i', $uptime) . ')'; + $client['logintime_s'] = date('d.m.Y H:i', $client['logintime']); + $client['gbram'] = round(round($client['mbram'] / 500) / 2, 1); + $client['gbtmp'] = round($client['id44mb'] / 1024); + $client['ramclass'] = $this->ramColorClass($client['mbram']); + $client['kvmclass'] = $this->kvmColorClass($client['kvmstate']); + $client['hddclass'] = $this->hddColorClass($client['gbtmp']); + // Parse the giant blob of data + $hdds = array(); + if (preg_match_all('/##### ([^#]+) #+$(.*?)^#####/ims', $client['data'] . '########', $out, PREG_SET_ORDER)) { + foreach ($out as $section) { + if ($section[1] === 'CPU') { + $this->parseCpu($client, $section[2]); + } + if ($section[1] === 'dmidecode') { + $this->parseDmiDecode($client, $section[2]); + } + if ($section[1] === 'Partition tables') { + $this->parseHdd($hdds, $section[2]); + } + if (isset($hdds['hdds']) && $section[1] === 'smartctl') { + // This currently required that the partition table section comes first... + $this->parseSmartctl($hdds['hdds'], $section[2]); + } + } + } + unset($client['data']); + // Throw output at user + Render::addTemplate('machine-main', $client); + // Sessions + $NOW = time(); + $cutoff = $NOW - 86400 * 7; + //if ($cutoff < $client['firstseen']) $cutoff = $client['firstseen']; + $scale = 100 / ($NOW - $cutoff); + $res = Database::simpleQuery("SELECT dateline, typeid, data FROM statistic" + . " WHERE dateline > :cutoff AND typeid IN ('~session-length', '~offline-length') AND machineuuid = :uuid ORDER BY dateline ASC", array( + 'cutoff' => $cutoff - 86400 * 14, + 'uuid' => $uuid + )); + $spans['rows'] = array(); + $spans['graph'] = ''; + $last = false; + $first = true; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if ($first && $row['dateline'] > $cutoff && $client['lastboot'] > $cutoff) { + // Special case: offline before + $spans['graph'] .= '
 
'; + } + $first = false; + if ($row['dateline'] + $row['data'] < $cutoff || $row['data'] > 864000) continue; + if ($last !== false && abs($last['dateline'] - $row['dateline']) < 30 + && abs($last['data'] - $row['data']) < 30) continue; + if ($last !== false && $last['dateline'] + $last['data'] > $row['dateline']) { + $point = $last['dateline'] + $last['data']; + $row['data'] -= ($point - $row['dateline']); + $row['dateline'] = $point; + } + if ($row['dateline'] < $cutoff) { + $row['data'] -= ($cutoff - $row['dateline']); + $row['dateline'] = $cutoff; + } + $row['from'] = date('d.m. H:i', $row['dateline']); + $row['duration'] = floor($row['data'] / 86400) . 'd ' . gmdate('H:i', $row['data']); + if ($row['typeid'] === '~offline-length') { + $row['glyph'] = 'off'; + $color = '#444'; + } else { + $row['glyph'] = 'user'; + $color = '#e77'; + } + $spans['graph'] .= '
 
'; + $spans['rows'][] = $row; + $last = $row; + } + if ($first && $client['lastboot'] > $cutoff) { + // Special case: offline before + $spans['graph'] .= '
 
'; + } + if (isset($client['state_occupied'])) { + $spans['graph'] .= '
 
'; + } elseif (isset($client['state_off'])) { + $spans['graph'] .= '
 
'; + } + $t = explode('-', date('Y-n-j-G', $cutoff)); + if ($t[3] >= 8 && $t[3] <= 22) { + $start = mktime(22, 0, 0, $t[1], $t[2], $t[0]); + } else { + $start = mktime(22, 0, 0, $t[1], $t[2] - 1, $t[0]); + } + for ($i = $start; $i < $NOW; $i += 86400) { + $spans['graph'] .= '
 
'; + } + if (count($spans['rows']) > 10) { + $spans['hasrows2'] = true; + $spans['rows2'] = array_slice($spans['rows'], ceil(count($spans['rows']) / 2)); + $spans['rows'] = array_slice($spans['rows'], 0, ceil(count($spans['rows']) / 2)); + } + Render::addTemplate('machine-usage', $spans); + // Any hdds? + if (!empty($hdds['hdds'])) { + Render::addScriptBottom('chart.min'); + Render::addTemplate('machine-hdds', $hdds); + } + // Client log + $lres = Database::simpleQuery("SELECT logid, dateline, logtypeid, clientip, description, extra FROM clientlog" + . " WHERE clientip = :clientip ORDER BY logid DESC LIMIT 25", array('clientip' => $client['clientip'])); + $today = date('d.m.Y'); + $yesterday = date('d.m.Y', time() - 86400); + $count = 0; + $log = array(); + while ($row = $lres->fetch(PDO::FETCH_ASSOC)) { + if (substr($row['description'], -5) === 'on :0' && strpos($row['description'], 'root logged') === false) continue; + $day = date('d.m.Y', $row['dateline']); + if ($day === $today) { + $day = Dictionary::translate('today'); + } elseif ($day === $yesterday) { + $day = Dictionary::translate('yesterday'); + } + $row['date'] = $day . date(' H:i', $row['dateline']); + $row['icon'] = $this->eventToIconName($row['logtypeid']); + $log[] = $row; + if (++$count === 10) break; + } + Render::addTemplate('syslog', array( + 'clientip' => $client['clientip'], + 'list' => $log + )); + // Notes + Render::addTemplate('machine-notes', $client); + } + + private function eventToIconName($event) + { + switch ($event) { + case 'session-open': + return 'glyphicon-log-in'; + case 'session-close': + return 'glyphicon-log-out'; + case 'partition-swap': + return 'glyphicon-info-sign'; + case 'partition-temp': + case 'smartctl-realloc': + return 'glyphicon-exclamation-sign'; + default: + return 'glyphicon-minus'; + } + } + + private function parseCpu(&$row, $data) + { + if (0 >= preg_match_all('/^(.+):\s+(\d+)$/im', $data, $out, PREG_SET_ORDER)) return; + foreach ($out as $entry) { + $row[str_replace(' ', '', $entry[1])] = $entry[2]; + } + } + + private function parseDmiDecode(&$row, $data) + { + $lines = preg_split("/[\r\n]+/", $data); + $section = false; + $ramOk = false; + $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false; + foreach ($lines as $line) { + if (empty($line)) continue; + if ($line{0} !== "\t" && $line{0} !== ' ') { + $section = $line; + $ramOk = false; + if (($ramForm || $ramType) && ($ramSpeed || $ramClockSpeed)) { + if (isset($row['ramtype']) && !$ramClockSpeed) continue; + $row['ramtype'] = $ramType . ' ' . $ramForm; + if ($ramClockSpeed) $row['ramtype'] .= ', ' . $ramClockSpeed; + elseif ($ramSpeed) $row['ramtype'] .= ', ' . $ramSpeed; + $ramForm = false; + $ramType = false; + $ramClockSpeed = false; + } + continue; + } + if ($section === 'System Information' || $section === 'Base Board Information') { + if (empty($row['pcmodel']) && preg_match('/^\s*Product Name: +(\S.+?) *$/i', $line, $out)) { + $row['pcmodel'] = $out[1]; + } + if (empty($row['manufacturer']) && preg_match('/^\s*Manufacturer: +(\S.+?) *$/i', $line, $out)) { + $row['manufacturer'] = $out[1]; + } + } + else if ($section === 'Physical Memory Array') { + if (!$ramOk && preg_match('/Use: System Memory/i', $line)) { + $ramOk = true; + } + if ($ramOk && preg_match('/^\s*Number Of Devices: +(\S.+?) *$/i', $line, $out)) { + $row['ramslotcount'] = $out[1]; + } + if ($ramOk && preg_match('/^\s*Maximum Capacity: +(\S.+?)\s*$/i', $line, $out)) { + $row['maxram'] = preg_replace('/([MGT])B/', '$1iB', $out[1]); + } + } + else if ($section === 'Memory Device') { + if (preg_match('/^\s*Size:\s*(.*?)\s*$/i', $line, $out)) { + $row['extram'] = true; + if (preg_match('/(\d+)\s*(\w)i?B/i', $out[1], $out)) { + $out[2] = strtoupper($out[2]); + if ($out[2] === 'K' || ($out[2] === 'M' && $out[1] < 500)) { + $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false; + continue; + } + if ($out[2] === 'M' && $out[1] >= 1024) { + $out[2] = 'G'; + $out[1] = floor(($out[1] + 100) / 1024); + } + $row['ramslot'][]['size'] = $out[1] . ' ' . strtoupper($out[2]) . 'iB'; + } else if (!isset($row['ramslot']) || (count($row['ramslot']) < 8 && (!isset($row['ramslotcount']) || $row['ramslotcount'] <= 8))) { + $row['ramslot'][]['size'] = '_____'; + } + } + if (preg_match('/^\s*Form Factor:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') { + $ramForm = $out[1]; + } + if (preg_match('/^\s*Type:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') { + $ramType = $out[1]; + } + if (preg_match('/^\s*Speed:\s*(\d.*?)\s*$/i', $line, $out)) { + $ramSpeed = $out[1]; + } + if (preg_match('/^\s*Configured Clock Speed:\s*(\d.*?)\s*$/i', $line, $out)) { + $ramClockSpeed = $out[1]; + } + } + } + if (empty($row['ramslotcount'])) $row['ramslotcount'] = count($row['ramslot']); + } + + private function parseHdd(&$row, $data) + { + $hdds = array(); + // Could have more than one disk - linear scan + $lines = preg_split("/[\r\n]+/", $data); + $dev = false; + $i = 0; + foreach ($lines as $line) { + if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) { + // disk total size and name + unset($hdd); + $unit = 0; + $hdd = array( + 'devid' => 'devid-' . ++$i, + 'dev' => $out[1], + 'size' => round($out[2] / (1024 * 1024 * 1024)), + 'used' => 0, + 'partitions' => array(), + 'json' => array(), + ); + $hdds[] = &$hdd; + } elseif (preg_match('/^Units =.*= (\d+) bytes/i', $line, $out)) { + // Unit for start and end + $unit = $out[1] / (1024 * 1024); // Convert so that multiplying by unit yields MiB + } else if (isset($hdd) && $unit !== 0 && preg_match(',^/dev/(\S+)\s+.*\s(\d+)[\+\-]?\s+(\d+)[\+\-]?\s+\d+[\+\-]?\s+([0-9a-f]+)\s+(.*)$,i', $line, $out)) { + // Some partition + $type = strtolower($out[4]); + if ($type === '5' || $type === 'f' || $type === '85') continue; + $partsize = round(($out[3] - $out[2]) * $unit); + $hdd['partitions'][] = array( + 'id' => $out[1], + 'name' => $out[1], + 'size' => round($partsize / 1024, $partsize < 1024 ? 1 : 0), + 'type' => ($type === '44' ? 'OpenSLX' : $out[5]), + ); + $hdd['json'][] = array( + 'label' => $out[1], + 'value' => $partsize, + 'color' => ($type === '44' ? '#4d4' : ($type === '82' ? '#48f' : '#e55')), + ); + $hdd['used'] += $partsize; + } + } + unset($hdd); + $i = 0; + foreach ($hdds as &$hdd) { + $hdd['used'] = round($hdd['used'] / 1024); + $free = $hdd['size'] - $hdd['used']; + if ($free > 5) { + $hdd['partitions'][] = array( + 'id' => 'free-id-' . $i, + 'name' => Dictionary::translate('unused'), + 'size' => $free, + 'type' => '-', + ); + $hdd['json'][] = array( + 'label' => 'free-id-' . $i, + 'value' => $free * 1024, + 'color' => '#aaa', + ); + ++$i; + } + $hdd['json'] = json_encode($hdd['json']); + } + unset($hdd); + $row['hdds'] = &$hdds; + } + + private function parseSmartctl(&$hdds, $data) + { + $lines = preg_split("/[\r\n]+/", $data); + $i = 0; + foreach ($lines as $line) { + if (preg_match('/^NEXTHDD=(.+)$/', $line, $out)) { + unset($dev); + foreach ($hdds as &$hdd) { + if ($hdd['dev'] === $out[1]) $dev =& $hdd; + } + continue; + } + if (!isset($dev)) continue; + if (preg_match('/^([A-Z][^:]+):\s*(.*)$/', $line, $out)) { + $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2]; + } elseif (preg_match('/^\s*\d+\s+(\S+)\s+\S+\s+\d+\s+\d+\s+\d+\s+\S+\s+(\d+)(\s|$)/', $line, $out)) { + $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2]; + } + } + // Format strings + foreach ($hdds as &$hdd) { + if (isset($hdd['s_PowerOnHours'])) { + $hdd['PowerOnTime'] = ''; + $val = (int)$hdd['s_PowerOnHours']; + if ($val > 8760) { + $hdd['PowerOnTime'] .= floor($val / 8760) . 'Y, '; + $val %= 8760; + } + if ($val > 720) { + $hdd['PowerOnTime'] .= floor($val / 720) . 'M, '; + $val %= 720; + } + if ($val > 24) { + $hdd['PowerOnTime'] .= floor($val / 24) . 'd, '; + $val %= 24; + } + $hdd['PowerOnTime'] .= $val . 'h'; + } + } + } + +} diff --git a/modules/statistics/templates/clientlist.html b/modules/statistics/templates/clientlist.html new file mode 100644 index 00000000..8e8565fe --- /dev/null +++ b/modules/statistics/templates/clientlist.html @@ -0,0 +1,45 @@ +

{{lang_clientList}}

+
{{filter}} ~= {{argument}}
+
+ + + + + + + + + + + + {{#rows}} + + + + + + + + + + {{/rows}} +
{{lang_machine}}{{lang_address}}{{lang_lastSeen}}{{lang_kvmSupport}}{{lang_gbRam}}{{lang_tmpGb}}{{lang_cpuModel}}
+ {{#hasnotes}}{{/hasnotes}} + {{#state_off}} + + {{/state_off}} + {{#state_idle}} + + {{/state_idle}} + {{#state_occupied}} + + {{/state_occupied}} + {{hostname}} +
{{machineuuid}}
+
{{subnet}}{{lastoctet}}
{{macaddr}}
{{lastseen}}{{kvmstate}}{{gbram}} GiB + {{gbtmp}} GiB + {{#badsectors}}
+ + {{badsectors}} +
{{/badsectors}} +
{{lang_realCores}}: {{realcores}}
{{cpumodel}}
diff --git a/modules/statistics/templates/cpumodels.html b/modules/statistics/templates/cpumodels.html new file mode 100644 index 00000000..2f24cd92 --- /dev/null +++ b/modules/statistics/templates/cpumodels.html @@ -0,0 +1,51 @@ +
+
+
+ {{lang_modelStats}} +
+
+
+
+ + + + + + + {{#rows}} + + + + + + {{/rows}} +
{{lang_modelName}}{{lang_cpuCores}}{{lang_modelCount}}
+ {{systemmodel}} + {{cores}}{{count}}
+
+
+ + +
+
+
+
+
diff --git a/modules/statistics/templates/id44.html b/modules/statistics/templates/id44.html new file mode 100644 index 00000000..730839b1 --- /dev/null +++ b/modules/statistics/templates/id44.html @@ -0,0 +1,48 @@ +
+
+
+ {{lang_tempPartStats}} +
+
+
+
+ + + + + + {{#rows}} + + + + + {{/rows}} +
{{lang_partitionSize}}{{lang_machineCount}}
{{gb}} GiB{{count}}
+
+
+ + +
+
+
+
+
\ No newline at end of file diff --git a/modules/statistics/templates/kvmstate.html b/modules/statistics/templates/kvmstate.html new file mode 100644 index 00000000..107a34f7 --- /dev/null +++ b/modules/statistics/templates/kvmstate.html @@ -0,0 +1,47 @@ +
+
+
+ {{lang_kvmStats}} +
+
+
+
+ + + + + + {{#rows}} + + + + + {{/rows}} +
{{lang_kvmState}}{{lang_machineCount}}
{{kvmstate}}{{count}}
+
+
+ + +
+
+
+
+
\ No newline at end of file diff --git a/modules/statistics/templates/machine-hdds.html b/modules/statistics/templates/machine-hdds.html new file mode 100644 index 00000000..fd6cf1be --- /dev/null +++ b/modules/statistics/templates/machine-hdds.html @@ -0,0 +1,67 @@ +

{{lang_hdds}}

+
+ {{#hdds}} +
+
+
+ {{s_ModelFamily}} {{dev}} +
+
+ {{#s_DeviceModel}} +
{{lang_modelNo}}: {{s_DeviceModel}}, {{lang_serialNo}}: {{s_SerialNumber}}
+ {{/s_DeviceModel}} + {{#s_ReallocatedSectorCt}} +
{{lang_reallocatedSectors}}: {{s_ReallocatedSectorCt}}
+ {{/s_ReallocatedSectorCt}} + {{#s_CurrentPendingSector}} +
{{lang_pendingSectors}}: {{s_CurrentPendingSector}}
+ {{/s_CurrentPendingSector}} + {{#s_PowerOnHours}} +
{{lang_powerOnTime}}: {{s_PowerOnHours}} {{lang_hours}} ({{PowerOnTime}})
+ {{/s_PowerOnHours}} +
+
+ + + + + + + {{#partitions}} + + + + + + {{/partitions}} +
{{lang_partName}}{{lang_partSize}}{{lang_partType}}
{{name}}{{size}} GiB{{type}}
+
{{lang_total}}: {{size}} GiB
+
+
+ + +
+
+
+
+
+ {{/hdds}} +
\ No newline at end of file diff --git a/modules/statistics/templates/machine-main.html b/modules/statistics/templates/machine-main.html new file mode 100644 index 00000000..8071416a --- /dev/null +++ b/modules/statistics/templates/machine-main.html @@ -0,0 +1,124 @@ +

+ {{hostname}} {{#hostname}}–{{/hostname}} {{clientip}} + {{#notes}}{{/notes}} +

+ +
+
+
+
+ {{lang_machineSummary}} +
+
+ + + + + + + + + + + + + + {{#hostname}} + + + + + {{/hostname}} + + + + + + + + + + + + + + + + +
{{lang_uuid}}{{machineuuid}}
{{lang_macAddr}}{{macaddr}}
{{lang_ip}}{{clientip}}
{{lang_hostname}}{{hostname}}
{{lang_firstSeen}}{{firstseen_s}}
{{lang_lastBoot}}{{lastboot_s}}
{{lang_lastSeen}}{{lastseen_s}}
{{lang_usageState}} + {{#state_off}} + {{lang_machineOff}} + {{/state_off}} + {{#state_idle}} + {{lang_machineIdle}} + {{/state_idle}} + {{#state_occupied}} + {{#username}} + {{lang_machineOccupiedBy}} {{username}} + {{/username}} + {{^username}} + {{lang_machineOccupied}} + {{/username}} +
{{logintime_s}}
+ {{/state_occupied}} + {{#session}} +
{{session}}
+ {{/session}} +
+
+
+
+
+
+
+ {{lang_hardwareSummary}} +
+
+ + + + + + + + + + + + + + {{#extram}} + + + + + {{/extram}} + + + + + + + + +
{{lang_cpuModel}} + {{cpumodel}} + {{#Sockets}} +
+ {{lang_sockets}}: {{Sockets}}, {{lang_cores}}: {{Realcores}}, {{lang_virtualCores}}: {{Virtualcores}} +
+ {{/Sockets}} +
{{lang_model}}{{pcmodel}} ({{manufacturer}})
{{lang_ram}} + {{gbram}} GiB + {{#maxram}}({{lang_maximumAbbrev}} {{maxram}}){{/maxram}} + {{ramtype}} +
{{lang_ramSlots}} + {{ramslotcount}}: + {{#ramslot}} + [ {{size}} ] + {{/ramslot}} +
{{lang_tempPart}}{{gbtmp}} GiB
{{lang_64bitSupport}}{{kvmstate}}
+
+
+
+
diff --git a/modules/statistics/templates/machine-notes.html b/modules/statistics/templates/machine-notes.html new file mode 100644 index 00000000..c4f97543 --- /dev/null +++ b/modules/statistics/templates/machine-notes.html @@ -0,0 +1,17 @@ + +

{{lang_notes}}

+
+
+
+
+
+ + + + + +
+
+
+
+
\ No newline at end of file diff --git a/modules/statistics/templates/machine-usage.html b/modules/statistics/templates/machine-usage.html new file mode 100644 index 00000000..ffaa747b --- /dev/null +++ b/modules/statistics/templates/machine-usage.html @@ -0,0 +1,51 @@ +
+
+
+
+ {{lang_usageDetails}} +
+
+
+
+ + + + + + + {{#rows}} + + + + + + {{/rows}} +
TypeWhenLength
{{from}}{{duration}}
+
+
+ + {{#hasrows2}} + + + + + + {{/hasrows2}} + {{#rows2}} + + + + + + {{/rows2}} +
TypeWhenLength
{{from}}{{duration}}
+
+
+
 {{{graph}}}
+
+ {{lang_timebarDesc}} +
+
+
+
+
diff --git a/modules/statistics/templates/memory.html b/modules/statistics/templates/memory.html new file mode 100644 index 00000000..f4d2ad24 --- /dev/null +++ b/modules/statistics/templates/memory.html @@ -0,0 +1,47 @@ +
+
+
+ {{lang_memoryStats}} +
+
+
+
+ + + + + + {{#rows}} + + + + + {{/rows}} +
{{lang_ramSize}}{{lang_machineCount}}
{{gb}} GiB{{count}}
+
+
+ + +
+
+
+
+
\ No newline at end of file diff --git a/modules/statistics/templates/newclients.html b/modules/statistics/templates/newclients.html new file mode 100644 index 00000000..0d9c74df --- /dev/null +++ b/modules/statistics/templates/newclients.html @@ -0,0 +1,44 @@ +
+
+
+ {{lang_newMachines}} +
+
+ + + + + + + + + {{#rows}} + + + + + + + + {{/rows}} + {{#openbutton}} + + + + {{/openbutton}} +
{{lang_machine}}64BitRAMHDD
{{hostname}}{{firstseen}}{{kvmicon}}{{gbram}} GiB{{gbtmp}} GiB
+ + + + + + +
+
+
+
\ No newline at end of file diff --git a/modules/statistics/templates/summary.html b/modules/statistics/templates/summary.html new file mode 100644 index 00000000..5f16fd89 --- /dev/null +++ b/modules/statistics/templates/summary.html @@ -0,0 +1,33 @@ +
+
+
+
+ {{lang_knownMachines}}: {{known}}  + {{lang_onlineMachines}}: {{online}}  + {{lang_inUseMachines}}: {{used}} ({{usedpercent}}%) +
+ {{#badhdd}} +
+ + + {{lang_withBadSectors}}: {{badhdd}} + +
+ {{/badhdd}} +
+
+
+ + +
+
+ diff --git a/modules/statistics/templates/syslog.html b/modules/statistics/templates/syslog.html new file mode 100644 index 00000000..c82cb8ac --- /dev/null +++ b/modules/statistics/templates/syslog.html @@ -0,0 +1,43 @@ +

{{lang_logHeadline}}

+ + + + + + + + + {{#list}} + + + + + + + {{/list}} + +
{{lang_when}}{{lang_event}}{{lang_details}}
{{date}}{{description}}{{#extra}} + » + + {{/extra}}
+
{{lang_more}} »
+
+ + + + diff --git a/modules/support/config.json b/modules/support/config.json new file mode 100644 index 00000000..4e7fa5fb --- /dev/null +++ b/modules/support/config.json @@ -0,0 +1,4 @@ +{ + "category":"content", + "enabled":"true" +} diff --git a/modules/support/module.inc.php b/modules/support/module.inc.php new file mode 100644 index 00000000..d4012c1a --- /dev/null +++ b/modules/support/module.inc.php @@ -0,0 +1,77 @@ +SMTPDebug = 3; // Enable verbose debug output + $mail->isSMTP(); // Set mailer to use SMTP + $mail->Host = 'mx.c3sl.ufpr.br'; // Specify main and backup SMTP servers + $mail->SMTPAuth = true; // Enable SMTP authentication + $mail->Username = 'xxx00@inf.ufpr.br'; // SMTP username + $mail->Password = ''; // SMTP password + // $mail->SMTPSecure = 'false'; // Enable TLS encryption, `ssl` also accepted + $mail->Port = 25; // TCP port to connect to + + $mail->From = 'xxx00@inf.ufpr.br'; + $mail->FromName = 'Someone'; + $mail->addAddress('receiver@email.com', 'Another One'); // Add a recipient + // $mail->addAttachment('/var/tmp/file.tar.gz'); // Add attachments + // $mail->addAttachment('/tmp/image.jpg', 'new.jpg'); // Optional name + $mail->isHTML(true); // Set email format to HTML + $mail->Subject = 'Here is the subject'; + $mail->Body = 'This is the HTML message body in bold!'; + $mail->AltBody = 'This is the body in plain text for non-HTML mail clients'; + + if(!$mail->send()) { + echo 'Message could not be sent.'; + echo 'Mailer Error: ' . $mail->ErrorInfo; + } else { + echo 'Message has been sent'; + } + */ +// $uploaddir = '/var/www/uploads/'; +// $uploadfile = $uploaddir . basename($_FILES['inp_file']['name']); +// if(move_uploaded_file($_FILES['inp_file']['tmp_name'], $uploadfile)) +// Message::addSuccess('news-save-success'); +// else +// Message::addError('news-empty'); +// mail($to,$_POST[assuntoEmail],$_POST[conteudoEmail],"-r".$from); +// mail($to,$assunto,$content,$headers); + + + } + + + + protected function doRender(){ + error_reporting(E_ALL); + ini_set('display_errors','on'); + if (strpos($_SERVER['REQUEST_URI'], "true") !== false) + Render::addTemplate('page-faq'); + else + Render::addTemplate('page-support'); +// Render::addTemplate('page-support', array( +// 'token' => Session:get('token')); + } + + +} diff --git a/modules/support/templates/go-pear.phar b/modules/support/templates/go-pear.phar new file mode 100644 index 00000000..9038c747 --- /dev/null +++ b/modules/support/templates/go-pear.phar @@ -0,0 +1,99492 @@ + + * @author Greg Beaver + * @link http://www.synapticmedia.net Synaptic Media + * @version Id: Archive.php,v 1.52 2007/09/01 20:28:14 cellog Exp $ + * @package PHP_Archive + * @category PHP + */ + +class PHP_Archive +{ + const GZ = 0x00001000; + const BZ2 = 0x00002000; + const SIG = 0x00010000; + const SHA1 = 0x0002; + const MD5 = 0x0001; + /** + * Whether this archive is compressed with zlib + * + * @var bool + */ + private $_compressed; + /** + * @var string Real path to the .phar archive + */ + private $_archiveName = null; + /** + * Current file name in the phar + * @var string + */ + protected $currentFilename = null; + /** + * Length of current file in the phar + * @var string + */ + protected $internalFileLength = null; + /** + * Current file statistics (size, creation date, etc.) + * @var string + */ + protected $currentStat = null; + /** + * @var resource|null Pointer to open .phar + */ + protected $fp = null; + /** + * @var int Current Position of the pointer + */ + protected $position = 0; + + /** + * Map actual realpath of phars to meta-data about the phar + * + * Data is indexed by the alias that is used by internal files. In other + * words, if a file is included via: + * + * require_once 'phar://PEAR.phar/PEAR/Installer.php'; + * + * then the alias is "PEAR.phar" + * + * Information stored is a boolean indicating whether this .phar is compressed + * with zlib, another for bzip2, phar-specific meta-data, and + * the precise offset of internal files + * within the .phar, used with the {@link $_manifest} to load actual file contents + * @var array + */ + private static $_pharMapping = array(); + /** + * Map real file paths to alias used + * + * @var array + */ + private static $_pharFiles = array(); + /** + * File listing for the .phar + * + * The manifest is indexed per phar. + * + * Files within the .phar are indexed by their relative path within the + * .phar. Each file has this information in its internal array + * + * - 0 = uncompressed file size + * - 1 = timestamp of when file was added to phar + * - 2 = offset of file within phar relative to internal file's start + * - 3 = compressed file size (actual size in the phar) + * @var array + */ + private static $_manifest = array(); + /** + * Absolute offset of internal files within the .phar, indexed by absolute + * path to the .phar + * + * @var array + */ + private static $_fileStart = array(); + /** + * file name of the phar + * + * @var string + */ + private $_basename; + + + /** + * Default MIME types used for the web front controller + * + * @var array + */ + public static $defaultmimes = array( + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'arc' => 'application/octet-stream', + 'arj' => 'application/octet-stream', + 'art' => 'image/x-jg', + 'asf' => 'video/x-ms-asf', + 'asx' => 'video/x-ms-asf', + 'avi' => 'video/avi', + 'bin' => 'application/octet-stream', + 'bm' => 'image/bmp', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bzip2', + 'css' => 'text/css', + 'doc' => 'application/msword', + 'dot' => 'application/msword', + 'dv' => 'video/x-dv', + 'dvi' => 'application/x-dvi', + 'eps' => 'application/postscript', + 'exe' => 'application/octet-stream', + 'gif' => 'image/gif', + 'gz' => 'application/x-gzip', + 'gzip' => 'application/x-gzip', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ico' => 'image/x-icon', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'js' => 'application/x-javascript', + 'log' => 'text/plain', + 'mid' => 'audio/x-midi', + 'mov' => 'video/quicktime', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg3', + 'mpg' => 'audio/mpeg', + 'pdf' => 'aplication/pdf', + 'png' => 'image/png', + 'rtf' => 'application/rtf', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'txt' => 'text/plain', + 'xml' => 'text/xml', + ); + + public static $defaultphp = array( + 'php' => true + ); + + public static $defaultphps = array( + 'phps' => true + ); + + public static $deny = array('/.+\.inc$/'); + + public static function viewSource($archive, $file) + { + // security, idea borrowed from PHK + if (!file_exists($archive . '.introspect')) { + header("HTTP/1.0 404 Not Found"); + return false; + } + if (self::_fileExists($archive, $_GET['viewsource'])) { + $source = highlight_file('phar://go-pear.phar/' . + $_GET['viewsource'], true); + header('Content-Type: text/html'); + header('Content-Length: ' . strlen($source)); + echo 'Source of ', + htmlspecialchars($_GET['viewsource']), ''; + echo '

Source of ', + htmlspecialchars($_GET['viewsource']), '

'; + if (isset($_GET['introspect'])) { + echo 'Return to ', htmlspecialchars($_GET['introspect']), '
'; + } + echo $source; + return false; + } else { + header("HTTP/1.0 404 Not Found"); + return false; + } + + } + + public static function introspect($archive, $dir) + { + // security, idea borrowed from PHK + if (!file_exists($archive . '.introspect')) { + header("HTTP/1.0 404 Not Found"); + return false; + } + if (!$dir) { + $dir = '/'; + } + $dir = self::processFile($dir); + if ($dir[0] != '/') { + $dir = '/' . $dir; + } + try { + $self = htmlspecialchars($_SERVER['PHP_SELF']); + $iterate = new DirectoryIterator('phar://go-pear.phar' . $dir); + echo 'Introspect ', htmlspecialchars($dir), + '

Introspect ', htmlspecialchars($dir), + '