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-2011 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'),
163 array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
164 array('name' => 'LBL_LIST_JOIN_MEETING', 'type' => 'singular', 'source' => 'Meetings'),
165 array('name' => 'LBL_PASSWORD', 'type' => 'singular', 'source' => 'Meetings'),
166 array('name' => 'LBL_TYPE', 'type' => 'singular', 'source' => 'Meetings'),
167 array('name' => 'LBL_URL', 'type' => 'singular', 'source' => 'Meetings'),
170 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
171 array('name' => 'LBL_CASE_ID', 'type' => 'singular', 'source' => 'Cases'),
172 array('name' => 'LBL_CONTACT_ID', 'type' => 'singular', 'source' => 'Contacts'),
173 array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
174 array('name' => 'LBL_LIST_CONTACT_NAME', 'type' => 'singular', 'source' => 'Contacts'),
175 array('name' => 'LBL_NOTE_STATUS', 'type' => 'singular', 'source' => 'Notes'),
176 array('name' => 'LBL_OPPORTUNITY_ID', 'type' => 'singular', 'source' => 'Opportunities'),
177 array('name' => 'LBL_PRODUCT_ID', 'type' => 'singular', 'source' => 'Products'),
178 array('name' => 'LBL_QUOTE_ID', 'type' => 'singular', 'source' => 'Quotes'),
180 'Opportunities' => array(
181 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
182 array('name' => 'LBL_AMOUNT', 'type' => 'singular', 'source' => 'Opportunities'),
183 array('name' => 'LBL_CAMPAIGN_OPPORTUNITY', 'type' => 'plural', 'source' => 'Campaigns'),
184 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contracts'),
185 array('name' => 'LBL_LEAD_SOURCE', 'type' => 'singular', 'source' => 'Leads'),
186 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
187 array('name' => 'LBL_OPPORTUNITY_NAME', 'type' => 'singular', 'source' => 'Opportunities'),
189 'ProductTemplates' => array(
190 array('name' => 'LBL_PRODUCT_ID', 'type' => 'singular', 'source' => 'Products'),
193 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
194 array('name' => 'LBL_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
195 array('name' => 'LBL_CONTACT_ID', 'type' => 'singular', 'source' => 'Contacts'),
196 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contacts'),
197 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
198 array('name' => 'LBL_LIST_NAME', 'type' => 'singular', 'source' => 'Products'),
199 array('name' => 'LBL_NAME', 'type' => 'singular', 'source' => 'Products'),
200 array('name' => 'LBL_QUOTE_ID', 'type' => 'singular', 'source' => 'Quotes'),
201 array('name' => 'LBL_RELATED_PRODUCTS', 'type' => 'plural', 'source' => 'Products'),
202 array('name' => 'LBL_URL', 'type' => 'singular', 'source' => 'Products'),
204 'ProjectTask' => array(
205 array('name' => 'LBL_PARENT_NAME', 'type' => 'singular', 'source' => 'Projects'),
206 array('name' => 'LBL_PROJECT_ID', 'type' => 'singular', 'source' => 'Projects'),
207 array('name' => 'LBL_PROJECT_NAME', 'type' => 'singular', 'source' => 'Projects'),
208 array('name' => 'LBL_PROJECT_TASK_ID', 'type' => 'singular', 'source' => 'Projects'),
211 array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural', 'source' => 'Bugs'),
212 array('name' => 'LBL_CONTACTS_RESOURCE', 'type' => 'singular', 'source' => 'Contacts'),
213 array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular', 'source' => 'Projects'),
214 array('name' => 'LBL_OPPORTUNITIES', 'type' => 'plural', 'source' => 'Opportunities'),
215 array('name' => 'LBL_PROJECT_HOLIDAYS_TITLE', 'type' => 'singular', 'source' => 'Projects'),
216 array('name' => 'LBL_PROJECT_TASKS_SUBPANEL_TITLE', 'type' => 'singular', 'source' => 'Projects'),
217 array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular', 'source' => 'Projects'),
218 array('name' => 'LNK_NEW_PROJECT', 'type' => 'singular', 'source' => 'Projects'),
219 array('name' => 'LNK_PROJECT_LIST', 'type' => 'singular', 'source' => 'Projects'),
222 array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular', 'source' => 'Accounts'),
223 array('name' => 'LBL_CONTRACTS', 'type' => 'plural', 'source' => 'Contracts'),
224 array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
225 array('name' => 'LBL_QUOTE_NUM', 'type' => 'singular', 'source' => 'Quotes'),
228 array('name' => 'LBL_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
229 array('name' => 'LBL_CAMPAIGN_ID', 'type' => 'plural', 'source' => 'Campaigns'),
230 array('name' => 'LBL_CAMPAIGN_LIST_SUBPANEL_TITLE', 'type' => 'singular', 'source' => 'Campaigns'),
231 array('name' => 'LBL_PROSPECT_LIST', 'type' => 'singular', 'source' => 'Prospects'),
234 array('name' => 'LBL_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
235 array('name' => 'LBL_CONTACT_ID', 'type' => 'singular', 'source' => 'Contacts'),
236 array('name' => 'LBL_CONTACT_PHONE', 'type' => 'singular', 'source' => 'Contacts'),
243 * @param string $options
246 public function process($options = '')
248 if($options == 'SaveDropDown')
256 * Main display function.
260 protected function display()
262 global $app_list_strings, $mod_strings;
265 require_once('modules/Studio/parsers/StudioParser.php');
266 $dh = new DropDownHelper();
268 $smarty = new Sugar_Smarty();
269 $smarty->assign('MOD', $GLOBALS['mod_strings']);
270 $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);
271 $smarty->assign('title', $title);
273 $selected_lang = (!empty($_REQUEST['dropdown_lang'])?$_REQUEST['dropdown_lang']:$_SESSION['authenticated_user_language']);
274 if(empty($selected_lang))
276 $selected_lang = $GLOBALS['sugar_config']['default_language'];
279 if($selected_lang == $GLOBALS['current_language'])
281 $my_list_strings = $GLOBALS['app_list_strings'];
285 $my_list_strings = return_app_list_strings_language($selected_lang);
288 $selected_dropdown = $my_list_strings['moduleList'];
289 $selected_dropdown_singular = $my_list_strings['moduleListSingular'];
292 foreach($selected_dropdown as $key=>$value)
294 $singularValue = isset($selected_dropdown_singular[$key]) ? $selected_dropdown_singular[$key] : $value;
295 if($selected_lang != $_SESSION['authenticated_user_language'] && !empty($app_list_strings['moduleList']) && isset($app_list_strings['moduleList'][$key]))
297 $selected_dropdown[$key]=array('lang'=>$value, 'user_lang'=> '['.$app_list_strings['moduleList'][$key] . ']', 'singular' => $singularValue);
301 $selected_dropdown[$key]=array('lang'=>$value, 'singular' => $singularValue);
306 $selected_dropdown = $dh->filterDropDown('moduleList', $selected_dropdown);
308 $smarty->assign('dropdown', $selected_dropdown);
309 $smarty->assign('dropdown_languages', get_languages());
312 $buttons[] = array('text'=>$mod_strings['LBL_BTN_UNDO'],'actionScript'=>"onclick='jstransaction.undo()'" );
313 $buttons[] = array('text'=>$mod_strings['LBL_BTN_REDO'],'actionScript'=>"onclick='jstransaction.redo()'" );
314 $buttons[] = array('text'=>$mod_strings['LBL_BTN_SAVE'],'actionScript'=>"onclick='if(check_form(\"editdropdown\")){document.editdropdown.submit();}'");
315 $buttonTxt = StudioParser::buildImageButtons($buttons);
316 $smarty->assign('buttons', $buttonTxt);
317 $smarty->assign('dropdown_lang', $selected_lang);
319 $editImage = SugarThemeRegistry::current()->getImage( 'edit_inline', '');
320 $smarty->assign('editImage',$editImage);
321 $deleteImage = SugarThemeRegistry::current()->getImage( 'delete_inline', '');
322 $smarty->assign('deleteImage',$deleteImage);
323 $smarty->display("modules/Studio/wizards/RenameModules.tpl");
327 * Save function responsible executing all sub-save functions required to rename a module.
331 public function save($redirect = TRUE)
333 $this->selectedLanguage = (!empty($_REQUEST['dropdown_lang'])? $_REQUEST['dropdown_lang']:$_SESSION['authenticated_user_language']);
335 //Clear all relevant language caches
336 $this->clearLanguageCaches();
338 //Retrieve changes the user is requesting and store previous values for future use.
339 $this->changedModules = $this->getChangedModules();
341 //Change module, appStrings, subpanels, and related links.
342 $this->changeAppStringEntries()->changeAllModuleModStrings()->renameAllRelatedLinks()->renameAllSubpanels()->renameAllDashlets();
344 foreach (self::$labelMap as $module=>$labelsArr) {
345 $this->renameCertainModuleModStrings($module, $labelsArr);
348 //Refresh the page again so module tabs are changed as the save process happens after module tabs are already generated.
350 SugarApplication::redirect('index.php?action=wizard&module=Studio&wizard=StudioWizard&option=RenameTabs');
354 * Rename all subpanels within the application.
357 * @return RenameModules
359 private function renameAllSubpanels()
363 foreach($beanList as $moduleName => $beanName)
365 if( class_exists($beanName) )
367 $this->renameModuleSubpanel($moduleName, $beanName, $this->changedModules);
371 $GLOBALS['log']->error("Class $beanName does not exist, unable to rename.");
380 * Rename subpanels for a particular module.
382 * @param string $moduleName The name of the module to be renamed
383 * @param string $beanName The name of the SugarBean to be renamed.
386 private function renameModuleSubpanel($moduleName, $beanName)
388 $GLOBALS['log']->info("About to rename subpanel for module: $moduleName");
389 $bean = new $beanName();
390 //Get the subpanel def
391 $subpanelDefs = $this->getSubpanelDefs($bean);
393 if( count($subpanelDefs) <= 0)
395 $GLOBALS['log']->debug("Found empty subpanel defs for $moduleName");
399 $mod_strings = return_module_language($this->selectedLanguage, $moduleName);
400 $replacementStrings = array();
402 //Iterate over all subpanel entries and see if we need to make a change.
403 foreach($subpanelDefs as $subpanelName => $subpanelMetaData)
405 $GLOBALS['log']->debug("Examining subpanel definition for potential rename: $subpanelName ");
406 //For each subpanel def, check if they are in our changed modules set.
407 foreach($this->changedModules as $changedModuleName => $renameFields)
409 if( !( isset($subpanelMetaData['type']) && $subpanelMetaData['type'] == 'collection') //Dont bother with collections
410 && isset($subpanelMetaData['module']) && $subpanelMetaData['module'] == $changedModuleName && isset($subpanelMetaData['title_key']) )
412 $replaceKey = $subpanelMetaData['title_key'];
413 if( !isset($mod_strings[$replaceKey]) )
415 $GLOBALS['log']->info("No module string entry defined for: {$mod_strings[$replaceKey]}");
418 $oldStringValue = $mod_strings[$replaceKey];
419 //At this point we don't know if we should replace the string with the plural or singular version of the new
420 //strings so we'll try both but with the plural version first since it should be longer than the singular.
421 // The saved old strings are html decoded, so we need to decode the new string first before str_replace.
422 $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_plural'], ENT_QUOTES), $renameFields['plural'], $oldStringValue);
423 if ($replacedString == $oldStringValue) {
424 // continue to replace singular only if nothing been replaced yet
425 $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_singular'], ENT_QUOTES), $renameFields['singular'], $replacedString);
427 $replacementStrings[$replaceKey] = $replacedString;
432 //Now we can write out the replaced language strings for each module
433 if(count($replacementStrings) > 0)
435 $GLOBALS['log']->debug("Writing out labels for subpanel changes for module $moduleName, labels: " . var_export($replacementStrings,true));
436 ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
437 $this->renamedModules[$moduleName] = true;
442 * Retrieve the subpanel definitions for a given SugarBean object. Unforunately we can't reuse
443 * any of the SubPanelDefinion.php functions.
445 * @param SugarBean $bean
446 * @return array The subpanel definitions.
448 private function getSubpanelDefs($bean )
450 if(empty($bean->module_dir)) {
454 $layout_defs = array();
456 if ( file_exists( 'modules/' . $bean->module_dir . '/metadata/subpaneldefs.php') )
457 require('modules/' . $bean->module_dir . '/metadata/subpaneldefs.php');
459 if ( file_exists( 'custom/modules/' . $bean->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php'))
460 require('custom/modules/' . $bean->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php');
462 return isset($layout_defs[$bean->module_dir]['subpanel_setup']) ? $layout_defs[$bean->module_dir]['subpanel_setup'] : $layout_defs;
466 * Rename all related linked within the application
468 * @return RenameModules
470 private function renameAllRelatedLinks()
474 foreach($beanList as $moduleName => $beanName)
476 if( class_exists($beanName) )
478 $this->renameModuleRelatedLinks($moduleName, $beanName);
482 $GLOBALS['log']->fatal("Class $beanName does not exist, unable to rename.");
490 * Rename the related links within a module.
492 * @param string $moduleName The module to be renamed
493 * @param string $moduleClass The class name of the module to be renamed
496 private function renameModuleRelatedLinks($moduleName, $moduleClass)
498 $GLOBALS['log']->info("Begining to renameModuleRelatedLinks for $moduleClass\n");
499 $tmp = new $moduleClass;
500 if( ! method_exists($tmp, 'get_related_fields') )
502 $GLOBALS['log']->info("Unable to resolve linked fields for module $moduleClass ");
506 $linkedFields = $tmp->get_related_fields();
507 $mod_strings = return_module_language($this->selectedLanguage, $moduleName);
508 $replacementStrings = array();
510 foreach($linkedFields as $link => $linkEntry)
512 //For each linked field check if the module referenced to is in our changed module list.
513 foreach($this->changedModules as $changedModuleName => $renameFields)
515 if( isset($linkEntry['module']) && $linkEntry['module'] == $changedModuleName)
517 $GLOBALS['log']->debug("Begining to rename for link field {$link}");
518 if( !isset($mod_strings[$linkEntry['vname']]) )
520 $GLOBALS['log']->debug("No label attribute for link $link, continuing.");
524 $replaceKey = $linkEntry['vname'];
525 $oldStringValue = $mod_strings[$replaceKey];
526 //At this point we don't know if we should replace the string with the plural or singular version of the new
527 //strings so we'll try both but with the plural version first since it should be longer than the singular.
528 $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_plural'], ENT_QUOTES), $renameFields['plural'], $oldStringValue);
529 if ($replacedString == $oldStringValue) {
530 $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_singular'], ENT_QUOTES), $renameFields['singular'], $replacedString);
532 $replacementStrings[$replaceKey] = $replacedString;
537 //Now we can write out the replaced language strings for each module
538 if(count($replacementStrings) > 0)
540 $GLOBALS['log']->debug("Writing out labels for link changes for module $moduleName, labels: " . var_export($replacementStrings,true));
541 ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
542 $this->renamedModules[$moduleName] = true;
547 * Clear all related language cache files.
551 private function clearLanguageCaches()
553 //remove the js language files
554 LanguageManager::removeJSLanguageFiles();
556 //remove lanugage cache files
557 LanguageManager::clearLanguageCache();
561 * Rename all module strings within the application for dashlets.
563 * @return RenameModules
565 private function renameAllDashlets()
567 //Load the Dashlet metadata so we know what needs to be changed
568 if(!is_file(sugar_cached('dashlets/dashlets.php')))
570 require_once('include/Dashlets/DashletCacheBuilder.php');
571 $dc = new DashletCacheBuilder();
574 require_once(sugar_cached('dashlets/dashlets.php'));
576 foreach($this->changedModules as $moduleName => $replacementLabels)
578 $this->changeModuleDashletStrings($moduleName, $replacementLabels, $dashletsFiles);
586 * Rename the title value for all dashlets associated with a particular module
589 private function changeModuleDashletStrings($moduleName, $replacementLabels, $dashletsFiles)
591 $GLOBALS['log']->debug("Beginning to change module dashlet labels for: $moduleName ");
592 $replacementStrings = array();
594 foreach($dashletsFiles as $dashletName => $dashletData)
596 if( isset($dashletData['module']) && $dashletData['module'] == $moduleName && file_exists($dashletData['meta']) )
598 require( $dashletData['meta'] );
599 $dashletTitle = $dashletMeta[$dashletName]['title'];
600 $currentModuleStrings = return_module_language($this->selectedLanguage, $moduleName);
601 $modStringKey = array_search($dashletTitle,$currentModuleStrings);
602 if($modStringKey !== FALSE)
604 $replacedString = str_replace(html_entity_decode_utf8($replacementLabels['prev_plural'], ENT_QUOTES), $replacementLabels['plural'], $dashletTitle);
605 if ($replacedString == $dashletTitle) {
606 $replacedString = str_replace(html_entity_decode_utf8($replacementLabels['prev_singular'], ENT_QUOTES), $replacementLabels['singular'], $replacedString);
608 $replacementStrings[$modStringKey] = $replacedString;
613 //Now we can write out the replaced language strings for each module
614 if(count($replacementStrings) > 0)
616 $GLOBALS['log']->debug("Writing out labels for dashlet changes for module $moduleName, labels: " . var_export($replacementStrings,true));
617 ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
623 * Rename all module strings within the application.
625 * @return RenameModules
627 private function changeAllModuleModStrings()
629 foreach($this->changedModules as $moduleName => $replacementLabels)
631 $this->changeModuleModStrings($moduleName, $replacementLabels);
638 * Rename all module strings within the leads module.
640 * @param string $targetModule The name of the module that owns the labels to be changed.
641 * @param array $labelKeysToReplace The labels to be changed.
642 * @return RenameModules
644 private function renameCertainModuleModStrings($targetModule, $labelKeysToReplace)
646 $GLOBALS['log']->debug("Beginning to rename labels for $targetModule module");
647 foreach($this->changedModules as $moduleName => $replacementLabels)
649 $this->changeCertainModuleModStrings($moduleName, $replacementLabels, $targetModule, $labelKeysToReplace);
656 * For a particular module, rename any relevant module strings that need to be replaced.
658 * @param string $moduleName The name of the module to be renamed.
659 * @param $replacementLabels
660 * @param string $targetModule The name of the module that owns the labels to be changed.
661 * @param array $labelKeysToReplace The labels to be changed.
664 private function changeCertainModuleModStrings($moduleName, $replacementLabels, $targetModule, $labelKeysToReplace)
666 $GLOBALS['log']->debug("Beginning to change module labels for : $moduleName");
667 $currentModuleStrings = return_module_language($this->selectedLanguage, $targetModule);
669 $replacedLabels = array();
670 foreach($labelKeysToReplace as $entry)
672 if (!isset($entry['source']) || $entry['source'] != $moduleName) {
673 // skip this entry if the source module does not match the module being renamed
677 $formattedLanguageKey = $this->formatModuleLanguageKey($entry['name'], $replacementLabels);
679 //If the static of dynamic key exists it should be replaced.
680 if( isset($currentModuleStrings[$formattedLanguageKey]) )
682 $oldStringValue = $currentModuleStrings[$formattedLanguageKey];
683 $newStringValue = $this->replaceSingleLabel($oldStringValue, $replacementLabels, $entry);
684 if ($oldStringValue != $newStringValue) {
685 $replacedLabels[$formattedLanguageKey] = $newStringValue;
691 ParserLabel::addLabels($this->selectedLanguage, $replacedLabels, $targetModule);
692 $this->renamedModules[$targetModule] = true;
697 * For a particular module, rename any relevant module strings that need to be replaced.
699 * @param string $moduleName The name of the module to be renamed.
700 * @param $replacementLabels
703 private function changeModuleModStrings($moduleName, $replacementLabels)
705 $GLOBALS['log']->info("Begining to change module labels for: $moduleName");
706 $currentModuleStrings = return_module_language($this->selectedLanguage, $moduleName);
707 $labelKeysToReplace = array(
708 array('name' => 'LNK_NEW_RECORD', 'type' => 'plural'), //Module built modules, Create <moduleName>
709 array('name' => 'LNK_LIST', 'type' => 'plural'), //Module built modules, View <moduleName>
710 array('name' => 'LNK_NEW_###MODULE_SINGULAR###', 'type' => 'singular'),
711 array('name' => 'LNK_###MODULE_SINGULAR###_LIST', 'type' => 'plural'),
712 array('name' => 'LNK_###MODULE_SINGULAR###_REPORTS', 'type' => 'singular'),
713 array('name' => 'LNK_IMPORT_VCARD', 'type' => 'singular'),
714 array('name' => 'LNK_IMPORT_###MODULE_PLURAL###', 'type' => 'plural'),
715 array('name' => 'MSG_SHOW_DUPLICATES', 'type' => 'singular', 'case' => 'both'),
716 array('name' => 'LBL_SAVE_###MODULE_SINGULAR###', 'type' => 'singular'),
717 array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular'), //Popup title
718 array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular'), //Popup title
721 $replacedLabels = array();
722 foreach($labelKeysToReplace as $entry)
724 $formattedLanguageKey = $this->formatModuleLanguageKey($entry['name'], $replacementLabels);
726 //If the static of dynamic key exists it should be replaced.
727 if( isset($currentModuleStrings[$formattedLanguageKey]) )
729 $oldStringValue = $currentModuleStrings[$formattedLanguageKey];
730 $replacedLabels[$formattedLanguageKey] = $this->replaceSingleLabel($oldStringValue, $replacementLabels, $entry);
731 if( isset($entry['case']) && $entry['case'] == 'both')
733 $replacedLabels[$formattedLanguageKey] = $this->replaceSingleLabel($replacedLabels[$formattedLanguageKey], $replacementLabels, $entry, 'strtolower');
739 ParserLabel::addLabels($this->selectedLanguage, $replacedLabels, $moduleName);
740 $this->renamedModules[$moduleName] = true;
744 * Format our dynamic keys containing module strings to a valid key depending on the module.
746 * @param string $unformatedKey
747 * @param string $replacementStrings
750 private function formatModuleLanguageKey($unformatedKey, $replacementStrings)
752 $unformatedKey = str_replace('###MODULE_SINGULAR###', strtoupper($replacementStrings['key_singular']), $unformatedKey);
753 return str_replace('###MODULE_PLURAL###', strtoupper($replacementStrings['key_plural']), $unformatedKey);
758 * Replace a label with a new value based on metadata which specifies the label as either singular or plural.
760 * @param string $oldStringValue
761 * @param string $replacementLabels
762 * @param array $replacementMetaData
765 private function replaceSingleLabel($oldStringValue, $replacementLabels, $replacementMetaData, $modifier = '')
767 $replaceKey = 'prev_' . $replacementMetaData['type'];
768 $search = html_entity_decode_utf8($replacementLabels[$replaceKey], ENT_QUOTES);
769 $replace = $replacementLabels[$replacementMetaData['type']];
770 if( !empty($modifier) )
772 $search = call_user_func($modifier, $search);
773 $replace = call_user_func($modifier, $replace);
776 return str_replace($search, $replace, $oldStringValue);
781 * Save changes to the module names to the app string entries for both the moduleList and moduleListSingular entries.
783 * @return RenameModules
785 private function changeAppStringEntries()
787 $GLOBALS['log']->debug('Begining to save app string entries');
788 //Save changes to the moduleList app string entry
789 DropDownHelper::saveDropDown($_REQUEST);
791 //Save changes to the moduleListSingular app string entry
792 $newParams = array();
793 $newParams['dropdown_name'] = 'moduleListSingular';
794 $newParams['dropdown_lang'] = isset($_REQUEST['dropdown_lang']) ? $_REQUEST['dropdown_lang'] : '';
795 $newParams['use_push'] = true;
796 DropDownHelper::saveDropDown($this->createModuleListSingularPackage($newParams, $this->changedModules));
798 //Save changes to the parent_type_display app_list_strings entry
799 global $app_list_strings;
800 $cur_app_list_strings = $app_list_strings;
801 foreach ($this->changedModules as $moduleName => $package) {
803 // only change if it exists
804 foreach ($cur_app_list_strings['parent_type_display'] as $moduleName2 => $parentDispName) {
805 if ($moduleName == $moduleName2) {
811 $newParams['dropdown_name'] = 'parent_type_display';
812 DropDownHelper::saveDropDown($this->createModuleListSingularPackage($newParams, array($moduleName => $this->changedModules[$moduleName])));
819 * Create an array entry that can be passed to the DropDownHelper:saveDropDown function so we can re-utilize
822 * @param array $params
823 * @param array $changedModules
826 private function createModuleListSingularPackage($params, $changedModules)
829 foreach($changedModules as $moduleName => $package)
831 $singularString = $package['singular'];
833 $params['slot_' . $count] = $count;
834 $params['key_' . $count] = $moduleName;
835 $params['value_' . $count] = $singularString;
836 $params['delete_' . $count] = '';
846 * Determine which modules have been updated and return an array with the module name as the key
847 * and the singular/plural entries as the value.
851 private function getChangedModules()
854 $allModuleEntries = array();
858 $selected_lang = (!empty($params['dropdown_lang'])?$params['dropdown_lang']:$_SESSION['authenticated_user_language']);
859 $current_app_list_string = return_app_list_strings_language($selected_lang);
861 while(isset($params['slot_' . $count]))
863 $index = $params['slot_' . $count];
864 $key = (isset($params['key_' . $index]))?to_html(remove_xss(from_html($params['key_' . $index]))): 'BLANK';
865 $value = (isset($params['value_' . $index]))?to_html(remove_xss(from_html($params['value_' . $index]))): '';
866 $svalue = (isset($params['svalue_' . $index]))?to_html(remove_xss(from_html($params['svalue_' . $index]))): $value;
871 $value = trim($value);
872 $svalue = trim($svalue);
874 //If the module key dne then do not continue with this rename.
875 if( isset($current_app_list_string['moduleList'][$key]) )
876 $allModuleEntries[$key] = array('s' => $svalue, 'p' => $value);
878 $_REQUEST['delete_' . $count] = TRUE;
885 foreach($allModuleEntries as $k => $e)
889 $prev_plural = $current_app_list_string['moduleList'][$k];
890 $prev_singular = isset($current_app_list_string['moduleListSingular'][$k]) ? $current_app_list_string['moduleListSingular'][$k] : $prev_plural;
891 if( strcmp($prev_plural, $pvalue) != 0 || (strcmp($prev_singular, $svalue) != 0) )
893 $results[$k] = array('singular' => $svalue, 'plural' => $pvalue, 'prev_singular' => $prev_singular, 'prev_plural' => $prev_plural,
894 'key_plural' => $k, 'key_singular' => $this->getModuleSingularKey($k)
905 * Return the 'singular' name of a module (Eg. Opportunity for Opportunities) given a moduleName which is a key
906 * in the app string moduleList array. If no entry is found, simply return the moduleName as this is consistant with modules
907 * built by moduleBuilder.
909 * @param string $moduleName
910 * @return string The 'singular' name of a module.
912 private function getModuleSingularKey($moduleName)
914 $className = isset($GLOBALS['beanList'][$moduleName]) ? $GLOBALS['beanList'][$moduleName] : null;
915 if( is_null($className) || ! class_exists($className) )
917 $GLOBALS['log']->error("Unable to get module singular key for class: $className");
921 $tmp = new $className();
922 if( property_exists($tmp, 'object_name') )
923 return $tmp->object_name;
929 * Return an array of the modules whos mod_strings have been modified.
933 public function getRenamedModules()
935 return $this->renamedModules;