2 if (! defined ( 'sugarEntry' ) || ! sugarEntry)
3 die ( 'Not A Valid Entry Point' ) ;
4 /*********************************************************************************
5 * SugarCRM Community Edition is a customer relationship management program developed by
6 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Affero General Public License version 3 as published by the
10 * Free Software Foundation with the addition of the following permission added
11 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
12 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
13 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
20 * You should have received a copy of the GNU Affero General Public License along with
21 * this program; if not, see http://www.gnu.org/licenses or write to the Free
22 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
26 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
28 * The interactive user interfaces in modified source and object code versions
29 * of this program must display Appropriate Legal Notices, as required under
30 * Section 5 of the GNU Affero General Public License version 3.
32 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
33 * these Appropriate Legal Notices must retain the display of the "Powered by
34 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
35 * technical reasons, the Appropriate Legal Notices must display the words
36 * "Powered by SugarCRM".
37 ********************************************************************************/
46 // open the layout_definitions file.
49 * Subpanel implementation
56 var $_instance_properties ;
59 var $panel_definition ;
63 //module's table name and column fields.
67 var $template_instance ;
69 function aSubPanel ( $name , $instance_properties , $parent_bean , $reload = false , $original_only = false )
72 $this->_instance_properties = $instance_properties ;
74 $this->parent_bean = $parent_bean ;
77 global $current_language ;
78 if (! isset ( $parent_bean->mbvardefs ))
80 $mod_strings = return_module_language ( $current_language, $parent_bean->module_dir ) ;
82 $this->mod_strings = $mod_strings ;
84 if ($this->isCollection ())
86 $this->load_sub_subpanels () ; //load sub-panel definition.
89 if (!is_dir('modules/' . $this->_instance_properties [ 'module' ])){
92 $def_path = 'modules/' . $this->_instance_properties [ 'module' ] . '/metadata/subpanels/' . $this->_instance_properties [ 'subpanel_name' ] . '.php' ;
94 $orig_exists = is_file($def_path);
101 if (is_file("custom/$def_path") && (!$original_only || !$orig_exists))
103 require ("custom/$def_path");
107 if (! $original_only && isset ( $this->_instance_properties [ 'override_subpanel_name' ] ) && file_exists ( 'custom/modules/' . $this->_instance_properties [ 'module' ] . '/metadata/subpanels/' . $this->_instance_properties [ 'override_subpanel_name' ] . '.php' ))
109 $cust_def_path = 'custom/modules/' . $this->_instance_properties [ 'module' ] . '/metadata/subpanels/' . $this->_instance_properties [ 'override_subpanel_name' ] . '.php' ;
110 require ($cust_def_path) ;
116 $GLOBALS['log']->fatal("Failed to load original or custom subpanel data for $name in $def_path");
119 // check that the loaded subpanel definition includes a $subpanel_layout section - some, such as projecttasks/default do not...
120 $this->panel_definition = array () ;
121 if (isset($subpanel_layout))
123 $this->panel_definition = $subpanel_layout ;
126 $this->load_module_info () ; //load module info from the module's bean file.
132 * is the sub panel default hidden?
136 public function isDefaultHidden()
138 if(isset($this->_instance_properties['default_hidden']) && $this->_instance_properties['default_hidden'] == true) {
146 function distinct_query ()
148 if (isset ( $this->_instance_properties [ 'get_distinct_data' ] ))
150 return !empty($this->_instance_properties['get_distinct_data']) ? true : false;
155 //return the translated header value.
156 function get_title ()
158 if (empty ( $this->mod_strings [ $this->_instance_properties [ 'title_key' ] ] ))
160 return translate ( $this->_instance_properties [ 'title_key' ], $this->_instance_properties [ 'module' ] ) ;
162 return $this->mod_strings [ $this->_instance_properties [ 'title_key' ] ] ;
165 //return the definition of buttons. looks for buttons in 2 locations.
166 function get_buttons ()
168 $buttons = array ( ) ;
169 if (isset ( $this->_instance_properties [ 'top_buttons' ] ))
171 //this will happen only in the case of sub-panels with multiple sources(activities).
172 $buttons = $this->_instance_properties [ 'top_buttons' ] ;
175 $buttons = $this->panel_definition [ 'top_buttons' ] ;
178 // permissions. hide SubPanelTopComposeEmailButton from activities if email module is disabled.
179 //only email is being tested becuase other submodules in activites/history such as notes, tasks, meetings and calls cannot be disabled.
180 //as of today these are the only 2 sub-panels that use the union clause.
181 $mod_name = $this->get_module_name () ;
182 if ($mod_name == 'Activities' || $mod_name == 'History')
184 global $modListHeader ;
185 global $modules_exempt_from_availability_check ;
186 if (isset ( $modListHeader ) && (! (array_key_exists ( 'Emails', $modListHeader ) or array_key_exists ( 'Emails', $modules_exempt_from_availability_check ))))
188 foreach ( $buttons as $key => $button )
190 foreach ( $button as $property => $value )
192 if ($value === 'SubPanelTopComposeEmailButton' || $value === 'SubPanelTopArchiveEmailButton')
194 //remove this button from the array.
195 unset ( $buttons [ $key ] ) ;
206 //call this function for sub-panels that have unions.
207 function load_sub_subpanels ()
210 global $modListHeader ;
211 // added a check for security of tabs to see if an user has access to them
212 // this prevents passing an "unseen" tab to the query string and pulling up its contents
213 if (! isset ( $modListHeader ))
215 global $current_user ;
216 if (isset ( $current_user ))
218 $modListHeader = query_module_access_list ( $current_user ) ;
222 //by default all the activities modules are exempt, so hiding them won't affect their appearance unless the 'activity' subpanel itself is hidden.
223 //add email to the list temporarily so it is not affected in activities subpanel
224 global $modules_exempt_from_availability_check ;
225 $modules_exempt_from_availability_check['Emails'] = 'Emails';
227 $listFieldMap = array();
229 if (empty ( $this->sub_subpanels ))
231 $panels = $this->get_inst_prop_value ( 'collection_list' ) ;
232 foreach ( $panels as $panel => $properties )
234 if (array_key_exists ( $properties [ 'module' ], $modListHeader ) or array_key_exists ( $properties [ 'module' ], $modules_exempt_from_availability_check ))
236 $this->sub_subpanels [ $panel ] = new aSubPanel ( $panel, $properties, $this->parent_bean ) ;
239 //Sync displayed list fields across the subpanels
240 $display_fields = $this->getDisplayFieldsFromCollection($this->sub_subpanels);
241 $query_fields = array();
242 foreach ( $this->sub_subpanels as $key => $subpanel )
244 $list_fields = $subpanel->get_list_fields();
245 $listFieldMap[$key] = array();
247 foreach($list_fields as $field => $def)
249 if (isset($def['vname']) && isset($def['width']))
252 if(!empty($def['alias']))
253 $listFieldMap[$key][$def['alias']] = $field;
255 $listFieldMap[$key][$field] = $field;
256 if (!isset($display_fields[$def['vname']]))
258 if(sizeof($display_fields) > $index)
260 //Try to insert the new field in an order that makes sense
261 $start = array_slice($display_fields, 0, $index);
262 $end = array_slice($display_fields, $index);
263 $display_fields = array_merge(
265 array($def['vname'] => array('name' => $field, 'vname' => $def['vname'], 'width' => $def['width'] )),
270 $display_fields[$def['vname']] = array(
271 'name' => empty($def['alias']) ? $field : $def['alias'],
272 'vname' => $def['vname'],
273 'width' => $def['width'],
278 $query_fields[$field] = $def;
282 foreach ( $this->sub_subpanels as $key => $subpanel )
284 $list_fields = array();
285 foreach($display_fields as $vname => $def)
287 $field = $def['name'];
288 $list_key = isset($listFieldMap[$key][$field]) ? $listFieldMap[$key][$field] : $field;
290 if (isset($subpanel->panel_definition['list_fields'][$field]))
292 $list_fields[$field] = $subpanel->panel_definition['list_fields'][$field];
294 else if ($list_key != $field && isset($subpanel->panel_definition['list_fields'][$list_key]))
296 $list_fields[$list_key] = $subpanel->panel_definition['list_fields'][$list_key];
300 $list_fields[$field] = $display_fields[$vname];
303 foreach($query_fields as $field => $def)
305 if (isset($subpanel->panel_definition['list_fields'][$field]))
307 $list_fields[$field] = $subpanel->panel_definition['list_fields'][$field];
310 $list_fields[$field] = $def;
313 $subpanel->panel_definition['list_fields'] = $list_fields;
318 protected function getDisplayFieldsFromCollection($sub_subpanels)
320 $display_fields = array();
321 foreach ($sub_subpanels as $key => $subpanel )
323 $list_fields = $subpanel->get_list_fields();
325 foreach($list_fields as $field => $def)
327 if (isset($def['vname']) && isset($def['width']))
330 if (!isset($display_fields[$def['vname']]))
332 if(sizeof($display_fields) > $index)
334 //Try to insert the new field in an order that makes sense
335 $start = array_slice($display_fields, 0, $index);
336 $end = array_slice($display_fields, $index);
337 $display_fields = array_merge(
339 array($def['vname'] => array('name' => $field, 'vname' => $def['vname'], 'width' => $def['width'] )),
344 $display_fields[$def['vname']] = array(
346 'vname' => $def['vname'],
347 'width' => $def['width'],
356 function isDatasourceFunction ()
358 if (strpos ( $this->get_inst_prop_value ( 'get_subpanel_data' ), 'function' ) === false)
364 function isCollection ()
366 if ($this->get_inst_prop_value ( 'type' ) == 'collection')
371 //get value of a property defined at the panel instance level.
372 function get_inst_prop_value ( $name )
374 return isset($this->_instance_properties[$name]) ? $this->_instance_properties [ $name ] : null;
376 //get value of a property defined at the panel definition level.
377 function get_def_prop_value ( $name )
379 if (isset ( $this->panel_definition [ $name ] ))
381 return $this->panel_definition [ $name ] ;
388 //if datasource is of the type function then return the function name
389 //else return the value as is.
390 function get_function_parameters ()
392 $parameters = array ( ) ;
393 if ($this->isDatasourceFunction ())
395 $parameters = $this->get_inst_prop_value ( 'function_parameters' ) ;
400 function get_data_source_name ( $check_set_subpanel_data = false )
403 if ($check_set_subpanel_data)
405 $prop_value = $this->get_inst_prop_value ( 'set_subpanel_data' ) ;
407 if (! empty ( $prop_value ))
412 //fall back to default behavior.
414 if ($this->isDatasourceFunction ())
416 return (substr_replace ( $this->get_inst_prop_value ( 'get_subpanel_data' ), '', 0, 9 )) ;
419 return $this->get_inst_prop_value ( 'get_subpanel_data' ) ;
423 //returns the where clause for the query.
424 function get_where ()
426 return $this->get_def_prop_value ( 'where' ) ;
429 function is_fill_in_additional_fields ()
431 // do both. inst_prop returns values from metadata/subpaneldefs.php and def_prop returns from subpanel/default.php
432 $temp = $this->get_inst_prop_value ( 'fill_in_additional_fields' ) || $this->get_def_prop_value ( 'fill_in_additional_fields' ) ;
436 function get_list_fields ()
438 if (isset ( $this->panel_definition [ 'list_fields' ] ))
440 return $this->panel_definition [ 'list_fields' ] ;
447 function get_module_name ()
449 return $this->get_inst_prop_value ( 'module' ) ;
457 //load subpanel mdoule's table name and column fields.
458 function load_module_info ()
463 $module_name = $this->get_module_name () ;
464 if (! empty ( $module_name ))
467 $bean_name = $beanList [ $this->get_module_name () ] ;
469 $this->bean_name = $bean_name ;
471 include_once ($beanFiles [ $bean_name ]) ;
472 $this->template_instance = new $bean_name ( ) ;
473 $this->template_instance->force_load_details = true ;
474 $this->table_name = $this->template_instance->table_name ;
475 //$this->db_fields=$this->template_instance->column_fields;
478 //this function is to be used only with sub-panels that are based
480 function get_header_panel_def ()
482 if (! empty ( $this->sub_subpanels ))
484 if (! empty ( $this->_instance_properties [ 'header_definition_from_subpanel' ] ) && ! empty ( $this->sub_subpanels [ $this->_instance_properties [ 'header_definition_from_subpanel' ] ] ))
486 return $this->sub_subpanels [ $this->_instance_properties [ 'header_definition_from_subpanel' ] ] ;
489 $display_fields = array();
490 //If we are not pulling from a specific subpanel, create a list of all list fields and use that.
491 foreach($this->sub_subpanels as $subpanel)
493 $list_fields = $subpanel->get_list_fields();
494 foreach($list_fields as $field => $def)
500 reset ( $this->sub_subpanels ) ;
501 return current ( $this->sub_subpanels ) ;
508 * Returns an array of current properties of the class.
509 * It will simply give the class name for instances of classes.
511 function _to_array ()
513 return array ( '_instance_properties' => $this->_instance_properties , 'db_fields' => $this->db_fields , 'mod_strings' => $this->mod_strings , 'name' => $this->name , 'panel_definition' => $this->panel_definition , 'parent_bean' => get_class ( $this->parent_bean ) , 'sub_subpanels' => $this->sub_subpanels , 'table_name' => $this->table_name , 'template_instance' => get_class ( $this->template_instance ) ) ;
518 class SubPanelDefinitions
522 var $_visible_tabs_array ;
527 * Enter description here...
529 * @param BEAN $focus - this is the bean you want to get the data from
530 * @param STRING $layout_def_key - if you wish to use a layout_def defined in the default metadata/subpaneldefs.php that is not keyed off of $bean->module_dir pass in the key here
531 * @param ARRAY $layout_def_override - if you wish to override the default loaded layout defs you pass them in here.
532 * @return SubPanelDefinitions
534 function SubPanelDefinitions ( $focus , $layout_def_key = '' , $layout_def_override = '' )
536 $this->_focus = $focus ;
537 if (! empty ( $layout_def_override ))
539 $this->layout_defs = $layout_def_override ;
543 $this->open_layout_defs ( false, $layout_def_key ) ;
548 * This function returns an ordered list of all "tabs", actually subpanels, for this module
549 * The source list is obtained from the subpanel layout contained in the layout_defs for this module,
550 * found either in the modules metadata/subpaneldefs.php file, or in the modules custom/.../Ext/Layoutdefs/layoutdefs.ext.php file
551 * and filtered through an ACL check.
552 * Note that the keys for the resulting array of tabs are in practice the name of the underlying source relationship for the subpanel
553 * So for example, the key for a custom module's subpanel with Accounts might be 'one_one_accounts', as generated by the Studio Relationship Editor
554 * Although OOB module subpanels have keys such as 'accounts', which might on the face of it appear to be a reference to the related module, in fact 'accounts' is still the relationship name
555 * @param boolean Optional - include the subpanel title label in the return array (false)
556 * @return array All tabs that pass an ACL check
558 function get_available_tabs ($FromGetModuleSubpanels=false)
560 global $modListHeader ;
561 global $modules_exempt_from_availability_check ;
563 if (isset ( $this->_visible_tabs_array ))
564 return $this->_visible_tabs_array ;
566 if (empty($modListHeader))
567 $modListHeader = query_module_access_list($GLOBALS['current_user']);
569 $this->_visible_tabs_array = array ( ) ; // bug 16820 - make sure this is an array for the later ksort
571 if (isset ( $this->layout_defs [ 'subpanel_setup' ] )) // bug 17434 - belts-and-braces - check that we have some subpanels first
573 //retrieve list of hidden subpanels
574 $hidden_panels = $this->get_hidden_subpanels();
576 //activities is a special use case in that if it is hidden,
577 //then the history tab should be hidden too.
578 if(!empty($hidden_panels) && is_array($hidden_panels) && in_array('activities',$hidden_panels)){
579 //add history to list hidden_panels
580 $hidden_panels['history'] = 'history';
583 foreach ( $this->layout_defs [ 'subpanel_setup' ] as $key => $values_array )
585 //exclude if this subpanel is hidden from admin screens
587 if ( isset($values_array['module']) )
588 $module = strtolower($values_array['module']);
589 if ($hidden_panels && is_array($hidden_panels) && (in_array($module, $hidden_panels) || array_key_exists($module, $hidden_panels)) ){
590 //this panel is hidden, skip it
594 // make sure the module attribute is set, else none of this works...
595 if ( !isset($values_array [ 'module' ])) {
596 $GLOBALS['log']->debug("SubPanelDefinitions->get_available_tabs(): no module defined in subpaneldefs for '$key' =>" . var_export($values_array,true) . " - ingoring subpanel defintion") ;
601 $exempt = array_key_exists ( $values_array [ 'module' ], $modules_exempt_from_availability_check ) ;
602 $ok = $exempt || ( (! ACLController::moduleSupportsACL ( $values_array [ 'module' ] ) || ACLController::checkAccess ( $values_array [ 'module' ], 'list', true ) ) ) ;
604 $GLOBALS [ 'log' ]->debug ( "SubPanelDefinitions->get_available_tabs(): " . $key . "= " . ( $exempt ? "exempt " : "not exempt " .( $ok ? " ACL OK" : "" ) ) ) ;
608 while ( ! empty ( $this->_visible_tabs_array [ $values_array [ 'order' ] ] ) )
610 $values_array [ 'order' ] ++ ;
613 $this->_visible_tabs_array [ $values_array ['order'] ] = ($FromGetModuleSubpanels) ? array($key=>$values_array['title_key']) : $key ;
618 ksort ( $this->_visible_tabs_array ) ;
619 return $this->_visible_tabs_array ;
623 * Load the definition of the a sub-panel.
624 * Also the sub-panel is added to an array of sub-panels.
625 * use of reload has been deprecated, since the subpanel is initialized every time.
627 function load_subpanel ( $name , $reload = false , $original_only = false )
629 if (!is_dir('modules/' . $this->layout_defs [ 'subpanel_setup' ][ strtolower ( $name ) ] [ 'module' ]))
631 return new aSubPanel ( $name, $this->layout_defs [ 'subpanel_setup' ] [ strtolower ( $name ) ], $this->_focus, $reload, $original_only ) ;
635 * Load the layout def file and associate the definition with a variable in the file.
637 function open_layout_defs ( $reload = false , $layout_def_key = '' , $original_only = false )
639 $layout_defs [ $this->_focus->module_dir ] = array ( ) ;
640 $layout_defs [ $layout_def_key ] = array ( ) ;
642 if (empty ( $this->layout_defs ) || $reload || (! empty ( $layout_def_key ) && ! isset ( $layout_defs [ $layout_def_key ] )))
644 if (file_exists ( 'modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php' ))
645 require ('modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php') ;
647 if (! $original_only && file_exists ( 'custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php' ))
648 require ('custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php') ;
650 if (! empty ( $layout_def_key ))
651 $this->layout_defs = $layout_defs [ $layout_def_key ] ;
653 $this->layout_defs = $layout_defs [ $this->_focus->module_dir ] ;
660 * Removes a tab from the list of loaded tabs.
661 * Returns true if successful, false otherwise.
662 * Hint: Used by Campaign's DetailView.
664 function exclude_tab ( $tab_name )
667 //unset layout definition
668 if (! empty ( $this->layout_defs [ 'subpanel_setup' ] [ $tab_name ] ))
670 unset ( $this->layout_defs [ 'subpanel_setup' ] [ $tab_name ] ) ;
672 //unset instance from _visible_tab_array
673 if (! empty ( $this->_visible_tabs_array ))
675 $key = array_search ( $tab_name, $this->_visible_tabs_array ) ;
678 unset ( $this->_visible_tabs_array [ $key ] ) ;
686 * return all available subpanels that belong to the list of tab modules. You can optionally return all
687 * available subpanels, and also optionally group by module (prepends the key with the bean class name).
689 function get_all_subpanels( $return_tab_modules_only = true, $group_by_module = false )
691 global $moduleList, $beanFiles, $beanList, $module;
693 //use tab controller function to get module list with named keys
694 require_once("modules/MySettings/TabController.php");
695 $modules_to_check = TabController::get_key_array($moduleList);
697 //change case to match subpanel processing later on
698 $modules_to_check = array_change_key_case($modules_to_check);
699 // Append on the CampaignLog module, because that is where the subpanels point, not directly to Campaigns
700 $modules_to_check['campaignlog'] = "CampaignLog";
705 //iterate through modules and build subpanel array
706 foreach($modules_to_check as $mod_name){
708 //skip if module name is not in bean list, otherwise get the bean class name
709 if(!isset($beanList[$mod_name])) continue;
710 $class = $beanList[$mod_name];
712 //skip if class name is not in file list, otherwise require the bean file and create new class
713 if(!isset($beanFiles[$class]) || !file_exists($beanFiles[$class])) continue;
715 //retrieve subpanels for this bean
716 require_once($beanFiles[$class]);
717 $bean_class = new $class();
719 //create new subpanel definition instance and get list of tabs
720 $spd = new SubPanelDefinitions($bean_class) ;
721 $sub_tabs = $spd->get_available_tabs();
723 //add each subpanel to array of total subpanles
724 foreach( $sub_tabs as $panel_key){
725 $panel_key = strtolower($panel_key);
726 $panel_module = $panel_key;
727 if ( isset($spd->layout_defs['subpanel_setup'][$panel_key]['module']) )
728 $panel_module = strtolower($spd->layout_defs['subpanel_setup'][$panel_key]['module']);
729 //if module_only flag is set, only if it is also in module array
730 if($return_tab_modules_only && !array_key_exists($panel_module, $modules_to_check)) continue;
731 $panel_key_name = $panel_module;
733 //group_by_key_name is set to true, then array will hold an entry for each
734 //subpanel, with the module name prepended in the key
735 if($group_by_module) $panel_key_name = $class.'_'.$panel_key_name;
736 //add panel name to subpanel array
737 $spd_arr[$panel_key_name] = $panel_module;
744 * save array of hidden panels to mysettings category in config table
746 function set_hidden_subpanels($panels){
747 $administration = new Administration();
748 $serialized = base64_encode(serialize($panels));
749 $administration->saveSetting('MySettings', 'hide_subpanels', $serialized);
753 * retrieve hidden subpanels
755 function get_hidden_subpanels(){
758 //create variable as static to minimize queries
759 static $hidden_subpanels = null;
761 // if the static value is not already cached, then retrieve it.
762 if(empty($hidden_subpanels))
765 //create Administration object and retrieve any settings for panels
766 $administration = new Administration();
767 $administration->retrieveSettings('MySettings');
769 if(isset($administration->settings) && isset($administration->settings['MySettings_hide_subpanels'])){
770 $hidden_subpanels = $administration->settings['MySettings_hide_subpanels'];
771 $hidden_subpanels = trim($hidden_subpanels);
773 //make sure serialized string is not empty
774 if (!empty($hidden_subpanels)){
775 //decode and unserialize to retrieve the array
776 $hidden_subpanels = base64_decode($hidden_subpanels);
777 $hidden_subpanels = unserialize($hidden_subpanels);
779 //Ensure modules saved in the preferences exist.
780 //get user preference
781 //unserialize and add to array if not empty
782 $pref_hidden = array();
783 foreach($pref_hidden as $id => $pref_hidden_panel) {
784 $hidden_subpanels[] = $pref_hidden_panel;
789 //no settings found, return empty
790 return $hidden_subpanels;
794 { //no settings found, return empty
795 return $hidden_subpanels;
799 return $hidden_subpanels;