]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Studio/wizards/RenameModules.php
Release 6.5.6
[Github/sugarcrm.git] / modules / Studio / wizards / RenameModules.php
1 <?php
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.
6  * 
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.
13  * 
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
17  * details.
18  * 
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
22  * 02110-1301 USA.
23  * 
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.
26  * 
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.
30  * 
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  ********************************************************************************/
37
38
39 require_once('modules/Studio/DropDowns/DropDownHelper.php');
40 require_once 'modules/ModuleBuilder/parsers/parser.label.php' ;
41
42 class RenameModules
43 {
44     /**
45      * Selected language user is renaming for (eg. en_us).
46      *
47      * @var string
48      */
49     private $selectedLanguage;
50
51     /**
52      * An array containing the modules which should be renamed.
53      *
54      * @var array
55      */
56     private $changedModules;
57
58     /**
59      * An array containing the modules which have had their module strings modified as part of the
60      * renaming process.
61      *
62      * @var array
63      */
64     private $renamedModules = array();
65
66
67     /**
68      * An array containing the modules and their labels to be changed when module is renamed.
69      */
70     private static $labelMap = array(
71         'Accounts' => 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'),
76         ),
77         'Bugs' => array(
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'),
85         ),
86         'Calls' => array(
87             array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular', 'source' => 'Contacts'),
88         ),
89         'Campaigns' => array(
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'),
97         ),
98         'Cases' => array(
99             array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural', 'source' => 'Bugs'),
100             array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular', 'source' => 'Accounts'),
101         ),
102         'Contacts' => array(
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'),
113         ),
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'),
125         ),
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'),
139         ),
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'),
144         ),
145         'Leads' => array(
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'),
166
167         ),
168         'Meetings' => array(
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'),
174         ),
175         'Notes' => array(
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'),
185         ),
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'),
194         ),
195         'ProductTemplates' => array(
196             array('name' => 'LBL_PRODUCT_ID', 'type' => 'singular', 'source' => 'Products'),
197         ),
198         'Products' => array(
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'),
209         ),
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'),
215         ),
216         'Project' => array(
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'),
226         ),
227         'Quotes' => array(
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'),
232         ),
233         'Targets' => array(
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'),
238         ),
239         'Tasks' => array(
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'),
243         ),
244     );
245
246
247     /**
248      *
249      * @param string $options
250      * @return void
251      */
252     public function process($options = '')
253     {
254         if($options == 'SaveDropDown')
255             $this->save();
256
257         $this->display();
258
259     }
260
261     /**
262      * Main display function.
263      *
264      * @return void
265      */
266     protected function display()
267     {
268         global $app_list_strings, $mod_strings;
269
270
271         require_once('modules/Studio/parsers/StudioParser.php');
272         $dh = new DropDownHelper();
273
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);
278
279         $selected_lang = (!empty($_REQUEST['dropdown_lang'])?$_REQUEST['dropdown_lang']:$_SESSION['authenticated_user_language']);
280         if(empty($selected_lang))
281         {
282             $selected_lang = $GLOBALS['sugar_config']['default_language'];
283         }
284
285         if($selected_lang == $GLOBALS['current_language'])
286         {
287             $my_list_strings = $GLOBALS['app_list_strings'];
288         }
289         else
290         {
291             $my_list_strings = return_app_list_strings_language($selected_lang);
292         }
293
294         $selected_dropdown = $my_list_strings['moduleList'];
295         $selected_dropdown_singular = $my_list_strings['moduleListSingular'];
296
297
298         foreach($selected_dropdown as $key=>$value)
299         {
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]))
302            {
303                 $selected_dropdown[$key]=array('lang'=>$value, 'user_lang'=> '['.$app_list_strings['moduleList'][$key] . ']', 'singular' => $singularValue);
304            }
305            else
306            {
307                $selected_dropdown[$key]=array('lang'=>$value, 'singular' => $singularValue);
308            }
309         }
310
311
312         $selected_dropdown = $dh->filterDropDown('moduleList', $selected_dropdown);
313
314         $smarty->assign('dropdown', $selected_dropdown);
315         $smarty->assign('dropdown_languages', get_languages());
316
317         $buttons = array();
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);
324
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");
330     }
331
332     /**
333      * Save function responsible executing all sub-save functions required to rename a module.
334      *
335      * @return void
336      */
337     public function save($redirect = TRUE)
338     {
339         $this->selectedLanguage = (!empty($_REQUEST['dropdown_lang'])? $_REQUEST['dropdown_lang']:$_SESSION['authenticated_user_language']);
340
341         //Clear all relevant language caches
342         $this->clearLanguageCaches();
343
344         //Retrieve changes the user is requesting and store previous values for future use.
345         $this->changedModules = $this->getChangedModules();
346
347         //Change module, appStrings, subpanels, and related links.
348         $this->changeAppStringEntries()->changeAllModuleModStrings()->renameAllRelatedLinks()->renameAllSubpanels()->renameAllDashlets();
349
350         foreach (self::$labelMap as $module=>$labelsArr) {
351             $this->renameCertainModuleModStrings($module, $labelsArr);
352         }
353
354         //Refresh the page again so module tabs are changed as the save process happens after module tabs are already generated.
355         if($redirect)
356             SugarApplication::redirect('index.php?action=wizard&module=Studio&wizard=StudioWizard&option=RenameTabs');
357     }
358
359     /**
360      * Rename all subpanels within the application.
361      *
362      *
363      * @return RenameModules
364      */
365     private function renameAllSubpanels()
366     {
367         global $beanList;
368
369         foreach($beanList as $moduleName => $beanName)
370         {
371             if( class_exists($beanName) )
372             {
373                 $this->renameModuleSubpanel($moduleName, $beanName, $this->changedModules);
374             }
375             else
376             {
377                 $GLOBALS['log']->error("Class $beanName does not exist, unable to rename.");
378             }
379         }
380
381         return $this;
382
383     }
384
385     /**
386      * Rename subpanels for a particular module.
387      *
388      * @param  string $moduleName The name of the module to be renamed
389      * @param  string $beanName  The name of the SugarBean to be renamed.
390      * @return void
391      */
392     private function renameModuleSubpanel($moduleName, $beanName)
393     {
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);
398
399         if(empty($subpanelDefs))
400         {
401             $GLOBALS['log']->debug("Found empty subpanel defs for $moduleName");
402             return;
403         }
404
405         $mod_strings = return_module_language($this->selectedLanguage, $moduleName);
406         $replacementStrings = array();
407
408         //Iterate over all subpanel entries and see if we need to make a change.
409         foreach($subpanelDefs as $subpanelName => $subpanelMetaData)
410         {
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)
414             {
415                 if( !( isset($subpanelMetaData['type']) &&  $subpanelMetaData['type'] == 'collection') //Dont bother with collections
416                     && isset($subpanelMetaData['module']) && $subpanelMetaData['module'] == $changedModuleName && isset($subpanelMetaData['title_key']) )
417                 {
418                     $replaceKey = $subpanelMetaData['title_key'];
419                     if( !isset($mod_strings[$replaceKey]) )
420                     {
421                         $GLOBALS['log']->info("No module string entry defined for: {$mod_strings[$replaceKey]}");
422                         continue;
423                     }
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);
432                     }
433                     $replacementStrings[$replaceKey] = $replacedString;
434                 }
435             }
436         }
437
438         //Now we can write out the replaced language strings for each module
439         if(count($replacementStrings) > 0)
440         {
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;
444         }
445     }
446
447     /**
448      * Retrieve the subpanel definitions for a given SugarBean object. Unforunately we can't reuse
449      * any of the SubPanelDefinion.php functions.
450      *
451      * @param  SugarBean $bean
452      * @return array The subpanel definitions.
453      */
454     private function getSubpanelDefs($bean )
455         {
456         if(empty($bean->module_dir)) {
457             return array();
458         }
459
460                 $layout_defs = array();
461
462         if ( file_exists( 'modules/' . $bean->module_dir . '/metadata/subpaneldefs.php') )
463             require('modules/' . $bean->module_dir . '/metadata/subpaneldefs.php');
464
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');
467
468          return isset($layout_defs[$bean->module_dir]['subpanel_setup']) ? $layout_defs[$bean->module_dir]['subpanel_setup'] : $layout_defs;
469         }
470
471     /**
472      * Rename all related linked within the application
473      *
474      * @return RenameModules
475      */
476     private function renameAllRelatedLinks()
477     {
478         global $beanList;
479
480         foreach($beanList as $moduleName => $beanName)
481         {
482             if( class_exists($beanName) )
483             {
484                 $this->renameModuleRelatedLinks($moduleName, $beanName);
485             }
486             else
487             {
488                 $GLOBALS['log']->fatal("Class $beanName does not exist, unable to rename.");
489             }
490         }
491
492         return $this;
493     }
494
495     /**
496      * Rename the related links within a module.
497      *
498      * @param  string $moduleName The module to be renamed
499      * @param  string $moduleClass The class name of the module to be renamed
500      * @return void
501      */
502     private function renameModuleRelatedLinks($moduleName, $moduleClass)
503     {
504         $GLOBALS['log']->info("Begining to renameModuleRelatedLinks for $moduleClass\n");
505         $tmp = new $moduleClass;
506         if( ! method_exists($tmp, 'get_related_fields') )
507         {
508             $GLOBALS['log']->info("Unable to resolve linked fields for module $moduleClass ");
509             return;
510         }
511
512         $linkedFields = $tmp->get_related_fields();
513         $mod_strings = return_module_language($this->selectedLanguage, $moduleName);
514         $replacementStrings = array();
515
516         foreach($linkedFields as $link => $linkEntry)
517         {
518             //For each linked field check if the module referenced to is in our changed module list.
519             foreach($this->changedModules as $changedModuleName => $renameFields)
520             {
521                 if( isset($linkEntry['module']) && $linkEntry['module'] ==  $changedModuleName)
522                 {
523                     $GLOBALS['log']->debug("Begining to rename for link field {$link}");
524                     if( !isset($mod_strings[$linkEntry['vname']]) )
525                     {
526                         $GLOBALS['log']->debug("No label attribute for link $link, continuing.");
527                         continue;
528                     }
529
530                     $replaceKey = $linkEntry['vname'];
531                     $oldStringValue = $mod_strings[$replaceKey];
532                    // Use the plural value of the two only if it's longer and the old language string contains it,
533                    // singular otherwise
534                     if (strlen($renameFields['prev_plural']) > strlen($renameFields['prev_singular']) && strpos($oldStringValue, $renameFields['prev_plural']) !== false) {
535                         $key = 'plural';
536                     } else {
537                        $key = 'singular';
538
539                     }
540                     $replacedString = str_replace(html_entity_decode_utf8($renameFields['prev_' . $key], ENT_QUOTES), $renameFields[$key], $oldStringValue);
541                     $replacementStrings[$replaceKey] = $replacedString;
542                 }
543             }
544         }
545
546         //Now we can write out the replaced language strings for each module
547         if(count($replacementStrings) > 0)
548         {
549             $GLOBALS['log']->debug("Writing out labels for link changes for module $moduleName, labels: " . var_export($replacementStrings,true));
550             ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
551             $this->renamedModules[$moduleName] = true;
552         }
553     }
554
555     /**
556      * Clear all related language cache files.
557      *
558      * @return void
559      */
560     private function clearLanguageCaches()
561     {
562         //remove the js language files
563         LanguageManager::removeJSLanguageFiles();
564
565         //remove lanugage cache files
566         LanguageManager::clearLanguageCache();
567     }
568
569     /**
570      * Rename all module strings within the application for dashlets.
571      *
572      * @return RenameModules
573      */
574     private function renameAllDashlets()
575     {
576         //Load the Dashlet metadata so we know what needs to be changed
577         if(!is_file(sugar_cached('dashlets/dashlets.php')))
578         {
579             require_once('include/Dashlets/DashletCacheBuilder.php');
580             $dc = new DashletCacheBuilder();
581             $dc->buildCache();
582         }
583
584         include(sugar_cached('dashlets/dashlets.php'));
585
586         foreach($this->changedModules as $moduleName => $replacementLabels)
587         {
588             $this->changeModuleDashletStrings($moduleName, $replacementLabels, $dashletsFiles);
589         }
590
591         return $this;
592
593     }
594
595     /*
596      * Rename the title value for all dashlets associated with a particular module
597      *
598      */
599     private function changeModuleDashletStrings($moduleName, $replacementLabels, $dashletsFiles)
600     {
601         $GLOBALS['log']->debug("Beginning to change module dashlet labels for: $moduleName ");
602         $replacementStrings = array();
603
604         foreach($dashletsFiles as $dashletName => $dashletData)
605         {
606             if( isset($dashletData['module']) && $dashletData['module'] == $moduleName && file_exists($dashletData['meta']) )
607             {
608                 require( $dashletData['meta'] );
609                 $dashletTitle = $dashletMeta[$dashletName]['title'];
610                 $currentModuleStrings = return_module_language($this->selectedLanguage, $moduleName);
611                 $modStringKey = array_search($dashletTitle,$currentModuleStrings);
612                 if($modStringKey !== FALSE)
613                 {
614                     $replacedString = str_replace(html_entity_decode_utf8($replacementLabels['prev_plural'], ENT_QUOTES), $replacementLabels['plural'], $dashletTitle);
615                     if ($replacedString == $dashletTitle) {
616                         $replacedString = str_replace(html_entity_decode_utf8($replacementLabels['prev_singular'], ENT_QUOTES), $replacementLabels['singular'], $replacedString);
617                     }
618                     $replacementStrings[$modStringKey] = $replacedString;
619                 }
620             }
621         }
622
623         //Now we can write out the replaced language strings for each module
624         if(count($replacementStrings) > 0)
625         {
626             $GLOBALS['log']->debug("Writing out labels for dashlet changes for module $moduleName, labels: " . var_export($replacementStrings,true));
627             ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
628         }
629     }
630
631
632     /**
633      * Rename all module strings within the application.
634      *
635      * @return RenameModules
636      */
637     private function changeAllModuleModStrings()
638     {
639         foreach($this->changedModules as $moduleName => $replacementLabels)
640         {
641             $this->changeModuleModStrings($moduleName, $replacementLabels);
642         }
643
644         return $this;
645     }
646
647     /**
648       * Rename all module strings within the leads module.
649       *
650       * @param  string $targetModule The name of the module that owns the labels to be changed.
651       * @param  array $labelKeysToReplace The labels to be changed.
652       * @return RenameModules
653       */
654      private function renameCertainModuleModStrings($targetModule, $labelKeysToReplace)
655      {
656          $GLOBALS['log']->debug("Beginning to rename labels for $targetModule module");
657          foreach($this->changedModules as $moduleName => $replacementLabels)
658          {
659              $this->changeCertainModuleModStrings($moduleName, $replacementLabels, $targetModule, $labelKeysToReplace);
660          }
661
662          return $this;
663      }
664
665     /**
666      * For a particular module, rename any relevant module strings that need to be replaced.
667      *
668      * @param  string $moduleName The name of the module to be renamed.
669      * @param  $replacementLabels
670      * @param  string $targetModule The name of the module that owns the labels to be changed.
671      * @param  array $labelKeysToReplace The labels to be changed.
672      * @return void
673      */
674     private function changeCertainModuleModStrings($moduleName, $replacementLabels, $targetModule, $labelKeysToReplace)
675     {
676         $GLOBALS['log']->debug("Beginning to change module labels for : $moduleName");
677         $currentModuleStrings = return_module_language($this->selectedLanguage, $targetModule);
678
679         $replacedLabels = array();
680         foreach($labelKeysToReplace as $entry)
681         {
682             if (!isset($entry['source']) || $entry['source'] != $moduleName) {
683                 // skip this entry if the source module does not match the module being renamed
684                 continue;
685             }
686
687             $formattedLanguageKey = $this->formatModuleLanguageKey($entry['name'], $replacementLabels);
688
689             //If the static of dynamic key exists it should be replaced.
690             if( isset($currentModuleStrings[$formattedLanguageKey]) )
691             {
692                 $oldStringValue = $currentModuleStrings[$formattedLanguageKey];
693                 $newStringValue = $this->replaceSingleLabel($oldStringValue, $replacementLabels, $entry);
694                 if ($oldStringValue != $newStringValue) {
695                     $replacedLabels[$formattedLanguageKey] = $newStringValue;
696                 }
697             }
698         }
699
700         //Save all entries
701         ParserLabel::addLabels($this->selectedLanguage, $replacedLabels, $targetModule);
702         $this->renamedModules[$targetModule] = true;
703     }
704
705
706     /**
707      * For a particular module, rename any relevant module strings that need to be replaced.
708      *
709      * @param  string $moduleName The name of the module to be renamed.
710      * @param  $replacementLabels
711      * @return void
712      */
713     private function changeModuleModStrings($moduleName, $replacementLabels)
714     {
715         $GLOBALS['log']->info("Begining to change module labels for: $moduleName");
716         $currentModuleStrings = return_module_language($this->selectedLanguage, $moduleName);
717         $labelKeysToReplace = array(
718             array('name' => 'LNK_NEW_RECORD', 'type' => 'plural'), //Module built modules, Create <moduleName>
719             array('name' => 'LNK_LIST', 'type' => 'plural'), //Module built modules, View <moduleName>
720             array('name' => 'LNK_NEW_###MODULE_SINGULAR###', 'type' => 'singular'),
721             array('name' => 'LNK_###MODULE_SINGULAR###_LIST', 'type' => 'plural'),
722             array('name' => 'LNK_###MODULE_SINGULAR###_REPORTS', 'type' => 'singular'),
723             array('name' => 'LNK_IMPORT_VCARD', 'type' => 'singular'),
724             array('name' => 'LNK_IMPORT_###MODULE_PLURAL###', 'type' => 'plural'),
725             array('name' => 'MSG_SHOW_DUPLICATES', 'type' => 'singular', 'case' => 'both'),
726             array('name' => 'LBL_SAVE_###MODULE_SINGULAR###', 'type' => 'singular'),
727             array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular'), //Popup title
728             array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular'), //Popup title
729         );
730
731         $replacedLabels = array();
732         foreach($labelKeysToReplace as $entry)
733         {
734             $formattedLanguageKey = $this->formatModuleLanguageKey($entry['name'], $replacementLabels);
735
736             //If the static of dynamic key exists it should be replaced.
737             if( isset($currentModuleStrings[$formattedLanguageKey]) )
738             {
739                 $oldStringValue = $currentModuleStrings[$formattedLanguageKey];
740                 $replacedLabels[$formattedLanguageKey] = $this->replaceSingleLabel($oldStringValue, $replacementLabels, $entry);
741                 if( isset($entry['case']) && $entry['case'] == 'both')
742                 {
743                     $replacedLabels[$formattedLanguageKey] = $this->replaceSingleLabel($replacedLabels[$formattedLanguageKey], $replacementLabels, $entry, 'strtolower');
744                 }
745             }
746         }
747
748         //Save all entries
749         ParserLabel::addLabels($this->selectedLanguage, $replacedLabels, $moduleName);
750         $this->renamedModules[$moduleName] = true;
751     }
752
753     /**
754      * Format our dynamic keys containing module strings to a valid key depending on the module.
755      *
756      * @param  string $unformatedKey
757      * @param  string $replacementStrings
758      * @return string
759      */
760     private function formatModuleLanguageKey($unformatedKey, $replacementStrings)
761     {
762         $unformatedKey = str_replace('###MODULE_SINGULAR###', strtoupper($replacementStrings['key_singular']), $unformatedKey);
763         return str_replace('###MODULE_PLURAL###', strtoupper($replacementStrings['key_plural']), $unformatedKey);
764
765     }
766
767     /**
768      * Replace a label with a new value based on metadata which specifies the label as either singular or plural.
769      *
770      * @param  string $oldStringValue
771      * @param  string $replacementLabels
772      * @param  array $replacementMetaData
773      * @return string
774      */
775     private function replaceSingleLabel($oldStringValue, $replacementLabels, $replacementMetaData, $modifier = '')
776     {
777         $replaceKey = 'prev_' . $replacementMetaData['type'];
778         $search = html_entity_decode_utf8($replacementLabels[$replaceKey], ENT_QUOTES);
779         $replace = $replacementLabels[$replacementMetaData['type']];
780         if( !empty($modifier) )
781         {
782             $search = call_user_func($modifier, $search);
783             $replace = call_user_func($modifier, $replace);
784         }
785         
786         // Bug 47957
787         // If nothing was replaced - try to replace original string
788         $result = '';
789         $replaceCount = 0;
790         $result = str_replace($search, $replace, $oldStringValue, $replaceCount);
791         if(!$replaceCount){
792             $replaceKey = 'key_' . $replacementMetaData['type'];
793             $search = html_entity_decode_utf8($replacementLabels[$replaceKey], ENT_QUOTES);
794             $result = str_replace($search, $replace, $oldStringValue, $replaceCount);
795         }
796         return $result;
797     }
798
799
800     /**
801      * Save changes to the module names to the app string entries for both the moduleList and moduleListSingular entries.
802      *
803      * @return RenameModules
804      */
805     private function changeAppStringEntries()
806     {
807         $GLOBALS['log']->debug('Begining to save app string entries');
808         //Save changes to the moduleList app string entry
809         DropDownHelper::saveDropDown($_REQUEST);
810
811         //Save changes to the moduleListSingular app string entry
812         $newParams = array();
813         $newParams['dropdown_name'] = 'moduleListSingular';
814         $newParams['dropdown_lang'] = isset($_REQUEST['dropdown_lang']) ? $_REQUEST['dropdown_lang'] : '';
815         $newParams['use_push'] = true;
816         DropDownHelper::saveDropDown($this->createModuleListSingularPackage($newParams, $this->changedModules));
817
818         //Save changes to the parent_type_display app_list_strings entry
819         global $app_list_strings;
820         $cur_app_list_strings = $app_list_strings;
821         foreach ($this->changedModules as $moduleName => $package) {
822             $found = false;
823             // only change if it exists
824             foreach ($cur_app_list_strings['parent_type_display'] as $moduleName2 => $parentDispName) {
825                 if ($moduleName == $moduleName2) {
826                     $found = true;
827                     break;
828                 }
829             }
830             if ($found) {
831                 $newParams['dropdown_name'] = 'parent_type_display';
832                 DropDownHelper::saveDropDown($this->createModuleListSingularPackage($newParams, array($moduleName => $this->changedModules[$moduleName])));
833             }
834         }
835         return $this;
836     }
837
838     /**
839      * Create an array entry that can be passed to the DropDownHelper:saveDropDown function so we can re-utilize
840      * the save logic.
841      *
842      * @param  array $params
843      * @param  array $changedModules
844      * @return
845      */
846     private function createModuleListSingularPackage($params, $changedModules)
847     {
848         $count = 0;
849         foreach($changedModules as $moduleName => $package)
850         {
851             $singularString = $package['singular'];
852
853             $params['slot_' . $count] = $count;
854             $params['key_' . $count] = $moduleName;
855             $params['value_' . $count] = $singularString;
856             $params['delete_' . $count] = '';
857
858             $count++;
859         }
860
861         return $params;
862
863     }
864
865     /**
866      * Determine which modules have been updated and return an array with the module name as the key
867      * and the singular/plural entries as the value.
868      *
869      * @return array
870      */
871     private function getChangedModules()
872     {
873         $count = 0;
874         $allModuleEntries = array();
875         $results = array();
876         $params = $_REQUEST;
877
878         $selected_lang = (!empty($params['dropdown_lang'])?$params['dropdown_lang']:$_SESSION['authenticated_user_language']);
879         $current_app_list_string = return_app_list_strings_language($selected_lang);
880
881         while(isset($params['slot_' . $count]))
882         {
883             $index = $params['slot_' . $count];
884
885             $key = (isset($params['key_' . $index]))?SugarCleaner::stripTags($params['key_' . $index]): 'BLANK';
886             $value = (isset($params['value_' . $index]))?SugarCleaner::stripTags($params['value_' . $index]): '';
887             $svalue = (isset($params['svalue_' . $index]))?SugarCleaner::stripTags($params['svalue_' . $index]): $value;
888             if($key == 'BLANK')
889                $key = '';
890
891             $key = trim($key);
892             $value = trim($value);
893             $svalue = trim($svalue);
894
895             //If the module key dne then do not continue with this rename.
896             if( isset($current_app_list_string['moduleList'][$key]) )
897                 $allModuleEntries[$key] = array('s' => $svalue, 'p' => $value);
898             else
899                 $_REQUEST['delete_' . $count] = TRUE;
900
901
902            $count++;
903         }
904
905
906         foreach($allModuleEntries as $k => $e)
907         {
908             $svalue = $e['s'];
909             $pvalue = $e['p'];
910             $prev_plural = $current_app_list_string['moduleList'][$k];
911             $prev_singular = isset($current_app_list_string['moduleListSingular'][$k]) ? $current_app_list_string['moduleListSingular'][$k] : $prev_plural;
912             if( strcmp($prev_plural, $pvalue) != 0 || (strcmp($prev_singular, $svalue) != 0) )
913             {
914                 $results[$k] = array('singular' => $svalue, 'plural' => $pvalue, 'prev_singular' => $prev_singular, 'prev_plural' => $prev_plural,
915                                      'key_plural' => $k, 'key_singular' => $this->getModuleSingularKey($k)
916                 );
917             }
918
919         }
920
921         return $results;
922     }
923
924
925     /**
926      * Return the 'singular' name of a module (Eg. Opportunity for Opportunities) given a moduleName which is a key
927      * in the app string moduleList array.  If no entry is found, simply return the moduleName as this is consistant with modules
928      * built by moduleBuilder.
929      *
930      * @param  string $moduleName
931      * @return string The 'singular' name of a module.
932      */
933     private function getModuleSingularKey($moduleName)
934     {
935         $className = isset($GLOBALS['beanList'][$moduleName]) ? $GLOBALS['beanList'][$moduleName] : null;
936         if( is_null($className) || ! class_exists($className) )
937         {
938             $GLOBALS['log']->error("Unable to get module singular key for class: $className");
939             return $moduleName;
940         }
941
942         $tmp = new $className();
943         if( property_exists($tmp, 'object_name') )
944             return $tmp->object_name;
945         else
946             return $moduleName;
947     }
948
949     /**
950      * Return an array of the modules whos mod_strings have been modified.
951      *
952      * @return array
953      */
954     public function getRenamedModules()
955     {
956         return $this->renamedModules;
957     }
958 }
959
960
961