]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/TemplateHandler/TemplateHandler.php
Release 6.5.6
[Github/sugarcrm.git] / include / TemplateHandler / TemplateHandler.php
1 <?php
2 /*********************************************************************************
3  * SugarCRM Community Edition is a customer relationship management program developed by
4  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
5  * 
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  * 
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
16  * details.
17  * 
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  * 
23  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
25  * 
26  * The interactive user interfaces in modified source and object code versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  * 
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the "Powered by
32  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33  * technical reasons, the Appropriate Legal Notices must display the words
34  * "Powered by SugarCRM".
35  ********************************************************************************/
36
37
38
39 /**
40  * TemplateHandler builds templates using SugarFields and a generic view.
41  * Currently it handles EditViews and DetailViews. It creates a smarty template cached in
42  * cache/modules/moduleName/view
43  * @api
44  */
45 class TemplateHandler {
46     var $cacheDir;
47     var $templateDir = 'modules/';
48     var $ss;
49     function TemplateHandler() {
50       $this->cacheDir = sugar_cached('');
51     }
52
53     function loadSmarty(){
54         if(empty($this->ss)){
55             $this->ss = new Sugar_Smarty();
56         }
57     }
58
59
60     /**
61      * clearAll
62      * Helper function to remove all .tpl files in the cache directory
63      *
64      */
65     function clearAll() {
66         global $beanList;
67                 foreach($beanList as $module_dir =>$object_name){
68                 TemplateHandler::clearCache($module_dir);
69                 }
70     }
71
72
73     /**
74      * clearCache
75      * Helper function to remove cached .tpl files for a particular module
76      *
77      * @param String $module The module directory to clear
78      * @param String $view Optional view value (DetailView, EditView, etc.)
79      */
80     function clearCache($module, $view=''){
81         $cacheDir = create_cache_directory('modules/'. $module . '/');
82         $d = dir($cacheDir);
83         while($e = $d->read()){
84             if(!empty($view) && $e != $view )continue;
85             $end =strlen($e) - 4;
86             if(is_file($cacheDir . $e) && $end > 1 && substr($e, $end) == '.tpl'){
87                 unlink($cacheDir . $e);
88             }
89         }
90     }
91
92     /**
93      * Builds a template
94      * This is a private function that should be called only from checkTemplate method
95      *
96      * @param module string module name
97      * @param view string view need (eg DetailView, EditView, etc)
98      * @param tpl string generic tpl to use
99      * @param ajaxSave boolean parameter indicating whether or not this is coming from an Ajax call
100      * @param metaDataDefs metadata definition as Array
101      **/
102     function buildTemplate($module, $view, $tpl, $ajaxSave, $metaDataDefs) {
103         $this->loadSmarty();
104
105         $cacheDir = create_cache_directory($this->templateDir. $module . '/');
106         $file = $cacheDir . $view . '.tpl';
107         $string = '{* Create Date: ' . date('Y-m-d H:i:s') . "*}\n";
108         $this->ss->left_delimiter = '{{';
109         $this->ss->right_delimiter = '}}';
110         $this->ss->assign('module', $module);
111         $this->ss->assign('built_in_buttons', array('CANCEL', 'DELETE', 'DUPLICATE', 'EDIT', 'FIND_DUPLICATES', 'SAVE', 'CONNECTOR'));
112         $contents = $this->ss->fetch($tpl);
113         //Insert validation and quicksearch stuff here
114         if($view == 'EditView' || strpos($view,'QuickCreate') || $ajaxSave || $view == "ConvertLead") {
115
116             global $dictionary, $beanList, $app_strings, $mod_strings;
117             $mod = $beanList[$module];
118
119             if($mod == 'aCase') {
120                 $mod = 'Case';
121             }
122
123             $defs = $dictionary[$mod]['fields'];
124             $defs2 = array();
125             //Retrieve all panel field definitions with displayParams Array field set
126             $panelFields = array();
127
128             foreach($metaDataDefs['panels'] as $panel) {
129                     foreach($panel as $row) {
130                             foreach($row as $entry) {
131                                     if(empty($entry)) {
132                                        continue;
133                                     }
134
135                                     if(is_array($entry) &&
136                                        isset($entry['name']) &&
137                                        isset($entry['displayParams']) &&
138                                        isset($entry['displayParams']['required']) &&
139                                        $entry['displayParams']['required']) {
140                                        $panelFields[$entry['name']] = $entry;
141                                     }
142
143                                     if(is_array($entry)) {
144                                       $defs2[$entry['name']] = $entry;
145                                     } else {
146                                       $defs2[$entry] = array('name' => $entry);
147                                     }
148                             } //foreach
149                     } //foreach
150             } //foreach
151
152             foreach($panelFields as $field=>$value) {
153                       $nameList = array();
154                       if(!is_array($value['displayParams']['required'])) {
155                          $nameList[] = $field;
156                       } else {
157                          foreach($value['displayParams']['required'] as $groupedField) {
158                                  $nameList[] = $groupedField;
159                          }
160                       }
161
162                       foreach($nameList as $x) {
163                          if(isset($defs[$x]) &&
164                             isset($defs[$x]['type']) &&
165                             !isset($defs[$x]['required'])) {
166                             $defs[$x]['required'] = true;
167                          }
168                       }
169             } //foreach
170
171             //Create a base class with field_name_map property
172             $sugarbean = new stdClass;
173             $sugarbean->field_name_map = $defs;
174             $sugarbean->module_dir = $module;
175
176             $javascript = new javascript();
177             $view = $view == 'QuickCreate' ? "QuickCreate_{$module}" : $view;
178             $javascript->setFormName($view);
179
180             $javascript->setSugarBean($sugarbean);
181             if ($view != "ConvertLead")
182                 $javascript->addAllFields('', null,true);
183
184             $validatedFields = array();
185             $javascript->addToValidateBinaryDependency('assigned_user_name', 'alpha', $javascript->buildStringToTranslateInSmarty('ERR_SQS_NO_MATCH_FIELD').': '.$javascript->buildStringToTranslateInSmarty('LBL_ASSIGNED_TO'), 'false', '', 'assigned_user_id');
186             $validatedFields[] = 'assigned_user_name';
187             //Add remaining validation dependency for related fields
188             //1) a relate type as defined in vardefs
189             //2) set in metadata layout
190             //3) not have validateDepedency set to false in metadata
191             //4) have id_name in vardef entry
192             //5) not already been added to Array
193             foreach($sugarbean->field_name_map as $name=>$def) {
194
195                if($def['type']=='relate' &&
196                   isset($defs2[$name]) &&
197                   (!isset($defs2[$name]['validateDependency']) || $defs2[$name]['validateDependency'] === true) &&
198                   isset($def['id_name']) &&
199                   !in_array($name, $validatedFields)) {
200
201                   if(isset($mod_strings[$def['vname']])
202                         || isset($app_strings[$def['vname']])
203                         || translate($def['vname'],$sugarbean->module_dir) != $def['vname']) {
204                      $vname = $def['vname'];
205                   }
206                   else{
207                      $vname = "undefined";
208                   }
209                   $javascript->addToValidateBinaryDependency($name, 'alpha', $javascript->buildStringToTranslateInSmarty('ERR_SQS_NO_MATCH_FIELD').': '.$javascript->buildStringToTranslateInSmarty($vname), (!empty($def['required']) ? 'true' : 'false'), '', $def['id_name']);
210                   $validatedFields[] = $name;
211                }
212             } //foreach
213
214             $contents .= "{literal}\n";
215             $contents .= $javascript->getScript();
216             $contents .= $this->createQuickSearchCode($defs, $defs2, $view, $module);
217             $contents .= "{/literal}\n";
218         }else if(preg_match('/^SearchForm_.+/', $view)){
219             global $dictionary, $beanList, $app_strings, $mod_strings;
220             $mod = $beanList[$module];
221
222             if($mod == 'aCase') {
223                 $mod = 'Case';
224             }
225
226             $defs = $dictionary[$mod]['fields'];
227             $contents .= '{literal}';
228             $contents .= $this->createQuickSearchCode($defs, array(), $view);
229             $contents .= '{/literal}';
230         }//if
231
232         //Remove all the copyright comments
233         $contents = preg_replace('/\{\*[^\}]*?\*\}/', '', $contents);
234
235         if($fh = @sugar_fopen($file, 'w')) {
236             fputs($fh, $contents);
237             fclose($fh);
238         }
239
240
241         $this->ss->left_delimiter = '{';
242         $this->ss->right_delimiter = '}';
243     }
244
245     /**
246      * Checks if a template exists
247      *
248      * @param module string module name
249      * @param view string view need (eg DetailView, EditView, etc)
250      */
251     function checkTemplate($module, $view, $checkFormName = false, $formName='') {
252         if(inDeveloperMode() || !empty($_SESSION['developerMode'])){
253             return false;
254         }
255         $view = $checkFormName ? $formName : $view;
256         return file_exists($this->cacheDir . $this->templateDir . $module . '/' .$view . '.tpl');
257     }
258
259     /**
260      * Retreives and displays a template
261      *
262      * @param module string module name
263      * @param view string view need (eg DetailView, EditView, etc)
264      * @param tpl string generic tpl to use
265      * @param ajaxSave boolean parameter indicating whether or not this is from an Ajax operation
266      * @param metaData Optional metadata definition Array
267      */
268     function displayTemplate($module, $view, $tpl, $ajaxSave = false, $metaDataDefs = null) {
269         $this->loadSmarty();
270         if(!$this->checkTemplate($module, $view)) {
271             $this->buildTemplate($module, $view, $tpl, $ajaxSave, $metaDataDefs);
272         }
273         $file = $this->cacheDir . $this->templateDir . $module . '/' . $view . '.tpl';
274         if(file_exists($file)) {
275            return $this->ss->fetch($file);
276         } else {
277            global $app_strings;
278            $GLOBALS['log']->fatal($app_strings['ERR_NO_SUCH_FILE'] .": $file");
279            return $app_strings['ERR_NO_SUCH_FILE'] .": $file";
280         }
281     }
282
283     /**
284      * Deletes an existing template
285      *
286      * @param module string module name
287      * @param view string view need (eg DetailView, EditView, etc)
288      */
289     function deleteTemplate($module, $view) {
290         if(is_file($this->cacheDir . $this->templateDir . $module . '/' .$view . '.tpl')) {
291             // Bug #54634 : RTC 18144 : Cannot add more than 1 user to role but popup is multi-selectable
292             if ( !isset($this->ss) )
293             {
294                 $this->loadSmarty();
295             }
296             $cache_file_name = $this->ss->_get_compile_path($this->cacheDir . $this->templateDir . $module . '/' .$view . '.tpl');
297             SugarCache::cleanFile($cache_file_name);
298
299             return unlink($this->cacheDir . $this->templateDir . $module . '/' .$view . '.tpl');
300         }
301         return false;
302     }
303
304
305     /**
306      * createQuickSearchCode
307      * This function creates the $sqs_objects array that will be used by the quicksearch Javascript
308      * code.  The $sqs_objects array is wrapped in a $json->encode call.
309      *
310      * @param array $def The vardefs.php definitions
311      * @param array $defs2 The Meta-Data file definitions
312      * @param string $view
313      * @param strign $module
314      * @return string
315      */
316     public function createQuickSearchCode($defs, $defs2, $view = '', $module='')
317     {
318         $sqs_objects = array();
319         require_once('include/QuickSearchDefaults.php');
320         if(isset($this) && $this instanceof TemplateHandler) //If someone calls createQuickSearchCode as a static method (@see ImportViewStep3) $this becomes anoter object, not TemplateHandler
321         {
322             $qsd = QuickSearchDefaults::getQuickSearchDefaults($this->getQSDLookup());
323         }else
324         {
325             $qsd = QuickSearchDefaults::getQuickSearchDefaults(array());
326         }
327         $qsd->setFormName($view);
328         if(preg_match('/^SearchForm_.+/', $view)){
329                 if(strpos($view, 'popup_query_form')){
330                         $qsd->setFormName('popup_query_form');
331                 $parsedView = 'advanced';
332                 }else{
333                         $qsd->setFormName('search_form');
334                 $parsedView = preg_replace("/^SearchForm_/", "", $view);
335                 }
336             //Loop through the Meta-Data fields to see which ones need quick search support
337             foreach($defs as $f) {
338                 $field = $f;
339                 $name = $qsd->form_name . '_' . $field['name'];
340
341                 if($field['type'] == 'relate' && isset($field['module']) && preg_match('/_name$|_c$/si',$name)) {
342                     if(preg_match('/^(Campaigns|Teams|Users|Contacts|Accounts)$/si', $field['module'], $matches)) {
343
344                         if($matches[0] == 'Campaigns') {
345                             $sqs_objects[$name.'_'.$parsedView] = $qsd->loadQSObject('Campaigns', 'Campaign', $field['name'], $field['id_name'], $field['id_name']);
346                         } else if($matches[0] == 'Users'){
347
348                             if(!empty($f['name']) && !empty($f['id_name'])) {
349                                 $sqs_objects[$name.'_'.$parsedView] = $qsd->getQSUser($f['name'],$f['id_name']);
350                             }
351                             else {
352                                 $sqs_objects[$name.'_'.$parsedView] = $qsd->getQSUser();
353                             }
354                         } else if($matches[0] == 'Campaigns') {
355                             $sqs_objects[$name.'_'.$parsedView] = $qsd->loadQSObject('Campaigns', 'Campaign', $field['name'], $field['id_name'], $field['id_name']);
356                         } else if($matches[0] == 'Accounts') {
357                             $nameKey = $name;
358                             $idKey = isset($field['id_name']) ? $field['id_name'] : 'account_id';
359
360                             //There are billingKey, shippingKey and additionalFields entries you can define in editviewdefs.php
361                             //entry to allow quick search to autocomplete fields with a suffix value of the
362                             //billing/shippingKey value (i.e. 'billingKey' => 'primary' in Contacts will populate
363                             //primary_XXX fields with the Account's billing address values).
364                             //addtionalFields are key/value pair of fields to fill from Accounts(key) to Contacts(value)
365                             $billingKey = isset($f['displayParams']['billingKey']) ? $f['displayParams']['billingKey'] : null;
366                             $shippingKey = isset($f['displayParams']['shippingKey']) ? $f['displayParams']['shippingKey'] : null;
367                             $additionalFields = isset($f['displayParams']['additionalFields']) ? $f['displayParams']['additionalFields'] : null;
368                             $sqs_objects[$name.'_'.$parsedView] = $qsd->getQSAccount($nameKey, $idKey, $billingKey, $shippingKey, $additionalFields);
369                         } else if($matches[0] == 'Contacts'){
370                             $sqs_objects[$name.'_'.$parsedView] = $qsd->getQSContact($field['name'], $field['id_name']);
371                         }
372                     } else {
373                          $sqs_objects[$name.'_'.$parsedView] = $qsd->getQSParent($field['module']);
374                          if(!isset($field['field_list']) && !isset($field['populate_list'])) {
375                              $sqs_objects[$name.'_'.$parsedView]['populate_list'] = array($field['name'], $field['id_name']);
376                              $sqs_objects[$name.'_'.$parsedView]['field_list'] = array('name', 'id');
377                          } else {
378                              $sqs_objects[$name.'_'.$parsedView]['populate_list'] = $field['field_list'];
379                              $sqs_objects[$name.'_'.$parsedView]['field_list'] = $field['populate_list'];
380                          }
381                     }
382                 } else if($field['type'] == 'parent') {
383                     $sqs_objects[$name.'_'.$parsedView] = $qsd->getQSParent();
384                 } //if-else
385             } //foreach
386
387             foreach ( $sqs_objects as $name => $field )
388                foreach ( $field['populate_list'] as $key => $fieldname )
389                     $sqs_objects[$name]['populate_list'][$key] = $sqs_objects[$name]['populate_list'][$key] . '_'.$parsedView;
390         }else{
391             //Loop through the Meta-Data fields to see which ones need quick search support
392             foreach($defs2 as $f) {
393                 if(!isset($defs[$f['name']])) continue;
394
395                 $field = $defs[$f['name']];
396                 if ($view == "ConvertLead")
397                 {
398                     $field['name'] = $module . $field['name'];
399                     if (isset($field['module']) && isset($field['id_name']) && substr($field['id_name'], -4) == "_ida") {
400                         $lc_module = strtolower($field['module']);
401                         $ida_suffix = "_".$lc_module.$lc_module."_ida";
402                         if (preg_match('/'.$ida_suffix.'$/', $field['id_name']) > 0) {
403                             $field['id_name'] = $module . $field['id_name'];
404                         }
405                         else
406                             $field['id_name'] = $field['name'] . "_" . $field['id_name'];
407                     }
408                     else {
409                         if (!empty($field['id_name']))
410                             $field['id_name'] = $field['name'] . "_" . $field['id_name'];
411                     }
412                 }
413                                 $name = $qsd->form_name . '_' . $field['name'];
414
415
416                 if($field['type'] == 'relate' && isset($field['module']) && (preg_match('/_name$|_c$/si',$name) || !empty($field['quicksearch']))) {
417                     if (!preg_match('/_c$/si',$name)
418                         && (!isset($field['id_name']) || !preg_match('/_c$/si',$field['id_name']))
419                         && preg_match('/^(Campaigns|Teams|Users|Contacts|Accounts)$/si', $field['module'], $matches)
420                     ) {
421
422                         if($matches[0] == 'Campaigns') {
423                             $sqs_objects[$name] = $qsd->loadQSObject('Campaigns', 'Campaign', $field['name'], $field['id_name'], $field['id_name']);
424                         } else if($matches[0] == 'Users'){
425                             if($field['name'] == 'reports_to_name'){
426                                 $sqs_objects[$name] = $qsd->getQSUser('reports_to_name','reports_to_id');
427                              // Bug #52994 : QuickSearch for a 1-M User relationship changes assigned to user
428                             }elseif($field['name'] == 'assigned_user_name'){
429                                  $sqs_objects[$name] = $qsd->getQSUser('assigned_user_name','assigned_user_id');
430                              }
431                              else
432                              {
433                                  $sqs_objects[$name] = $qsd->getQSUser($field['name'], $field['id_name']);
434
435                                                         }
436                         } else if($matches[0] == 'Campaigns') {
437                             $sqs_objects[$name] = $qsd->loadQSObject('Campaigns', 'Campaign', $field['name'], $field['id_name'], $field['id_name']);
438                         } else if($matches[0] == 'Accounts') {
439                             $nameKey = $name;
440                             $idKey = isset($field['id_name']) ? $field['id_name'] : 'account_id';
441
442                             //There are billingKey, shippingKey and additionalFields entries you can define in editviewdefs.php
443                             //entry to allow quick search to autocomplete fields with a suffix value of the
444                             //billing/shippingKey value (i.e. 'billingKey' => 'primary' in Contacts will populate
445                             //primary_XXX fields with the Account's billing address values).
446                             //addtionalFields are key/value pair of fields to fill from Accounts(key) to Contacts(value)
447                             $billingKey = SugarArray::staticGet($f, 'displayParams.billingKey');
448                             $shippingKey = SugarArray::staticGet($f, 'displayParams.shippingKey');
449                             $additionalFields = SugarArray::staticGet($f, 'displayParams.additionalFields');
450                             $sqs_objects[$name] = $qsd->getQSAccount($nameKey, $idKey, $billingKey, $shippingKey, $additionalFields);
451                         } else if($matches[0] == 'Contacts'){
452                             $sqs_objects[$name] = $qsd->getQSContact($field['name'], $field['id_name']);
453                             if(preg_match('/_c$/si',$name) || !empty($field['quicksearch'])){
454                                 $sqs_objects[$name]['field_list'] = array('salutation', 'first_name', 'last_name', 'id');
455                             }
456                         }
457                     } else {
458                         $sqs_objects[$name] = $qsd->getQSParent($field['module']);
459                         if(!isset($field['field_list']) && !isset($field['populate_list'])) {
460                             $sqs_objects[$name]['populate_list'] = array($field['name'], $field['id_name']);
461                             // now handle quicksearches where the column to match is not 'name' but rather specified in 'rname'
462                             if (!isset($field['rname']))
463                                 $sqs_objects[$name]['field_list'] = array('name', 'id');
464                             else
465                             {
466                                 $sqs_objects[$name]['field_list'] = array($field['rname'], 'id');
467                                 $sqs_objects[$name]['order'] = $field['rname'];
468                                 $sqs_objects[$name]['conditions'] = array(array('name'=>$field['rname'],'op'=>'like_custom','end'=>'%','value'=>''));
469                             }
470                         } else {
471                             $sqs_objects[$name]['populate_list'] = $field['field_list'];
472                             $sqs_objects[$name]['field_list'] = $field['populate_list'];
473                         }
474                     }
475                 } else if($field['type'] == 'parent') {
476                     $sqs_objects[$name] = $qsd->getQSParent();
477                 } //if-else
478
479                 // Bug 53949 - Captivea (sve) - Partial fix : Append metadata fields that are not already included in $sqs_objects array
480                 // (for example with hardcoded modules before, metadata arrays are not taken into account in 6.4.x 6.5.x)
481                 // As QuickSearchDefault methods are called at other places, this will not fix the SQS problem for everywhere, but it fixes it on Editview
482
483                 //merge populate_list && field_list with vardef
484                 if (!empty($field['field_list']) && !empty($field['populate_list'])) {
485                     for ($j=0; $j<count($field['field_list']); $j++) {
486                                 //search for the same couple (field_list_item,populate_field_item)
487                                 $field_list_item = $field['field_list'][$j];
488                                 $field_list_item_alternate = $qsd->form_name . '_' . $field['field_list'][$j];
489                                 $populate_list_item = $field['populate_list'][$j];
490                                 $found = false;
491                                 for ($k=0; $k<count($sqs_objects[$name]['field_list']); $k++) {
492                                         if (($field_list_item == $sqs_objects[$name]['populate_list'][$k] || $field_list_item_alternate == $sqs_objects[$name]['populate_list'][$k]) && //il faut inverser field_list et populate_list (cf lignes 465,466 ci-dessus)
493                                                 $populate_list_item == $sqs_objects[$name]['field_list'][$k]) {
494                                                 $found = true;
495                                                 break;
496                                         }
497                                 }
498                                 if (!$found) {
499                                         $sqs_objects[$name]['field_list'][] = $field['populate_list'][$j]; // as in lines 462 and 463
500                                         $sqs_objects[$name]['populate_list'][] = $field['field_list'][$j];
501                                 }
502                         }
503                 }
504
505             } //foreach
506         }
507
508        //Implement QuickSearch for the field
509        if(!empty($sqs_objects) && count($sqs_objects) > 0) {
510            $quicksearch_js = '<script language="javascript">';
511            $quicksearch_js.= 'if(typeof sqs_objects == \'undefined\'){var sqs_objects = new Array;}';
512            $json = getJSONobj();
513            foreach($sqs_objects as $sqsfield=>$sqsfieldArray){
514                $quicksearch_js .= "sqs_objects['$sqsfield']={$json->encode($sqsfieldArray)};";
515            }
516            return $quicksearch_js . '</script>';
517        }
518        return '';
519     }
520
521     
522     /**
523      * Get lookup array for QuickSearchDefaults custom class
524      * @return array
525      * @see QuickSearchDefaults::getQuickSearchDefaults()
526      */
527     protected function getQSDLookup()
528     {
529         return array();
530     }
531 }
532 ?>