Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

include/SubPanel/SubPanelDefinitions.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
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.
6
7
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2016 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 *
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 *
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 *
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
42
43
44
45
//input
46
//	module directory
47
//constructor
48
//	open the layout_definitions file.
49
//
50
/**
51
 * Subpanel implementation
52
 * @api
53
 */
54
class aSubPanel
55
{
56
57
	var $name ;
58
	var $_instance_properties ;
59
60
	var $mod_strings ;
61
	var $panel_definition ;
62
	var $sub_subpanels ;
63
	var $parent_bean ;
64
65
    /**
66
     * Can we display this subpanel?
67
     *
68
     * This is set after it loads the def's for the subpanel.  If there are no beans to display in the collection
69
     * we don't want to display this as it will just throw errors.
70
     *
71
     * @var bool
72
     */
73
    var $canDisplay = true;
74
75
	//module's table name and column fields.
76
	var $table_name ;
77
	var $db_fields ;
78
	var $bean_name ;
79
	var $template_instance ;
80
81
	var $search_query;
82
	var $base_collection_list = array();
83
84
	function __construct ($name , $instance_properties , $parent_bean , $reload = false , $original_only = false, $search_query = '', $collections = array() ){
85
86
		$this->_instance_properties = $instance_properties ;
87
88
		if(isset($instance_properties['collection_list' ])) $this->base_collection_list = $instance_properties['collection_list' ];
89
90
		if(!empty($collections) && isset($instance_properties['collection_list' ])){
91
			foreach($instance_properties['collection_list' ] as $cname => $value){
92
				if(!in_array($value['module'], $collections)){
93
					unset($instance_properties['collection_list'][$cname]);
94
				}
95
			}
96
		}
97
		if (!$this->isCollection()){
98
			$table = strtolower($instance_properties['module']);
99
			$search_query = str_replace('meetings',$table,$search_query);
100
		}
101
102
		$this->search_query = $search_query;
103
		$this->name = $name ;
104
		$this->parent_bean = $parent_bean ;
105
106
		//set language
107
		global $current_language ;
108
		if (! isset ( $parent_bean->mbvardefs ))
109
		{
110
			$mod_strings = return_module_language ( $current_language, $parent_bean->module_dir ) ;
111
		}
112
		$this->mod_strings = $mod_strings ;
113
114
        if ($this->isCollection ())
115
		{
116
			$this->canDisplay = $this->load_sub_subpanels () ; //load sub-panel definition.
117
		} else
118
		{
119
			if (!is_dir('modules/' . $this->_instance_properties [ 'module' ])){
120
				_pstack_trace();
121
			}
122
			$def_path = 'modules/' . $this->_instance_properties [ 'module' ] . '/metadata/subpanels/' . $this->_instance_properties [ 'subpanel_name' ] . '.php' ;
123
124
			$orig_exists = is_file($def_path);
125
			$loaded = false;
126
			if ($orig_exists)
127
			{
128
				require ($def_path);
129
				$loaded = true;
130
			}
131
			if (is_file("custom/$def_path") && (!$original_only  || !$orig_exists))
132
			{
133
				require ("custom/$def_path");
134
				$loaded = true;
135
			}
136
137
			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' ))
138
			{
139
				$cust_def_path = 'custom/modules/' . $this->_instance_properties [ 'module' ] . '/metadata/subpanels/' . $this->_instance_properties [ 'override_subpanel_name' ] . '.php' ;
140
				require ($cust_def_path) ;
141
				$loaded = true;
142
			}
143
144
			if (!$loaded)
145
			{
146
				$GLOBALS['log']->fatal("Failed to load original or custom subpanel data for $name in $def_path");
147
                $this->canDisplay = false;
148
			}
149
150
            // load module info from the module's bean file
151
            $this->load_module_info();
152
153
            // check that the loaded subpanel definition includes a $subpanel_layout section - some, such as
154
            // projecttasks/default do not...
155
            $this->panel_definition = array();
156
            if (isset($subpanel_layout) && is_array($subpanel_layout)) {
0 ignored issues
show
The variable $subpanel_layout seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
157
                $this->set_panel_definition($subpanel_layout);
158
159
			}
160
		}
161
162
	}
163
164
	/**
165
	 * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead
166
	 */
167
	function aSubPanel($name , $instance_properties , $parent_bean , $reload = false , $original_only = false, $search_query = '', $collections = array() ){
168
		$deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
169
		if(isset($GLOBALS['log'])) {
170
			$GLOBALS['log']->deprecated($deprecatedMessage);
171
		}
172
		else {
173
			trigger_error($deprecatedMessage, E_USER_DEPRECATED);
174
		}
175
		self::__construct($name, $instance_properties, $parent_bean, $reload, $original_only, $search_query, $collections);
176
	}
177
178
    /**
179
     * is the sub panel default hidden?
180
     *
181
     * @return bool
182
     */
183
    public function isDefaultHidden()
184
    {
185
        if(isset($this->_instance_properties['default_hidden']) && $this->_instance_properties['default_hidden'] == true) {
186
            return true;
187
        }
188
189
        return false;
190
    }
191
192
193
	function distinct_query ()
194
	{
195
		if (isset ( $this->_instance_properties [ 'get_distinct_data' ] ))
196
		{
197
			return !empty($this->_instance_properties['get_distinct_data']) ? true : false;
198
		}
199
		return false ;
200
	}
201
202
	//return the translated header value.
203
	function get_title ()
204
	{
205
		if (empty ( $this->mod_strings [ $this->_instance_properties [ 'title_key' ] ] ))
206
		{
207
			return translate ( $this->_instance_properties [ 'title_key' ], $this->_instance_properties [ 'module' ] ) ;
208
		}
209
		return $this->mod_strings [ $this->_instance_properties [ 'title_key' ] ] ;
210
	}
211
212
	//return the definition of buttons. looks for buttons in 2 locations.
213
	function get_buttons ()
214
	{
215
		$buttons = array ( ) ;
216
		if (isset ( $this->_instance_properties [ 'top_buttons' ] ))
217
		{
218
			//this will happen only in the case of sub-panels with multiple sources(activities).
219
			$buttons = $this->_instance_properties [ 'top_buttons' ] ;
220
		} else
221
		{
222
			$buttons = $this->panel_definition [ 'top_buttons' ] ;
223
		}
224
225
		// permissions. hide SubPanelTopComposeEmailButton from activities if email module is disabled.
226
		//only email is  being tested becuase other submodules in activites/history such as notes, tasks, meetings and calls cannot be disabled.
227
		//as of today these are the only 2 sub-panels that use the union clause.
228
		$mod_name = $this->get_module_name () ;
229
		if ($mod_name == 'Activities' || $mod_name == 'History')
230
		{
231
			global $modListHeader ;
232
			global $modules_exempt_from_availability_check ;
233
			if (isset ( $modListHeader ) && (! (array_key_exists ( 'Emails', $modListHeader ) or array_key_exists ( 'Emails', $modules_exempt_from_availability_check ))))
234
			{
235
				foreach ( $buttons as $key => $button )
236
				{
237
					foreach ( $button as $property => $value )
238
					{
239
						if ($value === 'SubPanelTopComposeEmailButton' || $value === 'SubPanelTopArchiveEmailButton')
240
						{
241
							//remove this button from the array.
242
							unset ( $buttons [ $key ] ) ;
243
						}
244
					}
245
				}
246
			}
247
		}
248
249
		return $buttons ;
250
	}
251
252
253
    /**
254
     * Load the Sub-Panel objects if it can from the metadata files.
255
     *
256
     * call this function for sub-panels that have unions.
257
     *
258
     * @return bool         True by default if the subpanel was loaded.  Will return false if none in the collection are
259
     *                      allowed by the current user.
260
     */
261
	function load_sub_subpanels ()
262
	{
263
264
		global $modListHeader ;
265
		// added a check for security of tabs to see if an user has access to them
266
		// this prevents passing an "unseen" tab to the query string and pulling up its contents
267
		if (! isset ( $modListHeader ))
268
		{
269
			global $current_user ;
270
			if (isset ( $current_user ))
271
			{
272
				$modListHeader = query_module_access_list ( $current_user ) ;
273
			}
274
		}
275
276
		//by default all the activities modules are exempt, so hiding them won't affect their appearance unless the 'activity' subpanel itself is hidden.
277
		//add email to the list temporarily so it is not affected in activities subpanel
278
		global $modules_exempt_from_availability_check ;
279
		$modules_exempt_from_availability_check['Emails'] = 'Emails';
280
281
		$listFieldMap = array();
282
283
		if (empty ( $this->sub_subpanels ))
284
		{
285
			$panels = $this->get_inst_prop_value ( 'collection_list' ) ;
286
			foreach ( $panels as $panel => $properties )
287
			{
288
				if (array_key_exists ( $properties [ 'module' ], $modListHeader ) or array_key_exists ( $properties [ 'module' ], $modules_exempt_from_availability_check ))
289
				{
290
					$this->sub_subpanels [ $panel ] = new aSubPanel ( $panel, $properties, $this->parent_bean, false, false, $this->search_query ) ;
291
				}
292
			}
293
			// if it's empty just dump out as there is nothing to process.
294
			if(empty($this->sub_subpanels)) return false;
295
			//Sync displayed list fields across the subpanels
296
			$display_fields = $this->getDisplayFieldsFromCollection($this->sub_subpanels);
0 ignored issues
show
Are you sure the assignment to $display_fields is correct as $this->getDisplayFieldsF...n($this->sub_subpanels) (which targets aSubPanel::getDisplayFieldsFromCollection()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
297
			$query_fields = array();
298
			foreach ( $this->sub_subpanels as $key => $subpanel )
299
			{
300
				$list_fields = $subpanel->get_list_fields();
301
				$listFieldMap[$key] = array();
302
				$index = 0;
303
				foreach($list_fields as $field => $def)
304
				{
305
					if (isset($def['vname']) && isset($def['width']))
306
					{
307
						$index++;
308
						if(!empty($def['alias']))
309
							$listFieldMap[$key][$def['alias']] = $field;
310
						else
311
							$listFieldMap[$key][$field] = $field;
312
						if (!isset($display_fields[$def['vname']]))
313
						{
314
							if(sizeof($display_fields) > $index)
315
							{
316
								//Try to insert the new field in an order that makes sense
317
								$start = array_slice($display_fields, 0, $index);
318
								$end = array_slice($display_fields, $index);
319
								$display_fields = array_merge(
320
									$start,
321
									array($def['vname'] => array('name' => $field, 'vname' => $def['vname'], 'width' => $def['width'] )),
322
									$end
323
								);
324
							} else
325
							{
326
								$display_fields[$def['vname']] = array(
327
									'name' => empty($def['alias']) ? $field : $def['alias'],
328
									'vname' => $def['vname'],
329
									'width' => $def['width'],
330
								);
331
							}
332
						}
333
					} else {
334
						$query_fields[$field] = $def;
335
					}
336
				}
337
			}
338
			foreach ( $this->sub_subpanels as $key => $subpanel )
339
			{
340
				$list_fields = array();
341
				foreach($display_fields as $vname => $def)
0 ignored issues
show
The expression $display_fields of type null|array<?,?> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
342
				{
343
					$field = $def['name'];
344
					$list_key = isset($listFieldMap[$key][$field]) ? $listFieldMap[$key][$field] : $field;
345
346
					if (isset($subpanel->panel_definition['list_fields'][$field]))
347
					{
348
						$list_fields[$field] = $subpanel->panel_definition['list_fields'][$field];
349
					}
350
					else if ($list_key != $field && isset($subpanel->panel_definition['list_fields'][$list_key]))
351
					{
352
						$list_fields[$list_key] = $subpanel->panel_definition['list_fields'][$list_key];
353
354
					}
355
					else {
356
						$list_fields[$field] = $display_fields[$vname];
357
					}
358
				}
359
				foreach($query_fields as $field => $def)
360
				{
361
					if (isset($subpanel->panel_definition['list_fields'][$field]))
362
					{
363
						$list_fields[$field] = $subpanel->panel_definition['list_fields'][$field];
364
					}
365
					else {
366
						$list_fields[$field] = $def;
367
					}
368
				}
369
				$subpanel->panel_definition['list_fields'] = $list_fields;
370
			}
371
		}
372
373
		return true;
374
	}
375
376
	protected function getDisplayFieldsFromCollection($sub_subpanels)
377
	{
378
		$display_fields = array();
379
		foreach ($sub_subpanels as $key => $subpanel )
380
		{
381
			$list_fields = $subpanel->get_list_fields();
382
			$index = 0;
383
			foreach($list_fields as $field => $def)
384
			{
385
				if (isset($def['vname']) && isset($def['width']))
386
				{
387
					$index++;
388
					if (!isset($display_fields[$def['vname']]))
389
					{
390
						if(sizeof($display_fields) > $index)
391
						{
392
							//Try to insert the new field in an order that makes sense
393
							$start = array_slice($display_fields, 0, $index);
394
							$end = array_slice($display_fields, $index);
395
							$display_fields = array_merge(
396
								$start,
397
								array($def['vname'] => array('name' => $field, 'vname' => $def['vname'], 'width' => $def['width'] )),
398
								$end
399
							);
400
						} else
401
						{
402
							$display_fields[$def['vname']] = array(
403
								'name' => $field,
404
								'vname' => $def['vname'],
405
								'width' => $def['width'],
406
							);
407
						}
408
					}
409
				}
410
			}
411
		}
412
	}
413
414
	function isDatasourceFunction ()
415
	{
416
		if (strpos ( $this->get_inst_prop_value ( 'get_subpanel_data' ), 'function' ) === false)
417
		{
418
			return false ;
419
		}
420
		return true ;
421
	}
422
423
    /**
424
     * Test to see if the sub panels defs contain a collection
425
     *
426
     * @return bool
427
     */
428
	function isCollection ()
429
	{
430
		return ($this->get_inst_prop_value ( 'type' ) == 'collection');
431
	}
432
433
	//get value of a property defined at the panel instance level.
434
	function get_inst_prop_value ( $name )
435
	{
436
		return isset($this->_instance_properties[$name]) ? $this->_instance_properties [ $name ] : null;
437
	}
438
	//get value of a property defined at the panel definition level.
439
	function get_def_prop_value ( $name )
440
	{
441
		if (isset ( $this->panel_definition [ $name ] ))
442
		{
443
			return $this->panel_definition [ $name ] ;
444
		} else
445
		{
446
			return null ;
447
		}
448
	}
449
450
	//if datasource is of the type function then return the function name
451
	//else return the value as is.
452
	function get_function_parameters ()
453
	{
454
		$parameters = array ( ) ;
455
		if ($this->isDatasourceFunction ())
456
		{
457
			$parameters = $this->get_inst_prop_value ( 'function_parameters' ) ;
458
		}
459
		return $parameters ;
460
	}
461
462
	function get_data_source_name ( $check_set_subpanel_data = false )
463
	{
464
		$prop_value = null ;
465
		if ($check_set_subpanel_data)
466
		{
467
			$prop_value = $this->get_inst_prop_value ( 'set_subpanel_data' ) ;
468
		}
469
		if (! empty ( $prop_value ))
470
		{
471
			return $prop_value ;
472
		} else
0 ignored issues
show
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
473
		{
474
			//fall back to default behavior.
475
		}
476
		if ($this->isDatasourceFunction ())
477
		{
478
			return (substr_replace ( $this->get_inst_prop_value ( 'get_subpanel_data' ), '', 0, 9 )) ;
479
		} else
480
		{
481
			return $this->get_inst_prop_value ( 'get_subpanel_data' ) ;
482
		}
483
	}
484
485
	//returns the where clause for the query.
486
	function get_where ()
487
	{
488
		if($this->get_def_prop_value ( 'where' ) != '' && $this->search_query != ''){
489
			return $this->get_def_prop_value ( 'where' ).' AND '.$this->search_query;
490
		} else if($this->search_query != ''){
491
			return $this->search_query;
492
		}
493
		return $this->get_def_prop_value ( 'where' ) ;
494
	}
495
496
	function is_fill_in_additional_fields ()
497
	{
498
		// do both. inst_prop returns values from metadata/subpaneldefs.php and def_prop returns from subpanel/default.php
499
		$temp = $this->get_inst_prop_value ( 'fill_in_additional_fields' ) || $this->get_def_prop_value ( 'fill_in_additional_fields' ) ;
500
		return $temp ;
501
	}
502
503
	function get_list_fields ()
504
	{
505
		if (isset ( $this->panel_definition [ 'list_fields' ] ))
506
		{
507
			return $this->panel_definition [ 'list_fields' ] ;
508
		} else
509
		{
510
			return array ( ) ;
511
		}
512
	}
513
514
	function get_module_name ()
515
	{
516
		return $this->get_inst_prop_value ( 'module' ) ;
517
	}
518
519
	function get_name ()
520
	{
521
		return $this->name ;
522
	}
523
524
	//load subpanel module's table name and column fields.
525
	function load_module_info ()
526
	{
527
		global $beanList ;
528
		global $beanFiles ;
529
530
		$module_name = $this->get_module_name () ;
531
		if (! empty ( $module_name ))
532
		{
533
534
			$bean_name = $beanList [ $this->get_module_name () ] ;
535
536
			$this->bean_name = $bean_name ;
537
538
			include_once ($beanFiles [ $bean_name ]) ;
539
			$this->template_instance = new $bean_name ( ) ;
540
			$this->template_instance->force_load_details = true ;
541
			$this->table_name = $this->template_instance->table_name ;
542
			//$this->db_fields=$this->template_instance->column_fields;
543
		}
544
	}
545
	//this function is to be used only with sub-panels that are based
546
	//on collections.
547
	function get_header_panel_def ()
548
	{
549
		if (! empty ( $this->sub_subpanels ))
550
		{
551
			if (! empty ( $this->_instance_properties [ 'header_definition_from_subpanel' ] ) && ! empty ( $this->sub_subpanels [ $this->_instance_properties [ 'header_definition_from_subpanel' ] ] ))
552
			{
553
				return $this->sub_subpanels [ $this->_instance_properties [ 'header_definition_from_subpanel' ] ] ;
554
			} else
555
			{
556
				$display_fields = array();
557
				//If we are not pulling from a specific subpanel, create a list of all list fields and use that.
558
				foreach($this->sub_subpanels as $subpanel)
559
				{
560
					$list_fields = $subpanel->get_list_fields();
561
					foreach($list_fields as $field => $def)
0 ignored issues
show
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
562
					{
563
564
					}
565
				}
566
567
				reset ( $this->sub_subpanels ) ;
568
				return current ( $this->sub_subpanels ) ;
569
			}
570
		}
571
		return null ;
572
	}
573
574
	/**
575
	 * Returns an array of current properties of the class.
576
	 * It will simply give the class name for instances of classes.
577
	 */
578
	function _to_array ()
579
	{
580
		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 ) ) ;
581
	}
582
583
    /**
584
     * Sets definition of the subpanel
585
     *
586
     * @param array $definition
587
     */
588
    protected function set_panel_definition(array $definition)
589
    {
590
        $this->panel_definition = $definition;
591
    }
592
593
}
594
;
595
596
class SubPanelDefinitions
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
597
{
598
599
	var $_focus ;
600
	var $_visible_tabs_array ;
601
	var $panels ;
602
	var $layout_defs ;
603
604
	/**
605
	 * Enter description here...
606
	 *
607
	 * @param BEAN $focus - this is the bean you want to get the data from
608
	 * @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
609
	 * @param ARRAY $layout_def_override - if you wish to override the default loaded layout defs you pass them in here.
610
	 * @return SubPanelDefinitions
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
611
	 */
612
	function __construct ( $focus , $layout_def_key = '' , $layout_def_override = '' )
613
	{
614
		$this->_focus = $focus ;
615
		if (! empty ( $layout_def_override ))
616
		{
617
			$this->layout_defs = $layout_def_override ;
618
619
		} else
620
		{
621
			$this->open_layout_defs ( false, $layout_def_key ) ;
622
		}
623
	}
624
625
	/**
626
	 * This function returns an ordered list of all "tabs", actually subpanels, for this module
627
	 * The source list is obtained from the subpanel layout contained in the layout_defs for this module,
628
	 * found either in the modules metadata/subpaneldefs.php file, or in the modules custom/.../Ext/Layoutdefs/layoutdefs.ext.php file
629
	 * and filtered through an ACL check.
630
	 * Note that the keys for the resulting array of tabs are in practice the name of the underlying source relationship for the subpanel
631
	 * 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
632
	 * 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
633
	 * @param boolean 	Optional - include the subpanel title label in the return array (false)
634
	 * @return array	All tabs that pass an ACL check
635
	 */
636
	function get_available_tabs ($FromGetModuleSubpanels=false)
637
	{
638
		global $modListHeader ;
639
		global $modules_exempt_from_availability_check ;
640
641
		if (isset ( $this->_visible_tabs_array ))
642
			return $this->_visible_tabs_array ;
643
644
		if (empty($modListHeader))
645
		    $modListHeader = query_module_access_list($GLOBALS['current_user']);
646
647
		$this->_visible_tabs_array = array ( ) ; // bug 16820 - make sure this is an array for the later ksort
648
649
		if (isset ( $this->layout_defs [ 'subpanel_setup' ] )) // bug 17434 - belts-and-braces - check that we have some subpanels first
650
		{
651
			//retrieve list of hidden subpanels
652
			$hidden_panels = $this->get_hidden_subpanels();
653
654
			//activities is a special use case in that if it is hidden,
655
			//then the history tab should be hidden too.
656
			if(!empty($hidden_panels) && is_array($hidden_panels) && in_array('activities',$hidden_panels)){
657
				//add history to list hidden_panels
658
				$hidden_panels['history'] = 'history';
659
			}
660
661
			foreach ( $this->layout_defs [ 'subpanel_setup' ] as $key => $values_array )
662
			{
663
				//exclude if this subpanel is hidden from admin screens
664
                $module = $key;
665
                if ( isset($values_array['module']) )
666
                    $module = strtolower($values_array['module']);
667
				 if ($hidden_panels && is_array($hidden_panels) && (in_array($module, $hidden_panels) || array_key_exists($module, $hidden_panels)) ){
668
				 	//this panel is hidden, skip it
669
				 	continue;
670
				 }
671
672
				// make sure the module attribute is set, else none of this works...
673
				if ( !isset($values_array [ 'module' ])) {
674
					$GLOBALS['log']->debug("SubPanelDefinitions->get_available_tabs(): no module defined in subpaneldefs for '$key' =>" . var_export($values_array,true) . " - ingoring subpanel defintion") ;
675
					continue;
676
				}
677
678
				//check permissions.
679
				$exempt = array_key_exists ( $values_array [ 'module' ], $modules_exempt_from_availability_check ) ;
680
				$ok = $exempt || ( (! ACLController::moduleSupportsACL ( $values_array [ 'module' ] ) || ACLController::checkAccess ( $values_array [ 'module' ], 'list', true ) ) ) ;
681
682
				$GLOBALS [ 'log' ]->debug ( "SubPanelDefinitions->get_available_tabs(): " . $key . "= " . ( $exempt ? "exempt " : "not exempt " .( $ok ? " ACL OK" : "" ) ) ) ;
683
684
				if ( $ok )
685
				{
686
					while ( ! empty ( $this->_visible_tabs_array [ $values_array [ 'order' ] ] ) )
687
					{
688
						$values_array [ 'order' ] ++ ;
689
					}
690
691
					$this->_visible_tabs_array [ $values_array ['order'] ] = ($FromGetModuleSubpanels) ? array($key=>$values_array['title_key']) : $key ;
692
				}
693
			}
694
		}
695
696
		ksort ( $this->_visible_tabs_array ) ;
697
		return $this->_visible_tabs_array ;
698
	}
699
700
	/**
701
	 * Load the definition of the a sub-panel.
702
	 * Also the sub-panel is added to an array of sub-panels.
703
	 * use of reload has been deprecated, since the subpanel is initialized every time.
704
     *
705
     * @param string $name              The name of the sub-panel to reload
706
     * @param boolean $reload           Reload the sub-panel (unused)
707
     * @param boolean $original_only    Only load the original sub-panel and no custom ones
708
     * @return boolean|aSubPanel        Returns aSubPanel object or boolean false if one is not found or it can't be
709
     *      displayed due to ACL reasons.
710
	 */
711
	function load_subpanel ( $name , $reload = false , $original_only = false, $search_query = '', $collections = array() )
712
	{
713
		if (!is_dir('modules/' . $this->layout_defs [ 'subpanel_setup' ][ strtolower ( $name ) ] [ 'module' ]))
714
			return false;
715
716
		$subpanel = new aSubPanel ( $name, $this->layout_defs [ 'subpanel_setup' ] [ strtolower ( $name ) ], $this->_focus, $reload, $original_only, $search_query, $collections ) ;
717
718
		// only return the subpanel object if we can display it.
719
		if($subpanel->canDisplay == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
720
			return $subpanel;
721
		}
722
723
		// by default return false so we don't show anything if it's not required.
724
		return false;
725
	}
726
727
	/**
728
	 * Load the layout def file and associate the definition with a variable in the file.
729
	 */
730
	function open_layout_defs ( $reload = false , $layout_def_key = '' , $original_only = false )
731
	{
732
		$layout_defs [ $this->_focus->module_dir ] = array ( ) ;
733
		$layout_defs [ $layout_def_key ] = array ( ) ;
734
735
		if (empty ( $this->layout_defs ) || $reload || (! empty ( $layout_def_key ) && ! isset ( $layout_defs [ $layout_def_key ] )))
736
		{
737
			if (file_exists ( 'modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php' ))
738
				require ('modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php') ;
739
740
			if (! $original_only && file_exists ( 'custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php' ))
741
				require ('custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php') ;
742
743
			if (! empty ( $layout_def_key ))
744
				$this->layout_defs = $layout_defs [ $layout_def_key ] ;
745
			else
746
				$this->layout_defs = $layout_defs [ $this->_focus->module_dir ] ;
747
748
		}
749
750
	}
751
752
	/**
753
	 * Removes a tab from the list of loaded tabs.
754
	 * Returns true if successful, false otherwise.
755
	 * Hint: Used by Campaign's DetailView.
756
	 */
757
	function exclude_tab ( $tab_name )
758
	{
759
		$result = false ;
760
		//unset layout definition
761
		if (! empty ( $this->layout_defs [ 'subpanel_setup' ] [ $tab_name ] ))
762
		{
763
			unset ( $this->layout_defs [ 'subpanel_setup' ] [ $tab_name ] ) ;
764
		}
765
		//unset instance from _visible_tab_array
766
		if (! empty ( $this->_visible_tabs_array ))
767
		{
768
			$key = array_search ( $tab_name, $this->_visible_tabs_array ) ;
769
			if ($key !== false)
770
			{
771
				unset ( $this->_visible_tabs_array [ $key ] ) ;
772
			}
773
		}
774
		return $result ;
775
	}
776
777
778
	/**
779
	 * return all available subpanels that belong to the list of tab modules.  You can optionally return all
780
	 * available subpanels, and also optionally group by module (prepends the key with the bean class name).
781
	 */
782
	function get_all_subpanels( $return_tab_modules_only = true, $group_by_module = false )
783
	{
784
		global $moduleList, $beanFiles, $beanList, $module;
785
786
		//use tab controller function to get module list with named keys
787
		require_once("modules/MySettings/TabController.php");
788
		$modules_to_check = TabController::get_key_array($moduleList);
789
790
		//change case to match subpanel processing later on
791
		$modules_to_check = array_change_key_case($modules_to_check);
792
        // Append on the CampaignLog module, because that is where the subpanels point, not directly to Campaigns
793
        $modules_to_check['campaignlog'] = "CampaignLog";
794
795
796
		$spd = '';
797
		$spd_arr = array();
798
		//iterate through modules and build subpanel array
799
		foreach($modules_to_check as $mod_name){
800
801
			//skip if module name is not in bean list, otherwise get the bean class name
802
			if(!isset($beanList[$mod_name])) continue;
803
			$class = $beanList[$mod_name];
804
805
			//skip if class name is not in file list, otherwise require the bean file and create new class
806
			if(!isset($beanFiles[$class]) || !file_exists($beanFiles[$class])) continue;
807
808
			//retrieve subpanels for this bean
809
			require_once($beanFiles[$class]);
810
			$bean_class = new $class();
811
812
			//create new subpanel definition instance and get list of tabs
813
			$spd = new SubPanelDefinitions($bean_class) ;
814
			$sub_tabs = $spd->get_available_tabs();
815
816
			//add each subpanel to array of total subpanles
817
			foreach( $sub_tabs as $panel_key){
818
				$panel_key = strtolower($panel_key);
819
                $panel_module = $panel_key;
820
                if ( isset($spd->layout_defs['subpanel_setup'][$panel_key]['module']) )
821
                    $panel_module = strtolower($spd->layout_defs['subpanel_setup'][$panel_key]['module']);
822
                //if module_only flag is set, only if it is also in module array
823
				if($return_tab_modules_only && !array_key_exists($panel_module, $modules_to_check)) continue;
824
				$panel_key_name = $panel_module;
825
826
				//group_by_key_name is set to true, then array will hold an entry for each
827
				//subpanel, with the module name prepended in the key
828
				if($group_by_module) $panel_key_name = $class.'_'.$panel_key_name;
829
				//add panel name to subpanel array
830
				$spd_arr[$panel_key_name] = $panel_module;
831
			}
832
		}
833
		return 	$spd_arr;
834
	}
835
836
	/*
837
	 * save array of hidden panels to mysettings category in config table
838
	 */
839
	function set_hidden_subpanels($panels){
840
		$administration = new Administration();
841
		$serialized = base64_encode(serialize($panels));
842
		$administration->saveSetting('MySettings', 'hide_subpanels', $serialized);
843
	}
844
845
	/*
846
	 * retrieve hidden subpanels
847
	 */
848
	function get_hidden_subpanels(){
849
		global $moduleList;
850
851
		//create variable as static to minimize queries
852
		static $hidden_subpanels = null;
853
854
		// if the static value is not already cached, then retrieve it.
855
		if(empty($hidden_subpanels))
856
		{
857
858
			//create Administration object and retrieve any settings for panels
859
			$administration = new Administration();
860
			$administration->retrieveSettings('MySettings');
861
862
			if(isset($administration->settings) && isset($administration->settings['MySettings_hide_subpanels'])){
863
				$hidden_subpanels = $administration->settings['MySettings_hide_subpanels'];
864
				$hidden_subpanels = trim($hidden_subpanels);
865
866
				//make sure serialized string is not empty
867
				if (!empty($hidden_subpanels)){
868
					//decode and unserialize to retrieve the array
869
					$hidden_subpanels = base64_decode($hidden_subpanels);
870
					$hidden_subpanels = unserialize($hidden_subpanels);
871
872
					//Ensure modules saved in the preferences exist.
873
					//get user preference
874
					//unserialize and add to array if not empty
875
					$pref_hidden = array();
876
					foreach($pref_hidden as $id => $pref_hidden_panel) {
877
						$hidden_subpanels[] = $pref_hidden_panel;
878
					}
879
880
881
				}else{
882
					//no settings found, return empty
883
					return $hidden_subpanels;
884
				}
885
			}
886
			else
887
			{	//no settings found, return empty
888
				return $hidden_subpanels;
889
			}
890
		}
891
892
		return $hidden_subpanels;
893
	}
894
895
896
}
897
?>
898