From b90c97b1f096008b5fa9abf8c50a120a85c47a4e Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 30 Nov 2017 18:30:01 +0100 Subject: [roomplanner] Refactor to use runmode --- modules-available/locations/inc/location.inc.php | 25 +++++++++++ .../roomplanner/inc/pvsgenerator.inc.php | 35 ++++++++++++++- modules-available/roomplanner/install.inc.php | 43 ++++++++++++------ modules-available/roomplanner/page.inc.php | 36 ++++++++++++--- modules-available/runmode/inc/runmode.inc.php | 23 ++++++++++ modules-available/runmode/page.inc.php | 21 +++++++-- modules-available/statistics/inc/machine.inc.php | 5 +++ .../statistics/inc/statistics.inc.php | 51 ++++++++++++++++++---- 8 files changed, 205 insertions(+), 34 deletions(-) (limited to 'modules-available') diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php index 476f4c68..0576e660 100644 --- a/modules-available/locations/inc/location.inc.php +++ b/modules-available/locations/inc/location.inc.php @@ -37,6 +37,11 @@ class Location return Database::queryFirst("SELECT * FROM location WHERE locationid = :locationId", compact('locationId')); } + /** + * Get name of location + * @param int $locationId id of location to get name for + * @return string|false Name of location, false if locationId doesn't exist + */ public static function getName($locationId) { self::getLocationsAssoc(); @@ -46,6 +51,26 @@ class Location return self::$assocLocationCache[$locationId]['locationname']; } + /** + * Get all the names of the given location and its parents, up + * to the root element. Array keys will be locationids, value the names. + * @param int $locationId + * @return array|false locations, from furthest to nearest or false if locationId doesn't exist + */ + public static function getNameChain($locationId) + { + self::getLocationsAssoc(); + settype($locationId, 'int'); + if (!isset(self::$assocLocationCache[$locationId])) + return false; + $ret = array(); + while (isset(self::$assocLocationCache[$locationId])) { + $ret[$locationId] = self::$assocLocationCache[$locationId]['locationname']; + $locationId = self::$assocLocationCache[$locationId]['parentlocationid']; + } + return array_reverse($ret, true); + } + public static function getLocationsAssoc() { if (self::$assocLocationCache === false) { diff --git a/modules-available/roomplanner/inc/pvsgenerator.inc.php b/modules-available/roomplanner/inc/pvsgenerator.inc.php index d61e826b..43285057 100644 --- a/modules-available/roomplanner/inc/pvsgenerator.inc.php +++ b/modules-available/roomplanner/inc/pvsgenerator.inc.php @@ -189,7 +189,7 @@ class PvsGenerator private static function boundingBox($grid, &$minX, &$minY, &$maxX, &$maxY) { - $minX = PHP_INT_MAX; /* PHP_INT_MIN is only avaiable since PHP 7 */ + $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; @@ -202,4 +202,37 @@ class PvsGenerator } } + public static function runmodeConfigHook($machineUuid, $locationId, $data) + { + if (!empty($data)) { + $data = json_decode($data, true); + } + if (!is_array($data)) { + $data = array(); + } + ConfigHolder::add("SLX_PVS_CONFIG_URL", 'http://' . $_SERVER['SERVER_ADDR'] . $_SERVER['SCRIPT_NAME'] . '?do=roomplanner'); + + if (isset($data['dedicatedmgr']) && $data['dedicatedmgr']) { + ConfigHolder::add("SLX_ADDONS", false, 100000); + ConfigHolder::add("SLX_PVS_DEDICATED", 'yes'); + ConfigHolder::add("SLX_EXAM", false, 100000); + //ConfigHolder::add("SLX_SYSTEMD_TARGET", 'pvs-dedicated', 100000); + } else { + ConfigHolder::add("SLX_PVS_HYBRID", 'yes'); + } + } + + /** + * Get display name for manager of given locationId. + * @param $locationId + * @return bool|string + */ + public static function getManagerName($locationId) + { + $names = Location::getNameChain($locationId); + if ($names === false) + return false; + return implode(' / ', $names); + } + } diff --git a/modules-available/roomplanner/install.inc.php b/modules-available/roomplanner/install.inc.php index a6d98384..13365fe1 100644 --- a/modules-available/roomplanner/install.inc.php +++ b/modules-available/roomplanner/install.inc.php @@ -6,7 +6,6 @@ $res = array(); $res[] = tableCreate('location_roomplan', " `locationid` INT(11) NOT NULL, `managerip` varchar(45) CHARACTER SET ascii DEFAULT '', - `dedicatedmgr` tinyint(1) NOT NULL DEFAULT 0, `tutoruuid` char(36) CHARACTER SET ascii DEFAULT NULL, `roomplan` BLOB DEFAULT NULL, PRIMARY KEY (`locationid`), @@ -29,24 +28,40 @@ if (!tableHasColumn('location_roomplan', 'tutoruuid')) { } $res[] = UPDATE_DONE; } -if (!tableHasColumn('location_roomplan', 'dedicatedmgr')) { - $ret = Database::exec("ALTER TABLE `location_roomplan` ADD `dedicatedmgr` tinyint(1) NOT NULL DEFAULT 0 AFTER `managerip`") !== false; - if ($ret === false) { - finalResponse(UPDATE_FAILED, 'Adding dedicatedmgr to location_roomplan failed: ' . Database::lastError()); - } - $res[] = UPDATE_DONE; -} if (in_array(UPDATE_DONE, $res)) { Database::exec("ALTER TABLE `location_roomplan` - ADD CONSTRAINT `location_roomplan_ibfk_1` FOREIGN KEY (`locationid`) REFERENCES `location` (`locationid`) ON DELETE CASCADE"); + ADD CONSTRAINT `location_roomplan_ibfk_1` FOREIGN KEY (`locationid`) REFERENCES `location` (`locationid`) ON DELETE CASCADE"); Database::exec("ALTER TABLE `location_roomplan` - ADD CONSTRAINT `location_roomplan_ibfk_2` FOREIGN KEY (`tutoruuid`) REFERENCES `machine` (`machineuuid`) ON DELETE SET NULL ON UPDATE CASCADE"); + ADD CONSTRAINT `location_roomplan_ibfk_2` FOREIGN KEY (`tutoruuid`) REFERENCES `machine` (`machineuuid`) ON DELETE SET NULL ON UPDATE CASCADE"); } -if (in_array(UPDATE_DONE, $res)) { - finalResponse(UPDATE_DONE, 'Table created successfully'); +// 2017-11-30: Refactor to runmode +// managerip, dedicatedmgr, --> runmode +if (tableHasColumn('location_roomplan', 'dedicatedmgr')) { + if (!tableExists('runmode') || !tableExists('machine')) { + $res[] = UPDATE_RETRY; + } else { + $ret = Database::simpleQuery('SELECT lr.locationid, lr.managerip, lr.dedicatedmgr, m.machineuuid + FROM location_roomplan lr INNER JOIN machine m ON (m.clientip = lr.managerip)'); + if ($ret === false) { + $res[] = UPDATE_FAILED; + } else { + while ($row = $ret->fetch(PDO::FETCH_ASSOC)) { + $dedi = $row['dedicatedmgr'] != 0; + $data = json_encode(array('dedicatedmgr' => $dedi)); + Database::exec("INSERT IGNORE INTO runmode (machineuuid, module, modeid, modedata, isclient) + VALUES (:machineuuid, 'roomplanner', :locationid, :modedata, :isclient)", array( + 'machineuuid' => $row['machineuuid'], + 'locationid' => $row['locationid'], + 'modedata' => $data, + 'isclient' => ($dedi ? 0 : 1) + )); + } + Database::exec('ALTER TABLE location_roomplan DROP COLUMN dedicatedmgr'); + $res[] = UPDATE_DONE; + } + } } -finalResponse(UPDATE_NOOP, 'Everything already up to date'); - +responseFromArray($res); diff --git a/modules-available/roomplanner/page.inc.php b/modules-available/roomplanner/page.inc.php index 4e36d3ba..71499caf 100644 --- a/modules-available/roomplanner/page.inc.php +++ b/modules-available/roomplanner/page.inc.php @@ -58,7 +58,17 @@ class Page_Roomplanner extends Page if ($this->action === 'show') { /* do nothing */ Dashboard::disable(); - $config = Database::queryFirst('SELECT roomplan, managerip, dedicatedmgr, tutoruuid FROM location_roomplan WHERE locationid = :locationid', ['locationid' => $this->locationid]); + $config = Database::queryFirst('SELECT roomplan, managerip, tutoruuid FROM location_roomplan WHERE locationid = :locationid', ['locationid' => $this->locationid]); + $runmode = RunMode::getForMode(Page::getModule(), $this->locationid, true); + if (empty($runmode)) { + $config['dedicatedmgr'] = false; + } else { + $runmode = array_pop($runmode); + $config['managerip'] = $runmode['clientip']; + $config['manageruuid'] = $runmode['machineuuid']; + $data = json_decode($runmode['modedata'], true); + $config['dedicatedmgr'] = (isset($data['dedicatedmgr']) && $data['dedicatedmgr']); + } if ($config !== false) { $managerIp = $config['managerip']; $dediMgr = $config['dedicatedmgr'] ? 'checked' : ''; @@ -207,16 +217,30 @@ class Page_Roomplanner extends Page protected function saveRoomConfig($furniture, $tutorUuid) { $obj = json_encode(['furniture' => $furniture]); - Database::exec('INSERT INTO location_roomplan (locationid, roomplan, managerip, tutoruuid, dedicatedmgr)' - . ' VALUES (:locationid, :roomplan, :managerip, :tutoruuid, :dedicatedmgr)' + $managerIp = Request::post('managerip', '', 'string'); + Database::exec('INSERT INTO location_roomplan (locationid, roomplan, managerip, tutoruuid)' + . ' VALUES (:locationid, :roomplan, :managerip, :tutoruuid)' . ' ON DUPLICATE KEY UPDATE ' - . ' roomplan=VALUES(roomplan), managerip=VALUES(managerip), tutoruuid=VALUES(tutoruuid), dedicatedmgr=VALUES(dedicatedmgr)', [ + . ' roomplan=VALUES(roomplan), managerip=VALUES(managerip), tutoruuid=VALUES(tutoruuid)', [ 'locationid' => $this->locationid, 'roomplan' => $obj, - 'managerip' => Request::post('managerip', '', 'string'), - 'dedicatedmgr' => (Request::post('dedimgr') === 'on' ? 1 : 0), + 'managerip' => $managerIp, 'tutoruuid' => $tutorUuid ]); + // See if the client is known, set run-mode + if (empty($managerIp)) { + RunMode::deleteMode(Page::getModule(), $this->locationid); + } else { + RunMode::deleteMode(Page::getModule(), $this->locationid); + $pc = Statistics::getMachinesByIp($managerIp, Machine::NO_DATA, 'lastseen DESC'); + if (!empty($pc)) { + $dedicated = (Request::post('dedimgr') === 'on'); + $pc = array_shift($pc); + RunMode::setRunMode($pc->machineuuid, Page::getModule()->getIdentifier(), $this->locationid, json_encode([ + 'dedicatedmgr' => $dedicated + ]), !$dedicated); + } + } } protected function getFurniture($config) diff --git a/modules-available/runmode/inc/runmode.inc.php b/modules-available/runmode/inc/runmode.inc.php index 59f8cf01..50ae2d72 100644 --- a/modules-available/runmode/inc/runmode.inc.php +++ b/modules-available/runmode/inc/runmode.inc.php @@ -36,6 +36,9 @@ class RunMode */ public static function setRunMode($machineuuid, $moduleId, $modeId, $modeData = null, $isClient = null) { + if (is_object($moduleId)) { + $moduleId = $moduleId->getIdentifier(); + } // - Check if machine exists $machine = Statistics::getMachine($machineuuid, Machine::NO_DATA); if ($machine === false) @@ -196,6 +199,21 @@ class RunMode return call_user_func($conf->getModeName, $modeId); } + /** + * Delete given runmode. + * + * @param string|\Module $module Module runmode belongs to + * @param string $modeId run mode id + */ + public static function deleteMode($module, $modeId) + { + if (is_object($module)) { + $module = $module->getIdentifier(); + } + Database::exec('DELETE FROM runmode WHERE module = :module AND modeid = :modeId', + compact('module', 'modeId')); + } + } /* *\ @@ -235,6 +253,10 @@ class RunModeModuleConfig * @var bool If true, config.tgz should not be downloaded by the client */ public $noSysconfig = false; + /** + * @var bool Allow adding and removing machines to this mode via the generic form + */ + public $allowGenericEditor = true; public function __construct($file) { @@ -248,6 +270,7 @@ class RunModeModuleConfig $this->loadType($data, 'configHook', 'string'); $this->loadType($data, 'isClient', 'boolean'); $this->loadType($data, 'noSysconfig', 'boolean'); + $this->loadType($data, 'allowGenericEditor', 'boolean'); } private function loadType($data, $key, $type) diff --git a/modules-available/runmode/page.inc.php b/modules-available/runmode/page.inc.php index 24566cec..ef42e7be 100644 --- a/modules-available/runmode/page.inc.php +++ b/modules-available/runmode/page.inc.php @@ -26,20 +26,33 @@ class Page_RunMode extends Page $machines = array_filter(Request::post('machines', [], 'array'), 'is_string'); $module = Request::post('module', false, 'string'); $modeId = Request::post('modeid', false, 'string'); - // TODO Validate + $modConfig = RunMode::getModuleConfig($module); + if ($modConfig === false) { + Message::addError('module-hasnt-runmode', $module); + return; + } + if (!$modConfig->allowGenericEditor) { + Message::addError('cannot-edit-module', $module); + return; + } + $test = RunMode::getModeName($module, $modeId); + if ($test === false) { + Message::addError('invalid-modeid', $module, $modeId); + return; + } $active = 0; foreach ($machines as $machine) { $ret = RunMode::setRunMode($machine, $module, $modeId, null, null); if ($ret) { $active++; } else { - Message::addError('invalid-module-or-machine', $module, $machine); + Message::addError('runmode.machine-not-found', $machine); } } $deleted = Database::exec('DELETE FROM runmode WHERE module = :module AND modeid = :modeId AND machineuuid NOT IN (:machines)', compact('module', 'modeId', 'machines')); - Message::addError('enabled-removed-save', $active, $deleted); + Message::addSuccess('runmode.enabled-removed-save', $active, $deleted); $redirect = Request::post('redirect', false, 'string'); if ($redirect !== false) { Util::redirect($redirect); @@ -141,7 +154,7 @@ class Page_RunMode extends Page $moduleId = $module->getIdentifier(); $modeName = RunMode::getModeName($moduleId, $modeId); if ($modeName === false) { - Message::addError('invalid-modeid', $modeId); + Message::addError('invalid-modeid', $moduleId, $modeId); Util::redirect('?do=runmode'); } Render::addTemplate('machine-selector', [ diff --git a/modules-available/statistics/inc/machine.inc.php b/modules-available/statistics/inc/machine.inc.php index 8cb5e884..8605749b 100644 --- a/modules-available/statistics/inc/machine.inc.php +++ b/modules-available/statistics/inc/machine.inc.php @@ -50,6 +50,11 @@ class Machine */ public $logintime; + /** + * @var string state of machine (OFFLINE, IDLE, OCCUPIED, STANDBY) + */ + public $state; + /** * @var string json data of position inside room (if any), null/empty otherwise */ diff --git a/modules-available/statistics/inc/statistics.inc.php b/modules-available/statistics/inc/statistics.inc.php index 1c9ebf07..2500f16f 100644 --- a/modules-available/statistics/inc/statistics.inc.php +++ b/modules-available/statistics/inc/statistics.inc.php @@ -7,17 +7,12 @@ class Statistics private static $machineFields = false; - /** - * @param string $machineuuid - * @param int $returnData - * @return \Machine|false - */ - public static function getMachine($machineuuid, $returnData) + private static function initFields($returnData) { if (self::$machineFields === false) { $r = new ReflectionClass('Machine'); $props = $r->getProperties(ReflectionProperty::IS_PUBLIC); - self::$machineFields = array_flip(array_map(function($e) { return $e->getName(); }, $props)); + self::$machineFields = array_flip(array_map(function(/* @var ReflectionProperty $e */ $e) { return $e->getName(); }, $props)); } if ($returnData === Machine::NO_DATA) { unset(self::$machineFields['data']); @@ -26,8 +21,19 @@ class Statistics } else { Util::traceError('Invalid $returnData option passed'); } - $fields = implode(',', array_keys(self::$machineFields)); - $row = Database::queryFirst("SELECT * FROM machine WHERE machineuuid = :machineuuid", compact('machineuuid')); + return implode(',', array_keys(self::$machineFields)); + } + + /** + * @param string $machineuuid + * @param int $returnData What kind of data to return Machine::NO_DATA, Machine::RAW_DATA, ... + * @return \Machine|false + */ + public static function getMachine($machineuuid, $returnData) + { + $fields = self::initFields($returnData); + + $row = Database::queryFirst("SELECT $fields FROM machine WHERE machineuuid = :machineuuid", compact('machineuuid')); if ($row === false) return false; $m = new Machine(); @@ -37,4 +43,31 @@ class Statistics return $m; } + /** + * @param string $ip + * @param int $returnData What kind of data to return Machine::NO_DATA, Machine::RAW_DATA, ... + * @param string $sort something like 'lastseen ASC' - not sanitized, don't pass user input! + * @return \Machine[] list of matches + */ + public static function getMachinesByIp($ip, $returnData, $sort = false) + { + $fields = self::initFields($returnData); + + if ($sort === false) { + $sort = ''; + } else { + $sort = "ORDER BY $sort"; + } + $res = Database::simpleQuery("SELECT $fields FROM machine WHERE clientip = :ip $sort", compact('ip')); + $list = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $m = new Machine(); + foreach ($row as $key => $val) { + $m->{$key} = $val; + } + $list[] = $m; + } + return $list; + } + } -- cgit v1.2.3-55-g7522