2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
39 require_once('include/SubPanel/SubPanel.php');
40 require_once('include/SubPanel/SubPanelTilesTabs.php');
41 require_once('include/SubPanel/SubPanelDefinitions.php');
55 var $show_tabs = false;
57 var $subpanel_definitions;
59 var $hidden_tabs=array(); //consumer of this class can array of tabs that should be hidden. the tab name
60 //should be the array.
62 function SubPanelTiles(&$focus, $layout_def_key='', $layout_def_override = '')
64 $this->focus = $focus;
65 $this->id = $focus->id;
66 $this->module = $focus->module_dir;
67 $this->layout_def_key = $layout_def_key;
68 $this->subpanel_definitions=new SubPanelDefinitions($focus, $layout_def_key, $layout_def_override);
72 * Return the current selected or requested subpanel tab
73 * @return string The identifier for the selected subpanel tab (e.g., 'Other')
75 function getSelectedGroup()
79 if(isset($_REQUEST['subpanelTabs']))
80 $_SESSION['subpanelTabs'] = $_REQUEST['subpanelTabs'];
82 // include/tabConfig.php in turn includes the customized file at custom/include/tabConfig.php
83 require 'include/tabConfig.php';
85 $subpanelTabsPref = $current_user->getPreference('subpanel_tabs');
86 if(!isset($subpanelTabsPref)) $subpanelTabsPref = $GLOBALS['sugar_config']['default_subpanel_tabs'];
87 if(!empty($GLOBALS['tabStructure']) && (!empty($_SESSION['subpanelTabs']) || !empty($sugar_config['subpanelTabs']) || !empty($subpanelTabsPref)))
89 // Determine selected group
90 if(!empty($_REQUEST['subpanel']))
92 $selected_group = $_REQUEST['subpanel'];
94 elseif(!empty($_COOKIE[$this->module.'_sp_tab']))
96 $selected_group = $_COOKIE[$this->module.'_sp_tab'];
98 elseif(!empty($_SESSION['parentTab']) && !empty($GLOBALS['tabStructure'][$_SESSION['parentTab']]) && in_array($this->module, $GLOBALS['tabStructure'][$_SESSION['parentTab']]['modules']))
100 $selected_group = $_SESSION['parentTab'];
104 $selected_group = '';
105 foreach($GLOBALS['tabStructure'] as $mainTab => $group)
107 if(in_array($this->module, $group['modules']))
109 $selected_group = $mainTab;
115 $selected_group = 'All';
121 $selected_group = '';
123 return $selected_group;
127 * Determine which subpanels should be shown within the selected tab group (e.g., 'Other');
128 * @param boolean $showTabs True if we should call the code to render each visible tab
129 * @param string $selectedGroup The requested tab group
130 * @return array Visible tabs
132 function getTabs($showTabs = true, $selectedGroup='')
134 global $current_user;
136 //get all the "tabs" - this actually means all the subpanels available for display within a tab
137 $tabs = $this->subpanel_definitions->get_available_tabs();
139 if(!empty($selectedGroup))
141 // Bug #44344 : Custom relationships under same module only show once in subpanel tabs
142 // use object property instead new object to have ability run unit test (can override subpanel_definitions)
143 $objSubPanelTilesTabs = new SubPanelTilesTabs($this->focus);
144 $tabs = $objSubPanelTilesTabs->getTabs($tabs, $showTabs, $selectedGroup);
145 unset($objSubPanelTilesTabs);
150 // see if user current user has custom subpanel layout
151 $tabs = SubPanelTilesTabs::applyUserCustomLayoutToTabs($tabs);
153 /* Check if the preference is set now,
154 * because there's no point in executing this code if
155 * we aren't going to render anything.
157 $subpanelLinksPref = $current_user->getPreference('subpanel_links');
158 if(!isset($subpanelLinksPref)) $subpanelLinksPref = $GLOBALS['sugar_config']['default_subpanel_links'];
160 if($showTabs && $subpanelLinksPref){
161 require_once('include/SubPanel/SugarTab.php');
162 $sugarTab = new SugarTab();
164 $displayTabs = array();
166 foreach($tabs as $tab){
167 $displayTabs []= array('key'=>$tab, 'label'=>translate($this->subpanel_definitions->layout_defs['subpanel_setup'][$tab]['title_key']));
168 //echo '<td nowrap="nowrap"><a class="subTabLink" href="#' . $tab . '">' . translate($this->subpanel_definitions->layout_defs['subpanel_setup'][$tab]['title_key']) . '</a></td><td> | </td>';
170 $sugarTab->setup(array(),array(),$displayTabs);
171 $sugarTab->display();
173 //echo '<td width="100%"> </td></tr></table>';
178 function display($showContainer = true, $forceTabless = false)
180 global $layout_edit_mode, $sugar_version, $sugar_config, $current_user, $app_strings;
181 if(isset($layout_edit_mode) && $layout_edit_mode){
185 global $modListHeader;
188 echo '<script type="text/javascript" src="'. getJSPath('include/SubPanel/SubPanelTiles.js') . '"></script>';
191 if(document.DetailView != null &&
192 document.DetailView.elements != null &&
193 document.DetailView.elements.layout_def_key != null &&
194 typeof document.DetailView.elements['layout_def_key'] != 'undefined'){
195 document.DetailView.elements['layout_def_key'].value = '<?php echo $this->layout_def_key; ?>';
201 $default_div_display = 'inline';
202 if(!empty($sugar_config['hide_subpanels_on_login'])){
203 if(!isset($_SESSION['visited_details'][$this->focus->module_dir])){
204 setcookie($this->focus->module_dir . '_divs', '');
205 unset($_COOKIE[$this->focus->module_dir . '_divs']);
206 $_SESSION['visited_details'][$this->focus->module_dir] = true;
209 $default_div_display = 'none';
211 $div_cookies = get_sub_cookies($this->focus->module_dir . '_divs');
214 //Display the group header. this section is executed only if the tabbed interface is being used.
216 if (! empty($this->show_tabs))
218 require_once('include/tabs.php');
219 $tab_panel = new SugarWidgetTabs($tabs, $current_key, 'showSubPanel');
220 echo get_form_header('Related', '', false);
221 echo "<br />" . $tab_panel->display();
224 if(empty($_REQUEST['subpanels']))
226 $selected_group = $forceTabless?'':$this->getSelectedGroup();
227 $usersLayout = $current_user->getPreference('subpanelLayout', $this->focus->module_dir);
229 // we need to use some intelligence here when restoring the user's layout, as new modules with new subpanels might have been installed since the user's layout was recorded
230 // this means that we can't just restore the old layout verbatim as the new subpanels would then go walkabout
231 // so we need to do a merge while attempting as best we can to preserve the sense of the specified order
232 // this is complicated by the different ordering schemes used in the two sources for the panels: the user's layout uses an ordinal layout, the panels from getTabs have an explicit ordering driven by the 'order' parameter
233 // it's not clear how to best reconcile these two schemes; so we punt on it, and add all new panels to the end of the user's layout. At least this will give them a clue that something has changed...
234 // we also now check for tabs that have been removed since the user saved his or her preferences.
236 $tabs = $this->getTabs($showContainer, $selected_group) ;
238 if(!empty($usersLayout))
240 $availableTabs = $tabs ;
241 $tabs = array_intersect ( $usersLayout , $availableTabs ) ; // remove any tabs that have been removed since the user's layout was saved
242 foreach (array_diff ( $availableTabs , $usersLayout ) as $tab)
248 $tabs = explode(',', $_REQUEST['subpanels']);
251 $tab_names = array();
255 echo '<ul class="noBullet" id="subpanel_list">';
257 //echo "<li id='hidden_0' style='height: 5px' class='noBullet'> </li>";
258 if (empty($GLOBALS['relationships'])) {
259 if (!class_exists('Relationship')) {
260 require('modules/Relationships/Relationship.php');
262 $rel= new Relationship();
263 $rel->load_relationship_meta();
266 // this array will store names of sub-panels that can contain items
268 $module_sub_panels = array();
270 foreach ($tabs as $tab)
272 //load meta definition of the sub-panel.
273 $thisPanel=$this->subpanel_definitions->load_subpanel($tab);
274 if ($thisPanel === false)
276 //this if-block will try to skip over ophaned subpanels. Studio/MB are being delete unloaded modules completely.
277 //this check will ignore subpanels that are collections (activities, history, etc)
278 if (!isset($thisPanel->_instance_properties['collection_list']) and isset($thisPanel->_instance_properties['get_subpanel_data']) ) {
279 //ignore when data source is a function
281 if (!isset($this->focus->field_defs[$thisPanel->_instance_properties['get_subpanel_data']])) {
282 if (stripos($thisPanel->_instance_properties['get_subpanel_data'],'function:') === false) {
283 $GLOBALS['log']->fatal("Bad subpanel definition, it has incorrect value for get_subpanel_data property " .$tab);
288 if (isset($this->focus->field_defs[$thisPanel->_instance_properties['get_subpanel_data']]['relationship'])) {
289 $rel_name=$this->focus->field_defs[$thisPanel->_instance_properties['get_subpanel_data']]['relationship'];
292 if (empty($rel_name) or !isset($GLOBALS['relationships'][$rel_name])) {
293 $GLOBALS['log']->fatal("Missing relationship definition " .$rel_name. ". skipping " .$tab ." subpanel");
299 if ($thisPanel->isCollection()) {
300 // collect names of sub-panels that may contain items of each module
301 $collection_list = $thisPanel->get_inst_prop_value('collection_list');
302 if (is_array($collection_list)) {
303 foreach ($collection_list as $data) {
304 if (!empty($data['module'])) {
305 $module_sub_panels[$data['module']][$tab] = true;
310 $module = $thisPanel->get_module_name();
311 if (!empty($module)) {
312 $module_sub_panels[$module][$tab] = true;
316 echo '<li class="noBullet" id="whole_subpanel_' . $tab . '">';
319 $div_display = $default_div_display;
320 $cookie_name = $tab . '_v';
322 if (isset($thisPanel->_instance_properties['collapsed']) && $thisPanel->_instance_properties['collapsed'])
324 $div_display = 'none';
327 if(isset($div_cookies[$cookie_name])){
328 //If defaultSubPanelExpandCollapse is set, ignore the cookie that remembers whether the panel is expanded or collapsed.
329 //To be used with the above 'collapsed' metadata setting so they will always be set the same when the page is loaded.
330 if(!isset($sugar_config['defaultSubPanelExpandCollapse']) || $sugar_config['defaultSubPanelExpandCollapse'] == false)
331 $div_display = $div_cookies[$cookie_name];
333 if(!empty($sugar_config['hide_subpanels'])){
334 $div_display = 'none';
336 if($thisPanel->isDefaultHidden()) {
337 $div_display = 'none';
339 if($div_display == 'none'){
340 $opp_display = 'inline';
342 $opp_display = 'none';
345 if (!empty($this->layout_def_key) ) {
346 $layout_def_key = $this->layout_def_key;
348 $layout_def_key = '';
351 if (empty($this->show_tabs))
353 $show_icon_html = SugarThemeRegistry::current()->getImage( 'advanced_search', 'border="0" align="absmiddle"',null,null,'.gif',translate('LBL_SHOW'));
354 $hide_icon_html = SugarThemeRegistry::current()->getImage( 'basic_search', 'border="0" align="absmiddle"',null,null,'.gif',translate('LBL_HIDE'));
356 $max_min = "<a name=\"$tab\"> </a><span id=\"show_link_".$tab."\" style=\"display: $opp_display\"><a href='#' class='utilsLink' onclick=\"current_child_field = '".$tab."';showSubPanel('".$tab."',null,null,'".$layout_def_key."');document.getElementById('show_link_".$tab."').style.display='none';document.getElementById('hide_link_".$tab."').style.display='';return false;\">"
357 . "" . $show_icon_html . "</a></span>";
358 $max_min .= "<span id=\"hide_link_".$tab."\" style=\"display: $div_display\"><a href='#' class='utilsLink' onclick=\"hideSubPanel('".$tab."');document.getElementById('hide_link_".$tab."').style.display='none';document.getElementById('show_link_".$tab."').style.display='';return false;\">"
359 . "" . $hide_icon_html . "</a></span>";
360 echo '<div id="subpanel_title_' . $tab . '"';
361 if(empty($sugar_config['lock_subpanels']) || $sugar_config['lock_subpanels'] == false) echo ' onmouseover="this.style.cursor = \'move\';"';
362 echo '>' . get_form_header( $thisPanel->get_title(), $max_min, false) . '</div>';
366 <div cookie_name="$cookie_name" id="subpanel_$tab" style="display:$div_display">
367 <script>document.getElementById("subpanel_$tab" ).cookie_name="$cookie_name";</script>
370 if($div_display != 'none'){
371 echo "<script>SUGAR.util.doWhen(\"typeof(markSubPanelLoaded) != 'undefined'\", function() {markSubPanelLoaded('$tab');});</script>";
372 $old_contents = ob_get_contents();
376 include_once('include/SubPanel/SubPanel.php');
377 $subpanel_object = new SubPanel($this->module, $_REQUEST['record'], $tab,$thisPanel,$layout_def_key);
378 $subpanel_object->setTemplateFile('include/SubPanel/SubPanelDynamic.html');
379 $subpanel_object->display();
380 $subpanel_data = ob_get_contents();
384 echo $this->get_buttons($thisPanel,$subpanel_object->subpanel_query);
385 $buttons = ob_get_contents();
391 $display_spd = $subpanel_data;
394 <div id="list_subpanel_$tab">$display_spd</div>
397 array_push($tab_names, $tab);
399 } // end $tabs foreach
405 if(!empty($selected_group))
407 // closing table from tpls/singletabmenu.tpl
408 echo '</td></tr></table>';
412 $tab_names = '["' . join($tab_names, '","') . '"]';
413 global $sugar_config;
415 if(empty($sugar_config['lock_subpanels']) || $sugar_config['lock_subpanels'] == false) {
418 var SubpanelInit = function() {
419 SubpanelInitTabNames({$tab_names});
421 var SubpanelInitTabNames = function(tabNames) {
422 subpanel_dd = new Array();
425 subpanel_dd[j] = new ygDDList('whole_subpanel_' + tabNames[i]);
426 subpanel_dd[j].setHandleElId('subpanel_title_' + tabNames[i]);
427 subpanel_dd[j].onMouseDown = SUGAR.subpanelUtils.onDrag;
428 subpanel_dd[j].afterEndDrag = SUGAR.subpanelUtils.onDrop;
432 YAHOO.util.DDM.mode = 1;
434 currentModule = '{$this->module}';
436 "typeof(SUGAR.subpanelUtils) == 'object' && typeof(SUGAR.subpanelUtils.onDrag) == 'function'" +
437 " && document.getElementById('subpanel_list')",
444 $module_sub_panels = array_map('array_keys', $module_sub_panels);
445 $module_sub_panels = json_encode($module_sub_panels);
448 var ModuleSubPanels = $module_sub_panels;
452 $ob_contents = ob_get_contents();
458 function getLayoutManager()
460 require_once('include/generic/LayoutManager.php');
461 if ( $this->layout_manager == null) {
462 $this->layout_manager = new LayoutManager();
464 return $this->layout_manager;
467 function get_buttons($thisPanel,$panel_query=null)
469 $subpanel_def = $thisPanel->get_buttons();
470 $layout_manager = $this->getLayoutManager();
472 //for action button at the top of each subpanel
473 // bug#51275: smarty widget to help provide the action menu functionality as it is currently sprinkled throughout the app with html
475 $widget_contents = '';
476 foreach($subpanel_def as $widget_data)
479 $widget_data['action'] = $_REQUEST['action'];
480 $widget_data['module'] = $thisPanel->get_inst_prop_value('module');
481 $widget_data['focus'] = $this->focus;
482 $widget_data['subpanel_definition'] = $thisPanel;
483 $widget_contents .= '<td class="buttons">' . "\n";
485 if(empty($widget_data['widget_class']))
487 $buttons[] = "widget_class not defined for top subpanel buttons";
491 $button = $layout_manager->widgetDisplay($widget_data);
493 $buttons[] = $button;
498 require_once('include/Smarty/plugins/function.sugar_action_menu.php');
499 $widget_contents = smarty_function_sugar_action_menu(array(
500 'buttons' => $buttons,
501 'class' => 'clickMenu fancymenu',
502 ), $this->xTemplate);
503 return $widget_contents;