Custom Labels Do Not Save

Version 7.4.11

Steps to reproduce:

  1. Go to Admin > Studio and select any module with a custom field created in it.
  2. Select “Labels” and attempt to change the label of the custom field.
  3. Click Save and Deploy

Expected Result:
The newly changed label appears in the form and on the page

Actual Result:
The label has reverted to the label as is was when the custom field was created

This is an old complaint stemming from SugarCE and in four years (an eternity for software) no solution is proferred. I can see forum posts about this going back 4 years with the following unacceptable solutions:

  1. Change the permissions on the file
    Permissions for the entire directory are 776, well more than enough

  2. Save the file changes in /custom/Extensions/…
    Administrative users do not have access to the files system and therefore CRM Managers who are often responsible for things like translating labels can not use this method of solving the problem and the business is not willing to pay the cost of a developer doing such label and UI maintenance because a tool is provided that should do this job and does not do it correctly.

The github issue log is not adequately searchable but it seems not to be designed for support of a customer but rather developers.

Has there been any progress on this problem? What triggers it and how it can be fixed?

Just to save a lot of time here are the links to the other two instances that are very similar to this.

  1. https://suitecrm.com/suitecrm/forum/bug-tracker/1467-bug-labels-not-being-saved

  2. https://suitecrm.com/suitecrm/forum/developer-help/1244-field-label-not-changing-from-studio

Ok,

After some investigation into the problem I found a non-upgrade safe solution that during local tests permanently fixes the problem. I am providing code here and a walk through of this for posterity sake since other developers may want to implement this on their end should they encounter this problem.

First:
The problem stems from how /modules/ModuleBuilder/parsers/parser.label.php operates. The addLabel method does not completely rebuild the labels, neither do any of the other methods, although they will read into them when changes are made.

Second:
This is a non-upgrade safe change and it is customer centric. It is designed for use by those who want Users driving development rather than developers pushing files into the system directly. I have tested the core functions of the label saves from multiple ways and it seems to work, but I have not done an exhaustive regression test.

Third:
Because it works from the user perspective first, it behaves much more like users expect in that both the views and ui are updated immediately and completely. Allowing them to export their changes in their entirety to packages for deployment to other sites.

Last:
This is a pseudo destructive approach to the problem in that I assume the user is right and the final authority on what ALL labels should be, not just the ones being changed.

The solution:
At line 275 of the 7.10.11 build of this a community fix to bug #51 makes a change very similar to this fix for relationship labels. After this block I put a call to a new method of self called “updateExtLabelLibraryFile” then place the following code block at the bottom of the class file as a new method.


public function updateExtLabelLibraryFile($moduleName='Accounts', $language='en_us', $changed_values=array())
    {
        //if there are changes, go ahead and update the array and save the work
        if (count($changed_values) > 0) {
            $extension_basepath = 'custom/Extension/modules/' . $moduleName . '/Ext/Language';
            $module_path = 'custom/modules/'. $moduleName. '/Language/'. $language.'.lang.php';
            $module_ext_path = 'custom/modules/'. $moduleName. '/Ext/Language/'. $language.'.lang.ext.php';
            $rolled_up_modstrings_file = $language . '.' . $moduleName . 'Customizations.php';
            $GLOBALS['log']->debug("ParserLabel::updateExtLabelLibraryFile: Updating the " . $moduleName . " module's extension roll up file [" . $extension_basepath . '/' . $rolled_up_modstrings_file . "].");

            //Update the extension framework file

            $rolled_up_mod_strings = array();
            $iterator = new DirectoryIterator($extension_basepath);
            foreach ($iterator as $fileinfo) {
                if ($fileinfo->isFile()) {
                    include($fileinfo->getPathname());
                    if(count($mod_strings)>0){
                        $rolled_up_mod_strings = array_replace_recursive($rolled_up_mod_strings, $mod_strings);
                    }
                }
            }
            $rolled_up_mod_strings = array_replace_recursive($rolled_up_mod_strings, $changed_values);

            //dump the contents of the updated mod string array to an out variable for write to files
            $date = new DateTime;
            $date_string = $date->format('Y-m-d H:i:s');
            $headerString = "<?php\n//This file was updated by the studio on ". $date_string. "\n";
            $out = $headerString;
            foreach ($rolled_up_mod_strings as $key => $val) {
                $out .= override_value_to_string_recursive2('mod_strings', $key, $val);
            }

            //Overwrite the extension file
            $handle_ext = fopen($extension_basepath . '/' . $rolled_up_modstrings_file, 'w');
            fwrite($handle_ext, $out);
            fclose($handle_ext);

            //Overwrite the module file
            $handle_mod = fopen($module_path, 'w');
            fwrite($handle_mod, $out);
            fclose($handle_mod);

            //Overwrite the extension file
            $handle_mod_ext = fopen($module_ext_path, 'w');
            fwrite($handle_mod_ext, $out);
            fclose($handle_mod_ext);

        }
        return true;
    }

This block makes some assumptions, the most critical being that all of the mod_strings will be “rolled” up into a single custom file named Customizations.php in the custom/Extensions/modules//Ext/Language/ directory. The method will use php directory iterator to go over the other files and merge all of the mod_strings in this directory to a core array and then merge that with the changed elements of that array.

Since I’m more interested in providing a workable solution to my customer than some more elegant solution for every possible custom these assumptions are acceptable and future features can extend this to be more flexible.

However, of key function here is the roll up of changes. We assume that the admin user’s wishes trump everything else and Studio is in charge, NOT an ftp’er or deployment tool for individual files. Therefore, this method wipes all three locations for the file and forces the updated array to be put into all three locations. This way, future QRR’s will not change the files and Studio changes always win any contest of file version.

Hello, and thanks for your investigation.

Do you know if this is the only solution possible? (I am facing the same issue with a pristine installation with the latest Suitecrm: 7.11.18)

Edited: I do NOT face the issue in the pristine installation with the latest Suitecrm, while I face in our installation 7.11.18 (but we are coming from Sugar), same server.

these are the upgrades

20140407-SugarCRBBW2014_04_07_105636.zip
SugarCE Migration 6.x to Suite 7.6.6.zip
SuiteCRM-Upgrade-7.6.x-to-7.8.31.zip
SuiteCRM-Upgrade-7.8.x-to-7.10.25.zip
SuiteCRM-Upgrade-7.10.x-to-7.11.13.zip
SuiteCRM-Upgrade-7.11.x-to-7.11.18.zip

Any suggestion on what I should check?

More info: it happens with Accounts (that we have been customizing) but the but is does NOT happens with another bean (e.g. Invoices) that we never customized.

uh, I found what was causing my isse:

I had the following custom file:

# cat custom/Extension/modules/Accounts/Ext/Language/en_us.20140407-SugarCRBBW.php
<?php 
 // created: 2014-04-07 10:56:33
$mod_strings['LBL_DATE_MODIFIED'] = 'Last Activity:';
$mod_strings['LBL_NAME'] = 'Customer:';
$mod_strings['LBL_BILLING_ADDRESS_STREET'] = 'Address:';
$mod_strings['LBL_BILLING_ADDRESS_CITY'] = 'City:';
$mod_strings['LBL_BILLING_ADDRESS_POSTALCODE'] = 'CAP_ZIP:';
$mod_strings['LBL_BILLING_ADDRESS_STATE'] = 'Province_USstate:';
$mod_strings['LBL_BILLING_ADDRESS_COUNTRY'] = 'Country:';
$mod_strings['LBL_OTHER_PHONE'] = 'Alternative Phone:';
$mod_strings['LBL_PIVA_VAT'] = 'PIVA_VAT:';
$mod_strings['LBL_ORIGIN_1'] = 'Origin#1:';
$mod_strings['LBL_ORIGIN_2'] = 'Origin#2:';
$mod_strings['LBL_NOTE'] = 'Note:';
$mod_strings['LBL_SOURCE'] = 'Source:';
$mod_strings['LBL_LIST_ACCOUNT_NAME'] = 'Customer';
$mod_strings['LBL_HISTORY'] = 'History:';
$mod_strings['LBL_OTHER_EMAIL_ADDRESS'] = 'Alternative Email';

so that file was just making a mess: I removed it!

sorry for the noise