diff options
author | Simon Rettberg | 2019-07-23 13:57:20 +0200 |
---|---|---|
committer | Simon Rettberg | 2019-07-23 13:57:20 +0200 |
commit | 04828b94e9df8321feae0bfb99daa468c0d3d383 (patch) | |
tree | e3b00490dc14a0f5d6ffda5bab1ce7c82bafee52 /modules-available/roomplanner | |
parent | slx-fixes.js: Make .cachedScript more compatible to .getScript (diff) | |
download | slx-admin-04828b94e9df8321feae0bfb99daa468c0d3d383.tar.gz slx-admin-04828b94e9df8321feae0bfb99daa468c0d3d383.tar.xz slx-admin-04828b94e9df8321feae0bfb99daa468c0d3d383.zip |
[roomplanner] Support creating recursive/composed rooms
Diffstat (limited to 'modules-available/roomplanner')
-rw-r--r-- | modules-available/roomplanner/api.inc.php | 17 | ||||
-rw-r--r-- | modules-available/roomplanner/inc/composedroom.inc.php | 175 | ||||
-rw-r--r-- | modules-available/roomplanner/inc/pvsgenerator.inc.php | 206 | ||||
-rw-r--r-- | modules-available/roomplanner/inc/room.inc.php | 176 | ||||
-rw-r--r-- | modules-available/roomplanner/inc/simpleroom.inc.php | 148 | ||||
-rw-r--r-- | modules-available/roomplanner/page.inc.php | 20 | ||||
-rw-r--r-- | modules-available/roomplanner/templates/edit-composed-room.html | 2 | ||||
-rw-r--r-- | modules-available/roomplanner/templates/svg-plan.html | 6 |
8 files changed, 537 insertions, 213 deletions
diff --git a/modules-available/roomplanner/api.inc.php b/modules-available/roomplanner/api.inc.php index f964bea1..8ddb945c 100644 --- a/modules-available/roomplanner/api.inc.php +++ b/modules-available/roomplanner/api.inc.php @@ -1,16 +1,29 @@ <?php +// SVG if (Request::any('show') === 'svg') { $ret = PvsGenerator::generateSvg(Request::any('locationid', false, 'int'), Request::any('machineuuid', false, 'string'), Request::any('rotate', 0, 'int'), Request::any('scale', 1, 'float')); if ($ret === false) { - Header('HTTP/1.1 404 Not Found'); - exit; + if (Request::any('fallback', 0, 'int') === 0) { + Header('HTTP/1.1 404 Not Found'); + exit; + } + $ret = <<<EOF +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 64 64" height="64" width="64"> + <g> + <path d="M 0,0 64,64 Z" style="stroke:#ff0000;stroke-width:5" /> + <path d="M 0,64 64,0 Z" style="stroke:#ff0000;stroke-width:5" /> + </g> +</svg> +EOF; } Header('Content-Type: image/svg+xml'); die($ret); } +// PVS.ini die(PvsGenerator::generate()); diff --git a/modules-available/roomplanner/inc/composedroom.inc.php b/modules-available/roomplanner/inc/composedroom.inc.php index 11e8455e..cdd50984 100644 --- a/modules-available/roomplanner/inc/composedroom.inc.php +++ b/modules-available/roomplanner/inc/composedroom.inc.php @@ -1,65 +1,80 @@ <?php -class ComposedRoom +class ComposedRoom extends Room { /** * @var string How to compose contained rooms. Value is either horizontal or vertical. */ - public $orientation = 'horizontal'; + private $orientation = 'horizontal'; /** * @var int[] Order in which contained rooms are composed. List of locationid. */ - public $list; + private $list; /** * @var bool Whether composed room is active, ie. visible in PVS. */ - public $enabled; + private $enabled; /** * @var int locationid of contained room that is the controlling room; */ - public $controlRoom; + private $controlRoom; - public function __construct($data) + /** + * ComposedRoom constructor. + * + * @param array|true $row DB row to instantiate from, or true to read from $_POST + */ + public function __construct($row, $sanitize = true) { - if ($data instanceof ComposedRoom) { - foreach ($data as $k => $v) { - $this->{$k} = $v; - } + if ($row === true) { + $this->orientation = Request::post('orientation', 'horizontal', 'string'); + $this->enabled = (bool)Request::post('enabled', 0, 'int'); + $this->controlRoom = Request::post('controlroom', 0, 'int'); + $vals = Request::post('sort', [], 'array'); + asort($vals, SORT_ASC | SORT_NUMERIC); + $this->list = array_keys($vals); } else { - if (is_array($data) && isset($data['roomplan'])) { + parent::__construct($row); + if (is_array($row) && isset($row['roomplan'])) { // From DB - $data = json_decode($data['roomplan'], true); - } elseif (is_string($data)) { - // Just JSON - $data = json_decode($data, true); + $row = json_decode($row['roomplan'], true); } - if (is_array($data)) { + if (is_array($row)) { foreach ($this as $k => $v) { - if (isset($data[$k])) { - $this->{$k} = $data[$k]; + if (isset($row[$k])) { + $this->{$k} = $row[$k]; } } } } - $this->sanitize(); + if ($sanitize) { + $this->sanitize(); + } } /** * Make sure all member vars have the proper type */ - private function sanitize() + protected function sanitize() { $this->orientation = ($this->orientation === 'horizontal' ? 'horizontal' : 'vertical'); settype($this->enabled, 'bool'); settype($this->list, 'array'); settype($this->controlRoom, 'int'); - foreach ($this->list as &$v) { + self::init(); + //error_log('List: ' . print_r($this->list, true)); + //error_log('Rooms: ' . print_r(self::$rooms, true)); + $old = $this->list; + $this->list = []; + foreach ($old as $v) { settype($v, 'int'); + if (isset(self::$rooms[$v])) { + $this->list[] = $v; + } } - $this->list = array_values($this->list); if (!empty($this->list) && !in_array($this->controlRoom, $this->list)) { $this->controlRoom = $this->list[0]; } @@ -71,7 +86,121 @@ class ComposedRoom public function serialize() { $this->sanitize(); - return json_encode($this); + $out = []; + foreach ($this as $k => $v) { + $out[$k] = $v; + } + return json_encode($out); + } + + public function orientation() + { + return $this->orientation; + } + + public function subLocationIds() + { + return $this->list; } + public function controlRoom() + { + return $this->controlRoom; + } + + public function machineCount() + { + $sum = 0; + foreach ($this->list as $lid) { + $sum += self::$rooms[$lid]->machineCount(); + } + return $sum; + } + + public function getSize(&$width, &$height) + { + $horz = ($this->orientation == 'horizontal'); + $width = $height = 0; + foreach ($this->list as $locId) { + self::$rooms[$locId]->getSize($w, $h); + $width = $horz ? $width + $w : max($width, $w); + $height = !$horz ? $height + $h : max($height, $h); + } + } + + public function getIniClientSection(&$i, $offX = 0, $offY = 0) + { + if (!$this->enabled) + return false; + if ($this->orientation == 'horizontal') { + $x = 1; + $y = 0; + } else { + $x = 0; + $y = 1; + } + $out = ''; + foreach ($this->list as $locId) { + $ret = self::$rooms[$locId]->getIniClientSection($i, $offX, $offY); + if ($ret !== false) { + $out .= $ret; + self::$rooms[$locId]->getSize($w, $h); + $offX += $w * $x; + $offY += $h * $y; + } + } + if (empty($out)) + return false; + return $out; + } + + public function getShiftedArray($offX = 0, $offY = 0) + { + if (!$this->enabled) + return false; + if ($this->orientation == 'horizontal') { + $x = 1; + $y = 0; + } else { + $x = 0; + $y = 1; + } + $ret = []; + foreach ($this->list as $locId) { + $new = self::$rooms[$locId]->getShiftedArray($offX, $offY); + if ($new !== false) { + $ret = array_merge($ret, $new); + self::$rooms[$locId]->getSize($w, $h); + $offX += $w * $x; + $offY += $h * $y; + } + } + if (empty($ret)) + return false; + return $ret; + } + + public function getManagerIp() + { + if (isset(self::$rooms[$this->controlRoom])) + return self::$rooms[$this->controlRoom]->getManagerIp(); + return false; + } + + public function getTutorIp() + { + if (isset(self::$rooms[$this->controlRoom])) + return self::$rooms[$this->controlRoom]->getTutorIp(); + return false; + } + + public function isLeaf() + { + return false; + } + + public function shouldSkip() + { + return !$this->enabled; + } } diff --git a/modules-available/roomplanner/inc/pvsgenerator.inc.php b/modules-available/roomplanner/inc/pvsgenerator.inc.php index cfb38fd2..3646ae6a 100644 --- a/modules-available/roomplanner/inc/pvsgenerator.inc.php +++ b/modules-available/roomplanner/inc/pvsgenerator.inc.php @@ -1,77 +1,31 @@ <?php -define('X', 0); -define('Y', 1); - class PvsGenerator { public static function generate() { - /* get all rooms */ - $rooms = array(); - // Use left joins everywhere so we still have the complete list of locations below - // for figuring out which locations are leafs and which aren't - $ret = Database::simpleQuery( - 'SELECT l.locationid, l.parentlocationid, l.locationname, lr.locationid AS notnull, lr.managerip, lr.tutoruuid, m.clientip as tutorip ' - . 'FROM location l ' - . 'LEFT JOIN location_roomplan lr ON (l.locationid = lr.locationid)' - . 'LEFT JOIN machine m ON (lr.tutoruuid = m.machineuuid)'); - while ($row = $ret->fetch(PDO::FETCH_ASSOC)) { - $row['locationname'] = str_replace(',', ';', $row['locationname']); // comma probably not the best sep here - settype($row['locationid'], 'int'); - settype($row['parentlocationid'], 'int'); - $rooms[$row['locationid']] = $row; - } - // Mark all non-leafs as skip - foreach ($rooms as &$room) { - if ($room['parentlocationid'] > 0 && isset($rooms[$room['parentlocationid']])) { - $rooms[$room['parentlocationid']]['skip'] = true; // Don't just unset, might be wrong order - } - } - // Now un-mark all where there's at least one child without valid room plan - foreach ($rooms as &$room) { - if (!isset($room['skip']) && (is_null($room['notnull']) || empty($room['managerip']))) { - $room['skip'] = true; - $r2 =& $room; - while ($r2['parentlocationid'] > 0) { - $r2 =& $rooms[$r2['parentlocationid']]; - if (!(is_null($room['notnull']) || empty($room['managerip']))) { - unset($r2['skip']); - break; - } - } - } - } - unset($room, $r2); // refd! - /* collect names and build room blocks - filter empty rooms while at it */ $roomNames = array(); $roomBlocks = ''; + $rooms = Room::getAll(); foreach ($rooms as $room) { - if (is_null($room['notnull']) || isset($room['skip'])) // Not leaf + if ($room->shouldSkip()) continue; - if (Module::isAvailable('runmode')) { - $pc = RunMode::getForMode('roomplanner', $room['locationid'], true); - if (!empty($pc)) { - $pc = array_pop($pc); - $room['managerip'] = $pc['clientip']; - } - } - if (empty($room['managerip'])) // rooms without managerips don't make sense + if ($room->getManagerIp() === false) // No .ini entry for rooms without manager (do we want this?) continue; $roomBlock = PvsGenerator::generateRoomBlock($room); if ($roomBlock === false) continue; // Room nonexistent or empty - $roomNames[] = md5($room['locationname']); - $roomBlocks .= $roomBlock; + $section = substr(md5($room->locationId() . '-' . $room->locationName()), 0, 10); + $roomNames[] = $section; + $roomBlocks .= "[$section]\n" . $roomBlock; } /* output room plus [General]-block */ return "[General]\n" . 'rooms=' . implode(', ', $roomNames) . "\n" . "allowClientQuit=False\n" // TODO: configurable - . "showLockDesktopButton=True\n" // TODO: Make this configurable (or not) . "\n\n" . $roomBlocks; } @@ -79,69 +33,36 @@ class PvsGenerator /** * Generate .ini section for specific room. * - * @param $room array room/location data as fetched from db - * @return string|bool .ini section for room, or false if room is empty + * @param Room $room room/location data as fetched from db + * @return string|false .ini section for room, or false if room is empty */ private static function generateRoomBlock($room) { - $out = '[' . md5($room['locationname']) . "]\n"; - - - /* find all clients in that room */ - $machines = PvsGenerator::getMachines($room['locationid']); - if (empty($machines)) + $room->getSize($sizeX, $sizeY); + if ($sizeX === 0 || $sizeY === 0) + return false; + $count = 0; + $section = $room->getIniClientSection($count); + if ($section === false) return false; - $out .= "name=" . $room['locationname'] . "\n"; + $cs = SimpleRoom::CLIENT_SIZE; + $out = "name=" . $room->locationName() . "\n"; /* manager */ - $mgr = $room['managerip']; - $tutor = $room['tutorip']; - if ($mgr) { - $out .= 'mgrIP=' . $mgr . "\n"; - } + $out .= 'mgrIP=' . $room->getManagerIp() . "\n"; /* tutor */ - if ($tutor) { - $out .= 'tutorIP=' . $tutor . "\n"; + if ($room->getTutorIp() !== false) { + $out .= 'tutorIP=' . $room->getTutorIp() . "\n"; } - /* grid */ - $out .= PvsGenerator::generateGrid($machines); - - return $out . "\n"; - } - - /** - * Generate grid size information and client position data for given clients. - * - * @param $machines array list of clients - * @return string grid and position data as required for a room's .ini section - */ - private static function generateGrid($machines) - { - $out = ""; - - /* find bounding box */ - PvsGenerator::boundingBox($machines, $minX, $minY, $maxX, $maxY); - $clientSizeX = 4; /* TODO: optimize */ - $clientSizeY = 4; /* TODO: optimize */ - $sizeX = max($maxX - $minX + $clientSizeX, 1); /* never negative */ - $sizeY = max($maxY - $minY + $clientSizeY, 1); /* and != 0 to avoid divide-by-zero in pvsmgr */ - - /* output basic settings for this room */ + /* basic settings for this room */ $out .= "gridSize=@Size($sizeX $sizeY)\n"; - $out .= "clientSize=@Size($clientSizeX $clientSizeY)\n"; - $out .= "client\\size=" . count($machines) . "\n"; - - /* output individual client positions, shift coordinates to origin */ - $i = 1; - foreach ($machines as $pos) { - $out .= "client\\$i\\ip={$pos['clientip']}\n"; - $out .= "client\\$i\\pos=@Point(" . ($pos['gridCol'] - $minX) . ' ' . ($pos['gridRow'] - $minY) . ")\n"; - $i++; - } + $out .= "clientSize=@Size($cs $cs)\n"; + $out .= "client\\size=$count\n"; - return $out; + /* output with grid */ + return $out . $section . "\n"; } /** @@ -154,6 +75,7 @@ class PvsGenerator * @param int|false $locationId * @param string|false $highlightUuid * @param int $rotate rotate plan (0-3 for N E S W up, -1 for "auto" if highlightUuid is given) + * @param float $scale scaling factor for output * @return string SVG */ public static function generateSvg($locationId = false, $highlightUuid = false, $rotate = 0, $scale = 1) @@ -167,10 +89,15 @@ class PvsGenerator return false; $locationId = $locationId['fixedlocationid']; } - $machines = self::getMachines($locationId); - if (empty($machines)) + // Load room + $room = Room::get($locationId); + if ($room === false) return false; + $room->getSize($sizeX, $sizeY); + if ($sizeX === 0 || $sizeY === 0) + return false; // Empty + $machines = $room->getShiftedArray(); $ORIENTATION = ['north' => 2, 'east' => 3, 'south' => 0, 'west' => 1]; if (is_string($highlightUuid)) { $highlightUuid = strtoupper($highlightUuid); @@ -186,22 +113,13 @@ class PvsGenerator } } $rotate %= 4; - // Highlight given machine, rotate it's "keyboard" + // Highlight given machine, rotate its "keyboard" foreach ($machines as &$machine) { if ($machine['machineuuid'] === $highlightUuid) { $machine['class'] = 'hl'; } $machine['rotation'] = $ORIENTATION[$machine['rotation']] * 90; } - PvsGenerator::boundingBox($machines, $minX, $minY, $maxX, $maxY); - $clientSizeX = 4; /* this is optimal */ - $clientSizeY = 4; - $minX--; - $minY--; - $maxX++; - $maxY++; - $sizeX = max($maxX - $minX + $clientSizeX, 1); /* never negative */ - $sizeY = max($maxY - $minY + $clientSizeY, 1); /* and != 0 to avoid divide-by-zero in pvsmgr */ if ($rotate === 0) { $centerY = $centerX = 0; } elseif ($rotate === 1) { @@ -221,11 +139,8 @@ class PvsGenerator 'centerX' => $centerX, 'centerY' => $centerY, 'rotate' => $rotate * 90, - 'shiftX' => -$minX, - 'shiftY' => -$minY, 'machines' => $machines, - 'line' => ['x1' => $minX, 'y1' => $maxY + $clientSizeY, - 'x2' => $maxX + $clientSizeX, 'y2' => $maxY + $clientSizeY], + 'line' => ['x' => $sizeX, 'y' => $sizeY], ], 'roomplanner'); // FIXME: Needs module param if called from api.inc.php } @@ -236,58 +151,6 @@ class PvsGenerator $b = $tmp; } - /** - * Get all clients for given room with IP and position. - * - * @param $roomid int locationid of room - * @return array - */ - private static function getMachines($roomid) - { - $ret = Database::simpleQuery( - 'SELECT machineuuid, clientip, position FROM machine WHERE fixedlocationid = :locationid', - ['locationid' => $roomid]); - - $machines = array(); - - while ($row = $ret->fetch(PDO::FETCH_ASSOC)) { - $position = json_decode($row['position'], true); - - if ($position === false || !isset($position['gridRow']) || !isset($position['gridCol'])) - continue; // TODO: Remove entry/set to NULL? - - $rotation = 'north'; - if (preg_match('/(north|east|south|west)/', $position['itemlook'], $out)) { - $rotation = $out[1]; - } - $machines[] = array( - 'machineuuid' => $row['machineuuid'], - 'clientip' => $row['clientip'], - 'gridRow' => $position['gridRow'], - 'gridCol' => $position['gridCol'], - 'rotation' => $rotation, - ); - } - - return $machines; - - } - - private static function boundingBox($machines, &$minX, &$minY, &$maxX, &$maxY) - { - $minX = PHP_INT_MAX; /* PHP_INT_MIN is only available since PHP 7 */ - $maxX = ~PHP_INT_MAX; - $minY = PHP_INT_MAX; - $maxY = ~PHP_INT_MAX; - - foreach ($machines as $pos) { - $minX = min($minX, $pos['gridCol']); - $maxX = max($maxX, $pos['gridCol']); - $minY = min($minY, $pos['gridRow']); - $maxY = max($maxY, $pos['gridRow']); - } - } - public static function runmodeConfigHook($machineUuid, $locationId, $data) { if (!empty($data)) { @@ -308,6 +171,7 @@ class PvsGenerator /** * Get display name for manager of given locationId. + * Hook for "runmode" module to resolve mode name. * @param $locationId * @return bool|string */ diff --git a/modules-available/roomplanner/inc/room.inc.php b/modules-available/roomplanner/inc/room.inc.php new file mode 100644 index 00000000..855bdbcf --- /dev/null +++ b/modules-available/roomplanner/inc/room.inc.php @@ -0,0 +1,176 @@ +<?php + +abstract class Room +{ + + /** + * @var Room[] list of all rooms + */ + protected static $rooms = null; + + /** + * @var int id for this room + */ + private $locationId; + + /** + * @var string name of this room + */ + private $locationName; + + protected static function init() + { + if (self::$rooms !== null) + return; + /* get all rooms */ + self::$rooms = []; + $ret = Database::simpleQuery( + 'SELECT lr.locationid, lr.managerip, lr.tutoruuid, lr.roomplan, m.clientip as tutorip + FROM location_roomplan lr + LEFT JOIN machine m ON (lr.tutoruuid = m.machineuuid)'); + while ($row = $ret->fetch(PDO::FETCH_ASSOC)) { + $row = self::loadSingleRoom($row); + if ($row === false) + continue; + self::$rooms[$row->locationId] = $row; + } + foreach (self::$rooms as $room) { + $room->sanitize(); + } + } + + /** + * Instantiate ComposedRoom or MachineGroup depending on contents of $row + * @param array $row DB row from location_roomplan. + * @return Room|false Room instance, false on error + */ + private static function loadSingleRoom($row) + { + $locations = Location::getLocationsAssoc(); + settype($row['locationid'], 'int'); + if (!isset($locations[$row['locationid']])) + return false; + if ($locations[$row['locationid']]['isleaf']) + return new SimpleRoom($row); + return new ComposedRoom($row, false); + } + + /** + * Get array of all rooms with room plan + * @return Room[] + */ + public static function getAll() + { + self::init(); + return self::$rooms; + } + + /** + * Get room instance for given location + * @param int $locationId room to get + * @return Room|false requested room, false if not configured or not found + */ + public static function get($locationId) + { + if (self::$rooms === null) { + $room = Database::queryFirst( + 'SELECT lr.locationid, lr.managerip, lr.tutoruuid, lr.roomplan, m.clientip as tutorip + FROM location_roomplan lr + LEFT JOIN machine m ON (lr.tutoruuid = m.machineuuid) + WHERE lr.locationid = :lid', ['lid' => $locationId]); + if ($room === false) + return false; + $room = self::loadSingleRoom($room); + // If it's a leaf room we probably don't need any other rooms, return it + if ($room->isLeaf()) + return $room; + // Otherwise init the full tree so we can resolve composed rooms later + self::init(); + } + if (isset(self::$rooms[$locationId])) + return self::$rooms[$locationId]; + return false; + } + + public function __construct($row) + { + $locations = Location::getLocationsAssoc(); + $this->locationId = (int)$row['locationid']; + $this->locationName = $locations[$this->locationId]['locationname']; + } + + /** + * @return int number of machines in this room + */ + abstract public function machineCount(); + + /** + * Size of this room, returned by reference. + * @param int $width OUT width of room + * @param int $height OUT height of room + */ + abstract public function getSize(&$width, &$height); + + /** + * Get clients in this room in .ini format for PVS. + * Adjusted so the top/left client is at (0|0), which + * is further adjustable with $offX and $offY. + * @param int $i offset for indexing clients + * @param int $offX positional X offset for clients + * @param int $offY positional Y offset for clients + * @return string|false + */ + abstract public function getIniClientSection(&$i, $offX = 0, $offY = 0); + + /** + * Get clients in this room as array. + * Adjusted so the top/left client is at (0|0), which + *is further adjustable with $offX and $offY. + * @param int $offX + * @param int $offY + * @return array + */ + abstract public function getShiftedArray($offX = 0, $offY = 0); + + /** + * @return string|false IP address of manager. + */ + abstract public function getManagerIp(); + + /** + * @return string|false IP address of tutor client. + */ + abstract public function getTutorIp(); + + /** + * @return bool true if this is a simple/leaf room, false for composed rooms. + */ + abstract public function isLeaf(); + + /** + * @return bool should this room be skipped from output? true for empty SimpleRoom or disabled ComposedRoom. + */ + abstract public function shouldSkip(); + + /** + * Sanitize this room's data. + */ + abstract protected function sanitize(); + + /** + * @return string get room's name. + */ + public function locationName() + { + return $this->locationName; + } + + /** + * @return int get room's id. + */ + public function locationId() + { + return $this->locationId; + } + +}
\ No newline at end of file diff --git a/modules-available/roomplanner/inc/simpleroom.inc.php b/modules-available/roomplanner/inc/simpleroom.inc.php new file mode 100644 index 00000000..78db6c4a --- /dev/null +++ b/modules-available/roomplanner/inc/simpleroom.inc.php @@ -0,0 +1,148 @@ +<?php + +class SimpleRoom extends Room +{ + + const CLIENT_SIZE = 4; + + private $machines = []; + + private $bb = false; + + private $tutorIp = false; + + private $managerIp = false; + + public function __construct($row) + { + parent::__construct($row); + $locationId = (int)$row['locationid']; + $ret = Database::simpleQuery( + 'SELECT machineuuid, clientip, position FROM machine WHERE fixedlocationid = :locationid', + ['locationid' => $locationId]); + + while ($clientRow = $ret->fetch(PDO::FETCH_ASSOC)) { + $position = json_decode($clientRow['position'], true); + + if ($position === false || !isset($position['gridRow']) || !isset($position['gridCol'])) + continue; // TODO: Remove entry/set to NULL? + + $rotation = 'north'; + if (preg_match('/(north|east|south|west)/', $position['itemlook'], $out)) { + $rotation = $out[1]; + } + $this->machines[] = array( + 'machineuuid' => $clientRow['machineuuid'], + 'clientip' => $clientRow['clientip'], + 'gridRow' => $position['gridRow'], + 'gridCol' => $position['gridCol'], + 'rotation' => $rotation, + ); + } + // Runmode info overrides IP given + if (Module::isAvailable('runmode')) { + $pc = RunMode::getForMode('roomplanner', $locationId, true); + if (!empty($pc)) { + $pc = array_pop($pc); + $row['managerip'] = $pc['clientip']; + } + } + if (!empty($row['managerip'])) { + $this->managerIp = $row['managerip']; + } + if (!empty($row['tutorip'])) { + $this->tutorIp = $row['tutorip']; + } + } + + public function machineCount() + { + return count($this->machines); + } + + public function getSize(&$width, &$height) + { + if (empty($this->machines)) { + $width = $height = 0; + return; + } + $this->boundingBox($minX, $minY, $maxX, $maxY); + // client's size that cannot be configured as of today + $width = max($maxX - $minX + self::CLIENT_SIZE, 1); + $height = max($maxY - $minY + self::CLIENT_SIZE, 1); + } + + public function getIniClientSection(&$i, $offX = 0, $offY = 0) + { + /* output individual client positions, shift coordinates to requested position */ + $out = ''; + $this->boundingBox($minX, $minY, $maxX, $maxY); + foreach ($this->machines as $pos) { + $i++; + $out .= "client\\$i\\ip={$pos['clientip']}\n" + . "client\\$i\\pos=@Point(" . ($pos['gridCol'] + $offX -$minX) . ' ' . ($pos['gridRow'] + $offY - $minY) . ")\n"; + } + + return $out; + } + + public function getShiftedArray($offX = 0, $offY = 0) + { + /* output individual client positions, shift coordinates to requested position */ + $ret = []; + $this->boundingBox($minX, $minY, $maxX, $maxY); + foreach ($this->machines as $pos) { + $pos['gridCol'] += $offX - $minX; + $pos['gridRow'] += $offY - $minY; + $ret[] = $pos; + } + + return $ret; + } + + private function boundingBox(&$minX, &$minY, &$maxX, &$maxY) + { + if ($this->bb !== false) { + $minX = $this->bb[0]; + $minY = $this->bb[1]; + $maxX = $this->bb[2]; + $maxY = $this->bb[3]; + } else { + $minX = $minY = PHP_INT_MAX; /* PHP_INT_MIN is only available since PHP 7 */ + $maxX = $maxY = ~PHP_INT_MAX; + foreach ($this->machines as $pos) { + $minX = min($minX, $pos['gridCol']); + $maxX = max($maxX, $pos['gridCol']); + $minY = min($minY, $pos['gridRow']); + $maxY = max($maxY, $pos['gridRow']); + } + $this->bb = [$minX, $minY, $maxX, $maxY]; + } + } + + public function getManagerIp() + { + return $this->managerIp; + } + + public function getTutorIp() + { + return $this->tutorIp; + } + + public function isLeaf() + { + return true; + } + + public function shouldSkip() + { + return empty($this->machines); + } + + protected function sanitize() + { + // Nothing + } + +} diff --git a/modules-available/roomplanner/page.inc.php b/modules-available/roomplanner/page.inc.php index d1543a9e..de917a1a 100644 --- a/modules-available/roomplanner/page.inc.php +++ b/modules-available/roomplanner/page.inc.php @@ -122,23 +122,23 @@ class Page_Roomplanner extends Page private function showComposedEditor() { // Load settings - $row = Database::queryFirst("SELECT roomplan FROM location_roomplan WHERE locationid = :lid", [ + $row = Database::queryFirst("SELECT locationid, roomplan FROM location_roomplan WHERE locationid = :lid", [ 'lid' => $this->locationid, ]); $room = new ComposedRoom($row); $params = [ 'location' => $this->location, 'locations' => [], - $room->orientation . '_checked' => 'checked', + $room->orientation() . '_checked' => 'checked', ]; - if ($room->enabled) { + if (!$room->shouldSkip()) { $params['enabled_checked'] = 'checked'; } - $inverseList = array_flip($room->list); + $inverseList = array_flip($room->subLocationIds()); $sortList = []; // Load locations $locs = Location::getLocationsAssoc(); - foreach ($this->location['children'] as $loc) { + foreach ($this->location['directchildren'] as $loc) { if (isset($locs[$loc])) { $data = $locs[$loc]; if (isset($inverseList[$loc])) { @@ -146,7 +146,7 @@ class Page_Roomplanner extends Page } else { $sortList[] = 1000 + $loc; } - if ($loc === $room->controlRoom) { + if ($loc === $room->controlRoom()) { $data['checked'] = 'checked'; } $params['locations'][] = $data; @@ -264,13 +264,7 @@ class Page_Roomplanner extends Page private function saveComposedRoom($isAjax) { - $room = new ComposedRoom(null); - $room->orientation = Request::post('orientation', 'horizontal', 'string'); - $room->enabled = (bool)Request::post('enabled', 0, 'int'); - $room->controlRoom = Request::post('controlroom', 0, 'int'); - $vals = Request::post('sort', [], 'array'); - asort($vals, SORT_ASC | SORT_NUMERIC); - $room->list = array_keys($vals); + $room = new ComposedRoom(true); $res = Database::exec('INSERT INTO location_roomplan (locationid, roomplan) VALUES (:lid, :plan) ON DUPLICATE KEY UPDATE roomplan = VALUES(roomplan)', ['lid' => $this->locationid, 'plan' => $room->serialize()]); diff --git a/modules-available/roomplanner/templates/edit-composed-room.html b/modules-available/roomplanner/templates/edit-composed-room.html index 64a02d61..3b87cfea 100644 --- a/modules-available/roomplanner/templates/edit-composed-room.html +++ b/modules-available/roomplanner/templates/edit-composed-room.html @@ -31,7 +31,7 @@ <div class="name text-nowrap small">{{locationname}}</div> <div class="img"> <input type="hidden" class="sort-val" name="sort[{{locationid}}]"> - <img src="api.php?do=roomplanner&show=svg&locationid={{locationid}}&scale=2.2"> + <img src="api.php?do=roomplanner&show=svg&locationid={{locationid}}&scale=2.2&fallback=1"> </div> <div class="clearfix"></div> </div> diff --git a/modules-available/roomplanner/templates/svg-plan.html b/modules-available/roomplanner/templates/svg-plan.html index 16899e5c..a2ecd5a7 100644 --- a/modules-available/roomplanner/templates/svg-plan.html +++ b/modules-available/roomplanner/templates/svg-plan.html @@ -33,9 +33,9 @@ <stop offset="100%" stop-color="#074" /> </radialGradient> </defs> - <g transform="scale({{scale}}) rotate({{rotate}} {{centerX}} {{centerY}}) translate({{shiftX}} {{shiftY}})"> - <line x1="{{line.x1}}" y1="{{line.y1}}" - x2="{{line.x2}}" y2="{{line.y2}}" + <g transform="scale({{scale}}) rotate({{rotate}} {{centerX}} {{centerY}})"> + <line x1="0" y1="{{line.y}}" + x2="{{line.x}}" y2="{{line.y}}" style="stroke:#555;stroke-width:.2;opacity:.5" /> {{#machines}} <g transform="translate({{gridCol}} {{gridRow}})"> |