Pārlūkot izejas kodu

YOU MUST RUN THE SETUP AFTER PERFORMING THIS UPDATE !!
- Better handling of 'auto_select' modules
- New way of implementing the "includes" of modules, now completely out of the configuration file !

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@3989 a333f486-631f-4898-b8df-5754b55c2be0

dflaven 9 gadi atpakaļ
vecāks
revīzija
4ab7616cf5

+ 37 - 0
core/autoload.php

@@ -0,0 +1,37 @@
+<?php
+// Copyright (C) 2016 Combodo SARL
+//
+//   This file is part of iTop.
+//
+//   iTop is free software; you can redistribute it and/or modify	
+//   it under the terms of the GNU Affero General Public License as published by
+//   the Free Software Foundation, either version 3 of the License, or
+//   (at your option) any later version.
+//
+//   iTop is distributed in the hope that it will be useful,
+//   but WITHOUT ANY WARRANTY; without even the implied warranty of
+//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//   GNU Affero General Public License for more details.
+//
+//   You should have received a copy of the GNU Affero General Public License
+//   along with iTop. If not, see <http://www.gnu.org/licenses/>
+
+MetaModel::IncludeModule('application/transaction.class.inc.php');
+MetaModel::IncludeModule('application/menunode.class.inc.php');
+MetaModel::IncludeModule('application/user.preferences.class.inc.php');
+MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
+MetaModel::IncludeModule('application/audit.rule.class.inc.php');
+MetaModel::IncludeModule('application/query.class.inc.php');
+
+MetaModel::IncludeModule('core/event.class.inc.php');
+MetaModel::IncludeModule('core/action.class.inc.php');
+MetaModel::IncludeModule('core/trigger.class.inc.php');
+MetaModel::IncludeModule('core/bulkexport.class.inc.php');
+MetaModel::IncludeModule('core/ownershiplock.class.inc.php');
+MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
+MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
+MetaModel::IncludeModule('core/inlineimage.class.inc.php');
+
+MetaModel::IncludeModule('webservices/webservices.basic.php');
+
+//MetaModel::IncludeModule('addons', 'user rights', 'addons/userrights/userrightsprofile.class.inc.php');

+ 4 - 126
core/config.class.inc.php

@@ -1022,32 +1022,6 @@ class Config
 			$bLoadConfig = false;
 		}
 
-		$this->m_aAppModules = array(
-			// Some default modules, always present can be move to an official iTop Module later if needed
-			'application/transaction.class.inc.php',
-			'application/menunode.class.inc.php',
-			'application/user.preferences.class.inc.php',
-			'application/user.dashboard.class.inc.php',
-			'application/audit.rule.class.inc.php',
-			'application/query.class.inc.php',
-// Romain - That's dirty, because those classes are in fact part of the core
-//          but I needed those classes to be derived from cmdbAbstractObject
-//          (to be managed via the GUI) and this class in not really known from
-//          the core, PLUS I needed the includes to be there also for the setup
-//          to create the tables.
-			'core/event.class.inc.php',
-			'core/action.class.inc.php',
-			'core/trigger.class.inc.php',
-			'core/bulkexport.class.inc.php',
-			'core/ownershiplock.class.inc.php',
-			'synchro/synchrodatasource.class.inc.php',
-			'core/backgroundtask.class.inc.php',
-			'core/inlineimage.class.inc.php',
-		);
-		$this->m_aDataModels = array();
-		$this->m_aWebServiceCategories = array(
-			'webservices/webservices.basic.php',
-		);
 		$this->m_aAddons = array(
 			// Default AddOn, always present can be moved to an official iTop Module later if needed
 			'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
@@ -1150,18 +1124,7 @@ class Config
 		{
 			throw new ConfigException('Missing array in configuration file', array('file' => $sConfigFile, 'expected' => '$MySettings'));
 		}
-		if (!isset($MyModules) || !is_array($MyModules))
-		{
-			throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules'));
-		}
-		if (!array_key_exists('application', $MyModules))
-		{
-			throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'application\']'));
-		}
-		if (!array_key_exists('business', $MyModules))
-		{
-			throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'business\']'));
-		}
+
 		if (!array_key_exists('addons', $MyModules))
 		{
 			throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'addons\']'));
@@ -1172,12 +1135,6 @@ class Config
 			$MyModules['addons']['user rights'] = '/addons/userrights/userrightsnull.class.inc.php';
 		}
 
-		$this->m_aAppModules = $MyModules['application'];
-		$this->m_aDataModels = $MyModules['business'];
-		if (isset($MyModules['webservices']))
-		{
-			$this->m_aWebServiceCategories = $MyModules['webservices'];
-		}
 		$this->m_aAddons = $MyModules['addons'];
 
 		foreach($MySettings as $sPropCode => $rawvalue)
@@ -1262,33 +1219,6 @@ class Config
 		$this->m_aModuleSettings[$sModule][$sProperty] = $value;
 	}
 
-	public function GetAppModules()
-	{
-		return $this->m_aAppModules;
-	}
-	public function SetAppModules($aAppModules)
-	{
-		$this->m_aAppModules = $aAppModules;
-	}
-
-	public function GetDataModels()
-	{
-		return $this->m_aDataModels;
-	}
-	public function SetDataModels($aDataModels)
-	{
-		$this->m_aDataModels = $aDataModels;
-	}
-
-	public function GetWebServiceCategories()
-	{
-		return $this->m_aWebServiceCategories;
-	}
-	public function SetWebServiceCategories($aWebServiceCategories)
-	{
-		$this->m_aWebServiceCategories = $aWebServiceCategories;
-	}
-
 	public function GetAddons()
 	{
 		return $this->m_aAddons;
@@ -1577,18 +1507,6 @@ class Config
 				$aSettings['module_settings'][$sModule][$sProperty] = $value;
 			}
 		}
-		foreach($this->m_aAppModules as $sFile)
-		{
-			$aSettings['application_list'][] = $sFile;
-		}
-		foreach($this->m_aDataModels as $sFile)
-		{
-			$aSettings['datamodel_list'][] = $sFile;
-		}
-		foreach($this->m_aWebServiceCategories as $sFile)
-		{
-			$aSettings['webservice_list'][] = $sFile;
-		}
 		foreach($this->m_aAddons as $sKey => $sFile)
 		{
 			$aSettings['addon_list'][] = $sFile;
@@ -1737,24 +1655,6 @@ class Config
 			fwrite($hFile, " *\n");
 			fwrite($hFile, " */\n");
 			fwrite($hFile, "\$MyModules = array(\n");
-			fwrite($hFile, "\t'application' => array (\n");
-			foreach($this->m_aAppModules as $sFile)
-			{
-				fwrite($hFile, "\t\t'$sFile',\n");
-			}
-			fwrite($hFile, "\t),\n");
-			fwrite($hFile, "\t'business' => array (\n");
-			foreach($this->m_aDataModels as $sFile)
-			{
-				fwrite($hFile, "\t\t'$sFile',\n");
-			}
-			fwrite($hFile, "\t),\n");
-			fwrite($hFile, "\t'webservices' => array (\n");
-			foreach($this->m_aWebServiceCategories as $sFile)
-			{
-				fwrite($hFile, "\t\t'$sFile',\n");
-			}
-			fwrite($hFile, "\t),\n");
 			fwrite($hFile, "\t'addons' => array (\n");
 			foreach($this->m_aAddons as $sKey => $sFile)
 			{
@@ -1830,15 +1730,6 @@ class Config
 			// Initialize the arrays below with default values for the application...
 			$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
 			$aAddOns = $oEmptyConfig->GetAddOns();
-			$aAppModules = $oEmptyConfig->GetAppModules();
-			if (file_exists(APPROOT.$sModulesDir.'/core/main.php'))
-			{
-				$aAppModules[] = $sModulesDir.'/core/main.php';
-			}
-			$aDataModels = $oEmptyConfig->GetDataModels();
-			$aWebServiceCategories = $oEmptyConfig->GetWebServiceCategories();
-			// Merge the values with the ones provided by the modules
-			// Make sure when don't load the same file twice...
 			
 			$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
 			foreach ($aModules as $sModuleId => $aModuleInfo)
@@ -1846,14 +1737,6 @@ class Config
 				list ($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
 				if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
 				{
-					if (isset($aModuleInfo['datamodel']))
-					{
-						$aDataModels = array_unique(array_merge($aDataModels, $aModuleInfo['datamodel']));
-					}
-					if (isset($aModuleInfo['webservice']))
-					{
-						$aWebServiceCategories = array_unique(array_merge($aWebServiceCategories, $aModuleInfo['webservice']));
-					}
 					if (isset($aModuleInfo['settings']))
 					{
 						list ($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
@@ -1886,9 +1769,6 @@ class Config
 				}
 			}
 			$this->SetAddOns($aAddOns);
-			$this->SetAppModules($aAppModules);
-			$this->SetDataModels($aDataModels);
-			$this->SetWebServiceCategories($aWebServiceCategories);
 		}
 	}
 
@@ -1907,14 +1787,12 @@ class Config
 	}
 
 	/**
-	 * Quick an dirty way to clone a config file into another environment	
+	 * Obsolete: kept only for backward compatibility of the Toolkit
+     * Quick and dirty way to clone a config file into another environment	
 	 */	
 	public function ChangeModulesPath($sSourceEnv, $sTargetEnv)
 	{
-		$sSearchPrefix = 'env-'.$sSourceEnv.'/';
-		$sNewPrefix = 'env-'.$sTargetEnv.'/';
-		self::ChangePrefix($this->m_aDataModels, $sSearchPrefix, $sNewPrefix);
-		self::ChangePrefix($this->m_aWebServiceCategories, $sSearchPrefix, $sNewPrefix);
+		// Now does nothing since the includes are built into the environment itself
 	}
 	
 	/**

+ 17 - 19
core/metamodel.class.php

@@ -4232,21 +4232,12 @@ abstract class MetaModel
 		//         classes have to be derived from cmdbabstract (to be editable in the UI)
 		require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
 
-		foreach (self::$m_oConfig->GetAppModules() as $sModule => $sToInclude)
-		{
-			self::IncludeModule('application', $sToInclude);
-		}
-		foreach (self::$m_oConfig->GetDataModels() as $sModule => $sToInclude)
-		{
-			self::IncludeModule('business', $sToInclude);
-		}
-		foreach (self::$m_oConfig->GetWebServiceCategories() as $sModule => $sToInclude)
-		{
-			self::IncludeModule('webservice', $sToInclude);
-		}
+		require_once(APPROOT.'core/autoload.php');
+		require_once(APPROOT.'env-'.utils::GetCurrentEnvironment().'/autoload.php');
+
 		foreach (self::$m_oConfig->GetAddons() as $sModule => $sToInclude)
 		{
-			self::IncludeModule('addons', $sToInclude);
+			self::IncludeModule($sToInclude, 'addons');
 		}
 
 		$sServer = self::$m_oConfig->GetDBHost();
@@ -4370,7 +4361,7 @@ abstract class MetaModel
 
 	protected static $m_aExtensionClasses = array();
 
-	protected static function IncludeModule($sModuleType, $sToInclude)
+	protected static function IncludeModule($sToInclude, $sModuleType = null)
 	{
 		$sFirstChar = substr($sToInclude, 0, 1);
 		$sSecondChar = substr($sToInclude, 1, 1);
@@ -4396,14 +4387,21 @@ abstract class MetaModel
 		if (!file_exists($sFile))
 		{
 			$sConfigFile = self::$m_oConfig->GetLoadedFile();
-			if (strlen($sConfigFile) > 0)
+			if ($sModuleType == null)
 			{
-				throw new CoreException('Include: wrong file name in configuration file', array('config file' => $sConfigFile, 'section' => $sModuleType, 'filename' => $sFile));
+					throw new CoreException("Include: unable to load the file '$sFile'");			
 			}
 			else
 			{
-				// The configuration is in memory only
-				throw new CoreException('Include: wrong file name in configuration file (in memory)', array('section' => $sModuleType, 'filename' => $sFile));
+				if (strlen($sConfigFile) > 0)
+				{
+					throw new CoreException('Include: wrong file name in configuration file', array('config file' => $sConfigFile, 'section' => $sModuleType, 'filename' => $sFile));
+				}
+				else
+				{
+					// The configuration is in memory only
+					throw new CoreException('Include: wrong file name in configuration file (in memory)', array('section' => $sModuleType, 'filename' => $sFile));
+				}
 			}
 		}
 
@@ -4419,7 +4417,7 @@ abstract class MetaModel
 		{
 			if ($sPreviousContent != '')
 			{
-				IssueLog::Error("Spurious characters injected by $sModuleType/$sToInclude");
+				IssueLog::Error("Spurious characters injected by '$sFile'");
 			}
 		}
 	}

+ 32 - 1
setup/compiler.class.inc.php

@@ -181,6 +181,8 @@ class MFCompiler
 		// Compile, module by module
 		//
 		$aModules = $this->oFactory->GetLoadedModules();
+		$aDataModelFiles = array();
+		$aWebservicesFiles = array();
 		foreach($aModules as $foo => $oModule)
 		{
 			$sModuleName = $oModule->GetName();
@@ -412,8 +414,19 @@ EOF;
 			{
 					$this->Log("Compilation of module $sModuleName in version $sModuleVersion produced not code at all. No file written.");
 			}
+			
+			// files to include (PHP datamodels)
+			foreach($oModule->GetFilesToInclude('business') as $sRelFileName)
+			{
+				$aDataModelFiles[] = "MetaModel::IncludeModule('".basename($sFinalTargetDir).'/'.$sRelativeDir.'/'.$sRelFileName."');";
+			}
+			// files to include (PHP webservices providers)
+			foreach($oModule->GetFilesToInclude('webservices') as $sRelFileName)
+			{
+				$aWebservicesFiles[] = "MetaModel::IncludeModule('".basename($sFinalTargetDir).'/'.$sRelativeDir.'/'.$sRelFileName."');";
+			}
 		} // foreach module
-
+		
 		// Compile the dictionaries -out of the modules
 		//
 		$sDictDir = $sTempTargetDir.'/dictionaries';
@@ -484,6 +497,24 @@ EOF;
 		SetupUtils::builddir($sTempTargetDir.'/core');
 		$sPHPFile = $sTempTargetDir.'/core/main.php';
 		file_put_contents($sPHPFile, $this->sMainPHPCode);
+	
+
+		// Autoload
+		$sPHPFile = $sTempTargetDir.'/autoload.php';
+		$sPHPFileContent = 
+<<<EOF
+<?php
+//
+// File generated on $sCurrDate
+// Please do not edit manually
+//
+EOF
+		;
+		
+		$sPHPFileContent .= "\nMetaModel::IncludeModule('".basename($sFinalTargetDir)."/core/main.php');\n";
+		$sPHPFileContent .= implode("\n", $aDataModelFiles);
+		$sPHPFileContent .= implode("\n", $aWebservicesFiles);
+		file_put_contents($sPHPFile, $sPHPFileContent);
 		
 	} // DoCompile()
 

+ 60 - 2
setup/modelfactory.class.inc.php

@@ -40,8 +40,11 @@ class MFModule
 	protected $sRootDir;
 	protected $sLabel;
 	protected $aDataModels;
+	protected $bAutoSelect;
+	protected $sAutoSelect;
+	protected $aFilesToInclude;
 	
-	public function __construct($sId, $sRootDir, $sLabel)
+	public function __construct($sId, $sRootDir, $sLabel, $bAutoSelect = false)
 	{
 		$this->sId = $sId;	
 		
@@ -54,6 +57,9 @@ class MFModule
 		$this->sRootDir = $sRootDir;
 		$this->sLabel = $sLabel;
 		$this->aDataModels = array();
+		$this->bAutoSelect = $bAutoSelect;
+		$this->sAutoSelect = 'false';
+		$this->aFilesToInclude = array('addons' => array(), 'business' => array(), 'webservices' => array(),);
 	
 		// Scan the module's root directory to find the datamodel(*).xml files
 		if ($hDir = opendir($sRootDir))
@@ -131,6 +137,38 @@ class MFModule
 		}
 		return $aDictionaries;		
 	}
+	
+	public function IsAutoSelect()
+	{
+		return $this->bAutoSelect;
+	}
+	
+	public function SetAutoSelect($sAutoSelect)
+	{
+		$this->sAutoSelect = $sAutoSelect;
+	}
+
+	public function GetAutoSelect()
+	{
+		return $this->sAutoSelect;
+	}
+	
+	public function SetFilesToInclude($aFiles, $sCategory)
+	{
+		$sDir = basename($this->sRootDir);
+		$iLen = strlen($sDir.'/');		
+		foreach($aFiles as $sFile)
+		{
+			$iPos = strpos($sFile, $sDir.'/');
+			$this->aFilesToInclude[$sCategory][] = substr($sFile, $iPos+$iLen);
+		}
+	}
+	
+	public function GetFilesToInclude($sCategory)
+	{
+		return $this->aFilesToInclude[$sCategory];
+	}
+	
 }
 
  /**
@@ -149,6 +187,7 @@ class MFDeltaModule extends MFModule
 		$this->sRootDir = '';
 		$this->sLabel = 'Additional Delta';
 		$this->aDataModels = array($sDeltaFile);
+		$this->aFilesToInclude = array('addons' => array(), 'business' => array(), 'webservices' => array(),);
 	}
 
 	public function GetName()
@@ -188,6 +227,7 @@ class MFCoreModule extends MFModule
 		$this->sRootDir = '';
 		$this->sLabel = $sLabel;
 		$this->aDataModels = array($sDeltaFile);
+		$this->aFilesToInclude = array('addons' => array(), 'business' => array(), 'webservices' => array(),);
 	}
 	
 	public function GetRootDir()
@@ -222,6 +262,7 @@ class MFDictModule extends MFModule
 		$this->sRootDir = $sRootDir;
 		$this->sLabel = $sLabel;
 		$this->aDataModels = array();
+		$this->aFilesToInclude = array('addons' => array(), 'business' => array(), 'webservices' => array(),);
 	}
 
 	public function GetRootDir()
@@ -1169,7 +1210,24 @@ EOF
 		$aResult = array();
 		foreach($aAvailableModules as $sId => $aModule)
 		{
-			$aResult[] = new MFModule($sId, $aModule['root_dir'], $aModule['label']);
+			$oModule = new MFModule($sId, $aModule['root_dir'], $aModule['label'], isset($aModule['auto_select']));
+			if (isset($aModule['auto_select']))
+			{
+				$oModule->SetAutoSelect($aModule['auto_select']);
+			}
+			if (isset($aModule['datamodel']) && is_array($aModule['datamodel']))
+			{
+				$oModule->SetFilesToInclude($aModule['datamodel'], 'business');
+			}
+			if (isset($aModule['webservice']) && is_array($aModule['webservice']))
+			{
+				$oModule->SetFilesToInclude($aModule['webservice'], 'webservices');
+			}
+			if (isset($aModule['addons']) && is_array($aModule['addons']))
+			{
+				$oModule->SetFilesToInclude($aModule['addons'], 'addons');
+			}
+			$aResult[] = $oModule;
 		}
 		return $aResult;
 	}

+ 36 - 7
setup/runtimeenv.class.inc.php

@@ -342,20 +342,20 @@ class RunTimeEnvironment
 		// Do load the required modules
 		//
 		$oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries');
-		$aRet[] = $oDictModule;
+		$aRet[$oDictModule->GetName()] = $oDictModule;
 		
 		$oFactory = new ModelFactory($aDirsToCompile);
 		$sDeltaFile = APPROOT.'core/datamodel.core.xml';
 		if (file_exists($sDeltaFile))
 		{
 			$oCoreModule = new MFCoreModule('core', 'Core Module', $sDeltaFile);
-			$aRet[] = $oCoreModule;
+			$aRet[$oCoreModule->GetName()] = $oCoreModule;
 		}
 		$sDeltaFile = APPROOT.'application/datamodel.application.xml';
 		if (file_exists($sDeltaFile))
 		{
 			$oApplicationModule = new MFCoreModule('application', 'Application Module', $sDeltaFile);
-			$aRet[] = $oApplicationModule;
+			$aRet[$oApplicationModule->GetName()] = $oApplicationModule;
 		}
 		
 		$aModules = $oFactory->FindModules();
@@ -366,18 +366,47 @@ class RunTimeEnvironment
 			$bIsExtra = (strpos($sModuleRootDir, $sExtraDir) !== false);
 			if (array_key_exists($sModule, $aAvailableModules)) 
 			{
-				if (($aAvailableModules[$sModule]['version_db'] != '') ||  $bIsExtra) //Extra modules are always selected
+				if (($aAvailableModules[$sModule]['version_db'] != '') ||  $bIsExtra && !$oModule->IsAutoSelect()) //Extra modules are always unless they are 'AutoSelect'
 				{
-					$aRet[] = $oModule;
+					$aRet[$oModule->GetName()] = $oModule;
 				}
 			}
 		}
-
+		
+		// Now process the 'AutoSelect' modules
+		do
+		{
+			// Loop while new modules are added...
+			$bModuleAdded = false;
+			foreach($aModules as $foo => $oModule)
+			{
+				if (!array_key_exists($oModule->GetName(), $aRet) && $oModule->IsAutoSelect())
+				{
+					try
+					{
+						$bSelected = false;
+						SetupInfo::SetSelectedModules($aRet);
+						eval('$bSelected = ('.$oModule->GetAutoSelect().');');
+					}
+					catch(Exception $e)
+					{
+						$bSelected = false;
+					}
+					if ($bSelected)
+					{
+						$aRet[$oModule->GetName()] = $oModule; // store the Id of the selected module
+						$bModuleAdded  = true;
+					}
+				}
+			}
+		}
+		while($bModuleAdded);
+		
 		$sDeltaFile = APPROOT.'data/'.$this->sTargetEnv.'.delta.xml';
 		if (file_exists($sDeltaFile))
 		{
 			$oDelta = new MFDeltaModule($sDeltaFile);
-			$aRet[] = $oDelta;
+			$aRet[$oDelta->GetName()] = $oDelta;
 		}
 
 		return $aRet;

+ 5 - 4
setup/wizardsteps.class.inc.php

@@ -1483,15 +1483,15 @@ EOF
 			// Check once (before recursing) that the hidden modules are selected
 			foreach(SetupUtils::AnalyzeInstallation($this->oWizard) as $sModuleId => $aModule)
 			{
-				if ($sModuleId != ROOT_MODULE)
+				if (($sModuleId != ROOT_MODULE) && !isset($aModules[$sModuleId]))
 				{
-					if (($aModule['category'] == 'authentication') || (!$aModule['visible']))
+					if (($aModule['category'] == 'authentication') || (!$aModule['visible'] && !isset($aModule['auto_select'])))
 					{
 						$aModules[$sModuleId] = true;
+						$sDisplayChoices .= '<li><i>'.$aModule['label'].' (hidden)</i></li>';
 					}
 				}
 			}
-			
 		}
 		$aOptions = isset($aInfo['options']) ? $aInfo['options'] : array();
 		foreach($aOptions as $index => $aChoice)
@@ -1578,6 +1578,7 @@ EOF
 						if ($bSelected)
 						{
 							$aModules[$sModuleId] = true; // store the Id of the selected module
+							$sDisplayChoices .= '<li><b>'.$aModule['label'].' (auto_select)</b></li>';
 							$bModuleAdded  = true;
 						}
 					}
@@ -1659,7 +1660,7 @@ EOF
 			$sModuleLabel = $aModule['label'];
 			$sModuleHelp = $aModule['doc.more_information'];
 			$sMoreInfo = (!empty($aModule['doc.more_information'])) ? "<a href=\"$sDefaultAppPath{$aModule['doc.more_information']}\" target=\"_blank\">more info</a>": '';
-			if (($aModule['category'] != 'authentication') && ($aModule['visible']))
+			if (($aModule['category'] != 'authentication') && ($aModule['visible'] && !isset($aModule['auto_select'])))
 			{
 				if (($bAddExtensionsOnly) && (!$this->IsExtension($aModule))) continue;