Creating Custom Field Types

Ok So Ive been with a company for some time now and we are looking to rebuild our CRM as the current one has become quite messy with code. All my fault, the way I designed modules ended up being used completely different as to what the company did. Also it was my first time delving into Sugar and Suite when I started.

Its crazy to think about it but a few years does fly and Ive been getting pretty comfortable messing with Suitecrm now for some time.

So as I am planning a rebuild because our company is going to offer new products, I noticed it would make my life easier if I took the time to learn how to create custom field data types.

Scenarios would include things like Social Security numbers here in the states, Date of Births, Encrypted fields. Honestly probably just those.

Any tips on how to create a custom field type?

Looks like my answer is right here. I will be giving this a shot and reporting back.

I really do need to get Jim Mackin’s book already.

It seems to have literally everything I would need to understand proper ways of customizing the system.

I’ll try to post back what I used to create a Social field type. My javascript isn’t so good but we’ll see what I can come up with.

I know as a thought I need to consider roles as I know some users need to only have access to the last 4 digits of a clients social, while others should see the full digits. Yet all users should be able to update the field.

That blog post looks really interesting! I’m going to have to look into creating custom fields types next week. Thanks for the tip!

I’m not sure how SSN look like and if you want to validate before submitting or not, but there shouldn’t be a lot of complicated javascript for the types of fields you’re mentioning - maybe a regular expression for some simple validation. And if you need it, there are loads of nice and small date-picker utilities available on the web.

Good luck on creating new custom fields! Looks really cool!

SSN has the format xxx-xx-xxxx, all digits acceptable storage would be xxxxxxxxx.

Its 9 digits used in the states to pretty much identify a person. Its primary purpose was to track a citizens Social Security moneys for retirement. Its its only official purpose but since one is given to every citizen, it is used by pretty much every company here as a unique ID.

I haven’t had time to take a crack at this, my DB had issues over a power failure on our local servers, as I have been planning to migrate our data to a vps server because of issues like this.

I’m glad I made a post on how to migrate to a different server as i actually have used it myself plenty of times.

I’ll get back when I can and post what I found.

Ok adding a custom field is actually fairly easy. As you can see in the image attached i got it working.

What I am having difficulty with is understanding the smarty template code.

I’ll look at some other template code and the color picker code from the example noted earlier here.here.

Ok I am not familiar with Smarty template coding but once I formatted this from the referenced blog post I could understand it a bit better and in the context of SuiteCRM

custom/include/SugarFields/Fields/SocialSecurity/EditView.tpl



{if strlen({{sugarvar key='value' string=true}}) <= 0}
    {assign var="value" value={{sugarvar key='default_value' string=true}} }
{else}
    {assign var="value" value={{sugarvar key='value' string=true}} }
{/if}
<input
	type='text'
	name='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
	id='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
	size='{{$displayParams.size|default:30}}'
	{{if isset($displayParams.maxlength)}}maxlength='{{$displayParams.maxlength}}'{{elseif isset($vardef.len)}}maxlength='{{$vardef.len}}'{{/if}}
	value='{$value}' title='{{$vardef.help}}' {{if !empty($tabindex)}} tabindex='{{$tabindex}}' {{/if}}
	{{if !empty($displayParams.accesskey)}} accesskey='{{$displayParams.accesskey}}' {{/if}} 
	
	{{$displayParams.field}}
	
	>

<script>
    {literal}
###some validation js on call to save###
    {/literal}
</script>

I understand I can add a script, here the script runs on when the document is ready.
I found some js code that may be used I believe to send a validation message.

Where I am stuck is how to trigger the code when user tries to save.

I do not know how I would do it in reference to SuiteCRM

I would then run the code through validation, prob using regex like /^\d{3}-\d{2}-\d{4}$/

first 3 digits, then 2 then 4 with dashes in between and nothing else. There are some better regex’s I could use but for now lets theoretically stick with this one.

Then I could display error it seems with this java and smarty code


{literal}
...
add_error_style( 'EditView' , '{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}' , "Ensure Proper Social XXX-XX-XXXX!" );
...
{/literal}

If anyone has experience with this, please help me out, I’ll see what I can pull together and test. I haven’t tested any of this code but it seems like I’m headed in the right direction.

Christmas has passed so Merry Christmas and Happy Holidays to all.

Some wonderful gentleman posted thison github
https://gist.github.com/cmourizard/5378073

I hope this works. I will be implementing this code into the edit template tpl.

get underscore-min.js and place
custom/include/javascript/underscore-min.js

create EditView.tpl for custom field type
custom/include/SugarFields/Fields/SocialSecurity/EditView.tpl


<script src="custom/include/javascript/underscore-min.js"></script>
{if strlen({{sugarvar key='value' string=true}}) <= 0}
    {assign var="value" value={{sugarvar key='default_value' string=true}} }
{else}
    {assign var="value" value={{sugarvar key='value' string=true}} }
{/if}
<input type='text' 
		name='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
		id='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
		size='{{$displayParams.size|default:30}}'
       {{if isset($displayParams.maxlength)}}maxlength='{{$displayParams.maxlength}}'{{elseif isset($vardef.len)}}maxlength='{{$vardef.len}}'{{/if}}
       value='{$value}'
	   title='{{$vardef.help}}' {{if !empty($tabindex)}} tabindex='{{$tabindex}}' {{/if}}
	   {{if !empty($displayParams.accesskey)}} accesskey='{{$displayParams.accesskey}}' {{/if}}
	   {{$displayParams.field}}
	   >

<script>
    {literal}
    SUGAR.util.doWhen("typeof(check_form) != 'undefined' && typeof check_form == 'function'", function() {
		//noticed on inline edit, it ask for validation on every instance of the
		//validation ran, so if i had 4 wrong entries, it then validates 5 times.
		//This would not be noticed by the user if it were not for me testing
		//using confirm but, it might cause a browser crash
		//or js error if called to many times? tested in chrome
		

			check_form = _.wrap(check_form, function(originalCheckFormFunction, originalCheckFormFunctionArg) {
				// Adding custom validation 
				isCustomValid = confirm('Are you sure you want to save this record?');
				
				if(isCustomValid) {
					// If custom validation is positive, calling original Sugar validation
					
					//remove error message if present for this field.
					errorMsg="";
					isError = false;
					
					return originalCheckFormFunction(originalCheckFormFunctionArg);
				} else {
					//remove previous error message?? it wont work and I end up with multiple error message lines if another bad instance is present. Its now just a minor annoyance.
					errorMsg="";
					isError = false;
					
					//display error message
					isError = true;
					add_error_style( 'EditView' , '{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}' , "Ensure Proper Social XXX-XX-XXXX!" );
					return false;
				}

			});

});

    {/literal}
</script>

include underscore js in there, and do validation.

for testing we are simply asking user to confirm or deny, I will change this to do regex. I am posting this because I am happy I got it to work.

Two issues I noticed,
If multiple fails, it just adds the error message and oyu end up with multiple error messages and it bothers me.
Secondly for inline edit, it runs the validation over and over for each time it failed. So 4 fails gets you 4 validation js runs. This could be bad if done too much by auser and maybe cause a js error?

Doesnt work with inline edit, I ended using this code

for validation, but inline edit still allows code save


check_form = _.wrap(check_form, function(originalCheckFormFunction, originalCheckFormFunctionArg) {
				// Adding custom validation 
				var SocialSecurityValue = $('#{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}').val();
				
				var regex = new RegExp("^(?!\\b(\\d)\\1+-(\\d)\\1+-(\\d)\\1+\\b)(?!123-45-6789|219-09-9999|078-05-1120)(?!666|000|9\\d{2})\\d{3}-(?!00)\\d{2}-(?!0{4})\\d{4}$");
				
				//isCustomValid = confirm('Are you sure ? SS: ' + SocialSecurityValue);
				//Test your current value
				if(regex.test(SocialSecurityValue) || SocialSecurityValue == "" || SocialSecurityValue == null){
					isCustomValid = true;
				}else{
					isCustomValid = false;
				}
				
				
				if(isCustomValid) {
					// If custom validation is positive, calling original Sugar validation
					
					//remove error message if present for this field.
					errorMsg="";
					isError = false;
					
					return originalCheckFormFunction(originalCheckFormFunctionArg);
				} else {
					//remove previous error message?? it wont work and I end up with multiple error message lines if another bad instance is present. Its now just a minor annoyance.
					errorMsg="";
					isError = false;
					
					//display error message
					isError = true;
					add_error_style( 'EditView' , '{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}' , "Ensure Proper Social XXX-XX-XXXX!" );
					return false;
				}

			});

Any suggestions?

Ok i figured somethign was being cached and that is why inline editing was not working. I enabled developer mode and sure enough inline works with this code as well!

Ok I have one final problem and I hope someone out there can take a look and give some advice. My only problem so far is with save manipulation when inline editing.

I have my custom field type check and validate in php. This code does not run when using inline editing.
The custom field type I have created does masking of the field is a user is in a specific role.

Our example uses United States Socials so, if a number is 123-45-6789, for certain users only *--6789 will appear. This also applies to the edit view so a user cannot try to view the number without permission. My code example checks if the field is formatted improperly and will not save the updated data keeping the correct number and not updated the filed to a masked social security number.

Everything else is working marvelously and I could disable Inline editing, but its all working so well and this is the last issue. It would be great for everything to work well, in an upgrade safe manner so others may use validation.

Ok I would like to give an overview of what I have done.

Give option to module builder to select this field type.
custom/modules/DynamicFields/templates/Fields/TemplateSocialSecurity.php


<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

require_once('modules/DynamicFields/templates/Fields/TemplateField.php');
class TemplateSocialSecurity extends TemplateField{
    var $type='SocialSecurity';
    
    function get_field_def(){
        $def = parent::get_field_def();
        $def['dbType'] = 'varchar';
        return $def;
    }
}

define language file
custom/Extension/modules/ModuleBuilder/Ext/Language/en_us.SocialSecurity.php


<?php
$mod_strings['fieldTypes']['SocialSecurity'] = 'Social Security';

also download underscore-min.js and place here
custom/include/javascript/underscore-min.js

This is used to stop the save if not validated field.

Then we add 3 tempaltes and php file to

First is edit view, standard edit view only it has validation logic, and it checks the users role, if the role is SSNBLOCK then it will alter the data to only show the last four of a social security number. The validation uses underscore to interrupt the save if not valid input.
custom/include/SugarFields/Fields/SocialSecurity/EditView.tpl


<script src="custom/include/javascript/underscore-min.js"></script>
{if strlen({{sugarvar key='value' string=true}}) <= 0}
    {assign var="value" value={{sugarvar key='default_value' string=true}} }
{else}
    {assign var="value" value={{sugarvar key='value' string=true}} }
{/if}

{* Get User Role and set smarty var to allow or not allow full SSN view *}
{php}
require_once('modules/ACLRoles/ACLRole.php');
global $sugar_config;
global $mod_strings;
//mask field display, for employees role
$objACLRole = new ACLRole();
$roles = $objACLRole->getUserRoles($GLOBALS['current_user']->id);


 $value = $this->get_template_vars('value');
 
 //for debug
 $this->assign('orig_value',$value);
 
       //$roles[]="glitchfix";
	   if(in_array("SNNBLOCK",$roles)){
		   $value = '***-**-'.substr($value, -4);
		   $this->assign('value',$value);
        }
        
{/php}
{*
//for debug purposes
{$orig_value}
*}

<input type='text' 
		name='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
		id='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
		size='{{$displayParams.size|default:30}}'
       {{if isset($displayParams.maxlength)}}maxlength='{{$displayParams.maxlength}}'{{elseif isset($vardef.len)}}maxlength='{{$vardef.len}}'{{/if}}
       value='{$value}'
	   title='{{$vardef.help}}' {{if !empty($tabindex)}} tabindex='{{$tabindex}}' {{/if}}
	   {{if !empty($displayParams.accesskey)}} accesskey='{{$displayParams.accesskey}}' {{/if}}
	   {{$displayParams.field}}
	   >

<script>
    {literal}
    SUGAR.util.doWhen("typeof(check_form) != 'undefined' && typeof check_form == 'function'", function() {
		
		//noticed on inline edit, it ask for validation on every instance of the
		//validation ran, so if i had 4 wrong entries, it then validates 5 times.
		//This would not be noticed by the user if it were not for me testing
		//using confirm but, it might cause a browser crash
		//or js error if called to many times? tested in chrome
			check_form = _.wrap(check_form, function(originalCheckFormFunction, originalCheckFormFunctionArg) {
				
				
				// Adding custom validation 
				var SocialSecurityValue = $('#{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}').val();
				
				//Allows ***-dd-dddd or ***-**-dddd for the masking, later the save logix will prevent these from being saved and peform additional validation and not save in case javascript is broken
				var regex = new RegExp("^(?!\\b(\\d)\\1+-(\\d)\\1+-(\\d)\\1+\\b)(?!123-45-6789|219-09-9999|078-05-1120)(?!666|000|9\\d{2})(\\*\\*\\*|\\d{3})-(?!00)(\\*\\*|\\d{2})-(?!0{4})\\d{4}$");
				
				
				//Test your current value
				if(regex.test(SocialSecurityValue) || SocialSecurityValue == "" || SocialSecurityValue == null){
					isCustomValid = true;
				}else{
					isCustomValid = false;
				}
				
				
				if(isCustomValid) {
					// If custom validation is positive, calling original Sugar validation
					
					//remove error message if present for this field.
					errorMsg="";
					isError = false;
					
					return originalCheckFormFunction(originalCheckFormFunctionArg);
				} else {
					

					//remove previous error message?? it wont work and I end up with multiple error message lines if another bad instance is present. Its now just a minor annoyance.
					errorMsg="";
					isError = false;
					
					//display error message
					isError = true;
					add_error_style( 'EditView' , '{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}' , "Ensure Proper Social XXX-XX-XXXX!" );
					return false;
					
				}

			});

});

    {/literal}
</script>

Then we define the display view, this also check the user role, except no field validation etc, and using span instead of input to display the data
custom/include/SugarFields/Fields/SocialSecurity/DetailView.tpl


{if strlen({{sugarvar key='value' string=true}}) <= 0}
    {assign var="value" value={{sugarvar key='default_value' string=true}} }
{else}
    {assign var="value" value={{sugarvar key='value' string=true}} }
{/if}



{* Get User Role and set smarty var to allow or not allow full SSN view *}
{php}
require_once('modules/ACLRoles/ACLRole.php');
global $sugar_config;
global $mod_strings;
//mask field display, for employees role
$objACLRole = new ACLRole();
$roles = $objACLRole->getUserRoles($GLOBALS['current_user']->id);


 $value = $this->get_template_vars('value');
 
 
       //$roles[]="glitchfix";
	   if(in_array("SNNBLOCK",$roles)){
		   // assign a variable to Smarty
		   //$ssnallow = 'false';
		   $value = '***-**-'.substr($value, -4);
		   $this->assign('value',$value);
        }
{/php}

<span class="sugar_field" id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}">{$value}</span>

{*
<input disabled="disabled" type='text' name='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
       id='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}' size='{{$displayParams.size|default:30}}'
       {{if isset($displayParams.maxlength)}}maxlength='{{$displayParams.maxlength}}'{{elseif isset($vardef.len)}}maxlength='{{$vardef.len}}'{{/if}}
       value='{$value}' title='{{$vardef.help}}' {{if !empty($tabindex)}} tabindex='{{$tabindex}}' {{/if}}
        {{if !empty($displayParams.accesskey)}} accesskey='{{$displayParams.accesskey}}' {{/if}} {{$displayParams.field}}>


*}

Then we need to define a search view, i noticed the system defaulted to the edit view, but that would insert the masking of *- to the search box, and that is not what a user would expect so we get rid of that user role code and keep it simple.

custom/include/SugarFields/Fields/SocialSecurity/SearchView.tpl


{if strlen({{sugarvar key='value' string=true}}) <= 0}
    {assign var="value" value={{sugarvar key='default_value' string=true}} }
{else}
    {assign var="value" value={{sugarvar key='value' string=true}} }
{/if}

<input type='text' 
		name='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
		id='{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}'
		size='{{$displayParams.size|default:30}}'
       {{if isset($displayParams.maxlength)}}maxlength='{{$displayParams.maxlength}}'{{elseif isset($vardef.len)}}maxlength='{{$vardef.len}}'{{/if}}
       value='{$value}'
	   title='{{$vardef.help}}' {{if !empty($tabindex)}} tabindex='{{$tabindex}}' {{/if}}
	   {{if !empty($displayParams.accesskey)}} accesskey='{{$displayParams.accesskey}}' {{/if}}
	   {{$displayParams.field}}
	   >

Then because of masking, the field validation DOES allow impropper social of EX: *--6789 and it will pass it to a save, but if a user is editing another field and not the social, it would then save the masked social and lose the data in the DB to somethign useless. So we needed to exten the SugarFielBase save, and also tell it to use our defined search view. This also does validation on the PHP side incase something goes wrong with js. If validation fails, it will save the previous DB value.

custom/include/SugarFields/Fields/SocialSecurity/SugarFieldSocialSecurity.php


<?php
/*********************************************************************************
 Custom SugarField that validates and compartmentalizes Social Security Numbers.
 
 User will have to enter social from javascript validation and php validation of XXX-XX-XXXX
 
 If user is in role SSNBLOCK only the last 4 of the social will be displayed EX: ***-**-6789
 
 So if the user hits save, this save logic will ensure the field is not saved as ***-**-6789
 but rather keep the original data of 123-45-6789
 
 If the field has an updated Social Security Number then it will save.
 
 EX: Original SS = 123-45-6789 and has a new value of ***-**-6789 it will not be saved.
 Original SS = 123-45-6789 and has a new value of 987-65-4321 it WILL be saved and updated.
 
 This is to allow only the correct users to view the full SS and any user to update the field.
 
 Great! and its a field type, so this type of code may be used now for CC validation or postal code validation.
 ********************************************************************************/

require_once('include/SugarFields/Fields/Base/SugarFieldBase.php');

//i do not know if this include is required but i saw it in the autoincrement custom field type floating around here and though it was needed.
require_once('data/SugarBean.php');
class SugarFieldSocialSecurity extends SugarFieldBase {
	
	//need to specify to system to use SearchView.tpl for search instead of the edit view field with masking
	function getSearchViewSmarty($parentFieldArray, $vardef, $displayParams, $tabindex) {
      return $this->getSmartyView($parentFieldArray, $vardef, $displayParams, $tabindex, 'SearchView');
    }
	
	//here we alter the save logic to check for ***-**- masking and prevent the system form saving this,
	//also do secondary validaiton to prevent save incase javascript fails to work on the webpage.
	public function save($bean, $params, $field, $properties, $prefix = '') {
	 if ( isset($params[$prefix.$field]) ) {
		 if(isset($properties['len']) && isset($properties['type']) && $this->isTrimmable($properties['type'])){
			 
			 
			 $bean->$field = trim($this->unformatField($params[$prefix.$field], $properties));
		 }
		 else {
			 
			 
			 $bean->$field = $this->unformatField($params[$prefix.$field], $properties);
		 }
	 }
	 
	 $bean_field_data = $bean->$field;
	 
		//Here we do php validation, so if the number contains * from masking, or is incorrect format and js
		//failed to load and validate then we prevent the save by using the previous db value
		
		
		if (!preg_match("/^(?!\b(\d)\1+-(\d)\1+-(\d)\1+\b)(?!123-45-6789|219-09-9999|078-05-1120)(?!666|000|9\d{2})\d{3}-(?!00)\d{2}-(?!0{4})\d{4}$/", $bean_field_data)){
			$bean->$field = $bean->fetched_row[$field];
		}

	}
	
	
}


?>

And i Think that is it, I’m pretty sure that is all the code I used to create the custom field type. With role based masking, and js and php validation of the field, now reusable on my custom modules that may need to sync this data, and it just makes my life easier in the long run. I will be implementing similar code now for Credit Card custom field type.

So again my issue is the save logic in SugarFieldBase.php is being bypassed by the way inline edit is being used. So I am curious if anyone has any idea how I can apply the PHP validation to the inline edit. Only reason I have this issue is because some users end up with a masked *--6789 type Social Security if they are not allowed to view those numbers. But administrators need to see them.

any help would be greatly appreciated!

I also have attached the code to this post for anyone to use on their own. And hopefully I’ll tackle making a package of it for CC field types, Social, and whatever other validation and secure field types are common. I had posted in the suggestion section that it would be cool to create a field type that uses regex, so it will automatically handle whatever type of validation someone might encounter and use this principle to execute it. I also saw a post on some blog somewhere where someone customized module builder to allow custom js code in module builder for this very reason.

The following information was of huge help to me:
http://www.jsmackin.co.uk/suitecrm/creating-custom-field-type-suitecrm/
https://www.w-systems.com/blog/sugarcrm/sugarcrm-tutorial-how-to-create-a-re-usable-sugarfield

1 Like

Thank you angrymob

I follow you example and i work fine.

But It still get EditView.tpl On List Search. Can you help me?