diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 0144209b3..c01879c84 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -37,6 +37,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage private $m_sMessage; private $m_sInitScript; protected $m_oTabs; + protected $bBreadCrumbEnabled; protected $sBreadCrumbEntryId; protected $sBreadCrumbEntryLabel; protected $sBreadCrumbEntryDescription; @@ -50,11 +51,15 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker'); - $this->sBreadCrumbEntryId = null; - $this->sBreadCrumbEntryLabel = null; - $this->sBreadCrumbEntryDescription = null; - $this->sBreadCrumbEntryUrl = null; - $this->sBreadCrumbEntryIcon = ''; + if ((count($_POST) == 0) || (array_key_exists('loginop', $_POST))) + { + // Create a breadcrumb entry for the current page, but get its title as late as possible (page title could be changed later) + $this->bBreadCrumbEnabled = true; + } + else + { + $this->bBreadCrumbEnabled = false; + } $this->m_sMenu = ""; $this->m_sMessage = ''; @@ -107,45 +112,44 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage } } - protected function PrepareLayout() + protected function IsMenuPaneVisible() { - $bForceMenuPane = utils::ReadParam('force_menu_pane', null); - $sInitClosed = ''; - if (($bForceMenuPane !== null) && ($bForceMenuPane == 0)) - { - $sInitClosed = 'initClosed: true,'; - } - - $this->add_script( -<<Get('demo_mode')) { // Leave the pane opened + } + else + { + if (utils::ReadParam('force_menu_pane', null) === 0) + { + $bLeftPaneOpen = false; + } + elseif (appUserPreferences::GetPref('menu_pane', 'open') == 'closed') + { + $bLeftPaneOpen = false; + } + } + return $bLeftPaneOpen; + } + + protected function PrepareLayout() + { + $bLeftPaneOpen = true; + if (MetaModel::GetConfig()->Get('demo_mode')) + { + // No pin button $sConfigureWestPane = ''; } else { $sConfigureWestPane = <<IsMenuPaneVisible() ? '' : 'initClosed: true,'; - $sJSDisconnectedMessage = json_encode(Dict::S('UI:DisconnectedDlgMessage')); $sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle')); $sJSLoginAgain = json_encode(Dict::S('UI:LoginAgain')); @@ -172,11 +176,12 @@ EOF; paneSize = GetUserPreference('menu_size', 300) myLayout = $('body').layout({ west : { - $sInitClosed minSize: 200, size: paneSize, spacing_open: 16, spacing_close: 16, slideTrigger_open: "mouseover", hideTogglerOnSlide: true, enableCursorHotkey: false, + $sInitClosed minSize: 200, size: paneSize, spacing_open: 16, spacing_close: 16, slideTrigger_open: "click", hideTogglerOnSlide: true, enableCursorHotkey: false, onclose_end: function(name, elt, state, options, layout) { if (state.isSliding == false) { + $('.menu-pane-exclusive').show(); SetUserPreference('menu_pane', 'closed', true); } }, @@ -192,6 +197,7 @@ EOF; { if (state.isSliding == false) { + $('.menu-pane-exclusive').hide(); SetUserPreference('menu_pane', 'open', true); } } @@ -570,6 +576,7 @@ EOF */ public function SetBreadCrumbEntry($sId, $sLabel, $sDescription, $sUrl = '', $sIcon = '') { + $this->bBreadCrumbEnabled = true; $this->sBreadCrumbEntryId = $sId; $this->sBreadCrumbEntryLabel = $sLabel; $this->sBreadCrumbEntryDescription = $sDescription; @@ -577,6 +584,19 @@ EOF $this->sBreadCrumbEntryIcon = $sIcon; } + /** + * State that there will be no breadcrumb item for the current page + */ + public function DisableBreadCrumb() + { + $this->bBreadCrumbEnabled = false; + $this->sBreadCrumbEntryId = null; + $this->sBreadCrumbEntryLabel = null; + $this->sBreadCrumbEntryDescription = null; + $this->sBreadCrumbEntryUrl = null; + $this->sBreadCrumbEntryIcon = null; + } + public function AddToMenu($sHtml) { $this->m_sMenu .= $sHtml; @@ -701,9 +721,6 @@ EOF 'selectedList' => 1, ); $sJSMultiselectOptions = json_encode($aMultiselectOptions); - - $siTopInstanceId = json_encode(APPROOT); - $sNewEntry = is_null($this->sBreadCrumbEntryId) ? 'null' : json_encode(array('id' => $this->sBreadCrumbEntryId, 'url' => $this->sBreadCrumbEntryUrl, 'label' => htmlentities($this->sBreadCrumbEntryLabel, ENT_QUOTES, 'UTF-8'), 'description' => htmlentities($this->sBreadCrumbEntryDescription, ENT_QUOTES, 'UTF-8'), 'icon' => $this->sBreadCrumbEntryIcon)); $this->add_ready_script( <<Get('breadcrumb.max_count'); + if ($iBreadCrumbMaxCount > 1) + { + $siTopInstanceId = json_encode(APPROOT); + if ($this->bBreadCrumbEnabled) + { + if (is_null($this->sBreadCrumbEntryId)) + { + $this->sBreadCrumbEntryId = $this->s_title; + $this->sBreadCrumbEntryLabel = $this->s_title; + $this->sBreadCrumbEntryDescription = $this->s_title; + $this->sBreadCrumbEntryUrl = ''; + $this->sBreadCrumbEntryIcon = utils::GetAbsoluteUrlAppRoot().'images/wrench.png'; + } + $sNewEntry = json_encode(array('id' => $this->sBreadCrumbEntryId, 'url' => $this->sBreadCrumbEntryUrl, 'label' => htmlentities($this->sBreadCrumbEntryLabel, ENT_QUOTES, 'UTF-8'), 'description' => htmlentities($this->sBreadCrumbEntryDescription, ENT_QUOTES, 'UTF-8'), 'icon' => $this->sBreadCrumbEntryIcon)); + } + else + { + $sNewEntry = 'null'; + } + + $this->add_ready_script( +<<GetOutputFormat() == 'html') { foreach($this->a_headers as $s_header) @@ -1009,8 +1052,18 @@ EOF $sHtml .= '
'; $sHtml .= self::FilterXSS($sApplicationBanner); + $GoHomeInitialStyle = $this->IsMenuPaneVisible() ? 'display: none;' : ''; + $sHtml .= ' '; $sHtml .= ' '; + $sHtml .= ' '; + $sHtml .= ' '; + $sHtml .= ' '; $sHtml .= ' '; diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 409560aa4..c8c817a04 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -906,6 +906,14 @@ class Config 'source_of_value' => '', 'show_in_conf_sample' => true, ), + 'breadcrumb.max_count' => array( + 'type' => 'integer', + 'description' => 'Maximum number of items kept in the history breadcrumb. Set it to 0 to entirely disable the breadcrumb.', + 'default' => 8, + 'value' => 8, + 'source_of_value' => '', + 'show_in_conf_sample' => false, + ), ); public function IsProperty($sPropCode) diff --git a/css/light-grey.css b/css/light-grey.css index aaa12fbc0..2f1b95b81 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -2202,6 +2202,32 @@ span.refresh-button { #top-bar-table { width: 100%; + padding-left: 5px; +} + +#top-bar-table #open-left-pane { + text-align: center; + width: 40px !important; + cursor: pointer; +} + +#top-bar-table #go-home { + text-align: center; + width: 40px !important; +} +#top-bar-table #go-home a { + text-decoration: none; + color: #555555; + font-size: 9pt; + padding: 0; + background: none; + display: inline-block; + line-height: 55px; + width: 100%; +} + +#top-bar-table .top-bar-spacer { + width: 35px !important; } #top-bar-table #top-bar-table-search { @@ -2213,13 +2239,16 @@ span.refresh-button { overflow: hidden; float: left; background: #f1f1f1; - margin-left: -20px; +} + +#itop-breadcrumb ul { + display: inline; } #itop-breadcrumb li { list-style: none; float: left; - margin: 0 22px 6px 0; + margin: 0 22px 0px 0; } #itop-breadcrumb li .icon img { height: 15px; @@ -2231,11 +2260,6 @@ span.refresh-button { filter: url("data:image/svg+xml;utf8,#greyscale"); opacity: 0.5; } -#itop-breadcrumb li:hover .icon img { - -webkit-filter: none; - filter: none; - opacity: 1; -} #itop-breadcrumb li a { text-decoration: none; color: #555555; @@ -2243,6 +2267,11 @@ span.refresh-button { padding: 0; background: none; } +#itop-breadcrumb li a:hover .icon img { + -webkit-filter: none; + filter: none; + opacity: 1; +} #itop-breadcrumb li a span.truncate { max-width: 200px; white-space: nowrap; @@ -2266,6 +2295,20 @@ span.refresh-button { #itop-breadcrumb li:last-child a::after { display: none; } +#itop-breadcrumb li .itop-breadcrumb-current { + text-decoration: none; + color: #555555; + font-size: 9pt; + padding: 0; + background: none; +} +#itop-breadcrumb li .itop-breadcrumb-current span.truncate { + max-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; +} .ui-datepicker-buttonpane, .ui-timepicker-div { diff --git a/css/light-grey.scss b/css/light-grey.scss index 07be67338..f0dd0fbb3 100644 --- a/css/light-grey.scss +++ b/css/light-grey.scss @@ -1622,6 +1622,37 @@ span.refresh-button { #top-bar-table { width: 100%; + padding-left: 5px; + + $top-button-width: 40px; + $top-button-heigth: 55px; + $top-button-spacer: 35px; + + #open-left-pane { + text-align: center; + width: $top-button-width !important; + cursor: pointer; + } + #go-home { + text-align: center; + width: $top-button-width !important; + + a { + text-decoration: none; + color: #555; + font-size: 9pt; + padding: 0; + background: none; + + // Make the whole cell clickable + display: inline-block; + line-height: $top-button-heigth; + width: 100%; + } + } + .top-bar-spacer{ + width: $top-button-spacer !important; + } #top-bar-table-search{ width: 347px; } @@ -1631,12 +1662,15 @@ span.refresh-button { overflow: hidden; float: left; background: $frame-background-color; - margin-left: -20px; + + ul { + display: inline; + } li{ list-style: none; float: left; - margin: 0 22px 6px 0; + margin: 0 22px 0px 0; .icon img{ height: 15px; @@ -1652,18 +1686,19 @@ span.refresh-button { opacity: 0.5; } - &:hover .icon img{ - -webkit-filter: none; - filter: none; - opacity: 1; - } - a{ text-decoration: none; color: #555; font-size: 9pt; padding: 0; background: none; + + &:hover .icon img{ + -webkit-filter: none; + filter: none; + opacity: 1; + } + span.truncate { // Ellipsis @@ -1693,6 +1728,24 @@ span.refresh-button { &:last-child a::after{ display: none; } + + .itop-breadcrumb-current{ + text-decoration: none; + color: #555; + font-size: 9pt; + padding: 0; + background: none; + span.truncate + { + // Ellipsis + max-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + } + + } } } .ui-datepicker-buttonpane, .ui-timepicker-div { diff --git a/images/home.png b/images/home.png new file mode 100644 index 000000000..e7efb8717 Binary files /dev/null and b/images/home.png differ diff --git a/images/menu.png b/images/menu.png new file mode 100644 index 000000000..7bd291559 Binary files /dev/null and b/images/menu.png differ diff --git a/images/wrench.png b/images/wrench.png new file mode 100644 index 000000000..31a01c464 Binary files /dev/null and b/images/wrench.png differ diff --git a/js/breadcrumb.js b/js/breadcrumb.js index fa26c9ef1..098232f56 100644 --- a/js/breadcrumb.js +++ b/js/breadcrumb.js @@ -11,6 +11,7 @@ $(function() { itop_instance_id: '', new_entry: null, + max_count: 8 }, // the constructor @@ -23,14 +24,12 @@ $(function() if(typeof(Storage) !== "undefined") { - var sBreadCrumbStorageKey = this.options.itop_instance_id + 'breadcrumb-v1'; - var aBreadCrumb = []; - var sBreadCrumbData = sessionStorage.getItem(sBreadCrumbStorageKey); - if (sBreadCrumbData !== null) + $(window).bind( 'hashchange', function(e) { - aBreadCrumb = JSON.parse(sBreadCrumbData); - } - var iDisplayableItems = aBreadCrumb.length; + me._RefreshLatestEntry(); + }); + + aBreadCrumb = this._Read(); if (this.options.new_entry !== null) { var sUrl = this.options.new_entry.url; @@ -49,13 +48,10 @@ $(function() icon: this.options.new_entry.icon, url: sUrl }); - // Keep only the last N items - aBreadCrumb = aBreadCrumb.slice(-8); - // Do not show the last = current item - iDisplayableItems = aBreadCrumb.length - 1; + // Keep only the last items + aBreadCrumb = aBreadCrumb.slice(-(this.options.max_count)); } - sBreadCrumbData = JSON.stringify(aBreadCrumb); - sessionStorage.setItem(sBreadCrumbStorageKey, sBreadCrumbData); + this._Write(aBreadCrumb); var sBreadCrumbHtml = '
    '; for (iEntry in aBreadCrumb) { @@ -72,7 +68,15 @@ $(function() if (sTitle.length == 0) { sTitle = oEntry['label']; } - sBreadCrumbHtml += '
  • '+sIconSpec+''+oEntry['label']+'
  • '; + if ((this.options.new_entry !== null) && (iEntry == aBreadCrumb.length - 1)) + { + // Last entry is the current page + sBreadCrumbHtml += '
  • '+sIconSpec+''+oEntry['label']+'
  • '; + } + else + { + sBreadCrumbHtml += '
  • '+sIconSpec+''+oEntry['label']+'
  • '; + } } } sBreadCrumbHtml += '
'; @@ -106,6 +110,36 @@ $(function() _setOption: function( key, value ) { this._super( key, value ); + }, + _Read: function() + { + var sBreadCrumbStorageKey = this.options.itop_instance_id + 'breadcrumb-v1'; + var aBreadCrumb = []; + var sBreadCrumbData = sessionStorage.getItem(sBreadCrumbStorageKey); + if (sBreadCrumbData !== null) + { + aBreadCrumb = JSON.parse(sBreadCrumbData); + } + return aBreadCrumb; + }, + _Write: function(aBreadCrumb) + { + var sBreadCrumbStorageKey = this.options.itop_instance_id + 'breadcrumb-v1'; + sBreadCrumbData = JSON.stringify(aBreadCrumb); + sessionStorage.setItem(sBreadCrumbStorageKey, sBreadCrumbData); + }, + // Refresh the latest entry (navigating to a tab) + _RefreshLatestEntry: function() + { + aBreadCrumb = this._Read(); + var iDisplayableItems = aBreadCrumb.length; + + if (this.options.new_entry !== null) { + // The current page is the last entry in the breadcrumb, let's refresh it + aBreadCrumb[aBreadCrumb.length - 1].url = window.location.href; + $('#itop-breadcrumb li:last-of-type a').attr('href', window.location.href); + } + this._Write(aBreadCrumb); } }); }); diff --git a/pages/UI.php b/pages/UI.php index 145e950f6..f91e4c769 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -319,7 +319,7 @@ try $oP->add_linked_script("../js/linksdirectwidget.js"); $oP->add_linked_script("../js/extkeywidget.js"); $oP->add_linked_script("../js/jquery.blockUI.js"); - break; + break; } switch($operation) @@ -363,13 +363,19 @@ try if (!is_null($oObj)) { $sClass = get_class($oObj); // get the leaf class - $oP->SetBreadCrumbEntry("ui-details-$sClass-$id", $oObj->Get('friendlyname'), $sClass.': '.$oObj->Get('friendlyname'), '', MetaModel::GetClassIcon($sClass, false)); + $sIcon = MetaModel::GetClassIcon($sClass, false); + if ($sIcon == '') + { + $sIcon = utils::GetAbsoluteUrlAppRoot().'images/breadcrumb_object.png'; + } + $oP->SetBreadCrumbEntry("ui-details-$sClass-$id", $oObj->Get('friendlyname'), MetaModel::GetName($sClass).': '.$oObj->Get('friendlyname'), '', $sIcon); DisplayDetails($oP, $sClass, $oObj, $id); } } break; case 'release_lock_and_details': + $oP->DisableBreadCrumb(); $sClass = utils::ReadParam('class', ''); $id = utils::ReadParam('id', ''); $oObj = MetaModel::GetObject($sClass, $id); @@ -561,6 +567,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'modify': // Form to modify an object + $oP->DisableBreadCrumb(); $sClass = utils::ReadParam('class', '', false, 'class'); $id = utils::ReadParam('id', ''); if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! @@ -590,6 +597,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'select_for_modify_all': // Select the list of objects to be modified (bulk modify) + $oP->DisableBreadCrumb(); $oP->set_title(Dict::S('UI:ModifyAllPageTitle')); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); if (empty($sFilter)) @@ -607,6 +615,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'form_for_modify_all': // Form to modify multiple objects (bulk modify) + $oP->DisableBreadCrumb(); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); $sClass = utils::ReadParam('class', '', false, 'class'); $oFullSetFilter = DBObjectSearch::unserialize($sFilter); @@ -619,6 +628,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'preview_or_modify_all': // Preview or apply bulk modify + $oP->DisableBreadCrumb(); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); // TO DO: limit the search filter by the user context $oFilter = DBObjectSearch::unserialize($sFilter); // TO DO : check that the filter is valid @@ -643,6 +653,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'new': // Form to create a new object + $oP->DisableBreadCrumb(); $sClass = utils::ReadParam('class', '', false, 'class'); $sStateCode = utils::ReadParam('state', ''); $bCheckSubClass = utils::ReadParam('checkSubclass', true); @@ -757,6 +768,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'apply_modify': // Applying the modifications to an existing object + $oP->DisableBreadCrumb(); $sClass = utils::ReadPostedParam('class', ''); $sClassLabel = MetaModel::GetName($sClass); $id = utils::ReadPostedParam('id', ''); @@ -860,6 +872,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'select_for_deletion': // Select multiple objects for deletion + $oP->DisableBreadCrumb(); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); if (empty($sFilter)) { @@ -876,6 +889,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'bulk_delete_confirmed': // Confirm bulk deletion of objects + $oP->DisableBreadCrumb(); $sTransactionId = utils::ReadPostedParam('transaction_id', ''); if (!utils::IsTransactionValid($sTransactionId)) { @@ -887,6 +901,7 @@ EOF case 'delete': case 'bulk_delete': // Actual bulk deletion (if confirmed) + $oP->DisableBreadCrumb(); $sClass = utils::ReadParam('class', '', false, 'class'); $sClassLabel = MetaModel::GetName($sClass); $aObjects = array(); @@ -938,6 +953,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'apply_new': // Creation of a new object + $oP->DisableBreadCrumb(); $sClass = utils::ReadPostedParam('class', '', 'class'); $sClassLabel = MetaModel::GetName($sClass); $sTransactionId = utils::ReadPostedParam('transaction_id', ''); @@ -1008,6 +1024,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'select_bulk_stimulus': // Form displayed when applying a stimulus to many objects + $oP->DisableBreadCrumb(); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); $sStimulus = utils::ReadParam('stimulus', ''); $sState = utils::ReadParam('state', ''); @@ -1031,6 +1048,7 @@ EOF break; case 'bulk_stimulus': + $oP->DisableBreadCrumb(); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); $sStimulus = utils::ReadParam('stimulus', ''); $sState = utils::ReadParam('state', ''); @@ -1196,6 +1214,7 @@ EOF break; case 'bulk_apply_stimulus': + $oP->DisableBreadCrumb(); $bPreviewMode = utils::ReadPostedParam('preview_mode', false); $sFilter = utils::ReadPostedParam('filter', '', false, 'raw_data'); $sStimulus = utils::ReadPostedParam('stimulus', ''); @@ -1329,6 +1348,7 @@ EOF break; case 'stimulus': // Form displayed when applying a stimulus (state change) + $oP->DisableBreadCrumb(); $sClass = utils::ReadParam('class', '', false, 'class'); $id = utils::ReadParam('id', ''); $sStimulus = utils::ReadParam('stimulus', ''); @@ -1351,6 +1371,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'apply_stimulus': // Actual state change + $oP->DisableBreadCrumb(); $sClass = utils::ReadPostedParam('class', ''); $id = utils::ReadPostedParam('id', ''); $sTransactionId = utils::ReadPostedParam('transaction_id', ''); @@ -1489,14 +1510,19 @@ EOF $sRelation = utils::ReadParam('relation', 'impact'); $sDirection = utils::ReadParam('direction', 'down'); $iGroupingThreshold = utils::ReadParam('g', 5); - + $oObj = MetaModel::GetObject($sClass, $id); $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth', 20); $aSourceObjects = array($oObj); - + $oP->set_title(MetaModel::GetRelationDescription($sRelation).' '.$oObj->GetName()); - - if ($sRelation == 'depends on') + + $sPageId = "ui-relation-graph-".$sClass.'::'.$id; + $sLabel = $oObj->GetName().' '.MetaModel::GetRelationLabel($sRelation); + $sDescription = MetaModel::GetRelationDescription($sRelation).' '.$oObj->GetName(); + $oP->SetBreadCrumbEntry($sPageId, $sLabel, $sDescription); + + if ($sRelation == 'depends on') { $sRelation = 'impacts'; $sDirection = 'up'; @@ -1561,6 +1587,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'kill_lock': + $oP->DisableBreadCrumb(); $sClass = utils::ReadParam('class', ''); $id = utils::ReadParam('id', ''); iTopOwnershipLock::KillLock($sClass, $id); @@ -1571,6 +1598,7 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// case 'cancel': // An action was cancelled + $oP->DisableBreadCrumb(); $oP->set_title(Dict::S('UI:OperationCancelled')); $oP->add('

'.Dict::S('UI:OperationCancelled').'

'); break; @@ -1582,7 +1610,6 @@ EOF $oMenuNode = ApplicationMenu::GetMenuNode(ApplicationMenu::GetMenuIndexById(ApplicationMenu::GetActiveNodeId())); if (is_object($oMenuNode)) { - $oMenuNode->RenderContent($oP, $oAppContext->GetAsHash()); $oP->set_title($oMenuNode->GetLabel()); } diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php index d2d208898..b092d96f2 100644 --- a/pages/UniversalSearch.php +++ b/pages/UniversalSearch.php @@ -54,6 +54,8 @@ $sOQLClause = utils::ReadParam('oql_clause', '', false, 'raw_data'); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); $sOperation = utils::ReadParam('operation', ''); +$oP->SetBreadCrumbEntry('ui-tool-universalsearch', Dict::S('Menu:UniversalSearchMenu'), Dict::S('Menu:UniversalSearchMenu+'), '', utils::GetAbsoluteUrlAppRoot().'images/wrench.png'); + // First part: select the class to search for $oP->add("
"); $oP->add(Dict::S('UI:UniversalSearch:LabelSelectTheClass')."
'; $sHtml .= '
'; $sHtml .= '