diff --git a/application/ajaxwebpage.class.inc.php b/application/ajaxwebpage.class.inc.php index a3b33cdaa..fc7250b34 100644 --- a/application/ajaxwebpage.class.inc.php +++ b/application/ajaxwebpage.class.inc.php @@ -26,16 +26,14 @@ require_once(APPROOT."/application/webpage.class.inc.php"); -class ajax_page extends WebPage +class ajax_page extends WebPage implements iTabbedPage { /** * Jquery style ready script * @var Hash */ protected $m_sReadyScript; - protected $m_sCurrentTab; - protected $m_sCurrentTabContainer; - protected $m_aTabs; + protected $m_oTabs; private $m_sMenu; // If set, then the menu will be updated /** @@ -48,9 +46,7 @@ class ajax_page extends WebPage $this->m_sReadyScript = ""; //$this->add_header("Content-type: text/html; charset=utf-8"); $this->add_header("Cache-control: no-cache"); - $this->m_sCurrentTabContainer = ''; - $this->m_sCurrentTab = ''; - $this->m_aTabs = array(); + $this->m_oTabs = new TabManager(); $this->sContentType = 'text/html'; $this->sContentDisposition = 'inline'; $this->m_sMenu = ""; @@ -58,41 +54,69 @@ class ajax_page extends WebPage public function AddTabContainer($sTabContainer, $sPrefix = '') { - $this->m_aTabs[$sTabContainer] = array('content' =>'', 'prefix' => $sPrefix); - $this->add("\$Tabs:$sTabContainer\$"); + $this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix)); } - + public function AddToTab($sTabContainer, $sTabLabel, $sHtml) { - if (!isset($this->m_aTabs[$sTabContainer]['content'][$sTabLabel])) - { - // Set the content of the tab - $this->m_aTabs[$sTabContainer]['content'][$sTabLabel] = $sHtml; - } - else - { - // Append to the content of the tab - $this->m_aTabs[$sTabContainer]['content'][$sTabLabel] .= $sHtml; - } + $this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabLabel, $sHtml)); } public function SetCurrentTabContainer($sTabContainer = '') { - $sPreviousTabContainer = $this->m_sCurrentTabContainer; - $this->m_sCurrentTabContainer = $sTabContainer; - return $sPreviousTabContainer; + return $this->m_oTabs->SetCurrentTabContainer($sTabContainer); } public function SetCurrentTab($sTabLabel = '') { - $sPreviousTab = $this->m_sCurrentTab; - $this->m_sCurrentTab = $sTabLabel; - return $sPreviousTab; + return $this->m_oTabs->SetCurrentTab($sTabLabel); + } + + /** + * Add a tab which content will be loaded asynchronously via the supplied URL + * + * Limitations: + * Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server. + * Static content cannot be added inside such tabs. + * + * @param string $sTabLabel The (localised) label of the tab + * @param string $sUrl The URL to load (on the same server) + * @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation. + * @since 2.0.3 + */ + public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true) + { + $this->add($this->m_oTabs->AddAjaxTab($sTabLabel, $sUrl, $bCache)); } public function GetCurrentTab() { - return $this->m_sCurrentTab; + return $this->m_oTabs->GetCurrentTab(); + } + + public function RemoveTab($sTabLabel, $sTabContainer = null) + { + $this->m_oTabs->RemoveTab($sTabLabel, $sTabContainer); + } + + /** + * Finds the tab whose title matches a given pattern + * @return mixed The name of the tab as a string or false if not found + */ + public function FindTab($sPattern, $sTabContainer = null) + { + return $this->m_oTabs->FindTab($sPattern, $sTabContainer); + } + + /** + * Make the given tab the active one, as if it were clicked + * DOES NOT WORK: apparently in the *old* version of jquery + * that we are using this is not supported... TO DO upgrade + * the whole jquery bundle... + */ + public function SelectTab($sTabContainer, $sTabLabel) + { + $this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel)); } public function AddToMenu($sHtml) @@ -118,7 +142,7 @@ class ajax_page extends WebPage { header($s_header); } - if (count($this->m_aTabs) > 0) + if ($this->m_oTabs->TabsContainerCount() > 0) { $this->add_ready_script( <<m_aTabs as $sTabContainerName => $aTabContainer) - { - $sTabs = ''; - $m_aTabs = $aTabContainer['content']; - $sPrefix = $aTabContainer['prefix']; - $container_index = 0; - if (count($m_aTabs) > 0) - { - $sTabs = "\n
\n"; - $sTabs .= "\n"; - // Now add the content of the tabs themselves - $i = 0; - foreach($m_aTabs as $sTabName => $sTabContent) - { - $sTabs .= "
".$sTabContent."
\n"; - $i++; - } - $sTabs .= "
\n\n"; - } - $this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content); - $container_index++; - } + $this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content); // Additional UI widgets to be activated inside the ajax fragment ?? if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) ) diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 54680443b..c6af15bdc 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -1,5 +1,5 @@ m_oTabs = new TabManager(); ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker'); - $this->m_sCurrentTabContainer = ''; - $this->m_sCurrentTab = ''; - $this->m_aTabs = array(); $this->m_sMenu = ""; $this->m_sMessage = ''; $this->SetRootUrl(utils::GetAbsoluteUrlAppRoot()); @@ -203,16 +199,31 @@ EOF; // unless their URL is equal to the URL of the page... $('div[id^=tabbedContent] > ul > li > a').each(function() { var sHash = location.hash; - var sCleanLocation = location.href.toString().replace(sHash, '').replace(/#$/, ''); - $(this).attr("href", sCleanLocation+$(this).attr("href")); + var sHref = $(this).attr("href"); + if (sHref.match(/^#/)) + { + var sCleanLocation = location.href.toString().replace(sHash, '').replace(/#$/, ''); + $(this).attr("href", sCleanLocation+$(this).attr("href")); + } }); // Enable tabs on all tab widgets. The `event` property must be overridden so // that the tabs aren't changed on click, and any custom event name can be // specified. Note that if you define a callback for the 'select' event, it // will be executed for the selected tab whenever the hash changes. - tabs.tabs({ event: 'change', 'show': function(event, ui) { + tabs.tabs({ + event: 'change', 'show': function(event, ui) { $('.resizable', ui.panel).resizable(); // Make resizable everything that claims to be resizable ! + }, + beforeLoad: function( event, ui ) { + if ( ui.tab.data('loaded') && (ui.tab.attr('data-cache') == 'true')) { + event.preventDefault(); + return; + } + + ui.jqXHR.success(function() { + ui.tab.data( "loaded", true ); + }); } }); @@ -551,7 +562,7 @@ EOF $sNorthPane .= '
'.ExecutionKPI::GetDescription().'
'; } - $sSouthPane = ''; + $sSouthPane = '

Peak memory Usage: '.sprintf('%.3f MB', memory_get_peak_usage(true) / (1024*1024)).'

'; foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance) { $sSouthPane .= $oExtensionInstance->GetSouthPaneHtml($this); @@ -674,34 +685,7 @@ EOF $sOnClick = " onclick=\"this.value='';this.onclick=null;\""; } // Render the tabs in the page (if any) - foreach($this->m_aTabs as $sTabContainerName => $m_aTabs) - { - $sTabs = ''; - $container_index = 0; - if (count($m_aTabs) > 0) - { - $sTabs = "\n
\n"; - $sTabs .= "\n"; - // Now add the content of the tabs themselves - $i = 0; - foreach($m_aTabs as $sTabName => $sTabContent) - { - $sTabs .= "
".$sTabContent."
\n"; - $i++; - } - $sTabs .= "
\n\n"; - } - $this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content); - $container_index++; - } + $this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content); if ($this->GetOutputFormat() == 'html') { @@ -892,62 +876,51 @@ EOF ExecutionKPI::ReportStats(); } - public function AddTabContainer($sTabContainer) + public function AddTabContainer($sTabContainer, $sPrefix = '') { - $this->m_aTabs[$sTabContainer] = array(); - $this->add("\$Tabs:$sTabContainer\$"); + $this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix)); } public function AddToTab($sTabContainer, $sTabLabel, $sHtml) { - if (!isset($this->m_aTabs[$sTabContainer][$sTabLabel])) - { - // Set the content of the tab - $this->m_aTabs[$sTabContainer][$sTabLabel] = $sHtml; - } - else - { - // Append to the content of the tab - $this->m_aTabs[$sTabContainer][$sTabLabel] .= $sHtml; - } + $this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabLabel, $sHtml)); } public function SetCurrentTabContainer($sTabContainer = '') { - $sPreviousTabContainer = $this->m_sCurrentTabContainer; - $this->m_sCurrentTabContainer = $sTabContainer; - return $sPreviousTabContainer; + return $this->m_oTabs->SetCurrentTabContainer($sTabContainer); } public function SetCurrentTab($sTabLabel = '') { - $sPreviousTab = $this->m_sCurrentTab; - $this->m_sCurrentTab = $sTabLabel; - return $sPreviousTab; + return $this->m_oTabs->SetCurrentTab($sTabLabel); } - + + /** + * Add a tab which content will be loaded asynchronously via the supplied URL + * + * Limitations: + * Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server. + * Static content cannot be added inside such tabs. + * + * @param string $sTabLabel The (localised) label of the tab + * @param string $sUrl The URL to load (on the same server) + * @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation. + * @since 2.0.3 + */ + public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true) + { + $this->add($this->m_oTabs->AddAjaxTab($sTabLabel, $sUrl, $bCache)); + } + public function GetCurrentTab() { - return $this->m_sCurrentTab; + return $this->m_oTabs->GetCurrentTab(); } public function RemoveTab($sTabLabel, $sTabContainer = null) { - if ($sTabContainer == null) - { - $sTabContainer = $this->m_sCurrentTabContainer; - } - if (isset($this->m_aTabs[$sTabContainer][$sTabLabel])) - { - // Delete the content of the tab - unset($this->m_aTabs[$sTabContainer][$sTabLabel]); - - // If we just removed the active tab, let's reset the active tab - if (($this->m_sCurrentTabContainer == $sTabContainer) && ($this->m_sCurrentTab == $sTabLabel)) - { - $this->m_sCurrentTab = ''; - } - } + $this->m_oTabs->RemoveTab($sTabLabel, $sTabContainer); } /** @@ -956,20 +929,7 @@ EOF */ public function FindTab($sPattern, $sTabContainer = null) { - $return = false; - if ($sTabContainer == null) - { - $sTabContainer = $this->m_sCurrentTabContainer; - } - foreach($this->m_aTabs[$sTabContainer] as $sTabLabel => $void) - { - if (preg_match($sPattern, $sTabLabel)) - { - $result = $sTabLabel; - break; - } - } - return $result; + return $this->m_oTabs->FindTab($sPattern, $sTabContainer); } /** @@ -980,26 +940,7 @@ EOF */ public function SelectTab($sTabContainer, $sTabLabel) { - $container_index = 0; - $tab_index = 0; - foreach($this->m_aTabs as $sCurrentTabContainerName => $aTabs) - { - if ($sTabContainer == $sCurrentTabContainerName) - { - foreach($aTabs as $sCurrentTabLabel => $void) - { - if ($sCurrentTabLabel == $sTabLabel) - { - break; - } - $tab_index++; - } - break; - } - $container_index++; - } - $sSelector = '#tabbedContent_'.$container_index.' > ul'; - $this->add_ready_script("window.setTimeout(\"$('$sSelector').tabs('select', $tab_index);\", 100);"); // Let the time to the tabs widget to initialize + $this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel)); } public function StartCollapsibleSection($sSectionLabel, $bOpen = false) @@ -1033,9 +974,9 @@ EOF public function add($sHtml) { - if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab)) + if (!empty($this->m_oTabs->GetCurrentTabContainer()) && !empty($this->m_oTabs->GetCurrentTab())) { - $this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml); + $this->m_oTabs->AddToCurrentTab($sHtml); } else { @@ -1049,10 +990,13 @@ EOF */ public function start_capture() { - if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab)) + $sCurrentTabContainer = $this->m_oTabs->GetCurrentTabContainer(); + $sCurrentTab = $this->m_oTabs->GetCurrentTab(); + + if (!empty($sCurrentTabContainer) && !empty($sCurrentTab)) { - $iOffset = isset($this->m_aTabs[$this->m_sCurrentTabContainer][$this->m_sCurrentTab]) ? strlen($this->m_aTabs[$this->m_sCurrentTabContainer][$this->m_sCurrentTab]): 0; - return array('tc' => $this->m_sCurrentTabContainer, 'tab' => $this->m_sCurrentTab, 'offset' => $iOffset); + $iOffset = $this->m_oTabs->GetCurrentTabLength(); + return array('tc' => $sCurrentTabContainer, 'tab' => $sCurrentTab, 'offset' => $iOffset); } else { @@ -1070,10 +1014,9 @@ EOF { if (is_array($offset)) { - if (isset($this->m_aTabs[$offset['tc']][$offset['tab']])) + if ($this->m_oTabs->TabExists($offset['tc'], $offset['tab'])) { - $sCaptured = substr($this->m_aTabs[$offset['tc']][$offset['tab']], $offset['offset']); - $this->m_aTabs[$offset['tc']][$offset['tab']] = substr($this->m_aTabs[$offset['tc']][$offset['tab']], 0, $offset['offset']); + $sCaptured = $this->m_oTabs->TruncateTab($offset['tc'], $offset['tab'], $offset['offset']); } else { diff --git a/application/webpage.class.inc.php b/application/webpage.class.inc.php index f7518c150..8d7a81e5f 100644 --- a/application/webpage.class.inc.php +++ b/application/webpage.class.inc.php @@ -718,4 +718,282 @@ class WebPage implements Page } } } -?> \ No newline at end of file + + +interface iTabbedPage +{ + public function AddTabContainer($sTabContainer, $sPrefix = ''); + + public function AddToTab($sTabContainer, $sTabLabel, $sHtml); + + public function SetCurrentTabContainer($sTabContainer = ''); + + public function SetCurrentTab($sTabLabel = ''); + + /** + * Add a tab which content will be loaded asynchronously via the supplied URL + * + * Limitations: + * Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server. + * Static content cannot be added inside such tabs. + * + * @param string $sTabLabel The (localised) label of the tab + * @param string $sUrl The URL to load (on the same server) + * @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation. + * @since 2.0.3 + */ + public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true); + + public function GetCurrentTab(); + + public function RemoveTab($sTabLabel, $sTabContainer = null); + + /** + * Finds the tab whose title matches a given pattern + * @return mixed The name of the tab as a string or false if not found + */ + public function FindTab($sPattern, $sTabContainer = null); +} + +/** + * Helper class to implement JQueryUI tabs inside a page + */ +class TabManager +{ + protected $m_aTabs; + protected $m_sCurrentTabContainer; + protected $m_sCurrentTab; + + public function __construct() + { + $this->m_aTabs = array(); + $this->m_sCurrentTabContainer = ''; + $this->m_sCurrentTab = ''; + } + + public function AddTabContainer($sTabContainer, $sPrefix = '') + { + $this->m_aTabs[$sTabContainer] = array('prefix' => $sPrefix, 'tabs' => array()); + return "\$Tabs:$sTabContainer\$"; + } + + public function AddToCurrentTab($sHtml) + { + $this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml); + } + + public function GetCurrentTabLength($sHtml) + { + $iLength = isset($this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html']) ? strlen($this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html']): 0; + return $iLength; + } + + public function TruncateTab($sTabContainer, $sTab, $iLength) + { + $this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html'] = substr($this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$this->m_sCurrentTab]['html'], 0, $iLength); + } + + public function TabExists($sTabContainer, $sTab) + { + return isset($this->m_aTabs[$sTabContainer]['tabs'][$sTab]); + } + + public function TabsContainerCount() + { + return count($this->m_aTabs); + } + + public function AddToTab($sTabContainer, $sTabLabel, $sHtml) + { + if (!isset($this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel])) + { + // Set the content of the tab + $this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel] = array( + 'type' => 'html', + 'html' => $sHtml, + ); + } + else + { + if ($this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]['type'] != 'html') + { + throw new Exception("Cannot add HTML content to the tab '$sTabLabel' of type '{$this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]['type']}'"); + } + // Append to the content of the tab + $this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]['html'] .= $sHtml; + } + return ''; // Nothing to add to the page for now + } + + public function SetCurrentTabContainer($sTabContainer = '') + { + $sPreviousTabContainer = $this->m_sCurrentTabContainer; + $this->m_sCurrentTabContainer = $sTabContainer; + return $sPreviousTabContainer; + } + + public function SetCurrentTab($sTabLabel = '') + { + $sPreviousTab = $this->m_sCurrentTab; + $this->m_sCurrentTab = $sTabLabel; + return $sPreviousTab; + } + + /** + * Add a tab which content will be loaded asynchronously via the supplied URL + * + * Limitations: + * Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server. + * Static content cannot be added inside such tabs. + * + * @param string $sTabLabel The (localised) label of the tab + * @param string $sUrl The URL to load (on the same server) + * @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation. + * @since 2.0.3 + */ + public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true) + { + // Set the content of the tab + $this->m_aTabs[$this->m_sCurrentTabContainer]['tabs'][$sTabLabel] = array( + 'type' => 'ajax', + 'url' => $sUrl, + 'cache' => $bCache, + ); + return ''; // Nothing to add to the page for now + } + + + public function GetCurrentTabContainer() + { + return $this->m_sCurrentTabContainer; + } + + public function GetCurrentTab() + { + return $this->m_sCurrentTab; + } + + public function RemoveTab($sTabLabel, $sTabContainer = null) + { + if ($sTabContainer == null) + { + $sTabContainer = $this->m_sCurrentTabContainer; + } + if (isset($this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel])) + { + // Delete the content of the tab + unset($this->m_aTabs[$sTabContainer]['tabs'][$sTabLabel]); + + // If we just removed the active tab, let's reset the active tab + if (($this->m_sCurrentTabContainer == $sTabContainer) && ($this->m_sCurrentTab == $sTabLabel)) + { + $this->m_sCurrentTab = ''; + } + } + } + + /** + * Finds the tab whose title matches a given pattern + * @return mixed The name of the tab as a string or false if not found + */ + public function FindTab($sPattern, $sTabContainer = null) + { + $return = false; + if ($sTabContainer == null) + { + $sTabContainer = $this->m_sCurrentTabContainer; + } + foreach($this->m_aTabs[$sTabContainer]['tabs'] as $sTabLabel => $void) + { + if (preg_match($sPattern, $sTabLabel)) + { + $result = $sTabLabel; + break; + } + } + return $result; + } + + /** + * Make the given tab the active one, as if it were clicked + * DOES NOT WORK: apparently in the *old* version of jquery + * that we are using this is not supported... TO DO upgrade + * the whole jquery bundle... + */ + public function SelectTab($sTabContainer, $sTabLabel) + { + $container_index = 0; + $tab_index = 0; + foreach($this->m_aTabs as $sCurrentTabContainerName => $aTabs) + { + if ($sTabContainer == $sCurrentTabContainerName) + { + foreach($aTabs['tabs'] as $sCurrentTabLabel => $void) + { + if ($sCurrentTabLabel == $sTabLabel) + { + break; + } + $tab_index++; + } + break; + } + $container_index++; + } + $sSelector = '#tabbedContent_'.$container_index.' > ul'; + return "window.setTimeout(\"$('$sSelector').tabs('select', $tab_index);\", 100);"; // Let the time to the tabs widget to initialize + } + + public function RenderIntoContent($sContent) + { + // Render the tabs in the page (if any) + foreach($this->m_aTabs as $sTabContainerName => $aTabs) + { + $sTabs = ''; + $sPrefix = $aTabs['prefix']; + $container_index = 0; + if (count($aTabs['tabs']) > 0) + { + $sTabs = "\n
\n"; + $sTabs .= "\n"; + // Now add the content of the tabs themselves + $i = 0; + foreach($aTabs['tabs'] as $sTabName => $aTabData) + { + switch($aTabData['type']) + { + case 'ajax': + // Nothing to add + break; + + case 'html': + default: + $sTabs .= "
".$aTabData['html']."
\n"; + } + $i++; + } + $sTabs .= "
\n\n"; + } + $sContent = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $sContent); + $container_index++; + } + return $sContent; + } +} \ No newline at end of file