]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/SugarFields/Parsers/MetaParser.php
Release 6.4.0
[Github/sugarcrm.git] / include / SugarFields / Parsers / MetaParser.php
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-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38
39 /**
40  * MetaParser.php
41  *
42  * This is a utility base file to parse HTML
43  * @author Collin Lee
44  * @api
45  */
46 class MetaParser {
47
48 var $mPHPFile;
49 var $mView;
50 var $mModule;
51 var $mCustomPanels;
52
53 function MetaParser() {
54
55 }
56
57 function parse() {
58    return "NOT AVAILABLE";
59 }
60
61 /**
62  * getFormContents
63  * Parses for contents enclosed within <form>...</form> tags
64  */
65 function getFormContents($contents, $all = true) {
66    if($all) {
67       preg_match_all("'(<form[^>]*?>)(.*?)(</form[^>]*?>)'si", $contents, $matches);
68       return $matches;
69    }
70
71    preg_match("'(<form[^>]*?>)(.*?)(</form[^>]*?>)'si", $contents, $matches);
72    return $this->convertToTagElement($matches);
73    //return $matches;
74 }
75
76
77 /**
78  * getFormElements
79  * Parses for input, select, textarea types from string content
80  * @param $contents The String contents to parse
81  * @return $matches Array of matches of PREG_SET_ORDER
82  */
83 function getFormElements($contents) {
84    preg_match_all("'(<[ ]*?)(textarea|input|select)([^>]*?)(>)'si", $contents, $matches, PREG_PATTERN_ORDER);
85    $elems = array();
86    foreach($matches[3] as $match) {
87           $elems[] = $match;
88    }
89    return $elems;
90 }
91
92
93 /**
94  * getFormElementsNames
95  * Parses for the name values of input, select, textarea types from string content
96  * @param $contents The String contents to parse
97  * @return $matches Array of name/value pairs
98  */
99 function getFormElementsNames($contents) {
100    preg_match_all("'(<[ ]*?)(textarea|input|select)[^>]*?name=[\'\"]([^\'\"]*?)(\[\])?(_basic)?[\'\"]([^>]*?>)'si", $contents, $matches, PREG_PATTERN_ORDER);
101    return !empty($matches[3]) ? $matches[3] : null;
102 }
103
104
105 /**
106  * getTagAttribute
107  * Returns the name/value of a tag attribute where name is set to $name
108  * @param $name The name of the attribute
109  * @param $contents The contents to parse
110  * @param $filter Option regular expression to filter value
111  * @return Array of name/value for matching attribute
112  */
113 function getTagAttribute($name, $contents, $filter = '') {
114    //$exp = "'".$name."[ ]*?=[ ]*?[\'\"]([a-zA-Z0-9\_\[\]]*)[\'\"]'si";
115
116    $exp = "'".$name."[\s]*?=[\s]*?[\'\"]([^\'^\"]*?)[\'\"]'si";
117    preg_match_all($exp, $contents, $matches, PREG_SET_ORDER);
118    if(empty($filter)) {
119           return !empty($matches[0][1]) ? $matches[0][1] : '';
120    }
121
122    $filtered = array();
123    foreach($matches as $tag) {
124           if(preg_match($filter, $tag[1])) {
125                  $filtered[] = $tag;
126           }
127    }
128    return $filtered;
129 }
130
131 /**
132  * getTables
133  * Returns an Array of the tables found in the file.  If $tableClass parameter
134  * is supplied, it'll return only those tables that have a matching class attribute
135  * equal to $tableClass
136  * @param $tableClass Optional table class parameter value
137  * @return Array of table elements found
138  */
139 function getTables($tableClass = null, $contents) {
140    preg_match_all("'(<table[^>]*?>)(.*?)(</table[^>]*?>)'si", $contents, $matches, PREG_SET_ORDER);
141    if($tableClass == null) {
142           return $matches;
143    }
144
145    $tables = array();
146    foreach($matches as $key => $table) {
147           if(strpos($table[1], $tableClass) > 0) {
148                  $tables[] = $table;
149           }
150    }
151    return $this->convertToTagElement($tables);
152 }
153
154 /**
155  * getElementsByType
156  *
157  * Returns an Array of all elements matching type.  It will match
158  * for the outermost tags.  For example given contents:
159  * "<tr><td>Text <table><tr><td>a</td></tr></table></td></tr>"
160  * and method call getElementsByType("<td>", $contents) returns
161  * "<td>Text <table><tr><td>a</td></tr></table></td>"
162  *
163  * @param $type The type of element to parse out and return
164  * @return a tag element format Array
165  */
166 function getElementsByType($type, $contents) {
167    $x = strlen($contents);
168    $mark = 0;
169    $count = 0;
170    $stag1 = "<" . trim($type, " <>") . '>';
171    $stag2 = "<" . trim($type, " <>") . ' ';
172    $etag = "</".$type.">";
173    $sincrement = strlen($stag1);
174    $eincrement = strlen($etag);
175    $sarr = array();
176    $values = array();
177
178    while($count < $x) {
179              $stok = substr($contents, $count, $sincrement);
180              $etok = substr($contents, $count, $eincrement);
181              if($stok == $stag1 || $stok == $stag2) {
182                 //Reset mark;
183                 if(count($sarr) == 0) {
184                    $mark = $count;
185                 }
186             $sarr[] = $count;
187
188              } else if($etok == $etag) {
189                 array_shift($sarr);
190                 if(count($sarr) == 0) {
191                    $val = substr($contents, $mark, ($count - $mark) + $eincrement);
192                    $values[] = $val;
193                    $mark = $count;
194                 }
195              }
196              $count++;
197    }
198
199    $count = 0;
200    return $values;
201 }
202
203
204
205 /**
206  * getElementValue
207  *
208  */
209 function getElementValue($type, $contents, $filter = "(.*?)") {
210    $exp = "'<".$type."[^>]*?>".$filter."</".$type."[^>]*?>'si";
211    preg_match($exp, $contents, $matches);
212    return isset($matches[1]) ? $matches[1] : '';
213 }
214
215
216 function stripComments($contents) {
217    return preg_replace("'(<!--.*?-->)'si", "", $contents);
218 }
219
220 /**
221  * stripFlavorTags
222  * This method accepts the file contents and uses the $GLOBALS['sugar_flavor'] value
223  * to remove the flavor tags in the file contents if present.  If $GLOBALS['sugar_flavor']
224  * is not set, it defaults to PRO flavor
225  * @param $contents The file contents as a String value
226  * @param $result The file contents with non-matching flavor tags and their nested comments removed
227  */
228 function stripFlavorTags($contents) {
229    $flavor = isset($GLOBALS['sugar_flavor']) ? $GLOBALS['sugar_flavor'] : 'PRO';
230    $isPro = ($flavor == 'ENT' || $flavor == 'PRO') ? true : false;
231    if($isPro) {
232          $contents = preg_replace('/<!-- BEGIN: open_source -->.*?<!-- END: open_source -->/', '', $contents);
233    } else {
234          $contents = preg_replace('/<!-- BEGIN: pro -->.*?<!-- END: pro -->/', '', $contents);
235    }
236    return $contents;
237 }
238
239 /**
240  * getMaxColumns
241  * Returns the highest number of <td>...</td> blocks within a <tr>...</tr> block.
242  * @param $contents The table contents to parse
243  * @param $filter Optional filter to parse for an attribute within the td block.
244  * @return The maximum column count
245  */
246 function getMaxColumns($contents, $filter) {
247    preg_match_all("'(<tr[^>]*?>)(.*?)(</tr[^>]*?>)'si", $contents, $matches, PREG_SET_ORDER);
248    $max = 0;
249    foreach($matches as $tableRows) {
250            $count = substr_count($tableRows[2], $filter);
251            if($count > $max) {
252                   $max = $count;
253            }
254    }
255
256    return $max;
257 }
258
259 function convertToTagElement($matches) {
260
261    $elements = array();
262
263    foreach($matches as $data) {
264            // We need 4 because the 1,2,3 indexes make up start,body,end
265            if(count($data) == 4) {
266                   $element = array();
267                   $element['start'] = $data[1];
268                   $element['body'] = $data[2];
269                   $element['end'] = $data[3];
270                   $elements[] = $element;
271            }
272    }
273
274    return empty($elements) ? $matches : $elements;
275 }
276
277 /*
278  * trimHTML
279  * This function removes the \r (return), \n (newline) and \t (tab) markup from string
280  */
281 function trimHTML($contents) {
282    $contents = str_replace(array("\r"), array(""), $contents);
283    $contents = str_replace(array("\n"), array(""), $contents);
284    $contents = str_replace(array("\t"), array(""), $contents);
285    return $contents;
286 }
287
288
289 /**
290  * getJavascript
291  *
292  * This method parses the given $contents String and grabs all <script...>...</script> blocks.
293  * The method also converts values enclosed within "{...}" blocks that may need to be converted
294  * to Smarty syntax.
295  *
296  * @param $contents The HTML String contents to parse
297  *
298  * @return $javascript The formatted script blocks or null if none found
299  */
300 function getJavascript($contents, $addLiterals = true) {
301
302 $javascript = null;
303
304 //Check if there are Javascript blocks of code to process
305 preg_match_all("'(<script[^>]*?>)(.*?)(</script[^>]*?>)'si", $contents, $matches, PREG_PATTERN_ORDER);
306 if(empty($matches)) {
307    return $javascript;
308 }
309
310 foreach($matches[0] as $scriptBlock) {
311             $javascript .= "\n" . $scriptBlock;
312 } //foreach
313
314 $javascript = substr($javascript, 1);
315
316 //Remove stuff first
317 //1) Calendar.setup {..} blocks
318 $javascript = preg_replace('/Calendar.setup[\s]*[\(][^\)]*?[\)][\s]*;/si', '', $javascript);
319
320 //Find all blocks that may need to be replaced with Smarty syntax
321 preg_match_all("'([\{])([a-zA-Z0-9_]*?)([\}])'si", $javascript, $matches, PREG_PATTERN_ORDER);
322 if(!empty($matches)) {
323         $replace = array();
324
325         foreach($matches[0] as $xTemplateCode) {
326                     if(!isset($replace[$xTemplateCode])) {
327                        $replace[$xTemplateCode] = str_replace("{", "{\$", $xTemplateCode);
328                     } //if
329         } //foreach
330
331         $javascript = str_replace(array_keys($replace), array_values($replace), $javascript);
332 } //if
333
334 if(!$addLiterals) {
335    return $javascript;
336 }
337
338 return $this->parseDelimiters($javascript);
339
340 }
341
342 function parseDelimiters($javascript) {
343         $newJavascript = '';
344         $scriptLength = strlen($javascript);
345         $count = 0;
346         $inSmartyVariable = false;
347
348         while($count < $scriptLength) {
349
350               if($inSmartyVariable) {
351                  $start = $count;
352                  $numOfChars = 1;
353                  while(isset($javascript[$count]) && $javascript[$count] != '}') {
354                            $count++;
355                            $numOfChars++;
356                  }
357
358                  $newJavascript .= substr($javascript, $start, $numOfChars);
359                  $inSmartyVariable = false;
360
361               } else {
362
363                           $char = $javascript[$count];
364                           $nextChar = ($count + 1 >= $scriptLength) ? '' : $javascript[$count + 1];
365
366                           if($char == "{" && $nextChar == "$") {
367                                  $inSmartyVariable = true;
368                                  $newJavascript .= $javascript[$count];
369                           } else if($char == "{") {
370                                  $newJavascript .=  " {ldelim} ";
371                           } else if($char == "}") {
372                                  $newJavascript .= " {rdelim} ";
373                           } else {
374                              $newJavascript .= $javascript[$count];
375                           }
376               }
377                   $count++;
378         } //while
379
380         return $newJavascript;
381 }
382
383 /**
384  * findAssignedVariableName
385  * This method provides additional support in attempting to parse the  module's corresponding
386  * PHP file for either the EditView or DetailView.  In the event that the subclasses cannot
387  * find a matching vardefs.php entry in the HTML file, this method can be called to parse the
388  * PHP file to see if the assignment was made using the bean's variable.  If so, we return
389  * this variable name.
390  *
391  * @param $name The tag name found in the HTML file for which we want to search
392  * @param $filePath The full file path for the HTML file
393  * @return The variable name found in PHP file, original $name variable if not found
394  */
395 function findAssignedVariableName($name, $filePath) {
396
397         if($this->mPHPFile == "INVALID") {
398            return $name;
399         }
400
401         if(!isset($this->mPHPFile)) {
402            if(preg_match('/(.*?)(DetailView).html$/', $filePath, $matches)) {
403                  $dir = $matches[1];
404            } else if(preg_match('/(.*?)(EditView).html$/', $filePath, $matches)) {
405                  $dir = $matches[1];
406            }
407
408            if(!isset($dir) || !is_dir($dir)) {
409               $this->mPHPFile = "INVALID";
410               return $name;
411            }
412
413        $filesInDir = $this->dirList($dir);
414        $phpFile = $matches[2].'.*?[\.]php';
415        foreach($filesInDir as $file) {
416           if(preg_match("/$phpFile/", $file)) {
417                  $this->mPHPFile = $matches[1] . $file;
418                  break;
419           }
420        }
421
422        if(!isset($this->mPHPFile) || !file_exists($this->mPHPFile)) {
423           $this->mPHPFile = "INVALID";
424           return $name;
425        }
426         }
427
428         $phpContents = file_get_contents($this->mPHPFile);
429         $uname = strtoupper($name);
430         if(preg_match("/xtpl->assign[\(][\"\']".$uname."[\"\'][\s]*?,[\s]*?[\$]focus->(.*?)[\)]/si", $phpContents, $matches)) {
431            return $matches[1];
432         }
433         return $name;
434 }
435
436
437 /**
438  * dirList
439  * Utility method to list all the files in a given directory.
440  *
441  * @param $directory The directory to scan
442  * @return $results The files in the directory that were found
443  */
444 function dirList ($directory) {
445
446     // create an array to hold directory list
447     $results = array();
448
449     // create a handler for the directory
450     $handler = opendir($directory);
451
452     // keep going until all files in directory have been read
453     while ($file = readdir($handler)) {
454         // if $file isn't this directory or its parent,
455         // add it to the results array
456         if ($file != '.' && $file != '..')
457             $results[] = $file;
458     }
459
460     // tidy up: close the handler
461     closedir($handler);
462     return $results;
463 }
464
465
466 /**
467  * isCustomField
468  * This method checks the mixed variable $elementNames to see if it is a custom field.  A custom
469  * field is simply defined as a field that ends with "_c".  If $elementNames is an Array
470  * any matching custom field value will result in a true evaluation
471  * @param $elementNames Array or String value of form element name(s).
472  * @return String name of custom field; null if none found
473  */
474 function getCustomField($elementNames) {
475
476    if(!isset($elementNames) || (!is_string($elementNames) && !is_array($elementNames))) {
477           return null;
478    }
479
480    if(is_string($elementNames)) {
481           if(preg_match('/(.+_c)(_basic)?(\[\])?$/', $elementNames, $matches)) {
482                  return count($matches) == 1 ? $matches[0] : $matches[1];
483           }
484           return null;
485    }
486
487    foreach($elementNames as $name) {
488           if(preg_match('/(.+_c)(_basic)?(\[\])?$/', $name, $matches)) {
489                  return count($matches) == 1 ? $matches[0] : $matches[1];
490           }
491    }
492
493    return null;
494 }
495
496 function applyPreRules($moduleDir, $panels) {
497    if(file_exists("include/SugarFields/Parsers/Rules/".$moduleDir."ParseRule.php")) {
498           require_once("include/SugarFields/Parsers/Rules/".$moduleDir."ParseRule.php");
499           $class = $moduleDir."ParseRule";
500           $parseRule = new $class();
501           $panels = $parseRule->preParse($panels, $this->mView);
502    }
503    return $panels;
504 }
505
506 function applyRules($moduleDir, $panels) {
507    return $this->applyPostRules($moduleDir, $panels);
508 }
509
510 function applyPostRules($moduleDir, $panels) {
511    //Run module specific rules
512    if(file_exists("include/SugarFields/Parsers/Rules/".$moduleDir."ParseRule.php")) {
513           require_once("include/SugarFields/Parsers/Rules/".$moduleDir."ParseRule.php");
514           $class = $moduleDir."ParseRule";
515           $parseRule = new $class();
516           $panels = $parseRule->parsePanels($panels, $this->mView);
517    }
518
519    //Now run defined rules
520    require_once("include/SugarFields/Parsers/Rules/ParseRules.php");
521    $rules = ParseRules::getRules();
522
523    foreach($rules as $rule) {
524           if(!file_exists($rule['file'])) {
525                  $GLOBALS['log']->error("Cannot run rule for " . $rule['file']);
526                  continue;
527           } //if
528           require_once($rule['file']);
529           $runRule = new $rule['class'];
530           $panels = $runRule->parsePanels($panels, $this->mView);
531
532    } //foreach
533
534    return $panels;
535 }
536
537 function createFileContents($moduleDir, $panels, $templateMeta=array(), $htmlFilePath) {
538
539 $header = "<?php\n\n";
540
541 if(empty($templateMeta)) {
542 $header .= "\$viewdefs['$moduleDir']['$this->mView'] = array(
543     'templateMeta' => array('maxColumns' => '2',
544                             'widths' => array(
545                                             array('label' => '10', 'field' => '30'),
546                                             array('label' => '10', 'field' => '30')
547                                             ),
548     ),";
549 } else {
550 $header .= "\$viewdefs['$moduleDir']['$this->mView'] = array(
551     'templateMeta' =>" . var_export($templateMeta, true) . ",";
552 }
553
554 //Replace all the @sq (single quote tags that may have been inserted)
555 $header = preg_replace('/\@sq/', "'", $header);
556
557 /*
558 $contents = file_get_contents($htmlFilePath);
559
560 $javascript = $this->getJavascript($contents, true);
561
562 if(!empty($javascript)) {
563         $javascript = str_replace("'", "\\'", $javascript);
564         $header .= "\n 'javascript' => '" . $javascript . "',\n";
565 } //if
566 */
567 $header .= "\n 'panels' =>";
568
569 $footer = "
570 \n
571 );
572 ?>";
573
574    $metadata = '';
575    $body = var_export($panels, true);
576    $metadata = $header . $body . $footer;
577    $metadata = preg_replace('/(\d+)[\s]=>[\s]?/',"",$metadata);
578    return $metadata;
579
580 }
581
582
583 /**
584  * mergePanels
585  * This function merges the $panels Array against the $masterCopy's meta data definition
586  * @param $panels meta data Array to merge
587  * @param $moduleDir Directory name of the module
588  * @param $masterCopy file path to the meta data master copy
589  * @return Array of merged $panel definition
590  */
591 function mergePanels($panels, $vardefs, $moduleDir, $masterCopy) {
592    require($masterCopy);
593    $masterpanels = $viewdefs[$moduleDir][$this->mView]['panels'];
594    $hasMultiplePanels = $this->hasMultiplePanels($masterpanels);
595
596    if(!$hasMultiplePanels) {
597             $keys = array_keys($viewdefs[$moduleDir][$this->mView]['panels']);
598         if(!empty($keys) && count($keys) == 1) {
599                 if(strtolower($keys[0]) == 'default') {
600                    $masterpanels = array('default'=>$viewdefs[$moduleDir][$this->mView]['panels'][$keys[0]]);
601                 } else {
602                    $firstPanel = array_values($viewdefs[$moduleDir][$this->mView]['panels']);
603                    $masterpanels = array('default'=> $firstPanel[0]);
604                 }
605         } else {
606                 $masterpanels = array('default'=>$viewdefs[$moduleDir][$this->mView]['panels']);
607         }
608    }
609    foreach($masterpanels as $name=>$masterpanel) {
610                if(isset($panels[$name])) {
611                           // Get all the names in the panel
612                           $existingElements = array();
613                           $existingLocation = array();
614
615                           foreach($panels[$name] as $rowKey=>$row) {
616                                  foreach($row as $colKey=>$column) {
617                                         if(is_array($column) && !empty($column['name'])) {
618                                            $existingElements[$column['name']] = $column['name'];
619                                            $existingLocation[$column['name']] = array("panel"=>$name, "row"=>$rowKey, "col"=>$colKey);
620                                         } else if(!is_array($column) && !empty($column)) {
621                                            $existingElements[$column] = $column;
622                                            $existingLocation[$column] = array("panel"=>$name, "row"=>$rowKey, "col"=>$colKey);
623                                         }
624                                  } //foreach
625                           } //foreach
626
627                           // Now check against the $masterCopy
628                           foreach($masterpanel as $rowKey=>$row) {
629
630                                  $addRow = array();
631
632                                  foreach($row as $colKey=>$column) {
633                                         if(is_array($column) && isset($column['name'])) {
634                                            $id = $column['name'];
635                                         } else if(!is_array($column) && !empty($column)) {
636                                            $id = $column;
637                                         } else {
638                                            continue;
639                                         }
640                                         if(empty($existingElements[$id])) {
641                                            //Only add if
642                                            // 1) if it is a required field (as defined in metadata)
643                                            // 2) or if it has a customLabel and customCode (a very deep customization)
644                                            if((is_array($column) && !empty($column['displayParams']['required'])) ||
645                                               (is_array($column) && !empty($column['customCode']) && !empty($column['customLabel']))) {
646                                                   $addRow[] = $column;
647                                            }
648                                         } else {
649                                            //Use definition from master copy instead
650                                            $panels[$existingLocation[$id]['panel']][$existingLocation[$id]['row']][$existingLocation[$id]['col']] = $column;
651                                         }
652                                  } //foreach
653
654                                  // Add it to the $panels
655                                  if(!empty($addRow)) {
656                                         $panels[$name][] = $addRow;
657                                  }
658                           } //foreach
659
660                } else {
661                           $panels[$name] = $masterpanel;
662                }
663    } //foreach
664
665    // We're not done yet... go through the $panels Array now and try to remove duplicate
666    // or empty panels
667    foreach($panels as $name=>$panel) {
668            if(count($panel) == 0 || !isset($masterpanels[$name])) {
669                   unset($panels[$name]);
670            }
671    } //foreach
672
673    return $panels;
674 }
675
676 /**
677  * mergeTemplateMeta
678  * This function merges the $templateMeta Array against the $masterCopy's meta data definition
679  * @param $templateMeta meta data Array to merge
680  * @param $moduleDir Directory name of the module
681  * @param $masterCopy file path to the meta data master copy
682  * @return Array of merged $templateMeta definition
683  */
684 function mergeTemplateMeta($templateMeta, $moduleDir, $masterCopy) {
685    require($masterCopy);
686    $masterTemplateMeta = $viewdefs[$moduleDir][$this->mView]['templateMeta'];
687
688    if(isset($masterTemplateMeta['javascript'])) {
689           //Insert the getJSPath code back into src value
690           $masterTemplateMeta['javascript'] = preg_replace('/src\s*=\s*[\'\"].*?(modules\/|include\/)([^\.]*?\.js)([^\'\"]*?)[\'\"]/i', 'src="@sq . getJSPath(@sq${1}${2}@sq) . @sq"', $masterTemplateMeta['javascript']);
691    }
692
693    return $masterTemplateMeta;
694 }
695
696 function hasRequiredSpanLabel($html) {
697    if(empty($html)) {
698           return false;
699    }
700
701    return preg_match('/\<(div|span) class=(\")?required(\")?\s?>\*<\/(div|span)>/si', $html);
702 }
703
704 function hasMultiplePanels($panels) {
705
706    if(!isset($panels) || empty($panels) || !is_array($panels)) {
707           return false;
708    }
709
710    if(is_array($panels) && (count($panels) == 0 || count($panels) == 1)) {
711           return false;
712    }
713
714    foreach($panels as $panel) {
715           if(!empty($panel) && !is_array($panel)) {
716                  return false;
717           } else {
718                  foreach($panel as $row) {
719                     if(!empty($row) && !is_array($row)) {
720                        return false;
721                     } //if
722                  } //foreach
723           } //if-else
724    } //foreach
725
726    return true;
727 }
728
729 function getRelateFieldName($mixed='') {
730    if(!is_array($mixed)) {
731           return '';
732    } else if(count($mixed) == 2){
733       $id = '';
734           $name = '';
735           foreach($mixed as $el) {
736                  if(preg_match('/_id$/', $el)) {
737                     $id = $el;
738                  } else if(preg_match('/_name$/', $el)) {
739                     $name = $el;
740                  }
741           }
742           return (!empty($id) && !empty($name)) ? $name : '';
743    }
744    return '';
745 }
746
747 function getCustomPanels() {
748    return $this->mCustomPanels;
749 }
750
751 /**
752  * fixTablesWithMissingTr
753  * This is a very crude function to fix instances where files declared a table as
754  * <table...><td> instead of <table...><tr><td>.  Without this helper function, the
755  * parsing could messed up.
756  *
757  */
758 function fixTablesWithMissingTr($tableContents) {
759    if(preg_match('/(<table[^>]*?[\/]?>\s*?<td)/i', $tableContents, $matches)) {
760           return preg_replace('/(<table[^>]*?[\/]?>\s*?<td)/i', '<table><tr><td', $tableContents);
761    }
762    return $tableContents;
763 }
764
765 /**
766  * fixRowsWithMissingTr
767  * This is a very crude function to fix instances where files have an </tr> tag immediately followed by a <td> tag
768  */
769 function fixRowsWithMissingTr($tableContents) {
770    if(preg_match('/(<\/tr[^>]*?[\/]?>\s*?<td)/i', $tableContents, $matches)) {
771           return preg_replace('/(<\/tr[^>]*?[\/]?>\s*?<td)/i', '</tr><tr><td', $tableContents);
772    }
773    return $tableContents;
774 }
775
776 /**
777  * fixDuplicateTrTags
778  * This is a very crude function to fix instances where files have two consecutive <tr> tags
779  */
780 function fixDuplicateTrTags($tableContents) {
781    if(preg_match('/(<tr[^>]*?[\/]?>\s*?<tr)/i', $tableContents, $matches)) {
782           return preg_replace('/(<tr[^>]*?[\/]?>\s*?<tr)/i', '<tr', $tableContents);
783    }
784    return $tableContents;
785 }
786
787 /**
788  * findSingleVardefElement
789  * Scans array of form elements to see if just one is a vardef element and, if so,
790  * return that vardef name
791  */
792 function findSingleVardefElement($formElements=array(), $vardefs=array()) {
793    if(empty($formElements) || !is_array($formElements)) {
794           return '';
795    }
796
797    $found = array();
798    foreach($formElements as $el) {
799            if(isset($vardefs[$el])) {
800                   $found[] = $el;
801            }
802    }
803
804    return count($found) == 1 ? $found[0] : '';
805 }
806
807
808 }
809 ?>