]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/DynamicFields/DynamicField.php
Release 6.4.0
[Github/sugarcrm.git] / modules / DynamicFields / DynamicField.php
1 <?php
2 if (! defined ( 'sugarEntry' ) || ! sugarEntry)
3     die ( 'Not A Valid Entry Point' ) ;
4
5 /*********************************************************************************
6  * SugarCRM Community Edition is a customer relationship management program developed by
7  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
8  * 
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU Affero General Public License version 3 as published by the
11  * Free Software Foundation with the addition of the following permission added
12  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
13  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
14  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
15  * 
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
19  * details.
20  * 
21  * You should have received a copy of the GNU Affero General Public License along with
22  * this program; if not, see http://www.gnu.org/licenses or write to the Free
23  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24  * 02110-1301 USA.
25  * 
26  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
27  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
28  * 
29  * The interactive user interfaces in modified source and object code versions
30  * of this program must display Appropriate Legal Notices, as required under
31  * Section 5 of the GNU Affero General Public License version 3.
32  * 
33  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
34  * these Appropriate Legal Notices must retain the display of the "Powered by
35  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
36  * technical reasons, the Appropriate Legal Notices must display the words
37  * "Powered by SugarCRM".
38  ********************************************************************************/
39
40
41 class DynamicField {
42
43     var $use_existing_labels = false; // this value is set to true by install_custom_fields() in ModuleInstaller.php; everything else expects it to be false
44     var $base_path = "";
45
46     function DynamicField($module = '') {
47         $this->module = (! empty ( $module )) ? $module :( (isset($_REQUEST['module']) && ! empty($_REQUEST['module'])) ? $_REQUEST ['module'] : '');
48         $this->base_path = "custom/Extension/modules/{$this->module}/Ext/Vardefs";
49     }
50
51    function getModuleName()
52     {
53         return $this->module ;
54     }
55
56     /*
57      * As DynamicField has a counterpart in MBModule, provide the MBModule function getPackagename() here also
58      */
59     function getPackageName()
60     {
61         return null ;
62     }
63
64     function deleteCache(){
65     }
66
67
68     /**
69     * This will add the bean as a reference in this object as well as building the custom field cache if it has not been built
70     * LOADS THE BEAN IF THE BEAN IS NOT BEING PASSED ALONG IN SETUP IT SHOULD BE SET PRIOR TO SETUP
71     *
72     * @param SUGARBEAN $bean
73     */
74     function setup($bean = null) {
75         if ($bean) {
76             $this->bean = $bean;
77         }
78         if (isset ( $this->bean->module_dir )) {
79             $this->module = $this->bean->module_dir;
80         }
81         if(!isset($GLOBALS['dictionary'][$this->bean->object_name]['custom_fields'])){
82             $this->buildCache ( $this->module );
83         }
84     }
85
86     function setLabel( $language='en_us' , $key , $value )
87     {
88         $params [ "label_" . $key ] = $value;
89         require_once 'modules/ModuleBuilder/parsers/parser.label.php' ;
90         $parser = new ParserLabel ( $this->module ) ;
91         $parser->handleSave( $params , $language);
92     }
93
94     /**
95     * Builds the cache for custom fields based on the vardefs
96     *
97     * @param STRING $module
98     * @return unknown
99     */
100     function buildCache($module = false) {
101         //We can't build the cache while installing as the required database tables may not exist.
102         if (!empty($GLOBALS['installing']) && $GLOBALS['installing'] == true|| empty($GLOBALS['db']))
103             return false;
104         if($module == '../data')return false;
105
106         static $results = array ( ) ;
107
108         $where = '';
109         if (! empty ( $module )) {
110             $where .= " custom_module='$module' AND ";
111             unset( $results[ $module ] ) ; // clear out any old results for the module as $results is declared static
112         }
113         else
114         {
115             $results = array ( ) ; // clear out results - if we remove a module we don't want to have its old vardefs hanging around
116         }
117
118         $GLOBALS['log']->debug('rebuilding cache for ' . $module);
119         $query = "SELECT * FROM fields_meta_data WHERE $where deleted = 0";
120
121         $result = $GLOBALS['db']->query ( $query );
122         require_once ('modules/DynamicFields/FieldCases.php');
123
124         // retrieve the field definition from the fields_meta_data table
125         // using 'encode'=false to fetchByAssoc to prevent any pre-formatting of the base metadata
126         // for immediate use in HTML. This metadata will be further massaged by get_field_def() and so should not be pre-formatted
127         while ( $row = $GLOBALS['db']->fetchByAssoc ( $result, false ) ) {
128             $field = get_widget ( $row ['type'] );
129
130             foreach ( $row as $key => $value ) {
131                 $field->$key = $value;
132             }
133             $field->default = $field->default_value;
134             $vardef = $field->get_field_def ();
135             $vardef ['id'] = $row ['id'];
136             $vardef ['custom_module'] = $row ['custom_module'];
137             if (empty ( $vardef ['source'] ))
138                 $vardef ['source'] = 'custom_fields';
139             if (empty ( $results [$row ['custom_module']] ))
140                 $results [$row ['custom_module']] = array ( );
141             $results [$row ['custom_module']] [$row ['name']] = $vardef;
142         }
143         if (empty ( $module )) {
144             foreach ( $results as $module => $result ) {
145                 $this->saveToVardef ( $module, $result );
146             }
147         } else {
148             if (! empty ( $results [$module] )) {
149                 $this->saveToVardef ( $module, $results [$module] );
150             }else{
151                 $this->saveToVardef ( $module, false );
152             }
153         }
154
155         return true;
156
157     }
158
159     /**
160     * Returns the widget for a custom field from the fields_meta_data table.
161     */
162     function getFieldWidget($module, $fieldName) {
163         if (empty($module) || empty($fieldName)){
164             sugar_die("Unable to load widget for '$module' : '$fieldName'");
165         }
166         $query = "SELECT * FROM fields_meta_data WHERE custom_module='$module' AND name='$fieldName' AND deleted = 0";
167         $result = $GLOBALS['db']->query ( $query );
168         require_once ('modules/DynamicFields/FieldCases.php');
169         if ( $row = $GLOBALS['db']->fetchByAssoc ( $result ) ) {
170             $field = get_widget ( $row ['type'] );
171             $field->populateFromRow($row);
172             return $field;
173         }
174     }
175
176
177     /**
178     * Updates the cached vardefs with the custom field information stored in result
179     *
180     * @param string $module
181     * @param array $result
182     */
183     function saveToVardef($module,$result) {
184
185
186         global $beanList;
187         if (! empty ( $beanList [$module] )) {
188             $object = BeanFactory::getObjectName($module);
189
190             if(empty($GLOBALS['dictionary'][$object]['fields'])){
191                 //if the vardef isn't loaded let's try loading it.
192                 VardefManager::refreshVardefs($module,$object, null, false);
193                 //if it's still not loaded we really don't have anything useful to cache
194                 if(empty($GLOBALS['dictionary'][$object]['fields']))return;
195             }
196             $GLOBALS ['dictionary'] [$object] ['custom_fields'] = false;
197             if (! empty ( $GLOBALS ['dictionary'] [$object] )) {
198                 if (! empty ( $result )) {
199                     // First loop to add
200
201                 foreach ( $result as $field ) {
202                     foreach($field as $k=>$v){
203                         //allows values for custom fields to be defined outside of the scope of studio
204                         if(!isset($GLOBALS ['dictionary'] [$object] ['fields'] [$field ['name']][$k])){
205                             $GLOBALS ['dictionary'] [$object] ['fields'] [$field ['name']][$k] = $v;
206                         }
207                     }
208                 }
209
210                     // Second loop to remove
211                     foreach ( $GLOBALS ['dictionary'] [$object] ['fields'] as $name => $fieldDef ) {
212
213                         if (isset ( $fieldDef ['custom_module'] )) {
214                             if (! isset ( $result [$name] )) {
215                                 unset ( $GLOBALS ['dictionary'] [$object] ['fields'] [$name] );
216                             } else {
217                                 $GLOBALS ['dictionary'] [$object] ['custom_fields'] = true;
218                             }
219                         }
220
221                     } //if
222                 }
223             }
224             $manager = new VardefManager ( );
225             $manager->saveCache ( $this->module, $object );
226             // Everything works off of vardefs, so let's have it save the users vardefs
227             // to the employees module, because they both use the same table behind
228             // the scenes
229             if ( $module == 'Users' ) {
230                 $manager->loadVardef('Employees', 'Employee', true);
231                 return;
232             }
233
234         }
235     }
236
237     /**
238     * returns either false or an array containing the select and join parameter for a query using custom fields
239     * @param $expandedList boolean      If true, return a list of all fields with source=custom_fields in the select instead of the standard _cstm.*
240     *     This is required for any downstream construction of a SQL statement where we need to manipulate the select list,
241     *     for example, listviews with custom relate fields where the value comes from join rather than from the custom table
242     *
243     * @return array select=>select columns, join=>prebuilt join statement
244     */
245   function getJOIN( $expandedList = false , $includeRelates = false, &$where = false){
246         if(!$this->bean->hasCustomFields()){
247             return false;
248         }
249
250         if (empty($expandedList) )
251         {
252             $select = ",{$this->bean->table_name}_cstm.*" ;
253         }
254         else
255         {
256             $select = '';
257             $isList = is_array($expandedList);
258             foreach($this->bean->field_defs as $name=>$field)
259             {
260                 if (!empty($field['source']) && $field['source'] == 'custom_fields' && (!$isList || !empty($expandedList[$name]))){
261                     // assumption: that the column name in _cstm is the same as the field name. Currently true.
262                     // however, two types of dynamic fields do not have columns in the custom table - html fields (they're readonly) and flex relates (parent_name doesn't exist)
263                     if ( $field['type'] != 'html' && $name != 'parent_name')
264                         $select .= ",{$this->bean->table_name}_cstm.{$name}" ;
265                 }
266             }
267         }
268         $join = " LEFT JOIN " .$this->bean->table_name. "_cstm ON " .$this->bean->table_name. ".id = ". $this->bean->table_name. "_cstm.id_c ";
269
270         if ($includeRelates) {
271             $jtAlias = "relJoin";
272             $jtCount = 1;
273             foreach($this->bean->field_defs as $name=>$field)
274             {
275                 if ($field['type'] == 'relate' && isset($field['custom_module'])) {
276                     $relateJoinInfo = $this->getRelateJoin($field, $jtAlias.$jtCount);
277                     $select .= $relateJoinInfo['select'];
278                     $join .= $relateJoinInfo['from'];
279                     //bug 27654 martin
280                     if ($where)
281                     {
282                         $pattern = '/'.$field['name'].'\slike/i';
283                         $replacement = $relateJoinInfo['name_field'].' like';
284                         $where = preg_replace($pattern,$replacement,$where);
285                     }
286                     $jtCount++;
287                 }
288             }
289         }
290
291         return array('select'=>$select, 'join'=>$join);
292
293     }
294
295    function getRelateJoin($field_def, $joinTableAlias) {
296         if (empty($field_def['type']) || $field_def['type'] != "relate") {
297             return false;
298         }
299         global $beanFiles, $beanList, $module;
300         $rel_module = $field_def['module'];
301         if(empty($beanFiles[$beanList[$rel_module]])) {
302             return false;
303         }
304
305         require_once($beanFiles[$beanList[$rel_module]]);
306         $rel_mod = new $beanList[$rel_module]();
307         $rel_table = $rel_mod->table_name;
308         if (isset($rel_mod->field_defs['name']))
309         {
310             $name_field_def = $rel_mod->field_defs['name'];
311             if(isset($name_field_def['db_concat_fields']))
312             {
313                 $name_field = db_concat($joinTableAlias, $name_field_def['db_concat_fields']);
314             }
315             //If the name field is non-db, we need to find another field to display
316             else if(!empty($rel_mod->field_defs['name']['source']) && $rel_mod->field_defs['name']['source'] == "non-db" && !empty($field_def['rname']))
317             {
318                 $name_field = "$joinTableAlias." . $field_def['rname'];
319             }
320             else
321             {
322                 $name_field = "$joinTableAlias.name";
323             }
324         }
325         $tableName = isset($field_def['custom_module']) ? "{$this->bean->table_name}_cstm" : $this->bean->table_name ;
326         $relID = $field_def['id_name'];
327         $ret_array['rel_table'] = $rel_table;
328         $ret_array['name_field'] = $name_field;
329         $ret_array['select'] = ", {$tableName}.{$relID}, {$name_field} {$field_def['name']} ";
330         $ret_array['from'] = " LEFT JOIN $rel_table $joinTableAlias ON $tableName.$relID = $joinTableAlias.id"
331                             . " AND $joinTableAlias.deleted=0 ";
332         return $ret_array;
333    }
334
335    /**
336     * Fills in all the custom fields of type relate relationships for an object
337     *
338     */
339    function fill_relationships(){
340         global $beanList, $beanFiles;
341         if(!empty($this->bean->relDepth)) {
342             if($this->bean->relDepth > 1)return;
343         }else{
344             $this->bean->relDepth = 0;
345         }
346         foreach($this->bean->field_defs as $field){
347             if(empty($field['source']) || $field['source'] != 'custom_fields')continue;
348             if($field['type'] == 'relate'){
349                 $related_module =$field['ext2'];
350                 $name = $field['name'];
351                 if (empty($this->bean->$name)) { //Don't load the relationship twice
352                     $id_name = $field['id_name'];
353                     if(isset($beanList[ $related_module])){
354                         $class = $beanList[$related_module];
355
356                         if(file_exists($beanFiles[$class]) && isset($this->bean->$name)){
357                             require_once($beanFiles[$class]);
358                             $mod = new $class();
359                             $mod->relDepth = $this->bean->relDepth + 1;
360                             $mod->retrieve($this->bean->$id_name);
361                             $this->bean->$name = $mod->name;
362                         }
363                     }
364                 }
365             }
366         }
367     }
368
369     /**
370      * Process the save action for sugar bean custom fields
371      *
372      * @param boolean $isUpdate
373      */
374      function save($isUpdate){
375
376         if($this->bean->hasCustomFields() && isset($this->bean->id)){
377
378             if($isUpdate){
379                 $query = "UPDATE ". $this->bean->table_name. "_cstm SET ";
380             }
381             $queryInsert = "INSERT INTO ". $this->bean->table_name. "_cstm (id_c";
382             $values = "('".$this->bean->id."'";
383             $first = true;
384             foreach($this->bean->field_defs as $name=>$field){
385
386                 if(empty($field['source']) || $field['source'] != 'custom_fields')continue;
387                 if($field['type'] == 'html' || $field['type'] == 'parent')continue;
388                 if(isset($this->bean->$name)){
389                     $quote = "'";
390
391                     if(in_array($field['type'], array('int', 'float', 'double', 'uint', 'ulong', 'long', 'short', 'tinyint', 'currency', 'decimal'))) {
392                         $quote = '';
393                         if(!isset($this->bean->$name) || !is_numeric($this->bean->$name) ){
394                             if($field['required']){
395                                 $this->bean->$name = 0;
396                             }else{
397                                 $this->bean->$name = 'NULL';
398                             }
399                         }
400                     }
401                     if ( $field['type'] == 'bool' ) {
402                         if ( $this->bean->$name === FALSE )
403                             $this->bean->$name = '0';
404                         elseif ( $this->bean->$name === TRUE )
405                             $this->bean->$name = '1';
406                     }
407
408                     $val = $this->bean->$name;
409                                         if(($field['type'] == 'date' || $field['type'] == 'datetimecombo') && (empty($this->bean->$name )|| $this->bean->$name == '1900-01-01')){
410                         $quote = '';
411                         $val = 'NULL';
412                         $this->bean->$name = ''; // do not set it to string 'NULL'
413                     }
414                     if($isUpdate){
415                         if($first){
416                             $query .= " $name=$quote".$GLOBALS['db']->quote($val)."$quote";
417
418                         }else{
419                             $query .= " ,$name=$quote".$GLOBALS['db']->quote($val)."$quote";
420                         }
421                     }
422                     $first = false;
423                     $queryInsert .= " ,$name";
424                     $values .= " ,$quote". $GLOBALS['db']->quote($val). "$quote";
425                 }
426             }
427             if($isUpdate){
428                 $query.= " WHERE id_c='" . $this->bean->id ."'";
429
430             }
431
432             $queryInsert .= " ) VALUES $values )";
433
434             if(!$first){
435                 if(!$isUpdate){
436                     $GLOBALS['db']->query($queryInsert);
437                 }else{
438                     $checkquery = "SELECT id_c FROM {$this->bean->table_name}_cstm WHERE id_c = '{$this->bean->id}'";
439                     if ( $GLOBALS['db']->getOne($checkquery) ) {
440                         $result = $GLOBALS['db']->query($query);
441                     } else {
442                         $GLOBALS['db']->query($queryInsert);
443                     }
444                 }
445             }
446         }
447
448     }
449     /**
450      * Deletes the field from fields_meta_data and drops the database column then it rebuilds the cache
451      * Use the widgets get_db_modify_alter_table() method to get the table sql - some widgets do not need any custom table modifications
452      * @param STRING $name - field name
453      */
454     function deleteField($widget){
455         require_once('modules/DynamicFields/templates/Fields/TemplateField.php');
456         global $beanList;
457         if (!($widget instanceof TemplateField)) {
458             $field_name = $widget;
459             $widget = new TemplateField();
460             $widget->name = $field_name;
461         }
462         $object_name = $beanList[$this->module];
463
464         //Some modules like cases have a bean name that doesn't match the object name
465         if (empty($GLOBALS['dictionary'][$object_name])) {
466             $newName = BeanFactory::getObjectName($this->module);
467             $object_name = $newName != false ? $newName : $object_name;
468         }
469
470         $GLOBALS['db']->query("DELETE FROM fields_meta_data WHERE id='" . $this->module . $widget->name . "'");
471         $sql = $widget->get_db_delete_alter_table( $this->bean->table_name . "_cstm" ) ;
472         if (! empty( $sql ) )
473             $GLOBALS['db']->query( $sql );
474
475         $this->removeVardefExtension($widget);
476         VardefManager::clearVardef();
477         VardefManager::refreshVardefs($this->module, $object_name);
478
479     }
480
481     /*
482      * Method required by the TemplateRelatedTextField->save() method
483      * Taken from MBModule's implementation
484      */
485     function fieldExists($name = '', $type = ''){
486         // must get the vardefs from the GLOBAL array as $bean->field_defs does not contain the values from the cache at this point
487         // TODO: fix this - saveToVardefs() updates GLOBAL['dictionary'] correctly, obtaining its information directly from the fields_meta_data table via buildCache()...
488         $vardefs = $GLOBALS['dictionary'][$this->bean->object_name]['fields'];
489         if(!empty($vardefs)){
490             if(empty($type) && empty($name))
491                 return false;
492             else if(empty($type))
493                 return !empty($vardefs[$name]);
494             else if(empty($name)){
495                 foreach($vardefs as $def){
496                     if($def['type'] == $type)
497                         return true;
498                 }
499                 return false;
500             }else
501                 return (!empty($vardefs[$name]) && ($vardefs[$name]['type'] == $type));
502         }else{
503             return false;
504         }
505     }
506
507
508     /**
509      * Adds a custom field using a field object
510      *
511      * @param Field Object $field
512      * @return boolean
513      */
514     function addFieldObject(&$field){
515         $GLOBALS['log']->debug('adding field');
516         $object_name = $this->module;
517         $db_name = $field->name;
518
519         $fmd = new FieldsMetaData();
520         $id =  $fmd->retrieve($object_name.$db_name,true, false);
521         $is_update = false;
522         $label = strtoupper( $field->label );
523         if(!empty($id)){
524             $is_update = true;
525         }else{
526             $db_name = $this->getDBName($field->name);
527             $field->name = $db_name;
528         }
529         $this->createCustomTable();
530         $fmd->id = $object_name.$db_name;
531         $fmd->custom_module= $object_name;
532         $fmd->name = $db_name;
533         $fmd->vname = $label;
534         $fmd->type = $field->type;
535         $fmd->help = $field->help;
536         if (!empty($field->len))
537             $fmd->len = $field->len; // tyoung bug 15407 - was being set to $field->size so changes weren't being saved
538         $fmd->required = ($field->required ? 1 : 0);
539         $fmd->default_value = $field->default;
540         $fmd->ext1 = $field->ext1;
541         $fmd->ext2 = $field->ext2;
542         $fmd->ext3 = $field->ext3;
543         $fmd->ext4 = (isset($field->ext4) ? $field->ext4 : '');
544         $fmd->comments = $field->comment;
545         $fmd->massupdate = $field->massupdate;
546         $fmd->importable = ( isset ( $field->importable ) ) ? $field->importable : null ;
547         $fmd->duplicate_merge = $field->duplicate_merge;
548         $fmd->audited =$field->audited;
549         $fmd->reportable = ($field->reportable ? 1 : 0);
550         if(!$is_update){
551             $fmd->new_with_id=true;
552         }
553         $fmd->save();
554         $this->buildCache($this->module);
555         if($field){
556             if(!$is_update){
557                 //Do two SQL calls here in this case
558                 //The first is to create the column in the custom table without the default value
559                 //The second is to modify the column created in the custom table to set the default value
560                 //We do this so that the existing entries in the custom table don't have the default value set
561                 $field->default = '';
562                 $field->default_value = '';
563                 // resetting default and default_value does not work for multienum and causes trouble for mssql
564                 // so using a temporary variable here to indicate that we don't want default for this query
565                 $field->no_default = 1;
566                 $query = $field->get_db_add_alter_table($this->bean->table_name . '_cstm');
567                 // unsetting temporary member variable
568                 unset($field->no_default);
569                 if(!empty($query)){
570                         $GLOBALS['db']->query($query);
571                         $field->default = $fmd->default_value;
572                         $field->default_value = $fmd->default_value;
573                         $query = $field->get_db_modify_alter_table($this->bean->table_name . '_cstm');
574                         if(!empty($query)){
575                                 $GLOBALS['db']->query($query);
576                         }
577                 }
578             }else{
579                 $query = $field->get_db_modify_alter_table($this->bean->table_name . '_cstm');
580                 if(!empty($query)){
581                         $GLOBALS['db']->query($query);
582                 }
583             }
584             $this->saveExtendedAttributes($field, array_keys($fmd->field_defs));
585         }
586
587         return true;
588     }
589
590     function saveExtendedAttributes($field, $column_fields)
591     {
592             require_once ('modules/ModuleBuilder/parsers/StandardField.php') ;
593             require_once ('modules/DynamicFields/FieldCases.php') ;
594             global $beanList;
595
596             $to_save = array();
597             $base_field = get_widget ( $field->type) ;
598         foreach ($field->vardef_map as $property => $fmd_col){
599
600             if (!isset($field->$property) || in_array($fmd_col, $column_fields) || in_array($property, $column_fields)
601                 || $this->isDefaultValue($property, $field->$property, $base_field)
602                 || $property == "action" || $property == "label_value" || $property == "label"
603                 || (substr($property, 0,3) == 'ext' && strlen($property) == 4))
604             {
605                 continue;
606             }
607             $to_save[$property] =
608                 is_string($field->$property) ? htmlspecialchars_decode($field->$property, ENT_QUOTES) : $field->$property;
609         }
610         $bean_name = $beanList[$this->module];
611
612         $this->writeVardefExtension($bean_name, $field, $to_save);
613     }
614
615     protected function isDefaultValue($property, $value, $baseField)
616     {
617         switch ($property) {
618             case "importable":
619                 return ( $value === 'true' || $value === '1' || $value === true || $value === 1 ); break;
620             case "required":
621             case "audited":
622             case "massupdate":
623                 return ( $value === 'false' || $value === '0' || $value === false || $value === 0); break;
624             case "default_value":
625             case "default":
626             case "help":
627             case "comments":
628                 return ($value == "");
629             case "duplicate_merge":
630                 return ( $value === 'false' || $value === '0' || $value === false || $value === 0 || $value === "disabled"); break;
631         }
632
633         if (isset($baseField->$property))
634         {
635             return $baseField->$property == $value;
636         }
637
638         return false;
639     }
640
641     protected function writeVardefExtension($bean_name, $field, $def_override)
642     {
643         //Hack for the broken cases module
644         $vBean = $bean_name == "aCase" ? "Case" : $bean_name;
645         $file_loc = "$this->base_path/sugarfield_{$field->name}.php";
646
647         $out =  "<?php\n // created: " . date('Y-m-d H:i:s') . "\n";
648         foreach ($def_override as $property => $val)
649         {
650             $out .= override_value_to_string_recursive(array($vBean, "fields", $field->name, $property), "dictionary", $val) . "\n";
651         }
652
653         $out .= "\n ?>";
654
655         if (!file_exists($this->base_path))
656             mkdir_recursive($this->base_path);
657
658         if( $fh = @sugar_fopen( $file_loc, 'w' ) )
659         {
660             fputs( $fh, $out);
661             fclose( $fh );
662             return true ;
663         }
664         else
665         {
666             return false ;
667         }
668     }
669
670     protected function removeVardefExtension($field)
671     {
672         $file_loc = "$this->base_path/sugarfield_{$field->name}.php";
673
674         if (is_file($file_loc))
675         {
676             unlink($file_loc);
677         }
678     }
679
680
681     /**
682      * DEPRECIATED: Use addFieldObject instead.
683      * Adds a Custom Field using parameters
684      *
685      * @param unknown_type $name
686      * @param unknown_type $label
687      * @param unknown_type $type
688      * @param unknown_type $max_size
689      * @param unknown_type $required_option
690      * @param unknown_type $default_value
691      * @param unknown_type $ext1
692      * @param unknown_type $ext2
693      * @param unknown_type $ext3
694      * @param unknown_type $audited
695      * @param unknown_type $mass_update
696      * @param unknown_type $ext4
697      * @param unknown_type $help
698      * @param unknown_type $duplicate_merge
699      * @param unknown_type $comment
700      * @return boolean
701      */
702     function addField($name,$label='', $type='Text',$max_size='255',$required_option='optional', $default_value='', $ext1='', $ext2='', $ext3='',$audited=0, $mass_update = 0 , $ext4='', $help='',$duplicate_merge=0, $comment=''){
703         require_once('modules/DynamicFields/templates/Fields/TemplateField.php');
704         $field = new TemplateField();
705         $field->label = $label;
706         if(empty($field->label)){
707             $field->label = $name;
708         }
709         $field->name = $name;
710         $field->type = $type;
711         $field->len = $max_size;
712         $field->required = (!empty($required_option) && $required_option != 'optional');
713         $field->default = $default_value;
714         $field->ext1 = $ext1;
715         $field->ext2 = $ext2;
716         $field->ext3 = $ext3;
717         $field->ext4 = $ext4;
718         $field->help = $help;
719         $field->comments = $comment;
720         $field->massupdate = $mass_update;
721         $field->duplicate_merge = $duplicate_merge;
722         $field->audited = $audited;
723         $field->reportable = 1;
724         return $this->addFieldObject($field);
725     }
726
727     /**
728      * Creates the custom table with an id of id_c
729      *
730      */
731     function createCustomTable($execute = true){
732         $out = "";
733         if (!$GLOBALS['db']->tableExists($this->bean->table_name."_cstm")) {
734             $GLOBALS['log']->debug('creating custom table for '. $this->bean->table_name);
735             $iddef = array(
736                 "id_c" => array(
737                     "name" => "id_c",
738                     "type" => "id",
739                     "required" => 1,
740                 )
741             );
742             $ididx = array(
743                         'id'=>array(
744                                 'name' =>$this->bean->table_name."_cstm_pk",
745                                 'type' =>'primary',
746                                 'fields'=>array('id_c')
747                 ),
748            );
749
750             $query = $GLOBALS['db']->createTableSQLParams($this->bean->table_name."_cstm", $iddef, $ididx);
751             if(!$GLOBALS['db']->supports("inline_keys")) {
752                 $indicesArr = $GLOBALS['db']->getConstraintSql($ididx, $this->bean->table_name."_cstm");
753             } else {
754                 $indicesArr = array();
755             }
756             if($execute) {
757                 $GLOBALS['db']->query($query);
758                 if(!empty($indicesArr)) {
759                     foreach($indicesArr as $idxq) {
760                         $GLOBALS['db']->query($idxq);
761                     }
762                 }
763             }
764             $out = $query . "\n";
765             if(!empty($indicesArr)) {
766                 $out .= join("\n", $indicesArr)."\n";
767             }
768
769             $out .= $this->add_existing_custom_fields($execute);
770         }
771
772         return $out;
773     }
774
775     /**
776      * Updates the db schema and adds any custom fields we have used if the custom table was dropped
777      *
778      */
779     function add_existing_custom_fields($execute = true){
780         $out = "";
781         if($this->bean->hasCustomFields()){
782                 foreach($this->bean->field_defs as $name=>$data){
783                         if(empty($data['source']) || $data['source'] != 'custom_fields')
784                     continue;
785                     $out .= $this->add_existing_custom_field($data, $execute);
786                 }
787         }
788         return $out;
789     }
790
791     function add_existing_custom_field($data, $execute = true)
792     {
793
794         $field = get_widget ( $data ['type'] );
795         $field->populateFromRow($data);
796         $query = "/*MISSING IN DATABASE - {$data['name']} -  ROW*/\n"
797                 . $field->get_db_add_alter_table($this->bean->table_name . '_cstm');
798         $out = $query . "\n";
799         if ($execute)
800             $GLOBALS['db']->query($query);
801
802         return $out;
803     }
804
805     public function repairCustomFields($execute = true)
806     {
807         $out = $this->createCustomTable($execute);
808         //If the table didn't exist, createCustomTable will have returned all the SQL to create and populate it
809         if (!empty($out))
810             return "/*Checking Custom Fields for module : {$this->module} */\n$out";
811         //Otherwise make sure all the custom fields defined in the vardefs exist in the custom table.
812         //We aren't checking for data types, just that the column exists.
813         $db = $GLOBALS['db'];
814         $tablename = $this->bean->table_name."_cstm";
815         $compareFieldDefs = $db->get_columns($tablename);
816         foreach($this->bean->field_defs as $name=>$data){
817             if(empty($data['source']) || $data['source'] != 'custom_fields')
818                 continue;
819             /**
820              * @bug 43471
821              * @issue 43471
822              * @itr 23441
823              *
824              * force the name to be lower as it needs to be lower since that is how it's put into the key
825              * in the get_columns() call above.
826              */
827             if(!empty($compareFieldDefs[strtolower($name)])) {
828                 continue;
829             }
830             $out .= $this->add_existing_custom_field($data, $execute);
831         }
832         if (!empty($out))
833             $out = "/*Checking Custom Fields for module : {$this->module} */\n$out";
834
835         return $out;
836     }
837
838     /**
839      * Adds a label to the module's mod_strings for the current language
840      * Note that the system label name
841      *
842      * @param string $displayLabel The label value to be displayed
843      * @return string The core of the system label name - returns currency_id5 for example, when the full label would then be LBL_CURRENCY_ID5
844      * TODO: Only the core is returned for historical reasons - switch to return the real system label
845      */
846     function addLabel ( $displayLabel )
847     {
848         $mod_strings = return_module_language($GLOBALS[ 'current_language' ], $this->module);
849         $limit = 10;
850         $count = 0;
851         $field_key = $this->getDBName($displayLabel, false);
852         $systemLabel = $field_key;
853         if(!$this->use_existing_labels){ // use_existing_labels defaults to false in this module; as of today, only set to true by ModuleInstaller.php
854             while( isset( $mod_strings [ $systemLabel ] ) && $count <= $limit )
855             {
856                 $systemLabel = $field_key. "_$count";
857                 $count++;
858             }
859         }
860         $selMod = (!empty($_REQUEST['view_module'])) ? $_REQUEST['view_module'] : $this->module;
861         require_once 'modules/ModuleBuilder/parsers/parser.label.php' ;
862         $parser = new ParserLabel ( $selMod , isset ( $_REQUEST [ 'view_package' ] ) ? $_REQUEST [ 'view_package' ] : null ) ;
863         $parser->handleSave ( array('label_'. $systemLabel => $displayLabel ) , $GLOBALS [ 'current_language' ] ) ;
864
865         return $systemLabel;
866     }
867
868     /**
869      * Returns a Database Safe Name
870      *
871      * @param STRING $name
872      * @param BOOLEAN $_C do we append _c to the name
873      * @return STRING
874      */
875     function getDBName($name, $_C= true){
876         static $cached_results = array();
877         if(!empty($cached_results[$name]))
878         {
879             return $cached_results[$name];
880         }
881         $exclusions = array('parent_type', 'parent_id', 'currency_id', 'parent_name');
882         // Remove any non-db friendly characters
883         $return_value = preg_replace("/[^\w]+/","_",$name);
884         if($_C == true && !in_array($return_value, $exclusions) && substr($return_value, -2) != '_c'){
885             $return_value .= '_c';
886         }
887         $cached_results[$name] = $return_value;
888         return $return_value;
889     }
890
891     function setWhereClauses(&$where_clauses){
892         if (isset($this->avail_fields)) {
893             foreach($this->avail_fields as $name=>$value){
894                 if(!empty($_REQUEST[$name])){
895                     $where_clauses[] = $this->bean->table_name . "_cstm.$name LIKE '". $GLOBALS['db']->quote($_REQUEST[$name]). "%'";
896                 }
897             }
898         }
899
900     }
901
902     /////////////////////////BACKWARDS COMPATABILITY MODE FOR PRE 5.0 MODULES\\\\\\\\\\\\\\\\\\\\\\\\\\\
903     ////////////////////////////END BACKWARDS COMPATABILITY MODE FOR PRE 5.0 MODULES\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
904
905     /**
906      *
907      * DEPRECATED
908      loads fields into the bean
909      This used to be called during the retrieve process now it is done through a join
910      Restored from pre-r30895 to maintain support for custom code that may have called retrieve() directly
911      */
912
913     function retrieve()
914     {
915         if(!isset($this->bean)){
916             $GLOBALS['log']->fatal("DynamicField retrieve, bean not instantiated: ".var_export(debug_print_backtrace(), true));
917             return false;
918         }
919
920         if(!$this->bean->hasCustomFields()){
921             return false;
922         }
923
924         $query = "SELECT * FROM ".$this->bean->table_name."_cstm WHERE id_c='".$this->bean->id."'";
925         $result = $GLOBALS['db']->query($query);
926         $row = $GLOBALS['db']->fetchByAssoc($result);
927
928         if($row)
929         {
930             foreach($row as $name=>$value)
931             {
932                 // originally in pre-r30895 we checked if this field was in avail_fields i.e., in fields_meta_data and not deleted
933                 // with the removal of avail_fields post-r30895 we have simplified this - we now retrieve every custom field even if previously deleted
934                 // this is considered harmless as the value although set in the bean will not otherwise be used (nothing else works off the list of fields in the bean)
935                 $this->bean->$name = $value;
936             }
937         }
938
939     }
940
941    function populateXTPL($xtpl, $view) {
942
943         if($this->bean->hasCustomFields()){
944             $results = $this->getAllFieldsView($view, 'xtpl');
945             foreach($results as $name=>$value){
946                 if(is_array($value['xtpl']))
947                 {
948                     foreach($value['xtpl'] as $xName=>$xValue)
949                     {
950                         $xtpl->assign(strtoupper($xName), $xValue);
951                     }
952                 } else {
953                     $xtpl->assign(strtoupper($name), $value['xtpl']);
954                 }
955             }
956         }
957
958     }
959
960     function populateAllXTPL($xtpl, $view){
961         $this->populateXTPL($xtpl, $view);
962
963     }
964
965     function getAllFieldsView($view, $type){
966          require_once ('modules/DynamicFields/FieldCases.php');
967          $results = array();
968          foreach($this->bean->field_defs as $name=>$data){
969             if(empty($data['source']) || $data['source'] != 'custom_fields')
970             {
971                 continue;
972             }
973             $field = get_widget ( $data ['type'] );
974             $field->populateFromRow($data);
975             $field->view = $view;
976             $field->bean = $this->bean;
977             switch(strtolower($type))
978             {
979                 case 'xtpl':
980                     $results[$name] = array('xtpl'=>$field->get_xtpl());
981                     break;
982                 case 'html':
983                     $results[$name] = array('html'=> $field->get_html(), 'label'=> $field->get_html_label(), 'fieldType'=>$field->data_type, 'isCustom' =>true);
984                     break;
985             }
986
987         }
988         return $results;
989     }
990
991     ////////////////////////////END BACKWARDS COMPATABILITY MODE FOR PRE 5.0 MODULES\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
992 }
993
994 ?>