2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
38 $GLOBALS['studioReadOnlyFields'] = array('date_entered'=>1, 'date_modified'=>1, 'created_by'=>1, 'id'=>1, 'modified_user_id'=>1);
41 The view is the context this field will be used in
53 var $required = false;
55 var $default_value = null;
56 var $type = 'varchar';
65 var $importable = 'true' ;
66 var $duplicate_merge=0;
67 var $new_field_definition;
68 var $reportable = true;
69 var $label_value = '';
72 var $unified_search = 0;
73 var $supports_unified_search = false;
74 var $vardef_map = array(
77 // bug 15801 - need to ALWAYS keep default and default_value consistent as some methods/classes use one, some use another...
78 'default_value'=>'default',
79 'default'=>'default_value',
80 'display_default'=>'default_value',
81 // 'default_value'=>'default_value',
82 // 'default'=>'default_value',
84 'required'=>'required',
87 'massupdate'=>'massupdate',
90 'comments'=>'comment',
91 'importable'=>'importable',
92 'duplicate_merge'=>'duplicate_merge',
93 'duplicate_merge_dom_value'=>'duplicate_merge_dom_value', //bug #14897
94 'merge_filter'=>'merge_filter',
95 'reportable' => 'reportable',
100 //'disable_num_format'=>'ext3',
102 'label_value'=>'label_value',
103 'unified_search'=>'unified_search',
104 'full_text_search'=>'full_text_search',
107 // fields to decode from post request
108 var $decode_from_request_fields_map = array('formula', 'dependency');
114 if(!empty($GLOBALS['studioReadOnlyFields'][$this->name]))$view = 'detail';
116 case 'search':return $this->get_html_search();
117 case 'edit': return $this->get_html_edit();
118 case 'list': return $this->get_html_list();
119 case 'detail': return $this->get_html_detail();
123 function set($values){
124 foreach($values as $name=>$value){
125 $this->$name = $value;
130 function get_html_edit(){
131 return 'not implemented';
134 function get_html_list(){
135 return $this->get_html_detail();
138 function get_html_detail(){
139 return 'not implemented';
142 function get_html_search(){
143 return $this->get_html_edit();
145 function get_html_label(){
147 $label = "{MOD." .$this->vname . "}";
148 if(!empty($GLOBALS['app_strings'][$this->vname])){
149 $label = "{APP." .$this->label . "}";
151 if($this->view == 'edit' && $this->is_required()){
152 $label .= '<span class="required">*</span>';
154 if($this->view == 'list'){
155 if(isset($this->bean)){
156 if(!empty($this->id)){
157 $name = $this->bean->table_name . '_cstm.'. $this->name;
158 $arrow = $this->bean->table_name . '_cstm_'. $this->name;
160 $name = $this->bean->table_name . '.'. $this->name;
161 $arrow = $this->bean->table_name . '_'. $this->name;
167 $label = "<a href='{ORDER_BY}$name' class='listViewThLinkS1'>{MOD.$this->label}{arrow_start}{".$arrow."_arrow}{arrow_end}</a>";
177 function get_xtpl($bean = false){
181 if(!empty($GLOBALS['studioReadOnlyFields'][$this->name]))$view = 'detail';
183 case 'search':return $this->get_xtpl_search();
184 case 'edit': return $this->get_xtpl_edit();
185 case 'list': return $this->get_xtpl_list();
186 case 'detail': return $this->get_xtpl_detail();
191 function get_xtpl_edit(){
192 return '/*not implemented*/';
195 function get_xtpl_list(){
196 return get_xtpl_detail();
199 function get_xtpl_detail(){
200 return '/*not implemented*/';
203 function get_xtpl_search(){
204 //return get_xtpl_edit();
207 function is_required(){
222 function get_db_type(){
223 if(!empty($this->type)) {
224 $type = $GLOBALS['db']->getColumnType($this->type);
226 if(!empty($type)) return " $type";
227 $type = $GLOBALS['db']->getColumnType("varchar");
228 return " $type({$this->len})";
231 function get_db_default($modify=false){
232 $GLOBALS['log']->debug('get_db_default(): default_value='.$this->default_value);
233 if (!$modify or empty($this->new_field_definition['default_value']) or $this->new_field_definition['default_value'] != $this->default_value ) {
234 if(!is_null($this->default_value)){ // add a default value if it is not null - we want to set a default even if default_value is '0', which is not null, but which is empty()
235 if(NULL == trim($this->default_value)){
236 return " DEFAULT NULL";
239 return " DEFAULT '$this->default_value'";
248 * Return the required clause for this field
249 * Confusingly, when modifying an existing field ($modify=true) there are two exactly opposite cases:
250 * 1. if called by Studio, only $this->required is set. If set, we return "NOT NULL" otherwise we return "NULL"
251 * 2. if not called by Studio, $this->required holds the OLD value of required, and new_field_definition['required'] is the NEW
252 * So if not called by Studio we want to return NULL if required=true (because we are changing FROM this setting)
255 function get_db_required($modify=false){
256 // $GLOBALS['log']->debug('get_db_required required='.$this->required." and ".(($modify)?"true":"false")." and ".print_r($this->new_field_definition,true));
260 if (!empty($this->new_field_definition['required'])) {
261 if ($this->required and $this->new_field_definition['required'] != $this->required) {
267 $req = ($this->required) ? " NOT NULL " : ''; // bug 17184 tyoung - set required correctly when modifying custom field in Studio
272 if (empty($this->new_field_definition['required']) or $this->new_field_definition['required'] != $this->required ) {
273 if(!empty($this->required) && $this->required){
282 /* function get_db_required($modify=false){
283 $GLOBALS['log']->debug('get_db_required required='.$this->required." and ".(($modify)?"true":"false")." and ".print_r($this->new_field_definition,true));
285 if (!empty($this->new_field_definition['required'])) {
286 if ($this->required and $this->new_field_definition['required'] != $this->required) {
292 if (empty($this->new_field_definition['required']) or $this->new_field_definition['required'] != $this->required ) {
293 if(!empty($this->required) && $this->required){
301 * Oracle Support: do not set required constraint if no default value is supplied.
302 * In this case the default value will be handled by the application/sugarbean.
304 function get_db_add_alter_table($table)
306 return $GLOBALS['db']->getHelper()->addColumnSQL($table, $this->get_field_def(), true);
309 function get_db_delete_alter_table($table)
311 return $GLOBALS['db']->getHelper()->dropColumnSQL(
313 $this->get_field_def()
318 * mysql requires the datatype caluse in the alter statment.it will be no-op anyway.
320 function get_db_modify_alter_table($table){
321 return $GLOBALS['db']->alterColumnSQL($table, $this->get_field_def());
329 function get_field_def(){
331 'required'=>$this->convertBooleanValue($this->required),
332 'source'=>'custom_fields',
334 'vname'=>$this->vname,
336 'massupdate'=>$this->massupdate,
337 'default'=>$this->default,
338 'comments'=> (isset($this->comments)) ? $this->comments : '',
339 'help'=> (isset($this->help)) ? $this->help : '',
340 'importable'=>$this->importable,
341 'duplicate_merge'=>$this->duplicate_merge,
342 'duplicate_merge_dom_value'=> $this->getDupMergeDomValue(),
343 'audited'=>$this->convertBooleanValue($this->audited),
344 'reportable'=>$this->convertBooleanValue($this->reportable),
345 'unified_search'=>$this->convertBooleanValue($this->unified_search),
346 'merge_filter' => empty($this->merge_filter) ? "disabled" : $this->merge_filter
348 if (isset($this->full_text_search)) {
349 $array['full_text_search'] = $this->full_text_search;
351 if(!empty($this->len)){
352 $array['len'] = $this->len;
354 if(!empty($this->size)){
355 $array['size'] = $this->size;
358 $this->get_dup_merge_def($array);
363 protected function convertBooleanValue($value)
365 if ($value === 'true' || $value === '1' || $value === 1)
367 else if ($value === 'false' || $value === '0' || $value === 0)
374 /* if the field is duplicate merge enabled this function will return the vardef entry for the same.
376 function get_dup_merge_def(&$def) {
377 switch ($def['duplicate_merge_dom_value']) {
379 $def['duplicate_merge']='disabled';
380 $def['merge_filter']='disabled';
383 $def['duplicate_merge']='enabled';
384 $def['merge_filter']='disabled';
387 $def['merge_filter']='enabled';
388 $def['duplicate_merge']='enabled';
391 $def['merge_filter']='selected';
392 $def['duplicate_merge']='enabled';
395 $def['merge_filter']='enabled';
396 $def['duplicate_merge']='disabled';
403 * duplicate_merge_dom_value drives the dropdown in the studio editor. This dropdown drives two fields though,
404 * duplicate_merge and merge_filter. When duplicate_merge_dom_value is not set, we need to derive it from the values
405 * of those two fields. Also, when studio sends this value down to be read in PopulateFromPost, it is set to
406 * duplicate_merge rather than duplicate_merge_dom_value, so we must check if duplicate_merge is a number rather
407 * than a string as well.
410 function getDupMergeDomValue(){
411 if (isset($this->duplicate_merge_dom_value)) {
412 return $this->duplicate_merge_dom_value;
415 //If duplicate merge is numeric rather than a string, it is probably what duplicate_merge_dom_value was set to.
416 if (is_numeric($this->duplicate_merge))
417 return $this->duplicate_merge;
420 //Figure out the duplicate_merge_dom_value based on the values of merge filter and duplicate merge
421 if (empty($this->merge_filter) || $this->merge_filter === 'disabled' )
423 if (empty($this->duplicate_merge) || $this->duplicate_merge === 'disabled') {
424 $this->duplicate_merge_dom_value = 0;
426 $this->duplicate_merge_dom_value = 1;
429 if ($this->merge_filter === "selected")
430 $this->duplicate_merge_dom_value = 3;
431 else if (empty($this->duplicate_merge) || $this->duplicate_merge === 'disabled') {
432 $this->duplicate_merge_dom_value = 4;
434 $this->duplicate_merge_dom_value = 2;
438 return $this->duplicate_merge_dom_value;
447 if(empty($this->id)){
448 $this->id = $this->name;
454 * This function supports setting the values of all TemplateField instances.
455 * @param $row The Array key/value pairs from fields_meta_data table
457 function populateFromRow($row=array()) {
458 $fmd_to_dyn_map = array('comments' => 'comment', 'require_option' => 'required', 'label' => 'vname',
459 'mass_update' => 'massupdate', 'max_size' => 'len', 'default_value' => 'default', 'id_name' => 'ext3');
460 if(!is_array($row)) {
461 $GLOBALS['log']->error("Error: TemplateField->populateFromRow expecting Array");
463 //Bug 24189: Copy fields from FMD format to Field objects and vice versa
464 foreach ($fmd_to_dyn_map as $fmd_key => $dyn_key) {
465 if (isset($row[$dyn_key])) {
466 $this->$fmd_key = $row[$dyn_key];
468 if (isset($row[$fmd_key])) {
469 $this->$dyn_key = $row[$fmd_key];
472 foreach($row as $key=>$value) {
473 $this->$key = $value;
477 function populateFromPost(){
478 foreach($this->vardef_map as $vardef=>$field){
480 if(isset($_REQUEST[$vardef])){
481 $this->$vardef = $_REQUEST[$vardef];
483 // Bug #48826. Some fields are allowed to have special characters and must be decoded from the request
484 if (is_string($this->$vardef) && in_array($vardef, $this->decode_from_request_fields_map))
485 $this->$vardef = html_entity_decode($this->$vardef);
487 // Bug 49774, 49775: Strip html tags from 'formula' and 'dependency'.
488 // Add to the list below if we need to do the same for other fields.
489 if (!empty($this->$vardef) && in_array($vardef, array('formula', 'dependency'))){
490 $this->$vardef = to_html(strip_tags(from_html($this->$vardef)));
493 //Remove potential xss code from help field
494 if($field == 'help' && !empty($this->$vardef))
496 $help = htmlspecialchars_decode($this->$vardef, ENT_QUOTES);
497 $this->$vardef = htmlentities(remove_xss($help));
501 if($vardef != $field){
502 $this->$field = $this->$vardef;
506 $this->applyVardefRules();
507 $GLOBALS['log']->debug('populate: '.print_r($this,true));
511 protected function applyVardefRules()
515 function get_additional_defs(){
519 function delete($df){
520 $df->deleteField($this);
526 * This is a helper function to return a field's proper name. It checks to see if an instance of the module can
527 * be created and then attempts to retrieve the field's name based on the name lookup skey supplied to the method.
529 * @param String $module The name of the module
530 * @param String $name The field name key
531 * @return The field name for the module
533 protected function get_field_name($module, $name)
535 $bean = loadBean($module);
536 if(empty($bean) || is_null($bean))
541 $field_defs = $bean->field_defs;
542 return isset($field_defs[$name]['name']) ? $field_defs[$name]['name'] : $name;
548 * This function says the field template by calling the DynamicField addFieldObject function. It then
549 * checks to see if updates are needed for the SearchFields.php file. In the event that the unified_search
550 * member variable is set to true, a search field definition is updated/created to the SearchFields.php file.
552 * @param DynamicField $df
555 // $GLOBALS['log']->debug('saving field: '.print_r($this,true));
556 $df->addFieldObject($this);
558 require_once('modules/ModuleBuilder/parsers/parser.searchfields.php');
559 $searchFieldParser = new ParserSearchFields( $df->getModuleName() , $df->getPackageName() ) ;
560 //If unified_search is enabled for this field, then create the SearchFields entry
561 $fieldName = $this->get_field_name($df->getModuleName(), $this->name);
562 if($this->unified_search && !isset($searchFieldParser->searchFields[$df->getModuleName()][$fieldName]))
564 $searchFieldParser->addSearchField($fieldName, array('query_type'=>'default'));
565 $searchFieldParser->saveSearchFields($searchFieldParser->searchFields);