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/SugarFields/Parsers/MetaParser.php (7 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 1
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 - 2014 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
 * MetaParser.php
44
 *
45
 * This is a utility base file to parse HTML
46
 * @author Collin Lee
47
 * @api
48
 */
49
class MetaParser {
50
51
var $mPHPFile;
52
var $mView;
53
var $mModule;
54
var $mCustomPanels;
55
56
function __construct() {
57
58
}
59
60
    /**
61
     * @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
62
     */
63
    function MetaParser(){
64
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
65
        if(isset($GLOBALS['log'])) {
66
            $GLOBALS['log']->deprecated($deprecatedMessage);
67
        }
68
        else {
69
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
70
        }
71
        self::__construct();
72
    }
73
74
75
function parse() {
76
   return "NOT AVAILABLE";
77
}
78
79
/**
80
 * getFormContents
81
 * Parses for contents enclosed within <form>...</form> tags
82
 */
83
function getFormContents($contents, $all = true) {
84
   if($all) {
85
      preg_match_all("'(<form[^>]*?>)(.*?)(</form[^>]*?>)'si", $contents, $matches);
86
      return $matches;
87
   }
88
89
   preg_match("'(<form[^>]*?>)(.*?)(</form[^>]*?>)'si", $contents, $matches);
90
   return $this->convertToTagElement($matches);
91
   //return $matches;
92
}
93
94
95
/**
96
 * getFormElements
97
 * Parses for input, select, textarea types from string content
98
 * @param $contents The String contents to parse
99
 * @return $matches Array of matches of PREG_SET_ORDER
0 ignored issues
show
The doc-type $matches could not be parsed: Unknown type name "$matches" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
100
 */
101
function getFormElements($contents) {
102
   preg_match_all("'(<[ ]*?)(textarea|input|select)([^>]*?)(>)'si", $contents, $matches, PREG_PATTERN_ORDER);
103
   $elems = array();
104
   foreach($matches[3] as $match) {
105
   	  $elems[] = $match;
106
   }
107
   return $elems;
108
}
109
110
111
/**
112
 * getFormElementsNames
113
 * Parses for the name values of input, select, textarea types from string content
114
 * @param $contents The String contents to parse
115
 * @return $matches Array of name/value pairs
0 ignored issues
show
The doc-type $matches could not be parsed: Unknown type name "$matches" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
116
 */
117
function getFormElementsNames($contents) {
118
   preg_match_all("'(<[ ]*?)(textarea|input|select)[^>]*?name=[\'\"]([^\'\"]*?)(\[\])?(_basic)?[\'\"]([^>]*?>)'si", $contents, $matches, PREG_PATTERN_ORDER);
119
   return !empty($matches[3]) ? $matches[3] : null;
120
}
121
122
123
/**
124
 * getTagAttribute
125
 * Returns the name/value of a tag attribute where name is set to $name
126
 * @param $name The name of the attribute
127
 * @param $contents The contents to parse
128
 * @param $filter Option regular expression to filter value
129
 * @return Array of name/value for matching attribute
130
 */
131
function getTagAttribute($name, $contents, $filter = '') {
132
   //$exp = "'".$name."[ ]*?=[ ]*?[\'\"]([a-zA-Z0-9\_\[\]]*)[\'\"]'si";
133
134
   $exp = "'".$name."[\s]*?=[\s]*?[\'\"]([^\'^\"]*?)[\'\"]'si";
135
   preg_match_all($exp, $contents, $matches, PREG_SET_ORDER);
136
   if(empty($filter)) {
137
   	  return !empty($matches[0][1]) ? $matches[0][1] : '';
138
   }
139
140
   $filtered = array();
141
   foreach($matches as $tag) {
0 ignored issues
show
The expression $matches of type null|array<integer,array<integer,string>> 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...
142
   	  if(preg_match($filter, $tag[1])) {
143
   	  	 $filtered[] = $tag;
144
   	  }
145
   }
146
   return $filtered;
147
}
148
149
/**
150
 * getTables
151
 * Returns an Array of the tables found in the file.  If $tableClass parameter
152
 * is supplied, it'll return only those tables that have a matching class attribute
153
 * equal to $tableClass
154
 * @param $tableClass Optional table class parameter value
155
 * @return Array of table elements found
156
 */
157
function getTables($tableClass = null, $contents) {
158
   preg_match_all("'(<table[^>]*?>)(.*?)(</table[^>]*?>)'si", $contents, $matches, PREG_SET_ORDER);
159
   if($tableClass == null) {
160
   	  return $matches;
161
   }
162
163
   $tables = array();
164
   foreach($matches as $key => $table) {
0 ignored issues
show
The expression $matches of type null|array<integer,array<integer,string>> 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...
165
   	  if(strpos($table[1], $tableClass) > 0) {
166
   	  	 $tables[] = $table;
167
   	  }
168
   }
169
   return $this->convertToTagElement($tables);
170
}
171
172
/**
173
 * getElementsByType
174
 *
175
 * Returns an Array of all elements matching type.  It will match
176
 * for the outermost tags.  For example given contents:
177
 * "<tr><td>Text <table><tr><td>a</td></tr></table></td></tr>"
178
 * and method call getElementsByType("<td>", $contents) returns
179
 * "<td>Text <table><tr><td>a</td></tr></table></td>"
180
 *
181
 * @param $type The type of element to parse out and return
182
 * @return a tag element format Array
183
 */
184
function getElementsByType($type, $contents) {
185
   $x = strlen($contents);
186
   $mark = 0;
187
   $count = 0;
188
   $stag1 = "<" . trim($type, " <>") . '>';
189
   $stag2 = "<" . trim($type, " <>") . ' ';
190
   $etag = "</".$type.">";
191
   $sincrement = strlen($stag1);
192
   $eincrement = strlen($etag);
193
   $sarr = array();
194
   $values = array();
195
196
   while($count < $x) {
197
   	     $stok = substr($contents, $count, $sincrement);
198
   	     $etok = substr($contents, $count, $eincrement);
199
   	     if($stok == $stag1 || $stok == $stag2) {
200
   	     	//Reset mark;
201
   	        if(count($sarr) == 0) {
202
   	           $mark = $count;
203
   	        }
204
            $sarr[] = $count;
205
206
   	     } else if($etok == $etag) {
207
   	        array_shift($sarr);
208
   	        if(count($sarr) == 0) {
209
   	           $val = substr($contents, $mark, ($count - $mark) + $eincrement);
210
   	           $values[] = $val;
211
   	           $mark = $count;
212
   	        }
213
   	     }
214
   	     $count++;
215
   }
216
217
   $count = 0;
218
   return $values;
219
}
220
221
222
223
/**
224
 * getElementValue
225
 *
226
 */
227
function getElementValue($type, $contents, $filter = "(.*?)") {
228
   $exp = "'<".$type."[^>]*?>".$filter."</".$type."[^>]*?>'si";
229
   preg_match($exp, $contents, $matches);
230
   return isset($matches[1]) ? $matches[1] : '';
231
}
232
233
234
function stripComments($contents) {
235
   return preg_replace("'(<!--.*?-->)'si", "", $contents);
236
}
237
238
/**
239
 * stripFlavorTags
240
 * This method accepts the file contents and uses the $GLOBALS['sugar_flavor'] value
241
 * to remove the flavor tags in the file contents if present.  If $GLOBALS['sugar_flavor']
242
 * is not set, it defaults to PRO flavor
243
 * @param $contents The file contents as a String value
244
 * @param $result The file contents with non-matching flavor tags and their nested comments removed
245
 */
246
function stripFlavorTags($contents) {
247
   $flavor = isset($GLOBALS['sugar_flavor']) ? $GLOBALS['sugar_flavor'] : 'PRO';
248
   $isPro = ($flavor == 'ENT' || $flavor == 'PRO') ? true : false;
249
   if($isPro) {
250
   	 $contents = preg_replace('/<!-- BEGIN: open_source -->.*?<!-- END: open_source -->/', '', $contents);
251
   } else {
252
   	 $contents = preg_replace('/<!-- BEGIN: pro -->.*?<!-- END: pro -->/', '', $contents);
253
   }
254
   return $contents;
255
}
256
257
/**
258
 * getMaxColumns
259
 * Returns the highest number of <td>...</td> blocks within a <tr>...</tr> block.
260
 * @param $contents The table contents to parse
261
 * @param $filter Optional filter to parse for an attribute within the td block.
262
 * @return The maximum column count
263
 */
264
function getMaxColumns($contents, $filter) {
265
   preg_match_all("'(<tr[^>]*?>)(.*?)(</tr[^>]*?>)'si", $contents, $matches, PREG_SET_ORDER);
266
   $max = 0;
267
   foreach($matches as $tableRows) {
0 ignored issues
show
The expression $matches of type null|array<integer,array<integer,string>> 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...
268
           $count = substr_count($tableRows[2], $filter);
269
           if($count > $max) {
270
           	  $max = $count;
271
           }
272
   }
273
274
   return $max;
275
}
276
277
function convertToTagElement($matches) {
278
279
   $elements = array();
280
281
   foreach($matches as $data) {
282
   	   // We need 4 because the 1,2,3 indexes make up start,body,end
283
	   if(count($data) == 4) {
284
	   	  $element = array();
285
	   	  $element['start'] = $data[1];
286
	   	  $element['body'] = $data[2];
287
	   	  $element['end'] = $data[3];
288
	   	  $elements[] = $element;
289
	   }
290
   }
291
292
   return empty($elements) ? $matches : $elements;
293
}
294
295
/*
296
 * trimHTML
297
 * This function removes the \r (return), \n (newline) and \t (tab) markup from string
298
 */
299
function trimHTML($contents) {
300
   $contents = str_replace(array("\r"), array(""), $contents);
301
   $contents = str_replace(array("\n"), array(""), $contents);
302
   $contents = str_replace(array("\t"), array(""), $contents);
303
   return $contents;
304
}
305
306
307
/**
308
 * getJavascript
309
 *
310
 * This method parses the given $contents String and grabs all <script...>...</script> blocks.
311
 * The method also converts values enclosed within "{...}" blocks that may need to be converted
312
 * to Smarty syntax.
313
 *
314
 * @param $contents The HTML String contents to parse
315
 *
316
 * @return $javascript The formatted script blocks or null if none found
0 ignored issues
show
The doc-type $javascript could not be parsed: Unknown type name "$javascript" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
317
 */
318
function getJavascript($contents, $addLiterals = true) {
319
320
$javascript = null;
321
322
//Check if there are Javascript blocks of code to process
323
preg_match_all("'(<script[^>]*?>)(.*?)(</script[^>]*?>)'si", $contents, $matches, PREG_PATTERN_ORDER);
324
if(empty($matches)) {
325
   return $javascript;
326
}
327
328
foreach($matches[0] as $scriptBlock) {
329
	    $javascript .= "\n" . $scriptBlock;
330
} //foreach
331
332
$javascript = substr($javascript, 1);
333
334
//Remove stuff first
335
//1) Calendar.setup {..} blocks
336
$javascript = preg_replace('/Calendar.setup[\s]*[\(][^\)]*?[\)][\s]*;/si', '', $javascript);
337
338
//Find all blocks that may need to be replaced with Smarty syntax
339
preg_match_all("'([\{])([a-zA-Z0-9_]*?)([\}])'si", $javascript, $matches, PREG_PATTERN_ORDER);
340
if(!empty($matches)) {
341
	$replace = array();
342
343
	foreach($matches[0] as $xTemplateCode) {
344
		    if(!isset($replace[$xTemplateCode])) {
345
		       $replace[$xTemplateCode] = str_replace("{", "{\$", $xTemplateCode);
346
		    } //if
347
	} //foreach
348
349
	$javascript = str_replace(array_keys($replace), array_values($replace), $javascript);
350
} //if
351
352
if(!$addLiterals) {
353
   return $javascript;
354
}
355
356
return $this->parseDelimiters($javascript);
357
358
}
359
360 2
static function parseDelimiters($javascript) {
361 2
	$newJavascript = '';
362 2
	$scriptLength = strlen($javascript);
363 2
	$count = 0;
364 2
	$inSmartyVariable = false;
365
366 2
	while($count < $scriptLength) {
367
368 2
	      if($inSmartyVariable) {
369
	      	 $start = $count;
370
	      	 $numOfChars = 1;
371
	      	 while(isset($javascript[$count]) && $javascript[$count] != '}') {
372
	      	 	   $count++;
373
	      	 	   $numOfChars++;
374
	      	 }
375
376
	      	 $newJavascript .= substr($javascript, $start, $numOfChars);
377
	      	 $inSmartyVariable = false;
378
379
	      } else {
380
381 2
			  $char = $javascript[$count];
382 2
			  $nextChar = ($count + 1 >= $scriptLength) ? '' : $javascript[$count + 1];
383
384 2
			  if($char == "{" && $nextChar == "$") {
385
			  	 $inSmartyVariable = true;
386
			  	 $newJavascript .= $javascript[$count];
387 2
			  } else if($char == "{") {
388 2
			  	 $newJavascript .=  " {ldelim} ";
389 2
			  } else if($char == "}") {
390 2
			  	 $newJavascript .= " {rdelim} ";
391
			  } else {
392 2
			     $newJavascript .= $javascript[$count];
393
			  }
394
	      }
395 2
		  $count++;
396
	} //while
397
398 2
	return $newJavascript;
399
}
400
401
/**
402
 * findAssignedVariableName
403
 * This method provides additional support in attempting to parse the  module's corresponding
404
 * PHP file for either the EditView or DetailView.  In the event that the subclasses cannot
405
 * find a matching vardefs.php entry in the HTML file, this method can be called to parse the
406
 * PHP file to see if the assignment was made using the bean's variable.  If so, we return
407
 * this variable name.
408
 *
409
 * @param $name The tag name found in the HTML file for which we want to search
410
 * @param $filePath The full file path for the HTML file
411
 * @return The variable name found in PHP file, original $name variable if not found
412
 */
413
function findAssignedVariableName($name, $filePath) {
414
415
	if($this->mPHPFile == "INVALID") {
416
	   return $name;
417
	}
418
419
	if(!isset($this->mPHPFile)) {
420
	   if(preg_match('/(.*?)(DetailView).html$/', $filePath, $matches)) {
421
	   	 $dir = $matches[1];
422
	   } else if(preg_match('/(.*?)(EditView).html$/', $filePath, $matches)) {
423
	   	 $dir = $matches[1];
424
	   }
425
426
	   if(!isset($dir) || !is_dir($dir)) {
427
	      $this->mPHPFile = "INVALID";
428
	      return $name;
429
	   }
430
431
       $filesInDir = $this->dirList($dir);
432
       $phpFile = $matches[2].'.*?[\.]php';
433
       foreach($filesInDir as $file) {
434
       	  if(preg_match("/$phpFile/", $file)) {
435
       	  	 $this->mPHPFile = $matches[1] . $file;
436
       	  	 break;
437
       	  }
438
       }
439
440
       if(!isset($this->mPHPFile) || !file_exists($this->mPHPFile)) {
441
       	  $this->mPHPFile = "INVALID";
442
       	  return $name;
443
       }
444
	}
445
446
	$phpContents = file_get_contents($this->mPHPFile);
447
	$uname = strtoupper($name);
448
	if(preg_match("/xtpl->assign[\(][\"\']".$uname."[\"\'][\s]*?,[\s]*?[\$]focus->(.*?)[\)]/si", $phpContents, $matches)) {
449
	   return $matches[1];
450
	}
451
	return $name;
452
}
453
454
455
/**
456
 * dirList
457
 * Utility method to list all the files in a given directory.
458
 *
459
 * @param $directory The directory to scan
460
 * @return $results The files in the directory that were found
0 ignored issues
show
The doc-type $results could not be parsed: Unknown type name "$results" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
461
 */
462
function dirList ($directory) {
463
464
    // create an array to hold directory list
465
    $results = array();
466
467
    // create a handler for the directory
468
    $handler = opendir($directory);
469
470
    // keep going until all files in directory have been read
471
    while ($file = readdir($handler)) {
472
        // if $file isn't this directory or its parent,
473
        // add it to the results array
474
        if ($file != '.' && $file != '..')
475
            $results[] = $file;
476
    }
477
478
    // tidy up: close the handler
479
    closedir($handler);
480
    return $results;
481
}
482
483
484
/**
485
 * isCustomField
486
 * This method checks the mixed variable $elementNames to see if it is a custom field.  A custom
487
 * field is simply defined as a field that ends with "_c".  If $elementNames is an Array
488
 * any matching custom field value will result in a true evaluation
489
 * @param $elementNames Array or String value of form element name(s).
490
 * @return String name of custom field; null if none found
491
 */
492
function getCustomField($elementNames) {
493
494
   if(!isset($elementNames) || (!is_string($elementNames) && !is_array($elementNames))) {
495
   	  return null;
496
   }
497
498
   if(is_string($elementNames)) {
499
   	  if(preg_match('/(.+_c)(_basic)?(\[\])?$/', $elementNames, $matches)) {
500
   	  	 return count($matches) == 1 ? $matches[0] : $matches[1];
501
   	  }
502
   	  return null;
503
   }
504
505
   foreach($elementNames as $name) {
506
   	  if(preg_match('/(.+_c)(_basic)?(\[\])?$/', $name, $matches)) {
507
   	  	 return count($matches) == 1 ? $matches[0] : $matches[1];
508
   	  }
509
   }
510
511
   return null;
512
}
513
514
function applyPreRules($moduleDir, $panels) {
515
   if(file_exists("include/SugarFields/Parsers/Rules/".$moduleDir."ParseRule.php")) {
516
	  require_once("include/SugarFields/Parsers/Rules/".$moduleDir."ParseRule.php");
517
	  $class = $moduleDir."ParseRule";
518
	  $parseRule = new $class();
519
	  $panels = $parseRule->preParse($panels, $this->mView);
520
   }
521
   return $panels;
522
}
523
524
function applyRules($moduleDir, $panels) {
525
   return $this->applyPostRules($moduleDir, $panels);
526
}
527
528
function applyPostRules($moduleDir, $panels) {
529
   //Run module specific rules
530
   if(file_exists("include/SugarFields/Parsers/Rules/".$moduleDir."ParseRule.php")) {
531
	  require_once("include/SugarFields/Parsers/Rules/".$moduleDir."ParseRule.php");
532
	  $class = $moduleDir."ParseRule";
533
	  $parseRule = new $class();
534
	  $panels = $parseRule->parsePanels($panels, $this->mView);
535
   }
536
537
   //Now run defined rules
538
   require_once("include/SugarFields/Parsers/Rules/ParseRules.php");
539
   $rules = ParseRules::getRules();
540
541
   foreach($rules as $rule) {
542
   	  if(!file_exists($rule['file'])) {
543
   	  	 $GLOBALS['log']->error("Cannot run rule for " . $rule['file']);
544
   	  	 continue;
545
   	  } //if
546
   	  require_once($rule['file']);
547
   	  $runRule = new $rule['class'];
548
   	  $panels = $runRule->parsePanels($panels, $this->mView);
549
550
   } //foreach
551
552
   return $panels;
553
}
554
555
function createFileContents($moduleDir, $panels, $templateMeta=array(), $htmlFilePath) {
556
557
$header = "<?php\n\n";
558
559
if(empty($templateMeta)) {
560
$header .= "\$viewdefs['$moduleDir']['$this->mView'] = array(
561
    'templateMeta' => array('maxColumns' => '2',
562
                            'widths' => array(
563
                                            array('label' => '10', 'field' => '30'),
564
                                            array('label' => '10', 'field' => '30')
565
                                            ),
566
    ),";
567
} else {
568
$header .= "\$viewdefs['$moduleDir']['$this->mView'] = array(
569
    'templateMeta' =>" . var_export($templateMeta, true) . ",";
570
}
571
572
//Replace all the @sq (single quote tags that may have been inserted)
573
$header = preg_replace('/\@sq/', "'", $header);
574
575
/*
576
$contents = file_get_contents($htmlFilePath);
577
578
$javascript = $this->getJavascript($contents, true);
579
580
if(!empty($javascript)) {
581
	$javascript = str_replace("'", "\\'", $javascript);
582
	$header .= "\n 'javascript' => '" . $javascript . "',\n";
583
} //if
584
*/
585
$header .= "\n 'panels' =>";
586
587
$footer = "
588
\n
589
);
590
?>";
591
592
   $metadata = '';
593
   $body = var_export($panels, true);
594
   $metadata = $header . $body . $footer;
595
   $metadata = preg_replace('/(\d+)[\s]=>[\s]?/',"",$metadata);
596
   return $metadata;
597
598
}
599
600
601
/**
602
 * mergePanels
603
 * This function merges the $panels Array against the $masterCopy's meta data definition
604
 * @param $panels meta data Array to merge
605
 * @param $moduleDir Directory name of the module
606
 * @param $masterCopy file path to the meta data master copy
607
 * @return Array of merged $panel definition
608
 */
609
function mergePanels($panels, $vardefs, $moduleDir, $masterCopy) {
610
   require($masterCopy);
611
   $masterpanels = $viewdefs[$moduleDir][$this->mView]['panels'];
612
   $hasMultiplePanels = $this->hasMultiplePanels($masterpanels);
613
614
   if(!$hasMultiplePanels) {
615
   	    $keys = array_keys($viewdefs[$moduleDir][$this->mView]['panels']);
616
        if(!empty($keys) && count($keys) == 1) {
617
        	if(strtolower($keys[0]) == 'default') {
618
        	   $masterpanels = array('default'=>$viewdefs[$moduleDir][$this->mView]['panels'][$keys[0]]);
619
        	} else {
620
        	   $firstPanel = array_values($viewdefs[$moduleDir][$this->mView]['panels']);
621
	           $masterpanels = array('default'=> $firstPanel[0]);
622
        	}
623
        } else {
624
        	$masterpanels = array('default'=>$viewdefs[$moduleDir][$this->mView]['panels']);
625
        }
626
   }
627
   foreach($masterpanels as $name=>$masterpanel) {
628
   	       if(isset($panels[$name])) {
629
	   	       	  // Get all the names in the panel
630
	   	       	  $existingElements = array();
631
	   	       	  $existingLocation = array();
632
633
	   	       	  foreach($panels[$name] as $rowKey=>$row) {
634
	   	       	  	 foreach($row as $colKey=>$column) {
635
	   	       	  	 	if(is_array($column) && !empty($column['name'])) {
636
	   	       	  	 	   $existingElements[$column['name']] = $column['name'];
637
	   	       	  	 	   $existingLocation[$column['name']] = array("panel"=>$name, "row"=>$rowKey, "col"=>$colKey);
638
	   	       	  	 	} else if(!is_array($column) && !empty($column)) {
639
	   	       	  	 	   $existingElements[$column] = $column;
640
	   	       	  	 	   $existingLocation[$column] = array("panel"=>$name, "row"=>$rowKey, "col"=>$colKey);
641
	   	       	  	 	}
642
	   	       	  	 } //foreach
643
	   	       	  } //foreach
644
645
	   	       	  // Now check against the $masterCopy
646
	   	       	  foreach($masterpanel as $rowKey=>$row) {
647
648
	   	       	  	 $addRow = array();
649
650
	   	       	  	 foreach($row as $colKey=>$column) {
651
	   	       	  	 	if(is_array($column) && isset($column['name'])) {
652
	   	       	  	 	   $id = $column['name'];
653
	   	       	  	 	} else if(!is_array($column) && !empty($column)) {
654
	   	       	  	 	   $id = $column;
655
	   	       	  	 	} else {
656
	   	       	  	 	   continue;
657
	   	       	  	 	}
658
	   	       	  	 	if(empty($existingElements[$id])) {
659
	   	       	  	 	   //Only add if
660
	   	       	  	 	   // 1) if it is a required field (as defined in metadata)
661
	   	       	  	 	   // 2) or if it has a customLabel and customCode (a very deep customization)
662
	   	       	  	 	   if((is_array($column) && !empty($column['displayParams']['required'])) ||
663
	   	       	  	 	      (is_array($column) && !empty($column['customCode']) && !empty($column['customLabel']))) {
664
	   	       	  	 	   	  $addRow[] = $column;
665
	   	       	  	 	   }
666
	   	       	  	 	} else {
667
	   	       	  	 	   //Use definition from master copy instead
668
	   	       	  	 	   $panels[$existingLocation[$id]['panel']][$existingLocation[$id]['row']][$existingLocation[$id]['col']] = $column;
669
	   	       	  	 	}
670
	   	       	  	 } //foreach
671
672
	   	       	  	 // Add it to the $panels
673
	   	       	  	 if(!empty($addRow)) {
674
	   	       	  	 	$panels[$name][] = $addRow;
675
	   	       	  	 }
676
	   	       	  } //foreach
677
678
   	       } else {
679
	   	       	  $panels[$name] = $masterpanel;
680
   	       }
681
   } //foreach
682
683
   // We're not done yet... go through the $panels Array now and try to remove duplicate
684
   // or empty panels
685
   foreach($panels as $name=>$panel) {
686
   	   if(count($panel) == 0 || !isset($masterpanels[$name])) {
687
   	   	  unset($panels[$name]);
688
   	   }
689
   } //foreach
690
691
   return $panels;
692
}
693
694
/**
695
 * mergeTemplateMeta
696
 * This function merges the $templateMeta Array against the $masterCopy's meta data definition
697
 * @param $templateMeta meta data Array to merge
698
 * @param $moduleDir Directory name of the module
699
 * @param $masterCopy file path to the meta data master copy
700
 * @return Array of merged $templateMeta definition
701
 */
702
function mergeTemplateMeta($templateMeta, $moduleDir, $masterCopy) {
703
   require($masterCopy);
704
   $masterTemplateMeta = $viewdefs[$moduleDir][$this->mView]['templateMeta'];
705
706
   if(isset($masterTemplateMeta['javascript'])) {
707
   	  //Insert the getJSPath code back into src value
708
   	  $masterTemplateMeta['javascript'] = preg_replace('/src\s*=\s*[\'\"].*?(modules\/|include\/)([^\.]*?\.js)([^\'\"]*?)[\'\"]/i', 'src="@sq . getJSPath(@sq${1}${2}@sq) . @sq"', $masterTemplateMeta['javascript']);
709
   }
710
711
   return $masterTemplateMeta;
712
}
713
714
function hasRequiredSpanLabel($html) {
715
   if(empty($html)) {
716
   	  return false;
717
   }
718
719
   return preg_match('/\<(div|span) class=(\")?required(\")?\s?>\*<\/(div|span)>/si', $html);
720
}
721
722
function hasMultiplePanels($panels) {
723
724
   if(!isset($panels) || empty($panels) || !is_array($panels)) {
725
   	  return false;
726
   }
727
728
   if(is_array($panels) && (count($panels) == 0 || count($panels) == 1)) {
729
   	  return false;
730
   }
731
732
   foreach($panels as $panel) {
733
   	  if(!empty($panel) && !is_array($panel)) {
734
   	  	 return false;
735
   	  } else {
736
   	  	 foreach($panel as $row) {
737
   	  	    if(!empty($row) && !is_array($row)) {
738
   	  	       return false;
739
   	  	    } //if
740
   	  	 } //foreach
741
   	  } //if-else
742
   } //foreach
743
744
   return true;
745
}
746
747
function getRelateFieldName($mixed='') {
748
   if(!is_array($mixed)) {
749
   	  return '';
750
   } else if(count($mixed) == 2){
751
      $id = '';
752
   	  $name = '';
753
   	  foreach($mixed as $el) {
754
   	  	 if(preg_match('/_id$/', $el)) {
755
   	  	    $id = $el;
756
   	  	 } else if(preg_match('/_name$/', $el)) {
757
   	  	    $name = $el;
758
   	  	 }
759
   	  }
760
   	  return (!empty($id) && !empty($name)) ? $name : '';
761
   }
762
   return '';
763
}
764
765
function getCustomPanels() {
766
   return $this->mCustomPanels;
767
}
768
769
/**
770
 * fixTablesWithMissingTr
771
 * This is a very crude function to fix instances where files declared a table as
772
 * <table...><td> instead of <table...><tr><td>.  Without this helper function, the
773
 * parsing could messed up.
774
 *
775
 */
776
function fixTablesWithMissingTr($tableContents) {
777
   if(preg_match('/(<table[^>]*?[\/]?>\s*?<td)/i', $tableContents, $matches)) {
778
   	  return preg_replace('/(<table[^>]*?[\/]?>\s*?<td)/i', '<table><tr><td', $tableContents);
779
   }
780
   return $tableContents;
781
}
782
783
/**
784
 * fixRowsWithMissingTr
785
 * This is a very crude function to fix instances where files have an </tr> tag immediately followed by a <td> tag
786
 */
787
function fixRowsWithMissingTr($tableContents) {
788
   if(preg_match('/(<\/tr[^>]*?[\/]?>\s*?<td)/i', $tableContents, $matches)) {
789
   	  return preg_replace('/(<\/tr[^>]*?[\/]?>\s*?<td)/i', '</tr><tr><td', $tableContents);
790
   }
791
   return $tableContents;
792
}
793
794
/**
795
 * fixDuplicateTrTags
796
 * This is a very crude function to fix instances where files have two consecutive <tr> tags
797
 */
798
function fixDuplicateTrTags($tableContents) {
799
   if(preg_match('/(<tr[^>]*?[\/]?>\s*?<tr)/i', $tableContents, $matches)) {
800
   	  return preg_replace('/(<tr[^>]*?[\/]?>\s*?<tr)/i', '<tr', $tableContents);
801
   }
802
   return $tableContents;
803
}
804
805
/**
806
 * findSingleVardefElement
807
 * Scans array of form elements to see if just one is a vardef element and, if so,
808
 * return that vardef name
809
 */
810
function findSingleVardefElement($formElements=array(), $vardefs=array()) {
811
   if(empty($formElements) || !is_array($formElements)) {
812
   	  return '';
813
   }
814
815
   $found = array();
816
   foreach($formElements as $el) {
817
   	   if(isset($vardefs[$el])) {
818
   	   	  $found[] = $el;
819
   	   }
820
   }
821
822
   return count($found) == 1 ? $found[0] : '';
823
}
824
825
826
}
827
?>
828