Call script when do quick repair and rebuild

@blqt

Thank you, always nice to have options.

I am trying to maintain the MVC architecture as much as I can. If I put my custom code into the view definition, I would be mixing the V and C parts of MVC.

The advantage of your suggestion is that It does not add another menu so the user actually does not need to take any additional steps; they just do what they have always done and behind-the-scenes the system does more than it used to.

So I need to noodle this and get some user feedback on this compared to the new menu option.

Appreciate the suggestion.

@p.konetskiy

I see that you are adding the edits directly to the .ext. file.

I thought we were supposed to add replacements to

custom/Extension/modules/Administration/Ext/Administration.php

or edits to

custom/Extension/modules/Administration/Ext/customAdministration.php

by extending the class.

After a QR&R, this creates the .ext. file.

Can you help me understand why you have recommended your approach? Or would the normal approach just not work in this case?

@Ramblin

Some time ago I tested the solution and created the following array. It worked.

<?php
$admin_option_defs=array();
$admin_option_defs['Administration']['BFM_admin_menu_configs'] = array(
    'BFM_admin_menu_configs',
    'LBL_BFM_HUBOT_CONFIG_TITLE',
    'LBL_BFM_HUBOT_CONFIG_DESCRIPTION',
    './index.php?module=BFM_Configs&action=EditView',
    'bfm-admin-menu bfm-configs'
);
$admin_option_defs['Administration']['BFM_admin_menu_outputs'] = array(
    'BFM_admin_menu_outputs',
    'LBL_BFM_HUBOT_OUTPUT_TITLE',
    'LBL_BFM_HUBOT_OUTPUT_DESCRIPTION',
    './index.php?module=BFM_Outputs&action=index',
    'bfm-admin-menu bfm-outputs'
);
$admin_group_header['BFM_admin_menu'] = array(
    'LBL_BFM_HUBOT_TITLE',
    '',
    false,
    $admin_option_defs,
    'LBL_BFM_HUBOT_DESCRIPTION'
);
echo getVersionedScript('custom/modules/Administration/js/bfm_admin_menu.js');

And installed the file using standard manifest.php:

...
   'administration' => array(
        0 => array(
           'from' => '<basepath>/SugarModules/Administration/BFM_admin_menu.php',
        ),
    ),
...

@Ramblin
I wrote already.

And you will be able to understand why the file name is ‘custom/modules/Administration/Ext/Administration/administration.ext.php’

@p.konetskiy

I had mmissed the include statement near the end of the file

if (file_exists('custom/modules/Administration/Ext/Administration/administration.ext.php')) {
    include('custom/modules/Administration/Ext/Administration/administration.ext.php');
}

Sorry about that.

I also edited my orginal post after getting a better understanding of what you had shown previously


******** DO NOT spend time reading this post ********
New discoveries changed the recomended solution
See later post marked Solution for the final word on this


I apologize for the bad formatting below (I have no idea why things turned red and I could not attach a PDF) but I was not going to re-type all the explanation in or re-format for this post (yes, I am that lazy). However the information is useful for anyone looking to try and create custom fields in SuiteCRM from code. My suggestion for anyone trying to read the solution is for them to copy the contents of the solution into a text processing document (Notepad++, Libre Office Writer, Microsoft Word (ugh) or whatever you prefer and see if it can be more easily read there.)

A BIG thank you to @p.konetskiy for sticking with me on this one.

1.1	Create new custom fields in the _cstm table of a module

SuiteCRM has two database tables for each module: the core table (eg contacts) and another table with a _cstm suffix (eg contacts_cstm).  
Fields that are part of the original SuiteCRM code are in the core database table.  Fields added to the module with Studio have _c appended as a suffix to the field name and go into the _cstm database table.  The _cstm database table for a module does not exist until a field is created that would be stored in the _cstm table.
Just to complicate things:
* 1. Fields added in Module Builder go into the core database table for the module, but since Module Builder is used to create a custom module, this is not an issue.
* 2. However, fields added via direct entry in the vardefs file (eg custom/Extension/modules/{modulename}/Ext/Vardefs/{filename}.php) go into the core database table, unless particular steps are taken which direct the system to add the new fields to the _cstm table.  These steps are convoluted to say the least.

Best practice for adding fields to CORE modules in SuiteCRM is to create new fields in the _cstm database table for each core module, not the core database table.  
* Installations on cloud SuiteCRM systems likely will not allow you to alter core database tables
* For CORE modules, if you add new fields to the core database table, if SuiteCRM updates a core module and it creates its own new fields in the core database table, you risk your new field having conflict with a future SuiteCRM update.  Additionally, there are times when an update to a core module does not just add to a database table but replaces it with the new structure, copying over the old data - but only for fields that were in the original core database table definition.
* Having said this, another developer could add another field to the _cstm module database table that has the same name as your new field and you would be back into the confict issue.  This is a risk you can control though (choosing which add-on modules to install).  However, it is recommended to ALWAYS have a prefix (I use ayu_ , the first 3 letters of my company Ayuda) for any new field you create in any module.  This minimizes the odds of a conflict in either the core or _cstm database tables.  Unfortunately, I learned this lesson after creating this system so you will not see an ayu_ prefix in my custom fields for this system.
* When you are creating your own CUSTOM module, you may be better off adding new fields to the core database table for the new module and that is fine since you would use some kind of installer (manifest file, …) to add fields for that new module so all would work fine, even on cloud SuiteCRM systems.  In fact, there is no JOIN required in the sql lookups if all fields are in the core dataqbase table, so if your custom module has a number of fields, it is preferable, from a performance perspective, to keep all the fields in the core database table.

You cannot add fields to the _cstm database table by just defining them in vardefs like you do for fields going into the core database table.  Even with the special array entries (like 'source' => 'custom_field',) included in the vardefs, you would still need to:
* Manually, via sql commands add entries into the fields_meta_data table
* Make sure a _cstm table exists for the module into which you want to add your custom field(s)
* Define the vardefs and run Quick Repair and Rebuild
You can add custom fields using the manifest file structure like what is created with Module Builder when it is Published

You can add custom fields using a SuiteCRM built-in function.  This is my choice and is explained below
Details are available at https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_11.0/Data_Framework/Vardefs/Manually_Creating_Custom_Fields 

Two steps involved here:
1) Create code that will call the install_custom_fields() method in the ModuleInstaller class in the  {SuiteCRM_Directory}/ModuleInstall/ModuleInstaller.php (around line 1272)
2) Trigger the code BEFORE a Quick Repair and Rebuild is run

Re 1) The code for calling the install_custom_fields() method will look something like the following sample text field shown):
Note: the name will have _c added as a suffix by the install_custom_fields() method.  If you do add the _c in your name definition, the install_custom_fields() method will recognize that and NOT add another _c suffix.

<?php
if(!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}
$fields = array (
    //Text
    array(
        'name' => 'program_text_field',
        'label' => 'LBL_PROGRAM_TEXT_FIELD',
        'type' => 'varchar',
        'module' => 'FP_events',
        '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
    ),
);
require_once('ModuleInstall/ModuleInstaller.php');
$moduleInstaller = new ModuleInstaller();
$moduleInstaller->install_custom_fields($fields);
// Return the user to the Administration menu
SugarApplication::redirect('index.php?module=Administration&action=index');

Notes:
In additional to the field elements that are defined, there are some entries required that are put into fields_meta_data and used by the install_custom_fields() method to add the fields to the _cstm database table
module

The only field elements that are defined in a _cstm table are shown below with their type shown after.
id varchar(255)
This is the equivalent of a foreign_key setup so is not restricted to the normal ID length of 36 characters
name varchar(255)
vname varchar(255)
comments varchar(255)
help varchar(255)
custom_module varchar(255)	
type varchar(255)
len int
required bool
default_value varchar(255)
date_modified datetime
deleted bool
audited bool
massupdate bool
duplicate_merge bool
reportable bool
importable varchar(255)
ext1 varchar(255)
ext2 varchar(255)
ext3 varchar(255)
ext4

If you have any additional elements you want defined (eg inline_edit or Label value), you need to separately define them in the appropriate custom/.. directory as follows.  Remember, the field name has _c as the suffix in a _cstm table

For additional elements

cd {SuiteCRM_Directory}
mkdir -p custom/Extension/modules{ModuleName}/Ext/Vardefs/
nano custom/Extension/modules{ModuleName}/Ext/Vardefs/{filename}.php
$dictionary['{ModuleName}']['fields']['field_name']['{element}'] = '{element_value}';
{ModuleName} may be singular; check the other entries in vardefs
{element} is the element you wish to define (eg inline_edit)
(element_value} matches the type definition (eg bool = true/false)

For Label defintions

cd {SuiteCRM_Directory}
mkdir -p custom/Extension/modules{ModuleName}/Ext/Language/
nano custom/Extension/modules{ModuleName}/Ext/Vardefs/en_us.{filename}.php
$mod_strings['LBL_...'] = 'The text string you want';

The method for triggering that code is up to you.  Options include
* Adding a logic hook that when triggered includes code like the above
* Using SuiteCRM APIs
* Manully calling the code from a browser after enabling the code as an EntryPoint
* Add a button/menu to a SuiteCRM GUI that triggers the code when clicked

Make sure that whatever method you use, you only run the code once for each field.  If you run it again for the same field, you will be trying to add a vield to a _cstm database table when the field already exists in the table and you will get an sql error.

Simplest method: add a file with the above code, define the file as an allowed EntryPoint, and access the file from your browser.  You could put the file anywhere; for the purpose of this explanation, I will put it into custom/…

Create the code
cd (SuiteCRM_Directory}
nano custom/addCustomFields.php
//
…  add the code shown above, editing to suit your situation …
… one array element defined for each field
//

Enable the page with the code to serve as an Entry Point
mkdir -p custom/Extension/application/Ext/EntryPointRegistry/
nano custom/Extension/application/Ext/EntryPointRegistry/addCustomFieldsEntryPoint.php
<?php
$entry_point_registry['addCustomFieldsEntryPoint'] = array(
    // Pointer to the location of the file for which this EntryPoint is defined
    'file' => 'custom/addCustomFields.php',
    // auth true means peron using the EntryPoint must be an authenticated User
    // auth false means anyone could the EntryPoint - NOT a good idea usually
    'auth' => true,
);

Call the code using your browser
http://{SuiteCRM _Site}/index.php?EntryPoint=addCustomFieldsEntryPoint

This is all that is needed and it will work.  The admin has to remember to populate the php code with each field to be added, manually run the code (ONCE for each field to be added) by calling the EntryPoint from their browser, and then running Quick Repar and Rebuild and then removing the field definition from the code once the field has been created.

However, if you are going to use this on an ongoing development basis, I would do the following:

Create the file and the EntryPoint as shown above
Add a submenu to the Repair option in Admin -> Admin Tools.  We add a new submenu "Create Custom Fields"

mkdir -p custom/modules/Administration
cp -a modules/Administration/Upgrade.php custom/modules/Administration/Upgrade.php 
nano custom/modules/Administration/Upgrade.php 
Change
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD']); ?>&nbsp;<a href="./index.php?module=Administration&action=repair"><?php echo $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD_DESC'] ; ?> </td>
</tr>
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_EXPAND_DATABASE_COLUMNS']); ?>&nbsp;<a href="./index.php?module=Administration&action=expandDatabase"><?php echo $mod_strings['LBL_EXPAND_DATABASE_COLUMNS']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_EXPAND_DATABASE_COLUMNS_DESC'] ; ?> </td>
</tr>
To (add a new item between them)
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD']); ?>&nbsp;<a href="./index.php?module=Administration&action=repair"><?php echo $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD_DESC'] ; ?> </td>
</tr>
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_CREATE_CUSTOM_FIELDS']); ?>&nbsp;<a href="./index.php?entryPoint=addCustomFieldsEntryPoint"><?php echo $mod_strings['LBL_CREATE_CUSTOM_FIELDS']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_CREATE_CUSTOM_FIELDS_DESC'] ; ?> </td>
</tr>
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_EXPAND_DATABASE_COLUMNS']); ?>&nbsp;<a href="./index.php?module=Administration&action=expandDatabase"><?php echo $mod_strings['LBL_EXPAND_DATABASE_COLUMNS']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_EXPAND_DATABASE_COLUMNS_DESC'] ; ?> </td>
</tr>

Add the language definitions
mkdir -p custom/Extension/modules/Administration/Ext/Language/
nano custom/Extension/modules/Administration/Ext/Language/en_us.repairSubMenu.php
<?php
$mod_strings['LBL_CREATE_CUSTOM_FIELDS'] = 'Create Custom Fields';
$mod_strings['LBL_CREATE_CUSTOM_FIELDS_DESC'] = 'Add fields specified in ./custom/AYU_CustomFields/... to _cstm database table(s)';

This provides a menu to trigger the code so users do not have to remember the URL to trigger the EntryPoint directly.  The user still needs to remove the field definition after each use to prevent attempts to create the same field again.

If I was going to use it a lot, I would create a module called AYU_Customfields with ModuleBuilder to store the code and be the target URL for the user access and add code in custom/AYU_Customfields to read files, programmatically add the new field definitions to the code, run the program, run Quick Repair and Rebuild and set a flag to show that the fields have been added.  

Create the AYU_Customfields module in Module Builder and in that new module's controller, add an action called add_custom_fields

Create a new Module AYU_Customfields using ModuleBuilder and Deploy
Edit/Create a custom controller in that module and add a add_custom_fields action
nano modules/AYU_Customfields/controller.php
if (!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}
class AYU_CustomfieldsController extends SugarController
{
    public function action_add_custom_fields () {
        // insert the above code, edited to suit the situation
        // (Optionally) add code to run Quick Repair and Rebuild 
        // from within this script
        require_once('modules/Administration/QuickRepairAndRebuild.php');
        $newFields = new RepairAndClear();
        //  Populate Parameters
        $newFields->repairAndClearAll($selected_actions, $modules, $autoexecute=false, $show_output=true);
    }
}

Change the URL in the new menu
From
./index.php?entryPoint=addCustomFieldsEntryPoint
To
./index.php?module=AYU_Customfields&action=add_custom_fields

And in the new module, add a list view to show what field(s) have been added and what field(s) remain to be added, and direct the return after running the code back to the list view (the normal default return).

Add code to store field definitions and flag ones which have already been created
One way to handle this would be to add one file per field to custom/Extension/application/Ext/Extensions/{filename1}.php
A Quick Repair and Rebuild consolidates all these files into one file at custom/application/Ext/Extensions/extensions.ext.php
You could run your check code on either location and set flags to prevent an attempt to add a custom field more than once.

Using a custom field prefix is a good idea.

I don’t know what document this is, but this statement is incorrect.

If you use the 'source' => 'custom_field' option in the field array, the column is created in the table with the DB suffix _cstm I tested it and it worked. SuiteCRM get data from DB table fields_meta_data if you created field using Studio.
SuiteCRM collent fields from 3 places:

  • module/<module_name>/vardefs.php
  • custom/modules/<module_name>/Ext/Vardefs/vardefs.ext.php
  • DB table fields_meta_data (after taking files with _override_sugarfield_ prefix from custom/Extension/modules/<module_name>/Ext/Vardefs/)
    After that SuiteCRM create files like this cache/modules/<module_name>/<object_name>vardefs.php and is working with that fields only.

I didn’t see the new version change the whole data structure in the db tables. This can be a big problem for custom code. I have sometimes seen developers make a direct database call and not use the SuiteCRM object model.

Thanks you to @Ramblin for the discussion.

@p.konetskiy

You won’t find the document anywhere; I created it so I could remember what I learned during this exercise.

I tried adding custom fields using the vardefs without first entering the field in fields_meta-data and could not get it to work. I did include ‘source’ => ‘custom_field’ in the vardef and it did not make a difference - until I manually added the record in fields_meta_data with id = {Modulename}{fieldname}_c and then the vardef was recognized

I did already have a _cstm table for the module

I did already have another field for the module listed in fields_meta_data

But when I put into vardefs the following - WITHOUT first entering anything in fields_meta_data for this field - and I tried with enerything in one array() definition and again with each array element individually defined with a $dictionary entries, there was no entry added to fields_meta_data after a Quick Repair and Rebuild and I could not see the field in Studio so I could not use it in layouts, …

$dictionary['FP_events']['fields']['wierdname_c']['name'] = 'wierdname_c';
$dictionary['FP_events']['fields']['wierdname_c']['inline_edit'] = '';
$dictionary['FP_events']['fields']['wierdname_c']['labelValue'] = 'A very wierdname';
$dictionary['FP_events']['fields']['wierdname_c']['required'] = false;
$dictionary['FP_events']['fields']['wierdname_c']['source'] = 'custom_fields';
$dictionary['FP_events']['fields']['wierdname_c']['vname'] = 'LBL_WIERDNAME';
$dictionary['FP_events']['fields']['wierdname_c']['type'] = 'varchar';
$dictionary['FP_events']['fields']['wierdname_c']['massupdate'] = '0';
$dictionary['FP_events']['fields']['wierdname_c']['default'] = '';
$dictionary['FP_events']['fields']['wierdname_c']['no_default'] = false;
$dictionary['FP_events']['fields']['wierdname_c']['comments'] = '';
$dictionary['FP_events']['fields']['wierdname_c']['help'] = '';
$dictionary['FP_events']['fields']['wierdname_c']['importable'] = 'true';
$dictionary['FP_events']['fields']['wierdname_c']['duplicate_merge'] = 'disabled';
$dictionary['FP_events']['fields']['wierdname_c']['duplicate_merge_dom_value'] = '0';
$dictionary['FP_events']['fields']['wierdname_c']['audited'] = false;
$dictionary['FP_events']['fields']['wierdname_c']['reportable'] = true;
$dictionary['FP_events']['fields']['wierdname_c']['unified_search'] = false;
$dictionary['FP_events']['fields']['wierdname_c']['merge_filter'] = 'disabled';
$dictionary['FP_events']['fields']['wierdname_c']['len'] = '255';
$dictionary['FP_events']['fields']['wierdname_c']['size'] = '20';
$dictionary['FP_events']['fields']['wierdname_c']['id'] = 'FP_eventswierdname_c';
$dictionary['FP_events']['fields']['wierdname_c']['custom_module'] = '';
$dictionary['FP_events']['fields']['wierdname_c']['studio'] = 'visible';

Can you see what I am missing to get it to work with vardefs?

@Ramblin

  1. What name of file in directory: custom/Extension/modules/<module_name>/Ext/Vardefs/ ? The file shoudn’t be prefix _override_sugarfield_
  2. Try to run ‘Quick Repair and Rebuild’ twice in a row.
  3. Using array. It is more comfortable.

@p.konetskiy

*** Edited after original post to correct typos and add relevant information ***

The vardef is in custom/Extension/modules/FP_events/Ext/Vardefs/wierdname.php

After a Quick Repair and Rebuild, the entries do appear in custom/modules/FP_events/Ext/Vardefs/vardefs.ext.php

I tried it with the file named:
wierdname.php
wierdname_c.php
_override_sugarfield_wierdname.php

I have tried it with $dictionary as an array and with each element individually defined in a $dictionary line. The last one I tried was with each element individually defined in a $dictionary line so that is what I showed you. I prefer the array as well, but the instructions linked previously said to use the individual $dictionary definitions so I tried that.

I just tried using Quick Repair and Rebuild twice in a row

There was no entry in the fp_events_cstm database table or in fields_meta_data

I cannot see the field in Studio

I am on SuiteCRM v7.12.8 on Debian 10. A relatively fresh install just to try this out.

I have disabled the CRON job which triggers the scheduler since I am just using this installation to test some install techniques.

Anything else you can think of that may be causing the difference between what you are doing and what I am doing?

@Ramblin

It was an interesting task. :slightly_smiling_face:

There are some details before the answer.

  1. I couldn’t create a new field for the FP_events module as you did.
  2. I could easily create a new field for the Contacts module because this module has custom fields created with Studio (e.g. custom/Extension/modules/Contacts/Ext/Vardefs/_override_sugarfield_jjwg_maps_address_c. php )
  3. SuiteCRM checks the fields_meta_data table when calling the ‘Quick Repair and Rebuild’ process. The process set the special variable to true. (The Studio writes data to the table when creating a new field and creates a file in the custom/Extension/modules/<module_name>/Ext/Vardefs/ directory with the prefix _override_sugarfield_.)

Answer:
You can directly set the value of a special variable. It is safe.

$dictionary[<object_name>]['custom_fields'] = true;

You can do this in any file with custom fields in the directory:
custom/Extension/modules/<module_name>/Ext/Vardefs/

Note: <module_name> and <object_name> do not match in some modules.

@p.konetskiy

You have a way of understating … :slight_smile:

Definitely an interesting task.

I cannot match your success even after duplicating what worked for you.

Question: Does the $dictionary[’’][‘custom_fields’] = true; declaration go into custom/Extension/modules//Ext/Vardefs/.php ?

That is where I tried putting it.

I tried adding, to custom/Extension/modules/FP_events/Ext/Vardefs/setCustomFlag.php

<?php
$dictionary['FP_events']['custom_fields'] = true;

to see if doing that enabled QR&R to create the custom field from the following vardef in custom/Extension/modules/FP_events/Ext/Vardefs/maybe_c.php

<?php
$dictionary['FP_events']['fields']['maybe_c'] = array(
    'name' => 'maybe_c',
    'vname' => 'LBL_MAYBE',
    'type' => 'varchar',
    'len' => '255',
    'size' => '20',
    'source' => 'custom_fields',
    'custom_module' => 'FP_events',
    'id' => 'FP_eventsmaybe_c',
    'help' => '',
    'comments' => '',
    'default' => '',
    'no_default' => true,
    'duplicate_merge' => 'disabled',
    'duplicate_merge_dom_value' => '0',
    'required' => false,
    'inline_edit' => '',
    'massupdate' => '0',
    'audited' => false,
    'reportable' => true,
    'importable' => 'true',
    'unified_search' => false,
    'merge_filter' => 'disabled',
    'studio' => 'visible',
);

There is an entry for FP_events in the fp_events_cstm table in the database from previously adding fields to FP_events via Studio and via the method in the above post.

There are previous entries in fields_meta_table for FP_events from previously adding fields to FP_events via Studio and via the method in the above post.

After QR&R the maybe_c field definition was added to custom/modules/FP_events/Ext/Vardefs/vardefs.ext.php

But, after the QR&R, nothing was added to either of the fields_metda_data or fp_events_cstm databae tables. I did try running QR&R twice

So I then tried adding the same field definition - edited for Contacts - to the the Contacts vardefs in custom/Extension/modules/Contacts/Ext/Vardefs/maybe_c.php

<?php
$dictionary['Contact']['fields']['maybe_c'] = array(
    'name' => 'maybe_c',
    'vname' => 'LBL_MAYBE',
    'type' => 'varchar',
    'len' => '255',
    'size' => '20',
    'source' => 'custom_fields',
    'custom_module' => 'Contacts',
    'id' => 'Contactsmaybe_c',
    'help' => '',
    'comments' => '',
    'default' => '',
    'no_default' => true,
    'duplicate_merge' => 'disabled',
    'duplicate_merge_dom_value' => '0',
    'required' => false,
    'inline_edit' => '',
    'massupdate' => '0',
    'audited' => false,
    'reportable' => true,
    'importable' => 'true',
    'unified_search' => false,
    'merge_filter' => 'disabled',
    'studio' => 'visible',
);

As you said, there were already entries in contacts_cstm and fields_meta_data for Contacts.

After a QR&R, nothing was added to fields_meta_data or contacts_cstm

So I THINK I am doing the same as you but I am not getting the same results as you.

I got my vardef parameters by looking at the cache/modules/FP_events/FP_eventsvardefs.php so I think I am using the right vardefs definition, but do you see anything in my vardef that would be preventing what I am doing from working with vardefs?

Just to see if my system was totally messed up, I tried adding a custom field to each of FP_events and Contacts using the programatic method described above and it worked, with 2 new entries in fields_meta_data and a new entry in each of fp_events_cstm and contacts_cstm

Create a new file in {SuiteCRM_Directory}/addCustomFields.php with

<?php

if(!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}

$fields = array ();

$fields[] = array(
    // Text Field
    'name' => 'maybe2_c',
    'label' => 'LBL_MAYBE2',
    'type' => 'varchar',
    'module' => 'FP_events',
    '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
);

$fields[] = array(
    // Text Field
    'name' => 'maybe2_c',
    'label' => 'LBL_MAYBE2',
    'type' => 'varchar',
    'module' => 'Contacts',
    '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
);

require_once('ModuleInstall/ModuleInstaller.php');
$newInstallerBean = new ModuleInstaller();
$newInstallerBean->install_custom_fields($fields);
// Return the user to the Administration menu
SugarApplication::redirect('index.php?module=Administration&action=index');

Enable that file to be an entry point by adding the file at custom/Extension/application/Ext/EntryPointRegistry/addCustomFieldsEntryPoint.php

<?php
$entry_point_registry['addCustomFieldsEntryPoint'] = array(
    // Pointer to the location of the file for which this EntryPoint is defined
    'file' => 'addCustomFields.php',
    // auth true means person using the EntryPoint must be an authenticated User
    // auth false means anyone could the EntryPoint - NOT a good idea usually
    'auth' => true,
);

Quick Repair and Rebuild

Point my browser to the EntryPoint at

{SuiteCRM_URL}/index.php?entryPoint=addCustomFieldsEntryPoint

and yes, the maybe2_c custom fields were created in fields_meta_data, fp_events_cstm and contacts_cstm

So this is DEFINITELY getting interesting.

I cannot get to work what works on your system

And you cannot get to work what works on my system

Maybe we should take our 2 systems and send them to counselling so they can agree … :slight_smile:

Any ideas?

@Ramblin

  1. I recommend fixing the bug here first. You missed the key for the field 'maybe_c'
`$dictionary['FP_events']['fields']['maybe_c'] = array(`
  1. I created the video so you can see my system.
    https://drive.google.com/file/d/1-1ujCJVGVXIR9jjRBj56Kbcpj29DT1u9/view?usp=share_link

  2. The fields_meta_data table is not used when the custom field is created from a file and not from Studio.

This is a good idea. :laughing:

@p.konetskiy [quote=“p.konetskiy, post:19, topic:87306”]
I recommend fixing the bug here first. You missed the key for the field 'maybe_c'
[/quote]

I copy/pasted the wrong text in the post above. In the version I used in my system, the ‘maybe_c’ field was included in the vardef. I have updated the post above. Thank you for the catch.

I watched your video.

I am doing exactly the same thing and am not getting the “Execute SQL” option after a QR&R so there is no field being added to the fp_events_cstm table. I even tried running QR&R twice.

I noticed that in your video, the field you added was the first/only custom field for FP_events.

I had previously added custom fields via Studio and via the programatic method described above to check the correct parameters and then tried adding via vardefs. I wonder if the prior existence of a Studio-generated or programatic-=generated custom field in the fp_events_cstm table is somehow blocking the addition of another custom field via vardefs. I’ll setup a fresh SuiteCRM and try the vardefs first.

Are you saying that the programatic method described in my last post for adding custom fields does not work on your sytem?

R

@Ramblin

I haven’t tested your code. This code looks like it’s working. I don’t see much point in it because it does the same thing as “QR&R”.

I can’t see your system and can’t figure out what the problem is.
:man_shrugging:

@p.konetskiy

Well, it seems there is some kind of interference between the methods.

I created a fresh installation of SuiteCRM 7.12.8

Before doing anything else, I created a custom field using the above vardef method

It worked. It added a field to the fp_events_cstm table and the field was visible in Studio.

I then added another custom field using Studio

The field created in vardefs was still in the fp_events_cstm database table but was no longer visible in Studio

I then added another custom field using the programatic method from above

All three custom fields were now in the fp_events_cstm table

The two fields created in Studio and using the programatic method were also in the fields_meta_data table

The fields created in Studio and the programatic method were both visible in studio but the field created via the vardef was not

I even tried manually adding the original field created using vardefs to the Editview layout for FP_events but when I tried to use Editview, it showed the field label but had nothing showing for the field itself so I could not enter anything for the field or use the field.

So it looks like once you create a custom field in a module using Studio, the vardef creation method for custom fields no longer works - and in fact custom fields previously created using vardefs are somehow removed from Studio and their access affected

It answers the question about why our two systems were reacting differently to us doing the same thing … the sequence was different.

If the above holds true, I would avoid adding custom fields via vardefs since they could be disabled later if Studio is used to add fields to the same module.

@Ramblin

If you are writing about this field, remove the 'custom_module' => 'FP_events', parameter. This is mistake.
You can use Studio and code together , but you have to be careful with code.

I created a field using Studio after adding another field with code. It worked. There are no restrictions on the order in which fields are added.

I can recommend choosing one way to add fields to the system to reduce errors.

@p.konetskiy

That was it!

Well, I learned a lot about the inner workings of SuiteCRM and I had never befor heard of the

$dictionary['FP_events']['custom_fields'] = true;

parameter, so I think that was key as was the NOT using

'custom_module' => '<ModuleName>',

I just tested this on multiple modules and it works, even when the module did NOT previously have a _cstm database table. The vardef definition created the _cstm table and then added the field definition to the _cstm database table.

This is now my preferred way to add custom fields.

I’ll update the post with new documentation.

THANK YOU!

I have said it before but will say it again … a HUGE thank you to @p.konetskiy for helping figure this out. Many posts in this and the Sugar forum tried to make sense of the topic but did not uncover some key things the way p.konetskiy did.

So, if you want to add fields to the _cstm database table instead of the core database table for a module - and you want to know why this is a good idea for core modules - and you do not want to use Studio - and you want to know why using Studio could create problems - read on.

It is a long-winded explanation but certainly instructive.

You can stop reading after reading the “Adding via vardefs” section if you want. That provides a very viable (preferrable) way to do it. The other options work as well so they have been included for completeness.


If you want to duplicate/transfer your SuiteCRM instance to another location/hoster, you can tar the SuiteCRM directory, mysqldump the sql database, take the files to the new location, untar the directory, run the sql file to duplicate the database, update the .htaccess and config file(s) and you have a duplicate system. If this is how you plan to duplicate/restore your system, you do not need the methods shown in this section, but you may want to read it and adjust your technique.

If you use a version control system (git) to duplicate your system in another location you definitely want to read this. If you use Studio to create new fields in your SuiteCRM installation, and someone tries to pull from your central repository and duplicate your setup, any fields created in Studio will NOT be recreated in the local system since Studio does not store the vardef definition in the custom directory for the custom fields it creates. Studio puts the configuration for its custom fields directly into the cache/ directory so after a pull and Quick Repair and Rebuild, the custom fields are not recreated. No, it is not a good use of a cache/ directory but that is what Studio does and it is one of the reasons you cannot just delete all the contents of the cache directory.

  • You could include the sql commands to recreate the custom fields in a dedicated directory to regenerate the custom fields on the local system, but that would require an understanding between all users of the repository of the requirement to do so. And the sql commands may be different, depending on the current and target branch of the local system.
  • You could include the full database with each push to the central repository but again, that would require an understanding between all users of the repository of the way to recreate the remote system AND it means the local system has a full copy of the CONTENTS of the database - which you may not want.
  • Ideally, you want the pull to bring down all the configuration setup required so that a simple Quick Repair and Rebuild makes the local system complete. The instructions below let you do that with custom fields in the _cstm database table.

SuiteCRM has two database tables for each module: the core table (eg contacts) and another table with a _cstm suffix (eg contacts_cstm). Fields that are part of the original SuiteCRM code are in the core database table. Fields added to the module with Studio have _c appended as a suffix to the field name and go into the _cstm database table. The _cstm database table for a module does not exist until a field is created that would be stored in the _cstm table.

Just to complicate things:

  • Fields added in Module Builder go into the core database table for the module, but since Module Builder is used to create a custom module, this is not an issue.
  • However, fields added via direct entry in the vardefs file (eg custom/Extension/modules/{modulename}/Ext/Vardefs/{filename}.php) go into the core database table, unless specific parameters in the field definition are included which direct the system to add the new fields to the _cstm table.

Best practice for adding fields to CORE modules in SuiteCRM is to create new fields in the _cstm database table for each core module, not the core database table.

  • Installations on cloud SuiteCRM systems likely will not allow you to alter core database tables
  • For CORE modules, if you add new fields to the core database table, if SuiteCRM updates a core module and it creates its own new fields in the core database table, you risk your new field having conflict with a future SuiteCRM update. Additionally, there are times when an update to a core module does not just add to a database table but replaces it with the new structure, copying over the old data - but only for fields that were in the original core database table definition.
  • Having said this, another developer could add another field to the cstm module database table that has the same name as your new field and you would be back into the conflict issue. This is a risk you can control though (choosing which add-on modules to install). However, it is recommended to ALWAYS have a prefix (I use ayu , the first 3 letters of my company Ayuda) for any new field you create in any module. This minimizes the odds of a conflict in either the core or _cstm database tables.
  • When you are creating your own CUSTOM module, you may be better off adding new fields to the core database table for the new module and that is fine since you would use some kind of installer (manifest file, …) to add fields for that new module so all would work fine, even on cloud SuiteCRM systems. In fact, there is no JOIN required in the sql lookups if all fields are in the core database table, so if your custom module has a number of fields, it is preferable, from a performance perspective, to keep all the fields in the core database table.

You can add fields to the _cstm database table by defining them in vardefs like you do for fields going into the core database table. There are special parameters required. The advantage of using this method becomes very obvious when using a version control system (git). When another developer does a pull on the central repository, the custom fields with their database entries are created in the developer’s system with a Repair and Rebuild, unlike the result with custom fields created using Studio, where the field definitions are put into the cache/ directory and NOT recreated with a Repair and Rebuild on the new system.

  • The field name must end with the suffix _c so the system knows to look in the _cstm database table for the field
  • Each field definition must include
  • 'source' => 'custom_fields'  
    
  • Each module into which you are adding custom field(s) must have somewhere in the vardefs for that module
  • $dictionary['{ModuleName}']['custom_fields'] = true;
    
  • The field definition - when NOT using Studio - must NOT include
  • 'custom_module' => '{ModuleName}'
    
  • This is created when a field is created with Studio, but if it is included when a custom field is created with vardefs, it conflicts with Studio and causes issue
    
  • The ID for the vardef is created as
  • {Module Name}{field_name with the _c suffix}
    
  • Unlike the post referenced below from the SugarCRM site where it said the fields_meta_data table is still used, when custom fields are created using vardefs, the fields_meta_data table does not have any entries for fields created with vardefs
  • Like when creating a field in the core database table using vardefs, when creating a custom field using vardefs, a separate language file is alse required.
  • As always, after creating the vardef and language file to define the custom field, a Quick Repair and Rebuild is required

The following example shows how to create a custom text field called ayu_my_custom_field_c in the Events module (FP_events) when the FP_events module did not previously have any vardefs or language files defined in the custom directory. It also works if there were previously defined custom fields, but in that case, you would not have to create the directory structures using mkdir.

You will see that I use a short prefix (ayu_) for my field name. You can use any characters you want - these are the first three letters of my company name . This reduces the chance of a conflict with a field created by another developer using the same field name (without the prefix).

mkdir -p custom/Extension/modules/FP_events/Ext/Vardefs
nano custom/Extension/modules/FP_events/Ext/Vardefs/my_custom_field.php
<?php

$dictionary['FP_events']['fields']['ayu_my_custom_field_c'] = array(
    'name' => 'ayu_my_custom_field_c',
    'vname' => 'LBL_MY_CUSTOM_FIELD',
    'id' => 'FP_eventsayu_my_custom_field_c',
    'type' => 'varchar',
    'len' => '255',
    'size' => '20',
    'source' => 'custom_fields',
    'help' => '',
    'comments' => '',
    'default' => '',
    'no_default' => true,
    'duplicate_merge' => 'disabled',
    'duplicate_merge_dom_value' => '0',
    'required' => false,
    'inline_edit' => '',
    'massupdate' => '0',
    'audited' => false,
    'reportable' => true,
    'importable' => 'true',
    'unified_search' => false,
    'merge_filter' => 'disabled',
    'studio' => 'visible',
);

$dictionary['FP_events']['custom_fields'] = true;

mkdir -p custom/Extension/modules/FP_events/Ext/Language
nano custom/Extension/modules/FP_events/Ext/Language/en_us.my_custom_field.php
<?php
$mod_strings['LBL_MY_CUSTOM_FIELD'] = 'It works when done properly';

Admin -> Dev Tools -> Repair -> Quick Repair and Rebuild

Read on if you want to see alternative ways to add fields to the _cstm database table
*
*

You can add custom fields using the manifest file structure like what is created with Module Builder when it is Published

You can add custom fields using a SuiteCRM built-in function.
Details are available at Manually Creating Custom Fields - SugarCRM Support Site
Two steps involved here:

  1. Create code that will call the install_custom_fields() method in the ModuleInstaller class in the {SuiteCRM_Directory}/ModuleInstall/ModuleInstaller.php (around line 1272)
  2. Trigger the code BEFORE a Quick Repair and Rebuild is run

Re 1) The code for calling the install_custom_fields() method will look something like the following sample (text field shown):

  • The name will have _c added as a suffix by the install_custom_fields() method. If you do add the _c in your name definition, the install_custom_fields() method will recognize that and NOT add another _c suffix.
  • The ID in the fields_meta_data table will be created by the install_custom_fields() method and will be {Modulename}{fieldname}_c
<?php
if(!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}
$fields = array ();
$fields[] = array(
    // Text Field
    'name' => 'ayu_program_text_field',
    'label' => 'LBL_PROGRAM_TEXT_FIELD_7',// No _c suffix used in LBL…
    'type' => 'varchar',
    'module' => 'FP_events',
    '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
);
require_once('ModuleInstall/ModuleInstaller.php');
$newInstallerBean = new ModuleInstaller();
$newInstallerBean->install_custom_fields($fields);
// Return the user to the Administration menu
SugarApplication::redirect('index.php?module=Administration&action=index');

Notes:
In additional to the field elements that are defined, there are some entries required that are put into fields_meta_data and used by the install_custom_fields() method to add the fields to the _cstm database table

  • module

The only field elements that are defined in a _cstm database table are shown below with their type shown after.

id varchar(255)
This is the equivalent of a foreign_key setup so is not restricted to the normal ID length of 36 characters
name varchar(255)
vname varchar(255)
comments varchar(255)
help varchar(255)
custom_module varchar(255)	
type varchar(255)
len int
required bool
default_value varchar(255)
date_modified datetime
deleted bool
audited bool
massupdate bool
duplicate_merge bool
reportable bool
importable varchar(255)
ext1 varchar(255)
ext2 varchar(255)
ext3 varchar(255)
ext4

If you have any additional elements you want defined (eg inline_edit or Label value), you need to separately define them in the appropriate custom/… directory as follows. Remember, the field name has _c as the suffix in a _cstm table

For additional elements, add one $dictionary definition per element

cd {SuiteCRM_Directory}
mkdir -p custom/Extension/modules{ModuleName}/Ext/Vardefs/
nano custom/Extension/modules{ModuleName}/Ext/Vardefs/{filename}.php
$dictionary['{ModuleName}']['fields']['field_name_c']['{element}'] = '{element_value}';
  • {ModuleName} may be singular; check the other entries in vardefs
  • {element} is the element you wish to define (eg inline_edit)
  • (element_value} matches the type definition (eg bool = true/false)

For Label defintions

cd {SuiteCRM_Directory}
mkdir -p custom/Extension/modules{ModuleName}/Ext/Language/
nano custom/Extension/modules{ModuleName}/Ext/Vardefs/en_us.{filename}.php
$mod_strings['LBL_...'] = 'The text string you want';

The method for triggering that code is up to you. Options include
Adding a logic hook that when triggered includes code like the above

  • Using SuiteCRM APIs
  • Manully calling the code from a browser after enabling the code as an EntryPoint
  • Add a button/menu to a SuiteCRM GUI that triggers the code when clicked

Make sure that whatever method you use, you only run the code once for each field. If you run it again for the same field, you will be trying to add a field to a _cstm database table when the field already exists in the table and you will get an sql error.
Simplest method: add a file with the above code, define the file as an allowed EntryPoint, and access the file from your browser. You could put the file anywhere; for the purpose of this explanation, I will put it into custom/…
Create the code

cd (SuiteCRM_Directory}
nano custom/addCustomFields.php
//
…  add the code shown above, editing to suit your situation …
… one array element defined for each field
//
Enable the page with the code to serve as an Entry Point
mkdir -p custom/Extension/application/Ext/EntryPointRegistry/
nano custom/Extension/application/Ext/EntryPointRegistry/addCustomFieldsEntryPoint.php
<?php
$entry_point_registry['addCustomFieldsEntryPoint'] = array(
    // Pointer to the location of the file for which this EntryPoint is defined
    'file' => 'custom/addCustomFields.php',
    // auth true means person using the EntryPoint must be an authenticated User
    // auth false means anyone could the EntryPoint - NOT a good idea usually
    'auth' => true,
);
Call the code using your browser
http://{SuiteCRM _Site}/index.php?EntryPoint=addCustomFieldsEntryPoint

This is all that is needed and it will work. The admin has to remember to populate the php code with each field to be added, manually run the code (ONCE for each field to be added) by calling the EntryPoint from their browser, and then remove the field definition from the code once the field has been created.

However, if you are going to use this on an ongoing development basis, I would do the following:
Create the file and the EntryPoint as shown above

Add a submenu to the Repair option in Admin → Admin Tools. We add a new submenu “Create Custom Fields”

mkdir -p custom/modules/Administration
cp -a modules/Administration/Upgrade.php custom/modules/Administration/Upgrade.php 
nano custom/modules/Administration/Upgrade.php 
Change
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD']); ?>&nbsp;<a href="./index.php?module=Administration&action=repair"><?php echo $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD_DESC'] ; ?> </td>
</tr>
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_EXPAND_DATABASE_COLUMNS']); ?>&nbsp;<a href="./index.php?module=Administration&action=expandDatabase"><?php echo $mod_strings['LBL_EXPAND_DATABASE_COLUMNS']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_EXPAND_DATABASE_COLUMNS_DESC'] ; ?> </td>
</tr>
To (add a new item between them)
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD']); ?>&nbsp;<a href="./index.php?module=Administration&action=repair"><?php echo $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_QUICK_REPAIR_AND_REBUILD_DESC'] ; ?> </td>
</tr>
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_CREATE_CUSTOM_FIELDS']); ?>&nbsp;<a href="./index.php?entryPoint=addCustomFieldsEntryPoint"><?php echo $mod_strings['LBL_CREATE_CUSTOM_FIELDS']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_CREATE_CUSTOM_FIELDS_DESC'] ; ?> </td>
</tr>
<tr>
	<td scope="row"><?php echo SugarThemeRegistry::current()->getImage('Repair', 'align="absmiddle" border="0"', null, null, '.gif', $mod_strings['LBL_EXPAND_DATABASE_COLUMNS']); ?>&nbsp;<a href="./index.php?module=Administration&action=expandDatabase"><?php echo $mod_strings['LBL_EXPAND_DATABASE_COLUMNS']; ?></a></td>
	<td> <?php echo $mod_strings['LBL_EXPAND_DATABASE_COLUMNS_DESC'] ; ?> </td>
</tr>

Add the language definitions

mkdir -p custom/Extension/modules/Administration/Ext/Language/
nano custom/Extension/modules/Administration/Ext/Language/en_us.repairSubMenu.php
<?php
$mod_strings['LBL_CREATE_CUSTOM_FIELDS'] = 'Create Custom Fields';
$mod_strings['LBL_CREATE_CUSTOM_FIELDS_DESC'] = 'Add fields specified in ./custom/AYU_CustomFields/... to _cstm database table(s)';

This provides a menu to trigger the code so users do not have to remember the URL to trigger the EntryPoint directly.

The user still needs to remove the field definition after each use to prevent attempts to create the same field again.

If I was going to use it a lot, I would create a module called AYU_Customfields with ModuleBuilder to store the code and be the target URL for the user access and add code in custom/AYU_Customfields to read files, programatically add the new field definitions to the code, run the program, and set a flag to show that the fields have been added.

  • Create the AYU_Customfields module in Module Builder and in that new module’s controller, add an action called add_custom_fields
  • Create a new Module AYU_Customfields using ModuleBuilder and Deploy
  • Edit/Create a custom controller in that module and add a add_custom_fields action
nano modules/AYU_Customfields/controller.php
if (!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}
class AYU_CustomfieldsController extends SugarController
{
    public function action_add_custom_fields () {
        // insert the above code, edited to suit the situation
        // …
        // (Optional) add code to run Quick Repair and Rebuild 
        // from within this script, after creating the new custom fields
        // The repairAndClearAll method uses the following arguments
        //        Argument1 => $selected_actions
        //        Argument2 => $modules
        //        Argument3 => $autoexecute (bool = true or false)
        //        Argument4 => $show_output (bool = true or false)
        // require_once('modules/Administration/QuickRepairAndRebuild.php');
        // $newFields = new RepairAndClear();
        // $newFields->repairAndClearAll(array('clearAll'),array(translate('LBL_ALL_MODULES')), true , true);
    }
}
Change the URL in the new menu
From
./index.php?entryPoint=addCustomFieldsEntryPoint
To
./index.php?module=AYU_Customfields&action=add_custom_fields

And in the new module, add a list view to show what field(s) have been added and what field(s) remain to be added, and direct the return after running the code back to the list view (the normal default return).

Add code to store field definitions and flag ones which have already been created

One way to handle this would be to add one file per field to custom/Extension/application/Ext/Extensions/{filename1}.php

A Quick Repair and Rebuild consolidates all these files into one file at custom/application/Ext/Extensions/extensions.ext.php

You could run your check code on either location and set flags to prevent an attempt to add a custom field more than once.

A post was split to a new topic: Custom changes not taking effect