2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
39 require_once('modules/Studio/DropDowns/DropDownHelper.php');
40 require_once 'modules/ModuleBuilder/parsers/parser.label.php' ;
45 * Selected language user is renaming for (eg. en_us).
49 private $selectedLanguage;
52 * An array containing the modules which should be renamed.
56 private $changedModules;
59 * An array containing the modules which have had their module strings modified as part of the
64 private $renamedModules = array();
68 * An array containing the modules and their labels to be changed when module is renamed.
70 private static $labelMap = array(
72 array('name' => 'LBL_CAMPAIGNS', 'type' => 'plural', 'source' => 'Campaigns'),
73 array('name' => 'LBL_CAMPAIGN_ID', 'type' => 'singular', 'source' => 'Campaigns'),
74 array('name' => 'LBL_PARENT_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
75 array('name' => 'LBL_PROSPECT_LIST', 'type' => 'singular', 'source' => 'Prospects'),
78 array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular', 'source' => 'Bugs'),
79 array('name' => 'LBL_LIST_MY_BUGS', 'type' => 'plural', 'source' => 'Bugs'),
80 array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular', 'source' => 'Bugs'),
81 array('name' => 'LNK_BUG_LIST', 'type' => 'plural', 'source' => 'Bugs'),
82 array('name' => 'LNK_BUG_REPORTS', 'type' => 'singular', 'source' => 'Bugs'),
83 array('name' => 'LNK_IMPORT_BUGS', 'type' => 'plural', 'source' => 'Bugs'),
84 array('name' => 'LNK_NEW_BUG', 'type' => 'singular', 'source' => 'Bugs'),
87 array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
90 array('name' => 'LBL_ACCOUNTS', 'type' => 'plural', 'source' => 'Accounts'),
91 array('name' => 'LBL_CONTACTS', 'type' => 'plural', 'source' => 'Contacts'),
92 array('name' => 'LBL_LIST_CAMPAIGN_NAME', 'type' => 'singular', 'source' => 'Campaigns'),
93 array('name' => 'LBL_LOG_ENTRIES_CONTACT_TITLE', 'type' => 'plural', 'source' => 'Contacts'),
94 array('name' => 'LBL_LOG_ENTRIES_LEAD_TITLE', 'type' => 'plural', 'source' => 'Leads'),
95 array('name' => 'LBL_OPPORTUNITIES', 'type' => 'plural', 'source' => 'Opportunities'),
96 array('name' => 'LBL_PROSPECT_LIST_SUBPANEL_TITLE', 'type' => 'singular', 'source' => 'Targets'),
99 array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural', 'source' => 'Bugs'),
100 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
103 array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural', 'source' => 'Bugs'),
104 array('name' => 'LBL_CAMPAIGN_LIST_SUBPANEL_TITLE', 'type' => 'plural', 'source' => 'Campaigns'),
105 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contracts'),
106 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
107 array('name' => 'LBL_LEAD_SOURCE', 'type' => 'singular', 'source' => 'Leads'),
108 array('name' => 'LBL_OPPORTUNITIES', 'type' => 'singular', 'source' => 'Opportunities'),
109 array('name' => 'LBL_OPPORTUNITY_ROLE', 'type' => 'singular', 'source' => 'Opportunities'),
110 array('name' => 'LBL_OPPORTUNITY_ROLE_ID', 'type' => 'singular', 'source' => 'Opportunities'),
111 array('name' => 'LBL_PRODUCTS_TITLE', 'type' => 'plural', 'source' => 'Products'),
112 array('name' => 'LBL_PROSPECT_LIST', 'type' => 'singular', 'source' => 'Prospects'),
114 'Contracts' => array(
115 array('name' => 'LBL_CONTRACT_NAME', 'type' => 'singular', 'source' => 'Contracts'),
116 array('name' => 'LBL_CONTRACT_TERM', 'type' => 'singular', 'source' => 'Contracts'),
117 array('name' => 'LBL_DOCUMENTS', 'type' => 'plural', 'source' => 'Documents'),
118 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
119 array('name' => 'LBL_LIST_CONTRACT_NAME', 'type' => 'singular', 'source' => 'Contracts'),
120 array('name' => 'LBL_OPPORTUNITY', 'type' => 'singular', 'source' => 'Opportunities'),
121 array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular', 'source' => 'Contracts'),
122 array('name' => 'LBL_TOTAL_CONTRACT_VALUE', 'type' => 'singular', 'source' => 'Contracts'),
123 array('name' => 'LBL_TOTAL_CONTRACT_VALUE_USDOLLAR', 'type' => 'singular', 'source' => 'Contracts'),
124 array('name' => 'LNK_NEW_CONTRACT', 'type' => 'singular', 'source' => 'Contracts'),
126 'Documents' => array(
127 array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural', 'source' => 'Bugs'),
128 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contracts'),
129 array('name' => 'LBL_CONTRACT_NAME', 'type' => 'singular', 'source' => 'Contracts'),
130 array('name' => 'LBL_CONTRACT_STATUS', 'type' => 'singular', 'source' => 'Contracts'),
131 array('name' => 'LBL_DET_RELATED_DOCUMENT_VERSION', 'type' => 'singular', 'source' => 'Documents'),
132 array('name' => 'LBL_DET_TEMPLATE_TYPE', 'type' => 'singular', 'source' => 'Documents'),
133 array('name' => 'LBL_DOC_ID', 'type' => 'singular', 'source' => 'Documents'),
134 array('name' => 'LBL_DOC_NAME', 'type' => 'singular', 'source' => 'Documents'),
135 array('name' => 'LBL_DOC_REV_HEADER', 'type' => 'singular', 'source' => 'Documents'),
136 array('name' => 'LBL_DOC_URL', 'type' => 'singular', 'source' => 'Documents'),
137 array('name' => 'LBL_NAME', 'type' => 'singular', 'source' => 'Documents'),
138 array('name' => 'LBL_TEMPLATE_TYPE', 'type' => 'singular', 'source' => 'Documents'),
140 'KBDocuments' => array(
141 array('name' => 'LBL_CASES', 'type' => 'plural', 'source' => 'Cases'),
142 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contracts'),
143 array('name' => 'LBL_CONTRACT_NAME', 'type' => 'plural', 'source' => 'Contracts'),
146 array('name' => 'LNK_SELECT_###MODULE_PLURAL###', 'type' => 'singular', 'source' => 'Leads'),
147 array('name' => 'LNK_SELECT_###MODULE_SINGULAR###', 'type' => 'singular', 'source' => 'Leads'),
148 array('name' => 'LBL_ACCOUNT_DESCRIPTION', 'type' => 'singular', 'source' => 'Accounts'),
149 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
150 array('name' => 'LBL_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
151 array('name' => 'LBL_CAMPAIGN_ID', 'type' => 'singular', 'source' => 'Campaigns'),
152 array('name' => 'LBL_CAMPAIGN_LEAD', 'type' => 'plural', 'source' => 'Campaigns'),
153 array('name' => 'LBL_CAMPAIGN_LIST_SUBPANEL_TITLE', 'type' => 'plural', 'source' => 'Campaigns'),
154 array('name' => 'LBL_CONTACT_ID', 'type' => 'singular', 'source' => 'Contacts'),
155 array('name' => 'LBL_LEAD_SOURCE', 'type' => 'singular', 'source' => 'Leads'),
156 array('name' => 'LBL_LEAD_SOURCE_DESCRIPTION', 'type' => 'singular', 'source' => 'Leads'),
157 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
158 array('name' => 'LBL_OPPORTUNITY_AMOUNT', 'type' => 'singular', 'source' => 'Opportunities'),
159 array('name' => 'LBL_OPPORTUNITY_ID', 'type' => 'singular', 'source' => 'Opportunities'),
160 array('name' => 'LBL_OPPORTUNITY_NAME', 'type' => 'singular', 'source' => 'Opportunities'),
161 array('name' => 'LBL_CONVERTED_ACCOUNT', 'type' => 'singular', 'source' => 'Accounts'),
162 array('name' => 'LNK_SELECT_ACCOUNTS', 'type' => 'singular', 'source' => 'Accounts'),
163 array('name' => 'LNK_NEW_ACCOUNT', 'type' => 'singular', 'source' => 'Accounts'),
164 array('name' => 'LBL_CONVERTED_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
165 array('name' => 'LBL_CONVERTED_OPP', 'type' => 'singular', 'source' => 'Opportunities'),
169 array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
170 array('name' => 'LBL_LIST_JOIN_MEETING', 'type' => 'singular', 'source' => 'Meetings'),
171 array('name' => 'LBL_PASSWORD', 'type' => 'singular', 'source' => 'Meetings'),
172 array('name' => 'LBL_TYPE', 'type' => 'singular', 'source' => 'Meetings'),
173 array('name' => 'LBL_URL', 'type' => 'singular', 'source' => 'Meetings'),
176 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
177 array('name' => 'LBL_CASE_ID', 'type' => 'singular', 'source' => 'Cases'),
178 array('name' => 'LBL_CONTACT_ID', 'type' => 'singular', 'source' => 'Contacts'),
179 array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
180 array('name' => 'LBL_LIST_CONTACT_NAME', 'type' => 'singular', 'source' => 'Contacts'),
181 array('name' => 'LBL_NOTE_STATUS', 'type' => 'singular', 'source' => 'Notes'),
182 array('name' => 'LBL_OPPORTUNITY_ID', 'type' => 'singular', 'source' => 'Opportunities'),
183 array('name' => 'LBL_PRODUCT_ID', 'type' => 'singular', 'source' => 'Products'),
184 array('name' => 'LBL_QUOTE_ID', 'type' => 'singular', 'source' => 'Quotes'),
186 'Opportunities' => array(
187 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
188 array('name' => 'LBL_AMOUNT', 'type' => 'singular', 'source' => 'Opportunities'),
189 array('name' => 'LBL_CAMPAIGN_OPPORTUNITY', 'type' => 'plural', 'source' => 'Campaigns'),
190 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contracts'),
191 array('name' => 'LBL_LEAD_SOURCE', 'type' => 'singular', 'source' => 'Leads'),
192 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
193 array('name' => 'LBL_OPPORTUNITY_NAME', 'type' => 'singular', 'source' => 'Opportunities'),
195 'ProductTemplates' => array(
196 array('name' => 'LBL_PRODUCT_ID', 'type' => 'singular', 'source' => 'Products'),
199 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
200 array('name' => 'LBL_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
201 array('name' => 'LBL_CONTACT_ID', 'type' => 'singular', 'source' => 'Contacts'),
202 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contacts'),
203 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
204 array('name' => 'LBL_LIST_NAME', 'type' => 'singular', 'source' => 'Products'),
205 array('name' => 'LBL_NAME', 'type' => 'singular', 'source' => 'Products'),
206 array('name' => 'LBL_QUOTE_ID', 'type' => 'singular', 'source' => 'Quotes'),
207 array('name' => 'LBL_RELATED_PRODUCTS', 'type' => 'plural', 'source' => 'Products'),
208 array('name' => 'LBL_URL', 'type' => 'singular', 'source' => 'Products'),
210 'ProjectTask' => array(
211 array('name' => 'LBL_PARENT_NAME', 'type' => 'singular', 'source' => 'Projects'),
212 array('name' => 'LBL_PROJECT_ID', 'type' => 'singular', 'source' => 'Projects'),
213 array('name' => 'LBL_PROJECT_NAME', 'type' => 'singular', 'source' => 'Projects'),
214 array('name' => 'LBL_PROJECT_TASK_ID', 'type' => 'singular', 'source' => 'Projects'),
217 array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural', 'source' => 'Bugs'),
218 array('name' => 'LBL_CONTACTS_RESOURCE', 'type' => 'singular', 'source' => 'Contacts'),
219 array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular', 'source' => 'Projects'),
220 array('name' => 'LBL_OPPORTUNITIES', 'type' => 'plural', 'source' => 'Opportunities'),
221 array('name' => 'LBL_PROJECT_HOLIDAYS_TITLE', 'type' => 'singular', 'source' => 'Projects'),
222 array('name' => 'LBL_PROJECT_TASKS_SUBPANEL_TITLE', 'type' => 'singular', 'source' => 'Projects'),
223 array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular', 'source' => 'Projects'),
224 array('name' => 'LNK_NEW_PROJECT', 'type' => 'singular', 'source' => 'Projects'),
225 array('name' => 'LNK_PROJECT_LIST', 'type' => 'singular', 'source' => 'Projects'),
228 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
229 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contracts'),
230 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
231 array('name' => 'LBL_QUOTE_NUM', 'type' => 'singular', 'source' => 'Quotes'),
234 array('name' => 'LBL_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
235 array('name' => 'LBL_CAMPAIGN_ID', 'type' => 'plural', 'source' => 'Campaigns'),
236 array('name' => 'LBL_CAMPAIGN_LIST_SUBPANEL_TITLE', 'type' => 'singular', 'source' => 'Campaigns'),
237 array('name' => 'LBL_PROSPECT_LIST', 'type' => 'singular', 'source' => 'Prospects'),
240 array('name' => 'LBL_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
241 array('name' => 'LBL_CONTACT_ID', 'type' => 'singular', 'source' => 'Contacts'),
242 array('name' => 'LBL_CONTACT_PHONE', 'type' => 'singular', 'source' => 'Contacts'),
249 * @param string $options
252 public function process($options = '')
254 if($options == 'SaveDropDown')
262 * Main display function.
266 protected function display()
268 global $app_list_strings, $mod_strings;
271 require_once('modules/Studio/parsers/StudioParser.php');
272 $dh = new DropDownHelper();
274 $smarty = new Sugar_Smarty();
275 $smarty->assign('MOD', $GLOBALS['mod_strings']);
276 $title=getClassicModuleTitle($mod_strings['LBL_MODULE_NAME'], array("<a href='index.php?module=Administration&action=index'>".$mod_strings['LBL_MODULE_NAME']."</a>", $mod_strings['LBL_RENAME_TABS']), false);
277 $smarty->assign('title', $title);
279 $selected_lang = (!empty($_REQUEST['dropdown_lang'])?$_REQUEST['dropdown_lang']:$_SESSION['authenticated_user_language']);
280 if(empty($selected_lang))
282 $selected_lang = $GLOBALS['sugar_config']['default_language'];
285 if($selected_lang == $GLOBALS['current_language'])
287 $my_list_strings = $GLOBALS['app_list_strings'];
291 $my_list_strings = return_app_list_strings_language($selected_lang);
294 $selected_dropdown = $my_list_strings['moduleList'];
295 $selected_dropdown_singular = $my_list_strings['moduleListSingular'];
298 foreach($selected_dropdown as $key=>$value)
300 $singularValue = isset($selected_dropdown_singular[$key]) ? $selected_dropdown_singular[$key] : $value;
301 if($selected_lang != $_SESSION['authenticated_user_language'] && !empty($app_list_strings['moduleList']) && isset($app_list_strings['moduleList'][$key]))
303 $selected_dropdown[$key]=array('lang'=>$value, 'user_lang'=> '['.$app_list_strings['moduleList'][$key] . ']', 'singular' => $singularValue);
307 $selected_dropdown[$key]=array('lang'=>$value, 'singular' => $singularValue);
312 $selected_dropdown = $dh->filterDropDown('moduleList', $selected_dropdown);
314 $smarty->assign('dropdown', $selected_dropdown);
315 $smarty->assign('dropdown_languages', get_languages());
318 $buttons[] = array('text'=>$mod_strings['LBL_BTN_UNDO'],'actionScript'=>"onclick='jstransaction.undo()'" );
319 $buttons[] = array('text'=>$mod_strings['LBL_BTN_REDO'],'actionScript'=>"onclick='jstransaction.redo()'" );
320 $buttons[] = array('text'=>$mod_strings['LBL_BTN_SAVE'],'actionScript'=>"onclick='if(check_form(\"editdropdown\")){document.editdropdown.submit();}'");
321 $buttonTxt = StudioParser::buildImageButtons($buttons);
322 $smarty->assign('buttons', $buttonTxt);
323 $smarty->assign('dropdown_lang', $selected_lang);
325 $editImage = SugarThemeRegistry::current()->getImage( 'edit_inline', '');
326 $smarty->assign('editImage',$editImage);
327 $deleteImage = SugarThemeRegistry::current()->getImage( 'delete_inline', '');
328 $smarty->assign('deleteImage',$deleteImage);
329 $smarty->display("modules/Studio/wizards/RenameModules.tpl");
333 * Save function responsible executing all sub-save functions required to rename a module.
337 public function save($redirect = TRUE)
339 $this->selectedLanguage = (!empty($_REQUEST['dropdown_lang'])? $_REQUEST['dropdown_lang']:$_SESSION['authenticated_user_language']);
341 //Clear all relevant language caches
342 $this->clearLanguageCaches();
344 //Retrieve changes the user is requesting and store previous values for future use.
345 $this->changedModules = $this->getChangedModules();
347 //Change module, appStrings, subpanels, and related links.
348 $this->changeAppStringEntries()->changeAllModuleModStrings()->renameAllRelatedLinks()->renameAllSubpanels()->renameAllDashlets();
350 foreach (self::$labelMap as $module=>$labelsArr) {
351 $this->renameCertainModuleModStrings($module, $labelsArr);
354 //Refresh the page again so module tabs are changed as the save process happens after module tabs are already generated.
356 SugarApplication::redirect('index.php?action=wizard&module=Studio&wizard=StudioWizard&option=RenameTabs');
360 * Rename all subpanels within the application.
363 * @return RenameModules
365 private function renameAllSubpanels()
369 foreach($beanList as $moduleName => $beanName)
371 if( class_exists($beanName) )
373 $this->renameModuleSubpanel($moduleName, $beanName, $this->changedModules);
377 $GLOBALS['log']->error("Class $beanName does not exist, unable to rename.");
386 * Rename subpanels for a particular module.
388 * @param string $moduleName The name of the module to be renamed
389 * @param string $beanName The name of the SugarBean to be renamed.
392 private function renameModuleSubpanel($moduleName, $beanName)
394 $GLOBALS['log']->info("About to rename subpanel for module: $moduleName");
395 $bean = new $beanName();
396 //Get the subpanel def
397 $subpanelDefs = $this->getSubpanelDefs($bean);
399 if(empty($subpanelDefs))
401 $GLOBALS['log']->debug("Found empty subpanel defs for $moduleName");
405 $mod_strings = return_module_language($this->selectedLanguage, $moduleName);
406 $replacementStrings = array();
408 //Iterate over all subpanel entries and see if we need to make a change.
409 foreach($subpanelDefs as $subpanelName => $subpanelMetaData)
411 $GLOBALS['log']->debug("Examining subpanel definition for potential rename: $subpanelName ");
412 //For each subpanel def, check if they are in our changed modules set.
413 foreach($this->changedModules as $changedModuleName => $renameFields)
415 if( !( isset($subpanelMetaData['type']) && $subpanelMetaData['type'] == 'collection') //Dont bother with collections
416 && isset($subpanelMetaData['module']) && $subpanelMetaData['module'] == $changedModuleName && isset($subpanelMetaData['title_key']) )
418 $replaceKey = $subpanelMetaData['title_key'];
419 if( !isset($mod_strings[$replaceKey]) )
421 $GLOBALS['log']->info("No module string entry defined for: {$mod_strings[$replaceKey]}");
424 $oldStringValue = $mod_strings[$replaceKey];
425 //At this point we don't know if we should replace the string with the plural or singular version of the new
426 //strings so we'll try both but with the plural version first since it should be longer than the singular.
427 // The saved old strings are html decoded, so we need to decode the new string first before str_replace.
428 $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_plural'], ENT_QUOTES), $renameFields['plural'], $oldStringValue);
429 if ($replacedString == $oldStringValue) {
430 // continue to replace singular only if nothing been replaced yet
431 $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_singular'], ENT_QUOTES), $renameFields['singular'], $replacedString);
433 $replacementStrings[$replaceKey] = $replacedString;
438 //Now we can write out the replaced language strings for each module
439 if(count($replacementStrings) > 0)
441 $GLOBALS['log']->debug("Writing out labels for subpanel changes for module $moduleName, labels: " . var_export($replacementStrings,true));
442 ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
443 $this->renamedModules[$moduleName] = true;
448 * Retrieve the subpanel definitions for a given SugarBean object. Unforunately we can't reuse
449 * any of the SubPanelDefinion.php functions.
451 * @param SugarBean $bean
452 * @return array The subpanel definitions.
454 private function getSubpanelDefs($bean )
456 if(empty($bean->module_dir)) {
460 $layout_defs = array();
462 if ( file_exists( 'modules/' . $bean->module_dir . '/metadata/subpaneldefs.php') )
463 require('modules/' . $bean->module_dir . '/metadata/subpaneldefs.php');
465 if ( file_exists( 'custom/modules/' . $bean->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php'))
466 require('custom/modules/' . $bean->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php');
468 return isset($layout_defs[$bean->module_dir]['subpanel_setup']) ? $layout_defs[$bean->module_dir]['subpanel_setup'] : $layout_defs;
472 * Rename all related linked within the application
474 * @return RenameModules
476 private function renameAllRelatedLinks()
480 foreach($beanList as $moduleName => $beanName)
482 if( class_exists($beanName) )
484 $this->renameModuleRelatedLinks($moduleName, $beanName);
488 $GLOBALS['log']->fatal("Class $beanName does not exist, unable to rename.");
496 * Rename the related links within a module.
498 * @param string $moduleName The module to be renamed
499 * @param string $moduleClass The class name of the module to be renamed
502 private function renameModuleRelatedLinks($moduleName, $moduleClass)
504 $GLOBALS['log']->info("Begining to renameModuleRelatedLinks for $moduleClass\n");
505 $tmp = new $moduleClass;
506 if( ! method_exists($tmp, 'get_related_fields') )
508 $GLOBALS['log']->info("Unable to resolve linked fields for module $moduleClass ");
512 $linkedFields = $tmp->get_related_fields();
513 $mod_strings = return_module_language($this->selectedLanguage, $moduleName);
514 $replacementStrings = array();
516 foreach($linkedFields as $link => $linkEntry)
518 //For each linked field check if the module referenced to is in our changed module list.
519 foreach($this->changedModules as $changedModuleName => $renameFields)
521 if( isset($linkEntry['module']) && $linkEntry['module'] == $changedModuleName)
523 $GLOBALS['log']->debug("Begining to rename for link field {$link}");
524 if( !isset($mod_strings[$linkEntry['vname']]) )
526 $GLOBALS['log']->debug("No label attribute for link $link, continuing.");
530 $replaceKey = $linkEntry['vname'];
531 $oldStringValue = $mod_strings[$replaceKey];
532 //At this point we don't know if we should replace the string with the plural or singular version of the new
533 //strings so we'll try both but with the plural version first since it should be longer than the singular.
534 $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_plural'], ENT_QUOTES), $renameFields['plural'], $oldStringValue);
535 if ($replacedString == $oldStringValue) {
536 $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_singular'], ENT_QUOTES), $renameFields['singular'], $replacedString);
538 $replacementStrings[$replaceKey] = $replacedString;
543 //Now we can write out the replaced language strings for each module
544 if(count($replacementStrings) > 0)
546 $GLOBALS['log']->debug("Writing out labels for link changes for module $moduleName, labels: " . var_export($replacementStrings,true));
547 ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
548 $this->renamedModules[$moduleName] = true;
553 * Clear all related language cache files.
557 private function clearLanguageCaches()
559 //remove the js language files
560 LanguageManager::removeJSLanguageFiles();
562 //remove lanugage cache files
563 LanguageManager::clearLanguageCache();
567 * Rename all module strings within the application for dashlets.
569 * @return RenameModules
571 private function renameAllDashlets()
573 //Load the Dashlet metadata so we know what needs to be changed
574 if(!is_file(sugar_cached('dashlets/dashlets.php')))
576 require_once('include/Dashlets/DashletCacheBuilder.php');
577 $dc = new DashletCacheBuilder();
581 include(sugar_cached('dashlets/dashlets.php'));
583 foreach($this->changedModules as $moduleName => $replacementLabels)
585 $this->changeModuleDashletStrings($moduleName, $replacementLabels, $dashletsFiles);
593 * Rename the title value for all dashlets associated with a particular module
596 private function changeModuleDashletStrings($moduleName, $replacementLabels, $dashletsFiles)
598 $GLOBALS['log']->debug("Beginning to change module dashlet labels for: $moduleName ");
599 $replacementStrings = array();
601 foreach($dashletsFiles as $dashletName => $dashletData)
603 if( isset($dashletData['module']) && $dashletData['module'] == $moduleName && file_exists($dashletData['meta']) )
605 require( $dashletData['meta'] );
606 $dashletTitle = $dashletMeta[$dashletName]['title'];
607 $currentModuleStrings = return_module_language($this->selectedLanguage, $moduleName);
608 $modStringKey = array_search($dashletTitle,$currentModuleStrings);
609 if($modStringKey !== FALSE)
611 $replacedString = str_replace(html_entity_decode_utf8($replacementLabels['prev_plural'], ENT_QUOTES), $replacementLabels['plural'], $dashletTitle);
612 if ($replacedString == $dashletTitle) {
613 $replacedString = str_replace(html_entity_decode_utf8($replacementLabels['prev_singular'], ENT_QUOTES), $replacementLabels['singular'], $replacedString);
615 $replacementStrings[$modStringKey] = $replacedString;
620 //Now we can write out the replaced language strings for each module
621 if(count($replacementStrings) > 0)
623 $GLOBALS['log']->debug("Writing out labels for dashlet changes for module $moduleName, labels: " . var_export($replacementStrings,true));
624 ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
630 * Rename all module strings within the application.
632 * @return RenameModules
634 private function changeAllModuleModStrings()
636 foreach($this->changedModules as $moduleName => $replacementLabels)
638 $this->changeModuleModStrings($moduleName, $replacementLabels);
645 * Rename all module strings within the leads module.
647 * @param string $targetModule The name of the module that owns the labels to be changed.
648 * @param array $labelKeysToReplace The labels to be changed.
649 * @return RenameModules
651 private function renameCertainModuleModStrings($targetModule, $labelKeysToReplace)
653 $GLOBALS['log']->debug("Beginning to rename labels for $targetModule module");
654 foreach($this->changedModules as $moduleName => $replacementLabels)
656 $this->changeCertainModuleModStrings($moduleName, $replacementLabels, $targetModule, $labelKeysToReplace);
663 * For a particular module, rename any relevant module strings that need to be replaced.
665 * @param string $moduleName The name of the module to be renamed.
666 * @param $replacementLabels
667 * @param string $targetModule The name of the module that owns the labels to be changed.
668 * @param array $labelKeysToReplace The labels to be changed.
671 private function changeCertainModuleModStrings($moduleName, $replacementLabels, $targetModule, $labelKeysToReplace)
673 $GLOBALS['log']->debug("Beginning to change module labels for : $moduleName");
674 $currentModuleStrings = return_module_language($this->selectedLanguage, $targetModule);
676 $replacedLabels = array();
677 foreach($labelKeysToReplace as $entry)
679 if (!isset($entry['source']) || $entry['source'] != $moduleName) {
680 // skip this entry if the source module does not match the module being renamed
684 $formattedLanguageKey = $this->formatModuleLanguageKey($entry['name'], $replacementLabels);
686 //If the static of dynamic key exists it should be replaced.
687 if( isset($currentModuleStrings[$formattedLanguageKey]) )
689 $oldStringValue = $currentModuleStrings[$formattedLanguageKey];
690 $newStringValue = $this->replaceSingleLabel($oldStringValue, $replacementLabels, $entry);
691 if ($oldStringValue != $newStringValue) {
692 $replacedLabels[$formattedLanguageKey] = $newStringValue;
698 ParserLabel::addLabels($this->selectedLanguage, $replacedLabels, $targetModule);
699 $this->renamedModules[$targetModule] = true;
704 * For a particular module, rename any relevant module strings that need to be replaced.
706 * @param string $moduleName The name of the module to be renamed.
707 * @param $replacementLabels
710 private function changeModuleModStrings($moduleName, $replacementLabels)
712 $GLOBALS['log']->info("Begining to change module labels for: $moduleName");
713 $currentModuleStrings = return_module_language($this->selectedLanguage, $moduleName);
714 $labelKeysToReplace = array(
715 array('name' => 'LNK_NEW_RECORD', 'type' => 'plural'), //Module built modules, Create <moduleName>
716 array('name' => 'LNK_LIST', 'type' => 'plural'), //Module built modules, View <moduleName>
717 array('name' => 'LNK_NEW_###MODULE_SINGULAR###', 'type' => 'singular'),
718 array('name' => 'LNK_###MODULE_SINGULAR###_LIST', 'type' => 'plural'),
719 array('name' => 'LNK_###MODULE_SINGULAR###_REPORTS', 'type' => 'singular'),
720 array('name' => 'LNK_IMPORT_VCARD', 'type' => 'singular'),
721 array('name' => 'LNK_IMPORT_###MODULE_PLURAL###', 'type' => 'plural'),
722 array('name' => 'MSG_SHOW_DUPLICATES', 'type' => 'singular', 'case' => 'both'),
723 array('name' => 'LBL_SAVE_###MODULE_SINGULAR###', 'type' => 'singular'),
724 array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular'), //Popup title
725 array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular'), //Popup title
728 $replacedLabels = array();
729 foreach($labelKeysToReplace as $entry)
731 $formattedLanguageKey = $this->formatModuleLanguageKey($entry['name'], $replacementLabels);
733 //If the static of dynamic key exists it should be replaced.
734 if( isset($currentModuleStrings[$formattedLanguageKey]) )
736 $oldStringValue = $currentModuleStrings[$formattedLanguageKey];
737 $replacedLabels[$formattedLanguageKey] = $this->replaceSingleLabel($oldStringValue, $replacementLabels, $entry);
738 if( isset($entry['case']) && $entry['case'] == 'both')
740 $replacedLabels[$formattedLanguageKey] = $this->replaceSingleLabel($replacedLabels[$formattedLanguageKey], $replacementLabels, $entry, 'strtolower');
746 ParserLabel::addLabels($this->selectedLanguage, $replacedLabels, $moduleName);
747 $this->renamedModules[$moduleName] = true;
751 * Format our dynamic keys containing module strings to a valid key depending on the module.
753 * @param string $unformatedKey
754 * @param string $replacementStrings
757 private function formatModuleLanguageKey($unformatedKey, $replacementStrings)
759 $unformatedKey = str_replace('###MODULE_SINGULAR###', strtoupper($replacementStrings['key_singular']), $unformatedKey);
760 return str_replace('###MODULE_PLURAL###', strtoupper($replacementStrings['key_plural']), $unformatedKey);
765 * Replace a label with a new value based on metadata which specifies the label as either singular or plural.
767 * @param string $oldStringValue
768 * @param string $replacementLabels
769 * @param array $replacementMetaData
772 private function replaceSingleLabel($oldStringValue, $replacementLabels, $replacementMetaData, $modifier = '')
774 $replaceKey = 'prev_' . $replacementMetaData['type'];
775 $search = html_entity_decode_utf8($replacementLabels[$replaceKey], ENT_QUOTES);
776 $replace = $replacementLabels[$replacementMetaData['type']];
777 if( !empty($modifier) )
779 $search = call_user_func($modifier, $search);
780 $replace = call_user_func($modifier, $replace);
784 // If nothing was replaced - try to replace original string
787 $result = str_replace($search, $replace, $oldStringValue, $replaceCount);
789 $replaceKey = 'key_' . $replacementMetaData['type'];
790 $search = html_entity_decode_utf8($replacementLabels[$replaceKey], ENT_QUOTES);
791 $result = str_replace($search, $replace, $oldStringValue, $replaceCount);
798 * Save changes to the module names to the app string entries for both the moduleList and moduleListSingular entries.
800 * @return RenameModules
802 private function changeAppStringEntries()
804 $GLOBALS['log']->debug('Begining to save app string entries');
805 //Save changes to the moduleList app string entry
806 DropDownHelper::saveDropDown($_REQUEST);
808 //Save changes to the moduleListSingular app string entry
809 $newParams = array();
810 $newParams['dropdown_name'] = 'moduleListSingular';
811 $newParams['dropdown_lang'] = isset($_REQUEST['dropdown_lang']) ? $_REQUEST['dropdown_lang'] : '';
812 $newParams['use_push'] = true;
813 DropDownHelper::saveDropDown($this->createModuleListSingularPackage($newParams, $this->changedModules));
815 //Save changes to the parent_type_display app_list_strings entry
816 global $app_list_strings;
817 $cur_app_list_strings = $app_list_strings;
818 foreach ($this->changedModules as $moduleName => $package) {
820 // only change if it exists
821 foreach ($cur_app_list_strings['parent_type_display'] as $moduleName2 => $parentDispName) {
822 if ($moduleName == $moduleName2) {
828 $newParams['dropdown_name'] = 'parent_type_display';
829 DropDownHelper::saveDropDown($this->createModuleListSingularPackage($newParams, array($moduleName => $this->changedModules[$moduleName])));
836 * Create an array entry that can be passed to the DropDownHelper:saveDropDown function so we can re-utilize
839 * @param array $params
840 * @param array $changedModules
843 private function createModuleListSingularPackage($params, $changedModules)
846 foreach($changedModules as $moduleName => $package)
848 $singularString = $package['singular'];
850 $params['slot_' . $count] = $count;
851 $params['key_' . $count] = $moduleName;
852 $params['value_' . $count] = $singularString;
853 $params['delete_' . $count] = '';
863 * Determine which modules have been updated and return an array with the module name as the key
864 * and the singular/plural entries as the value.
868 private function getChangedModules()
871 $allModuleEntries = array();
875 $selected_lang = (!empty($params['dropdown_lang'])?$params['dropdown_lang']:$_SESSION['authenticated_user_language']);
876 $current_app_list_string = return_app_list_strings_language($selected_lang);
878 while(isset($params['slot_' . $count]))
880 $index = $params['slot_' . $count];
881 $key = (isset($params['key_' . $index]))?to_html(remove_xss(from_html($params['key_' . $index]))): 'BLANK';
882 $value = (isset($params['value_' . $index]))?to_html(remove_xss(from_html($params['value_' . $index]))): '';
883 $svalue = (isset($params['svalue_' . $index]))?to_html(remove_xss(from_html($params['svalue_' . $index]))): $value;
888 $value = trim($value);
889 $svalue = trim($svalue);
891 //If the module key dne then do not continue with this rename.
892 if( isset($current_app_list_string['moduleList'][$key]) )
893 $allModuleEntries[$key] = array('s' => $svalue, 'p' => $value);
895 $_REQUEST['delete_' . $count] = TRUE;
902 foreach($allModuleEntries as $k => $e)
906 $prev_plural = $current_app_list_string['moduleList'][$k];
907 $prev_singular = isset($current_app_list_string['moduleListSingular'][$k]) ? $current_app_list_string['moduleListSingular'][$k] : $prev_plural;
908 if( strcmp($prev_plural, $pvalue) != 0 || (strcmp($prev_singular, $svalue) != 0) )
910 $results[$k] = array('singular' => $svalue, 'plural' => $pvalue, 'prev_singular' => $prev_singular, 'prev_plural' => $prev_plural,
911 'key_plural' => $k, 'key_singular' => $this->getModuleSingularKey($k)
922 * Return the 'singular' name of a module (Eg. Opportunity for Opportunities) given a moduleName which is a key
923 * in the app string moduleList array. If no entry is found, simply return the moduleName as this is consistant with modules
924 * built by moduleBuilder.
926 * @param string $moduleName
927 * @return string The 'singular' name of a module.
929 private function getModuleSingularKey($moduleName)
931 $className = isset($GLOBALS['beanList'][$moduleName]) ? $GLOBALS['beanList'][$moduleName] : null;
932 if( is_null($className) || ! class_exists($className) )
934 $GLOBALS['log']->error("Unable to get module singular key for class: $className");
938 $tmp = new $className();
939 if( property_exists($tmp, 'object_name') )
940 return $tmp->object_name;
946 * Return an array of the modules whos mod_strings have been modified.
950 public function getRenamedModules()
952 return $this->renamedModules;