mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-08 02:24:12 +01:00
Modularization of the portal. The entry points for portals is now defined in XML, and thus can be altered by an extension.
SVN:trunk[3509]
This commit is contained in:
22
application/datamodel.application.xml
Normal file
22
application/datamodel.application.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design>
|
||||
<portals>
|
||||
<portal id="legacy_portal" _delta="define">
|
||||
<url>portal/index.php</url>
|
||||
<rank>1.0</rank>
|
||||
<handler/>
|
||||
<allow>
|
||||
</allow>
|
||||
<deny/>
|
||||
</portal>
|
||||
<portal id="backoffice" _delta="define">
|
||||
<url>pages/UI.php</url>
|
||||
<rank>2.0</rank>
|
||||
<handler/>
|
||||
<allow/>
|
||||
<deny>
|
||||
<profile id="Portal user"/>
|
||||
</deny>
|
||||
</portal>
|
||||
</portals>
|
||||
</itop_design>
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
||||
require_once(APPROOT.'/application/portaldispatcher.class.inc.php');
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
*/
|
||||
@@ -428,6 +429,7 @@ EOF
|
||||
// Unset all of the session variables.
|
||||
unset($_SESSION['auth_user']);
|
||||
unset($_SESSION['login_mode']);
|
||||
unset($_SESSION['profile_list']);
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
}
|
||||
@@ -654,12 +656,22 @@ EOF
|
||||
|
||||
/**
|
||||
* Overridable: depending on the user, head toward a dedicated portal
|
||||
* @param bool $bIsAllowedToPortalUsers Whether or not the current page is considered as part of the portal
|
||||
* @param string|null $sRequestedPortalId
|
||||
* @param int $iOnExit How to complete the call: redirect or return a code
|
||||
*/
|
||||
protected static function ChangeLocation($bIsAllowedToPortalUsers, $iOnExit = self::EXIT_PROMPT)
|
||||
protected static function ChangeLocation($sRequestedPortalId = null, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
if ( (!$bIsAllowedToPortalUsers) && (UserRights::IsPortalUser()))
|
||||
$fStart = microtime(true);
|
||||
$ret = call_user_func(array(self::$sHandlerClass, 'Dispatch'), $sRequestedPortalId);
|
||||
if ($ret === true)
|
||||
{
|
||||
return self::EXIT_CODE_OK;
|
||||
}
|
||||
else if($ret === false)
|
||||
{
|
||||
throw new Exception('Nowhere to go??');
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
@@ -668,16 +680,11 @@ EOF
|
||||
else
|
||||
{
|
||||
// No rights to be here, redirect to the portal
|
||||
header('Location: '.utils::GetAbsoluteUrlAppRoot().'portal/index.php');
|
||||
header('Location: '.$ret);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::EXIT_CODE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check if the user is already authentified, if yes, then performs some additional validations:
|
||||
* - if $bMustBeAdmin is true, then the user must be an administrator, otherwise an error is displayed
|
||||
@@ -688,9 +695,56 @@ EOF
|
||||
*/
|
||||
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
$sMessage = ''; // In case we need to return a message to the calling web page
|
||||
$sRequestedPortalId = $bIsAllowedToPortalUsers ? 'legacy_portal' : 'backoffice';
|
||||
return self::DoLoginEx($sRequestedPortalId, $bMustBeAdmin, $iOnExit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is already authentified, if yes, then performs some additional validations to redirect towards the desired "portal"
|
||||
* @param string|null $sRequestedPortalId The requested "portal" interface, null for any
|
||||
* @param bool $bMustBeAdmin Whether or not the user must be an admin to access the current page
|
||||
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
|
||||
*/
|
||||
static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
$operation = utils::ReadParam('loginop', '');
|
||||
|
||||
|
||||
$sMessage = self::HandleOperations($operation); // May exit directly
|
||||
|
||||
$iRet = self::Login($iOnExit);
|
||||
|
||||
if ($iRet == self::EXIT_CODE_OK)
|
||||
{
|
||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||
{
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return self::EXIT_CODE_MUSTBEADMIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
|
||||
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
|
||||
$oP->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
|
||||
}
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return $iRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sMessage;
|
||||
}
|
||||
}
|
||||
protected static function HandleOperations($operation)
|
||||
{
|
||||
$sMessage = ''; // most of the operations never return, but some can return a message to be displayed
|
||||
if ($operation == 'logoff')
|
||||
{
|
||||
if (isset($_SESSION['login_mode']))
|
||||
@@ -714,7 +768,7 @@ EOF
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else if ($operation == 'forgot_pwd')
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
@@ -767,36 +821,54 @@ EOF
|
||||
}
|
||||
$sMessage = Dict::S('UI:Login:PasswordChanged');
|
||||
}
|
||||
return $sMessage;
|
||||
}
|
||||
|
||||
protected static function Dispatch($sRequestedPortalId)
|
||||
{
|
||||
if ($sRequestedPortalId === null) return true; // allowed to any portal => return true
|
||||
|
||||
$iRet = self::Login($iOnExit);
|
||||
|
||||
if ($iRet == self::EXIT_CODE_OK)
|
||||
$aPortalsConf = PortalDispatcherData::GetData();
|
||||
$aDispatchers = array();
|
||||
foreach($aPortalsConf as $sPortalId => $aConf)
|
||||
{
|
||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||
$sHandlerClass = $aConf['handler'];
|
||||
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
|
||||
}
|
||||
|
||||
if (array_key_exists($sRequestedPortalId, $aDispatchers) && $aDispatchers[$sRequestedPortalId]->IsUserAllowed())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
foreach($aDispatchers as $sPortalId => $oDispatcher)
|
||||
{
|
||||
if ($oDispatcher->IsUserAllowed()) return $oDispatcher->GetUrl();
|
||||
}
|
||||
return false; // nothing matched !!
|
||||
}
|
||||
|
||||
public static function GetAllowedPortals()
|
||||
{
|
||||
$aAllowedPortals = array();
|
||||
$aPortalsConf = PortalDispatcherData::GetData();
|
||||
$aDispatchers = array();
|
||||
foreach($aPortalsConf as $sPortalId => $aConf)
|
||||
{
|
||||
$sHandlerClass = $aConf['handler'];
|
||||
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
|
||||
}
|
||||
|
||||
foreach($aDispatchers as $sPortalId => $oDispatcher)
|
||||
{
|
||||
if ($oDispatcher->IsUserAllowed())
|
||||
{
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return self::EXIT_CODE_MUSTBEADMIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
|
||||
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
|
||||
$oP->output();
|
||||
exit;
|
||||
}
|
||||
$aAllowedPortals[] = array(
|
||||
'id' => $sPortalId,
|
||||
'label' => $oDispatcher->GetLabel(),
|
||||
'url' => $oDispatcher->GetUrl(),
|
||||
);
|
||||
}
|
||||
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $bIsAllowedToPortalUsers, $iOnExit);
|
||||
}
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return $iRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sMessage;
|
||||
}
|
||||
}
|
||||
return $aAllowedPortals;
|
||||
}
|
||||
} // End of class
|
||||
|
||||
63
application/portaldispatcher.class.inc.php
Normal file
63
application/portaldispatcher.class.inc.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
class PortalDispatcher
|
||||
{
|
||||
protected $sPortalid;
|
||||
protected $aData;
|
||||
|
||||
public function __construct($sPortalId)
|
||||
{
|
||||
$this->sPortalid = $sPortalId;
|
||||
$this->aData = PortalDispatcherData::GetData($sPortalId);
|
||||
}
|
||||
|
||||
public function IsUserAllowed()
|
||||
{
|
||||
if (array_key_exists('profile_list', $_SESSION))
|
||||
{
|
||||
$aProfiles = $_SESSION['profile_list'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$oSet = $oUser->Get('profile_list');
|
||||
while(($oLnkUserProfile = $oSet->Fetch()) !== null)
|
||||
{
|
||||
$aProfiles[] = $oLnkUserProfile->Get('profileid_friendlyname');
|
||||
}
|
||||
$_SESSION['profile_list'] = $aProfiles;
|
||||
}
|
||||
|
||||
foreach($this->aData['deny'] as $sDeniedProfile)
|
||||
{
|
||||
// If one denied profile is present, it's enough => return false
|
||||
if (in_array($sDeniedProfile, $aProfiles))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach($this->aData['allow'] as $sAllowProfile)
|
||||
{
|
||||
// if one required profile is missing, it's enough => return false
|
||||
if (!in_array($sAllowProfile, $aProfiles))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetURL()
|
||||
{
|
||||
return utils::GetAbsoluteUrlAppRoot().$this->aData['url'];
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return Dict::S('portal:'.$this->sPortalid);
|
||||
}
|
||||
|
||||
public function GetRank()
|
||||
{
|
||||
return $this->aData['rank'];
|
||||
}
|
||||
}
|
||||
@@ -1730,7 +1730,7 @@ class Config
|
||||
$this->SetDBName($sDBName);
|
||||
$this->SetDBSubname($aParamValues['db_prefix']);
|
||||
}
|
||||
|
||||
|
||||
if (!is_null($sModulesDir))
|
||||
{
|
||||
if (isset($aParamValues['selected_modules']))
|
||||
@@ -1746,6 +1746,10 @@ class Config
|
||||
$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();
|
||||
$aDictionaries = $oEmptyConfig->GetDictionaries();
|
||||
|
||||
3
core/datamodel.core.xml
Normal file
3
core/datamodel.core.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design>
|
||||
</itop_design>
|
||||
@@ -35,7 +35,7 @@ try
|
||||
// require_once(APPROOT.'/application/user.preferences.class.inc.php');
|
||||
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
LoginWebPage::DoLogin(false /* bMustBeAdmin */, true /* IsAllowedToPortalUsers */); // Check user rights and prompt if needed
|
||||
LoginWebPage::DoLoginEx(null /* any portal */, false);
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
|
||||
@@ -1224,5 +1224,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
|
||||
'ExcelExport:AutoDownload' => 'Start the download automatically when the export is ready',
|
||||
'ExcelExport:PreparingExport' => 'Preparing the export...',
|
||||
'ExcelExport:Statistics' => 'Statistics',
|
||||
'portal:legacy_portal' => 'End-User Portal',
|
||||
'portal:backoffice' => 'iTop Back-Office User Interface',
|
||||
));
|
||||
?>
|
||||
|
||||
@@ -1065,5 +1065,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
|
||||
'ExcelExport:AutoDownload' => 'Téléchargement automatique dès que le fichier est prêt',
|
||||
'ExcelExport:PreparingExport' => 'Préparation de l\'export...',
|
||||
'ExcelExport:Statistics' => 'Statistiques',
|
||||
'portal:legacy_portal' => 'Portail Utilisateurs',
|
||||
'portal:backoffice' => 'Console iTop',
|
||||
));
|
||||
?>
|
||||
|
||||
@@ -40,7 +40,7 @@ try
|
||||
require_once(APPROOT.'/application/user.preferences.class.inc.php');
|
||||
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
LoginWebPage::DoLogin(false /* bMustBeAdmin */, true /* IsAllowedToPortalUsers */); // Check user rights and prompt if needed
|
||||
LoginWebPage::DoLoginEx(null /* any portal */, false);
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
|
||||
@@ -465,6 +465,20 @@ class ApplicationInstaller
|
||||
}
|
||||
|
||||
$oFactory = new ModelFactory($aDirsToScan);
|
||||
|
||||
$sDeltaFile = APPROOT.'core/datamodel.core.xml';
|
||||
if (file_exists($sDeltaFile))
|
||||
{
|
||||
$oCoreModule = new MFCoreModule('core', 'Core Module', $sDeltaFile);
|
||||
$oFactory->LoadModule($oCoreModule);
|
||||
}
|
||||
$sDeltaFile = APPROOT.'application/datamodel.application.xml';
|
||||
if (file_exists($sDeltaFile))
|
||||
{
|
||||
$oApplicationModule = new MFCoreModule('application', 'Application Module', $sDeltaFile);
|
||||
$oFactory->LoadModule($oApplicationModule);
|
||||
}
|
||||
|
||||
$aModules = $oFactory->FindModules();
|
||||
|
||||
foreach($aModules as $foo => $oModule)
|
||||
|
||||
@@ -32,12 +32,14 @@ class MFCompiler
|
||||
|
||||
protected $aRootClasses;
|
||||
protected $aLog;
|
||||
protected $sMainPHPCode; // Code that goes into core/main.php
|
||||
|
||||
public function __construct($oModelFactory)
|
||||
{
|
||||
$this->oFactory = $oModelFactory;
|
||||
|
||||
$this->aLog = array();
|
||||
$this->sMainPHPCode = '<'.'?'."php\n";
|
||||
}
|
||||
|
||||
protected function Log($sText)
|
||||
@@ -377,7 +379,16 @@ EOF;
|
||||
//
|
||||
$oBrandingNode = $this->oFactory->GetNodes('branding')->item(0);
|
||||
$this->CompileBranding($oBrandingNode, $sTempTargetDir, $sFinalTargetDir);
|
||||
|
||||
|
||||
// Compile the portals
|
||||
$oPortalsNode = $this->oFactory->GetNodes('/itop_design/portals')->item(0);
|
||||
$this->CompilePortals($oPortalsNode, $sTempTargetDir, $sFinalTargetDir);
|
||||
|
||||
// Write core/main.php
|
||||
SetupUtils::builddir($sTempTargetDir.'/core');
|
||||
$sPHPFile = $sTempTargetDir.'/core/main.php';
|
||||
file_put_contents($sPHPFile, $this->sMainPHPCode);
|
||||
|
||||
} // DoCompile()
|
||||
|
||||
/**
|
||||
@@ -1929,6 +1940,60 @@ EOF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function CompilePortals($oPortalsNode, $sTempTargetDir, $sFinalTargetDir)
|
||||
{
|
||||
if ($oPortalsNode)
|
||||
{
|
||||
// Create some static PHP data in <env-xxx>/core/main.php
|
||||
$oPortals = $oPortalsNode->GetNodes('portal');
|
||||
$aPortalsConfig = array();
|
||||
foreach($oPortals as $oPortal)
|
||||
{
|
||||
$sPortalId = $oPortal->getAttribute('id');
|
||||
$aPortalsConfig[$sPortalId] = array();
|
||||
$aPortalsConfig[$sPortalId]['rank'] = (float)$oPortal->GetChildText('rank', 0);
|
||||
$aPortalsConfig[$sPortalId]['handler'] = $oPortal->GetChildText('handler', 'PortalDispatcher');
|
||||
$aPortalsConfig[$sPortalId]['url'] = $oPortal->GetChildText('url', 'portal/index.php');
|
||||
$oAllow = $oPortal->GetOptionalElement('allow');
|
||||
$aPortalsConfig[$sPortalId]['allow'] = array();
|
||||
if ($oAllow)
|
||||
{
|
||||
foreach($oAllow->GetNodes('profile') as $oProfile)
|
||||
{
|
||||
$aPortalsConfig[$sPortalId]['allow'][] = $oProfile->getAttribute('id');
|
||||
}
|
||||
}
|
||||
$oDeny = $oPortal->GetOptionalElement('deny');
|
||||
$aPortalsConfig[$sPortalId]['deny'] = array();
|
||||
if ($oDeny)
|
||||
{
|
||||
foreach($oDeny->GetNodes('profile') as $oProfile)
|
||||
{
|
||||
$aPortalsConfig[$sPortalId]['deny'][] = $oProfile->getAttribute('id');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uasort($aPortalsConfig, array(get_class($this), 'SortOnRank'));
|
||||
|
||||
$this->sMainPHPCode .= "class PortalDispatcherData\n";
|
||||
$this->sMainPHPCode .= "{\n";
|
||||
$this->sMainPHPCode .= "\tprotected static \$aData = ".str_replace("\n", "\n\t", var_export($aPortalsConfig, true)).";\n\n";
|
||||
$this->sMainPHPCode .= "\tpublic static function GetData(\$sPortalId = null)\n";
|
||||
$this->sMainPHPCode .= "\t{\n";
|
||||
$this->sMainPHPCode .= "\t\tif (\$sPortalId === null) return self::\$aData;\n";
|
||||
$this->sMainPHPCode .= "\t\tif (!array_key_exists(\$sPortalId, self::\$aData)) return array();\n";
|
||||
$this->sMainPHPCode .= "\t\treturn self::\$aData[\$sPortalId];\n";
|
||||
$this->sMainPHPCode .= "\t}\n";
|
||||
$this->sMainPHPCode .= "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
public static function SortOnRank($aConf1, $aConf2)
|
||||
{
|
||||
return ($aConf1['rank'] < $aConf2['rank']) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -171,6 +171,40 @@ class MFDeltaModule extends MFModule
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MFDeltaModule: an optional module, made of a single file
|
||||
* @package ModelFactory
|
||||
*/
|
||||
class MFCoreModule extends MFModule
|
||||
{
|
||||
public function __construct($sName, $sLabel, $sDeltaFile)
|
||||
{
|
||||
$this->sId = $sName;
|
||||
|
||||
$this->sName = $sName;
|
||||
$this->sVersion = '1.0';
|
||||
|
||||
$this->sRootDir = '';
|
||||
$this->sLabel = $sLabel;
|
||||
$this->aDataModels = array($sDeltaFile);
|
||||
}
|
||||
|
||||
public function GetRootDir()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetModuleDir()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetDictionaryFiles()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ModelFactory: the class that manages the in-memory representation of the XML MetaModel
|
||||
* @package ModelFactory
|
||||
|
||||
@@ -335,6 +335,19 @@ class RunTimeEnvironment
|
||||
// Do load the required modules
|
||||
//
|
||||
$oFactory = new ModelFactory($aDirsToCompile);
|
||||
$sDeltaFile = APPROOT.'core/datamodel.core.xml';
|
||||
if (file_exists($sDeltaFile))
|
||||
{
|
||||
$oCoreModule = new MFCoreModule('core', 'Core Module', $sDeltaFile);
|
||||
$aRet[] = $oCoreModule;
|
||||
}
|
||||
$sDeltaFile = APPROOT.'application/datamodel.application.xml';
|
||||
if (file_exists($sDeltaFile))
|
||||
{
|
||||
$oApplicationModule = new MFCoreModule('application', 'Application Module', $sDeltaFile);
|
||||
$aRet[] = $oApplicationModule;
|
||||
}
|
||||
|
||||
$aModules = $oFactory->FindModules();
|
||||
foreach($aModules as $foo => $oModule)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user