//add a field to copy
$dictionary['Bug']['fields']['priority']['duplicate_on_record_copy'] = 'always';
Once in place, navigate to Admin > Repair > Quick Repair & Rebuild.
Note: You can name the file 'copyPrefill.php' anything you like. We advise against making these changes in theÂ ./custom/Extension/modules/<module>/Ext/Vardefs/sugarfield_<field>.phpÂ files as these changes may be removed during studio edits.
Modifying the Copy Prefill View Using JavaScript Controllers
The following section will outline how to modify the fields that are prefilled when copying a Bug record from the record view using the JavaScript Controller. This is helpful when you need to dependently determine the fields to copy by a field on the bean. You can apply this to any module in the system.


./custom/modules/Bugs/clients/base/views/record/record.js
({
    extendsFrom: 'RecordView',
    setupDuplicateFields: function (prefill) {
        this._super('setupDuplicateFields', prefill);
        var fields = [
            'name',
            'assigned_user_id',
            'priority',
            'type',
            'product_category',
            'description'
        ];
        //determines whether the field list above is a set of allowlisted (allowed) or denylisted (denied) fields
        var denylist = false;
        if (denylist) {
            _.each(fields, function (field) {
                if (field && prefill.has(field)) {
                    //set denylist field to the default value if exists


//set denylist field to the default value if exists
                    if (!_.isUndefined(prefill.fields[field]) && !_.isUndefined(prefill.fields[field].default)) {
                        prefill.set(field, prefill.fields[field].default);
                    } else {
                        prefill.unset(field);
                    }
                }
            });
        } else {
            _.each(prefill.fields, function (value, field) {
                if (!_.contains(fields, field)) {
                    if (!_.isUndefined(prefill.fields[field].default)) {
                        prefill.set(field, prefill.fields[field].default);
                    } else {
                        prefill.unset(field);
                    }
                }
            });
        }
    }
})


}
                }
            });
        }
    }
})
Once in place, navigate to Admin > Repair > Quick Repair & Rebuild. The denylist variable will determine whether the list of fields are allowlisted or denylisted from the copy feature.
  	Last modified: 2023-02-03 21:04:03


Security
Security documents for securing SugarsÂ infastructure.
TopicsEndpoints and Entry PointsThis document describes how to disable out of the box REST API endpoints and legacy MVC entry points.Web Server ConfigurationThis document serves as a guideline to harden the web server configuration with regard to running SugarCRM. Note that this is a guideline and certain settings will depend on your specific environment and setup. This guideline focuses on Apache web server as this is SugarCRM's primary supported web server. However, the recommendations in this document apply to all web servers in general.XSS PreventionThis document describes how to prevent cross-site scripting (XSS) attacks in Sugar customizations. XSS attacks occur when malicious entities are able to inject client-side scripts into web pages.
  	Last modified: 2023-02-03 21:04:03


XSS Prevention
Overview
This document describes how to prevent cross-site scripting (XSS) attacks in Sugar customizations. XSS attacks occur when malicious entities are able toÂ inject client-side scripts into web pages.
Best Practices
When creating custom code for Sugar, be sure to respect the followingÂ best practices. These rules serve to protect the elements of your customizations that are most susceptible to XSS vulnerabilities:
Create safe variables when injecting HTML via Handlebars templates or JavaScript methods such as: $(selector).html(variable), $(selector).before(variable), or $(selector).after(variable).
Avoid using triple-bracket (e.g.Â {{{variable}}}) syntax to allow unescaped HTML content in Handlebars templates.
Never use input from an unknown source as it may contain unsafe data.
Protect dynamic content, which may also contain unsafe data.


Protect dynamic content, which may also contain unsafe data.
Please refer to the following sections for more information and examples that demonstrate these best practices.
Creating Safe Variables
You must always encode a variable before you inject or display it on a webpage. The piece that is not encoded should be isolated and marked as a safe string very carefully. The the following sections outline protectingÂ your system when using JavaScriptÂ and Handlebars templates.
JavaScript
To ensure displayedÂ JavaScript variables are safe, respect these two rules:
If you are displaying plainÂ text without any HTML, use .text() instead of .html()Â with your selectors.
If your text contains HTML formatting, you can use .html()Â as long as it does not contain any dynamic data coming from a variable.
Vulnerable Setup


Vulnerable Setup
This is an example of bad code because it utilizes theÂ .html()Â method to display a dynamic-value variable, leaving the JavaScript vulnerable to injection:
var inputData = $('#myInput').val(); // "<script>alert(0)</script>"
$(selector).html('This is the value: ' + inputData); // "This is the value: <script>alert(0)</script>"
Protected Setup
This isÂ an example of good code, which uses the safe .text() method to protect the JavaScript from potential injection:
var status = 'safe';
$(selector).text('This is very ' + status); // "This is very safe"
This is also an acceptable approach, which uses theÂ Handlebars.Utils.escapeExpression to safely escape the content:


var inputData = $('#myinput').val(); // "<script>alert(0)</script>"
var safeData = Handlebars.Utils.escapeExpression(inputData); // "<script>alert(0)</script>"
$(selector).html('This is the value: ' + safeData); // "This is the value: <script>alert(0)</script>"
Handlebars Templates


Handlebars Templates
Handlebars, by default, handles the encoding of data passed to the template in double brackets (e.g. {{variable}}), however, it also allows you to bypass this encoding by using triple brackets (e.g. {{{variable}}}). Text passed via triple brackets will not be encoded. As a rule, do not use triple brackets. If you don't want Handlebars to encode a piece of a string, use double bracketsÂ and execute Handlebars.SafeString() in your JavaScript controller.Â
As anÂ example, we will apply best practices to a complex use case with dynamicÂ values thatÂ need to be encoded. For this example, weÂ want to displayÂ a message similar to:
Congrats! You just created the record ABCD


The values ABCD and /#Accounts/abcd are dynamic values and, therefore, must be encoded. But the message must also be displayed as HTML, so the overall element cannot be entirely encoded. To address this use case, we must properly script the JavaScript controller and the Handlebars template.
The following JavaScript encodes the message's dynamic values, marks the displayed hyperlink as a safe string, and then outputs the safe message:
var record = {
    id: 'abcd',
    name: 'ABCD'
};
// escape `ABCD`
var safeName = Handlebars.Utils.escapeExpression(record.name);
// escape `abcd`
var safeId = Handlebars.Utils.escapeExpression(record.id);
// Mark the link as a SafeString now that the unsafe pieces are encoded and the content has safe HTML.


var link = new Handlebars.SafeString('' + safeName . '');
// This can be displayed with double brackets in the template because html parts will not be encoded.
this.safeMessage = new Handlebars.SafeString('Congrats! You just created the record ' + link + '.');
To display the safe message in the application, use the following syntax in your Handlebars template:

{{safeMessage}}

Please refer to the HTML Escaping documentation on the Handlebars website for more information.Â
  	Last modified: 2023-02-03 21:04:03


Endpoints and Entry Points
Overview
This document describes how to disable out of the box REST API endpoints and legacy MVC entry points.
Advisory ID: sugarcrm-sr-2015-001Revision: 1.1Last updated: 2015-10-01
Description
SugarCRM has both legacy entry points and REST API endpoints which are shipped out of the box. Not every customer uses all capabilities of the SugarCRM product. To harden the configuration both entry points can be disabled based on the customer's requirements.
Legacy Entry Points
All stock entry points are defined in include/MVC/Controller/entry_point_registry.php. Using the SugarCRM Extension framework the configuration directives can be overridden in an upgrade safe manner. As an example consider the entrypoint "removeme". To disable this entrypoint use the following procedure.


Create a new php file ./custom/Extension/application/Ext/EntryPointRegistry/disable_removeme.php and add the following content:
<?php
if (isset($entry_point_registry['removeme'])) {
    unset($entry_point_registry['removeme']);
}
Execute a quick repair and rebuild as SugarCRM administrator. The entry point is now fully disabled and no longer accessible to respond to any calls. Note that when trying to hit an non-existing (or disabled) entry point, the application will redirect you to the homepage (or login screen if the user has no session).
REST API Endpoints
To disable the HelpAPI which is located at clients/base/api/HelpApi.php use the following procedure.
Create a new php file custom/clients/base/api/CustomHelpApi.php and add the following content:


<?php
require_once 'clients/base/api/HelpApi.php';
class CustomHelpApi extends HelpApi
{
    public function getHelp($api, $args)
    {
        throw new SugarApiExceptionNotFound();
    }
}
Execute a quick repair and rebuild as SugarCRM administrator. The entry point is now fully disabled. When making a REST API call to /rest/v10/help the followingHTTP 404 Not FoundÂ error will be returned:
{
    "error": "not_found",
    "error_message": "Your requested resource was not found."
}
Publication History
2015-10-01
Adding REST API endpoint example
2015-06-16
Initial publication


Adding REST API endpoint example
2015-06-16
Initial publication
A stand-alone copy of this document that omits the distribution URL is an uncontrolled copy, and may lack important information or contain factual errors. SugarCRM reserves the right to change or update this document at any time.
  	Last modified: 2023-02-03 21:04:03


Web Server Configuration
Overview
This document serves as a guideline to harden the web server configuration with regard to running SugarCRM. Note that this is a guideline and certain settings will depend on your specific environment and setup. This guideline focuses on Apache web server as this is SugarCRM's primary supported web server. However, the recommendations in this document apply to all web servers in general.
Advisory ID: sugarcrm-sr-2015-003Revision: 1.1Last updated: 2015-10-01Â
SugarCRM .htaccess file
During installation, SugarCRM deploys an .htaccess file in SugarCRM's root folder. The content of this file may change during upgrades as well. The primary configuration directives managed by SugarCRM are focused around disabling direct access to certain directories and files. Secondly, the configuration contains specific URL rewrite rules which are required for SugarCRM.


Although this file can be used to ship most of the settings which are explained in this document to harden the web server, SugarCRM has chosen not to do so as most of them depend on the customer's setup which cannot be fully controlled by the SugarCRM application itself.
All directives which are managed by SugarCRM are placed between the following markers inside the .htaccess file:
# BEGIN SUGARCRM RESTRICTIONS
RedirectMatch 403 .*\.log$
...
# END SUGARCRM RESTRICTIONS
Customers can add additional directives if need. Make sure to put those directives outside of the above-mentioned markers. SugarCRM can rebuild during upgrades all directives enclosed in the above markers.
Securing .htaccess


Securing .htaccess
Apache allows admins to drop in .htaccess files in any given directory. When a request comes in, Apache will scan the directory recursively for any .htaccess file and apply its configuration directives. This ability is orchestrated by the AllowOverride directive which is enabled by default on most Apache configurations.
To disable this dynamic Apache configuration behavior, disable AllowOverride and make sure to copy paste the .htaccess file content as well.
<Directory "/var/www/html">
    AllowOverride None
    # BEGIN SUGARCRM RESTRICTIONS
    RedirectMatch 403 .*\.log$
    ...
    # END SUGARCRM RESTRICTIONS
</Directory>


...
    # END SUGARCRM RESTRICTIONS
</Directory>
When choosing for this security measure, make sure to verify if any changes have happened in the .htaccess after an upgrade. Those changes need to be applied manually back in the Apache configuration to ensure SugarCRM functions properly. Disabling the .htaccess file support also has a positive impact on the performance as Apache will no longer need to scan for the presence of .htaccess files.
Although Apache does not allow to download .htaccess files, to tighten the security around .htaccess, even more, the following directives can be added:
<Files ".ht*">
    Order deny,allow
    Deny from all
</Files>
Prevent version exposure


Deny from all
</Files>
Prevent version exposure
By default Apache web server exposes its version and OS details in most *nix distributions in both the HTTP response headers as well as on the default error pages. To prevent this behavior use the following configuration directives:
ServerSignature Off
ServerTokens Prod
Â The exposure of the PHP version can be disabled in your php.ini file. When exposed an X-Powered-By response header will be added containing the PHP version information.
expose_php = Off
Another optionÂ is to unset the header explicitly in the Apache configuration:
<IfModule mod_headers.c>
    Header unset X-Powered-By
</IfModule>


Header unset X-Powered-By
</IfModule>
Note that unsetting the Server header is not possible using this way. The web server will always respond with a Server header containing the string "Apache". If it is desired to remove this header as well, additional modules need to be used for this (i.e. mod_security can do this).
Directory listing and index page
Make sure to configure a default index page which should only be index.php for SugarCRM. This is mostly configured but worth checking. Additional SugarCRM does not require the directory browsing support and this is recommended to be disabled. This can be accomplished by removing Options Indexes and/or completely disable the mod_autoindex module.


Optionally additional Allow/Deny directives can be used to apply access lists to certain directories. It is not recommended to add additional authentication through Apache as SugarCRM already fully manages user authentication. The Allow/Deny directives can still be used to apply any IP access list if required by the customer.
The following example secures the root directory (default configuration), disable directory browsing and an additional IP access list is applied.
DocumentRoot "/var/www/html"
<Directory />
    AllowOverride none
    Order allow,deny
    Deny from all
</Directory>
<Directory "/var/www/html">
    DirectoryIndex index.php
    Options -Indexes
    Order deny,allow
    Allow from 10.0.0.0/8
    Deny from all
</Directory>
Additional Options directives


Deny from all
</Directory>
Additional Options directives
SugarCRM does not rely on both CGI and SSI and it is recommended to disable this functionality explicitly. Optionally the FollowSymLinks options can also be disabled depending on your directory setup.
<Directory "/var/www/html">
    Options -ExecCGI -Includes -FollowSymLinks
</Directory>
Web server permissions
Ensure your web server is configured to run as a dedicated non-privileged user. Certain *nix distributions use out of the box the user/group nobody. It is recommended to use a dedicated user/group instead.
User wwwrun
Group www
Ensure the configured user/group have the proper file and directory permissions for SugarCRM. See the installation/upgrade guide for more detailed instructions regarding the required permissions.
Disable unnecessary loadable modules


Disable unnecessary loadable modules
Apache is pluggable by means of loadable modules. To minimize the chances of becoming a victim of an attack it is recommended to disable all loadable modules which are not needed. SugarCRM recommends the following base Apache modules:
mod_authz_host
mod_dir
mod_expires
mod_rewrite
mod_headers
mod_mime
mod_alias
From an Apache perspective to disable a loadable module, it is sufficient to comment out the LoadModule directives. However certain *nix distribution has additional configuration scripts to manage the loaded Apache modules. Please refer to your *nix distribution for this.
Based on your setup additional Apache modules can be used. When using MPM prefork you will also need mod_php5 and when terminating secure HTTP connection on the web server directly also mod_ssl (see below).
HTTP methods


HTTP methods
HTTP uses different methods (verbs) in requests. As per RFC-7231 for HTTP/1.1 different request methods are defined which serve the primary source of the request semantics. The following methods are used by SugarCRM:
For new REST API:
GET
POST
PUT
DELETE
HEAD (*)
OPTIONS (*)
Bootstrap, BWC access, old SOAP/REST API:
GET
POST
(*) Currently not in use, but may be implemented in the future.
The new REST API is accessed using the URL "/rest/vxx/â¦" where xxx represents the API version number. To harden the usage of the additional HTTP methods the following directives can be used for the new REST API endpoint while only allowing GET and POST on the legacy access.
<Directory "/var/www/html">
    <LimitExcept GET POST>


<Directory "/var/www/html">
    <LimitExcept GET POST>
        Order deny,allow
        Deny from all
    </LimitExcept>
</Directory>
<Location "/rest">
    <LimitExcept GET POST PUT DELETE>
        Order deny,allow
        Deny from all
    </LimitExcept>
</Location>
<Location "/api/rest.php">
    <LimitExcept GET POST PUT DELETE>
        Order deny,allow
        Deny from all
    </LimitExcept>
</Location>
Instead of using the Limit directive, an alternative is using mod_allowmethods which is available since Apache 2.4. However this module is flagged as experimental.
Secure HTTP traffic


Secure HTTP traffic
SugarCRM requires the usage of HTTPS transport without any exception. Depending on your setup you can terminate the secure connection on a separate endpoint (load balancer, dedicated HTTPS termination point) or directly on the web server(s). You will also need a valid private key and X.509 certificate. In most cases, the customer needs to acquire an X.509 certificate through a public certification authority of their choice. For internal deployments, this may not be necessary when a local PKI environment is available.
When terminating the secure connection directly on the Apache web server, mod_ssl is required. A dedicated VirtualHost needs to be configured on port 443 (standard HTTPS port). The following is a basic SSL/TLS configuration as a reference. More details are covered in the next paragraphs.
<VirtualHost 1.2.3.4:443>
    SSLEngine on


SSLEngine on
    SSLCertificateFile /etc/pki/tls/certs/example.com.crt
    SSLCertificateKeyFile /etc/pki/tls/certs/example.com.key
    SSLCertificateChainFile /etc/pki/tls/certs/example_bundle.crt
    ServerName crm.example.com
</VirtualHost>
Make sure to have your server name properly configured as it should match the X.509 certificate's CommonName (or one of the SNA's, subject alternative names). More information regarding the certificate requests including the certificate chain can be obtained from your certification authority.
Ensure to redirect all non-secure HTTP (port 80) traffic to the secure virtual host.
<VirtualHost *:80>
    Redirect "/" "https://crm.example.com/"


Redirect "/" "https://crm.example.com/"
</VirtualHost >
Cipher suite hardening
One of the crucial points of HTTPS is to force only secure cryptographic algorithms. Based on new future weaknesses which may be discovered in the future the recommended cipher suite settings can change. As per publish date of this document the usage of SSLv2 and SSLv3 is recommended to be disabled. The current safe secure transport protocols are TLSv1.0, TLSv1.1, and TLSv1.2 with properly configured cipher suites.
For proper deployment of Diffie-Hellman (DH) key exchange algorithms the following guidelines apply:
No longer use out-dated export cipher suites
Ensure the usage of Elliptic Curve Diffie-Hellman (ECDHE) based algorithms
Use of non-common Diffie-Hellman groupsÂ


Use of non-common Diffie-Hellman groupsÂ
More detail around the DH configuration and a detailed list of recommended cipher suites including web server configuration examples can be found at https://weakdh.org/sysadmin.html. Additionally, an online SSL/TLS checker can be used to generate a full report of your current configuration in case your web server is publically reachable. A recommended tool is https://www.ssllabs.com/ssltest/analyze.html. The output of this SSL/TLS test suite will guide you into further (optional) details which can be tweaked regarding your configuration (i.e. OCSP stapling, certificate pinning, â¦).


Please note that using the above instruction "perfect forward secrecy" will be enabled. This basically means that captured TLS traffic can only be decoded using the session key between a specific client and server. Nonperfect forwarding secrecy connections can always be decrypted by having access to only the server's private key. If the customer's setup has intermediate IDS/WAF systems to inspect traffic looking inside the secure traffic, having perfect forwarding secrecy configured may not be desirable.
Public trusted root CA's


Public trusted root CA's
It is recommended that the *nix system where Apache is running always has the latest public root CA (certification authority) files available to authenticate server certificates during the SSL/TLS handshake. Most *nix distributions take care of this through their online update process. If this is not the case, the system administrator is responsible to ensure the latest trusted root CA's are up-to-date. Out of the box, the SugarCRM application contacts the SugarCRM heartbeat and licensing server using a secure connection.
HSTS header


HSTS header
As all traffic requires HTTPS for SugarCRM, it is recommended to configure a global HSTS (HTTP Strict Transport Security) header. This is an additional opt-in security enhancement supported by most modern browsers. The HSTS will instruct the browser to always use HTTPS in the future for the given FQDN. More details regarding HSTS can be found here: https://www.owasp.org/index.php/HTTP_Strict_Transport_Security.
Before enabling the HSTS header, ensure that no other non-HTTPS website is running for the given FQDN and any subdomains.
Header always set Strict-Transport-Security \
"max-age=10886400; includeSubdomains; preload"


"max-age=10886400; includeSubdomains; preload"
Optionally when your SugarCRM application is publically reachable, you can submit your FQDN on the preload which is maintained by Chrome and included by Firefox, Safari, Internet Explorer 11 and Edge browser. Understand the consequences and requirements to end up on the preload list. Once registered, this cannot be undone easily. Submissions for the HSTS preload list can be found at https://hstspreload.appspot.com.
CSP header


CSP header
A Content Security Policy header can be added to offer additional protection against XSS (cross-site scripting). Most modern browsers support CSP and will apply the limitation as directed by the CSP header. The CSP header defines the allowed sources through different directives which are allowed regarding JScript, CSS, images, fonts, form actions, plugins, â¦ More information can be found at https://www.owasp.org/index.php/Content_Security_Policy.
The management of CSP headers can become complex and may depend on different types of customizations which can be deployed on the SugarCRM platform.Â
For now, CSP headers can be configured directly on the web server. A basic configuration looks like this:Â
Header always set Content-Security-Policy \
"default-src 'self'; script-src 'self'"


"default-src 'self'; script-src 'self'"
Note:Â This is a syntax example of a CSP header and not a recommended value.
Older browsers use the header X-Content-Security-Policy or X-WebKit-CSP instead of the standardized one. The CSP header can also safely be added first in "report only" mode before deploying it in production using Content-Security-Policy-Report-Only.
Additional security headers
To prevent clickjacking an X-Frame-Options header can be added. This header can also be used to iframe the SugarCRM application inside another web site if required. This can be better controlled with the above mentioned CSP header. Both can coexist together but it is encouraged to used the CSP approach.
Header always set X-Frame-Options "SAMEORIGIN"


Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Frame-Options "ALLOW-FROM https://someothersite.com"
To protect against "drive-by download" attacks, it is encouraged to disable Content-Type sniffing. This is particularly targeted at Chrome and IE.
Header always set X-Content-Type-Options "nosniff"
Ensure XSS browser protection is enabled by adding the following header. By default, this feature should be enabled in every browser. In case it has been disabled, sending this header will re-enable it.
Header always set X-XSS-Protection "1; mode=block"
Secure cookie settings
Ensure the following settings are applied in php.ini to ensure legacy cookies are properly protected against XSS attacks and are only transmitted over a secure HTTPS connection.
session.cookie_httponly = 1


session.cookie_httponly = 1
session.cookie_secure = 1
Additionally, the session.cookie_path can be tweaked if other applications are running on the same web server.
Security modules
Additional Apache loadable security modules exist which can optionally be installed to better protect your system from malicious requests and/or DDOS attacks. The following modules are available for Apache 2.4:
mod_security - This module adds WAF (web application firewall) capabilities to your Apache setup. In combination with the OWASP CRS (core rule set) gives you a solid starting point for a WAF implementation.
mod_evasive - This module helps to prevent (D)DOS attacks
Covering both modules in details is out of the scope of this Security Response. Refer to the module documentation for additional details. In the future, SugarCRM may publish specific settings and fine-tuning for both modules.Â
Additional resources


Additional resources
Generic Apache security tipshttp://httpd.apache.org/docs/2.4/misc/security_tips.html
Online SSL/TLS testerhttps://www.ssllabs.com/ssltest/analyze.html
Useful HTTP headershttps://www.owasp.org/index.php/List_of_useful_HTTP_headers
Publication History
2015-10-01
Adding HTTP method restrictions
2015-09-08
Initial publication
A stand-alone copy of this document that omits the distribution URL is an uncontrolled copy and may lack important information or contain factual errors. SugarCRM reserves the right to change or update this document at any time.
  	Last modified: 2023-02-03 21:04:03


Data Framework
The Sugar application comprises core elements such as modules, fields, vardefs, and other foundational components. The Data Framework pages document how these core elements are modeled and implemented in Sugar.


TopicsModulesHow modules are defined and used within the systemModelsAn overview of the SugarBean model.VardefsVardefs (Variable Definitions) provide the Sugar application with information about SugarBeans. Vardefs specify information on the individual fields, relationships between beans, and the indexes for a given bean.FieldsHow fields interact with the various aspects of Sugar.RelationshipsRelationships are the basis for linking information within the system. This page explains the various aspects of relationships. For information on custom relationships, please refer to the Custom Relationships documentation.SubpanelsFor Sidecar, Sugar's subpanel layouts have been modified to work as simplified metadata. This page is an overview of the metadata framework for subpanels.DatabaseAll Sugar products support the MySQL and Microsoft SQL Server databases. Sugar Enterprise and Sugar Ultimate also support the DB2 and Oracle databases. In general, Sugar uses only common database functionality, and the application logic is embedded in the


databases. In general, Sugar uses only common database functionality, and the application logic is embedded in the PHP code. Sugar does not use or recommend database triggers or stored procedures. This design simplifies coding and testing across different database vendors. The only implementation difference across the various supported databases is column types.


Last modified: 2023-02-03 21:04:03


Models
Overview
Each module in Sugar is extending theÂ Sugar Model. This model is determined by the SugarBean, which contains methods to create, read/retrieve, update, and delete records in the database and any subclass of the SugarBean. Many of the common Sugar modules also use the SugarObjects class, which is explained in the next section.
SugarObject Templates
Sugar objects extend the concept of subclassing a step further and allow you to subclass the vardefs. This includes inheriting of fields, relationships, indexes, and language files. However, unlike subclassing, you are not limited to a single inheritance. For example, if there were a Sugar object for fields used in every module (e.g. id, deleted, or date_modified), you could have the module inherit from both the Basic Sugar object and the Person Sugar object.


To further illustrate this, let's say the Basic object type has a field 'name' with length 10 and the Company object has a field 'name' with length 20. If you inherit from Basic first and then Company, your field will inherit the Company object's length of 20. However, the module-level setting always overrides any values provided by Sugar objects, so, if the field 'name' in your module has been set to length 60, then the field's length will ultimately be 60.
There are six types of SugarObject templates:
Basic : Contains the basic fields required by all Sugar modules
Person : Based on the Contacts, Prospects, and Leads modules
Issue : Based on the Bugs and Cases modules
Company : Based on the Accounts module
File : Based on the Documents module
Sale : Based on the Opportunities module
SugarObject Interfaces


Sale : Based on the Opportunities module
SugarObject Interfaces
In addition to the object templates above, "assignable" object templates can beÂ used for modules with records that should contain an Assigned To field. Although every module does not use this, most modules allow assignment of records to users. SugarObject interfaces allow us to add the assignable attribute to these modules.
SugarObject interfaces and SugarObject templates are very similar to one another, but the main distinction is that templates have a base class you can subclass while interfaces do not. If you look into the file structure, templates include many additional files, including a full metadata directory. This is used primarily for Module Builder.
File Structure
./include/SugarObjects/interfaces
./include/SugarObjects/templates
Implementation
There are two things you need to do to take advantage of SugarÂ objects:


There are two things you need to do to take advantage of SugarÂ objects:
Subclass the SugarObject class you wish to extend for your class:class MyClass extends Person
{
    function MyClass()
    {
        parent::Person();
    }
}
Add the following line to the end of theÂ vardefs.php file:VardefManager::createVardef('Contacts', 'Contact', array('default', 'assignable', 'team_security', 'person'));
This snippet tells the VardefManager to create a cache of the Contacts vardefs with the addition of all the default fields, assignable fields, team security fields, and all fields from the person class.
Performance Considerations


Performance Considerations
VardefManager caches the generated vardefs into a single file that is loaded at runtime. If that file is not found, SugarÂ loads the vardefs.php file, located in your module's directory. The same is true for language files. This caching also includes data for custom fields and any vardef or language extensions that are dropped into the extension framework.
Cache Files
./cache/modules/<module>/<object_name>vardefs.php
./cache/modules/<module>/languages/en_us.lang.php


./cache/modules/<module>/languages/en_us.lang.php
TopicsSugarBean$bean = BeanFactory::retrieveBean($module, $id);BeanFactoryThe BeanFactory class, located in ./data/BeanFactory.php, is used for loading an instance of a SugarBean. This class should be used any time you are creating or retrieving bean objects. It will automatically handle any classes required for the bean.
  	Last modified: 2023-02-03 21:04:03


SugarBean
//Retrieve bean
$bean = BeanFactory::retrieveBean($module, $id);
//Set deleted to true
$bean->mark_deleted($bean->id);
//Save
$bean->save();
Overview
The SugarBean class supports all the model operations to and from the database. Any module that interacts with the database utilizes the SugarBean class, which contains methods to create, read/retrieve, update, and delete records in the database (CRUD), as well as fetch related records.
CRUD Handling
The SugarBean handles the most basic functions of the Sugar database, allowing you to create, retrieve, update, and delete data.Â
Creating and Retrieving Records


Creating and Retrieving Records
The BeanFactory class is used for bean loading. The class should be used wheneverÂ you are creating or retrieving bean objects. It will automatically handle any classes required for the bean. More information on this can be found in the BeanFactory section.
Obtaining the Id of a Recently Saved Bean
For new records, a GUID is generated and assigned to the record id field. Saving new or existing records returns a single value to the calling routine, which is the id attribute of the saved record. The id has the following format:
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
You can retrieve a newly created record's id with the following:
//Create bean
$bean = BeanFactory::newBean($module);
//Populate bean fields
$bean->name = 'Example Record';
//Save
$bean->save();
//Retrieve the bean id


//Save
$bean->save();
//Retrieve the bean id
$record_id = $bean->id;
Saving a Bean with a Specific ID
Saving a record with a specific id requires the id and new_with_id attribute of the bean to be set. When doing so, it is important that you use a globally unique identifier. Failing to do so may result in unexpected behavior in the system. An example setting an id is shown below:
//Create bean
$bean = BeanFactory::newBean($module);
//set the new flag
$bean->new_with_id = true;
//Set the record id with a static id
$id = '38c90c70-7788-13a2-668d-513e2b8df5e1';
$bean->id = $id;
//or have the system generate an id for you


$bean->id = $id;
//or have the system generate an id for you
//$id = Sugarcrm\Sugarcrm\Util\Uuid::uuid1()
//$bean->id = $id;
//Populate bean fields
$bean->name = 'Example Record';
//Save
$bean->save();
Setting new_with_id to true prevents the save method from creating a new id value and uses the assigned id attribute. If the id attribute is empty and the new_with_id attribute is set to true, the save will fail. If you would like for the system to generate an id for you, you can use Sugarcrm\Sugarcrm\Util\Uuid::uuid1().
Distinguishing New from Existing Records


Distinguishing New from Existing Records
To identify whether or not a record is new or existing, you can check the fetched_rows property. If the $bean has a fetched_row, it was loaded from the database. An example is shown below:
if (!isset($bean->fetched_row['id'])) {
    //new record
} else {
    //existing record
}
If you are working with a logic hook such as before_save or after_save, you should check the arguments.isUpdate property to determine this as shown below:
<?php
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
class logic_hooks_class
{
    function hook_method($bean, $event, $arguments)
    {


function hook_method($bean, $event, $arguments)
    {
        if (isset($arguments['isUpdate']) && $arguments['isUpdate'] == false) {
            //new record
        } else {
            //existing record
        }
    }
}
?>
Retrieving a Bean by Unique Field
Sometimes developers have a need to fetch a record based on a unique field other than the id. In previous versions you were able to use theÂ retrieve_by_string_fields()Â method to accomplishÂ this, however, that has now been deprecated. To search andÂ fetch records,Â you shouldÂ use the SugarQueryÂ builder. An example of this is shown below:
require_once('include/SugarQuery/SugarQuery.php');
$sugarQuery = new SugarQuery();


$sugarQuery = new SugarQuery();
//fetch the bean of the module to query
$bean = BeanFactory::newBean('<modules>');
//create first query
$sql = new SugarQuery();
$sql->select('id');
$sql->from($bean);
$sql->Where()->equals('<field>', '<unique value>');
$result = $sql->execute();
$count = count($result);
if ($count == 0) {
    //no results were found
} elseif ($count == 1) {
    //one result was found
    $bean = BeanFactory::getBean('<module>', $result[0]['id']);
} else {
    //multiple results were found
}
Updating a Bean
You can updateÂ a bean by fetching a record and then updating the property:


You can updateÂ a bean by fetching a record and then updating the property:
//Retrieve bean
$bean = BeanFactory::retrieveBean($module, $id);
//Fields to update
$bean->name = 'Updated Name';
//Save
$bean->save();
Note: Disabling row-level security when accessing a bean should be set to trueÂ only when it is absolutely necessary to bypass security, for example, when updating a Lead record from a custom Entry Point.Â An example of accessing the bean while bypassing row security is:
$bean = BeanFactory::retrieveBean($module, $record_id, array('disable_row_level_security' => true));
Updating a Bean Without Changing the Date Modified


Updating a Bean Without Changing the Date Modified
The SugarBean class contains an attribute called update_date_modified, which is set to true when the class is instantiated and means that the date_modified attribute is updated to the current database date timestamp. SettingÂ update_date_modified to false would result in the date_modified attribute not being set with the current database date timestamp.
//Retrieve bean
$bean = BeanFactory::retrieveBean($module, $id);
//Set modified flag
$bean->update_date_modified = false;
//Fields to update
$bean->name = 'Updated Name';
//Save
$bean->save();


$bean->name = 'Updated Name';
//Save
$bean->save();
Note: Disabling row-level security when accessing a bean should be set to trueÂ only when it is absolutely necessary to bypass security, for example, when updating a Lead record from a custom Entry Point.Â An example of accessing the bean while bypassing row security is:
$bean = BeanFactory::retrieveBean($module, $record_id, array('disable_row_level_security' => true));
Deleting a Bean
Deleting a bean can be done by fetching it then calling theÂ mark_deleted()Â method which makes sure any relationships with related records are removedÂ as well:
//Retrieve bean
$bean = BeanFactory::retrieveBean($module, $id);
//Set deleted to true
$bean->mark_deleted($id);
//Save


//Set deleted to true
$bean->mark_deleted($id);
//Save
$bean->save();
Note: Disabling row-level security when accessing a bean should be set to trueÂ only when it is absolutely necessary to bypass security, for example, when updating a Lead record from a custom Entry Point.Â An example of accessing the bean while bypassing row security is:
$bean = BeanFactory::retrieveBean($module, $record_id, array('disable_row_level_security' => true));
Fetching Relationships
This sectionÂ explains how theÂ SugarBean class can be used to fetch related information from the database.
Fetching Related Records
To fetch related records, load the relationship using the link:
//If relationship is loaded
if ($bean->load_relationship($link)) {
    //Fetch related beans


if ($bean->load_relationship($link)) {
    //Fetch related beans
    $relatedBeans = $bean->$link->getBeans();
}
An example of this is to load the contacts related to an account:
//Load Account
$bean = BeanFactory::getBean('Accounts', $id);
//If relationship is loaded
if ($bean->load_relationship('contacts')) {
    //Fetch related beans
    $relatedBeans = $bean->contacts->getBeans();
}
Fetching Related Record IDs
To fetch only related record IDs, load the relationship using the link:
//If relationship is loaded
if ($bean->load_relationship($link)) {
    //Fetch related record IDs
    $relatedBeans = $bean->$link->get();
}


$relatedBeans = $bean->$link->get();
}
An example of this is to load the record IDs of contacts related to an account:
//Load Account
$bean = BeanFactory::getBean('Accounts', $id);
//If relationship is loaded
if ($bean->load_relationship('contacts')) {
    //Fetch related beans
    $relatedBeans = $bean->contacts->get();
}
Fetching a Parent Record
Fetching a parent record is similar to fetching child records in that you will still need to load the relationship using the link:
//If relationship is loaded
if ($bean->load_relationship($link)) {
    //Fetch related beans
    $relatedBeans = $bean->$link->getBeans();
    $parentBean = false;
    if (!empty($relatedBeans)) {


$parentBean = false;
    if (!empty($relatedBeans)) {
        //order the results
        reset($relatedBeans);
        //first record in the list is the parent
        $parentBean = current($relatedBeans);
    }
}
An example of this is to load a contact and fetch its parent account:
//Load Contact
$bean = BeanFactory::getBean('Contacts', $id);
//If relationship is loaded
if ($bean->load_relationship('accounts')) {
    //Fetch related beans
    $relatedBeans = $bean->accounts->getBeans();
    $parentBean = false;
    if (!empty($relatedBeans)) {
        //order the results
        reset($relatedBeans);
        //first record in the list is the parent


reset($relatedBeans);
        //first record in the list is the parent
        $parentBean = current($relatedBeans);
    }
}
TopicsCustomizing Core SugarBeansThis article covers how to extend core SugarBean objects to implement custom code. This can be used to alter the stock assignment notification message or to add logic to change the default modules behavior.Implementing Custom SugarBean TemplatesThis article covers how to implement custom SugarBean templates, which can then be extended by custom modules. It also covers vardef templates, which can more portable and used by multiple modules. By default, Sugar comes with a few templates such as Company, Person, Issue, and File templates.
  	Last modified: 2023-02-03 21:04:03


Implementing Custom SugarBean Templates
Overview
This article covers how to implement custom SugarBean templates, which can then be extended by custom modules. It also covers vardef templates, which can more portable and used by multiple modules. By default, Sugar comes with a few templates such as Company, Person, Issue, and File templates.
Creating a Custom SugarBean Template


Creating a Custom SugarBean Template
The following example will create a custom Bean template that can be used by custom modules to shrink down the overall size of the Bean model, by only implementing the two fields required by all modules, the id field and the deleted field. In order to do this, we will have to override some core SugarBean methods, as the base SugarBean is statically configured to do things like Save/Delete the model with the date_entered, date_modified, modified_user_id and created_user_id fields. In order to accommodate custom Beans that wish to use those fields, this template will also have properties for configuring those types of fields so that they are populated using the default SugarBean logic.


To create a template for use by custom modules, you create a class that extends from SugarBean inÂ ./custom/include/SugarObjects/templates/<template_name>/<class_name>.php. For this example, we will create the "bare" template with the following file:
./custom/include/SugarObjects/templates/bare/BareBean.php
<?php
class BareBean extends SugarBean
{
    /**
     * The configured modified date field
     * @var string|false
     */
    protected $_modified_date_field = false;
    /**
     * The configured modified user field
     * @var string|false
     */
    protected $_modified_user_field = false;
    /**
     * The configured created date field
     * @var string|false
     */


* The configured created date field
     * @var string|false
     */
    protected $_created_date_field = false;
    /**
     * The configured created user field
     * @var string|false
     */
    protected $_created_user_field = false;
    /**
     * Get the field name where modified date is stored, if in use by Module
     * @return string|false
     */
    public function getModifiedDateField()
    {
        if ($this->_modified_date_field !== FALSE){
            $field = $this->_modified_date_field;
            if (isset($this->field_defs[$field]) && $this->field_defs[$field]['type'] == 'datetime'){
                return $field;
            }
        }
        return FALSE;
    }


return $field;
            }
        }
        return FALSE;
    }
    /**
     * Override the stock setModifiedDate method
     *  - Check that field is in use by this Module
     *  - If in use, set the configured field to current time
     * @inheritdoc
     **/
    public function setModifiedDate($date = '')
    {
        global $timedate;
        $field = $this->getModifiedDateField();
        if ($field !== FALSE){
            // This code was duplicated from the stock SugarBean::setModifiedDate
            if ($this->update_date_modified || empty($this->$field)) {
                // This only needs to be calculated if it is going to be used
                if (empty($date)) {


if (empty($date)) {
                    $date = $timedate->nowDb();
                }
                $this->$field = $date;
            }
        }
    }
    /**
     * Get the field name where modified user ID is stored, if in use by Module
     * @return string|false
     **/
    public function getModifiedUserField()
    {
        if ($this->_modified_user_field !== FALSE){
            $field = $this->_modified_user_field;
            if (isset($this->field_defs[$field]) && $this->field_defs[$field]['type'] == 'assigned_user_name'){
                return $field;
            }
        }
        return FALSE;
    }
    /**
     * Override the stock setModifiedUser Method


}
    /**
     * Override the stock setModifiedUser Method
     * - Check that field is in use by this Module
     * - If in use, set the configured field to user_id
     * @inheritdoc
     * @param User|null $user [description]
     */
    public function setModifiedUser(User $user = null)
    {
        global $current_user;
        $field = $this->getModifiedUserField();
        if ($field !== FALSE){
            // If the update date modified by flag is set then carry out this directive
            if ($this->update_modified_by) {
                // Default the modified user id to the default
                $this->$field = 1;
                // If a user was not presented, default to the current user
                if (empty($user)) {


if (empty($user)) {
                    $user = $current_user;
                }
                // If the user is set, use it
                if (!empty($user)) {
                    $this->$field = $user->id;
                }
            }
        }
    }
    /**
     * Get the field name where created date is stored, if in use by Module
     * @return string|false
     */
    public function getCreatedDateField()
    {
        if ($this->_created_date_field !== FALSE){
            $field = $this->_created_date_field;
            if (isset($this->field_defs[$field]) && $this->field_defs[$field]['type'] == 'datetime'){
                return $field;
            }
        }
        return FALSE;
    }


return $field;
            }
        }
        return FALSE;
    }
    /**
     * Get the field name where created user ID is stored, if in use by Module
     * @return string|false
     */
    public function getCreatedUserField()
    {
        if ($this->_created_user_field !== FALSE){
            $field = $this->_created_user_field;
            if (isset($this->field_defs[$field]) && $this->field_defs[$field]['type'] == 'assigned_user_name'){
                return $field;
            }
        }
        return FALSE;
    }
    /**
     * Override the stock setCreateData method
     * - Code was duplicated from stock, to accommodate not having created date or created user fields


* @inheritdoc
     */
    public function setCreateData($isUpdate, User $user = null)
    {
        if (!$isUpdate) {
            global $current_user;
            $field = $this->getCreatedDateField();
            if ($field !== FALSE){
                //Duplicated from SugarBean::setCreateData with modifications for dynamic field name
                if (empty($this->$field)) {
                    $this->$field = $this->getDateModified();
                }
                if (empty($this->$field)){
                    global $timedate;
                    $this->$field = $timedate->nowDb();
                }
            }
            $field = $this->getCreatedUserField();
            if ($field !== FALSE){


if ($field !== FALSE){
                //Duplicated from SugarBean::setCreateData with modifications for dynamic field name
                if ($this->set_created_by == true) {
                    // created by should always be this user
                    // unless it was set outside of the bean
                    if ($user) {
                        $this->$field = $user->id;
                    } else {
                        $this->$field = isset($current_user) ? $current_user->id : "";
                    }
                }
            }
            if ($this->new_with_id == false) {
                $this->id = Sugarcrm\Sugarcrm\Util\Uuid::uuid1();
            }
        }
    }
    /**
     * Get the Date Modified fields value
     * @return mixed|false
     */


* Get the Date Modified fields value
     * @return mixed|false
     */
    public function getDateModified()
    {
        if ($this->_modified_date_field !== FALSE){
            $field = $this->_modified_date_field;
            return $this->$field;
        }
        return FALSE;
    }
    /**
     * Get the Date Created Fields value
     * @return mixed|false
     */
    public function getDateCreated()
    {
        if ($this->_created_date_field !== FALSE){
            $field = $this->_created_date_field;
            return $this->$field;
        }
        return FALSE;
    }
    /**
     * @inheritdoc
     */
    public function mark_deleted($id)
    {


*/
    public function mark_deleted($id)
    {
        $date_modified = $GLOBALS['timedate']->nowDb();
        if (isset($_SESSION['show_deleted'])) {
            $this->mark_undeleted($id);
        } else {
            // Ensure that Activity Messages do not occur in the context of a Delete action (e.g. unlink)
            // and do so for all nested calls within the Top Level Delete Context
            $opflag = static::enterOperation('delete');
            $aflag = Activity::isEnabled();
            Activity::disable();
            // call the custom business logic
            $custom_logic_arguments['id'] = $id;
            $this->call_custom_logic("before_delete", $custom_logic_arguments);


$this->deleted = 1;
            if (isset($this->field_defs['team_id'])) {
                if (empty($this->teams)) {
                    $this->load_relationship('teams');
                }
                if (!empty($this->teams)) {
                    $this->teams->removeTeamSetModule();
                }
            }
            $this->mark_relationships_deleted($id);
            $updateFields = array('deleted' => 1);
            $field = $this->getModifiedDateField();
            if ($field !== FALSE){
                $this->setModifiedDate();
                $updateFields[$field] = $this->$field;
            }
            $field = $this->getModifiedUserField();
            if ($field !== FALSE){


if ($field !== FALSE){
                $this->setModifiedUser();
                $updateFields[$field] = $this->$field;
            }
            $this->db->updateParams(
                $this->table_name,
                $this->field_defs,
                $updateFields,
                array('id' => $id)
            );
            if ($this->isFavoritesEnabled()) {
                SugarFavorites::markRecordDeletedInFavorites($id, $date_modified);
            }
            // Take the item off the recently viewed lists
            $tracker = BeanFactory::newBean('Trackers');
            $tracker->makeInvisibleForAll($id);
            SugarRelationship::resaveRelatedBeans();
            // call the custom business logic


SugarRelationship::resaveRelatedBeans();
            // call the custom business logic
            $this->call_custom_logic("after_delete", $custom_logic_arguments);
            if(static::leaveOperation('delete', $opflag) && $aflag) {
                Activity::enable();
            }
        }
    }
    /**
     * @inheritdoc
     */
    public function mark_undeleted($id)
    {
        // call the custom business logic
        $custom_logic_arguments['id'] = $id;
        $this->call_custom_logic("before_restore", $custom_logic_arguments);
        $this->deleted = 0;
        $modified_date_field = $this->getModifiedDateField();


$modified_date_field = $this->getModifiedDateField();
        if ($modified_date_field !== FALSE){
            $this->setModifiedDate();
        }
        $query = "UPDATE {$this->table_name} SET deleted = ?".(!$modified_date_field?"":",$modified_date_field = ?")." WHERE id = ?";
        $conn = $this->db->getConnection();
        $params = array($this->deleted);
        if ($modified_date_field){
            $params[] = $this->$modified_date_field;
        }
        $params[] = $id;
        $conn->executeQuery($query, $params);
        // call the custom business logic


// call the custom business logic
        $this->call_custom_logic("after_restore", $custom_logic_arguments);
    }
}
With the Bean template in place, we can define the default vardefs for the template by creating the following file:
./custom/include/SugarObjects/templates/bare/vardefs.php
<?php
$vardefs = array(
    'audited' => false,
    'favorites' => false,
    'activity_enabled' => false,
    'fields' => array(
        'id' => array(
            'name' => 'id',
            'vname' => 'LBL_ID',
            'type' => 'id',
            'required' => true,
            'reportable' => true,


'required' => true,
            'reportable' => true,
            'duplicate_on_record_copy' => 'no',
            'comment' => 'Unique identifier',
            'mandatory_fetch' => true,
        ),
        'deleted' => array(
            'name' => 'deleted',
            'vname' => 'LBL_DELETED',
            'type' => 'bool',
            'default' => '0',
            'reportable' => false,
            'duplicate_on_record_copy' => 'no',
            'comment' => 'Record deletion indicator'
        )
    ),
    'indices' => array(
        'id' => array(


),
    'indices' => array(
        'id' => array(
            'name' => 'idx_' . preg_replace('/[^a-z0-9_\-]/i', '', strtolower($module)) . '_pk',
            'type' => 'primary',
            'fields' => array('id')
        ),
        'deleted' => array(
            'name' => 'idx_' . strtolower($table_name) . '_id_del',
            'type' => 'index',
            'fields' => array('id', 'deleted')
        )
    ),
    'duplicate_check' => array(
        'enabled' => false
    )
);


'enabled' => false
    )
);
Note: This vardef template also shrinks the SugarBean template down even further, by defaulting auditing, favorites, and activity stream to false.
Using a Custom Bean Template


With the custom SugarBean template in place, we can now implement the template on a custom module. Typically if you deployed a module from Module Builder, there would be two classes generated by Sugar, "custom_Module_sugar" and "custom_Module". The "_sugar" generated class extends from the Bean template that was selected in Module Builder, and the primary Bean class is what is utilized in the system and where your development would take place. For the above example template that is created, the assumption is that you are writing the Bean class, rather than using the generated classes by Module Builder, or at the very least removing the intermediary "_sugar" class generated by Module Builder and extending from the desired template. If you deployed a module via Module Builder, and are going to switch to a custom template, please note that some of the stock templates contain internal logic for the setup of fields for that template and that would need to be duplicated if you intend to continue using


setup of fields for that template and that would need to be duplicated if you intend to continue using those fields. The stock templates, located inÂ ./include/SugarObjects/templates/, are available for your reference to copy over any of the required logic to your custom Bean class. With all that being said, let us take a look at a custom Bean class that extends from our custom template.


This example will use the "custom_Module" module which is a module that will be used for Tagging records. Since the module is just storing tags, a slimmed down Bean works well as we only need a "name" field to store those tags. The following class would implement the custom Bean template created above.
./modules/custom_Module/custom_Module.php
<?php
require_once 'custom/include/SugarObjects/templates/bare/BareBean.php';
class custom_Module extends BareBean {
    public $new_schema = true;
    public $module_dir = 'custom_Module';
    public $object_name = 'custom_Module';
    public $table_name = 'custom_Module';
    public $importable = true;
    public $id;
    public $name;
    public $deleted;


public $id;
    public $name;
    public $deleted;
    public function __construct(){
        parent::__construct();
    }
    public function bean_implements($interface){
        switch($interface){
            case 'ACL': return true;
        }
        return false;
    }
}
With the modules SugarBean class created, the other thing that needs to be implemented is the vardefs.php file:
./modules/custom_Module/vardefs.php
<?php
$module = 'custom_Module';
$table_name = strtolower($module);
$dictionary[$module] = array(
    'table' => $table_name,
    'fields' => array(
        'name' => array(
            'name' => 'name',


'name' => array(
            'name' => 'name',
            'vname' => 'LBL_NAME',
            'type' => 'username',
            'link' => true,
            'dbType' => 'varchar',
            'len' => 255,
            'unified_search' => true,
            'full_text_search' => array(),
            'required' => true,
            'importable' => 'required',
            'duplicate_merge' => 'enabled',
            //'duplicate_merge_dom_value' => '3',
            'merge_filter' => 'selected',
            'duplicate_on_record_copy' => 'always',
        ),
    ),
    'relationships' => array (
    ),
    'optimistic_locking' => false,


),
    'optimistic_locking' => false,
    'unified_search' => false,
);
VardefManager::createVardef(
    $module,
    $module,
    //Specify the bare template to be used to create the vardefs
    array('bare')
);
With these files implemented, the "custom_Module" module would implement the "bare" template we created.
Creating Custom Vardef Templates


Creating Custom Vardef Templates
For some customizations, you might not need a SugarBean template as you may not be implementing logic that needs to be shared across multiple module's Bean classes. However, you may have field definitions that are common across multiple modules that would be beneficial for implementing as Vardef Templates. To create a vardef template, a file as follows, ./custom/include/SugarObjects/implements/<template_name>/vardefs.php.


Continuing on with our example custom_Module module from above, we might want to have a creation date on this custom tags module since our 'bare' SugarBean template does not come with one by default. We could easily just add one in the modules vardef file, but for our example purposes, we know that we will use our 'bare' SugarBean template on other customizations, and on some of those we might also want to include a creation date. To implement the vardef template for the creation date field, we create the following:
./custom/include/SugarObjects/implements/date_entered/vardefs.php
<?php
$vardefs = array(
    'fields' => array(
        'date_entered' => array(
            'name' => 'date_entered',


'name' => 'date_entered',
            'vname' => 'LBL_DATE_ENTERED',
            'type' => 'datetime',
            'group' => 'created_by_name',
            'comment' => 'Date record created',
            'enable_range_search' => true,
            'options' => 'date_range_search_dom',
            'studio' => array(
                'portaleditview' => false,
            ),
            'duplicate_on_record_copy' => 'no',
            'readonly' => true,
            'massupdate' => false,
            'full_text_search' => array(
                'enabled' => true,
                'searchable' => false
            ),
        )
    ),
    'indices' => array(


),
        )
    ),
    'indices' => array(
        'date_entered' => array(
            'name' => 'idx_' . strtolower($table_name) . '_date_entered',
            'type' => 'index',
            'fields' => array('date_entered')
        )
    )
);
Using a Custom Vardef Template
Once the vardef template is in place, you can use the template by adding it to the 'uses' array property of your module vardefs. Continuing with our example module custom_Module, we can update the vardefs file as follows:
./modules/custom_Module/vardefs.php
<?php
$module = 'custom_Module';
$table_name = strtolower($module);
$dictionary[$module] = array(


$dictionary[$module] = array(
    'table' => $table_name,
    'fields' => array(
        'name' => array(
            'name' => 'name',
            'vname' => 'LBL_NAME',
            'type' => 'username',
            'link' => true,
            'dbType' => 'varchar',
            'len' => 255,
            'unified_search' => true,
            'full_text_search' => array(),
            'required' => true,
            'importable' => 'required',
            'duplicate_merge' => 'enabled',
            //'duplicate_merge_dom_value' => '3',
            'merge_filter' => 'selected',
            'duplicate_on_record_copy' => 'always',


'duplicate_on_record_copy' => 'always',
        ),
    ),
    'relationships' => array (
    ),
    //Add the desired vardef templates to this list
    'uses' => array(
        'date_entered'
    ),
    'optimistic_locking' => false,
    'unified_search' => false,
);
VardefManager::createVardef(
    $module,
    $module,
    //Specify the bare template to be used to create the vardefs
    array('bare')
);
After a Quick Repair and Rebuild, a SQL statement should be generated to update the table of the module with the new date_entered field that was added to the vardefs using the vardef template.


Last modified: 2023-02-03 21:04:03


Customizing Core SugarBeans
Overview
This article covers how to extend core SugarBean objects to implement custom code. This can be used to alter the stock assignment notification message or toÂ add logic to change theÂ default modules behavior.
Customizing a Core Module
The best approach to customizing a core SugarBean object is to become familiar with how the core Bean operates and only modify the bean logic where absolutely necessary. After understanding where you wish to make your customization in the module Bean class, you can extend the class in the custom directory.Â In order to avoid duplicating code from the core Bean class, it is always best to extend the class rather than duplicate the class to the custom directory.Â
Extending the SugarBean
For this example, we will look at modifying the Leads SugarBean object. To extend the core Leads bean, create the following file:


./custom/modules/Leads/CustomLead.php
<?php
require_once 'modules/Leads/Lead.php';
class CustomLead extends Lead
{
    /**
     * Saves the record
     * - You can use this method to implement before save or after save logic
     *
     * @param bool $check_notify
     * @return string
     */
    function save($check_notify = FALSE)
    {
        $id = parent::save($check_notify);
        return $id;
    }
    /**
     * Retrieves a record
     * - You can use this method to set properties when fetching a bean
     *
     * @param string $id
     * @param bool $encode
     * @param bool $deleted
     * @return $this
     */


* @param bool $deleted
     * @return $this
     */
    function retrieve($id = '-1', $encode = true, $deleted = true)
    {
        return parent::retrieve($id, $encode, $deleted);
    }
    /**
     * Calls custom logic events
     * - You can use this method to watch for specific logic hook events
     *
     * @param $event
     * @param array $arguments
     */
    function call_custom_logic($event, $arguments = array())
    {
        parent::call_custom_logic($event, $arguments);
    }
}
Register the Custom Class


}
}
Register the Custom Class
Once the custom SugarBean class file has been created, register that class to be used by the module so that the BeanFactory knows which class to use. To register the class you will create the following file in the ./custom/Extension/ directory:
./custom/Extension/application/Ext/Include/customLead.php
<?php
/**
* The $objectList array, maps the module name to the Vardef property
* By default only a few core modules have this defined, since their Class/Object names differs from their Vardef Property
**/
$objectList['Leads'] = 'Lead';
// $beanList maps the Bean/Module name to the Class name
$beanList['Leads'] = 'CustomLead';
// $beanFiles maps the Class name to the PHP Class file


// $beanFiles maps the Class name to the PHP Class file
$beanFiles['CustomLead'] = 'custom/modules/Leads/CustomLead.php';
Note:Â The $objectListÂ array only needs to be set on those modules that do not have it set already. You can view ./include/modules.php to see the core modules that have it defined already.
Once the registration file is in place, go to Admin > Repairs, and run a Quick Repair and Rebuild so that the system starts using the custom class.
Things to Note
Overriding a core SugarBean class comes with some caveats:
The custom class is only used when the product core code uses theÂ BeanFactory.Â


For most circumstances, Sugar is set up to use BeanFactory, however, legacy code or specific logic that is pairs core modules together may use direct calls to a core SugarBean class, which would then cause the custom class to not be used. In those scenarios,Â it is recommended to use a logic look instead.
Extending the Cases module doesn't affect the email Import process.
If theÂ $objectListÂ property is not defined for the module, the custom object will be used, however, things like Studio will no longer work correctly.
For the example above, we defined the $objectList property for the Leads module to make sure that Studio continued working as expected. Without this definition, if you navigate to Admin > Studio, fields and relationships will not render properly.
  	Last modified: 2023-02-03 21:04:03


BeanFactory
Overview
The BeanFactory class, located in ./data/BeanFactory.php, is used for loading an instance of a SugarBean. This class should be used any time you are creating or retrieving bean objects. It will automatically handle any classes required for the bean.
Creating a SugarBean Object
newBean()
To create a new, empty SugarBean, use the newBean() method. This method is typically used when creating a new record for a module or to call properties of the module's bean object.
$bean = BeanFactory::newBean($module);
newBeanByName()
Used to fetch a bean by its beanList name.
$bean = BeanFactory::newBeanByName($name);
Retrieving a SugarBean Object
getBean()


Retrieving a SugarBean Object
getBean()
The getBean() method can be used to retrieve a specific record from the database. If a record id is not passed, a new bean object will be created.
$bean = BeanFactory::getBean($module, $record_id);
Note: Disabling row-level security when accessing a bean should be set to trueÂ only when it is absolutely necessary to bypass security, for example when updating a Lead record from a custom Entry Point.Â An example of accessing the bean while bypassing row security is:
$bean = BeanFactory::getBean($module, $record_id, array('disable_row_level_security' => true));
retrieveBean()


retrieveBean()
The retrieveBean() method can also be used to retrieve a specific record from the database. The difference between this method and getBean() is that null will be returned instead of an empty bean object if the retrieve fails.
$bean = BeanFactory::retrieveBean($module, $record_id);
Note: Disabling row-level security when accessing a bean should be set to true only when it is absolutely necessary to bypass security, for example, when updating a Lead record from a custom Entry Point.Â An example of accessing the bean while bypassing row security is:
$bean = BeanFactory::retrieveBean($module, $record_id, array('disable_row_level_security' => true));
Retrieving Module Keys
getObjectName()


Retrieving Module Keys
getObjectName()
The getObjectName() method will return the object name / dictionary key for a given module. This is normally the same as the bean name, but may not be for some modules such as Cases which has a key of 'aCase' and a name of 'Case'.
$moduleKey = BeanFactory::getObjectName($moduleName);
getBeanName()
The getBeanName() method will retrieve the bean class name given a module name.
$moduleClass = BeanFactory::getBeanName($module);
Â
  	Last modified: 2023-02-03 21:04:03


Fields
Overview
How fields interact with the various aspects of Sugar.Â
SugarField Widgets
The SugarField widgets, located in ./include/SugarFields/Fields/Â ,Â defineÂ the dataÂ formatting and search query structure for the various field types. They also define the rendering of fields for modules running in backward compatibility mode. When creating or overridingÂ field widgets, developers should place their customization in ./custom/include/SugarFields/Fields/. For information on how Sidecar renders fields, please refer to the fields section in our user interfaceÂ documentation.Â Creating Custom Fields
Implementation


Implementation
All fields for a module are defined withinÂ vardefs. Within this definition, theÂ type attribute will determine all of the logic applied to the field. For example, the Contacts moduleÂ has aÂ  'Do Not Call' field. In the vardefs, thisÂ field is defined as follows:
'do_not_call' => array (
    'name' => 'do_not_call', // the name of the field
    'vname' => 'LBL_DO_NOT_CALL', // the label for the field name
    'type' => 'bool', // the fields type
    'default' => '0', // the fields default value
    'audited'=>true, // whether the field is audited
    'duplicate_on_record_copy' => 'always', // whether to duplicate the fields value when being copied


'comment' => 'An indicator of whether contact can be called' // admin context of the field
),


),
Â The boolÂ type field is rendered in the UI from the ./clients/base/fields/bool/bool.js field controller which rendersÂ the appropriate handlebars template as defined by the users current view for sidecar enabled modules. When the user saves data, the controller formats the data for the API and passes it to an endpoint. Once the data is received by theÂ server, The SugarFieldÂ definition calls any additional logic in the apiSave function to format the data for saving to the database.Â The same concept is applied in the apiFormatField function when retrieving data from the database to be passed back to the user interface throughÂ the API. For modules running in backward compatibility mode, the bool field is rendered using the Smarty .tpl) templates located in ./include/SugarFields/Fields/Bool/.


While the vardefsÂ define the default type for a field, this value can be overridden in the metadata of the view rendering the field. The example being that in ./custom/modules/Contacts/clients/base/views/record/record.php, you can modify theÂ do_not_call field array to point to a custom field type you have created. For more information on creating custom field types, please refer to Creating Custom Fields documentation.Â Â
Â
  	Last modified: 2023-02-03 21:04:03


Vardefs
Overview
Vardefs (Variable Definitions) provide the Sugar application with information about SugarBeans. Vardefs specify information on the individual fields, relationships between beans, and the indexes for a given bean.Â
Each module that contains a SugarBean file has a vardefs.php file located in it, which specifies the fields for that SugarBean. For example, the vardefs for the Contact bean are located in ./modules/Contacts/vardefs.php.
Dictionary Array
Vardef files create an array called $dictionary, which contains several entries including tables, fields, indices, and relationships.
table : The name of the database table (usually, the name of the module) for this bean that contains the fields
audited : Specifies whether the module has field-level auditing enabled


audited : Specifies whether the module has field-level auditing enabled
duplicate_checkÂ : Determines if duplicate checking is enabled on the module, and what duplicate check strategy will be used if enabled.Â
fields : A list of fields and their attributes
indices : A list of indexes that should be created in the database
optimistic_locking : Determines whether the module has optimistic locking enabled
Optimistic locking prevents loss of data by using the bean's modified date to ensure that it is not being modified simultaneously by more than one person or process.
unified_search : Determines whether the module canÂ be searched via Global SearchÂ This setting defaults to false and has no effect if all of the fields in the fields array have the 'unified_search' field attribute set to false.


unified_search_default_enabled : Determines whether the module should be searched by default for new users via Global Search
This setting defaults to true but has no effect ifÂ unified_search is set to false.
visibilityÂ : A list of visibility classes enabled on the module
Duplicate Check Array
The duplicate_checkÂ array contains two properties, that control if duplicate checking is enabled on the module, and which duplicate check strategy will be used to check for duplicates.
The two properties for the array are as follows:
Name
Type
Description
enabled
Boolean
Specifies whether or not the Bean is utilizing the duplicate check framework
<class_name>
Array


<class_name>
Array
<class_name> is the name of the duplicate check strategy class that is handling the duplicate checking. It is set to an array of Metadata, specific to the strategy defined inÂ the key. Review the Duplicate Check Framework for further information.
Fields Array
The fields array contains one array for each field in the SugarBean. At the top level of this array, the key is the name of the field, and the value is an array of attributes about that field.
The list of possible attributes are as follows:
name : The name of the field
vname : The language pack ID for the label of this field
type : The type of the attribute
assigned_user_name : A linked user name
bool : A boolean value
char : A character array
date : A date value with no time
datetime : A date and time
email : An email address field


datetime : A date and time
email : An email address field
enum : An enumeration (dropdown list from the language pack)
id : A database GUID
image : A photo-type field
link : A link through an explicit relationship. This attribute should only be used when working with related fields. It does not make the field render as a link.
name : A non-database field type that concatenates other field values
phone : A phone number field to utilize with callto:// links
relate : Related bean
team_list : A team-based ID
text : A text area field
url : A hyperlinked field on the detail view
varchar : A variable-sized string
table : The database table the field comes from.
The table attribute is only neededÂ to join fields from another table outside of the module in focus.
isnull : Whether the field can contain a null value


isnull : Whether the field can contain a null value
len : The length of the field (number of characters if a string)
options : The name of the enumeration (dropdown list) in the language pack for the field
dbtype : The database type of the field (if different than the type)
reportable : Determines whether the field will be available in the Reports and Workflow modules
default :Â The default value for this field.Â Default values for the record are populated by default on create for the record view (for Sidecar modules) and edit view (for Legacy modules) layout but can be modified by users. The Default Value option is available for all data type fields except HTML, Image, Flex Relate, and Relate.
massupdate : Determines whether the fieldÂ will show up in the mass-update panel on its module's list view


Some field types are restricted from mass update regardless of this setting.
rname : For relate-type fields, the field from the related variable that containsÂ the text
id_name : For relate-type fields, the field from the bean that stores the ID for the related bean
source : Set to 'non-db' if the field value does not come from the database
The source attribute can be used for calculated values or values retrieved in some other way.
sort_on : The field to sort by if multiple fields are used in the presentation of field's information
fields : For concatenated values, an array containing the fields that are concatenated
db_concat_fields : For concatenated values, an array containing the fields to concatenate in the database
unified_search : Determines whether the fieldÂ canÂ be searched via Global Search


This has no effect if the dictionary array setting 'unified_search' is not set to true.
enable_range_search : For date, datetime, and numeric fields, determines if this field should be searchable by range in module searches
dependency : Allows a field to have a predefined formula to control the field's visibility
studio : Controls the visibility of the field in the Studio editor
If set to false, then the field will not appear in any studio screens for the module. Otherwise, you may specify an Array of view keys from which the field's visibility should be removed (e.g.Â array('listview'=>false) will hide the field in the listview layout screen).
The following example illustrates a standard ID field for a bean:
'id' => array (
    'name' => 'id',
    'vname' => 'LBL_ID',


'vname' => 'LBL_ID',
    'type' => 'id',
    'required' => true,
),
Indices Array
This array contains a list of arrays that are used to create indices in the database. The fields in this array are:
name : The unique name of the index
type : The type of index (primary, unique, or index)
fields : An ordered array of the fields to index
source : Set to 'non-db' if you are creating an index for added application functionality such as duplication checking on imports
The following exampleÂ creates a primary index called 'userspk' on the 'id' column:
array(
    'name' => 'userspk',
    'type' => 'primary',
    'fields'=> array('id')
),
Relationships Array


'fields'=> array('id')
),
Relationships Array
The relationships array specifies relationships between beans. Like the indices array entries, it is a list of names with array values.
lhs_module : The module on the left-hand side of the relationship
lhs_table : The table on the left-hand side of the relationship
lhs_key : The primary key column of the left-hand side of the relationship
rhs_module : The module on the right-hand side of the relationship
rhs_table : The table on the right-hand side of the relationship
rhs_key : The primary key column of the right-hand side of the relationship
relationship_type : The type of relationship (e.g. one-to-many,Â many-to-many)
relationship_role_column : The type of relationship role


relationship_role_column : The type of relationship role
relationship_role_column_value : Defines the unique identifier for the relationship role
The following example creates a relationship between a contact and the contact that they report to. The reports_to_id field maps to the id of the record that belongs to the higher-ranked contact. This is a one-to-many relationship in that a contact may only report to one person, but many people may report to the same contact.
'contact_direct_reports' => array(
    'lhs_module' => 'Contacts',
    'lhs_table' => 'contacts',
    'lhs_key' => 'id',
    'rhs_module' => 'Contacts',
    'rhs_table' => 'contacts',
    'rhs_key' => 'reports_to_id',


'rhs_key' => 'reports_to_id',
    'relationship_type' => 'one-to-many'
),
Visibility Array
The visibilityÂ array specifies the row level visibility classes that are enabled on the bean. Each entry in the array, is a key-value pair, where the key is the name of the Visibility class and the value is set to boolean True. More information on configuring custom Visibility strategies can be found in the Architecture section under Visibility Framework.
Extending Vardefs
More information about extending and overriding vardefs can be found in the Extensions Framework documentation under Vardefs.


TopicsManually Creating Custom FieldsThe most common way to create custom fields in Sugar is via Studio inside the application. This page describes how to use the ModuleInstaller class or vardef extensions as alternative methods of creating custom fields.Specifying Custom Indexes for Import Duplicate CheckingWhen importing records to Sugar via the Import Wizard, users can select which of the mapped fields they would like to use to perform a duplicate check and thereby avoid creating duplicate records. This article explains how to enable an additional field or set of fields for selection in this step.Working With IndexesSugar provides a simple method for creating custom indexes through the vardef framework. Indexes can be built on one or more fields within a module. Indexes can be saved down to the database level or made available only in the application for functions such as Import Duplicate Checking.
  	Last modified: 2023-02-03 21:04:03


Working With Indexes
Overview
Sugar provides a simple method for creating custom indexes through the vardef framework. Indexes can be built on one or more fields within a module. Indexes can be saved down to the database level or made available only in the application for functions such as Import Duplicate Checking.
Index Metadata
Indexes have the followingÂ metadata options that can be configured per index:
Key
Value
DescriptionÂ
name
string
A Unique identifier to define the index. Best practices recommend indexes start with idx and contain the suffix cstm to avoid conflicting with a stock index.Â
Note : Some databases have restrictions on the length of index names. Please check with your database vendor to avoid any issues.
type
stringÂ
Â All indexes should use the type of "index"
fields
array
A PHP array of the fields for the index to utilizeÂ
source
string


array
A PHP array of the fields for the index to utilizeÂ
source
string
Specify as "non-db" to avoid creating the index in the database
Creating Indexes
Stock indexes are initially defined in the module's vardefs file under the indices array. For reference, you can find them using the vardef pathÂ of your module. The path will be Â ./modules/<module>/vardefs.php.
Custom indexesÂ should Â be created using the Extension Framework.Â First, create a PHPÂ file in the extension directory of yourÂ desired module. The path should similar toÂ ./custom/Extension/modules/<module>/Ext/Vardefs/<name>.php.
In the new file, add the appropriate $dictionary reference to define the custom index:
<?php


<?php
$dictionary['<module>']['indices'][] = array(
	'name' => '<index name>',
	'type' => 'index',
	'fields' => array(
		'field1',
		'field2',
	)
);
Note : For performance reasons, it is not recommended to create an index on a single field unless the source is set to non-db.
Once installed,you will need to navigateÂ to Admin > Repair > Quick Repair and Rebuild to enable the custom index. You will need to execute any scripts generated by the rebuild process.
Removing Indexes
Stock indexes are initially defined in the module's vardefs file under the indices array. For reference, you can find them using the vardef path of your module. The path will be ./modules/<module>/vardefs.php.


Stock indexes should be removed using the Extension Framework. First, create a PHP file in the extension directory of your desired module. The path should similar to ./custom/Extension/modules/<module>/Ext/Vardefs/<name>.php.
In the new file, loop through the existing 'indices' sub-array of the $dictionary to locate the stock index to remove, and use unset() to remove it from the array.
Example
The following is an example to remove the idx_calls_date_start index from the Call module's vardefs.
First, create ./custom/Extension/modules/Calls/Ext/Vardefs/remove_idx_calls_date_start.phpÂ from the root directory of your Sugar installation on the web server. When creating the file, keep in mind the following requirements:


The name of the file is not important, as long as it ends with a .php extension.
The rest of the directory path is case sensitive so be sure to create the directories as shown.
If you are removing the index for a module other than Calls, then substitute the corresponding directory name with that module.
Ensure that the entire directory path and file have the correct ownership and sufficient permissions for the web server to access the file.
The contents of the file should look similar to the following code:
<?php
$call_indexes = $dictionary['Call']['indices'];
$remove_index = "idx_calls_date_start";
foreach($call_indexes as $index_key => $index_item) {
    if( $index_item['name'] == $remove_index ) {


if( $index_item['name'] == $remove_index ) {
        unset($dictionary['Call']['indices'][$index_key]);
    }
}
Note : Removing the reference to the index from the module's indices array does not actually remove the index from the module's database table. Removing the reference from the indices array ensures that the index is not added back to the module's database table when performing any future Quick Repair and Rebuilds. The database index must be removed directly at the database level. On MySQL, with the current example, this could be done with a query like:
ALTER TABLE calls DROP INDEX idx_calls_date_start;
Once installed,you will need to navigate to Admin > Repair > Quick Repair and Rebuild to remove the index from the $dictionary array. You will need to execute any scripts generated by the rebuilding process.


Creating Indexes for Import Duplicate Checking
When importing records to Sugar via the Import Wizard, users can select which of the mapped fields theyÂ would like to use to perform a duplicate checkÂ and thereby avoid creating duplicate records. The following instructionsÂ explain how to enable an additional field or set of fields for selection in this step.
Example
The following is an example to add the home phone field to the Contact module's duplicate check.
First, create ./custom/Extension/modules/Contacts/Ext/Vardefs/custom_import_index.phpÂ from the root directory of your Sugar installation on theÂ web server. When creating the file, keep in mind the following requirements:
The name of the file is not important, as long as it ends with a .php extension.
The rest of the directory path is case sensitive so be sure to create the directories as shown.


The rest of the directory path is case sensitive so be sure to create the directories as shown.
If you are creating the import index for a module other than Contacts, then substitute the corresponding directory name with that module.
Ensure that the entire directory path and file have the correct ownership and sufficient permissions for the web server to access the file.
The contents of the file should look similar to the following code:
<?php
$dictionary['Contact']['indices'][] = array(
     'name' => 'idx_home_phone_cstm',
     'type' => 'index',
     'fields' => array(
         0 => 'phone_home',
     ),
     'source' => 'non-db',
);


),
     'source' => 'non-db',
);
PleaseÂ note that the module name in line 2 of the code is singular (i.e. Contact, not Contacts). If you are unsure of what to enter for the module name, you can verify the name by opening the ./cache/modules/<module_name>/<module_name>vardefs.php file. The second line of that file will have text like the following:
$GLOBALS["dictionary"]["Contact"] = array (...);
The parameter following "dictionary" is the same parameter you should use in the file defining theÂ custom index. To verify duplicates against a combination of fields (i.e. duplicates will only be flagged if the values of multiple fields match those of an existing record), then simplyÂ add the desired fields to the 'fields' array in the code example.


Finally, navigateÂ to Admin > Repair > Quick Repair and Rebuild to enable the custom index for duplicate verification when importing records in theÂ module.Â
  	Last modified: 2023-02-03 21:04:03


Manually Creating Custom Fields
Overview
The most common way to create custom fields in Sugar is via Studio inside the application. This page describes how to use the ModuleInstaller classÂ or vardef extensions as alternative methods ofÂ creating custom fields.
Note: Sugar Sell Essentials customers do not have the ability to upload custom file packages to Sugar using Module Loader.
Using ModuleInstaller to Create Custom Fields
There are two ways to create a field using the ModuleInstaller class: via installer package orÂ programmatically. An example of creating a field from a module-loadable package is explained in the Module Loader documentation,,Â Creating an Installable Package that Creates New Fields. The following example shows how toÂ programmatically add custom fields using the ModuleInstaller class with the install_custom_fields() method:
<?php
    $fields = array (
        //Text
        array(


<?php
    $fields = array (
        //Text
        array(
            'name' => 'text_field_example',
            'label' => 'LBL_TEXT_FIELD_EXAMPLE',
            'type' => 'varchar',
            'module' => 'Accounts',
            'help' => 'Text Field Help Text',
            'comment' => 'Text Field Comment Text',
            'default_value' => '',
            'max_size' => 255,
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'importable' => 'true', // 'true', 'false', 'required'
            'duplicate_merge' => false, // true or false
        ),
        //DropDown


),
        //DropDown
        array(
            'name' => 'dropdown_field_example',
            'label' => 'LBL_DROPDOWN_FIELD_EXAMPLE',
            'type' => 'enum',
            'module' => 'Accounts',
            'help' => 'Enum Field Help Text',
            'comment' => 'Enum Field Comment Text',
            'ext1' => 'account_type_dom', //maps to options - specify list name
            'default_value' => 'Analyst', //key of entry in specified list
            'mass_update' => false, // true or false
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false


'audited' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
            'duplicate_merge' => false, // true or false
        ),
        //MultiSelect
        array(
            'name' => 'multiselect_field_example',
            'label' => 'LBL_MULTISELECT_FIELD_EXAMPLE',
            'type' => 'multienum',
            'module' => 'Accounts',
            'help' => 'Multi-Enum Field Help Text',
            'comment' => 'Multi-Enum Field Comment Text',
            'ext1' => 'account_type_dom', //maps to options - specify list name
            'default_value' => 'Analyst', //key of entry in specified list


'default_value' => 'Analyst', //key of entry in specified list
            'mass_update' => false, // true or false
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
            'duplicate_merge' => false, // true or false
        ),
        //Checkbox
        array(
            'name' => 'checkbox_field_example',
            'label' => 'LBL_CHECKBOX_FIELD_EXAMPLE',
            'type' => 'bool',
            'module' => 'Accounts',
            'default_value' => true, // true or false
            'help' => 'Bool Field Help Text',


'help' => 'Bool Field Help Text',
            'comment' => 'Bool Field Comment',
            'audited' => false, // true or false
            'mass_update' => false, // true or false
            'duplicate_merge' => false, // true or false
            'reportable' => true, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
        ),
        //Date
        array(
            'name' => 'date_field_example',
            'label' => 'LBL_DATE_FIELD_EXAMPLE',
            'type' => 'date',
            'module' => 'Accounts',
            'default_value' => '',
            'help' => 'Date Field Help Text',
            'comment' => 'Date Field Comment',


'comment' => 'Date Field Comment',
            'mass_update' => false, // true or false
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'duplicate_merge' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
        ),
        //DateTime
        array(
            'name' => 'datetime_field_example',
            'label' => 'LBL_DATETIME_FIELD_EXAMPLE',
            'type' => 'datetime',
            'module' => 'Accounts',
            'default_value' => '',
            'help' => 'DateTime Field Help Text',


'help' => 'DateTime Field Help Text',
            'comment' => 'DateTime Field Comment',
            'mass_update' => false, // true or false
            'enable_range_search' => false, // true or false
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'duplicate_merge' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
        ),
        //Encrypt
        array(
            'name' => 'encrypt_field_example',
            'label' => 'LBL_ENCRYPT_FIELD_EXAMPLE',
            'type' => 'encrypt',
            'module' => 'Accounts',


'type' => 'encrypt',
            'module' => 'Accounts',
            'default_value' => '',
            'help' => 'Encrypt Field Help Text',
            'comment' => 'Encrypt Field Comment',
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'duplicate_merge' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
        ),
    );
    require_once('ModuleInstall/ModuleInstaller.php');
    $moduleInstaller = new ModuleInstaller();
    $moduleInstaller->install_custom_fields($fields);
AddÂ labels for custom fields by creating a corresponding language extension file:


AddÂ labels for custom fields by creating a corresponding language extension file:
./custom/Extension/modules/Accounts/Ext/Language/en_us.<name>.php
<?php
    $mod_strings['LBL_TEXT_FIELD_EXAMPLE'] = 'Text Field Example';
    $mod_strings['LBL_DROPDOWN_FIELD_EXAMPLE'] = 'DropDown Field Example';
    $mod_strings['LBL_CHECKBOX_FIELD_EXAMPLE'] = 'Checkbox Field Example';
    $mod_strings['LBL_MULTISELECT_FIELD_EXAMPLE'] = 'Multi-Select Field Example';
    $mod_strings['LBL_DATE_FIELD_EXAMPLE'] = 'Date Field Example';


$mod_strings['LBL_DATETIME_FIELD_EXAMPLE'] = 'DateTime Field Example';
    $mod_strings['LBL_ENCRYPT_FIELD_EXAMPLE'] = 'Encrypt Field Example';
Finally, navigate to Admin > Repair > Quick Repair and Rebuild to make the new field available for users.
Using the Vardef Extensions
You should try to avoid creating your own custom fields using the vardefs as there are several caveats:
If your installation does not already contain custom fields, you must manually create the custom table. Otherwise, the system will not recognize your field's custom vardef. This situation is outlined in the following section.
You mustÂ run a Quick Repair and Rebuild and then execute the generated SQL after the vardef is installed.


You must correctly define the properties of a vardef. If you miss any, the field may not work properly.
Your field name must end with "_c" and have the property 'source' set to 'custom_fields'. This is required as you should not modify core tables in Sugar and it is not permitted onÂ Sugar's cloud service.
Your vardef must specify the exact indexes of the properties you want to set. For example, use: Â $dictionary['<module singular>']['fields']['example_c']['name'] = 'myfield_c';Â instead of $dictionary['<module singular>']['fields']['example_c'] = array(['name' => 'myfield_c');. This will help prevent the system from losing any properties when loading from the extension framework.


The initial challenge when creating your own custom vardef is getting the system to recognize the vardef and generate the database field. ThisÂ issueÂ is illustrated with the example below:
./custom/Extension/modules/<module>/Ext/Vardefs/<file>.php
<?php
$dictionary['<module singular>']['fields']['example_c']['name'] = 'example_c';
$dictionary['<module singular>']['fields']['example_c']['vname'] = 'LBL_EXAMPLE_C';
$dictionary['<module singular>']['fields']['example_c']['type'] = 'varchar';
$dictionary['<module singular>']['fields']['example_c']['enforced'] = '';