diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 1da974202..fa94cc53b 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -174,7 +174,46 @@ EOF } } "); - $this->DisplayMenu(); + + // Add the standard menus + /* + * +--------------------+ + * | Welcome | + * +--------------------+ + * Welcome To iTop + * +--------------------+ + * | Tools | + * +--------------------+ + * CSV Import + * +--------------------+ + * | Admin Tools | << Only present if the user is an admin + * +--------------------+ + * User Accounts + * Profiles + * Notifications + * Run Queries + * Export + * Data Model + * Universal Search + */ + $oWelcomeMenu = new MenuGroup('UI:WelcomeMenu', 0 /* fRank */); + new TemplateMenuNode('UI:WelcomeMenu', '../business/templates/welcome_menu.html', $oWelcomeMenu->GetIndex() /* oParent */, 1 /* fRank */); + + $oToolsMenu = new MenuGroup('UI:AdvancedToolsMenu', 2 /* fRank */); + new WebPageMenuNode('UI:CSVImportMenu', '../pages/csvimport.php', $oToolsMenu->GetIndex(), 1 /* fRank */); + + if (userRights::IsAdministrator()) + { + // Add the admin menus + $oAdminMenu = new MenuGroup('UI:AdminToolsMenu', 999 /* fRank */); + new OQLMenuNode('UI:UserAccountsMenu', 'UI:UserAccountsMenu:Title', 'SELECT URP_Users', $oAdminMenu->GetIndex(), 1 /* fRank */); + new OQLMenuNode('UI:ProfilesMenu', 'UI:ProfilesMenu:Title', 'SELECT URP_Profiles', $oAdminMenu->GetIndex(), 2 /* fRank */); + new TemplateMenuNode('UI:NotificationsMenu', '../business/templates/notifications_menu.html', $oAdminMenu->GetIndex(), 3 /* fRank */); + new WebPageMenuNode('UI:RunQueriesMenu', '../pages/run_query.php', $oAdminMenu->GetIndex(), 8 /* fRank */); + new WebPageMenuNode('UI:ExportMenu', '../webservices/export.php', $oAdminMenu->GetIndex(), 9 /* fRank */); + new WebPageMenuNode('UI:DataModelMenu', '../pages/schema.php', $oAdminMenu->GetIndex(), 10 /* fRank */); + new WebPageMenuNode('UI:UniversalSearchMenu', '../pages/UniversalSearch.php', $oAdminMenu->GetIndex(), 11 /* fRank */); + } } public function AddToMenu($sHtml) @@ -222,62 +261,10 @@ EOF $oContext = new UserContext(); // Display the menu $oAppContext = new ApplicationContext(); - $iActiveNodeId = utils::ReadParam('menu', ''); + $iActiveNodeId = ApplicationMenu::GetActiveNodeId(); $iAccordionIndex = 0; - - // 1) Application defined menus - $oSearchFilter = $oContext->NewFilter("menuNode"); - $oSearchFilter->AddCondition('parent_id', 0, '='); - $oSearchFilter->AddCondition('type', 'application', '='); - // There may be more criteria added later to have a specific menu based on the user's profile - $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); - while ($oRootMenuNode = $oSet->Fetch()) - { - $bResult = $oRootMenuNode->DisplayMenu($this, 'application', $oAppContext->GetAsHash(), true, $iActiveNodeId); - if ($bResult) - { - $this->add_ready_script("$('#accordion').accordion('activate', $iAccordionIndex)"); - } - $iAccordionIndex++; - } - - // 2) User defined menus (Bookmarks) - $oSearchFilter = $oContext->NewFilter("menuNode"); - $oSearchFilter->AddCondition('parent_id', 0, '='); - $oSearchFilter->AddCondition('type', 'user', '='); - $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); - // There may be more criteria added later to have a specific menu based on the user's profile - $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); - while ($oRootMenuNode = $oSet->Fetch()) - { - $oRootMenuNode->DisplayMenu($this, 'user', $oAppContext->GetAsHash(), true, $iActiveNodeId); - if ($bResult) - { - $this->add_ready_script("$('#accordion').accordion('activate', $iAccordionIndex)"); - } - $iAccordionIndex++; - } - - // 3) Administrator menu - if (userRights::IsAdministrator()) - { - $oSearchFilter = $oContext->NewFilter("menuNode"); - $oSearchFilter->AddCondition('parent_id', 0, '='); - $oSearchFilter->AddCondition('type', 'administrator', '='); - // There may be more criteria added later to have a specific menu based on the user's profile - $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); - while ($oRootMenuNode = $oSet->Fetch()) - { - $oRootMenuNode->DisplayMenu($this, 'administrator', $oAppContext->GetAsHash(), true, $iActiveNodeId); - if ($bResult) - { - $this->add_ready_script("$('#accordion').accordion('activate', $iAccordionIndex)"); - } - $iAccordionIndex++; - } - } - $this->AddToMenu("\n"); + ApplicationMenu::DisplayMenu($this, $oAppContext->GetAsHash(), $iActiveNodeId); } /** @@ -285,6 +272,7 @@ EOF */ public function output() { + $this->DisplayMenu(); // Compute the menu foreach($this->a_headers as $s_header) { header($s_header); diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index fa4eb616b..bd68f0ca4 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -15,7 +15,7 @@ // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA /** - * Persistent class menuNode + * Construction and display of the application's main menu * * @author Erwan Taloc * @author Romain Quetiez @@ -23,231 +23,415 @@ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */ -require_once('../core/attributedef.class.inc.php'); -require_once('../core/filterdef.class.inc.php'); -require_once('../core/stimulus.class.inc.php'); -require_once('../core/MyHelpers.class.inc.php'); +require_once('../application/utils.inc.php'); +require_once('../application/template.class.inc.php'); -require_once('../core/cmdbsource.class.inc.php'); -require_once('../core/sqlquery.class.inc.php'); - -require_once('../core/dbobject.class.php'); -require_once('../core/dbobjectsearch.class.php'); -require_once('../core/dbobjectset.class.php'); - -require_once('../application/displayblock.class.inc.php'); /** - * This class manages en entries in the menu tree on the left of the iTop pages + * This class manipulates, stores and displays the navigation menu used in the application + * In order to improve the modularity of the data model and to ease the update/migration + * between evolving data models, the menus are no longer stored in the database, but are instead + * built on the fly each time a page is loaded. + * The application's menu is organized into top-level groups with, inside each group, a tree of menu items. + * Top level groups do not display any content, they just expand/collapse. + * Sub-items drive the actual content of the page, they are based either on templates, OQL queries or full (external?) web pages. + * + * Example: + * Here is how to insert the following items in the application's menu: + * +----------------------------------------+ + * | Configuration Management Group | >> Top level group + * +----------------------------------------+ + * + Configuration Management Overview >> Template based menu item + * + Contacts >> Template based menu item + * + Persons >> Plain list (OQL based) + * + Teams >> Plain list (OQL based) + * + * // Create the top-level group. fRank = 1, means it will be inserted after the group '0', which is usually 'Welcome' + * $oConfigMgmtMenu = new MenuGroup('UI:ConfigurationManagementMenu', 1); + * // Create an entry, based on a custom template, for the Configuration management overview, under the top-level group + * new TemplateMenuNode('UI:ConfigurationManagementMenu', '../business/templates/configuration_management_menu.html', $oConfigMgmtMenu->GetIndex(), 0); + * // Create an entry (template based) for the overview of contacts + * $oContactsMenu = new TemplateMenuNode('UI:ContactsMenu', '../business/templates/configuration_management_menu.html',$oConfigMgmtMenu->GetIndex(), 1); + * // Plain list of persons + * new OQLMenuNode('UI:PersonsMenu', 'UI:PersonsMenu:Title', 'SELECT bizPerson', $oContactsMenu->GetIndex(), 0); + * // Plain list of teams + * new OQLMenuNode('UI:TeamsMenu', 'UI:TeamsMenu:Title', 'SELECT bizTeam', $oContactsMenu->GetIndex(), 1); + * */ -class menuNode extends DBObject + +class ApplicationMenu { - public static function Init() - { - $aParams = array - ( - "category" => "gui", - "key_type" => "autoincrement", - "name_attcode" => "name", - "state_attcode" => "", - "reconc_keys" => array(), - "db_table" => "priv_menunode", - "db_key_field" => "id", - "db_finalclass_field" => "", - ); - MetaModel::Init_Params($aParams); -// MetaModel::Init_AddAttribute(new AttributeExternalKey("change", array("allowed_values"=>null, "sql"=>"changeid", "targetclass"=>"CMDBChange", "jointype"=>"closed"))); -// MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date"))); - MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("label", array("allowed_values"=>null, "sql"=>"label", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("hyperlink", array("allowed_values"=>null, "sql"=>"hyperlink", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("icon_path", array("allowed_values"=>null, "sql"=>"icon_path", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeText("template", array("allowed_values"=>null, "sql"=>"template", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("type", array("allowed_values"=>new ValueSetEnum('application,user,administrator'), "sql"=>"type", "default_value"=>"application", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeInteger("rank", array("allowed_values"=>null, "sql"=>"rank", "default_value" => 999, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_id", array("allowed_values"=>null, "sql"=>"parent_id", "targetclass"=>"menuNode", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalField("parent_name", array("allowed_values"=>null, "extkey_attcode"=>"parent_id", "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value" => 0, "is_null_allowed"=>false, "depends_on"=>array()))); - - MetaModel::Init_SetZListItems('details', array('parent_id', 'name', 'label', 'hyperlink', 'template', 'rank', 'type')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('parent_id', 'label', 'rank', 'type')); // Attributes to be displayed for a list - } + static $aRootMenus = array(); + static $aMenusIndex = array(); - public function IsVisible() + /** + * 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) { - return true; - } - - public function GetMenuName() - { - return Dict::S($this->Get('name')); - } - - public function GetMenuIcon() - { - return $this->Get('icon_path'); - } - - public function GetMenuLabel() - { - return Dict::S($this->Get('label')); - } - - public function GetMenuLink($aExtraParams) - { - $aExtraParams['menu'] = $this->GetKey(); // Make sure we overwrite the current menu id (if any) - $aParams = array(); - foreach($aExtraParams as $sName => $sValue) + $index = count(self::$aMenusIndex); + self::$aMenusIndex[$index] = array( 'node' => $oMenuNode, 'children' => array()); + if ($iParentIndex == -1) { - $aParams[] = urlencode($sName)."=".urlencode($sValue); - } - return $this->Get('hyperlink')."?".implode("&", $aParams); - } - - public function GetChildNodesSet($sType = null) - { - $aParams = array(); - - if ($sType == 'user') - { - $sSelectChilds = 'SELECT menuNode AS m WHERE m.parent_id = :parent AND type = :type AND m.user_id = :user'; - $aParams = array('parent' => $this->GetKey(), 'type' => $sType, 'user' => UserRights::GetUserId()); - } - elseif ($sType != null) - { - $sSelectChilds = 'SELECT menuNode AS m WHERE m.parent_id = :parent AND type = :type'; - $aParams = array('parent' => $this->GetKey(), 'type' => $sType); + self::$aRootMenus[] = array ('rank' => $fRank, 'index' => $index); } else { - $sSelectChilds = 'SELECT menuNode AS m WHERE m.parent_id = :parent'; - $aParams = array('parent' => $this->GetKey()); + self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index); } - $oSearchFilter = DBObjectSearch::FromOQL($sSelectChilds); - $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true), $aParams); - return $oSet; - } - - public function RenderContent(WebPage $oPage, $aExtraParams = array()) - { - $sTemplate = $this->Get('template'); - $oTemplate = new DisplayTemplate($sTemplate); - $oTemplate->Render($oPage, $aExtraParams); - //$this->ProcessTemplate($sTemplate, $oPage, $aExtraParams); + return $index; } - public function DisplayMenu(iTopWebPage $oP, $sType, $aExtraParams, $bRootLevel = true, $iActiveNodeId = -1) + /** + * Entry point to display the whole menu into the web page, used by iTopWebPage + */ + static public function DisplayMenu(iTopWebPage $oPage, $aExtraParams) { + // Sort the root menu based on the rank + usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank')); + $iAccordion = 0; + $iActiveMenu = ApplicationMenu::GetActiveNodeId(); + foreach(self::$aRootMenus as $aMenu) + { + $oMenuNode = self::GetMenuNode($aMenu['index']); + $oPage->AddToMenu('

'.$oMenuNode->GetTitle().'

'); + $oPage->AddToMenu('
'); + $aChildren = self::GetChildren($aMenu['index']); + if (count($aChildren) > 0) + { + $oPage->AddToMenu('
    '); + $bActive = self::DisplaySubMenu($oPage, $aChildren, $aExtraParams, $iActiveMenu); + $oPage->AddToMenu('
'); + if ($bActive) + { + $oPage->add_ready_script("$('#accordion').accordion('activate', $iAccordion);"); + } + } + $oPage->AddToMenu('
'); + $iAccordion++; + } + } + + /** + * Handles the display of the sub-menus (called recursively if necessary) + * @return true if the currently selected menu is one of the submenus + */ + static protected function DisplaySubMenu($oPage, $aMenus, $aExtraParams, $iActiveMenu = -1) + { + // Sort the menu based on the rank $bActive = false; - if ($bRootLevel) + usort($aMenus, array('ApplicationMenu', 'CompareOnRank')); + foreach($aMenus as $aMenu) { - $oP->AddToMenu("

GetMenuLink($aExtraParams)."\" title=\"".$this->GetMenuLabel()."\">".$this->GetMenuName()."

\n"); - $oP->AddToMenu("\n
\n"); - $oP->AddToMenu("

GetMenuLink($aExtraParams)."\" title=\"".$this->GetMenuLabel()."\">".$this->GetMenuLabel()."

\n"); - $oSet = $this->GetChildNodesSet($sType); - if ($oSet->Count() > 0) + $index = $aMenu['index']; + $oMenu = self::GetMenuNode($index); + $oPage->AddToMenu('
  • '.$oMenu->GetTitle().'
  • '); + $aCurrentMenu = self::$aMenusIndex[$index]; + $aChildren = self::GetChildren($index); + if ($iActiveMenu == $index) { - $oP->AddToMenu("\n
      \n"); - while($oChildNode = $oSet->Fetch()) - { - $bActive |= $oChildNode->DisplayMenu($oP, $sType, $aExtraParams, false /* ! RootLevel */, $iActiveNodeId); - } - $oP->AddToMenu("
    \n"); + $bActive = true; } - $oP->AddToMenu("\n
    \n"); - } - else - { - $oP->AddToMenu("
  • GetMenuLink($aExtraParams)."\" title=\"".$this->GetMenuLabel()."\">".$this->GetMenuName().""); - $oSet = $this->GetChildNodesSet($sType); - if ($oSet->Count() > 0) + if (count($aChildren) > 0) { - $oP->AddToMenu("\n
      \n"); - while($oChildNode = $oSet->Fetch()) - { - $bActive |= $oChildNode->DisplayMenu($oP, $sType, $aExtraParams, false /* ! RootLevel */, $iActiveNodeId); - } - $oP->AddToMenu("
    \n"); - } - $oP->AddToMenu("
  • \n"); - } - $bResult = ($iActiveNodeId == $this->GetKey()) | $bActive; - return $bResult; - } - static public function DisplayCreationForm(WebPage $oP, $sClass, $sFilter, $aExtraParams = array()) - { - $oFilter = DBObjectSearch::unserialize($sFilter); - $oP->p('Create a new menu item for: '.$oFilter->__DescribeHTML()); - $oP->add('
    '); - $oP->add(''); - $oP->add(''); - $oP->add(''); - $oP->p('Menu Label: '); - $oP->p('Description: '); - $oP->add('

    Insert after:

    '); - $oP->p(' Create as a child menu item'); - $oP->p('      '); - $oP->add('
    '); - } - - static public function GetMenuAsArray($oRootNode = null, $sType = 'application', $iDepth = 0) - { - $aNodes = array(); - if (is_object($oRootNode)) - { - $oChildSet = $oRootNode->GetChildNodesSet($sType); - while($oNode = $oChildSet->Fetch()) - { - $aNodes[] = array('depth' => $iDepth, 'id' => $oNode->GetKey(), 'label' => $oNode->GetName()); - $aNodes = array_merge($aNodes, self::GetMenuAsArray($oNode, $sType, $iDepth+1)); + $oPage->AddToMenu(''); } } - else - { - $oSearchFilter = new DbObjectSearch("menuNode"); - $oSearchFilter->AddCondition('parent_id', 0, '='); - if ($sType != null) - { - $oSearchFilter->AddCondition('type', $sType, '='); - if ($sType == 'user') - { - $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); - } - } - $oRootSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); - while($oNode = $oRootSet->Fetch()) - { - $aNodes[] = array('depth' => $iDepth, 'id' => $oNode->GetKey(), 'label' => $oNode->GetName()); - $aNodes = array_merge($aNodes, self::GetMenuAsArray($oNode, $sType, $iDepth+1)); - } - } - return $aNodes; + return $bActive; } /** - * Returns a set of all the nodes following the current node in the tree - * (i.e. nodes with the same parent but with a greater rank) + * Helper function to sort the menus based on their rank */ - public function GetNextNodesSet($sType = 'application') + static public function CompareOnRank($a, $b) { - $oSearchFilter = new DBObjectSearch("menuNode"); - $oSearchFilter->AddCondition('parent_id', $this->Get('parent_id')); - $oSearchFilter->AddCondition('rank', $this->Get('rank'), '>'); - if ($sType != null) + $result = 1; + if ($a['rank'] == $b['rank']) { - $oSearchFilter->AddCondition('type', $sType, '='); - if ($sType == 'user') + $result = 0; + } + if ($a['rank'] < $b['rank']) + { + $result = -1; + } + return $result; + } + + /** + * Helper function to retrieve the MenuNodeObject based on its ID + */ + static public function GetMenuNode($index) + { + return isset(self::$aMenusIndex[$index]) ? self::$aMenusIndex[$index]['node'] : null; + } + + /** + * Helper function to get the list of child(ren) of a menu + */ + static protected function GetChildren($index) + { + return self::$aMenusIndex[$index]['children']; + } + + /** + * Helper function to get the ID of a menu based on its name + * @param string $sTitle Title of the menu (as passed when creating the menu) + * @return integer ID of the menu, or -1 if not found + */ + static public function GetMenuIndexByTitle($sTitle) + { + $index = -1; + foreach(self::$aMenusIndex as $aMenu) + { + if ($aMenu['node']->GetTitle() == $sTitle) { - $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + $id = $aMenu['node']->GetIndex(); + break; } } - $oSet = new DBObjectSet($oSearchFilter, array('rank'=> true)); // Order by rank (true means ascending) - return $oSet; + return $index; + } + + /** + * Retrieves the currently active menu (if any, otherwise the first menu is the default) + * @return MenuNode or null if there is no menu at all ! + */ + static public function GetActiveNodeId() + { + $iMenuIndex = utils::ReadParam('menu', -1); + + if ($iMenuIndex == -1) + { + // Make sure the root menu is sorted on 'rank' + usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank')); + $oFirstGroup = self::GetMenuNode(self::$aRootMenus[0]['index']); + $oMenuNode = self::GetMenuNode(self::$aMenusIndex[$oFirstGroup->GetIndex()]['children'][0]['index']); + $iMenuIndex = $oMenuNode->GetIndex(); + } + return $iMenuIndex; + } +} + +/** + * Root class for all the kind of node in the menu tree, data model providers are responsible for instantiating + * MenuNodes (i.e instances from derived classes) in order to populate the application's menu. Creating an objet + * derived from MenuNode is enough to have it inserted in the application's main menu. + * The class iTopWebPage, takes care of 3 items: + * +--------------------+ + * | Welcome | + * +--------------------+ + * Welcome To iTop + * +--------------------+ + * | Tools | + * +--------------------+ + * CSV Import + * +--------------------+ + * | Admin Tools | + * +--------------------+ + * User Accounts + * Profiles + * Notifications + * Run Queries + * Export + * Data Model + * Universal Search + * + * All the other menu items must constructed along with the various data model modules + */ +abstract class MenuNode +{ + protected $sTitle; + protected $index = null; + + /** + * Create a menu item and inserts it into the application's main menu + * @param string $sTitle Title of the menu (will be looked-up in the dictionnary, for translation) + * @param integer $iParentIndex ID of the parent menu, pass -1 for top level (group) items + * @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value + * @return MenuNode + */ + public function __construct($sTitle, $iParentIndex = -1, $fRank = 0) + { + $this->sTitle = $sTitle; + $this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank); + } + + public function GetTitle() + { + return Dict::S($this->sTitle); + } + + public function GetLabel() + { + return Dict::S($this->sTitle); + } + + public function GetIndex() + { + return $this->index; + } + + public function GetHyperlink($aExtraParams) + { + $aExtraParams['menu'] = $this->GetIndex(); + return $this->AddParams('../pages/UI.php?menu=', $aExtraParams); + } + + public abstract function RenderContent(WebPage $oPage, $aExtraParams = array()); + + protected function AddParams($sHyperlink, $aExtraParams) + { + if (count($aExtraParams) > 0) + { + $aQuery = array(); + $sSeparator = '?'; + if (strpos($sHyperlink, '?') !== false) + { + $sSeparator = '&'; + } + foreach($aExtraParams as $sName => $sValue) + { + $aQuery[] = urlencode($sName).'='.urlencode($sValue); + } + $sHyperlink .= $sSeparator.implode('&', $aQuery); + } + return $sHyperlink; + } +} + +/** + * This class implements a top-level menu group. A group is just a container for sub-items + * it does not display a page by itself + */ +class MenuGroup extends MenuNode +{ + /** + * Create a top-level menu group and inserts it into the application's main menu + * @param string $sTitle Title of the menu (will be looked-up in the dictionnary for translation) + * @param float $fRank Number used to order the list, the groups are sorted based on this value + * @return MenuGroup + */ + public function __construct($sTitle, $fRank) + { + parent::__construct($sTitle, -1 /* no parent, groups are at root level */, $fRank); + } + + public function RenderContent(WebPage $oPage, $aExtraParams = array()) + { + assert(false); // Shall never be called, groups do not display any content + } +} + +/** + * This class defines a menu item which content is based on a custom template. + * Note the template can be either a local file or an URL ! + */ +class TemplateMenuNode extends MenuNode +{ + protected $sTemplateFile; + + /** + * Create a menu item based on a custom template and inserts it into the application's main menu + * @param string $sTitle Title of the menu (will be looked-up in the dictionnary for translation) + * @param string $sTemplateFile Path (or URL) to the file that will be used as a template for displaying the page's content + * @param integer $iParentIndex ID of the parent menu + * @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value + * @return MenuNode + */ + public function __construct($sTitle, $sTemplateFile, $iParentIndex, $fRank = 0) + { + parent::__construct($sTitle, $iParentIndex, $fRank); + $this->sTemplateFile = $sTemplateFile; + } + + public function RenderContent(WebPage $oPage, $aExtraParams = array()) + { + $sTemplate = @file_get_contents($this->sTemplateFile); + if ($sTemplate !== false) + { + $oTemplate = new DisplayTemplate($sTemplate); + $oTemplate->Render($oPage, $aExtraParams); + } + else + { + $oPage->p("Error: failed to load template file: '{$this->sTemplateFile}'"); // No need to translate ? + } + } +} + +/** + * This class defines a menu item that uses a standard template to display a list of items therefore it allows + * only two parameters: the page's title and the OQL expression defining the list of items to be displayed + */ +class OQLMenuNode extends MenuNode +{ + protected $sPageTitle; + protected $sOQL; + + /** + * Create a menu item based on an OQL query and inserts it into the application's main menu + * @param string $sTitle Title of the menu (will be looked-up in the dictionnary for translation) + * @param string $sPageTitle Title displayed into the page's content (will be looked-up in the dictionnary for translation) + * @param integer $iParentIndex ID of the parent menu + * @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value + * @return MenuNode + */ + public function __construct($sTitle, $sPageTitle, $sOQL, $iParentIndex, $fRank = 0) + { + parent::__construct($sTitle, $iParentIndex, $fRank); + $this->sPageTitle = $sPageTitle; + $this->sOQL = $sOQL; + } + + public function RenderContent(WebPage $oPage, $aExtraParams = array()) + { + // The standard template used for all such pages: a (closed) search form at the top and a list of results at the bottom + $sTemplate = <<$this->sOQL + +$this->sOQL +EOF; + $oTemplate = new DisplayTemplate($sTemplate); + $oTemplate->Render($oPage, $aExtraParams); + } +} + +/** + * This class defines a menu that points to any web page. It takes only two parameters: + * - The hyperlink to point to + * - The name of the menu + * Note: the parameter menu=xxx (where xxx is the id of the menu itself) will be added to the hyperlink + * in order to make it the active one, if the target page is based on iTopWebPage and therefore displays the menu + */ +class WebPageMenuNode extends MenuNode +{ + protected $sHyperlink; + + /** + * Create a menu item that points to any web page (not only UI.php) + * @param string $sTitle Title of the menu (will be looked-up in the dictionnary for translation) + * @param string $sHyperlink URL to the page to load. Use relative URL if you want to keep the application portable ! + * @param integer $iParentIndex ID of the parent menu + * @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value + * @return MenuNode + */ + public function __construct($sTitle, $sHyperlink, $iParentIndex, $fRank = 0) + { + parent::__construct($sTitle, $iParentIndex, $fRank); + $this->sHyperlink = $sHyperlink; + } + + public function GetHyperlink($aExtraParams) + { + $aExtraParams['menu'] = $this->GetIndex(); + return $this->AddParams( $this->sHyperlink, $aExtraParams); + } + + public function RenderContent(WebPage $oPage, $aExtraParams = array()) + { + assert(false); // Shall never be called, the external web page will handle the display by itself } } ?> diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index d901ecc0e..867e17f96 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -37,48 +37,6 @@ define('STANDARD_STATUSES', 'production,implementation,obsolete'); */ MetaModel::RegisterRelation("impacts"); -class classetest extends cmdbObject -{ - - public static function Init() - { - $aParams = array - ( - "category" => "bizmodel,searchable", - "key_type" => "automincrement", - "name_attcode" => "name", - "state_attcode" => "", - "reconc_keys" => array("name"), - "db_table" => "myclasstable", - "db_key_field" => "id", - "db_finalclass_field" => "", - "display_template" => "", - ); - MetaModel::Init_Params($aParams); - //MetaModel::Init_InheritAttributes(); - - MetaModel::Init_AddAttribute(new AttributeString("aaaa", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeText("b", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEmailAddress("c", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeIPAddress("d", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributePassword("e", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("f", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeInteger("g", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeBoolean("h", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeDateTime("i", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeDate("j", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeBlob("k", array("depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributePropertySet("l", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeTable("m", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("n", array("targetclass"=>"bizOrganization", "jointype"=>null, "allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalField("o", array("allowed_values"=>null, "extkey_attcode"=>"n", "target_attcode"=>"name", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("p", array("linked_class"=>"class_to_be_defined", "ext_key_to_me"=>"attribute_to_be_defined", "ext_key_to_remote"=>"attribute_to_be_defined", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeOQL("q", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeClass("r", array("class_category"=>"bizmodel", "more_values"=>"myvalue1,myvalue2,myvalue3", "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeTemplateString("s", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeTemplateText("t", array("allowed_values"=>null, "sql"=>"mysqlcolumn_to_be_defined", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - } -} //////////////////////////////////////////////////////////////////////////////////// /** @@ -159,7 +117,7 @@ class logRealObject extends cmdbAbstractObject "db_table" => "objects", "db_key_field" => "id", "db_finalclass_field" => "obj_class", - "display_template" => "../business/templates/default.html", + "icon" => "../images/tar.png", ); MetaModel::Init_Params($aParams); MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -175,6 +133,18 @@ class logRealObject extends cmdbAbstractObject MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'org_id')); // Criteria of the advanced search form } + public static function GetRelationQueries($sRelCode) + { + switch ($sRelCode) + { + case "impacts": + $aRels = array( + "owner" => array("sQuery"=>"SELECT bizPerson AS p JOIN lnkContactRealObject AS l1 ON l1.contact_id = p.id WHERE l1.object_id = :this->id", "bPropagate"=>true, "iDistance"=>3), + ); + return array_merge($aRels, parent::GetRelationQueries($sRelCode)); + } + } + public function Generate(cmdbDataGenerator $oGenerator) { $this->Set('org_id', $oGenerator->GetOrganizationId()); @@ -312,6 +282,8 @@ class bizTeam extends bizContact MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'org_id')); // Criteria of the advanced search form } } + + //////////////////////////////////////////////////////////////////////////////////// /** * n-n link between any Object and a contact @@ -1525,7 +1497,52 @@ class bizPatch extends logRealObject } } -/*** Insert here all modules requires for ITOP application ***/ +//////////////////////////////////////////////////////////////////////////////////// +// Menu: +// +----------------------------------------+ +// | Configuration Management Group | +// +----------------------------------------+ +// + Configuration Management Overview +// + Contacts +// + Persons +// + Teams +// + Configuration Items +// + PCs +// + Servers +// + Network Devices +// + Interfaces +// + Circuits +// + Applications +// + Subnets +// + Infra Groups +// + Locations +// + Documents +//////////////////////////////////////////////////////////////////////////////////// + +// Create the top-level group. fRank = 1, means it will be inserted after the group '0', which is usually 'Welcome' +$oConfigMgmtMenu = new MenuGroup('UI:ConfigurationManagementMenu', 1 /* fRank */); +// Create an entry, based on a custom template, for the Configuration management overview, under the top-level group +new TemplateMenuNode('UI:ConfigurationManagementMenu', '../business/templates/configuration_management_menu.html', $oConfigMgmtMenu->GetIndex(), 0 /* fRank */); +// Create an entry (template based) for the overview of contacts +$oContactsMenu = new TemplateMenuNode('UI:ContactsMenu', '../business/templates/configuration_management_menu.html',$oConfigMgmtMenu->GetIndex(), 1 /* fRank */); + // Plain list of persons + new OQLMenuNode('UI:PersonsMenu', 'UI:PersonsMenu:Title', 'SELECT bizPerson', $oContactsMenu->GetIndex(), 0 /* fRank */); + // Plain list of teams + new OQLMenuNode('UI:TeamsMenu', 'UI:TeamsMenu:Title', 'SELECT bizTeam', $oContactsMenu->GetIndex(), 1 /* fRank */); +// Create an entry (template based) to provide an overview of all the 'infrastructure' CIs +$oConfigItemsMenu = new TemplateMenuNode('UI:ConfigurationItemsMenu', '../business/templates/configuration_items_menu.html',$oConfigMgmtMenu->GetIndex(), 2 /* fRank */); +// Quick and dirty way to add in one pass an entry for a few classes of CIs... good enough for testing, but not compatible with the localization +$index = 0; +foreach( array('bizPC', 'bizServer', 'bizNetworkDevice', 'bizInterface', 'bizCircuit', 'bizApplication', 'bizSubnet', 'bizInfraGroup') as $sClass) +{ + new OQLMenuNode($sClass,$sClass, 'SELECT '.$sClass, $oConfigItemsMenu->GetIndex(), $index++ /* fRank */); +} +// Add an entry (plain list) for locations +new OQLMenuNode('UI:LocationsMenu', 'UI:LocationsMenu:Title', 'SELECT bizLocation', $oConfigMgmtMenu->GetIndex(), 3 /* fRank */); +// Last but not least, add an entry (plain list) for documents +new OQLMenuNode('UI:DocumentsMenu', 'UI:DocumentsMenu:Title', 'SELECT bizDocument', $oConfigMgmtMenu->GetIndex(), 4 /* fRank */); + +/*** Insert here all modules required for the whole iTop application ***/ require_once('incidentMgmt.business.php'); require_once('ServiceMgmt.business.php'); diff --git a/business/templates/person.html b/business/templates/person.html index f90961ad9..d0196757d 100644 --- a/business/templates/person.html +++ b/business/templates/person.html @@ -13,7 +13,7 @@ SELECT lnkContactRealObject WHERE contact_id = $id$ - SELECT bizServiceCall WHERE caller_id = $id$ + SELECT bizServiceCall WHERE caller_id = $id$ diff --git a/pages/UI.php b/pages/UI.php index 0fc40a71a..991c60865 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -364,45 +364,14 @@ try require_once('../application/startup.inc.php'); $oContext = new UserContext(); $oAppContext = new ApplicationContext(); - $iActiveNodeId = utils::ReadParam('menu', ''); - - if (empty($iActiveNodeId) && !is_numeric($iActiveNodeId)) - { - // No menu specified, let's get the default one: - // 1) It's a root menu item (parent_id == 0) - // 2) with the lowest rank - $oFilter = DBObjectSearch::FromOQL('SELECT menuNode AS M WHERE M.parent_id = 0'); - if ($oFilter) - { - $oMenuSet = new CMDBObjectSet($oFilter); - while($oMenu = $oMenuSet->Fetch()) - { - $aRanks[$oMenu->GetKey()] = $oMenu->Get('rank'); - } - asort($aRanks); // sort by ascending rank: menuId => rank - $aKeys = array_keys($aRanks); - $iActiveNodeId = array_shift($aKeys); // Takes the first key, i.e. the menuId with the lowest rank - } - } $currentOrganization = utils::ReadParam('org_id', ''); $operation = utils::ReadParam('operation', ''); require_once('../application/loginwebpage.class.inc.php'); LoginWebPage::DoLogin(); // Check user rights and prompt if needed - $oP = new iTopWebPage(Dict::S('UI:WelcomeToITop'), $currentOrganization); - // From now on the context is limited to the the selected organization ?? - if ($iActiveNodeId != -1) - { - $oActiveNode = $oContext->GetObject('menuNode', $iActiveNodeId); - } - else - { - $oActiveNode = null; - } - switch($operation) { case 'details': @@ -1331,10 +1300,12 @@ EOF break; default: - if (is_object($oActiveNode)) + $oMenuNode = ApplicationMenu::GetMenuNode(ApplicationMenu::GetActiveNodeId()); + if (is_object($oMenuNode)) { - $oActiveNode->RenderContent($oP, $oAppContext->GetAsHash()); - $oP->set_title($oActiveNode->GetMenuLabel()); + + $oMenuNode->RenderContent($oP, $oAppContext->GetAsHash()); + $oP->set_title($oMenuNode->GetLabel()); } } ////MetaModel::ShowQueryTrace(); diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php index 5b19d3107..fb697a739 100644 --- a/pages/UniversalSearch.php +++ b/pages/UniversalSearch.php @@ -64,7 +64,9 @@ foreach($aClassLabels as $sCurrentClass => $sLabel) $sSelected = ($sCurrentClass == $sBaseClass) ? " SELECTED" : ""; $oP->add(""); } -$oP->add(""); +$oP->add("\n"); +$oP->add($oAppContext->GetForForm()); +$oP->add("\n"); try { diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml deleted file mode 100644 index 79424b2a1..000000000 --- a/setup/data/structure/1.menus.xml +++ /dev/null @@ -1,819 +0,0 @@ - - - -UI:AdminToolsMenu - -UI.php - - -administrator -1000 -0 -0 - - -UI:AdvancedToolsMenu - -UI.php - - -application -9 -0 -0 - - -UI:ApplicationLogMenu - -UI.php -../images/std_view.gif - -administrator -2 -1 -0 - - -UI:ApplicationsMenu - -UI.php - - -application -999 -4 -0 - - -UI:AuditMenu - -./audit.php - - -application -4 -5 -0 - - -UI:ChangeManagementMenu - -./UI.php - - -application -7 -0 -0 - - -UI:CircuitsMenu - -UI.php - - -application -999 -4 -0 - - -UI:ClosedChangesMenu - -UI.php - - -application -5 -3 -0 - - -UI:ClosedIncidentsMenu - -./UI.php - - -application -5 -10 -0 - - -UI:ConfigurationItemsMenu - -UI.php - - -application -2 -5 -0 - - -UI:ConfigurationManagementMenu - -UI.php - - -application -2 -0 -0 - - -UI:ContactsMenu - -UI.php - - -application -1 -5 -0 - - -UI:ContractsMenu - -./UI.php - - -application -2 -14 -0 - - -UI:CSVImportMenu - -csvimport.php - - -application -999 -15 -0 - - -UI:DataModelMenu - -schema.php - - -administrator -1500 -1 -0 - - -UI:DocumentsMenu - -UI.php - - -application -6 -5 -0 - - -UI:ExportMenu - -../webservices/export.php - - -administrator -1001 -1 -0 - - -UI:GroupingMenu - -UI.php - - -application -3 -5 -0 - - -UI:IncidentManagementMenu - -./UI.php - - -application -5 -0 -0 - - -UI:InterfacesMenu - -UI.php - - -application -999 -4 -0 - - -UI:KnownErrorsMenu - -./UI.php - - -application -999 -10 -0 - - -UI:LocationsMenu - -UI.php - - -application -5 -5 -0 - - -UI:MyChangesMenu - -UI.php - - -application -1 -3 -0 - - -UI:MyIncidentsMenu - -UI.php - - -application -1 -10 -0 - - -UI:MyServiceCallsMenu - -UI.php - - -application -1 -13 -0 - - -UI:NetworkDevicesMenu - -UI.php - - -application -999 -4 -0 - - -UI:NotificationsMenu - -UI.php - - -administrator -2 -1 -0 - - -UI:OpenChangesMenu - -./UI.php - - -application -3 -3 -0 - - -UI:OpenIncidentsMenu - -UI.php - - -application -3 -10 -0 - - -UI:OpenServiceCallsMenu - -UI.php - - -application -2 -13 -0 - - -UI:PatchesMenu - -UI.php - - -application -999 -4 -0 - - -UI:PCsMenu - -UI.php - - -application -999 -4 -0 - - -UI:PersonsMenu - -UI.php - - -application -7 -6 -0 - - -UI:ProfilesMenu - -UI.php -../images/std_view.gif - -administrator -11 -45 -0 - - -UI:RunQueriesMenu - -./run_query.php - - -administrator -1002 -1 -0 - - -UI:ScheduledOutagesMenu - -./UI.php - - -application -7 -3 -0 - - -UI:ServersMenu - -UI.php - - -application -999 -4 -0 - - -UI:ServiceDeskMenu - -UI.php - - -application -3 -0 -0 - - -UI:ServiceManagementMenu - -./UI.php - - -application -8 -0 -0 - - -UI:ServicesMenu - -UI.php - - -application -1 -14 -0 - - -UI:SubnetsMenu - -UI.php - - -application -999 -4 -0 - - -UI:TeamsMenu - -UI.php - - -application -8 -6 -0 - - -UI:UniversalSearchMenu - -UniversalSearch.php - - -administrator -1600 -1 -0 - - -UI:UserAccountsMenu - -UI.php -../images/std_view.gif - -administrator -10 -45 -0 - - -UI:UserManagementMenu - -UI.php - - -administrator -1 -1 -0 - - -UI:WelcomeMenu - -./UI.php - - -application -1 -0 -0 - - \ No newline at end of file