\n";
+
+ echo "\n";
+ }
+
//echo $this->s_deferred_content;
if (count($this->a_scripts) > 0)
{
@@ -240,6 +265,7 @@ EOF
echo $this->m_sReadyScript; // Ready Scripts are output as simple scripts
echo "\n\n";
}
+
if (trim($s_captured_output) != "")
{
echo self::FilterXSS($s_captured_output);
diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php
index 10df7c912..5b430845b 100644
--- a/application/displayblock.class.inc.php
+++ b/application/displayblock.class.inc.php
@@ -1614,6 +1614,7 @@ class ExtraMenus implements iPopupMenuExtension
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?body=".urlencode($sUrl)),
new URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), $sUrl."&format=csv"),
new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')"),
+ new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sContext')"),
);
break;
diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php
index 2f2ecee85..024279d8a 100644
--- a/application/menunode.class.inc.php
+++ b/application/menunode.class.inc.php
@@ -80,6 +80,15 @@ class ApplicationMenu
call_user_func($aCallSpec);
}
}
+
+ // Build menus from the menus themselves (e.g. the ShortcutContainerMenuNode will do that)
+ //
+ foreach(self::$aRootMenus as $aMenu)
+ {
+ $oMenuNode = self::GetMenuNode($aMenu['index']);
+ $oMenuNode->PopulateChildMenus();
+ }
+
self::$bAdditionalMenusLoaded = true;
}
}
@@ -131,7 +140,7 @@ class ApplicationMenu
// they were not used to display the menus (redundant or unused)
//
$aBacktrace = debug_backtrace();
- $sFile = $aBacktrace[2]["file"];
+ $sFile = isset($aBacktrace[2]["file"]) ? $aBacktrace[2]["file"] : $aBacktrace[1]["file"];
self::$aMenusIndex[$index] = array('node' => $oMenuNode, 'children' => array(), 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile);
}
else
@@ -154,7 +163,7 @@ class ApplicationMenu
/**
* Entry point to display the whole menu into the web page, used by iTopWebPage
*/
- static public function DisplayMenu(iTopWebPage $oPage, $aExtraParams)
+ static public function DisplayMenu($oPage, $aExtraParams)
{
self::LoadAdditionalMenus();
// Sort the root menu based on the rank
@@ -253,11 +262,11 @@ class ApplicationMenu
/**
* Helper function to get the list of child(ren) of a menu
*/
- static protected function GetChildren($index)
+ static public 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)
@@ -407,6 +416,16 @@ abstract class MenuNode
return $this->index;
}
+ public function PopulateChildMenus()
+ {
+ foreach (ApplicationMenu::GetChildren($this->GetIndex()) as $aMenu)
+ {
+ $index = $aMenu['index'];
+ $oMenu = ApplicationMenu::GetMenuNode($index);
+ $oMenu->PopulateChildMenus();
+ }
+ }
+
public function GetHyperlink($aExtraParams)
{
$aExtraParams['c[menu]'] = $this->GetMenuId();
@@ -617,31 +636,38 @@ class OQLMenuNode extends MenuNode
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
- $aExtraParams = array_merge($aExtraParams, $this->m_aParams);
- try
- {
- $oSearch = DBObjectSearch::FromOQL($this->sOQL);
- $sIcon = MetaModel::GetClassIcon($oSearch->GetClass());
- }
- catch(Exception $e)
- {
- $sIcon = '';
- }
+ OQLMenuNode::RenderOQLSearch
+ (
+ $this->sOQL,
+ Dict::S($this->sPageTitle),
+ 'Menu_'.$this->GetMenuId(),
+ $this->bSearch, // Search pane
+ true, // Search open
+ $oPage,
+ $aExtraParams
+ );
+ }
- if ($this->bSearch)
+ public static function RenderOQLSearch($sOql, $sTitle, $sUsageId, $bSearchPane, $bSearchOpen, WebPage $oPage, $aExtraParams = array())
+ {
+ $oSearch = DBObjectSearch::FromOQL($sOql);
+ $sIcon = MetaModel::GetClassIcon($oSearch->GetClass());
+
+ if ($bSearchPane)
{
- $aParams = array_merge(array('open' => true, 'table_id' => 'Menu_'.$this->GetMenuId()), $aExtraParams);
+ $aParams = array_merge(array('open' => $bSearchOpen, 'table_id' => $sUsageId), $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, 0);
}
- $oPage->add("
$sIcon ".Dict::S($this->sPageTitle)."
");
+ $oPage->add("
$sIcon ".Dict::S($sTitle)."
");
- $aParams = array_merge(array('table_id' => 'Menu_'.$this->GetMenuId()), $aExtraParams);
+ $aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
- $oBlock->Display($oPage, 1);
+ $oBlock->Display($oPage, $sUsageId);
}
}
+
/**
* This class defines a menu item that displays a search form for the given class of objects
*/
@@ -893,3 +919,95 @@ class DashboardMenuNode extends MenuNode
}
+/**
+ * A shortcut container is the preferred destination of newly created shortcuts
+ */
+class ShortcutContainerMenuNode extends MenuNode
+{
+ public function GetHyperlink($aExtraParams)
+ {
+ return '';
+ }
+
+ public function RenderContent(WebPage $oPage, $aExtraParams = array())
+ {
+ }
+
+ public function PopulateChildMenus()
+ {
+ // Load user shortcuts in DB
+ //
+ $oBMSearch = new DBObjectSearch('Shortcut');
+ $oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
+ $oBMSet = new DBObjectSet($oBMSearch, array('friendlyname' => true)); // ascending on friendlyname
+ $fRank = 1;
+ while ($oShortcut = $oBMSet->Fetch())
+ {
+ $sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
+ $oShortcutMenu = new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
+ }
+
+ // Complete the tree
+ //
+ parent::PopulateChildMenus();
+ }
+}
+
+
+require_once(APPROOT.'application/shortcut.class.inc.php');
+/**
+ * This class defines a menu item which content is a shortcut.
+ */
+class ShortcutMenuNode extends MenuNode
+{
+ protected $oShortcut;
+
+ /**
+ * Create a menu item based on a custom template and inserts it into the application's main menu
+ * @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
+ * @param object $oShortcut Shortcut object
+ * @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
+ * @param string $sEnableClass Name of class of object
+ * @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
+ * @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
+ * @return MenuNode
+ */
+ public function __construct($sMenuId, $oShortcut, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
+ {
+ parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
+ $this->oShortcut = $oShortcut;
+ $this->aReflectionProperties['shortcut'] = $oShortcut->GetKey();
+ }
+
+ public function GetHyperlink($aExtraParams)
+ {
+ $sContext = $this->oShortcut->Get('context');
+ $aContext = unserialize($sContext);
+ if (isset($aContext['menu']))
+ {
+ unset($aContext['menu']);
+ }
+ foreach ($aContext as $sArgName => $sArgValue)
+ {
+ $aExtraParams[$sArgName] = $sArgValue;
+ }
+ return parent::GetHyperlink($aExtraParams);
+ }
+
+ public function RenderContent(WebPage $oPage, $aExtraParams = array())
+ {
+ $this->oShortcut->RenderContent($oPage, $aExtraParams);
+ }
+
+ public function GetTitle()
+ {
+ return $this->oShortcut->Get('name');
+ }
+
+ public function GetLabel()
+ {
+ return $this->oShortcut->Get('name');
+ }
+}
+
diff --git a/application/shortcut.class.inc.php b/application/shortcut.class.inc.php
new file mode 100644
index 000000000..1fdb5d9a1
--- /dev/null
+++ b/application/shortcut.class.inc.php
@@ -0,0 +1,257 @@
+
+
+
+/**
+ * Persistent class Shortcut and derived
+ * Shortcuts of any kind
+ *
+ * @copyright Copyright (C) 2010-2012 Combodo SARL
+ * @license http://opensource.org/licenses/AGPL-3.0
+ */
+
+abstract class Shortcut extends cmdbAbstractObject
+{
+ public static function Init()
+ {
+ $aParams = array
+ (
+ "category" => "core/cmdb,view_in_gui,application",
+ "key_type" => "autoincrement",
+ "name_attcode" => "name",
+ "state_attcode" => "",
+ "reconc_keys" => array(),
+ "db_table" => "priv_shortcut",
+ "db_key_field" => "id",
+ "db_finalclass_field" => "realclass",
+ "display_template" => "",
+ );
+ MetaModel::Init_Params($aParams);
+ //MetaModel::Init_InheritAttributes();
+ MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
+ MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+ MetaModel::Init_AddAttribute(new AttributeText("context", array("allowed_values"=>null, "sql"=>"context", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+
+ // Display lists
+ MetaModel::Init_SetZListItems('details', array('name', 'context')); // Attributes to be displayed for the complete details
+ MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
+ // Search criteria
+// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
+// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
+ }
+
+ abstract public function RenderContent(WebPage $oPage, $aExtraParams = array());
+
+ protected function OnInsert()
+ {
+ $this->Set('user_id', UserRights::GetUserId());
+ }
+
+ public function StartRenameDialog($oPage)
+ {
+ $oPage->add('