Browse Source

Compiler: compile menus (updated the reference datamodel)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@1943 a333f486-631f-4898-b8df-5754b55c2be0
romainq 13 years ago
parent
commit
6af7a3eae1

+ 43 - 2
application/menunode.class.inc.php

@@ -87,22 +87,31 @@ class ApplicationMenu
 	 * Main function to add a menu entry into the application, can be called during the definition
 	 * of the data model objects
 	 */
-	static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex = -1, $fRank)
+	static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex, $fRank)
 	{
 		$index = self::GetMenuIndexById($oMenuNode->GetMenuId());
 		if ($index == -1)
 		{
 			// The menu does not already exist, insert it
 			$index = count(self::$aMenusIndex);
-			self::$aMenusIndex[$index] = array( 'node' => $oMenuNode, 'children' => array());
+
 			if ($iParentIndex == -1)
 			{
+				$sParentId = '';
 				self::$aRootMenus[] = array ('rank' => $fRank, 'index' => $index);
 			}
 			else
 			{
+				$sParentId = self::$aMenusIndex[$iParentIndex]['node']->GetMenuId();
 				self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index);
 			}
+
+			// Note: At the time when 'parent', 'rank' and 'source_file' have been added for the reflection API,
+			//       they were not used to display the menus (redundant or unused)
+			//
+			$aBacktrace = debug_backtrace();
+			$sFile = $aBacktrace[2]["file"];
+			self::$aMenusIndex[$index] = array('node' => $oMenuNode, 'children' => array(), 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile);
 		}
 		else
 		{
@@ -111,6 +120,14 @@ class ApplicationMenu
 		}
 		return $index;
 	}
+
+	/**
+	 * Reflection API - Get menu entries
+	 */
+	static public function ReflectionMenuNodes()
+	{
+		return self::$aMenusIndex;
+	}
 	
 	/**
 	 * Entry point to display the whole menu into the web page, used by iTopWebPage
@@ -290,6 +307,11 @@ abstract class MenuNode
 	protected $index;
 
 	/**
+	 * Properties reflecting how the node has been declared
+	 */
+	protected $aReflectionProperties;
+
+	/**
 	 * Class of objects to check if the menu is enabled, null if none
 	 */
 	protected $m_aEnableClasses;
@@ -323,12 +345,25 @@ abstract class MenuNode
 	public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
 	{
 		$this->sMenuId = $sMenuId;
+		$this->aReflectionProperties = array();
+		if (strlen($sEnableClass) > 0)
+		{
+			$this->aReflectionProperties['enable_class'] = $sEnableClass;
+			$this->aReflectionProperties['enable_action'] = $iActionCode;
+			$this->aReflectionProperties['enable_permission'] = $iAllowedResults;
+			$this->aReflectionProperties['enable_stimulus'] = $sEnableStimulus;
+		}
 		$this->m_aEnableClasses = array($sEnableClass);
 		$this->m_aEnableActions = array($iActionCode);
 		$this->m_aEnableActionResults = array($iAllowedResults);
 		$this->m_aEnableStimuli = array($sEnableStimulus);
 		$this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank);
 	}
+
+	public function ReflectionProperties()
+	{
+		return $this->aReflectionProperties;
+	}
 	
 	public function GetMenuId()
 	{
@@ -479,6 +514,7 @@ class TemplateMenuNode extends MenuNode
 	{
 		parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
 		$this->sTemplateFile = $sTemplateFile;
+		$this->aReflectionProperties['template_file'] = $sTemplateFile;
 	}
 	
 	public function GetHyperlink($aExtraParams)
@@ -537,6 +573,8 @@ class OQLMenuNode extends MenuNode
 		$this->sOQL = $sOQL;
 		$this->bSearch = $bSearch;
 		$this->m_aParams = array();
+		$this->aReflectionProperties['oql'] = $sOQL;
+		$this->aReflectionProperties['do_search'] = $bSearch;
 		// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
 		// of the class specified by the OQL...
 	}
@@ -614,6 +652,7 @@ class SearchMenuNode extends MenuNode
 		parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
 		$this->sPageTitle = "Menu:$sMenuId+";
 		$this->sClass = $sClass;
+		$this->aReflectionProperties['class'] = $sClass;
 	}
 	
 	public function RenderContent(WebPage $oPage, $aExtraParams = array())
@@ -653,6 +692,7 @@ class WebPageMenuNode extends MenuNode
 	{
 		parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
 		$this->sHyperlink = $sHyperlink;
+		$this->aReflectionProperties['url'] = $sHyperlink;
 	}
 
 	public function GetHyperlink($aExtraParams)
@@ -690,6 +730,7 @@ class NewObjectMenuNode extends MenuNode
 	{
 		parent::__construct($sMenuId, $iParentIndex, $fRank);
 		$this->sClass = $sClass;
+		$this->aReflectionProperties['class'] = $sClass;
 	}
 
 	public function GetHyperlink($aExtraParams)

+ 49 - 0
datamodel/itop-change-mgmt-1.0.0/datamodel.itop-change-mgmt.xml

@@ -2958,4 +2958,53 @@
       </presentation>
     </class>
   </classes>
+  <menus>
+    <menu id="ChangeManagement" type="MenuGroup">
+      <rank value="50"/>
+    </menu>
+    <menu id="Change:Overview" type="TemplateMenuNode">
+      <template_file value="overview.html"/>
+      <parent value="ChangeManagement"/>
+      <rank value="0"/>
+    </menu>
+    <menu id="NewChange" type="NewObjectMenuNode">
+      <class value="Change"/>
+      <parent value="ChangeManagement"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="SearchChanges" type="SearchMenuNode">
+      <class value="Change"/>
+      <parent value="ChangeManagement"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="Change:Shortcuts" type="TemplateMenuNode">
+      <template_file value=""/>
+      <parent value="ChangeManagement"/>
+      <rank value="3"/>
+    </menu>
+    <menu id="MyChanges" type="OQLMenuNode">
+      <oql value="SELECT Change WHERE agent_id = :current_contact_id AND status NOT IN (&quot;closed&quot;, &quot;resolved&quot;)"/>
+      <do_search value=""/>
+      <parent value="Change:Shortcuts"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="Changes" type="OQLMenuNode">
+      <oql value="SELECT Change WHERE status != &quot;closed&quot;"/>
+      <do_search value=""/>
+      <parent value="Change:Shortcuts"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="WaitingApproval" type="OQLMenuNode">
+      <oql value="SELECT ApprovedChange WHERE status IN (&quot;plannedscheduled&quot;)"/>
+      <do_search value=""/>
+      <parent value="Change:Shortcuts"/>
+      <rank value="3"/>
+    </menu>
+    <menu id="WaitingAcceptance" type="OQLMenuNode">
+      <oql value="SELECT NormalChange WHERE status IN (&quot;new&quot;)"/>
+      <do_search value=""/>
+      <parent value="Change:Shortcuts"/>
+      <rank value="4"/>
+    </menu>
+  </menus>
 </itop_design>

+ 134 - 0
datamodel/itop-config-mgmt-1.0.0/datamodel.itop-config-mgmt.xml

@@ -2895,4 +2895,138 @@
       </presentation>
     </class>
   </classes>
+  <menus>
+    <menu id="DataAdministration" type="MenuGroup">
+      <enable_class value="Organization"/>
+      <enable_action value="UR_ACTION_MODIFY"/>
+      <enable_permission value="UR_ALLOWED_YES"/>
+      <enable_stimulus value=""/>
+      <rank value="70"/>
+    </menu>
+    <menu id="Audit" type="WebPageMenuNode">
+      <url value="$pages/audit.php"/>
+      <parent value="DataAdministration"/>
+      <rank value="33"/>
+    </menu>
+    <menu id="Catalogs" type="TemplateMenuNode">
+      <template_file value=""/>
+      <parent value="DataAdministration"/>
+      <rank value="50"/>
+    </menu>
+    <menu id="Organization" type="OQLMenuNode">
+      <oql value="SELECT Organization"/>
+      <do_search value="1"/>
+      <parent value="Catalogs"/>
+      <rank value="10"/>
+    </menu>
+    <menu id="Application" type="OQLMenuNode">
+      <oql value="SELECT Application"/>
+      <do_search value=""/>
+      <parent value="Catalogs"/>
+      <rank value="20"/>
+    </menu>
+    <menu id="DBServer" type="OQLMenuNode">
+      <oql value="SELECT DBServer"/>
+      <do_search value=""/>
+      <parent value="Catalogs"/>
+      <rank value="40"/>
+    </menu>
+    <menu id="ConfigManagement" type="MenuGroup">
+      <rank value="20"/>
+    </menu>
+    <menu id="ConfigManagementOverview" type="TemplateMenuNode">
+      <template_file value="overview.html"/>
+      <parent value="ConfigManagement"/>
+      <rank value="0"/>
+    </menu>
+    <menu id="Contact" type="TemplateMenuNode">
+      <template_file value="contacts_menu.html"/>
+      <parent value="ConfigManagement"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="NewContact" type="NewObjectMenuNode">
+      <class value="Contact"/>
+      <parent value="Contact"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="SearchContacts" type="SearchMenuNode">
+      <class value="Contact"/>
+      <parent value="Contact"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="Document" type="OQLMenuNode">
+      <oql value="SELECT Document"/>
+      <do_search value="1"/>
+      <parent value="ConfigManagement"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="Location" type="OQLMenuNode">
+      <oql value="SELECT Location"/>
+      <do_search value="1"/>
+      <parent value="ConfigManagement"/>
+      <rank value="3"/>
+    </menu>
+    <menu id="Group" type="OQLMenuNode">
+      <oql value="SELECT Group"/>
+      <do_search value="1"/>
+      <parent value="ConfigManagement"/>
+      <rank value="4"/>
+    </menu>
+    <menu id="ConfigManagementCI" type="TemplateMenuNode">
+      <template_file value="cis_menu.html"/>
+      <parent value="ConfigManagement"/>
+      <rank value="5"/>
+    </menu>
+    <menu id="NewCI" type="NewObjectMenuNode">
+      <class value="FunctionalCI"/>
+      <parent value="ConfigManagementCI"/>
+      <rank value="0"/>
+    </menu>
+    <menu id="SearchCIs" type="SearchMenuNode">
+      <class value="FunctionalCI"/>
+      <parent value="ConfigManagementCI"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="ConfigManagement:Shortcuts" type="TemplateMenuNode">
+      <template_file value=""/>
+      <parent value="ConfigManagement"/>
+      <rank value="6"/>
+    </menu>
+    <menu id="Server" type="OQLMenuNode">
+      <oql value="SELECT Server"/>
+      <do_search value=""/>
+      <parent value="ConfigManagement:Shortcuts"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="NetworkDevice" type="OQLMenuNode">
+      <oql value="SELECT NetworkDevice"/>
+      <do_search value=""/>
+      <parent value="ConfigManagement:Shortcuts"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="Printer" type="OQLMenuNode">
+      <oql value="SELECT Printer"/>
+      <do_search value=""/>
+      <parent value="ConfigManagement:Shortcuts"/>
+      <rank value="3"/>
+    </menu>
+    <menu id="PC" type="OQLMenuNode">
+      <oql value="SELECT PC"/>
+      <do_search value=""/>
+      <parent value="ConfigManagement:Shortcuts"/>
+      <rank value="4"/>
+    </menu>
+    <menu id="BusinessProcess" type="OQLMenuNode">
+      <oql value="SELECT BusinessProcess"/>
+      <do_search value=""/>
+      <parent value="ConfigManagement:Shortcuts"/>
+      <rank value="5"/>
+    </menu>
+    <menu id="ApplicationSolution" type="OQLMenuNode">
+      <oql value="SELECT ApplicationSolution"/>
+      <do_search value=""/>
+      <parent value="ConfigManagement:Shortcuts"/>
+      <rank value="6"/>
+    </menu>
+  </menus>
 </itop_design>

+ 43 - 0
datamodel/itop-incident-mgmt-1.0.0/datamodel.itop-incident-mgmt.xml

@@ -707,4 +707,47 @@
       </presentation>
     </class>
   </classes>
+  <menus>
+    <menu id="IncidentManagement" type="MenuGroup">
+      <rank value="40"/>
+    </menu>
+    <menu id="Incident:Overview" type="TemplateMenuNode">
+      <template_file value="overview.html"/>
+      <parent value="IncidentManagement"/>
+      <rank value="0"/>
+    </menu>
+    <menu id="NewIncident" type="NewObjectMenuNode">
+      <class value="Incident"/>
+      <parent value="IncidentManagement"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="SearchIncidents" type="SearchMenuNode">
+      <class value="Incident"/>
+      <parent value="IncidentManagement"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="Incident:Shortcuts" type="TemplateMenuNode">
+      <template_file value=""/>
+      <parent value="IncidentManagement"/>
+      <rank value="3"/>
+    </menu>
+    <menu id="Incident:MyIncidents" type="OQLMenuNode">
+      <oql value="SELECT Incident WHERE agent_id = :current_contact_id AND status NOT IN (&quot;closed&quot;, &quot;resolved&quot;)"/>
+      <do_search value=""/>
+      <parent value="Incident:Shortcuts"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="Incident:EscalatedIncidents" type="OQLMenuNode">
+      <oql value="SELECT Incident WHERE status IN (&quot;escalated_tto&quot;, &quot;escalated_ttr&quot;)"/>
+      <do_search value=""/>
+      <parent value="Incident:Shortcuts"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="Incident:OpenIncidents" type="OQLMenuNode">
+      <oql value="SELECT Incident WHERE status IN (&quot;new&quot;, &quot;assigned&quot;, &quot;escalated_tto&quot;, &quot;escalated_ttr&quot;, &quot;resolved&quot;)"/>
+      <do_search value=""/>
+      <parent value="Incident:Shortcuts"/>
+      <rank value="3"/>
+    </menu>
+  </menus>
 </itop_design>

+ 26 - 0
datamodel/itop-knownerror-mgmt-1.0.0/datamodel.itop-knownerror-mgmt.xml

@@ -180,4 +180,30 @@
       </presentation>
     </class>
   </classes>
+  <menus>
+    <menu id="ProblemManagement" type="MenuGroup">
+      <rank value="42"/>
+    </menu>
+    <menu id="NewError" type="NewObjectMenuNode">
+      <class value="KnownError"/>
+      <parent value="ProblemManagement"/>
+      <rank value="3"/>
+    </menu>
+    <menu id="SearchError" type="SearchMenuNode">
+      <class value="KnownError"/>
+      <parent value="ProblemManagement"/>
+      <rank value="4"/>
+    </menu>
+    <menu id="Problem:Shortcuts" type="TemplateMenuNode">
+      <template_file value=""/>
+      <parent value="ProblemManagement"/>
+      <rank value="5"/>
+    </menu>
+    <menu id="Problem:KnownErrors" type="OQLMenuNode">
+      <oql value="SELECT KnownError"/>
+      <do_search value="1"/>
+      <parent value="Problem:Shortcuts"/>
+      <rank value="3"/>
+    </menu>
+  </menus>
 </itop_design>

+ 29 - 0
datamodel/itop-problem-mgmt-1.0.0/datamodel.itop-problem-mgmt.xml

@@ -432,4 +432,33 @@
       </presentation>
     </class>
   </classes>
+  <menus>
+    <menu id="Problem:Overview" type="TemplateMenuNode">
+      <template_file value="overview.html"/>
+      <parent value="ProblemManagement"/>
+      <rank value="0"/>
+    </menu>
+    <menu id="NewProblem" type="NewObjectMenuNode">
+      <class value="Problem"/>
+      <parent value="ProblemManagement"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="SearchProblems" type="SearchMenuNode">
+      <class value="Problem"/>
+      <parent value="ProblemManagement"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="Problem:MyProblems" type="OQLMenuNode">
+      <oql value="SELECT Problem WHERE agent_id = :current_contact_id AND status NOT IN (&quot;closed&quot;, &quot;resolved&quot;)"/>
+      <do_search value=""/>
+      <parent value="Problem:Shortcuts"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="Problem:OpenProblems" type="OQLMenuNode">
+      <oql value="SELECT Problem WHERE status IN (&quot;new&quot;, &quot;assigned&quot;, &quot;resolved&quot;)"/>
+      <do_search value=""/>
+      <parent value="Problem:Shortcuts"/>
+      <rank value="2"/>
+    </menu>
+  </menus>
 </itop_design>

+ 43 - 0
datamodel/itop-request-mgmt-1.0.0/datamodel.itop-request-mgmt.xml

@@ -669,4 +669,47 @@
       </presentation>
     </class>
   </classes>
+  <menus>
+    <menu id="RequestManagement" type="MenuGroup">
+      <rank value="30"/>
+    </menu>
+    <menu id="UserRequest:Overview" type="TemplateMenuNode">
+      <template_file value="overview.html"/>
+      <parent value="RequestManagement"/>
+      <rank value="0"/>
+    </menu>
+    <menu id="NewUserRequest" type="NewObjectMenuNode">
+      <class value="UserRequest"/>
+      <parent value="RequestManagement"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="SearchUserRequests" type="SearchMenuNode">
+      <class value="UserRequest"/>
+      <parent value="RequestManagement"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="UserRequest:Shortcuts" type="TemplateMenuNode">
+      <template_file value=""/>
+      <parent value="RequestManagement"/>
+      <rank value="3"/>
+    </menu>
+    <menu id="UserRequest:MyRequests" type="OQLMenuNode">
+      <oql value="SELECT UserRequest WHERE agent_id = :current_contact_id AND status NOT IN (&quot;closed&quot;,&quot;resolved&quot;)"/>
+      <do_search value=""/>
+      <parent value="UserRequest:Shortcuts"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="UserRequest:EscalatedRequests" type="OQLMenuNode">
+      <oql value="SELECT UserRequest WHERE status IN (&quot;escalated_tto&quot;, &quot;escalated_ttr&quot;)"/>
+      <do_search value=""/>
+      <parent value="UserRequest:Shortcuts"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="UserRequest:OpenRequests" type="OQLMenuNode">
+      <oql value="SELECT UserRequest WHERE status IN (&quot;new&quot;, &quot;assigned&quot;, &quot;escalated_tto&quot;, &quot;escalated_ttr&quot;, &quot;frozen&quot;, &quot;resolved&quot;)"/>
+      <do_search value=""/>
+      <parent value="UserRequest:Shortcuts"/>
+      <rank value="3"/>
+    </menu>
+  </menus>
 </itop_design>

+ 46 - 0
datamodel/itop-service-mgmt-1.0.0/datamodel.itop-service-mgmt.xml

@@ -964,4 +964,50 @@
       </presentation>
     </class>
   </classes>
+  <menus>
+    <menu id="ServiceManagement" type="MenuGroup">
+      <rank value="60"/>
+    </menu>
+    <menu id="Service:Overview" type="TemplateMenuNode">
+      <template_file value="overview.html"/>
+      <parent value="ServiceManagement"/>
+      <rank value="0"/>
+    </menu>
+    <menu id="ProviderContract" type="OQLMenuNode">
+      <oql value="SELECT ProviderContract"/>
+      <do_search value="1"/>
+      <parent value="ServiceManagement"/>
+      <rank value="1"/>
+    </menu>
+    <menu id="CustomerContract" type="OQLMenuNode">
+      <oql value="SELECT CustomerContract"/>
+      <do_search value="1"/>
+      <parent value="ServiceManagement"/>
+      <rank value="2"/>
+    </menu>
+    <menu id="Service" type="OQLMenuNode">
+      <oql value="SELECT Service"/>
+      <do_search value="1"/>
+      <parent value="ServiceManagement"/>
+      <rank value="3"/>
+    </menu>
+    <menu id="ServiceSubcategory" type="OQLMenuNode">
+      <oql value="SELECT ServiceSubcategory"/>
+      <do_search value="1"/>
+      <parent value="ServiceManagement"/>
+      <rank value="4"/>
+    </menu>
+    <menu id="SLA" type="OQLMenuNode">
+      <oql value="SELECT SLA"/>
+      <do_search value="1"/>
+      <parent value="ServiceManagement"/>
+      <rank value="5"/>
+    </menu>
+    <menu id="SLT" type="OQLMenuNode">
+      <oql value="SELECT SLT"/>
+      <do_search value="1"/>
+      <parent value="ServiceManagement"/>
+      <rank value="6"/>
+    </menu>
+  </menus>
 </itop_design>

+ 149 - 2
setup/compiler.class.inc.php

@@ -153,6 +153,37 @@ EOF;
 					}
 				}
 			}
+
+			$oMenus = $this->oFactory->ListMenus($sModuleName);
+			$iMenuCount = $oMenus->length;
+			if ($iMenuCount == 0)
+			{
+				$oP->p("Found module without menus declared: $sModuleName");
+			}
+			else
+			{
+				$sMenusHeader =
+<<<EOF
+//
+// Menus
+//
+
+EOF;
+				file_put_contents($sResultFile, $sMenusHeader, FILE_APPEND);
+
+				foreach($oMenus as $oMenu)
+				{
+					try
+					{
+						$this->CompileMenu($oMenu, $sResultFile, $sRelativeDir, $oP);
+					}
+					catch (ssDOMFormatException $e)
+					{
+						$sClass = $oClass->getAttribute("name");
+						throw new Exception("Failed to process class '$sClass', from '$sModuleRootDir': ".$e->getMessage());
+					}
+				}
+			}
 		}
 		
 		if (count($aResultFiles))
@@ -168,6 +199,7 @@ EOF;
 		}
 		
 		$oP->output();
+		//$this->oFactory->Dump();
 	}
 
 	/**
@@ -340,8 +372,45 @@ EOF;
 		$sRes = implode(' | ', $aFlags);
 		return $sRes;
 	}
-	
-	
+
+
+	/**
+	 * Format a path (file or url) as an absolute path or relative to the module or the app
+	 */ 
+	protected function PathToPHP($sPath, $sModuleRelativeDir, $bIsUrl = false)
+	{
+		if (substr($sPath, 0, 2) == '$$')
+		{
+			// Absolute
+			$sPHP = "'".addslashes(substr($sPath, 2))."'";
+		}
+		elseif (substr($sPath, 0, 1) == '$')
+		{
+			// Relative to the application
+			if ($bIsUrl)
+			{
+				$sPHP = "utils::GetAbsoluteUrlAppRoot().'".addslashes(substr($sPath, 1))."'";
+			}
+			else
+			{
+				$sPHP = "APPROOT.'".addslashes(substr($sPath, 1))."'";
+			}
+		}
+		else
+		{
+			// Relative to the module
+			if ($bIsUrl)
+			{
+				$sPHP = "utils::GetAbsoluteUrlAppRoot().'".addslashes($sModuleRelativeDir.''.$sPath)."'";
+			}
+			else
+			{
+				$sPHP = "dirname(__FILE__).'/$sPath'";
+			}
+		}
+		return $sPHP;
+	}
+
 	protected function CompileClass($oClass, $sResFile, $sModuleRelativeDir, $oP)
 	{
 		$sClass = $oClass->getAttribute('name');
@@ -726,7 +795,85 @@ $sMethods
 }
 EOF;
 		file_put_contents($sResFile, $sPHP, FILE_APPEND);
+	}// function CompileClass()
+
+
+	protected function CompileMenu($oMenu, $sResFile, $sModuleRelativeDir, $oP)
+	{
+		$sMenuId = $oMenu->getAttribute("id");
+		$sMenuClass = $oMenu->getAttribute("type");
+
+		$oParent = $this->GetOptionalElement($oMenu, 'parent');
+		if ($oParent)
+		{
+			$sParent = $oParent->GetAttribute('value');
+			$sParentSpec = "\$__comp_menus__['$sParent']->GetIndex()";
+		}
+		else
+		{
+			$sParentSpec = '-1';
+		}
+
+		$fRank = $this->GetUniqueElement($oMenu, 'rank')->GetAttribute('value');
+		switch($sMenuClass)
+		{
+		case 'WebPageMenuNode':
+			$sUrl = $this->GetUniqueElement($oMenu, 'url')->GetAttribute('value');
+			$sUrlSpec = $this->PathToPHP($sUrl, $sModuleRelativeDir, true /* Url */);
+			$sNewMenu = "new WebPageMenuNode('$sMenuId', $sUrlSpec, $sParentSpec, $fRank);";
+			break;
+
+		case 'TemplateMenuNode':
+			$sTemplateFile = $this->GetUniqueElement($oMenu, 'template_file')->GetAttribute('value');
+			$sTemplateSpec = $this->PathToPHP($sTemplateFile, $sModuleRelativeDir);
+			$sNewMenu = "new TemplateMenuNode('$sMenuId', $sTemplateSpec, $sParentSpec, $fRank);";
+			break;
+
+		case 'OQLMenuNode':
+			$sOQL = $this->GetUniqueElement($oMenu, 'oql')->GetAttribute('value');
+			$bSearch = ($this->GetUniqueElement($oMenu, 'do_search')->GetAttribute('value') == '1') ? 'true' : 'false';
+			$sNewMenu = "new OQLMenuNode('$sMenuId', '$sOQL', $sParentSpec, $fRank, $bSearch);";
+			break;
+
+		case 'NewObjectMenuNode':
+			$sClass = $this->GetUniqueElement($oMenu, 'class')->GetAttribute('value');
+			$sNewMenu = "new NewObjectMenuNode('$sMenuId', '$sClass', $sParentSpec, $fRank);";
+			break;
+
+		case 'SearchMenuNode':
+			$sClass = $this->GetUniqueElement($oMenu, 'class')->GetAttribute('value');
+			$sNewMenu = "new SearchMenuNode('$sMenuId', '$sClass', $sParentSpec, $fRank);";
+			break;
+
+		case 'MenuGroup':
+		default:
+			if ($oEnableClass = $this->GetOptionalElement($oMenu, 'enable_class'))
+			{
+				$sEnableClass = $oEnableClass->GetAttribute('value');
+				$sEnableAction = $this->GetUniqueElement($oMenu, 'enable_action')->GetAttribute('value');
+				$sEnablePermission = $this->GetUniqueElement($oMenu, 'enable_permission')->GetAttribute('value');
+				$sEnableStimulus = $this->GetUniqueElement($oMenu, 'enable_stimulus')->GetAttribute('value');
+				if (strlen($sEnableStimulus) > 0)
+				{
+					$sNewMenu = "new MenuGroup('$sMenuId', $fRank, '$sEnableClass', $sEnableAction, $sEnablePermission, '$sEnableStimulus');";
+				}
+				else
+				{
+					$sNewMenu = "new MenuGroup('$sMenuId', $fRank, '$sEnableClass', $sEnableAction, $sEnablePermission);";
+				}
+				//$sNewMenu = "new MenuGroup('$sMenuId', $fRank, '$sEnableClass', UR_ACTION_MODIFY, UR_ALLOWED_YES|UR_ALLOWED_DEPENDS);";
+			}
+			else
+			{
+				$sNewMenu = "new MenuGroup('$sMenuId', $fRank);";
+			}
+		}
+		$sPHP = "\$__comp_menus__['$sMenuId'] = $sNewMenu\n";
+
+		file_put_contents($sResFile, $sPHP, FILE_APPEND);
 	}
 }
 
+
+
 ?>

+ 86 - 1
setup/modelfactory.class.inc.php

@@ -272,7 +272,9 @@ class ModelFactory
 	protected $oDOMDocument;
 	protected $oRoot;
 	protected $oClasses;
+	protected $oMenus;
 	static protected $aLoadedClasses;
+	static protected $aLoadedMenus;
 	static protected $aWellKnownParents = array('DBObject', 'CMDBObject','cmdbAbstractObject');
 	static protected $aLoadedModules;
 	
@@ -285,7 +287,10 @@ class ModelFactory
 		$this->oDOMDocument->AppendChild($this->oRoot);
 		$this->oClasses = $this->oDOMDocument->CreateElement('classes');
 		$this->oRoot->AppendChild($this->oClasses);
+		$this->oMenus = $this->oDOMDocument->CreateElement('menus');
+		$this->oRoot->AppendChild($this->oMenus);
 		self::$aLoadedClasses = array();
+		self::$aLoadedMenus = array();
 		self::$aLoadedModules = array();
 	}
 	
@@ -293,7 +298,7 @@ class ModelFactory
 	{
 		if (is_null($oNode))
 		{
-			$oNode = $this->oClasses;
+			$oNode = $this->oMenus;
 		}
 		echo htmlentities($this->oDOMDocument->saveXML($oNode));
 	}
@@ -332,6 +337,13 @@ class ModelFactory
 				$sClassName = $oNode->GetAttribute('name');
 				$aClasses[$sClassName] = array('name' => $sClassName, 'parent' => $sParentClass, 'node' => $oNode);
 			}
+
+			// Menus - temporary for compiling ???
+			$oNodeList = $oXPath->query('/itop_design/menus/menu');
+			foreach($oNodeList as $oNode)
+			{
+				$this->AddMenu($oNode, $sModuleName);
+			}
 		}
 		
 		$index = 1;
@@ -542,6 +554,12 @@ class ModelFactory
 		
 	}
 
+	/**
+	 * Modify a class within the DOM
+	 * @param string $sMenuId
+	 * @param DOMNode $oMenuNode
+	 * @throws Exception
+	 */
 	public function AlterClass($sClassName, DOMNode $oClassNode)
 	{
 		$sOriginalName = $sClassName;
@@ -571,7 +589,58 @@ class ModelFactory
 		}
 		$this->_priv_SetFlag($oDestNode, 'modified');
 	}
+
+	/**
+	 * Add the given menu to the DOM
+	 * @param DOMNode $oMenuNode
+	 * @param string $sModuleName The name of the module in which this class is declared
+	 * @throws Exception
+	 */
+	public function AddMenu($oMenuNode, $sModuleName)
+	{
+		$sMenuId = $oMenuNode->GetAttribute('id');
+
+		self::$aLoadedMenus[$sMenuId] = $this->oDOMDocument->ImportNode($oMenuNode, true /* bDeep */);
+		self::$aLoadedMenus[$sMenuId]->SetAttribute('_operation', 'added');
+		if ($sModuleName != '')
+		{
+			self::$aLoadedMenus[$sMenuId]->SetAttribute('_created_in', $sModuleName);
+		}
+		$this->oMenus->AppendChild(self::$aLoadedMenus[$sMenuId]);
+	}
 	
+	/**
+	 * Remove a menu from the DOM
+	 * @param string $sMenuId
+	 * @throws Exception
+	 */
+	public function RemoveMenu($sMenuId)
+	{
+		$oMenuNode = self::$aLoadedMenus[$sClass];
+		if ($oMenuNode->getAttribute('_operation') == 'added')
+		{
+			$oMenuNode->parentNode->RemoveChild($oMenuNode);
+			unset(self::$aLoadedMenus[$sMenuId]);	
+		}
+		else
+		{
+			self::$aLoadedMenus[$sMenuId]->SetAttribute('_operation', 'removed');
+		}
+		
+	}
+
+	/**
+	 * Modify a menu within the DOM
+	 * @param string $sMenuId
+	 * @param DOMNode $oMenuNode
+	 * @throws Exception
+	 */
+	public function AlterMenu($sMenuId, DOMNode $oMenuNode)
+	{
+		// Todo - implement... do we have to handle menu renaming ???
+	}
+
+
 	protected function _priv_AlterNode(DOMNode $oNode, DOMNode $oDeltaNode)
 	{
 		foreach ($oDeltaNode->attributes as $sName => $oAttrNode)
@@ -1074,6 +1143,22 @@ EOF
 		return array();
 	}
 		
+	/**
+	 * List all menus from the DOM, for a given module
+	 * @param string $sModuleName
+	 * @param bool $bFlattenLayers
+	 * @throws Exception
+	 */
+	public function ListMenus($sModuleName, $bFlattenLayers = true)
+	{
+		$sXPath = "//menu[@_created_in='$sModuleName']";
+		if ($bFlattenLayers)
+		{
+			$sXPath = "//menu[@_created_in='$sModuleName' and @_operation!='removed']";
+		}
+		return $this->_priv_GetNodes($sXPath, $this->oMenus);
+	}
+		
 	public function ApplyChanges()
 	{
 		$oNodes = $this->ListChanges();