summaryrefslogblamecommitdiffstats
path: root/modules-available/sysconfig/page.inc.php
blob: c015d059a2921689df3965f26b58d0a9f279c721 (plain) (tree)
1
2
3
4
5
6
7
8

     

                                 
 

                                                                                                                 
          




                                                










                                                         


                                         
 
                                          
                                                                
                                                   
                 
 















                                                                                              
                                                         
 
                                                                                      

                                                                                               


                                           

                                                          
                                                              
                                               

                                                     
 


                                                                  
                                                                      
                                                   
                         
                                                                       
                                                                          


                                                                      
                                                                      

                                                       
                 
 

                                                                                
                                                              






                                                                  
                                                                      
                                                   
                         

                                                                       
                                                                                           
                                                        
                         

                                                                                 
                                                                      

                                                       
                 
         
 





                                                                                   


                                                         
                                                                   
                                  
                                 
                                                              


                                                 
                                                              


                                                 


                                                                          
                                                                           
                         
                                                                        



                                                                


                                                     


                                                                                                                                 
                                                                                                   

                               
                                                                   


                                                                                  
                              
                                                                   


                                                                                  
                        
                 
                                                                     
         
 
                                                                               










                                                                         



                                                             
         
                          
                                                                                                                       
                                                                                                                               


                                                                                     
                                                
                                             
                                   





                                                                                            
                                        

















                                                                                         
                                           
                                                               
                                                                                                                           

                                                               
                                                             



                                                                                                         

                                                                                   
                          
                 
                              




                                                                            



                                                                                                



                                      
                                 
                                                        


                                                                                             
                              

                                                              

                                                                                                    
                                                                                 
                                                           
         

                                                      

                                 
                                                                                                                                                               
                                     
                                                                       
                                                                                        
                 
 
                                             
                                                                   
                                                  




                                                                                          
                                                                                        
                 
                                                                    

                                      
                                                                                                                                 


                                         
 





                                                                                                                                                 
                                                                                        






                                                                                                  
 
                                   
                                        




                                                                
 
                                      
                                                                                                                                       
                                             

                   
 

                                         
                                                                                


                                                                                                              





                                                                                                                                                                
                 
                                                






                                                                                                                              



                                                                                
                                                                                
         
 

                                        
                                                                               
                                                    
                                       
                                                                       
                                                                                        
                 
                                                             





                                                                                     
                                                                                

         

                                    
                                                                           

                                                                                                                                                                  
                                                                       
                                                        
                 




                                                                                                                                  
                                                                
                                                     
                   
                                                                                                      

                                                                       
                                                                                                     
                                                                                       
                                                                               


                                                                                
                                              
                                                                    
                                               

                                                    
                 
                                                
         
 

                                         
                                                                         


                                                                                                                                                               
                                                        

                                                                              
                                                        




                                        
                                                                        
                                                       
                                       
                                                                       
                                                        
                 
                                                           





                                                                                     
                                                

         

                                    
                                                                    
                                                    
                                       
                                                                       
                                                                                        
                 


                                                                                        

                                                                                
                                                                                
         
 

                                        
                                       

                                                                                
                                                                                            
                                                                                                                    
                 

                                               
 

                                        
                                       
                                                                                
                                                

                                                  

                                               
 







                                                                                 

                                                               

                                                        


                                                      
                                                                                                  
                                                                                                       
                                
                                                                                                      
                                                                                                         


                                                                                          





                                                              

         
 
<?php

class Page_SysConfig extends Page
{

	/**
	 * Holds all the known configuration modules, with title, description, start class for their wizard, etc.
	 *
	 * @var array
	 */
	protected static $moduleTypes = array();

	/**
	 * @var int current locationid, 0 if global
	 */
	private $currentLoc;

	/**
	 * @var array Associative list of known locations
	 */
	private $locations;

	private $haveOverriddenLocations = false;

	protected function doPreprocess()
	{
		User::load();

		if (!User::isLoggedIn()) {
			Message::addError('main.no-permission');
			Util::redirect('?do=Main');
		}

		// Determine location we're editing
		if (!Module::isAvailable('locations')) {
			$this->locations = array();
			$this->currentLoc = 0;
		} else {
			$this->locations = Location::getLocationsAssoc();
			$this->currentLoc = Request::any('locationid', 0, 'int');
		}
		// Location valid?
		if ($this->currentLoc !== 0 && !isset($this->locations[$this->currentLoc])) {
			Message::addError('locations.invalid-location-id', $this->currentLoc);
			Util::redirect('?do=sysconfig');
		}

		// Action handling

		$action = Request::any('action', 'list');

		// Load all addmodule classes, as they populate the $moduleTypes array
		require_once Page::getModule()->getDir() . '/addmodule.inc.php';
		foreach (glob(Page::getModule()->getDir() . '/addmodule_*.inc.php') as $file) {
			require_once $file;
		}

		// Action: "addmodule" (upload new module)
		if ($action === 'addmodule') {
			User::assertPermission('module.edit');
			$this->initAddModule();
			AddModule_Base::preprocess();
		}

		if ($action === 'module') {
			// Action: "delmodule" (delete module)
			if (Request::post('del', 'no') !== 'no') {
				User::assertPermission('module.edit');
				$this->delModule();
			}
			if (Request::post('download', 'no') !== 'no') {
				User::assertPermission('module.download');
				$this->downloadModule();
			}
			if (Request::post('rebuild', 'no') !== 'no') {
				User::assertPermission('module.edit');
				$this->rebuildModule();
			}
		}

		// Action: "addconfig" (compose config from one or more modules)
		if ($action === 'addconfig') {
			User::assertPermission('config.edit');
			$this->initAddConfig();
			AddConfig_Base::preprocess();
		}

		if ($action === 'config') {
			// Action: "delconfig" (delete config)
			if (Request::post('del', 'no') !== 'no') {
				User::assertPermission('config.edit');
				$this->delConfig();
			}
			// Action "activate" (set sysconfig as active)
			if (Request::post('activate', 'no') !== 'no') {
				User::assertPermission('config.assign', $this->currentLoc);
				$this->activateConfig();
			}
			// Action "rebuild" (rebuild config.tgz from its modules)
			if (Request::post('rebuild', 'no') !== 'no') {
				User::assertPermission('config.edit');
				$this->rebuildConfig();
			}
		}
	}

	/**
	 * Render module; called by main script when this module page should render
	 * its content.
	 */
	protected function doRender()
	{

		Render::addTemplate('sysconfig_heading');

		$action = Request::any('action', 'list', 'string');
		switch ($action) {
		case 'addmodule':
			User::assertPermission('module.edit');
			AddModule_Base::render();
			return;
		case 'addconfig':
			User::assertPermission('config.edit');
			AddConfig_Base::render();
			return;
		case 'list':
			$pMods = User::hasPermission('module.view-list');
			$pConfs = User::hasPermission('config.view-list');
			if (!($pMods || $pConfs)) {
				User::assertPermission('config.view-list');
			}
			Render::openTag('div', array('class' => 'row'));
			if ($pConfs) {
				$this->listConfigs();
			}
			if ($this->currentLoc === 0 && $pMods) {
				$this->listModules();
			}
			Render::closeTag('div');
			if ($this->currentLoc === 0) {
				Render::addTemplate('list-legend', array('showLocationBadge' => $this->haveOverriddenLocations));
			}
			Render::addTemplate('js'); // Make this js snippet a template so i18n works
			return;
		case 'module':
			User::assertPermission('module.view-list');
			$listid = Request::post('list', Request::REQUIRED, 'int');
			$this->listModuleContents($listid);
			return;
		case 'config':
			User::assertPermission('config.view-list');
			$listid = Request::post('list', Request::REQUIRED, 'int');
			$this->listConfigContents($listid);
			return;
		default:
		}
		Message::addError('invalid-action', $action, 'main');
	}

	private function getLocationNames(array $locations, array $ids): string
	{
		$ret = array();
		foreach ($ids as $id) {
			settype($id, 'int');
			if (isset($locations[$id])) {
				$ret[] = $locations[$id]['locationname'];
			}
		}
		return implode(', ', $ret);
	}

	/**
	 * List all configurations and configuration modules.
	 */
	private function listConfigs()
	{
		// Configs
		$res = Database::simpleQuery("SELECT c.configid, c.title, c.filepath, c.status, c.dateline, c.warnings,
				GROUP_CONCAT(DISTINCT cl.locationid) AS loclist, GROUP_CONCAT(DISTINCT cxm.moduleid) AS modlist
			FROM configtgz c
			LEFT JOIN configtgz_x_module cxm USING (configid)
			LEFT JOIN configtgz_location cl ON (c.configid = cl.configid)
			GROUP BY configid, title
			ORDER BY title ASC");
		$configs = array();
		if ($this->currentLoc !== 0) {
			$locationName = $this->locations[$this->currentLoc]['locationname'];
		} else {
			$locationName = false;
		}
		$hasDefault = false;
		foreach ($res as $row) {
			if (is_null($row['loclist'])) {
				$locList = array();
			} else {
				$locList = explode(',', $row['loclist']);
			}
			$isDefault = in_array((string)$this->currentLoc, $locList, true);
			$hasDefault |= $isDefault;
			if ($this->currentLoc !== 0) {
				$locCount = 0;
			} else {
				$locCount = count($locList);
				if ($isDefault) {
					$locCount--;
				}
			}
			if ($locCount > 0) {
				$this->haveOverriddenLocations = true;
			}
			$configs[] = array(
				'warnings' => $row['warnings'],
				'warnings_hidden' => (!empty($row['warnings']) && $row['status'] === 'OK') ? '' : 'hidden',
				'configid' => $row['configid'],
				'config' => $row['title'],
				'modlist' => $row['modlist'],
				'current' => $isDefault,
				'loclist' => $row['loclist'],
				'readableLocList' => $this->getLocationNames($this->locations, $locList),
				'locationCount' => $locCount,
				'needrebuild' => ($row['status'] !== 'OK'),
				'dateline_s' => Util::prettyTime($row['dateline']),
			);
		}
		$data = array(
			'locationid' => $this->currentLoc,
			'locationname' => $locationName,
			'havelocations' => Module::isAvailable('locations'),
			'configs' => $configs,
			'inheritConfig' => !$hasDefault,
		);
		Permission::addGlobalTags($data['perms'], null, ['config.edit']);
		Permission::addGlobalTags($data['perms'], $this->currentLoc, ['config.assign']);
		Render::addTemplate('list-configs', $data);
	}

	private function listModules()
	{
		// Config modules
		$modules = ConfigModule::getAll() ?? [];
		$types = array_map(function ($mod) { return $mod->moduleType(); }, $modules);
		$titles = array_map(function ($mod) { return $mod->title(); }, $modules);
		array_multisort($types, SORT_ASC, $titles, SORT_ASC, $modules);
		$data = array(
			'modules' => $modules,
			'havemodules' => (count($modules) > 0)
		);
		Permission::addGlobalTags($data['perms'], null, ['module.edit', 'module.download']);
		Permission::addGlobalTags($data['perms'], null, ['config.edit']);
		Render::addTemplate('list-modules', $data);
	}

	private function listModuleContents($moduleid)
	{
		// fetch the data
		$row = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
		if ($row === false) {
			Message::addError('config-invalid', $moduleid);
			Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
		}

		// find files in that archive
		$status = Taskmanager::submit('ListArchive', array(
			'file' => $row['filepath']
		));
		if (isset($status['id']))
			$status = Taskmanager::waitComplete($status, 4000);
		if (!Taskmanager::isFinished($status) || Taskmanager::isFailed($status)) {
			Taskmanager::addErrorMessage($status);
			Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
		}
		$list = SysConfig::archiveContentsFromTask($status);

		// render the template
		Render::addDialog(Dictionary::translate('lang_contentOf') . ' ' . $row['title'], false, 'custom-filelist', array(
			'files' => $list,
		));
	}

	private function listConfigContents($configid)
	{
		// get config name
		$config = Database::queryFirst("SELECT title FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid));
		if ($config === false) {
			Message::addError('config-invalid', $configid);
			Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
		}
		// fetch the data
		$res = Database::simpleQuery("SELECT module.moduleid, module.title AS moduletitle"
			. " FROM configtgz_module module"
			. " INNER JOIN configtgz_x_module USING (moduleid)"
			. " WHERE configtgz_x_module.configid = :configid"
			. " ORDER BY module.title ASC", array('configid' => $configid));

		$modules = array();
		foreach ($res as $row) {
			$modules[] = array(
				'module' => $row['moduletitle'],
				'moduleid' => $row['moduleid']
			);
		}

		// render the template
		Render::addDialog(Dictionary::translate('lang_contentOf') . ' ' . $config['title'], false, 'config-module-list', array(
			'modules' => $modules
		));
	}

	private function activateConfig()
	{
		$configid = Request::post('activate', Request::REQUIRED, 'int');
		// Validate that either the configid is valid (in case we override for a specific location)
		// or that if the locationid is 0 (=global) that the configid exists, because it's not allowed
		// to unset the global config
		if ($this->currentLoc === 0 || $configid !== 0) {
			$row = Database::queryFirst("SELECT title, filepath FROM configtgz WHERE configid = :configid LIMIT 1", array('configid' => $configid));
			if ($row === false) {
				Message::addError('config-invalid', $configid);
				Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
			}
		}
		$locationid = $this->currentLoc;
		if ($configid === 0) {
			Database::exec("DELETE FROM configtgz_location WHERE locationid = :locationid",
				compact('locationid'));
		} else {
			Database::exec("INSERT INTO configtgz_location (locationid, configid) VALUES (:locationid, :configid)"
				. " ON DUPLICATE KEY UPDATE configid = :configid", compact('locationid', 'configid'));
		}
		$task = ConfigModuleBaseLdap::ldadp();
		if ($task !== false) {
			TaskmanagerCallback::addCallback($task, 'ldadpStartup');
		}
		Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
	}

	private function rebuildConfig()
	{
		$configid = Request::post('rebuild', Request::REQUIRED, 'int');
		$config = ConfigTgz::get($configid);
		if ($config === null) {
			Message::addError('config-invalid', $configid);
			Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
		}
		$ret = $config->generate(false, 500); // TODO
		if ($ret === true)
			Message::addSuccess('module-rebuilt', $config->title());
		elseif ($ret === false)
			Message::addError('module-rebuild-failed', $config->title());
		else
			Message::addInfo('module-rebuilding', $config->title());
		Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
	}

	private function delModule()
	{
		$moduleid = Request::post('del', Request::REQUIRED, 'int');
		$module = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
		if ($module === false) {
			Message::addError('config-invalid', $moduleid);
			Util::redirect('?do=sysconfig');
		}
		// Get config.tgz using this module *before* deleting it
		$existing = Database::simpleQuery("SELECT configid FROM configtgz_x_module
			WHERE moduleid = :moduleid", array('moduleid' => $moduleid));
		// Delete DB entries and file
		Database::exec("DELETE FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
		$task = Taskmanager::submit('DeleteFile', array(
			'file' => $module['filepath']
		));
		if (isset($task['statusCode']) && $task['statusCode'] === Taskmanager::TASK_WAITING) {
			$task = Taskmanager::waitComplete($task['id']);
		}
		if (!isset($task['statusCode']) || $task['statusCode'] === Taskmanager::TASK_ERROR) {
			Message::addWarning('main.task-error', $task['data']['error']);
		} elseif ($task['statusCode'] === Taskmanager::TASK_FINISHED) {
			Message::addSuccess('module-deleted', $module['title']);
		}
		// Rebuild depending config.tgz
		foreach ($existing as $crow) {
			$config = ConfigTgz::get($crow['configid']);
			if ($config !== null) {
				$config->generate();
			}
		}
		Util::redirect('?do=sysconfig');
	}

	private function downloadModule()
	{
		$moduleid = Request::post('download', Request::REQUIRED);
		$row = Database::queryFirst("SELECT title, filepath FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array('moduleid' => $moduleid));
		if ($row === false) {
			Message::addError('config-invalid', $moduleid);
			Util::redirect('?do=sysconfig');
		}
		if (!Util::sendFile($row['filepath'], $row['title'] . '.tgz'))
			Util::redirect('?do=sysconfig');
		exit(0);
	}

	private function rebuildModule()
	{
		$moduleid = Request::post('rebuild', Request::REQUIRED);
		$module = ConfigModule::get($moduleid);
		if ($module === null) {
			Message::addError('config-invalid', $moduleid);
			Util::redirect('?do=sysconfig');
		}
		$ret = $module->generate(false, null, 500);
		if ($ret === true)
			Message::addSuccess('module-rebuilt', $module->title());
		elseif ($ret === false)
			Message::addError('module-rebuild-failed', $module->title());
		else
			Message::addInfo('module-rebuilding', $module->title());
		Util::redirect('?do=sysconfig');
	}

	private function delConfig()
	{
		$configid = Request::post('del', Request::REQUIRED);
		$config = ConfigTgz::get($configid);
		if ($config === null) {
			Message::addError('config-invalid', $configid);
			Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
		}
		if ($config->delete() === false) {
			Message::addError('config-delete-error', Database::lastError());
		} else {
			Message::addSuccess('config-deleted', $config->title());
		}
		Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc);
	}

	private function initAddModule()
	{
		ConfigModule::loadDb();
		require_once Page::getModule()->getDir() . '/addmodule.inc.php';
		$step = Request::any('step', 'AddModule_Start', 'string');
		if (!class_exists($step) && preg_match('/^([a-zA-Z0-9]+)_/', $step, $out)) {
			require_once Page::getModule()->getDir() . '/addmodule_' . strtolower($out[1]) . '.inc.php';
		}
		AddModule_Base::setStep($step);
	}

	private function initAddConfig()
	{
		ConfigModule::loadDb();
		require_once Page::getModule()->getDir() . '/addconfig.inc.php';
		$step = Request::any('step', 0);
		if ($step === 0)
			$step = 'AddConfig_Start';
		AddConfig_Base::setStep($step);
	}

	/**
	 * If modules need updates (blue refresh buttons), we query their state
	 * via ajax, in case they are about to generate. This happens for example
	 * if you edit a module and a bunch of configs depend on it and will be
	 * rebuilt.
	 */
	protected function doAjax()
	{
		$action = Request::any('action', '', 'string');
		if ($action === 'status') {
			$mods = Request::post('mods');
			$confs = Request::post('confs');
			$mods = explode(',', $mods);
			$confs = explode(',', $confs);
			// Mods
			$outMods = Database::queryAll("SELECT moduleid AS id FROM configtgz_module
					WHERE moduleid in (:mods) AND status = 'OK'", compact('mods'));
			// Confs
			$outConfs = Database::queryAll("SELECT configid AS id, warnings FROM configtgz
					WHERE configid in (:confs) AND status = 'OK'", compact('confs'));
			Header('Content-Type: application/json');
			die(json_encode(array('mods' => $outMods, 'confs' => $outConfs)));
		}
		if ($action === 'addmodule') {
			User::load();
			User::assertPermission('module.edit');
			$this->initAddModule();
			AddModule_Base::ajax();
		}
	}

}