2 if (! defined ( 'sugarEntry' ) || ! sugarEntry)
3 die ( 'Not A Valid Entry Point' ) ;
5 /*********************************************************************************
6 * SugarCRM Community Edition is a customer relationship management program developed by
7 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
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.
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
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
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.
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.
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 ********************************************************************************/
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
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";
51 function getModuleName()
53 return $this->module ;
57 * As DynamicField has a counterpart in MBModule, provide the MBModule function getPackagename() here also
59 function getPackageName()
64 function deleteCache(){
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
72 * @param SUGARBEAN $bean
74 function setup($bean = null) {
78 if (isset ( $this->bean->module_dir )) {
79 $this->module = $this->bean->module_dir;
81 if(!isset($GLOBALS['dictionary'][$this->bean->object_name]['custom_fields'])){
82 $this->buildCache ( $this->module );
86 function setLabel( $language='en_us' , $key , $value )
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);
95 * Builds the cache for custom fields based on the vardefs
97 * @param STRING $module
98 * @param boolean saveCache Boolean value indicating whether or not to pass saveCache value to saveToVardef, defaults to true
101 function buildCache($module = false, $saveCache=true) {
102 //We can't build the cache while installing as the required database tables may not exist.
103 if (!empty($GLOBALS['installing']) && $GLOBALS['installing'] == true|| empty($GLOBALS['db']))
105 if($module == '../data')return false;
107 static $results = array ( ) ;
110 if (! empty ( $module )) {
111 $where .= " custom_module='$module' AND ";
112 unset( $results[ $module ] ) ; // clear out any old results for the module as $results is declared static
116 $results = array ( ) ; // clear out results - if we remove a module we don't want to have its old vardefs hanging around
119 $GLOBALS['log']->debug('rebuilding cache for ' . $module);
120 $query = "SELECT * FROM fields_meta_data WHERE $where deleted = 0";
122 $result = $GLOBALS['db']->query ( $query );
123 require_once ('modules/DynamicFields/FieldCases.php');
125 // retrieve the field definition from the fields_meta_data table
126 // using 'encode'=false to fetchByAssoc to prevent any pre-formatting of the base metadata
127 // for immediate use in HTML. This metadata will be further massaged by get_field_def() and so should not be pre-formatted
128 while ( $row = $GLOBALS['db']->fetchByAssoc ( $result, false ) ) {
129 $field = get_widget ( $row ['type'] );
131 foreach ( $row as $key => $value ) {
132 $field->$key = $value;
134 $field->default = $field->default_value;
135 $vardef = $field->get_field_def ();
136 $vardef ['id'] = $row ['id'];
137 $vardef ['custom_module'] = $row ['custom_module'];
138 if (empty ( $vardef ['source'] ))
139 $vardef ['source'] = 'custom_fields';
140 if (empty ( $results [$row ['custom_module']] ))
141 $results [$row ['custom_module']] = array ( );
142 $results [$row ['custom_module']] [$row ['name']] = $vardef;
144 if (empty ( $module )) {
145 foreach ( $results as $module => $result ) {
146 $this->saveToVardef ( $module, $result, $saveCache);
149 if (! empty ( $results [$module] )) {
150 $this->saveToVardef ( $module, $results [$module], $saveCache);
152 $this->saveToVardef ( $module, false, $saveCache);
161 * Returns the widget for a custom field from the fields_meta_data table.
163 function getFieldWidget($module, $fieldName) {
164 if (empty($module) || empty($fieldName)){
165 sugar_die("Unable to load widget for '$module' : '$fieldName'");
167 $query = "SELECT * FROM fields_meta_data WHERE custom_module='$module' AND name='$fieldName' AND deleted = 0";
168 $result = $GLOBALS['db']->query ( $query );
169 require_once ('modules/DynamicFields/FieldCases.php');
170 if ( $row = $GLOBALS['db']->fetchByAssoc ( $result ) ) {
171 $field = get_widget ( $row ['type'] );
172 $field->populateFromRow($row);
179 * Updates the cached vardefs with the custom field information stored in result
181 * @param string $module
182 * @param array $result
183 * @param boolean saveCache Boolean value indicating whether or not to call VardefManager::saveCache, defaults to true
185 function saveToVardef($module,$result,$saveCache=true) {
189 if (! empty ( $beanList [$module] )) {
190 $object = BeanFactory::getObjectName($module);
192 if(empty($GLOBALS['dictionary'][$object]['fields'])){
193 //if the vardef isn't loaded let's try loading it.
194 VardefManager::refreshVardefs($module,$object, null, false);
195 //if it's still not loaded we really don't have anything useful to cache
196 if(empty($GLOBALS['dictionary'][$object]['fields']))return;
198 $GLOBALS ['dictionary'] [$object] ['custom_fields'] = false;
199 if (! empty ( $GLOBALS ['dictionary'] [$object] )) {
200 if (! empty ( $result )) {
203 foreach ( $result as $field ) {
204 foreach($field as $k=>$v){
205 //allows values for custom fields to be defined outside of the scope of studio
206 if(!isset($GLOBALS ['dictionary'] [$object] ['fields'] [$field ['name']][$k])){
207 $GLOBALS ['dictionary'] [$object] ['fields'] [$field ['name']][$k] = $v;
212 // Second loop to remove
213 foreach ( $GLOBALS ['dictionary'] [$object] ['fields'] as $name => $fieldDef ) {
215 if (isset ( $fieldDef ['custom_module'] )) {
216 if (! isset ( $result [$name] )) {
217 unset ( $GLOBALS ['dictionary'] [$object] ['fields'] [$name] );
219 $GLOBALS ['dictionary'] [$object] ['custom_fields'] = true;
227 $manager = new VardefManager();
230 $manager->saveCache ($this->module, $object);
233 // Everything works off of vardefs, so let's have it save the users vardefs
234 // to the employees module, because they both use the same table behind
236 if ($module == 'Users')
238 $manager->loadVardef('Employees', 'Employee', true);
246 * returns either false or an array containing the select and join parameter for a query using custom fields
247 * @param $expandedList boolean If true, return a list of all fields with source=custom_fields in the select instead of the standard _cstm.*
248 * This is required for any downstream construction of a SQL statement where we need to manipulate the select list,
249 * for example, listviews with custom relate fields where the value comes from join rather than from the custom table
251 * @return array select=>select columns, join=>prebuilt join statement
253 function getJOIN( $expandedList = false , $includeRelates = false, &$where = false){
254 if(!$this->bean->hasCustomFields()){
258 if (empty($expandedList) )
260 $select = ",{$this->bean->table_name}_cstm.*" ;
265 $isList = is_array($expandedList);
266 foreach($this->bean->field_defs as $name=>$field)
268 if (!empty($field['source']) && $field['source'] == 'custom_fields' && (!$isList || !empty($expandedList[$name]))){
269 // assumption: that the column name in _cstm is the same as the field name. Currently true.
270 // 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)
271 if ( $field['type'] != 'html' && $name != 'parent_name')
272 $select .= ",{$this->bean->table_name}_cstm.{$name}" ;
276 $join = " LEFT JOIN " .$this->bean->table_name. "_cstm ON " .$this->bean->table_name. ".id = ". $this->bean->table_name. "_cstm.id_c ";
278 if ($includeRelates) {
279 $jtAlias = "relJoin";
281 foreach($this->bean->field_defs as $name=>$field)
283 if ($field['type'] == 'relate' && isset($field['custom_module'])) {
284 $relateJoinInfo = $this->getRelateJoin($field, $jtAlias.$jtCount);
285 $select .= $relateJoinInfo['select'];
286 $join .= $relateJoinInfo['from'];
290 $pattern = '/'.$field['name'].'\slike/i';
291 $replacement = $relateJoinInfo['name_field'].' like';
292 $where = preg_replace($pattern,$replacement,$where);
299 return array('select'=>$select, 'join'=>$join);
303 function getRelateJoin($field_def, $joinTableAlias) {
304 if (empty($field_def['type']) || $field_def['type'] != "relate") {
307 global $beanFiles, $beanList, $module;
308 $rel_module = $field_def['module'];
309 if(empty($beanFiles[$beanList[$rel_module]])) {
313 require_once($beanFiles[$beanList[$rel_module]]);
314 $rel_mod = new $beanList[$rel_module]();
315 $rel_table = $rel_mod->table_name;
316 if (isset($rel_mod->field_defs['name']))
318 $name_field_def = $rel_mod->field_defs['name'];
319 if(isset($name_field_def['db_concat_fields']))
321 $name_field = db_concat($joinTableAlias, $name_field_def['db_concat_fields']);
323 //If the name field is non-db, we need to find another field to display
324 else if(!empty($rel_mod->field_defs['name']['source']) && $rel_mod->field_defs['name']['source'] == "non-db" && !empty($field_def['rname']))
326 $name_field = "$joinTableAlias." . $field_def['rname'];
330 $name_field = "$joinTableAlias.name";
333 $tableName = isset($field_def['custom_module']) ? "{$this->bean->table_name}_cstm" : $this->bean->table_name ;
334 $relID = $field_def['id_name'];
335 $ret_array['rel_table'] = $rel_table;
336 $ret_array['name_field'] = $name_field;
337 $ret_array['select'] = ", {$tableName}.{$relID}, {$name_field} {$field_def['name']} ";
338 $ret_array['from'] = " LEFT JOIN $rel_table $joinTableAlias ON $tableName.$relID = $joinTableAlias.id"
339 . " AND $joinTableAlias.deleted=0 ";
344 * Fills in all the custom fields of type relate relationships for an object
347 function fill_relationships(){
348 global $beanList, $beanFiles;
349 if(!empty($this->bean->relDepth)) {
350 if($this->bean->relDepth > 1)return;
352 $this->bean->relDepth = 0;
354 foreach($this->bean->field_defs as $field){
355 if(empty($field['source']) || $field['source'] != 'custom_fields')continue;
356 if($field['type'] == 'relate'){
357 $related_module =$field['ext2'];
358 $name = $field['name'];
359 if (empty($this->bean->$name)) { //Don't load the relationship twice
360 $id_name = $field['id_name'];
361 if(isset($beanList[ $related_module])){
362 $class = $beanList[$related_module];
364 if(file_exists($beanFiles[$class]) && isset($this->bean->$name)){
365 require_once($beanFiles[$class]);
367 $mod->relDepth = $this->bean->relDepth + 1;
368 $mod->retrieve($this->bean->$id_name);
369 $this->bean->$name = $mod->name;
378 * Process the save action for sugar bean custom fields
380 * @param boolean $isUpdate
382 function save($isUpdate){
384 if($this->bean->hasCustomFields() && isset($this->bean->id)){
387 $query = "UPDATE ". $this->bean->table_name. "_cstm SET ";
389 $queryInsert = "INSERT INTO ". $this->bean->table_name. "_cstm (id_c";
390 $values = "('".$this->bean->id."'";
392 foreach($this->bean->field_defs as $name=>$field){
394 if(empty($field['source']) || $field['source'] != 'custom_fields')continue;
395 if($field['type'] == 'html' || $field['type'] == 'parent')continue;
396 if(isset($this->bean->$name)){
399 if(in_array($field['type'], array('int', 'float', 'double', 'uint', 'ulong', 'long', 'short', 'tinyint', 'currency', 'decimal'))) {
401 if(!isset($this->bean->$name) || !is_numeric($this->bean->$name) ){
402 if($field['required']){
403 $this->bean->$name = 0;
405 $this->bean->$name = 'NULL';
409 if ( $field['type'] == 'bool' ) {
410 if ( $this->bean->$name === FALSE )
411 $this->bean->$name = '0';
412 elseif ( $this->bean->$name === TRUE )
413 $this->bean->$name = '1';
416 $val = $this->bean->$name;
417 if(($field['type'] == 'date' || $field['type'] == 'datetimecombo') && (empty($this->bean->$name )|| $this->bean->$name == '1900-01-01')){
420 $this->bean->$name = ''; // do not set it to string 'NULL'
424 $query .= " $name=$quote".$GLOBALS['db']->quote($val)."$quote";
427 $query .= " ,$name=$quote".$GLOBALS['db']->quote($val)."$quote";
431 $queryInsert .= " ,$name";
432 $values .= " ,$quote". $GLOBALS['db']->quote($val). "$quote";
436 $query.= " WHERE id_c='" . $this->bean->id ."'";
440 $queryInsert .= " ) VALUES $values )";
444 $GLOBALS['db']->query($queryInsert);
446 $checkquery = "SELECT id_c FROM {$this->bean->table_name}_cstm WHERE id_c = '{$this->bean->id}'";
447 if ( $GLOBALS['db']->getOne($checkquery) ) {
448 $result = $GLOBALS['db']->query($query);
450 $GLOBALS['db']->query($queryInsert);
458 * Deletes the field from fields_meta_data and drops the database column then it rebuilds the cache
459 * Use the widgets get_db_modify_alter_table() method to get the table sql - some widgets do not need any custom table modifications
460 * @param STRING $name - field name
462 function deleteField($widget){
463 require_once('modules/DynamicFields/templates/Fields/TemplateField.php');
465 if (!($widget instanceof TemplateField)) {
466 $field_name = $widget;
467 $widget = new TemplateField();
468 $widget->name = $field_name;
470 $object_name = $beanList[$this->module];
472 //Some modules like cases have a bean name that doesn't match the object name
473 if (empty($GLOBALS['dictionary'][$object_name])) {
474 $newName = BeanFactory::getObjectName($this->module);
475 $object_name = $newName != false ? $newName : $object_name;
478 $GLOBALS['db']->query("DELETE FROM fields_meta_data WHERE id='" . $this->module . $widget->name . "'");
479 $sql = $widget->get_db_delete_alter_table( $this->bean->table_name . "_cstm" ) ;
480 if (! empty( $sql ) )
481 $GLOBALS['db']->query( $sql );
483 $this->removeVardefExtension($widget);
484 VardefManager::clearVardef();
485 VardefManager::refreshVardefs($this->module, $object_name);
490 * Method required by the TemplateRelatedTextField->save() method
491 * Taken from MBModule's implementation
493 function fieldExists($name = '', $type = ''){
494 // must get the vardefs from the GLOBAL array as $bean->field_defs does not contain the values from the cache at this point
495 // TODO: fix this - saveToVardefs() updates GLOBAL['dictionary'] correctly, obtaining its information directly from the fields_meta_data table via buildCache()...
496 $vardefs = $GLOBALS['dictionary'][$this->bean->object_name]['fields'];
497 if(!empty($vardefs)){
498 if(empty($type) && empty($name))
500 else if(empty($type))
501 return !empty($vardefs[$name]);
502 else if(empty($name)){
503 foreach($vardefs as $def){
504 if(!empty($def['type']) && $def['type'] == $type)
509 return (!empty($vardefs[$name]) && ($vardefs[$name]['type'] == $type));
517 * Adds a custom field using a field object
519 * @param Field Object $field
522 function addFieldObject(&$field){
523 $GLOBALS['log']->debug('adding field');
524 $object_name = $this->module;
525 $db_name = $field->name;
527 $fmd = new FieldsMetaData();
528 $id = $fmd->retrieve($object_name.$db_name,true, false);
530 $label = strtoupper( $field->label );
534 $db_name = $this->getDBName($field->name);
535 $field->name = $db_name;
537 $this->createCustomTable();
538 $fmd->id = $object_name.$db_name;
539 $fmd->custom_module= $object_name;
540 $fmd->name = $db_name;
541 $fmd->vname = $label;
542 $fmd->type = $field->type;
543 $fmd->help = $field->help;
544 if (!empty($field->len))
545 $fmd->len = $field->len; // tyoung bug 15407 - was being set to $field->size so changes weren't being saved
546 $fmd->required = ($field->required ? 1 : 0);
547 $fmd->default_value = $field->default;
548 $fmd->ext1 = $field->ext1;
549 $fmd->ext2 = $field->ext2;
550 $fmd->ext3 = $field->ext3;
551 $fmd->ext4 = (isset($field->ext4) ? $field->ext4 : '');
552 $fmd->comments = $field->comment;
553 $fmd->massupdate = $field->massupdate;
554 $fmd->importable = ( isset ( $field->importable ) ) ? $field->importable : null ;
555 $fmd->duplicate_merge = $field->duplicate_merge;
556 $fmd->audited =$field->audited;
557 $fmd->reportable = ($field->reportable ? 1 : 0);
559 $fmd->new_with_id=true;
562 $this->buildCache($this->module);
565 //Do two SQL calls here in this case
566 //The first is to create the column in the custom table without the default value
567 //The second is to modify the column created in the custom table to set the default value
568 //We do this so that the existing entries in the custom table don't have the default value set
569 $field->default = '';
570 $field->default_value = '';
571 // resetting default and default_value does not work for multienum and causes trouble for mssql
572 // so using a temporary variable here to indicate that we don't want default for this query
573 $field->no_default = 1;
574 $query = $field->get_db_add_alter_table($this->bean->table_name . '_cstm');
575 // unsetting temporary member variable
576 unset($field->no_default);
578 $GLOBALS['db']->query($query);
579 $field->default = $fmd->default_value;
580 $field->default_value = $fmd->default_value;
581 $query = $field->get_db_modify_alter_table($this->bean->table_name . '_cstm');
583 $GLOBALS['db']->query($query);
587 $query = $field->get_db_modify_alter_table($this->bean->table_name . '_cstm');
589 $GLOBALS['db']->query($query);
592 $this->saveExtendedAttributes($field, array_keys($fmd->field_defs));
598 function saveExtendedAttributes($field, $column_fields)
600 require_once ('modules/ModuleBuilder/parsers/StandardField.php') ;
601 require_once ('modules/DynamicFields/FieldCases.php') ;
605 $base_field = get_widget ( $field->type) ;
606 foreach ($field->vardef_map as $property => $fmd_col){
608 if (!isset($field->$property) || in_array($fmd_col, $column_fields) || in_array($property, $column_fields)
609 || $this->isDefaultValue($property, $field->$property, $base_field)
610 || $property == "action" || $property == "label_value" || $property == "label"
611 || (substr($property, 0,3) == 'ext' && strlen($property) == 4))
615 $to_save[$property] =
616 is_string($field->$property) ? htmlspecialchars_decode($field->$property, ENT_QUOTES) : $field->$property;
618 $bean_name = $beanList[$this->module];
620 $this->writeVardefExtension($bean_name, $field, $to_save);
623 protected function isDefaultValue($property, $value, $baseField)
627 return ( $value === 'true' || $value === '1' || $value === true || $value === 1 ); break;
631 return ( $value === 'false' || $value === '0' || $value === false || $value === 0); break;
632 case "default_value":
636 return ($value == "");
637 case "duplicate_merge":
638 return ( $value === 'false' || $value === '0' || $value === false || $value === 0 || $value === "disabled"); break;
641 if (isset($baseField->$property))
643 return $baseField->$property == $value;
649 protected function writeVardefExtension($bean_name, $field, $def_override)
651 //Hack for the broken cases module
652 $vBean = $bean_name == "aCase" ? "Case" : $bean_name;
653 $file_loc = "$this->base_path/sugarfield_{$field->name}.php";
655 $out = "<?php\n // created: " . date('Y-m-d H:i:s') . "\n";
656 foreach ($def_override as $property => $val)
658 $out .= override_value_to_string_recursive(array($vBean, "fields", $field->name, $property), "dictionary", $val) . "\n";
663 if (!file_exists($this->base_path))
664 mkdir_recursive($this->base_path);
666 if( $fh = @sugar_fopen( $file_loc, 'w' ) )
678 protected function removeVardefExtension($field)
680 $file_loc = "$this->base_path/sugarfield_{$field->name}.php";
682 if (is_file($file_loc))
690 * DEPRECIATED: Use addFieldObject instead.
691 * Adds a Custom Field using parameters
693 * @param unknown_type $name
694 * @param unknown_type $label
695 * @param unknown_type $type
696 * @param unknown_type $max_size
697 * @param unknown_type $required_option
698 * @param unknown_type $default_value
699 * @param unknown_type $ext1
700 * @param unknown_type $ext2
701 * @param unknown_type $ext3
702 * @param unknown_type $audited
703 * @param unknown_type $mass_update
704 * @param unknown_type $ext4
705 * @param unknown_type $help
706 * @param unknown_type $duplicate_merge
707 * @param unknown_type $comment
710 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=''){
711 require_once('modules/DynamicFields/templates/Fields/TemplateField.php');
712 $field = new TemplateField();
713 $field->label = $label;
714 if(empty($field->label)){
715 $field->label = $name;
717 $field->name = $name;
718 $field->type = $type;
719 $field->len = $max_size;
720 $field->required = (!empty($required_option) && $required_option != 'optional');
721 $field->default = $default_value;
722 $field->ext1 = $ext1;
723 $field->ext2 = $ext2;
724 $field->ext3 = $ext3;
725 $field->ext4 = $ext4;
726 $field->help = $help;
727 $field->comments = $comment;
728 $field->massupdate = $mass_update;
729 $field->duplicate_merge = $duplicate_merge;
730 $field->audited = $audited;
731 $field->reportable = 1;
732 return $this->addFieldObject($field);
736 * Creates the custom table with an id of id_c
739 function createCustomTable($execute = true){
741 if (!$GLOBALS['db']->tableExists($this->bean->table_name."_cstm")) {
742 $GLOBALS['log']->debug('creating custom table for '. $this->bean->table_name);
752 'name' =>$this->bean->table_name."_cstm_pk",
754 'fields'=>array('id_c')
758 $query = $GLOBALS['db']->createTableSQLParams($this->bean->table_name."_cstm", $iddef, $ididx);
759 if(!$GLOBALS['db']->supports("inline_keys")) {
760 $indicesArr = $GLOBALS['db']->getConstraintSql($ididx, $this->bean->table_name."_cstm");
762 $indicesArr = array();
765 $GLOBALS['db']->query($query);
766 if(!empty($indicesArr)) {
767 foreach($indicesArr as $idxq) {
768 $GLOBALS['db']->query($idxq);
772 $out = $query . "\n";
773 if(!empty($indicesArr)) {
774 $out .= join("\n", $indicesArr)."\n";
777 $out .= $this->add_existing_custom_fields($execute);
784 * Updates the db schema and adds any custom fields we have used if the custom table was dropped
787 function add_existing_custom_fields($execute = true){
789 if($this->bean->hasCustomFields()){
790 foreach($this->bean->field_defs as $name=>$data){
791 if(empty($data['source']) || $data['source'] != 'custom_fields')
793 $out .= $this->add_existing_custom_field($data, $execute);
799 function add_existing_custom_field($data, $execute = true)
802 $field = get_widget ( $data ['type'] );
803 $field->populateFromRow($data);
804 $query = "/*MISSING IN DATABASE - {$data['name']} - ROW*/\n"
805 . $field->get_db_add_alter_table($this->bean->table_name . '_cstm');
806 $out = $query . "\n";
808 $GLOBALS['db']->query($query);
813 public function repairCustomFields($execute = true)
815 $out = $this->createCustomTable($execute);
816 //If the table didn't exist, createCustomTable will have returned all the SQL to create and populate it
818 return "/*Checking Custom Fields for module : {$this->module} */\n$out";
819 //Otherwise make sure all the custom fields defined in the vardefs exist in the custom table.
820 //We aren't checking for data types, just that the column exists.
821 $db = $GLOBALS['db'];
822 $tablename = $this->bean->table_name."_cstm";
823 $compareFieldDefs = $db->get_columns($tablename);
824 foreach($this->bean->field_defs as $name=>$data){
825 if(empty($data['source']) || $data['source'] != 'custom_fields')
832 * force the name to be lower as it needs to be lower since that is how it's put into the key
833 * in the get_columns() call above.
835 if(!empty($compareFieldDefs[strtolower($name)])) {
838 $out .= $this->add_existing_custom_field($data, $execute);
841 $out = "/*Checking Custom Fields for module : {$this->module} */\n$out";
847 * Adds a label to the module's mod_strings for the current language
848 * Note that the system label name
850 * @param string $displayLabel The label value to be displayed
851 * @return string The core of the system label name - returns currency_id5 for example, when the full label would then be LBL_CURRENCY_ID5
852 * TODO: Only the core is returned for historical reasons - switch to return the real system label
854 function addLabel ( $displayLabel )
856 $mod_strings = return_module_language($GLOBALS[ 'current_language' ], $this->module);
859 $field_key = $this->getDBName($displayLabel, false);
860 $systemLabel = $field_key;
861 if(!$this->use_existing_labels){ // use_existing_labels defaults to false in this module; as of today, only set to true by ModuleInstaller.php
862 while( isset( $mod_strings [ $systemLabel ] ) && $count <= $limit )
864 $systemLabel = $field_key. "_$count";
868 $selMod = (!empty($_REQUEST['view_module'])) ? $_REQUEST['view_module'] : $this->module;
869 require_once 'modules/ModuleBuilder/parsers/parser.label.php' ;
870 $parser = new ParserLabel ( $selMod , isset ( $_REQUEST [ 'view_package' ] ) ? $_REQUEST [ 'view_package' ] : null ) ;
871 $parser->handleSave ( array('label_'. $systemLabel => $displayLabel ) , $GLOBALS [ 'current_language' ] ) ;
877 * Returns a Database Safe Name
879 * @param STRING $name
880 * @param BOOLEAN $_C do we append _c to the name
883 function getDBName($name, $_C= true){
884 static $cached_results = array();
885 if(!empty($cached_results[$name]))
887 return $cached_results[$name];
889 $exclusions = array('parent_type', 'parent_id', 'currency_id', 'parent_name');
890 // Remove any non-db friendly characters
891 $return_value = preg_replace("/[^\w]+/","_",$name);
892 if($_C == true && !in_array($return_value, $exclusions) && substr($return_value, -2) != '_c'){
893 $return_value .= '_c';
895 $cached_results[$name] = $return_value;
896 return $return_value;
899 function setWhereClauses(&$where_clauses){
900 if (isset($this->avail_fields)) {
901 foreach($this->avail_fields as $name=>$value){
902 if(!empty($_REQUEST[$name])){
903 $where_clauses[] = $this->bean->table_name . "_cstm.$name LIKE '". $GLOBALS['db']->quote($_REQUEST[$name]). "%'";
910 /////////////////////////BACKWARDS COMPATABILITY MODE FOR PRE 5.0 MODULES\\\\\\\\\\\\\\\\\\\\\\\\\\\
911 ////////////////////////////END BACKWARDS COMPATABILITY MODE FOR PRE 5.0 MODULES\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
916 loads fields into the bean
917 This used to be called during the retrieve process now it is done through a join
918 Restored from pre-r30895 to maintain support for custom code that may have called retrieve() directly
923 if(!isset($this->bean)){
924 $GLOBALS['log']->fatal("DynamicField retrieve, bean not instantiated: ".var_export(debug_print_backtrace(), true));
928 if(!$this->bean->hasCustomFields()){
932 $query = "SELECT * FROM ".$this->bean->table_name."_cstm WHERE id_c='".$this->bean->id."'";
933 $result = $GLOBALS['db']->query($query);
934 $row = $GLOBALS['db']->fetchByAssoc($result);
938 foreach($row as $name=>$value)
940 // originally in pre-r30895 we checked if this field was in avail_fields i.e., in fields_meta_data and not deleted
941 // with the removal of avail_fields post-r30895 we have simplified this - we now retrieve every custom field even if previously deleted
942 // 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)
943 $this->bean->$name = $value;
949 function populateXTPL($xtpl, $view) {
951 if($this->bean->hasCustomFields()){
952 $results = $this->getAllFieldsView($view, 'xtpl');
953 foreach($results as $name=>$value){
954 if(is_array($value['xtpl']))
956 foreach($value['xtpl'] as $xName=>$xValue)
958 $xtpl->assign(strtoupper($xName), $xValue);
961 $xtpl->assign(strtoupper($name), $value['xtpl']);
968 function populateAllXTPL($xtpl, $view){
969 $this->populateXTPL($xtpl, $view);
973 function getAllFieldsView($view, $type){
974 require_once ('modules/DynamicFields/FieldCases.php');
976 foreach($this->bean->field_defs as $name=>$data){
977 if(empty($data['source']) || $data['source'] != 'custom_fields')
981 $field = get_widget ( $data ['type'] );
982 $field->populateFromRow($data);
983 $field->view = $view;
984 $field->bean = $this->bean;
985 switch(strtolower($type))
988 $results[$name] = array('xtpl'=>$field->get_xtpl());
991 $results[$name] = array('html'=> $field->get_html(), 'label'=> $field->get_html_label(), 'fieldType'=>$field->data_type, 'isCustom' =>true);
999 ////////////////////////////END BACKWARDS COMPATABILITY MODE FOR PRE 5.0 MODULES\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\