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