diff options
author | Simon Rettberg | 2017-06-10 16:15:22 +0200 |
---|---|---|
committer | Simon Rettberg | 2017-06-10 16:15:22 +0200 |
commit | a0b42aa257d0a6c56cd4a099aa0a2cea4a8dc2c9 (patch) | |
tree | 3e1655c828e0cb6f069b8c17ddf260237e0f4ce2 | |
parent | [inc/Util] Add randomUuid() function (diff) | |
download | slx-admin-a0b42aa257d0a6c56cd4a099aa0a2cea4a8dc2c9.tar.gz slx-admin-a0b42aa257d0a6c56cd4a099aa0a2cea4a8dc2c9.tar.xz slx-admin-a0b42aa257d0a6c56cd4a099aa0a2cea4a8dc2c9.zip |
[locationinfo] Started rewrite for panel-based approach
13 files changed, 851 insertions, 786 deletions
diff --git a/modules-available/locationinfo/clientscript.js b/modules-available/locationinfo/clientscript.js new file mode 100644 index 00000000..b18cc5bd --- /dev/null +++ b/modules-available/locationinfo/clientscript.js @@ -0,0 +1,143 @@ +/* + * Generic helpers. + */ + +/** + * Initialize timepicker on given element. + */ +function setTimepicker($e) { + $e.timepicker({ + minuteStep: 15, + appendWidgetTo: 'body', + showSeconds: false, + showMeridian: false, + defaultTime: false + }); +} + +function getTime(str) { + if (!str) return false; + str = str.split(':'); + if (str.length !== 2) return false; + var h = parseInt(str[0].replace(/^0/, '')); + var m = parseInt(str[1].replace(/^0/, '')); + if (h < 0 || h > 23) return false; + if (m < 0 || m > 59) return false; + return h * 60 + m; +} + +const allDays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; + +/* + * Opening times related... + */ + +/** + * Adds a new opening time to the table in expert mode. + */ +function newOpeningTime(vals) { + var $row = $('#expert-template div.row').clone(); + if (vals['days'] && Array.isArray(vals['days'])) { + for (var i = 0; i < allDays.length; ++i) { + $row.find('.i-' + allDays[i]).attr('checked', vals['days'].indexOf(allDays[i]) !== -1); + } + } + $row.find('.i-openingtime').val(vals['openingtime']); + $row.find('.i-closingtime').val(vals['closingtime']); + $('#expert-table').append($row); + return $row; +} + +/** + * Convert fields from simple mode view to entries in expert mode. + * @returns {Array} + */ +function simpleToExpert() { + var retval = []; + if ($('#week-open').val() || $('#week-close').val()) { + retval.push({ + 'days': ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], + 'openingtime': $('#week-open').val(), + 'closingtime': $('#week-close').val(), + 'tag': '#week' + }); + } + if ($('#saturday-open').val() || $('#saturday-close').val()) { + retval.push({ + 'days': ['Saturday'], + 'openingtime': $('#saturday-open').val(), + 'closingtime': $('#saturday-close').val(), + 'tag': '#saturday' + }); + } + if ($('#sunday-open').val() || $('#sunday-close').val()) { + retval.push({ + 'days': ['Sunday'], + 'openingtime': $('#sunday-open').val(), + 'closingtime': $('#sunday-close').val(), + 'tag': '#sunday' + }); + } + return retval; +} + +/** + * Triggered when the form is submitted + */ +function submitLocationSettings(event) { + var schedule, s, e; + var badFormat = false; + $('#settings-outer').find('.red-bg').removeClass('red-bg'); + if ($('#week-open').length > 0) { + schedule = simpleToExpert(); + for (var i = 0; i < schedule.length; ++i) { + s = getTime(schedule[i].openingtime); + e = getTime(schedule[i].closingtime); + if (s === false) { + $(schedule[i].tag + '-open').addClass('red-bg'); + badFormat = true; + } + if (e === false || e <= s) { + $(schedule[i].tag + '-close').addClass('red-bg'); + badFormat = true; + } + } + } else { + // Serialize + schedule = []; + $('#expert-table').find('.expert-row').each(function () { + var $t = $(this); + if ($t.find('.i-delete').is(':checked')) return; // Skip marked as delete + var entry = { + 'days': [], + 'openingtime': $t.find('.i-openingtime').val(), + 'closingtime': $t.find('.i-closingtime').val() + }; + s = getTime(entry.openingtime); + e = getTime(entry.closingtime); + if (s === false) { + $t.find('.i-openingtime').addClass('red-bg'); + badFormat = true; + } + if (e === false || e <= s) { + $t.find('.i-closingtime').addClass('red-bg'); + badFormat = true; + } + if (badFormat) return; + for (var i = 0; i < allDays.length; ++i) { + if ($t.find('.i-' + allDays[i]).is(':checked')) { + entry['days'].push(allDays[i]); + } + } + if (entry.days.length === 0) { + $t.find('.days-box').addClass('red-bg'); + badFormat = true; + } + schedule.push(entry); + }); + } + if (badFormat) { + event.preventDefault(); + } + $('#json-openingtimes').val(JSON.stringify(schedule)); +}
\ No newline at end of file diff --git a/modules-available/locationinfo/lang/de/template-tags.json b/modules-available/locationinfo/lang/de/template-tags.json index 253ae674..379730e5 100644 --- a/modules-available/locationinfo/lang/de/template-tags.json +++ b/modules-available/locationinfo/lang/de/template-tags.json @@ -24,8 +24,6 @@ "lang_general": "Allgemein", "lang_language": "Sprache", "lang_languageTooltip": "Legt die Sprache der angezeigten Oberfl\u00e4che fest", - "lang_locationIsHidden": "Versteckt", - "lang_locationIsHidden_title": "Wenn aktiv, dann liefert die API keine Informationen \u00fcber diesen Raum", "lang_locationName": "Name", "lang_locationSettings": "Einstellungen", "lang_mainHeader": "Infoscreen", @@ -38,8 +36,9 @@ "lang_monTilFr": "Montag - Freitag", "lang_nameTooltip": "Legt den Namen des Servers fest", "lang_noServer": "<Kein Server>", - "lang_openingTime": "\u00d6ffnungszeiten", + "lang_openingTime": "\u00d6ffnungszeit", "lang_pleaseSelect": "Bitte w\u00e4hlen...", + "lang_remoteSchedule": "Abruf Belegungsplan", "lang_room": "Raum", "lang_roomId": "Raum ID", "lang_roomIdTooltip": "Die Raum ID, die der Server ben\u00f6tigt, um Kalenderdaten abzurufen", diff --git a/modules-available/locationinfo/lang/en/template-tags.json b/modules-available/locationinfo/lang/en/template-tags.json index 60e5c9b5..6df405c9 100644 --- a/modules-available/locationinfo/lang/en/template-tags.json +++ b/modules-available/locationinfo/lang/en/template-tags.json @@ -24,8 +24,6 @@ "lang_general": "General", "lang_language": "Language", "lang_languageTooltip": "The language the frontend uses", - "lang_locationIsHidden": "Hidden", - "lang_locationIsHidden_title": "If checked the API doesn't return information about the room", "lang_locationName": "Name", "lang_locationSettings": "Settings", "lang_mainHeader": "Infoscreen", @@ -38,8 +36,9 @@ "lang_monTilFr": "Monday - Friday", "lang_nameTooltip": "Defines the name of the server", "lang_noServer": "<no server>", - "lang_openingTime": "Opening times", + "lang_openingTime": "Opening time", "lang_pleaseSelect": "Please select...", + "lang_remoteSchedule": "Time table retrieval", "lang_room": "Room", "lang_roomId": "Room ID", "lang_roomIdTooltip": "The ID of the room the server needs, for querying the calendar data", diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php index 52964b10..fe53bc99 100644 --- a/modules-available/locationinfo/page.inc.php +++ b/modules-available/locationinfo/page.inc.php @@ -16,13 +16,13 @@ class Page_LocationInfo extends Page Util::redirect('?do=Main'); // does not return } + $show = Request::any('show', '', 'string'); $this->action = Request::post('action'); - if ($this->action === 'updateOpeningTimeExpert') { - $this->updateOpeningTimeExpert(); - } elseif ($this->action === 'updateOpeningTimeEasy') { - $this->updateOpeningTimeEasy(); - } elseif ($this->action === 'updateConfig') { - $this->updateLocationConfig(); + if ($this->action === 'writePanelConfig') { + $this->writePanelConfig(); + } elseif ($this->action === 'writeLocationConfig') { + $this->writeLocationConfig(); + $show = 'locations'; } elseif ($this->action === 'deleteServer') { $this->deleteServer(); } elseif ($this->action === 'checkConnection') { @@ -33,7 +33,10 @@ class Page_LocationInfo extends Page Messages::addWarning('main.invalid-action', $this->action); } if (Request::isPost()) { - Util::redirect('?do=locationinfo'); + if (!empty($show)) { + $show = '&show=' . $show; + } + Util::redirect('?do=locationinfo' . $show); } } @@ -42,7 +45,20 @@ class Page_LocationInfo extends Page */ protected function doRender() { - $this->getInfoScreenTable(); + $show = Request::get('show', '', 'string'); + switch ($show) { + case 'locations': + $this->showLocationsTable(); + break; + case 'edit-panel': + $this->showConfigPanel(); + break; + case '': + $this->showInfoScreenTable(); + break; + default: + Message::addError('main.value-invalid', 'show', $show); + } } /** @@ -58,51 +74,147 @@ class Page_LocationInfo extends Page Database::exec("DELETE FROM `locationinfo_coursebackend` WHERE serverid=:id", array('id' => $id)); } - /** - * Updated the config in the db. - */ - private function updateLocationConfig() + private function getTime($str) { - $result = array(); + $str = explode(':', $str); + if (count($str) !== 2) return false; + if ($str[0] < 0 || $str[0] > 23 || $str[1] < 0 || $str[1] > 59) return false; + return $str[0] * 60 + $str[1]; + } - $locationid = Request::post('id', 0, 'int'); - if ($locationid <= 0) { + private function writeLocationConfig() + { + // Check locations + $locationid = Request::post('locationid', false, 'int'); + if ($locationid === false) { + Message::addError('main.paramter-missing', 'locationid'); + return false; + } + if (Location::get($locationid) === false) { Message::addError('location.invalid-location-id', $locationid); - Util::redirect('?do=locationinfo'); + return false; } $serverid = Request::post('serverid', 0, 'int'); if ($serverid === 0) { $serverid = null; } - $result['language'] = Request::post('language', 'en', 'string'); - $result['mode'] = Request::post('mode', 1, 'int'); - $result['vertical'] = Request::post('vertical', false, 'bool'); - $result['eco'] = Request::post('eco', false, 'bool'); - $result['scaledaysauto'] = Request::post('scaledaysauto', false, 'bool'); - $result['daystoshow'] = Request::post('daystoshow', 7, 'int'); - $result['rotation'] = Request::post('rotation', 0, 'int'); - $result['scale'] = Request::post('scale', 50, 'int'); - $result['switchtime'] = Request::post('switchtime', 20, 'int'); - $result['calupdate'] = Request::post('calupdate', 120, 'int'); - $result['roomupdate'] = Request::post('roomupdate', 30, 'int'); - $result['configupdate'] = Request::post('configupdate', 180, 'int'); - if ($result['roomupdate'] < 30) { - $result['roomupdate'] = 30; - } - if ($result['calupdate'] < 120) { - $result['calupdate'] = 120; - } $serverlocationid = Request::post('serverlocationid', '', 'string'); - Database::exec("INSERT INTO `locationinfo_locationconfig` (locationid, serverid, serverlocationid, config, lastcalendarupdate) - VALUES (:id, :serverid, :serverlocationid, :config, 0) - ON DUPLICATE KEY UPDATE config = VALUES(config), serverid = VALUES(serverid), - serverlocationid = VALUES(serverlocationid), lastcalendarupdate = 0", array( + // Opening times + $openingtimes = Request::post('openingtimes', '', 'string'); + if ($openingtimes !== '') { + $openingtimes = json_decode($openingtimes, true); + if (!is_array($openingtimes)) { + $openingtimes = ''; + } else { + $mangled = array(); + foreach (array_keys($openingtimes) as $key) { + $entry = $openingtimes[$key]; + if (!isset($entry['days']) || !is_array($entry['days']) || empty($entry['days'])) { + Message::addError('ignored-line-no-days'); + continue; + } + $s = $this->getTime($entry['openingtime']); + $e = $this->getTime($entry['closingtime']); + if ($s === false) { + Message::addError('ignored-invalid-start', $entry['openingtime']); + continue; + } + if ($e === false) { + Message::addError('ignored-invalid-end', $entry['closingtime']); + continue; + } + if ($e <= $s) { + Message::addError('ignored-invalid-range', $entry['openingtime'], $entry['closingtime']); + continue; + } + unset($entry['tag']); + $mangled[] = $entry; + } + if (empty($mangled)) { + $openingtimes = ''; + } else { + $openingtimes = json_encode($mangled); + } + } + } + + Database::exec("INSERT INTO `locationinfo_locationconfig` (locationid, serverid, serverlocationid, openingtime, lastcalendarupdate) + VALUES (:id, :serverid, :serverlocationid, :openingtimes, 0) + ON DUPLICATE KEY UPDATE serverid = VALUES(serverid), serverlocationid = VALUES(serverlocationid), + openingtime = VALUES(openingtime), lastcalendarupdate = 0", array( 'id' => $locationid, - 'config' => json_encode($result), 'serverid' => $serverid, + 'openingtimes' => $openingtimes, 'serverlocationid' => $serverlocationid, )); + return true; + } + + /** + * Updated the config in the db. + */ + private function writePanelConfig() + { + // UUID - existing or new + $paneluuid = Request::post('paneluuid', false, 'string'); + if (($paneluuid === false || strlen($paneluuid) !== 36) && $paneluuid !== 'new') { + Message::addError('invalid-panel-id', $paneluuid); + Util::redirect('?do=locationinfo'); + } + // Check locations + $locationids = Request::post('locationids', false, 'array'); + if ($locationids === false) { + Message::addError('main.paramter-missing', 'locationids'); + Util::redirect('?do=locationinfo'); + } + $all = array_map(function ($item) { return $item['locationid']; }, Location::queryLocations()); + $locationids = array_filter($locationids, function ($item) use ($all) { return in_array($item, $all); }); + if (empty($locationids)) { + Message::addError('main.paramter-empty', 'locationids'); + Util::redirect('?do=locationinfo'); + } + if (count($locationids) > 4) { + $locationids = array_slice($locationids, 0, 4); + } + // Build json struct + $conf = array( + 'ts' => time(), + 'language' => Request::post('language', 'en', 'string'), + 'mode' => Request::post('mode', 1, 'int'), + 'vertical' => Request::post('vertical', false, 'bool'), + 'eco' => Request::post('eco', false, 'bool'), + 'scaledaysauto' => Request::post('scaledaysauto', false, 'bool'), + 'daystoshow' => Request::post('daystoshow', 7, 'int'), + 'rotation' => Request::post('rotation', 0, 'int'), + 'scale' => Request::post('scale', 50, 'int'), + 'switchtime' => Request::post('switchtime', 20, 'int'), + 'calupdate' => Request::post('calupdate', 120, 'int'), + 'roomupdate' => Request::post('roomupdate', 30, 'int'), + 'configupdate' => Request::post('configupdate', 180, 'int'), + ); + if ($conf['roomupdate'] < 30) { + $conf['roomupdate'] = 30; + } + if ($conf['calupdate'] < 120) { + $conf['calupdate'] = 120; + } + + if ($paneluuid === 'new') { + $paneluuid = Util::randomUuid(); + $query = "INSERT INTO `locationinfo_panel` (paneluuid, locationids, paneltype, panelconfig) + VALUES (:id, :locationids, :type, :config)"; + } else { + $query = "UPDATE `locationinfo_panel` + SET locationids = :locationids, paneltype = :type, panelconfig = :config + WHERE paneluuid = :id"; + } + Database::exec($query, array( + 'id' => $paneluuid, + 'locationids' => implode(',', $locationids), + 'type' => 'DEFAULT', // TODO + 'config' => json_encode($conf), + )); Message::addSuccess('config-saved'); Util::redirect('?do=locationinfo'); @@ -148,133 +260,6 @@ class Page_LocationInfo extends Page } /** - * Updates the opening time in the db from the expert mode. - */ - private function updateOpeningTimeExpert() - { - $days = Request::post('days', array(), 'array'); - $locationid = Request::post('id', 0, 'int'); - $openingtime = Request::post('openingtime', array(), 'array'); - $closingtime = Request::post('closingtime', array(), 'array'); - $easyMode = Request::post('easyMode', false, 'bool'); - $delete = Request::post('delete', array(), 'array'); - $dontadd = Request::post('dontadd', array(), 'array'); - $count = 0; - $result = array(); - $resulttmp = array(); - $deleteCounter = 0; - - if (!$easyMode) { - $resulttmp = Database::queryFirst("SELECT openingtime FROM `locationinfo_locationconfig` WHERE locationid = :id", array('id' => $locationid)); - if ($resulttmp !== false) { - $resulttmp = json_decode($resulttmp['openingtime'], true); - } - if (!is_array($resulttmp)) { - $resulttmp = array(); - } - - $index = 0; - - foreach ($resulttmp as $day) { - $skip = false; - - foreach ($delete as $del) { - if ($del == $index) { - $skip = true; - break; - } - } - if ($skip) { - $index++; - $deleteCounter++; - continue; - } - - $result[] = $day; - $index++; - } - } - - if (!empty($days) && !is_array($days)) { - Message::addError('no-days-selected'); - Util::redirect('?do=locationinfo'); - } else { - - $dayz = array(); - $da = array(); - - foreach ($days as $d) { - if ($d != '-') { - $da[] = $d; - } else { - $dayz[$count] = $da; - $da = array(); - $count++; - } - } - - $optime = array(); - for ($x = 0; $x < $count; $x++) { - if ($dontadd[$x] == 'dontadd') { - continue; - } - $optime['days'] = $dayz[$x]; - $optime['openingtime'] = $openingtime[$x]; - $optime['closingtime'] = $closingtime[$x]; - $result[] = $optime; - } - } - - Database::exec("INSERT INTO `locationinfo_locationconfig` (locationid, openingtime) - VALUES (:id, :openingtime) - ON DUPLICATE KEY UPDATE openingtime = VALUES(openingtime)", - array('id' => $locationid, 'openingtime' => json_encode($result))); - - if ($deleteCounter > 0) { - Message::addSuccess('deleted-x-entries', $deleteCounter); - } - if ($count > 0) { - Message::addSuccess('added-x-entries', $count); - } - - Util::redirect('?do=locationinfo'); - } - - /** - * Updates the opening time in the db from the easy mode. - */ - private function updateOpeningTimeEasy() - { - $locationid = Request::post('id', 0, 'int'); - $openingtime = Request::post('openingtime', array(), 'array'); - $closingtime = Request::post('closingtime', array(), 'array'); - $result = array(); - - $blocks = array( - 0 => array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), - 1 => array("Saturday"), - 2 => array("Sunday"), - ); - foreach ($blocks as $idx => $days) { - //if (!empty($openingtime[$idx]) && !empty($closingtime[$idx])) { - $result[] = array( - 'days' => $days, - 'openingtime' => $openingtime[$idx], - 'closingtime' => $closingtime[$idx], - ); - //} - } - - Database::exec("INSERT INTO `locationinfo_locationconfig` (locationid, openingtime) - VALUES (:id, :openingtime) - ON DUPLICATE KEY UPDATE openingtime = VALUES(openingtime)", - array('id' => $locationid, 'openingtime' => json_encode($result))); - - Message::addSuccess('openingtime-updated'); - Util::redirect('?do=locationinfo'); - } - - /** * Checks if the server connection to a backend is valid. * * @param int $id Server id which connection should be checked. @@ -303,65 +288,11 @@ class Page_LocationInfo extends Page LocationInfo::setServerError($serverid, $serverInstance->getError()); } - - /** - * Sets the new hidden value and checks childs and parents. - * - * @param int $id The location id which was toggled - * @param bool $hidden The hidden value true / false - */ - protected function toggleHidden($id, $hidden) - { - $locs = Location::getLocationsAssoc(); - if (!isset($locs[$id])) - die('Invalid location id'); - $loc = $locs[$id]; - - // The JSON to return, telling the client which locationids to update in the view - $return = array(); - $return[] = array('locationid' => $id, 'hidden' => $hidden); - - // Update the location, plus all child locations - $qs = '(?,?)' . str_repeat(',(?,?)', count($loc['children'])); - $params = array($id, $hidden); - foreach ($loc['children'] as $child) { - $params[] = $child; - $params[] = $hidden; - $return[] = array('locationid' => $child, 'hidden' => $hidden); - } - Database::exec("INSERT INTO locationinfo_locationconfig (locationid, hidden) - VALUES $qs ON DUPLICATE KEY UPDATE hidden = VALUES(hidden)", $params); - - // Handle parents - uncheck if not all children are checked - while ($loc['parentlocationid'] != 0) { - $stats = Database::queryFirst('SELECT Count(*) AS total, Sum(li.hidden > 0) AS hidecount FROM location l - LEFT JOIN locationinfo_locationconfig li USING (locationid) - WHERE l.parentlocationid = :parent', array('parent' => $loc['parentlocationid'])); - $hidden = ($stats['total'] == $stats['hidecount']) ? 1 : 0; - $params = array('locationid' => $loc['parentlocationid'], 'hidden' => $hidden); - Database::exec('INSERT INTO locationinfo_locationconfig (locationid, hidden) - VALUES (:locationid, :hidden) ON DUPLICATE KEY UPDATE hidden = VALUES(hidden)', $params); - $return[] = $params; - $loc = $locs[$loc['parentlocationid']]; - } - return $return; - } - /** * Loads the Infoscreen page in the admin-panel and passes all needed information. */ - protected function getInfoScreenTable() + private function showInfoScreenTable() { - $locations = Location::getLocations(0, 0, false, true); - - // Get hidden state of all locations - $dbquery = Database::simpleQuery("SELECT li.locationid, li.hidden FROM `locationinfo_locationconfig` AS li"); - - while ($row = $dbquery->fetch(PDO::FETCH_ASSOC)) { - $locid = (int)$row['locationid']; - $locations[$locid]['hidden_checked'] = $row['hidden'] != 0 ? 'checked' : ''; - } - // Get a list of all the backend types. $servertypes = array(); $s_list = CourseBackend::getList(); @@ -395,12 +326,35 @@ class Page_LocationInfo extends Page } // Pass the data to the html and render it. - Render::addTemplate('location-info', array( - 'list' => array_values($locations), + Render::addTemplate('page-servers', array( 'serverlist' => $serverlist, )); } + private function showLocationsTable() + { + $locations = Location::getLocations(0, 0, false, true); + + // Get hidden state of all locations + $dbquery = Database::simpleQuery("SELECT li.locationid, li.serverid, li.serverlocationid, li.openingtime, li.lastcalendarupdate + FROM `locationinfo_locationconfig` AS li"); + + while ($row = $dbquery->fetch(PDO::FETCH_ASSOC)) { + $locid = (int)$row['locationid']; + $hasTable = is_array(json_decode($row['openingtime'], true)); + $hasBackend = !empty($row['serverid']) && !empty($row['serverlocationid']); + $locations[$locid] += array( + 'hasTable' => $hasTable, + 'hasBackend' => $hasBackend, + 'lastCalendarUpdate' => $row['lastcalendarupdate'], // TODO + ); + } + + Render::addTemplate('page-locations', array( + 'list' => array_values($locations), + )); + } + /** * AJAX */ @@ -412,32 +366,14 @@ class Page_LocationInfo extends Page } $action = Request::any('action'); $id = Request::any('id', 0, 'int'); - if ($action === 'timetable') { - $this->ajaxTimeTable($id); - } elseif ($action === 'config') { - $this->ajaxLoadLocationConfig($id); + if ($action === 'config-location') { + $this->ajaxConfigLocation($id); } elseif ($action === 'serverSettings') { $this->ajaxServerSettings($id); - } elseif ($action === 'hide') { - $this->ajaxHideLocation(); } } /** - * Request to deny displaying the door sign for the - * given location. Sends a list of all affected - * locations, so the client can update its view. - */ - private function ajaxHideLocation() - { - $locationId = Request::post('locationid', 0, 'int'); - $hidden = Request::post('hidden', 0, 'int'); - Header('Content-Type: application/json; charset=utf-8'); - $ret = $this->toggleHidden($locationId, $hidden); - echo json_encode(array('changed' => $ret)); - } - - /** * Ajax the server settings. * * @param int $id Serverid @@ -477,7 +413,7 @@ class Page_LocationInfo extends Page } $serverBackends[] = $backend; } - echo Render::parse('server-settings', array('id' => $id, + echo Render::parse('ajax-config-server', array('id' => $id, 'name' => $oldConfig['servername'], 'currentbackend' => $oldConfig['servertype'], 'backendList' => $serverBackends, @@ -489,57 +425,140 @@ class Page_LocationInfo extends Page * * @param $id id of the location */ - private function ajaxTimeTable($id) + private function ajaxConfigLocation($id) { - $row = Database::queryFirst("SELECT openingtime FROM `locationinfo_locationconfig` WHERE locationid = :id", array('id' => $id)); - if ($row !== false) { - $openingtimes = json_decode($row['openingtime'], true); + $locConfig = Database::queryFirst("SELECT serverid, serverlocationid, openingtime FROM `locationinfo_locationconfig` WHERE locationid = :id", array('id' => $id)); + if ($locConfig !== false) { + $openingtimes = json_decode($locConfig['openingtime'], true); } if (!isset($openingtimes) || !is_array($openingtimes)) { $openingtimes = array(); } - if ($this->isEasyMode($openingtimes)) { - $data = array('id' => $id, - 'easyMode' => true, - 'expertMode' => false - ); - foreach ($openingtimes as $idx => $ot) { - foreach ($ot as $k => $v) { - $data[$k . $idx] = $v; - } - } - echo Render::parse('timetable', $data); - } else { - $index = 0; - foreach ($openingtimes as &$entry) { - $entry['days'] = implode(', ', $entry['days']); - $entry['index'] = $index++; + // get Server / ID list + $res = Database::simpleQuery("SELECT serverid, servername FROM locationinfo_coursebackend ORDER BY servername ASC"); + $serverList = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if ($row['serverid'] == $locConfig['serverid']) { + $row['selected'] = 'selected'; } - echo Render::parse('timetable', array('id' => $id, - 'openingtimes' => array_values($openingtimes), - 'easyMode' => false, - 'expertMode' => true)); + $serverList[] = $row; } + + $data = array( + 'id' => $id, + 'serverlist' => $serverList, + 'serverlocationid' => $locConfig['serverlocationid'], + ); + $data['expertMode'] = !$this->isSimpleMode($openingtimes); + // !! isSimpleMode might have changed $openingtimes, so order is important here... + $data['schedule_data'] = json_encode($openingtimes); + + echo Render::parse('ajax-config-location', $data); } /** - * Checks if easymode or expert mode is active. + * Checks if simple mode or expert mode is active. + * Tries to merge/compact the opening times schedule, and + * will actually modify the passed array iff it can be + * transformed into easy opening times. * - * @param $array Array of the saved openingtimes. - * @return bool True if easy mode, false if expert mode + * @param array $array of the saved openingtimes. + * @return bool True if simple mode, false if expert mode */ - private function isEasyMode($array) + private function isSimpleMode(&$array) { if (empty($array)) return true; - if (count($array) === 3 - && $array[0]['days'] == array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday") - && $array[1]['days'][0] == "Saturday" && $array[2]['days'][0] == "Sunday" - ) { - return true; + // Decompose by day + $new = array(); + foreach ($array as $row) { + $s = $this->getTime($row['openingtime']); + $e = $this->getTime($row['closingtime']); + if ($s === false || $e === false || $e <= $s) + continue; + foreach ($row['days'] as $day) { + $this->addDay($new, $day, $s, $e); + } + } + // Merge by timespan, but always keep saturday and sunday separate + $merged = array(); + foreach ($new as $day => $ranges) { + foreach ($ranges as $range) { + if ($day === 'Saturday' || $day === 'Sunday') { + $add = $day; + } else { + $add = ''; + } + $key = '#' . $range[0] . '#' . $range[1] . '#' . $add; + if (!isset($merged[$key])) { + $merged[$key] = array(); + } + $merged[$key][$day] = true; + } } - return false; + // Check if it passes as simple mode + if (count($merged) > 3) + return false; + foreach ($merged as $days) { + if (count($days) === 5) { + $res = array_keys($days); + $res = array_intersect($res, array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")); + if (count($res) !== 5) + return false; + } elseif (count($days) === 1) { + if (!isset($days['Saturday']) && !isset($days['Sunday'])) { + return false; + } + } else { + return false; + } + } + // Valid simple mode, finally transform back to what we know + $new = array(); + foreach ($merged as $span => $days) { + preg_match('/^#(\d+)#(\d+)#/', $span, $out); + $new[] = array( + 'days' => array_keys($days), + 'openingtime' => floor($out[1] / 60) . ':' . ($out[1] % 60), + 'closingtime' => floor($out[2] / 60) . ':' . ($out[2] % 60), + ); + } + $array = $new; + return true; + } + + private function addDay(&$array, $day, $s, $e) + { + if (!isset($array[$day])) { + $array[$day] = array(array($s, $e)); + return; + } + foreach (array_keys($array[$day]) as $key) { + $current = $array[$day][$key]; + if ($s <= $current[0] && $e >= $current[1]) { + // Fully dominated + unset($array[$day][$key]); + continue; // Might partially overlap with additional ranges, keep going + } + if ($current[0] <= $s && $current[1] >= $s) { + // $start lies within existing range + if ($current[0] <= $e && $current[1] >= $e) + return; // Fully in existing range, do nothing + // $end seems to extend range we're checking against but $start lies within this range, update and keep going + $s = $current[0]; + unset($array[$day][$key]); + continue; + } + // Last possibility: $start is before range, $end within range + if ($current[0] <= $e && $current[1] >= $e) { + // $start must lie before range start, otherwise we'd have hit the case above + $e = $current[1]; + unset($array[$day][$key]); + continue; + } + } + $array[$day][] = array($s, $e); } /** @@ -547,28 +566,19 @@ class Page_LocationInfo extends Page * * @param $id Location ID */ - private function ajaxLoadLocationConfig($id) + private function showConfigPanel() { + $id = Request::get('paneluuid', false, 'string'); // Get Config data from db - $location = Database::queryFirst("SELECT lc.config, lc.serverid, lc.serverlocationid - FROM location l - LEFT JOIN `locationinfo_locationconfig` lc USING (locationid) - WHERE l.locationid = :id", array('id' => $id)); - if ($location === false) { - die("Invalid location id: $id"); + $panel = Database::queryFirst("SELECT locationids, paneltype, panelconfig + FROM locationinfo_panel + WHERE paneluuid = :id", array('id' => $id)); + if ($panel === false) { + die("Invalid panel id: $id"); } - $config = json_decode($location['config'], true); // TODO: Validate we got an array, fill with defaults otherwise + $config = json_decode($panel['panelconfig'], true); // TODO: Validate we got an array, fill with defaults otherwise - // get Server / ID list - $dbq = Database::simpleQuery("SELECT serverid, servername FROM locationinfo_coursebackend ORDER BY servername ASC"); - $serverList = array(); - while ($row = $dbq->fetch(PDO::FETCH_ASSOC)) { - if ($row['serverid'] == $location['serverid']) { - $row['selected'] = 'selected'; - } - $serverList[] = $row; - } $langs = Dictionary::getLanguages(true); foreach ($langs as &$lang) { if ($lang['cc'] === $config['language']) { @@ -576,7 +586,7 @@ class Page_LocationInfo extends Page } } - echo Render::parse('config', array( + Render::addTemplate('page-config-panel', array( 'id' => $id, 'languages' => $langs, 'mode' => $config['mode'], @@ -590,9 +600,6 @@ class Page_LocationInfo extends Page 'calupdate' => $config['calupdate'], 'roomupdate' => $config['roomupdate'], 'configupdate' => $config['configupdate'], - 'serverlist' => $serverList, - 'serverid' => $location['serverid'], - 'serverlocationid' => $location['serverlocationid'] )); } diff --git a/modules-available/locationinfo/templates/ajax-config-location.html b/modules-available/locationinfo/templates/ajax-config-location.html new file mode 100644 index 00000000..2cbad89d --- /dev/null +++ b/modules-available/locationinfo/templates/ajax-config-location.html @@ -0,0 +1,178 @@ +<input type="hidden" name="locationid" value="{{id}}"> +<div id="settings-outer"> + <h3>{{lang_openingTime}}</h3> + {{^expertMode}} + <div id="simple-mode"> + + <div align="right"> + <a href="#" class="btn btn-default btn-sm" id="btn-show-expert">{{lang_expertMode}}</a> + </div> + <div class="clearfix"></div> + + <table class="table table-condensed" style="margin-bottom:0"> + <tr> + <th>{{lang_day}}</th> + <th>{{lang_openingTime}}</th> + <th>{{lang_closingTime}}</th> + </tr> + + <tr class="tablerow"> + <td>{{lang_monTilFr}}</td> + <td> + <div class="input-group bootstrap-timepicker"> + <span class="input-group-addon"> + <span class="glyphicon glyphicon-time"></span> + </span> + <input type="text" class="form-control timepicker2" id="week-open" pattern="[0-9]{1,2}:[0-9]{2}"> + </div> + </td> + <td> + <div class="input-group bootstrap-timepicker"> + <span class="input-group-addon"> + <span class="glyphicon glyphicon-time"></span> + </span> + <input type="text" class="form-control timepicker2" id="week-close" pattern="[0-9]{1,2}:[0-9]{2}"> + </div> + </td> + </tr> + <tr class="tablerow"> + <td>{{lang_saturday}}</td> + <td> + <div class="input-group bootstrap-timepicker"> + <span class="input-group-addon"> + <span class="glyphicon glyphicon-time"></span> + </span> + <input type="text" class="form-control timepicker2" id="saturday-open" pattern="[0-9]{1,2}:[0-9]{2}"> + </div> + </td> + <td> + <div class="input-group bootstrap-timepicker"> + <span class="input-group-addon"> + <span class="glyphicon glyphicon-time"></span> + </span> + <input type="text" class="form-control timepicker2" id="saturday-close" pattern="[0-9]{1,2}:[0-9]{2}"> + </div> + </td> + </tr> + <tr class="tablerow"> + <td>{{lang_sunday}}</td> + <td> + <div class="input-group bootstrap-timepicker"> + <span class="input-group-addon"> + <span class="glyphicon glyphicon-time"></span> + </span> + <input type="text" class="form-control timepicker2" id="sunday-open" pattern="[0-9]{1,2}:[0-9]{2}"> + </div> + </td> + <td> + <div class="input-group bootstrap-timepicker"> + <span class="input-group-addon"> + <span class="glyphicon glyphicon-time"></span> + </span> + <input type="text" class="form-control timepicker2" id="sunday-close" pattern="[0-9]{1,2}:[0-9]{2}"> + </div> + </td> + </tr> + </table> + </div> + {{/expertMode}} + + <div id="expert-mode" style="{{^expertMode}}display:none{{/expertMode}}"> + <div class="pull-right"> + <a class="btn btn-success btn-sm" id="new-openingtime"> + <span class="glyphicon glyphicon-plus-sign"></span> + {{lang_openingTime}} + </a> + </div> + <div class="clearfix"></div> + <div id="expert-table"> + <div class="row"> + <div class="col-xs-9">{{lang_day}}</div> + <div class="col-xs-3 text-right">{{lang_delete}}</div> + <div class="col-sm-6">{{lang_openingTime}}</div> + <div class="col-sm-6">{{lang_closingTime}}</div> + </div> + </div> + </div> +</div> + +<h3>{{lang_remoteSchedule}}</h3> +<div class="row"> + <div class="col-sm-3"> + {{lang_server}} + </div> + <div class="col-sm-7"> + <select class="form-control" name="serverid"> + <option value="0">{{lang_noServer}}</option> + {{#serverlist}} + <option value="{{serverid}}" {{selected}}>{{servername}}</option> + {{/serverlist}} + </select> + </div> + <div class="col-sm-2"> + <a class="btn btn-default helptext" title="{{lang_serverTooltip}}"> + <span class="glyphicon glyphicon-question-sign"></span> + </a> + </div> +</div> +<div class="row"> + <div class="col-sm-3"> + {{lang_roomId}} + </div> + <div class="col-sm-7"> + <input class="form-control" name="serverlocationid" id="serverlocationid" value="{{serverlocationid}}"> + </div> + <div class="col-sm-2"> + <a class="btn btn-default helptext" title="{{lang_roomIdTooltip}}"> + <span class="glyphicon glyphicon-question-sign"></span> + </a> + </div> +</div> + +<script type="application/javascript"><!-- + (function() { + + var scheduleData = {{{schedule_data}}}; + + {{#expertMode}} + for (var i = 0; i < scheduleData.length; ++i) { + newOpeningTime(scheduleData[i]); + } + {{/expertMode}} + {{^expertMode}} + for (var i = 0; i < scheduleData.length; ++i) { + if (scheduleData[i].days.length === 5) { + $('#week-open').val(scheduleData[i]['openingtime']); + $('#week-close').val(scheduleData[i]['closingtime']); + } else if (scheduleData[i].days.length === 1 && scheduleData[i].days[0] === 'Saturday') { + $('#saturday-open').val(scheduleData[i]['openingtime']); + $('#saturday-close').val(scheduleData[i]['closingtime']); + } else if (scheduleData[i].days.length === 1 && scheduleData[i].days[0] === 'Sunday') { + $('#sunday-open').val(scheduleData[i]['openingtime']); + $('#sunday-close').val(scheduleData[i]['closingtime']); + } + } + {{/expertMode}} + + setTimepicker($('#settings-outer').find('.timepicker2')); + + $('a.helptext').tooltip(); + + $('#new-openingtime').click(function (e) { + e.preventDefault(); + setTimepicker(newOpeningTime({}).find('.timepicker2')); + }) + + $('#btn-show-expert').click(function (e) { + e.preventDefault(); + scheduleData = simpleToExpert(); + for (var i = 0; i < scheduleData.length; ++i) { + setTimepicker(newOpeningTime(scheduleData[i]).find('.timepicker2')); + } + $('#simple-mode').remove(); + $('#expert-mode').show(); + }); + + })(); + +//--></script> diff --git a/modules-available/locationinfo/templates/server-settings.html b/modules-available/locationinfo/templates/ajax-config-server.html index 940bc55a..940bc55a 100644 --- a/modules-available/locationinfo/templates/server-settings.html +++ b/modules-available/locationinfo/templates/ajax-config-server.html diff --git a/modules-available/locationinfo/templates/location-info.html b/modules-available/locationinfo/templates/location-info.html deleted file mode 100644 index e2ee2960..00000000 --- a/modules-available/locationinfo/templates/location-info.html +++ /dev/null @@ -1,219 +0,0 @@ -<div> - <h1>{{lang_mainHeader}}</h1> - - <h4>{{lang_serverTable}}</h4> - - <table class="table table-hover"> - <tr> - <th width="1">{{lang_serverType}}</th> - <th>{{lang_locationName}}</th> - <th width="1"></th> - <th width="1"></th> - </tr> - {{#serverlist}} - <form method="post" action="?do=locationinfo"> - <input type="hidden" name="token" value="{{token}}"> - <input type="hidden" name="serverid" value="{{serverid}}"> - <tr> - <td nowrap>{{typename}}</td> - <td nowrap>{{servername}}</td> - - <td align="center" nowrap> - <button class="btn btn-sm {{^autherror}}btn-success{{/autherror}}{{#autherror}}btn-danger{{/autherror}}" - data-server-edit="{{serverid}}" {{disabled}} type="button"> - <span style="margin-right: 5px;" class="glyphicon glyphicon-cog"></span> - {{lang_locationSettings}} - </button> - <button class="btn btn-sm btn-primary server-check" {{disabled}} name="action" value="checkConnection" - type="submit"> - <span style="margin-right: 5px;" class="glyphicon glyphicon-refresh"></span> - {{lang_checkConnection}} - </button> - </td> - <td align="center" nowrap> - <button class="btn btn-sm btn-danger server-delete" type="submit" name="action" value="deleteServer"> - <span style="margin-right: 5px;" class="glyphicon glyphicon-remove"></span> - {{lang_delete}} - </button> - </td> - </tr> - </form> - {{/serverlist}} - </table> - - <div> - <button class="btn btn-sm btn-success" id="addServerButton" onclick="addServer()"> - <span style="margin-right: 5px;" class="glyphicon glyphicon-plus"></span> - {{lang_addServer}} - </button> - </div> - - <h4>{{lang_buildingTable}}</h4> - <table class="table table-condensed table-hover" style="margin-bottom:0"> - - <tr> - <th>{{lang_locationName}}</th> - <th width="50" title="{{lang_locationIsHidden_title}}">{{lang_locationIsHidden}}</th> - <th width="50">{{lang_openingTime}}</th> - <th width="50">{{lang_locationSettings}}</th> - </tr> - - {{#list}} - <tr id="row{{locationid}}"> - <td> - <div style="display:inline-block;width:{{depth}}em"></div> - <a href="modules/locationinfo/frontend/doorsign.html?id={{locationid}}" target="_blank">{{locationname}}</a> - </td> - <td align="center"> - <div class="checkbox" style="margin:0"> - <input class="hidden-toggle" type="checkbox" data-locationid="{{locationid}}" {{hidden_checked}}> - <label></label> - </div> - </td> - <td> - <a class="btn btn-sm btn-default" role="button" style="width: 100%" - onclick="loadTimeModal({{locationid}}, '{{locationname}}');"> - <span style="margin-right: 5px;" class="glyphicon glyphicon-time"></span> - </a> - </td> - <td> - <a class="btn btn-sm btn-default" role="button" style="width: 100%;" - onclick="loadLocationConfigModal({{locationid}}, '{{locationname}}');"> - <span style="margin-right: 5px;" class="glyphicon glyphicon-cog"></span> - </a> - </td> - </tr> - - {{/list}} - </table> - - <div class="modal fade" id="myModal" tabindex="-1" role="dialog"> - <div class="modal-dialog"> - - <div class="modal-content"> - <div class="modal-header" id="myModalHeader"></div> - <div class="modal-body" id="myModalBody"></div> - <div class="modal-footer"> - <button type="submit" id="myModalSubmitButton" class="btn btn-primary" form="">{{lang_save}}</button> - - <a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a> - </div> - </div> - - </div> - </div> -</div> -<script type="text/javascript"><!-- - - document.addEventListener("DOMContentLoaded", function () { - - /** - * React to click on a "hidden" checkbox. - */ - $('.hidden-toggle').change(function() { - $input = $(this); - $.ajax({ - type: 'POST', - url: '?do=locationinfo&action=hide', - data: {locationid: $input.data('locationid'), hidden: $input[0].checked ? 1 : 0, token: TOKEN }, - dataType: 'json' - }).done(function(data) { - if (data && $.isArray(data.changed)) { - markBoxes(data.changed); - } else { - $input.replaceWith('ERROR'); - } - }).fail(function () { - $input.replaceWith('ERROR'); - }); - }).parent().click(function() { - $this = $(this); - $box = $this.find('input'); - if (!$this.is($box)) { - $box.click(); - } - }); - - /** - * Confirm deleting a server. - */ - $('.server-delete').click(function(ev) { - var del = confirm("{{lang_deleteConfirmation}}"); - if (!del) ev.preventDefault(); - }); - - /** - * Animate refresh icon while page is loading - */ - $('.server-check').click(function() { - $(this).find('.glyphicon').addClass('slx-rotation'); - }); - - $('button[data-server-edit]').click(function() { - var id = $(this).data('server-edit'); - loadServerSettingsModal(id); - }); - - }); - - /** - * Gets a status array delivered by backend's ajaxHideLocation() - * and sets the checkbox states accordingly. - */ - function markBoxes(boxArray) { - for (var i = 0; i < boxArray.length; ++i) { - if (boxArray[i].locationid) { - var lid = parseInt(boxArray[i].locationid); - $('input.hidden-toggle[data-locationid="' + lid + '"]').prop('checked', !!boxArray[i].hidden); - } - } - } - - /** - * Loads the settings modal of a server. - * - * @param serverid The id of the server. - */ - function loadServerSettingsModal(serverid) { - $('#myModalHeader').text("{{lang_locationSettings}}").css("font-weight", "Bold"); - $('#myModal .modal-dialog').css('width', ''); - $('#myModal').modal('show'); - $('#myModalBody').load("?do=locationinfo&action=serverSettings&id=" + serverid); - } - - /** - * Load a opening time modal of a location. - * - * @param locationId The id of the location. - * @param locationName The name of the location. - */ - function loadTimeModal(locationId, locationName) { - $('#myModalHeader').text("[" + locationId + "] " + locationName).css("font-weight", "Bold"); - $('#myModal .modal-dialog').css('width', ''); - $('#myModal').modal('show'); - $('#myModalBody').load("?do=locationinfo&action=timetable&id=" + locationId); - } - - /** - * Loads the config modal of a location. - * - * @param locationId The id of the location - * @param locationName the name of the location - */ - function loadLocationConfigModal(locationId, locationName) { - $('#myModalHeader').text("[" + locationId + "] " + locationName).css("font-weight", "Bold"); - $('#myModalSubmitButton').attr("form", "configForm"); - $('#myModal .modal-dialog').css('width', '90%'); - $('#myModal').modal('show'); - $('#myModalBody').load("?do=locationinfo&action=config&id=" + locationId); - } - - // ########### Server Table ########### - - /** - * Loads a new / empty server settings modal. - */ - function addServer() { - loadServerSettingsModal(0); - } -//--></script> diff --git a/modules-available/locationinfo/templates/config.html b/modules-available/locationinfo/templates/page-config-panel.html index cce63de1..244b80c9 100644 --- a/modules-available/locationinfo/templates/config.html +++ b/modules-available/locationinfo/templates/page-config-panel.html @@ -5,52 +5,6 @@ <div class="row"> <div class="col-md-6"> - <div class="panel panel-default"> - <div class="panel-heading">{{lang_server}}</div> - <div class="panel-body"> - <div class="list-group"> - <div class="list-group-item"> - <div class="row"> - <div class="col-sm-3"> - <label>{{lang_server}}</label> - </div> - <div class="col-sm-7"> - <select class="form-control" name="serverid"> - <option value="0">{{lang_noServer}}</option> - {{#serverlist}} - <option value="{{serverid}}" {{selected}}>{{servername}}</option> - {{/serverlist}} - </select> - </div> - <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_serverTooltip}}"> - <span class="glyphicon glyphicon-question-sign"></span> - </a> - </div> - </div> - </div> - - <div class="list-group-item"> - <div class="row"> - <div class="col-sm-3"> - <label>{{lang_roomId}}</label> - </div> - <div class="col-sm-7"> - <input class="form-control" name="serverlocationid" id="serverlocationid" value="{{serverlocationid}}"> - </div> - <div class="col-sm-2"> - <a class="btn btn-default helptext" title="{{lang_roomIdTooltip}}"> - <span class="glyphicon glyphicon-question-sign"></span> - </a> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - - <div class="col-md-6"> <div class="modify-inputs panel panel-default"> <div class="panel-heading">{{lang_display}}</div> <div class="panel-body"> diff --git a/modules-available/locationinfo/templates/page-locations.html b/modules-available/locationinfo/templates/page-locations.html new file mode 100644 index 00000000..436aa8d5 --- /dev/null +++ b/modules-available/locationinfo/templates/page-locations.html @@ -0,0 +1,98 @@ +<h4>{{lang_buildingTable}}</h4> +<table class="table table-condensed table-hover" style="margin-bottom:0"> + + <tr> + <th>{{lang_locationName}}</th> + <th width="50">{{lang_openingTime}}</th> + <th width="50">{{lang_locationSettings}}</th> + </tr> + + {{#list}} + <tr> + <td> + <div style="display:inline-block;width:{{depth}}em"></div> + <a href="#" class="loc-name" data-locationid="{{locationid}}">{{locationname}}</a> + </td> + <td> + <a class="btn btn-sm btn-default" role="button" style="width: 100%" + onclick="loadTimeModal({{locationid}}, '{{locationname}}');"> + <span style="margin-right: 5px;" class="glyphicon glyphicon-time"></span> + </a> + </td> + <td> + <a class="btn btn-sm btn-default" role="button" style="width: 100%;" + onclick="loadLocationConfigModal({{locationid}}, '{{locationname}}');"> + <span style="margin-right: 5px;" class="glyphicon glyphicon-cog"></span> + </a> + </td> + </tr> + + {{/list}} +</table> + +<div class="modal fade" id="location-modal" tabindex="-1" role="dialog"> + <div class="modal-dialog"> <!--style="min-width:600px;width:70%"--> + + <div class="modal-content"> + <form method="post" action="?do=locationinfo" id="settings-form"> + <input type="hidden" name="token" value="{{token}}"> + <input type="hidden" name="action" value="writeLocationConfig"> + <input type="hidden" name="openingtimes" id="json-openingtimes" value=""> + <div class="modal-header"><h2 id="location-modal-header"></h2></div> + <div class="modal-body"></div> + <div class="modal-footer"> + <button type="submit" class="btn btn-primary">{{lang_save}}</button> + <a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a> + </div> + </form> + </div> + + </div> +</div> + +<div class="hidden" id="expert-template"> + <div class="row expert-row" style="margin-top:1em;border-top:1px solid #ddd"> + <div class="col-xs-9 days-box"> + <label><input type="checkbox" class="i-Monday">{{lang_shortMonday}}</label> | + <label><input type="checkbox" class="i-Tuesday">{{lang_shortTuesday}}</label> | + <label><input type="checkbox" class="i-Wednesday">{{lang_shortWednesday}}</label> | + <label><input type="checkbox" class="i-Thursday">{{lang_shortThursday}}</label> | + <label><input type="checkbox" class="i-Friday">{{lang_shortFriday}}</label> | + <label><input type="checkbox" class="i-Saturday">{{lang_shortSaturday}}</label> | + <label><input type="checkbox" class="i-Sunday">{{lang_shortSunday}}</label> + </div> + <div class="col-xs-3 text-right"> + <label><input type="checkbox" class="i-delete"><span class="glyphicon glyphicon-trash"></span></label> + </div> + <div class="col-sm-6"> + <div class="input-group bootstrap-timepicker"> + <span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span> + <input type="text" class="form-control timepicker2 i-openingtime" pattern="[0-9]{1,2}:[0-9]{2}"> + </div> + </div> + <div class="col-sm-6"> + <div class="input-group bootstrap-timepicker"> + <span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span> + <input type="text" class="form-control timepicker2 i-closingtime" pattern="[0-9]{1,2}:[0-9]{2}"> + </div> + </div> + </div> +</div> + +<script type="text/javascript"><!-- + +document.addEventListener("DOMContentLoaded", function () { + /** + * Load a opening time modal of a location. + */ + $('.loc-name').click(function (e) { + e.preventDefault(); + var locationId = $(this).data('locationid'); + var locationName = $(this).text(); + $('#location-modal-header').text("[" + locationId + "] " + locationName); + $('#location-modal').modal('show').find('.modal-body').load("?do=locationinfo&action=config-location&id=" + locationId); + }); + $('#settings-form').submit(submitLocationSettings); +}); + +//--></script>
\ No newline at end of file diff --git a/modules-available/locationinfo/templates/page-servers.html b/modules-available/locationinfo/templates/page-servers.html new file mode 100644 index 00000000..2dfe63e1 --- /dev/null +++ b/modules-available/locationinfo/templates/page-servers.html @@ -0,0 +1,113 @@ +<div> + <h1>{{lang_mainHeader}}</h1> + + <h4>{{lang_serverTable}}</h4> + + <table class="table table-hover"> + <tr> + <th width="1">{{lang_serverType}}</th> + <th>{{lang_locationName}}</th> + <th width="1"></th> + <th width="1"></th> + </tr> + {{#serverlist}} + <form method="post" action="?do=locationinfo"> + <input type="hidden" name="token" value="{{token}}"> + <input type="hidden" name="serverid" value="{{serverid}}"> + <tr> + <td nowrap>{{typename}}</td> + <td nowrap>{{servername}}</td> + + <td align="center" nowrap> + <button class="btn btn-sm {{^autherror}}btn-success{{/autherror}}{{#autherror}}btn-danger{{/autherror}}" + data-server-edit="{{serverid}}" {{disabled}} type="button"> + <span style="margin-right: 5px;" class="glyphicon glyphicon-cog"></span> + {{lang_locationSettings}} + </button> + <button class="btn btn-sm btn-primary server-check" {{disabled}} name="action" value="checkConnection" + type="submit"> + <span style="margin-right: 5px;" class="glyphicon glyphicon-refresh"></span> + {{lang_checkConnection}} + </button> + </td> + <td align="center" nowrap> + <button class="btn btn-sm btn-danger server-delete" type="submit" name="action" value="deleteServer"> + <span style="margin-right: 5px;" class="glyphicon glyphicon-remove"></span> + {{lang_delete}} + </button> + </td> + </tr> + </form> + {{/serverlist}} + </table> + + <div> + <button class="btn btn-sm btn-success" id="addServerButton" onclick="addServer()"> + <span style="margin-right: 5px;" class="glyphicon glyphicon-plus"></span> + {{lang_addServer}} + </button> + </div> + + <div class="modal fade" id="myModal" tabindex="-1" role="dialog"> + <div class="modal-dialog"> + + <div class="modal-content"> + <div class="modal-header" id="myModalHeader"></div> + <div class="modal-body" id="myModalBody"></div> + <div class="modal-footer"> + <button type="submit" id="myModalSubmitButton" class="btn btn-primary" form="">{{lang_save}}</button> + + <a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a> + </div> + </div> + + </div> + </div> +</div> +<script type="text/javascript"><!-- + + document.addEventListener("DOMContentLoaded", function () { + + /** + * Confirm deleting a server. + */ + $('.server-delete').click(function(ev) { + var del = confirm("{{lang_deleteConfirmation}}"); + if (!del) ev.preventDefault(); + }); + + /** + * Animate refresh icon while page is loading + */ + $('.server-check').click(function() { + $(this).find('.glyphicon').addClass('slx-rotation'); + }); + + $('button[data-server-edit]').click(function() { + var id = $(this).data('server-edit'); + loadServerSettingsModal(id); + }); + + }); + + /** + * Loads the settings modal of a server. + * + * @param serverid The id of the server. + */ + function loadServerSettingsModal(serverid) { + $('#myModalHeader').text("{{lang_locationSettings}}").css("font-weight", "Bold"); + $('#myModal .modal-dialog').css('width', ''); + $('#myModal').modal('show'); + $('#myModalBody').load("?do=locationinfo&action=serverSettings&id=" + serverid); + } + + // ########### Server Table ########### + + /** + * Loads a new / empty server settings modal. + */ + function addServer() { + loadServerSettingsModal(0); + } +//--></script> diff --git a/modules-available/locationinfo/templates/timetable.html b/modules-available/locationinfo/templates/timetable.html deleted file mode 100644 index 91a80877..00000000 --- a/modules-available/locationinfo/templates/timetable.html +++ /dev/null @@ -1,216 +0,0 @@ -<div> - <div align="right"> - <label for="CB_1">{{lang_expertMode}}</label> - <input class="bs-switch" name="1" id="CB_1" type="checkbox" {{#expertMode}}checked{{/expertMode}}> - </div> - <br> - - <div id="easyMode" style="{{#expertMode}}display: none;{{/expertMode}}"> - <form method="post" action="?do=locationinfo" id="timeFormEasy"> - <input type="hidden" name="token" value="{{token}}"> - <input type="hidden" name="action" value="updateOpeningTimeEasy"> - <input type="hidden" name="id" value="{{id}}"> - <input type="hidden" name="easyMode" value="{{easyMode}}"> - - <table class="table table-condensed locations" style="margin-bottom:0"> - <tr> - <th>{{lang_day}}</th> - <th>{{lang_openingTime}}</th> - <th>{{lang_closingTime}}</th> - </tr> - - <tr class="tablerow"> - <td>{{lang_monTilFr}}</td> - <td> - <div class="input-group bootstrap-timepicker"> - <span class="input-group-addon"> - <span class="glyphicon glyphicon-time"></span> - </span> - <input type="text" class="form-control timepicker2" name="openingtime[]" id="openingtimepicker" - pattern="[0-9]{1,2}:[0-9]{2}" value="{{openingtime0}}"> - </div> - </td> - <td> - <div class="input-group bootstrap-timepicker"> - <span class="input-group-addon"> - <span class="glyphicon glyphicon-time"></span> - </span> - <input type="text" class="form-control timepicker2" name="closingtime[]" id="openingtimepicker" - pattern="[0-9]{1,2}:[0-9]{2}" value="{{closingtime0}}"> - </div> - </td> - </tr> - <tr class="tablerow"> - <td>{{lang_saturday}}</td> - <td> - <div class="input-group bootstrap-timepicker"> - <span class="input-group-addon"> - <span class="glyphicon glyphicon-time"></span> - </span> - <input type="text" class="form-control timepicker2" name="openingtime[]" id="openingtimepicker" - pattern="[0-9]{1,2}:[0-9]{2}" value="{{openingtime1}}"> - </div> - </td> - <td> - <div class="input-group bootstrap-timepicker"> - <span class="input-group-addon"> - <span class="glyphicon glyphicon-time"></span> - </span> - <input type="text" class="form-control timepicker2" name="closingtime[]" id="openingtimepicker" - pattern="[0-9]{1,2}:[0-9]{2}" value="{{closingtime1}}"> - </div> - </td> - </tr> - <tr class="tablerow"> - <td>{{lang_sunday}}</td> - <td> - <div class="input-group bootstrap-timepicker"> - <span class="input-group-addon"> - <span class="glyphicon glyphicon-time"></span> - </span> - <input type="text" class="form-control timepicker2" name="openingtime[]" id="openingtimepicker" - pattern="[0-9]{1,2}:[0-9]{2}" value="{{openingtime2}}"> - </div> - </td> - <td> - <div class="input-group bootstrap-timepicker"> - <span class="input-group-addon"> - <span class="glyphicon glyphicon-time"></span> - </span> - <input type="text" class="form-control timepicker2" name="closingtime[]" id="openingtimepicker" - pattern="[0-9]{1,2}:[0-9]{2}" value="{{closingtime2}}"> - </div> - </td> - </tr> - </table> - </form> - </div> - - <div id="expertMode" style="{{#easyMode}}display: none;{{/easyMode}}"> - <form method="post" action="?do=locationinfo" id="timeFormExpert"> - <input type="hidden" name="token" value="{{token}}"> - <input type="hidden" name="action" value="updateOpeningTimeExpert"> - <input type="hidden" name="id" value="{{id}}"> - <input type="hidden" name="easyMode" value="{{easyMode}}"> - - - <table class="table table-condensed locations" style="margin-bottom:0"> - <tr> - <th>{{lang_day}}</th> - <th>{{lang_openingTime}}</th> - <th>{{lang_closingTime}}</th> - <th>{{lang_delete}}</th> - </tr> - - {{#openingtimes}} - <tr class=tablerow> - <td>{{days}}</td> - <td>{{openingtime}}</td> - <td>{{closingtime}}</td> - <td align="center"><input type="checkbox" name="delete[]" value="{{index}}"></td> - </tr> - {{/openingtimes}} - - <tr id="lastOpenTimesTableElement"></tr> - </table> - - <br> - <a class="btn btn-success btn-sm" onclick=newOpeningTime()> - <span class="glyphicon glyphicon-plus-sign"></span> - {{lang_openingTime}} - </a> - </form> - </div> -</div> - -<script> - setTimepicker(); - $('#CB_1').bootstrapSwitch(); - $('#CB_1').on('switchChange.bootstrapSwitch', function (e, data) { - - if (data == false) { - $('#expertMode').hide(); - $('#easyMode').show(); - $('#myModalSubmitButton').attr("Form", "timeFormEasy"); - } else { - $('#easyMode').hide(); - $('#expertMode').show(); - $('#myModalSubmitButton').attr("Form", "timeFormExpert"); - } - }); - - if ('{{easyMode}}' == true) { - $('#myModalSubmitButton').attr("Form", "timeFormEasy"); - } else if ('{{expertMode}}' == true) { - $('#myModalSubmitButton').attr("Form", "timeFormExpert"); - } - - /** - * Sets the timepicker element. - */ - function setTimepicker() { - $('.timepicker2').timepicker({ - minuteStep: 1, - template: 'modal', - appendWidgetTo: 'body', - showSeconds: false, - showMeridian: false, - defaultTime: false - }); - } - - /** - * Adds a new opening time to the table in expert mode. - */ - function newOpeningTime() { - $('#lastOpenTimesTableElement').before('<tr>\ - <td>\ - <div class="form-group options">\ - <label><input required type="checkbox" name="days[]" value="Monday">{{lang_shortMonday}}</label>\ - <label><input required type="checkbox" name="days[]" value="Tuesday">{{lang_shortTuesday}}</label>\ - <label><input required type="checkbox" name="days[]" value="Wednesday">{{lang_shortWednesday}}</label>\ - <label><input required type="checkbox" name="days[]" value="Thursday">{{lang_shortThursday}}</label>\ - <label><input required type="checkbox" name="days[]" value="Friday">{{lang_shortFriday}}</label>\ - <label><input required type="checkbox" name="days[]" value="Saturday">{{lang_shortSaturday}}</label>\ - <label><input required type="checkbox" name="days[]" value="Sunday">{{lang_shortSunday}}</label>\ - </div>\ - <input type="hidden" name="days[]" value="-">\ - </td>\ - <td>\ - <div class="input-group bootstrap-timepicker">\ - <span class="input-group-addon">\ - <span class="glyphicon glyphicon-time"></span>\ - </span>\ - <input required type="text" class="form-control timepicker2" name="openingtime[]" id="openingtimepicker" pattern="[0-9]{1,2}:[0-9]{2}" value="8:00">\ - </div>\ - \ - </td>\ - <td>\ - \ - <div class="input-group bootstrap-timepicker">\ - <span class="input-group-addon">\ - <span class="glyphicon glyphicon-time"></span>\ - </span>\ - <input required type="text" class="form-control timepicker2" name="closingtime[]" id="closingtimepicker" pattern="[0-9]{1,2}:[0-9]{2}" value="18:00">\ - </div>\ - \ - </td>\ - <td align="center">\ - <input type="checkbox" name="dontadd[]" value="dontadd"\ - </td>\ - </tr>'); - setTimepicker(); - - $(function () { - var requiredCheckboxes = $('.options :checkbox[required]'); - requiredCheckboxes.change(function () { - if (requiredCheckboxes.is(':checked')) { - requiredCheckboxes.removeAttr('required'); - } else { - requiredCheckboxes.attr('required', 'required'); - } - }); - }); - } - -</script> diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php index b2b6da4a..aff2dec5 100644 --- a/modules-available/locations/inc/location.inc.php +++ b/modules-available/locations/inc/location.inc.php @@ -27,6 +27,11 @@ class Location return $rows; } + /** + * Return row from location table for $locationId. + * @param $locationId + * @return array|bool row from DB, false if not found + */ public static function get($locationId) { return Database::queryFirst("SELECT * FROM location WHERE locationid = :locationId", compact('locationId')); diff --git a/style/default.css b/style/default.css index 894aa752..35b61277 100644 --- a/style/default.css +++ b/style/default.css @@ -467,6 +467,10 @@ nav.navbar.sidebar { color: #fff; } +.red-bg, input[type="checkbox"]:checked { + background:#f77; +} + /* this is based on https://github.com/flatlogic/awesome-bootstrap-checkbox and "fixes" the style of radio buttons and check boxes. it only applies if they're in a container that has the checkbox class */ |