summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2020-04-20 11:49:17 +0200
committerSimon Rettberg2020-04-20 11:49:17 +0200
commit5eaa4292c6db2c1ee1282c938c899dc5b88db65f (patch)
tree137eaa063302b055ddec03e2c4c3ff5c084f11bf
parent[serversetup-bwlp-ipxe] Fix: Imported PXELINUX entries are invalid (diff)
downloadslx-admin-5eaa4292c6db2c1ee1282c938c899dc5b88db65f.tar.gz
slx-admin-5eaa4292c6db2c1ee1282c938c899dc5b88db65f.tar.xz
slx-admin-5eaa4292c6db2c1ee1282c938c899dc5b88db65f.zip
[statistics] New filter UI
-rw-r--r--inc/arrayutil.inc.php23
-rw-r--r--modules-available/statistics/config.json1
-rw-r--r--modules-available/statistics/hooks/translation.inc.php30
-rw-r--r--modules-available/statistics/inc/statisticsfilter.inc.php270
-rw-r--r--modules-available/statistics/lang/de/filters.json24
-rw-r--r--modules-available/statistics/lang/de/template-tags.json11
-rw-r--r--modules-available/statistics/lang/en/filters.json24
-rw-r--r--modules-available/statistics/lang/en/template-tags.json11
-rw-r--r--modules-available/statistics/page.inc.php24
-rw-r--r--modules-available/statistics/pages/list.inc.php11
-rw-r--r--modules-available/statistics/pages/summary.inc.php13
-rw-r--r--modules-available/statistics/style.css31
-rw-r--r--modules-available/statistics/templates/cpumodels.html4
-rw-r--r--modules-available/statistics/templates/filterbox.html330
-rw-r--r--modules-available/statistics/templates/id44.html2
-rw-r--r--modules-available/statistics/templates/kvmstate.html2
-rw-r--r--modules-available/statistics/templates/memory.html2
17 files changed, 423 insertions, 390 deletions
diff --git a/inc/arrayutil.inc.php b/inc/arrayutil.inc.php
index ec6e2a5f..490b5a4f 100644
--- a/inc/arrayutil.inc.php
+++ b/inc/arrayutil.inc.php
@@ -10,7 +10,7 @@ class ArrayUtil
* @param string $key
* @return array
*/
- public static function flattenByKey($list, $key)
+ public static function flattenByKey(array $list, string $key)
{
$ret = [];
foreach ($list as $item) {
@@ -21,4 +21,25 @@ class ArrayUtil
return $ret;
}
+ /**
+ * Pass an array of arrays you want to merge. The keys of the outer array will become
+ * the inner keys of the resulting array, and vice versa.
+ * @param array $arrays
+ * @return array
+ */
+ public static function mergeByKey(array $arrays)
+ {
+ $empty = array_combine(array_keys($arrays), array_fill(0, count($arrays), false));
+ $out = [];
+ foreach ($arrays as $subkey => $array) {
+ foreach ($array as $key => $item) {
+ if (!isset($out[$key])) {
+ $out[$key] = $empty;
+ }
+ $out[$key][$subkey] = $item;
+ }
+ }
+ return $out;
+ }
+
} \ No newline at end of file
diff --git a/modules-available/statistics/config.json b/modules-available/statistics/config.json
index 412dc3cb..11d3fba3 100644
--- a/modules-available/statistics/config.json
+++ b/modules-available/statistics/config.json
@@ -2,7 +2,6 @@
"category": "main.status",
"dependencies": [
"js_chart",
- "js_selectize",
"bootstrap_datepicker"
],
"permission": "0"
diff --git a/modules-available/statistics/hooks/translation.inc.php b/modules-available/statistics/hooks/translation.inc.php
new file mode 100644
index 00000000..4d09553a
--- /dev/null
+++ b/modules-available/statistics/hooks/translation.inc.php
@@ -0,0 +1,30 @@
+<?php
+
+$HANDLER = array();
+
+/**
+ * List of valid subsections
+ */
+$HANDLER['subsections'] = array(
+ 'filters'
+);
+
+/*
+ * Handlers for the subsections that will return an array of expected tags.
+ * This is optional, if you don't want to define expected tags, don't create a function.
+ */
+
+/**
+ * Configuration categories.
+ * @param \Module $module
+ * @return array
+ */
+$HANDLER['grep_filters'] = function($module) {
+ if (!$module->activate(1, false))
+ return array();
+ $want = StatisticsFilter::$columns;
+ foreach ($want as &$entry) {
+ $entry = true;
+ }
+ return $want;
+};
diff --git a/modules-available/statistics/inc/statisticsfilter.inc.php b/modules-available/statistics/inc/statisticsfilter.inc.php
index 1bc512f5..6999654d 100644
--- a/modules-available/statistics/inc/statisticsfilter.inc.php
+++ b/modules-available/statistics/inc/statisticsfilter.inc.php
@@ -6,9 +6,9 @@
class StatisticsFilter
{
/**
- * Delimiter for js_selectize filters
+ * Legacy delimiter for js_selectize filters - used to redirect old URLs
*/
- const DELIMITER = '~,~';
+ const LEGACY_DELIMITER = '~,~';
const SIZE_ID44 = array(0, 8, 16, 24, 30, 40, 50, 60, 80, 100, 120, 150, 180, 250, 300, 400, 500, 1000, 2000, 4000);
const SIZE_RAM = array(1, 2, 3, 4, 6, 8, 10, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 320, 480, 512, 768, 1024);
@@ -90,61 +90,34 @@ class StatisticsFilter
}
/* parse a query into an array of filters */
- public static function parseQuery($query)
+ public static function parseQuery()
{
- $operators = ['<=', '>=', '!=', '!~', '=', '~', '<', '>'];
+ // Get current settings from GET
+ $currentValues = self::loadFilterFromGet();
$filters = [];
- if (empty($query))
- return $filters;
- foreach (explode(self::DELIMITER, $query) as $q) {
- $q = trim($q);
- if (empty($q))
+ foreach ($currentValues as $filterType => $data) {
+ if (!$data['filter'])
continue;
- // Special case: User pasted UUID, turn into filter
- if (preg_match('/^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/', $q)) {
- $filters[] = new StatisticsFilter('machineuuid', '=', $q);
- continue;
- }
- // Special case: User pasted IP, turn into filter
- if (preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $q)) {
- $filters[] = new StatisticsFilter('clientip', '=', $q);
- continue;
- }
- /* find position of first operator */
- $pos = 10000;
- $operator = false;
- foreach ($operators as $op) {
- $newpos = strpos($q, $op);
- if ($newpos > -1 && ($newpos < $pos)) {
- $pos = $newpos;
- $operator = $op;
- }
- }
- if ($pos == 10000) {
- error_log("couldn't find operator in segment " . $q);
- /* TODO */
- continue;
- }
- $lhs = trim(substr($q, 0, $pos));
- $rhs = trim(substr($q, $pos + strlen($operator)));
-
- if ($lhs === 'gbram') {
- $filters[] = new RamGbStatisticsFilter($operator, $rhs);
- } elseif ($lhs === 'runtime') {
- $filters[] = new RuntimeStatisticsFilter($operator, $rhs);
- } elseif ($lhs === 'state') {
- $filters[] = new StateStatisticsFilter($operator, $rhs);
- } elseif ($lhs === 'hddgb') {
- $filters[] = new Id44StatisticsFilter($operator, $rhs);
- } elseif ($lhs === 'location') {
- $filters[] = new LocationStatisticsFilter($operator, $rhs);
- } elseif ($lhs === 'subnet') {
- $filters[] = new SubnetStatisticsFilter($operator, $rhs);
+ $operator = $data['op'];
+ $argument = $data['argument'];
+
+ if ($filterType === 'gbram') {
+ $filters[] = new RamGbStatisticsFilter($operator, $argument);
+ } elseif ($filterType === 'runtime') {
+ $filters[] = new RuntimeStatisticsFilter($operator, $argument);
+ } elseif ($filterType === 'state') {
+ $filters[] = new StateStatisticsFilter($operator, $argument);
+ } elseif ($filterType === 'hddgb') {
+ $filters[] = new Id44StatisticsFilter($operator, $argument);
+ } elseif ($filterType === 'location') {
+ $filters[] = new LocationStatisticsFilter($operator, $argument);
+ } elseif ($filterType === 'subnet') {
+ $filters[] = new SubnetStatisticsFilter($operator, $argument);
} else {
- if (array_key_exists($lhs, self::$columns) && self::$columns[$lhs]['column']) {
- $filters[] = new StatisticsFilter($lhs, $operator, $rhs);
+ if (array_key_exists($filterType, self::$columns)) {
+ $filters[] = new StatisticsFilter($filterType, $operator, $argument);
} else {
- Message::addError('invalid-filter-key', $lhs);
+ Message::addError('invalid-filter-key', $filterType);
}
}
}
@@ -152,18 +125,107 @@ class StatisticsFilter
return $filters;
}
+ private static function loadFilterFromGet()
+ {
+ $ops = Request::get('op', [], 'array');
+ $currentValues = ArrayUtil::mergeByKey([
+ 'filter' => Request::get('filter', [], 'array'),
+ 'op' => $ops,
+ 'argument' => Request::get('arg', [], 'array'),
+ ]);
+ if (Request::get('show') === false && empty($ops)) {
+ $currentValues['lastseen'] = [
+ 'filter' => true,
+ 'op' => '>',
+ 'argument' => gmdate('Y-m-d', strtotime('-30 day')),
+ ];
+ }
+ return $currentValues;
+ }
+
/**
* @param \StatisticsFilterSet $filterSet
*/
- public static function renderFilterBox($show, $filterSet, $query)
+ public static function renderFilterBox($show, $filterSet)
{
+ // Build location list, with permissions
+ $locs = [];
+ if (Module::isAvailable('locations')) {
+ $allowed = $filterSet->getAllowedLocations();
+ foreach (Location::getLocations(-1, 0, true) as $loc) {
+ $locs[] = [
+ 'key' => $loc['locationid'],
+ 'value' => $loc['locationpad'] . ' ' . $loc['locationname'],
+ 'disabled' => $allowed !== false && !in_array($loc['locationid'], $allowed)
+ ? 'disabled' : '',
+ ];
+ }
+ }
+ // Get current settings from GET
+ $currentValues = self::loadFilterFromGet();
+ // Build column array for rendering
+ $columns = [];
+ foreach (self::$columns as $key => $col) {
+ if ($key === 'location') {
+ $col['values'] = $locs;
+ }
+ $col['key'] = $key;
+ $col['name'] = Dictionary::translateFile('filters', $key, true);
+ if ($col['type'] === 'int') {
+ $col['input'] = 'number';
+ } elseif ($col['type'] === 'string') {
+ $col['input'] = 'text';
+ } elseif ($col['type'] === 'date') {
+ $col['input'] = 'text';
+ $col['inputclass'] = 'is-date';
+ } elseif ($col['type'] === 'enum') {
+ $col['enum'] = true;
+ if (isset($col['values'][0])) {
+ if (!is_array($col['values'][0])) {
+ // Arrayize
+ $col['values'] = array_map(function($e) { return [
+ 'key' => $e,
+ 'value' => $e,
+ ]; }, $col['values']);
+ }
+ } else {
+ $col['values'] = array_map(function($v, $k) { return [
+ 'key' => $k,
+ 'value' => $v,
+ ]; }, $col['values'], array_keys($col['values']));
+ }
+ if (isset($currentValues[$key]['argument'])) {
+ // Current value from GET
+ foreach ($col['values'] as &$value) {
+ if ($value['key'] == $currentValues[$key]['argument']) {
+ $value['selected'] = 'selected';
+ }
+ }
+ }
+ }
+ // current value from GET
+ if (isset($currentValues[$key])) {
+ $col['currentvalue'] = $currentValues[$key]['argument'] ?? '';
+ if ($currentValues[$key]['filter']) {
+ $col['checked'] = 'checked';
+ } elseif (!isset($col['show']) || !$col['show']) {
+ $col['collapse'] = 'collapse';
+ }
+ } elseif (!isset($col['show']) || !$col['show']) {
+ $col['collapse'] = 'collapse';
+ }
+ // Current value, arrayize
+ foreach ($col['op'] as &$value) {
+ $value = ['op' => $value];
+ if (($currentValues[$key]['op'] ?? '=') === $value['op']) {
+ $value['selected'] = 'selected';
+ }
+ }
+ $columns[] = $col;
+ }
$data = array(
'show' => $show,
- 'query' => $query,
- 'delimiter' => StatisticsFilter::DELIMITER,
- 'sortDirection' => $filterSet->getSortDirection(),
- 'sortColumn' => $filterSet->getSortColumn(),
- 'columns' => json_encode(StatisticsFilter::$columns),
+ 'columns' => $columns,
);
if ($show === 'list') {
@@ -175,42 +237,16 @@ class StatisticsFilter
}
- $locsFlat = array();
- if (Module::isAvailable('locations')) {
- $allowed = $filterSet->getAllowedLocations();
- foreach (Location::getLocations() as $loc) {
- $locsFlat['L' . $loc['locationid']] = array(
- 'pad' => $loc['locationpad'],
- 'name' => $loc['locationname'],
- 'disabled' => $allowed !== false && !in_array($loc['locationid'], $allowed),
- );
- }
- }
-
Permission::addGlobalTags($data['perms'], null, ['view.summary', 'view.list']);
- $data['locations'] = json_encode($locsFlat);
Render::addTemplate('filterbox', $data);
}
- private static $query = false;
-
- public static function getQuery()
- {
- if (self::$query === false) {
- self::$query = Request::any('filters', false, 'string');
- if (self::$query === false) {
- self::$query = 'lastseen > ' . gmdate('Y-m-d', strtotime('-30 day'));
- }
- }
- return self::$query;
- }
-
/*
* Simple filters that map directly to DB columns
*/
- const OP_ORDINAL = ['!=', '<=', '>=', '=', '<', '>'];
- const OP_STRCMP = ['!~', '~', '=', '!='];
+ const OP_ORDINAL = ['=', '!=', '<', '>', '<=', '>='];
+ const OP_STRCMP = [ '=', '!=', '!~', '~'];
const OP_NOMINAL = ['!=', '='];
public static $columns;
@@ -221,127 +257,123 @@ class StatisticsFilter
{
self::$columns = [
+ 'clientip' => [
+ 'op' => self::OP_NOMINAL,
+ 'type' => 'string',
+ 'placeholder' => '1.2.3.4',
+ 'show' => true,
+ ],
+ 'hostname' => [
+ 'op' => self::OP_STRCMP,
+ 'type' => 'string',
+ 'placeholder' => 'pc.fqdn.example.com',
+ ],
'machineuuid' => [
'op' => self::OP_NOMINAL,
'type' => 'string',
- 'column' => true,
+ 'placeholder' => '88888888-4444-4444-121212121212',
+ 'show' => true,
],
'macaddr' => [
'op' => self::OP_NOMINAL,
'type' => 'string',
- 'column' => true,
+ 'placeholder' => '11-22-33-44-55-66',
],
'firstseen' => [
'op' => self::OP_ORDINAL,
'type' => 'date',
- 'column' => true,
+ 'placeholder' => '2020-10-15',
],
'lastseen' => [
'op' => self::OP_ORDINAL,
'type' => 'date',
- 'column' => true,
+ 'placeholder' => '2020-10-15',
],
'logintime' => [
'op' => self::OP_ORDINAL,
'type' => 'date',
- 'column' => true,
+ 'placeholder' => '2020-10-15',
],
'lastboot' => [
'op' => self::OP_ORDINAL,
'type' => 'date',
- 'column' => true
+ 'placeholder' => '2020-10-15',
],
'runtime' => [
'op' => self::OP_ORDINAL,
'type' => 'int',
- 'column' => true
],
'realcores' => [
'op' => self::OP_ORDINAL,
'type' => 'int',
- 'column' => true,
],
'systemmodel' => [
'op' => self::OP_STRCMP,
'type' => 'string',
- 'column' => true,
+ 'placeholder' => 'PC-365 (IBM)',
],
'cpumodel' => [
'op' => self::OP_STRCMP,
'type' => 'string',
- 'column' => true,
+ 'placeholder' => 'Pentium Pro 200',
],
'hddgb' => [
'op' => self::OP_ORDINAL,
'type' => 'int',
- 'column' => false,
+ 'placeholder' => 'GiB',
'map_sort' => 'id44mb'
],
'gbram' => [
'op' => self::OP_ORDINAL,
'type' => 'int',
'map_sort' => 'mbram',
- 'column' => false,
+ 'placeholder' => 'GiB',
],
'kvmstate' => [
'op' => self::OP_NOMINAL,
'type' => 'enum',
- 'column' => true,
'values' => ['ENABLED', 'DISABLED', 'UNSUPPORTED']
],
'badsectors' => [
'op' => self::OP_ORDINAL,
'type' => 'int',
- 'column' => true
- ],
- 'clientip' => [
- 'op' => self::OP_NOMINAL,
- 'type' => 'string',
- 'column' => true
- ],
- 'hostname' => [
- 'op' => self::OP_STRCMP,
- 'type' => 'string',
- 'column' => true
],
'subnet' => [
'op' => self::OP_NOMINAL,
'type' => 'string',
- 'column' => false
],
'currentuser' => [
'op' => self::OP_NOMINAL,
'type' => 'string',
- 'column' => true
+ 'placeholder' => 'login',
],
'state' => [
'op' => self::OP_NOMINAL,
'type' => 'enum',
- 'column' => true,
'values' => ['occupied', 'on', 'off', 'idle', 'standby']
],
'live_swapfree' => [
'op' => self::OP_ORDINAL,
'type' => 'int',
- 'column' => true
+ 'placeholder' => 'MiB',
],
'live_memfree' => [
'op' => self::OP_ORDINAL,
'type' => 'int',
- 'column' => true
+ 'placeholder' => 'MiB',
],
'live_tmpfree' => [
'op' => self::OP_ORDINAL,
'type' => 'int',
- 'column' => true
+ 'placeholder' => 'MiB',
],
];
if (Module::isAvailable('locations')) {
self::$columns['location'] = [
'op' => self::OP_STRCMP,
'type' => 'enum',
- 'column' => false,
- 'values' => array_keys(Location::getLocationsAssoc()),
+ 'show' => true,
+ // values filled on render NOCOMMIT
];
}
}
@@ -534,4 +566,4 @@ class IsClientStatisticsFilter extends StatisticsFilter
}
-StatisticsFilter::initConstants(); \ No newline at end of file
+StatisticsFilter::initConstants();
diff --git a/modules-available/statistics/lang/de/filters.json b/modules-available/statistics/lang/de/filters.json
new file mode 100644
index 00000000..3fe97532
--- /dev/null
+++ b/modules-available/statistics/lang/de/filters.json
@@ -0,0 +1,24 @@
+{
+ "badsectors": "Defekte Sektoren",
+ "clientip": "IP-Adresse",
+ "cpumodel": "CPU-Modell",
+ "currentuser": "Aktueller\/Letzter Benutzer",
+ "firstseen": "Erster Boot",
+ "gbram": "RAM (GB)",
+ "hddgb": "ID44 (GB)",
+ "hostname": "Hostname",
+ "kvmstate": "Virtualisierung",
+ "lastboot": "Letzter Boot",
+ "lastseen": "Letzte Aktivit\u00e4t",
+ "live_memfree": "RAM frei (MB)",
+ "live_swapfree": "swap frei (MB)",
+ "live_tmpfree": "ID44 frei (MB)",
+ "location": "Raum\/Ort",
+ "logintime": "Letzter Login",
+ "macaddr": "MAC-Adresse",
+ "machineuuid": "System-UUID",
+ "realcores": "CPU-Kerne (real)",
+ "runtime": "Laufzeit (Stunden)",
+ "state": "Zustand",
+ "systemmodel": "System-Modell"
+} \ No newline at end of file
diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json
index f48518ab..1916abfa 100644
--- a/modules-available/statistics/lang/de/template-tags.json
+++ b/modules-available/statistics/lang/de/template-tags.json
@@ -1,8 +1,7 @@
{
"lang_64bitSupport": "64\u2009Bit Gast-Support",
- "lang_add": "Hinzuf\u00fcgen",
- "lang_add_filter": "Filter hinzuf\u00fcgen",
"lang_address": "Adresse",
+ "lang_apply": "Anwenden",
"lang_biosDate": "Ver\u00f6ffentlichungsdatum",
"lang_biosFixes": "BIOS-Fehlerkorrekturen",
"lang_biosUpdate": "BIOS Update",
@@ -13,7 +12,6 @@
"lang_cores": "Kerne",
"lang_cpuCores": "CPU-Kerne",
"lang_cpuModel": "CPU",
- "lang_currentUser": "Aktueller\/Letzter Benutzer",
"lang_details": "Details",
"lang_devices": "Ger\u00e4te",
"lang_duration": "Dauer",
@@ -34,7 +32,6 @@
"lang_kvmSupport": "64\u2009Bit G\u00e4ste",
"lang_labelFilter": "Aktive Filter (UND-Logik)",
"lang_lastBoot": "Letzter Boot",
- "lang_lastLogin": "Letzer Login",
"lang_lastSeen": "Zuletzt gesehen",
"lang_listDropdown": "Als Text",
"lang_location": "Ort",
@@ -50,10 +47,8 @@
"lang_machineSummary": "Zusammenfassung",
"lang_maximumAbbrev": "Max.",
"lang_mediaIntegrityErrors": "\"Media Integrity Errors\"",
- "lang_memFree": "RAM frei (MB)",
"lang_memoryStats": "Arbeitsspeicher",
"lang_mobomodel": "Mainboard",
- "lang_model": "Modell",
"lang_modelCount": "Anzahl",
"lang_modelName": "Modellname",
"lang_modelNo": "Modell",
@@ -93,7 +88,6 @@
"lang_roomplan": "Raumplan",
"lang_runMode": "Betriebsmodus",
"lang_runmodeMachines": "Mit besonderem Betriebsmodus",
- "lang_runtimeHours": "Laufzeit (Stunden)",
"lang_screens": "Bildschirme",
"lang_serialNo": "Serien-Nr",
"lang_showList": "Liste",
@@ -101,16 +95,13 @@
"lang_shutdown": "Herunterfahren",
"lang_shutdownConfirm": "Ausgew\u00e4hlte Rechner wirklich herunterfahren?",
"lang_sockets": "Sockel",
- "lang_subnet": "Subnetz",
"lang_sureDeletePermanent": "M\u00f6chten Sie diese(n) Rechner wirklich unwiderruflich aus der Datenbank entfernen?\r\n\r\nWichtig: L\u00f6schen verhindert nicht, dass ein Rechner nach erneutem Starten von bwLehrpool wieder in die Datenbank aufgenommen wird.",
"lang_sureReplaceNoUndo": "Wollen Sie die Daten der ausgew\u00e4hlten Rechner \u00fcbertragen? Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
"lang_swap": "Swap",
- "lang_swapFree": "swap frei (MB)",
"lang_tempPart": "Temp. Partition",
"lang_tempPartStats": "Tempor\u00e4re Partition",
"lang_thoseAreProjectors": "Diese Modellnamen werden als Beamer behandelt, auch wenn die EDID-Informationen des Ger\u00e4tes anderes berichten.",
"lang_timebarDesc": "Visuelle Darstellung der letzten Tage. Rote Abschnitte zeigen, wann der Rechner belegt war, gr\u00fcne, wann er nicht verwendet wurde, aber eingeschaltet war. Die leicht abgedunkelten Abschnitte markieren N\u00e4chte (22 bis 8 Uhr).",
- "lang_tmpFree": "ID44 frei (MB)",
"lang_tmpGb": "Temp-HDD",
"lang_total": "Gesamt",
"lang_usageDetails": "Nutzungsdetails",
diff --git a/modules-available/statistics/lang/en/filters.json b/modules-available/statistics/lang/en/filters.json
new file mode 100644
index 00000000..bd262d32
--- /dev/null
+++ b/modules-available/statistics/lang/en/filters.json
@@ -0,0 +1,24 @@
+{
+ "badsectors": "Bad sectors",
+ "clientip": "IP address",
+ "cpumodel": "CPU model",
+ "currentuser": "Current\/last user",
+ "firstseen": "First boot",
+ "gbram": "RAM (GB)",
+ "hddgb": "ID44 (GB)",
+ "hostname": "Host name",
+ "kvmstate": "Virtualization",
+ "lastboot": "Last boot",
+ "lastseen": "Last activity",
+ "live_memfree": "RAM free (MB)",
+ "live_swapfree": "swap free (MB)",
+ "live_tmpfree": "ID44 free (MB)",
+ "location": "Room\/Location",
+ "logintime": "Last login",
+ "macaddr": "MAC address",
+ "machineuuid": "System UUID",
+ "realcores": "CPU cores (real)",
+ "runtime": "Uptime (hours)",
+ "state": "State",
+ "systemmodel": "System model"
+} \ No newline at end of file
diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json
index a3260727..fca82b83 100644
--- a/modules-available/statistics/lang/en/template-tags.json
+++ b/modules-available/statistics/lang/en/template-tags.json
@@ -1,8 +1,7 @@
{
"lang_64bitSupport": "64\u2009Bit guest support",
- "lang_add": "Add",
- "lang_add_filter": "Add Filter",
"lang_address": "Address",
+ "lang_apply": "Apply",
"lang_biosDate": "Release date",
"lang_biosFixes": "BIOS fixes",
"lang_biosUpdate": "BIOS update",
@@ -13,7 +12,6 @@
"lang_cores": "Cores",
"lang_cpuCores": "CPU cores",
"lang_cpuModel": "CPU",
- "lang_currentUser": "Current\/last user",
"lang_details": "Details",
"lang_devices": "Devices",
"lang_duration": "Duration",
@@ -34,7 +32,6 @@
"lang_kvmSupport": "64\u2009Bit guests",
"lang_labelFilter": "Active filters (AND logic)",
"lang_lastBoot": "Last boot",
- "lang_lastLogin": "Last login",
"lang_lastSeen": "Last seen",
"lang_listDropdown": "As text",
"lang_location": "Location",
@@ -50,10 +47,8 @@
"lang_machineSummary": "Summary",
"lang_maximumAbbrev": "max.",
"lang_mediaIntegrityErrors": "Media Integrity Errors",
- "lang_memFree": "RAM free (MB)",
"lang_memoryStats": "Memory",
"lang_mobomodel": "Mainboard",
- "lang_model": "Model",
"lang_modelCount": "Count",
"lang_modelName": "Model name",
"lang_modelNo": "Model",
@@ -93,7 +88,6 @@
"lang_roomplan": "Location",
"lang_runMode": "Mode of operation",
"lang_runmodeMachines": "With special mode of operation",
- "lang_runtimeHours": "Runtime (hours)",
"lang_screens": "Screens",
"lang_serialNo": "Serial no",
"lang_showList": "List",
@@ -101,16 +95,13 @@
"lang_shutdown": "Shutdown",
"lang_shutdownConfirm": "Shutdown selected machines?",
"lang_sockets": "Sockets",
- "lang_subnet": "Subnet",
"lang_sureDeletePermanent": "Are your sure you want to delete the selected machine(s) from the database? This cannot be undone.\r\n\r\nNote: Deleting machines from the database does not prevent booting up bwLehrpool again, which would recreate their respective database entries.",
"lang_sureReplaceNoUndo": "Are you sure you want to replace the selected machine pairs? This action cannot be undone.",
"lang_swap": "swap",
- "lang_swapFree": "swap free (MB)",
"lang_tempPart": "Temp. partition",
"lang_tempPartStats": "Temporary partition",
"lang_thoseAreProjectors": "These model names will always be treated as beamers, even if the device's EDID data says otherwise.",
"lang_timebarDesc": "Visual representation of the last few days. Red parts mark periods where the client was occupied, green parts where the client was idle. Dimmed parts mark nights (10pm to 8am).",
- "lang_tmpFree": "ID44 free (MB)",
"lang_tmpGb": "Temp HDD",
"lang_total": "Total",
"lang_usageDetails": "Detailed usage",
diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php
index 533a9bf9..deba6681 100644
--- a/modules-available/statistics/page.inc.php
+++ b/modules-available/statistics/page.inc.php
@@ -19,6 +19,7 @@ class Page_Statistics extends Page
}
if (Request::isGet()) {
+ $this->transformLegacyQuery();
$this->show = Request::any('show', false, 'string');
if ($this->show === false) {
if (Request::get('uuid') !== false) {
@@ -85,6 +86,27 @@ class Page_Statistics extends Page
Util::redirect('?do=statistics');
}
+ private function transformLegacyQuery()
+ {
+ if (!Request::isGet())
+ return;
+ $query = Request::get('filters', false, 'string');
+ if ($query === false)
+ return;
+ foreach (explode(StatisticsFilter::LEGACY_DELIMITER, $query) as $q) {
+ if (!preg_match('/^\s*(\w+)\s*([<>=!~]{1,2})\s*(.*?)\s*$/', $q, $out))
+ continue;
+ $key = $out[1];
+ $_GET['filter'][$key] = '1';
+ $_GET['op'][$key] = $out[2];
+ $_GET['arg'][$key] = $out[3];
+ }
+ unset($_GET['filters']);
+ if (!empty($_GET['filter'])) {
+ Util::redirect('?' . http_build_query($_GET));
+ }
+ }
+
private function wol()
{
if (!Module::isAvailable('rebootcontrol'))
@@ -201,7 +223,7 @@ class Page_Statistics extends Page
$sortColumn = Request::any('sortColumn');
$sortDirection = Request::any('sortDirection');
- $filters = StatisticsFilter::parseQuery(StatisticsFilter::getQuery());
+ $filters = StatisticsFilter::parseQuery();
$filterSet = new StatisticsFilterSet($filters);
$filterSet->setSort($sortColumn, $sortDirection);
diff --git a/modules-available/statistics/pages/list.inc.php b/modules-available/statistics/pages/list.inc.php
index 4df60044..a709ab3d 100644
--- a/modules-available/statistics/pages/list.inc.php
+++ b/modules-available/statistics/pages/list.inc.php
@@ -13,7 +13,7 @@ class SubPage
$sortColumn = Request::any('sortColumn');
$sortDirection = Request::any('sortDirection');
- $filters = StatisticsFilter::parseQuery(StatisticsFilter::getQuery());
+ $filters = StatisticsFilter::parseQuery();
$filterSet = new StatisticsFilterSet($filters);
$filterSet->setSort($sortColumn, $sortDirection);
@@ -21,9 +21,7 @@ class SubPage
Message::addError('main.no-permission');
Util::redirect('?do=main');
}
- Render::openTag('div', array('class' => 'row'));
- StatisticsFilter::renderFilterBox('list', $filterSet, StatisticsFilter::getQuery());
- Render::closeTag('div');
+ StatisticsFilter::renderFilterBox('list', $filterSet);
self::showMachineList($filterSet);
}
@@ -119,11 +117,6 @@ class SubPage
$data = array(
'rowCount' => count($rows),
'rows' => $rows,
- 'query' => StatisticsFilter::getQuery(),
- 'delimiter' => StatisticsFilter::DELIMITER,
- 'sortDirection' => $filterSet->getSortDirection(),
- 'sortColumn' => $filterSet->getSortColumn(),
- 'columns' => json_encode(StatisticsFilter::$columns),
'showList' => 1,
'show' => 'list',
'redirect' => $_SERVER['QUERY_STRING'],
diff --git a/modules-available/statistics/pages/summary.inc.php b/modules-available/statistics/pages/summary.inc.php
index bf150f3e..c2e3ac80 100644
--- a/modules-available/statistics/pages/summary.inc.php
+++ b/modules-available/statistics/pages/summary.inc.php
@@ -15,7 +15,7 @@ class SubPage
$sortColumn = Request::any('sortColumn');
$sortDirection = Request::any('sortDirection');
- $filters = StatisticsFilter::parseQuery(StatisticsFilter::getQuery());
+ $filters = StatisticsFilter::parseQuery();
$filterSet = new StatisticsFilterSet($filters);
$filterSet->setSort($sortColumn, $sortDirection);
@@ -31,8 +31,8 @@ class SubPage
}
$filterSet->filterNonClients();
+ StatisticsFilter::renderFilterBox('summary', $filterSet);
Render::openTag('div', array('class' => 'row'));
- StatisticsFilter::renderFilterBox('summary', $filterSet, StatisticsFilter::getQuery());
self::showSummary($filterSet);
self::showMemory($filterSet);
self::showId44($filterSet);
@@ -87,7 +87,6 @@ class SubPage
}
}
$data['json'] = json_encode(array('labels' => $labels, 'datasets' => array($points1, $points2)));
- $data['query'] = StatisticsFilter::getQuery();
if (Module::get('runmode') !== false) {
$res = Database::queryFirst('SELECT Count(*) AS cnt FROM runmode');
$data['runmode'] = $res['cnt'];
@@ -123,7 +122,7 @@ class SubPage
++$id;
}
self::capChart($json, $lines, 0.92);
- Render::addTemplate('cpumodels', array('rows' => $lines, 'query' => StatisticsFilter::getQuery(), 'json' => json_encode($json)));
+ Render::addTemplate('cpumodels', array('rows' => $lines, 'json' => json_encode($json)));
}
/**
@@ -167,7 +166,6 @@ class SubPage
}
self::capChart($json, $data['rows'], 0.92);
$data['json'] = json_encode($json);
- $data['query'] = StatisticsFilter::getQuery();
Render::addTemplate('memory', $data);
}
@@ -189,7 +187,7 @@ class SubPage
'value' => $row['count'],
);
}
- Render::addTemplate('kvmstate', array('rows' => $lines, 'query' => StatisticsFilter::getQuery(),'json' => json_encode($json)));
+ Render::addTemplate('kvmstate', array('rows' => $lines, 'json' => json_encode($json)));
}
/**
@@ -239,7 +237,6 @@ class SubPage
}
self::capChart($json, $data['rows'], 0.95);
$data['json'] = json_encode($json);
- $data['query'] = StatisticsFilter::getQuery();
Render::addTemplate('id44', $data);
}
@@ -313,4 +310,4 @@ class SubPage
}
}
-} \ No newline at end of file
+}
diff --git a/modules-available/statistics/style.css b/modules-available/statistics/style.css
index 000cefaf..7e1539ec 100644
--- a/modules-available/statistics/style.css
+++ b/modules-available/statistics/style.css
@@ -63,4 +63,33 @@
.open > .dropdown-menu {
min-width: inherit;
-} \ No newline at end of file
+}
+
+.filter-list .filter-row {
+ margin-bottom: 2px;
+}
+
+.filter-list input, .filter-list select {
+ padding: 3px 7px;
+}
+
+@media(min-width: 992px) {
+ .filter-list {
+ column-count:2;
+ column-gap:20px;
+ column-rule: 1px solid #eee;
+ }
+}
+
+.slx-focus {
+ animation-name: slxFocus;
+ animation-duration: .3s;
+ animation-iteration-count: 2;
+ animation-timing-function: ease;
+}
+
+@keyframes slxFocus {
+ 0% { background: unset }
+ 50% { background: #f2dede }
+ 100% { background: unset }
+}
diff --git a/modules-available/statistics/templates/cpumodels.html b/modules-available/statistics/templates/cpumodels.html
index d89a5b2f..2e36a287 100644
--- a/modules-available/statistics/templates/cpumodels.html
+++ b/modules-available/statistics/templates/cpumodels.html
@@ -19,11 +19,11 @@
<tr id="{{id}}" class="{{collapse}}">
<td data-sort-value="{{systemmodel}}" class="text-left text-nowrap filter-col" data-filter-col="systemmodel">
<table style="width:100%; table-layout: fixed;"><tr><td style="overflow:hidden;text-overflow: ellipsis;">
- <a class="filter-val" data-filter-val="{{systemmodel}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~systemmodel={{urlsystemmodel}}">{{systemmodel}}</a>
+ <a class="filter-val" data-filter-val="{{systemmodel}}" href="#">{{systemmodel}}</a>
</td></tr></table>
</td>
<td data-sort-value="{{cores}}" class="text-right filter-col" data-filter-col="realcores">
- <a class="filter-val" data-filter-val="{{cores}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~realcores={{cores}}">{{cores}}</a>
+ <a class="filter-val" data-filter-val="{{cores}}" href="#">{{cores}}</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
diff --git a/modules-available/statistics/templates/filterbox.html b/modules-available/statistics/templates/filterbox.html
index f71c918d..34f4d3a6 100644
--- a/modules-available/statistics/templates/filterbox.html
+++ b/modules-available/statistics/templates/filterbox.html
@@ -1,201 +1,104 @@
-<div id="modal-add-filter" class="modal fade" role="dialog" style="position: absolute">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <b>{{lang_add_filter}}</b>
- </div>
- <form class="form-inline center" onsubmit="$('#add-btn').click(); return false">
- <div class="modal-body">
- <div class="form-group">
- <select id="columnSelect" name="column" class="form-control col-4-xs"> </select>
- </div>
- <div class="form-group">
- <select id="operatorSelect" name="operator" class="form-control col-4-xs"> </select>
- </div>
- <div class="form-group">
- <input name="argument" id="argumentInput" class="form-control col-4-xs">
- <select name="argument" id="argumentSelect" class="form-control col-4-xs"> </select>
- </div>
+<a href="#top" class="btn btn-default to-top-btn"><span class="glyphicon glyphicon-menu-up"></span></a>
+<h1>{{lang_moduleHeading}}</h1>
+
+<form id="query-form" method="GET" action="?do=statistics" role="form">
+ <input type="hidden" name="show" value="{{show}}">
+ <input type="hidden" name="do" value="statistics">
+ <div class="btn-group pull-right">
+ <button type="submit" hidden><!-- first button, so hitting enter in the form fields doesn't jump to summary -->
+ <button class="btn btn-default {{statButtonClass}}" type="submit" name="show" value="summary"
+ {{perms.view.summary.disabled}}>
+ <span class="glyphicon glyphicon-stats"></span>
+ {{lang_showVisualization}}
+ </button>
+ <button class="btn btn-default {{listButtonClass}}" type="submit" name="show" value="list"
+ {{perms.view.list.disabled}}>
+ <span class="glyphicon glyphicon-list"></span>
+ {{lang_showList}}
+ </button>
+ </div>
+
+ <h3>{{lang_labelFilter}}</h3>
+ <div class="filter-list">
+ {{#columns}}
+ <div class="{{collapse}} row filter-row" id="filter-{{key}}">
+ <div class="col-sm-4 col-xs-12">
+ <div class="checkbox checkbox-inline text-nowrap">
+ <input id="check-{{key}}" type="checkbox" name="filter[{{key}}]" value="1" class="filter-enable"
+ {{checked}}>
+ <label for="check-{{key}}">{{name}}</label>
</div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
- <button id="add-btn" type="button" class="btn btn-success" onclick="addFilterFromForm()">
- <span class="glyphicon glyphicon-plus"></span>
- {{lang_add}}
- </button>
- </div>
- </form>
+ </div>
+ <div class="col-sm-2 col-xs-3">
+ <select name="op[{{key}}]" class="form-control enable-check op">
+ {{#op}}
+ <option {{selected}}>{{op}}</option>
+ {{/op}}
+ </select>
+ </div>
+ <div class="col-sm-6 col-xs-9">
+ {{#input}}
+ <input name="arg[{{key}}]" type="{{.}}" class="form-control enable-check {{inputclass}} arg"
+ value="{{currentvalue}}" placeholder="{{placeholder}}">
+ {{/input}}
+ {{#enum}}
+ <select name="arg[{{key}}]" class="form-control enable-check arg">
+ {{#values}}
+ <option value="{{key}}" {{disabled}} {{selected}}>{{value}}</option>
+ {{/values}}
+ </select>
+ {{/enum}}
+ </div>
</div>
+ <div class="clearfix"></div><!-- fixes jumping around of items on firefox -->
+ {{/columns}}
</div>
-</div>
-
-<a href="#top" class="btn btn-default to-top-btn"><span class="glyphicon glyphicon-menu-up"></span></a>
-
-
-<div class="col-md-12">
- <!-- use GET here, to avoid the "resend form?" confirmation, and anyway this is stateless, so GET makes more sense -->
- <form id="queryForm" method="GET" action="?do=Statistics" class="" role="form">
- <input type="hidden" name="show" value="{{show}}">
- <button type="submit" hidden></button>
-
-
- <div class="btn-group pull-right">
- <button class="btn btn-default {{statButtonClass}}" type="submit" name="show" value="summary" {{perms.view.summary.disabled}}>
- <span class="glyphicon glyphicon-stats"></span>
- {{lang_showVisualization}}
+ <div class="row">
+ <div class="col-md-12 col-sm-10 col-xs-12">
+ <button type="submit" class="btn btn-primary pull-right">
+ <span class="glyphicon glyphicon-ok"></span>
+ {{lang_apply}}
</button>
- <button class="btn btn-default {{listButtonClass}}" type="submit" name="show" value="list" {{perms.view.list.disabled}}>
- <span class="glyphicon glyphicon-list"></span>
- {{lang_showList}}
+ <button type="button" class="btn btn-default" id="filter-expand">
+ <span class="glyphicon glyphicon-arrow-down"></span>
+ {{lang_more}}
</button>
</div>
- <h1>{{lang_moduleHeading}}</h1>
-
- <br/>
-
- <input type="hidden" name="do" value="statistics">
- <input type="hidden" name="sortColumn" id="sortColumn" value="{{sortColumn}}"/>
- <input type="hidden" name="sortDirection" id="sortDirection" value="{{sortDirection}}"/>
-
- <label for="filterInput">{{lang_labelFilter}}</label>
- <div class="row">
- <div class="col-md-12">
- <div class="input-group">
- <input type="text" name="filters" class="" id="filterInput"/>
- <span class="input-group-btn" style=" width: 1%; padding-bottom: 5px;">
- <button type="button" class="btn btn-success" onclick="popupFilter(null)">
- <span class="glyphicon glyphicon-plus"></span>
- {{lang_add_filter}}
- </button>
- </span>
- </div>
- </div>
- </div>
-
- <br/>
- </form>
-</div>
+ </div>
+ <div class="clearfix slx-space"></div>
+</form>
<script type="application/javascript"><!--
-var filterSelectize;
-
-var slxFilterNames = {
- machineuuid: '{{lang_uuid}}',
- macaddr: '{{lang_macAddr}}',
- firstseen: '{{lang_firstSeen}}',
- lastseen: '{{lang_lastSeen}}',
- lastboot: '{{lang_lastBoot}}',
- logintime: '{{lang_lastLogin}}',
- realcores: '{{lang_cores}}',
- systemmodel: '{{lang_model}}',
- cpumodel: '{{lang_cpuModel}}',
- hddgb: '{{lang_tmpGb}}',
- gbram: '{{lang_gbRam}}',
- kvmstate: '{{lang_kvmSupport}}',
- badsectors: '{{lang_reallocatedSectors}}',
- clientip: '{{lang_ip}}',
- state: '{{lang_usageState}}',
- location: '{{lang_location}}',
- currentuser: '{{lang_currentUser}}',
- subnet: '{{lang_subnet}}',
- runtime: '{{lang_runtimeHours}}',
- hostname: '{{lang_hostname}}',
- live_swapfree: '{{lang_swapFree}}',
- live_memfree: '{{lang_memFree}}',
- live_tmpfree: '{{lang_tmpFree}}'
-};
-
-slxLocations = {{{locations}}};
-
-var slxFilterDel = '{{delimiter}}';
-var $modal, $queryForm;
-
-
document.addEventListener("DOMContentLoaded", function () {
- /* some objects */
- var $columnSelect = $('#columnSelect');
- $modal = $('#modal-add-filter');
- $queryForm = $('#queryForm');
-
- var columns= {{{columns}}};
+ $('.is-date').datepicker({format : 'yyyy-mm-dd'});
- /* add options to column select */
- for (var key in columns) {
- $columnSelect.append($('<option>', {
- value: key, text: (slxFilterNames[key] ? slxFilterNames[key] : key) }));
- };
-
-
- /* initialize selectize */
- filterSelectize = $('#filterInput').selectize({
- delimiter: slxFilterDel,
- persist: false,
- plugins: ['remove_button'],
- create: function(input) {
- return {value: input, text: input}
- },
- onChange: function() {
- // if (initComplete && !$('#filterInput').is(':focus')) {
- // reload();
- // }
- },
- onItemRemove: function(value) {
- refresh();
- }
- })[0].selectize;
- /* add query */
- var str = "{{{query}}}";
- var eExp = /^(\w+)\s*([=><!~]+)\s*(.*)$/;
- str.split(slxFilterDel).forEach(function(v) {
- if (v.trim().length === 0)
- return;
- var match = eExp.exec(v);
- if (match && match.length === 4) {
- addFilter(match[1], match[2], match[3]);
- } else {
- filterSelectize.addOption({value: v, text: v});
- filterSelectize.addItem(v);
- }
+ $('#filter-expand').click(function() {
+ $('#query-form .filter-row.collapse').show();
+ $(this).remove();
});
- $('#columnSelect').on('change', function() {
- $('#operatorSelect option').remove();
- var col = $('#columnSelect').val();
- var opS = $('#operatorSelect');
- columns[col]['op'].sort(myOpSort);
- columns[col]['op'].forEach(function (v) {
- $(opS).append($('<option>', {
- value: v, text: v
- }));
+ // Cosmetic - less clutter in URL
+ $('#query-form').submit(function(e) {
+ $(this).find('.filter-row').each(function() {
+ var $row = $(this);
+ if ($row.find('.filter-enable').prop('checked'))
+ return;
+ $row.find('input, select').prop('name', '');
});
- /* also set the type of the input */
- if (columns[col]['type'] === 'date') {
- $('#argumentInput').datepicker({format : 'yyyy-mm-dd'});
- $('#argumentSelect').hide();
- } else if(columns[col]['type'] === 'enum') {
- $('#argumentSelect').empty();
- $('#argumentInput').hide();
- $('#argumentSelect').show();
- columns[col]['values'].forEach(function (v) {
- var t = v;
- var disabled = (col === 'location');
- if (col === 'location' && slxLocations['L' + v]) {
- t = slxLocations['L' + v].pad + ' ' + slxLocations['L' + v].name;
- disabled = slxLocations['L' + v].disabled;
- }
- $('#argumentSelect').append($('<option>', { value: v, text: t, disabled: disabled }));
- });
- } else {
- $('#argumentInput').datepicker('remove');
- $('#argumentSelect option').remove();
- $('#argumentInput').show();
- $('#argumentSelect').hide();
- }
});
+ var check = function() {
+ $(this).closest('.filter-row').find('.filter-enable:visible').prop('checked', true);
+ };
+
+ // This sucks - we need to wait a bit otherwise datepicker triggers change
+ setTimeout(function() {
+ $('.enable-check').change(check).keypress(check);
+ }, 100);
+
$('.filter-col').each(function(idx, elem) {
var e = $(elem);
var col = e.data('filter-col');
@@ -203,7 +106,7 @@ document.addEventListener("DOMContentLoaded", function () {
e.find('.filter-val').each(function(idx, elem) {
var e = $(elem);
var val = e.data('filter-val');
- if (!val) return;
+ if (val === null || val === undefined) return;
e.click(function(ev) {
ev.preventDefault();
addFilter(col, '=', val);
@@ -214,58 +117,35 @@ document.addEventListener("DOMContentLoaded", function () {
}, false);
-window.addEventListener('unload', function() {
- filterSelectize.clear();
-});
-
function popupFilter(field) {
- if (field != null) {
- $('#columnSelect').val(field);
- }
- $('#columnSelect').change();
- $modal.modal('show');
-}
-
-function addFilterFromForm() {
- var argument1 = $('#argumentInput').val();
- var argument2 = $('#argumentSelect').val();
- var argument = argument1 ? argument1 : argument2;
- var col = $('#columnSelect').val();
- var op = $('#operatorSelect').val();
-
- addFilter(col, op, argument);
- refresh(); // TODO: AJAX
+ var $row = addFilter(field, null, null);
+ if ($row !== null) {
+ $row.find('.arg').focus();
+ $row.removeClass('slx-focus')
+ setTimeout(function() { $row.addClass('slx-focus'); }, 10);
+ }
}
-function addFilter(col, op, argument) {
- var filterValue = col + ' ' + op + ' ' + argument;
- var filterText = filterValue;
- var displayArgument = argument;
- if (col === 'location' && slxLocations['L' + argument]) {
- displayArgument = slxLocations['L' + argument].name;
+function addFilter(field, op, argument) {
+ if (field === null)
+ return null;
+ var $row = $('#filter-' + field);
+ if ($row.length === 0)
+ return null;
+ if (argument !== null) {
+ $row.find('.op').val(op);
+ $row.find('.arg').val(argument);
}
- if (slxFilterNames[col]) {
- filterText = slxFilterNames[col] + ' ' + op + ' ' + displayArgument;
+ // Enable checkbox only if we got a predefined value, or if argument is in a select, as the user might want the preselected item and doesn't notice the checkbox is unchecked
+ if (argument !== null || $row.find('select.arg').length !== 0) {
+ $row.find('.filter-enable').prop('checked', true);
}
- filterSelectize.addOption({value: filterValue, text: filterText});
- filterSelectize.addItem(filterValue);
-}
-
-function toggleSort(field) {
- $('#sort').val(field + ' ' + order);
- refresh();
-}
-
-/* equal sign should always be first, the rest doesn't matter*/
-function myOpSort(a,b) {
- if (a === '=') { return -1; }
- else if (a === b) {return 0}
- else { return 1;}
-
+ $row.show();
+ return $row;
}
function refresh() {
- $queryForm.submit(); /* TODO: use AJAX */
+ $('#query-form').submit();
}
// --></script>
diff --git a/modules-available/statistics/templates/id44.html b/modules-available/statistics/templates/id44.html
index de1c71ad..ec0dac09 100644
--- a/modules-available/statistics/templates/id44.html
+++ b/modules-available/statistics/templates/id44.html
@@ -17,7 +17,7 @@
{{#rows}}
<tr id="tmpid{{gb}}" class="{{class}} {{collapse}}">
<td data-sort-value="{{gb}}" class="text-left text-nowrap">
- <a class="filter-val" data-filter-val="{{gb}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~hddgb={{gb}}">{{gb}}&thinsp;GiB</a>
+ <a class="filter-val" data-filter-val="{{gb}}" href="#">{{gb}}&thinsp;GiB</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
diff --git a/modules-available/statistics/templates/kvmstate.html b/modules-available/statistics/templates/kvmstate.html
index efa3bad3..4f8994d1 100644
--- a/modules-available/statistics/templates/kvmstate.html
+++ b/modules-available/statistics/templates/kvmstate.html
@@ -17,7 +17,7 @@
{{#rows}}
<tr id="kvm{{kvmstate}}">
<td data-sort-value="{{kvmstate}}" class="text-left text-nowrap">
- <a class="filter-val" data-filter-val="{{kvmstate}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~kvmstate={{kvmstate}}">{{kvmstate}}</a>
+ <a class="filter-val" data-filter-val="{{kvmstate}}" href="#">{{kvmstate}}</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
diff --git a/modules-available/statistics/templates/memory.html b/modules-available/statistics/templates/memory.html
index cfb86062..f6f4c446 100644
--- a/modules-available/statistics/templates/memory.html
+++ b/modules-available/statistics/templates/memory.html
@@ -17,7 +17,7 @@
{{#rows}}
<tr id="ramid{{gb}}" class="{{class}} {{collapse}}">
<td class="text-left text-nowrap" data-sort-value="{{gb}}">
- <a class="filter-val" data-filter-val="{{gb}}" href="?do=Statistics&amp;show=summary&amp;filters={{query}}~,~gbram={{gb}}">{{gb}}&thinsp;GiB</a>
+ <a class="filter-val" data-filter-val="{{gb}}" href="#">{{gb}}&thinsp;GiB</a>
</td>
<td class="text-right">{{count}}</td>
</tr>