]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Studio/wizards/RenameModules.php
Release 6.3.0beta4
[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-2011 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'),
73             array('name' => 'LBL_CAMPAIGN_ID', 'type' => 'singular'),
74             array('name' => 'LBL_PARENT_ACCOUNT_ID', 'type' => 'singular'),
75             array('name' => 'LBL_PROSPECT_LIST', 'type' => 'singular'),
76         ),
77         'Bugs' => array(
78             array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular'),
79             array('name' => 'LBL_LIST_MY_BUGS', 'type' => 'plural'),
80             array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular'),
81             array('name' => 'LNK_BUG_LIST', 'type' => 'plural'),
82             array('name' => 'LNK_BUG_REPORTS', 'type' => 'singular'),
83             array('name' => 'LNK_IMPORT_BUGS', 'type' => 'plural'),
84             array('name' => 'LNK_NEW_BUG', 'type' => 'singular'),
85         ),
86         'Calls' => array(
87             array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular'),
88         ),
89         'Campaigns' => array(
90             array('name' => 'LBL_ACCOUNTS', 'type' => 'plural'),
91             array('name' => 'LBL_CONTACTS', 'type' => 'plural'),
92             array('name' => 'LBL_LIST_CAMPAIGN_NAME', 'type' => 'singular'),
93             array('name' => 'LBL_LOG_ENTRIES_CONTACT_TITLE', 'type' => 'plural'),
94             array('name' => 'LBL_LOG_ENTRIES_LEAD_TITLE', 'type' => 'plural'),
95             array('name' => 'LBL_OPPORTUNITIES', 'type' => 'plural'),
96             array('name' => 'LBL_PROSPECT_LIST_SUBPANEL_TITLE', 'type' => 'singular'),
97         ),
98         'Cases' => array(
99             array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural'),
100             array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular'),
101         ),
102         'Contacts' => array(
103             array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural'),
104             array('name' => 'LBL_CAMPAIGN_LIST_SUBPANEL_TITLE', 'type' => 'plural'),
105             array('name' => 'LBL_CONTRACTS', 'type' => 'plural'),
106             array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular'),
107             array('name' => 'LBL_LEAD_SOURCE', 'type' => 'singular'),
108             array('name' => 'LBL_OPPORTUNITIES', 'type' => 'singular'),
109             array('name' => 'LBL_OPPORTUNITY_ROLE', 'type' => 'singular'),
110             array('name' => 'LBL_OPPORTUNITY_ROLE_ID', 'type' => 'singular'),
111             array('name' => 'LBL_PRODUCTS_TITLE', 'type' => 'plural'),
112             array('name' => 'LBL_PROSPECT_LIST', 'type' => 'singular'),
113         ),
114         'Contracts' => array(
115             array('name' => 'LBL_CONTRACT_NAME', 'type' => 'singular'),
116             array('name' => 'LBL_CONTRACT_TERM', 'type' => 'singular'),
117             array('name' => 'LBL_DOCUMENTS', 'type' => 'plural'),
118             array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular'),
119             array('name' => 'LBL_LIST_CONTRACT_NAME', 'type' => 'singular'),
120             array('name' => 'LBL_OPPORTUNITY', 'type' => 'singular'),
121             array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular'),
122             array('name' => 'LBL_TOTAL_CONTRACT_VALUE', 'type' => 'singular'),
123             array('name' => 'LBL_TOTAL_CONTRACT_VALUE_USDOLLAR', 'type' => 'singular'),
124             array('name' => 'LNK_NEW_CONTRACT', 'type' => 'singular'),
125         ),
126         'Documents' => array(
127             array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural'),
128             array('name' => 'LBL_CONTRACTS', 'type' => 'plural'),
129             array('name' => 'LBL_CONTRACT_NAME', 'type' => 'singular'),
130             array('name' => 'LBL_CONTRACT_STATUS', 'type' => 'singular'),
131             array('name' => 'LBL_DET_RELATED_DOCUMENT_VERSION', 'type' => 'singular'),
132             array('name' => 'LBL_DET_TEMPLATE_TYPE', 'type' => 'singular'),
133             array('name' => 'LBL_DOC_ID', 'type' => 'singular'),
134             array('name' => 'LBL_DOC_NAME', 'type' => 'singular'),
135             array('name' => 'LBL_DOC_REV_HEADER', 'type' => 'singular'),
136             array('name' => 'LBL_DOC_URL', 'type' => 'singular'),
137             array('name' => 'LBL_NAME', 'type' => 'singular'),
138             array('name' => 'LBL_TEMPLATE_TYPE', 'type' => 'singular'),
139         ),
140         'KBDocuments' => array(
141             array('name' => 'LBL_CASES', 'type' => 'plural'),
142             array('name' => 'LBL_CONTRACTS', 'type' => 'plural'),
143             array('name' => 'LBL_CONTRACT_NAME', 'type' => 'plural'),
144         ),
145         'Leads' => array(
146             array('name' => 'LNK_NEW_###MODULE_SINGULAR###', 'type' => 'singular'),
147             array('name' => 'LNK_SELECT_###MODULE_PLURAL###', 'type' => 'singular'),
148             array('name' => 'LNK_SELECT_###MODULE_SINGULAR###', 'type' => 'singular'),
149             array('name' => 'LBL_ACCOUNT_DESCRIPTION', 'type' => 'singular'),
150             array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular'),
151             array('name' => 'LBL_ACCOUNT_NAME', 'type' => 'singular'),
152             array('name' => 'LBL_CAMPAIGN_ID', 'type' => 'singular'),
153             array('name' => 'LBL_CAMPAIGN_LEAD', 'type' => 'plural'),
154             array('name' => 'LBL_CAMPAIGN_LIST_SUBPANEL_TITLE', 'type' => 'plural'),
155             array('name' => 'LBL_CONTACT_ID', 'type' => 'singular'),
156             array('name' => 'LBL_LEAD_SOURCE', 'type' => 'singular'),
157             array('name' => 'LBL_LEAD_SOURCE_DESCRIPTION', 'type' => 'singular'),
158             array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular'),
159             array('name' => 'LBL_OPPORTUNITY_AMOUNT', 'type' => 'singular'),
160             array('name' => 'LBL_OPPORTUNITY_ID', 'type' => 'singular'),
161             array('name' => 'LBL_OPPORTUNITY_NAME', 'type' => 'singular'),
162         ),
163         'Meetings' => array(
164             array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular'),
165             array('name' => 'LBL_LIST_JOIN_MEETING', 'type' => 'singular'),
166             array('name' => 'LBL_PASSWORD', 'type' => 'singular'),
167             array('name' => 'LBL_TYPE', 'type' => 'singular'),
168             array('name' => 'LBL_URL', 'type' => 'singular'),
169         ),
170         'Notes' => array(
171             array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular'),
172             array('name' => 'LBL_CASE_ID', 'type' => 'singular'),
173             array('name' => 'LBL_CONTACT_ID', 'type' => 'singular'),
174             array('name' => 'LBL_LIST_CONTACT', 'type' => 'singular'),
175             array('name' => 'LBL_LIST_CONTACT_NAME', 'type' => 'singular'),
176             array('name' => 'LBL_NOTE_STATUS', 'type' => 'singular'),
177             array('name' => 'LBL_OPPORTUNITY_ID', 'type' => 'singular'),
178             array('name' => 'LBL_PRODUCT_ID', 'type' => 'singular'),
179             array('name' => 'LBL_QUOTE_ID', 'type' => 'singular'),
180         ),
181         'Opportunities' => array(
182             array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular'),
183             array('name' => 'LBL_AMOUNT', 'type' => 'singular'),
184             array('name' => 'LBL_CAMPAIGN_OPPORTUNITY', 'type' => 'plural'),
185             array('name' => 'LBL_CONTRACTS', 'type' => 'plural'),
186             array('name' => 'LBL_LEAD_SOURCE', 'type' => 'singular'),
187             array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular'),
188             array('name' => 'LBL_OPPORTUNITY_NAME', 'type' => 'singular'),
189         ),
190         'ProductTemplates' => array(
191             array('name' => 'LBL_PRODUCT_ID', 'type' => 'singular'),
192         ),
193         'Products' => array(
194             array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular'),
195             array('name' => 'LBL_CONTACT', 'type' => 'singular'),
196             array('name' => 'LBL_CONTACT_ID', 'type' => 'singular'),
197             array('name' => 'LBL_CONTRACTS', 'type' => 'plural'),
198             array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular'),
199             array('name' => 'LBL_LIST_NAME', 'type' => 'singular'),
200             array('name' => 'LBL_NAME', 'type' => 'singular'),
201             array('name' => 'LBL_QUOTE_ID', 'type' => 'singular'),
202             array('name' => 'LBL_RELATED_PRODUCTS', 'type' => 'plural'),
203             array('name' => 'LBL_URL', 'type' => 'singular'),
204         ),
205         'ProjectTask' => array(
206             array('name' => 'LBL_PARENT_NAME', 'type' => 'singular'),
207             array('name' => 'LBL_PROJECT_ID', 'type' => 'singular'),
208             array('name' => 'LBL_PROJECT_NAME', 'type' => 'singular'),
209             array('name' => 'LBL_PROJECT_TASK_ID', 'type' => 'singular'),
210         ),
211         'Project' => array(
212             array('name' => 'LBL_BUGS_SUBPANEL_TITLE', 'type' => 'plural'),
213             array('name' => 'LBL_CONTACTS_RESOURCE', 'type' => 'singular'),
214             array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular'),
215             array('name' => 'LBL_OPPORTUNITIES', 'type' => 'plural'),
216             array('name' => 'LBL_PROJECT_HOLIDAYS_TITLE', 'type' => 'singular'),
217             array('name' => 'LBL_PROJECT_TASKS_SUBPANEL_TITLE', 'type' => 'singular'),
218             array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular'),
219             array('name' => 'LNK_NEW_PROJECT', 'type' => 'singular'),
220             array('name' => 'LNK_PROJECT_LIST', 'type' => 'singular'),
221         ),
222         'Quotes' => array(
223             array('name' => 'LBL_ACCOUNT_ID', 'type' => 'singular'),
224             array('name' => 'LBL_CONTRACTS', 'type' => 'plural'),
225             array('name' => 'LBL_LIST_ACCOUNT_NAME', 'type' => 'singular'),
226             array('name' => 'LBL_QUOTE_NUM', 'type' => 'singular'),
227         ),
228         'Targets' => array(
229             array('name' => 'LBL_ACCOUNT_NAME', 'type' => 'singular'),
230             array('name' => 'LBL_CAMPAIGN_ID', 'type' => 'plural'),
231             array('name' => 'LBL_CAMPAIGN_LIST_SUBPANEL_TITLE', 'type' => 'singular'),
232             array('name' => 'LBL_PROSPECT_LIST', 'type' => 'singular'),
233         ),
234         'Tasks' => array(
235             array('name' => 'LBL_CONTACT', 'type' => 'singular'),
236             array('name' => 'LBL_CONTACT_ID', 'type' => 'singular'),
237             array('name' => 'LBL_CONTACT_PHONE', 'type' => 'singular'),
238         ),
239     );
240
241
242     /**
243      *
244      * @param string $options
245      * @return void
246      */
247     public function process($options = '')
248     {
249         if($options == 'SaveDropDown')
250             $this->save();
251
252         $this->display();
253
254     }
255
256     /**
257      * Main display function.
258      *
259      * @return void
260      */
261     protected function display()
262     {
263         global $app_list_strings, $mod_strings;
264
265         
266         require_once('modules/Studio/parsers/StudioParser.php');
267         $dh = new DropDownHelper();
268         
269         $smarty = new Sugar_Smarty();
270         $smarty->assign('MOD', $GLOBALS['mod_strings']);
271         $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);
272         $smarty->assign('title', $title);
273
274         $selected_lang = (!empty($_REQUEST['dropdown_lang'])?$_REQUEST['dropdown_lang']:$_SESSION['authenticated_user_language']);
275         if(empty($selected_lang))
276         {
277             $selected_lang = $GLOBALS['sugar_config']['default_language'];
278         }
279
280         if($selected_lang == $GLOBALS['current_language'])
281         {
282             $my_list_strings = $GLOBALS['app_list_strings'];
283         }
284         else
285         {
286             $my_list_strings = return_app_list_strings_language($selected_lang);
287         }
288
289         $selected_dropdown = $my_list_strings['moduleList'];
290         $selected_dropdown_singular = $my_list_strings['moduleListSingular'];
291
292
293         foreach($selected_dropdown as $key=>$value)
294         {
295            $singularValue = isset($selected_dropdown_singular[$key]) ? $selected_dropdown_singular[$key] : $value;
296            if($selected_lang != $_SESSION['authenticated_user_language'] && !empty($app_list_strings['moduleList']) && isset($app_list_strings['moduleList'][$key]))
297            {
298                 $selected_dropdown[$key]=array('lang'=>$value, 'user_lang'=> '['.$app_list_strings['moduleList'][$key] . ']', 'singular' => $singularValue);
299            }
300            else
301            {
302                $selected_dropdown[$key]=array('lang'=>$value, 'singular' => $singularValue);
303            }
304         }
305
306
307         $selected_dropdown = $dh->filterDropDown('moduleList', $selected_dropdown);
308
309         $smarty->assign('dropdown', $selected_dropdown);
310         $smarty->assign('dropdown_languages', get_languages());
311
312         $buttons = array();
313         $buttons[] = array('text'=>$mod_strings['LBL_BTN_UNDO'],'actionScript'=>"onclick='jstransaction.undo()'" );
314         $buttons[] = array('text'=>$mod_strings['LBL_BTN_REDO'],'actionScript'=>"onclick='jstransaction.redo()'" );
315         $buttons[] = array('text'=>$mod_strings['LBL_BTN_SAVE'],'actionScript'=>"onclick='if(check_form(\"editdropdown\")){document.editdropdown.submit();}'");
316         $buttonTxt = StudioParser::buildImageButtons($buttons);
317         $smarty->assign('buttons', $buttonTxt);
318         $smarty->assign('dropdown_lang', $selected_lang);
319
320         $editImage = SugarThemeRegistry::current()->getImage( 'edit_inline', '');
321         $smarty->assign('editImage',$editImage);
322         $deleteImage = SugarThemeRegistry::current()->getImage( 'delete_inline', '');
323         $smarty->assign('deleteImage',$deleteImage);
324         $smarty->display("modules/Studio/wizards/RenameModules.tpl");
325     }
326
327     /**
328      * Save function responsible executing all sub-save functions required to rename a module.
329      *
330      * @return void
331      */
332     public function save($redirect = TRUE)
333     {
334         $this->selectedLanguage = (!empty($_REQUEST['dropdown_lang'])? $_REQUEST['dropdown_lang']:$_SESSION['authenticated_user_language']);
335
336         //Clear all relevant language caches
337         $this->clearLanguageCaches();
338         
339         //Retrieve changes the user is requesting and store previous values for future use.
340         $this->changedModules = $this->getChangedModules();
341
342         //Change module, appStrings, subpanels, and related links.
343         $this->changeAppStringEntries()->changeAllModuleModStrings()->renameAllRelatedLinks()->renameAllSubpanels()->renameAllDashlets();
344
345         foreach (self::$labelMap as $module=>$labelsArr) {
346             $this->renameCertainModuleModStrings($module, $labelsArr);
347         }
348
349         //Refresh the page again so module tabs are changed as the save process happens after module tabs are already generated.
350         if($redirect)
351             SugarApplication::redirect('index.php?action=wizard&module=Studio&wizard=StudioWizard&option=RenameTabs');
352     }
353
354     /**
355      * Rename all subpanels within the application.
356      *
357      *
358      * @return RenameModules
359      */
360     private function renameAllSubpanels()
361     {
362         global $beanList;
363
364         foreach($beanList as $moduleName => $beanName)
365         {
366             if( class_exists($beanName) )
367             {
368                 $this->renameModuleSubpanel($moduleName, $beanName, $this->changedModules);
369             }
370             else
371             {
372                 $GLOBALS['log']->error("Class $beanName does not exist, unable to rename.");
373             }
374         }
375
376         return $this;
377
378     }
379
380     /**
381      * Rename subpanels for a particular module.
382      *
383      * @param  string $moduleName The name of the module to be renamed
384      * @param  string $beanName  The name of the SugarBean to be renamed.
385      * @return void
386      */
387     private function renameModuleSubpanel($moduleName, $beanName)
388     {
389         $GLOBALS['log']->info("About to rename subpanel for module: $moduleName");
390         $bean = new $beanName();
391         //Get the subpanel def
392         $subpanelDefs = $this->getSubpanelDefs($bean);
393
394         if( count($subpanelDefs) <= 0)
395         {
396             $GLOBALS['log']->debug("Found empty subpanel defs for $moduleName");
397             return;
398         }
399
400         $mod_strings = return_module_language($this->selectedLanguage, $moduleName);
401         $replacementStrings = array();
402
403         //Iterate over all subpanel entries and see if we need to make a change.
404         foreach($subpanelDefs as $subpanelName => $subpanelMetaData)
405         {
406             $GLOBALS['log']->debug("Examining subpanel definition for potential rename: $subpanelName ");
407             //For each subpanel def, check if they are in our changed modules set.
408             foreach($this->changedModules as $changedModuleName => $renameFields)
409             {
410                 if( !( isset($subpanelMetaData['type']) &&  $subpanelMetaData['type'] == 'collection') //Dont bother with collections
411                     && $subpanelMetaData['module'] == $changedModuleName && isset($subpanelMetaData['title_key']) )
412                 {
413                     $replaceKey = $subpanelMetaData['title_key'];
414                     if( !isset($mod_strings[$replaceKey]) )
415                     {
416                         $GLOBALS['log']->info("No module string entry defined for: {$mod_strings[$replaceKey]}");
417                         continue;
418                     }
419                     $oldStringValue = $mod_strings[$replaceKey];
420                     //At this point we don't know if we should replace the string with the plural or singular version of the new
421                     //strings so we'll try both but with the plural version first since it should be longer than the singular.
422                     $replacedString = str_replace($renameFields['prev_plural'], $renameFields['plural'], $oldStringValue);
423                     $replacedString = str_replace($renameFields['prev_singular'], $renameFields['singular'], $replacedString);
424                     $replacementStrings[$replaceKey] = $replacedString;
425                 }
426             }
427         }
428
429         //Now we can write out the replaced language strings for each module
430         if(count($replacementStrings) > 0)
431         {
432             $GLOBALS['log']->debug("Writing out labels for subpanel changes for module $moduleName, labels: " . var_export($replacementStrings,true));
433             ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
434             $this->renamedModules[$moduleName] = true;
435         }
436     }
437
438     /**
439      * Retrieve the subpanel definitions for a given SugarBean object. Unforunately we can't reuse
440      * any of the SubPanelDefinion.php functions.
441      *
442      * @param  SugarBean $bean
443      * @return array The subpanel definitions.
444      */
445     private function getSubpanelDefs($bean )
446         {
447
448                 $layout_defs = array();
449
450         if ( file_exists( 'modules/' . $bean->module_dir . '/metadata/subpaneldefs.php') )
451             require('modules/' . $bean->module_dir . '/metadata/subpaneldefs.php');
452
453         if ( file_exists( 'custom/modules/' . $bean->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php'))
454             require('custom/modules/' . $bean->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php');
455
456          return isset($layout_defs[$bean->module_dir]['subpanel_setup']) ? $layout_defs[$bean->module_dir]['subpanel_setup'] : $layout_defs;
457         }
458
459     /**
460      * Rename all related linked within the application
461      *
462      * @return RenameModules
463      */
464     private function renameAllRelatedLinks()
465     {
466         global $beanList;
467
468         foreach($beanList as $moduleName => $beanName)
469         {
470             if( class_exists($beanName) )
471             {
472                 $this->renameModuleRelatedLinks($moduleName, $beanName);
473             }
474             else
475             {
476                 $GLOBALS['log']->fatal("Class $beanName does not exist, unable to rename.");
477             }
478         }
479
480         return $this;
481     }
482
483     /**
484      * Rename the related links within a module.
485      *
486      * @param  string $moduleName The module to be renamed
487      * @param  string $moduleClass The class name of the module to be renamed
488      * @return void
489      */
490     private function renameModuleRelatedLinks($moduleName, $moduleClass)
491     {
492         $GLOBALS['log']->info("Begining to renameModuleRelatedLinks for $moduleClass\n");
493         $tmp = new $moduleClass;
494         if( ! method_exists($tmp, 'get_related_fields') )
495         {
496             $GLOBALS['log']->info("Unable to resolve linked fields for module $moduleClass ");
497             return;
498         }
499
500         $linkedFields = $tmp->get_related_fields();
501         $mod_strings = return_module_language($this->selectedLanguage, $moduleName);
502         $replacementStrings = array();
503
504         foreach($linkedFields as $link => $linkEntry)
505         {
506             //For each linked field check if the module referenced to is in our changed module list.
507             foreach($this->changedModules as $changedModuleName => $renameFields)
508             {
509                 if( isset($linkEntry['module']) && $linkEntry['module'] ==  $changedModuleName)
510                 {
511                     $GLOBALS['log']->debug("Begining to rename for link field {$link}");
512                     if( !isset($mod_strings[$linkEntry['vname']]) )
513                     {
514                         $GLOBALS['log']->debug("No label attribute for link $link, continuing.");
515                         continue;
516                     }
517
518                     $replaceKey = $linkEntry['vname'];
519                     $oldStringValue = $mod_strings[$replaceKey];
520                     //At this point we don't know if we should replace the string with the plural or singular version of the new
521                     //strings so we'll try both but with the plural version first since it should be longer than the singular.
522                     $replacedString = str_replace($renameFields['prev_plural'], $renameFields['plural'], $oldStringValue);
523                     $replacedString = str_replace($renameFields['prev_singular'], $renameFields['singular'], $replacedString);
524                     $replacementStrings[$replaceKey] = $replacedString;
525                 }
526             }
527         }
528
529         //Now we can write out the replaced language strings for each module
530         if(count($replacementStrings) > 0)
531         {
532             $GLOBALS['log']->debug("Writing out labels for link changes for module $moduleName, labels: " . var_export($replacementStrings,true));
533             ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
534             $this->renamedModules[$moduleName] = true;
535         }
536     }
537
538     /**
539      * Clear all related language cache files.
540      *
541      * @return void
542      */
543     private function clearLanguageCaches()
544     {
545         //remove the js language files
546         LanguageManager::removeJSLanguageFiles();
547
548         //remove lanugage cache files
549         LanguageManager::clearLanguageCache();
550     }
551
552     /**
553      * Rename all module strings within the application for dashlets.
554      *
555      * @return RenameModules
556      */
557     private function renameAllDashlets()
558     {
559         //Load the Dashlet metadata so we know what needs to be changed
560         if(!is_file($GLOBALS['sugar_config']['cache_dir'].'dashlets/dashlets.php'))
561         {
562             require_once('include/Dashlets/DashletCacheBuilder.php');
563             $dc = new DashletCacheBuilder();
564             $dc->buildCache();
565                 }
566                 require_once($GLOBALS['sugar_config']['cache_dir'].'dashlets/dashlets.php');
567
568         foreach($this->changedModules as $moduleName => $replacementLabels)
569         {
570             $this->changeModuleDashletStrings($moduleName, $replacementLabels, $dashletsFiles);
571         }
572
573         return $this;
574
575     }
576
577     /*
578      * Rename the title value for all dashlets associated with a particular module
579      *
580      */
581     private function changeModuleDashletStrings($moduleName, $replacementLabels, $dashletsFiles)
582     {
583         $GLOBALS['log']->debug("Beginning to change module dashlet labels for: $moduleName ");
584         $replacementStrings = array();
585
586         foreach($dashletsFiles as $dashletName => $dashletData)
587         {
588             if( isset($dashletData['module']) && $dashletData['module'] == $moduleName && file_exists($dashletData['meta']) )
589             {
590                 require_once( $dashletData['meta'] );
591                 $dashletTitle = $dashletMeta[$dashletName]['title'];
592                 $currentModuleStrings = return_module_language($this->selectedLanguage, $moduleName);
593                 $modStringKey = array_search($dashletTitle,$currentModuleStrings);
594                 if($modStringKey !== FALSE)
595                 {
596                     $replacedString = str_replace($replacementLabels['prev_plural'], $replacementLabels['plural'], $dashletTitle);
597                     $replacedString = str_replace($replacementLabels['prev_singular'], $replacementLabels['singular'], $replacedString);
598                     $replacementStrings[$modStringKey] = $replacedString;
599                 }
600             }
601         }
602
603         //Now we can write out the replaced language strings for each module
604         if(count($replacementStrings) > 0)
605         {
606             $GLOBALS['log']->debug("Writing out labels for dashlet changes for module $moduleName, labels: " . var_export($replacementStrings,true));
607             ParserLabel::addLabels($this->selectedLanguage, $replacementStrings, $moduleName);
608         }
609     }
610
611
612     /**
613      * Rename all module strings within the application.
614      *
615      * @return RenameModules
616      */
617     private function changeAllModuleModStrings()
618     {
619         foreach($this->changedModules as $moduleName => $replacementLabels)
620         {
621             $this->changeModuleModStrings($moduleName, $replacementLabels);
622         }
623
624         return $this;
625     }
626
627     /**
628       * Rename all module strings within the leads module.
629       *
630       * @param  string $targetModule The name of the module that owns the labels to be changed.
631       * @param  array $labelKeysToReplace The labels to be changed.
632       * @return RenameModules
633       */
634      private function renameCertainModuleModStrings($targetModule, $labelKeysToReplace)
635      {
636          $GLOBALS['log']->debug("Beginning to rename labels for $targetModule module");
637          foreach($this->changedModules as $moduleName => $replacementLabels)
638          {
639              $this->changeCertainModuleModStrings($moduleName, $replacementLabels, $targetModule, $labelKeysToReplace);
640          }
641
642          return $this;
643      }
644
645     /**
646      * For a particular module, rename any relevant module strings that need to be replaced.
647      *
648      * @param  string $moduleName The name of the module to be renamed.
649      * @param  $replacementLabels
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 void
653      */
654     private function changeCertainModuleModStrings($moduleName, $replacementLabels, $targetModule, $labelKeysToReplace)
655     {
656         $GLOBALS['log']->debug("Beginning to change module labels for : $moduleName");
657         $currentModuleStrings = return_module_language($this->selectedLanguage, $targetModule);
658
659         $replacedLabels = array();
660         foreach($labelKeysToReplace as $entry)
661         {
662             $formattedLanguageKey = $this->formatModuleLanguageKey($entry['name'], $replacementLabels);
663
664             //If the static of dynamic key exists it should be replaced.
665             if( isset($currentModuleStrings[$formattedLanguageKey]) )
666             {
667                 $oldStringValue = $currentModuleStrings[$formattedLanguageKey];
668                 $newStringValue = $this->replaceSingleLabel($oldStringValue, $replacementLabels, $entry);
669                 if ($oldStringValue != $newStringValue) {
670                     $replacedLabels[$formattedLanguageKey] = $newStringValue;
671                 }
672             }
673         }
674
675         //Save all entries
676         ParserLabel::addLabels($this->selectedLanguage, $replacedLabels, $targetModule);
677         $this->renamedModules[$targetModule] = true;
678     }
679
680
681     /**
682      * For a particular module, rename any relevant module strings that need to be replaced.
683      *
684      * @param  string $moduleName The name of the module to be renamed.
685      * @param  $replacementLabels
686      * @return void
687      */
688     private function changeModuleModStrings($moduleName, $replacementLabels)
689     {
690         $GLOBALS['log']->info("Begining to change module labels for: $moduleName");
691         $currentModuleStrings = return_module_language($this->selectedLanguage, $moduleName);
692         $labelKeysToReplace = array(
693             array('name' => 'LNK_NEW_RECORD', 'type' => 'plural'), //Module built modules, Create <moduleName>
694             array('name' => 'LNK_LIST', 'type' => 'plural'), //Module built modules, View <moduleName>
695             array('name' => 'LNK_NEW_###MODULE_SINGULAR###', 'type' => 'singular'),
696             array('name' => 'LNK_###MODULE_SINGULAR###_LIST', 'type' => 'plural'),
697             array('name' => 'LNK_###MODULE_SINGULAR###_REPORTS', 'type' => 'singular'),
698             array('name' => 'LNK_IMPORT_VCARD', 'type' => 'singular'),
699             array('name' => 'LNK_IMPORT_###MODULE_PLURAL###', 'type' => 'plural'),
700             array('name' => 'LBL_LIST_FORM_TITLE', 'type' => 'singular'), //Popup title
701             array('name' => 'LBL_SEARCH_FORM_TITLE', 'type' => 'singular'), //Popup title
702             array('name' => 'LBL_HOMEPAGE_TITLE', 'type' => 'plural'),
703         );
704
705         $replacedLabels = array();
706         foreach($labelKeysToReplace as $entry)
707         {
708             $formattedLanguageKey = $this->formatModuleLanguageKey($entry['name'], $replacementLabels);
709
710             //If the static of dynamic key exists it should be replaced.
711             if( isset($currentModuleStrings[$formattedLanguageKey]) )
712             {
713                 $oldStringValue = $currentModuleStrings[$formattedLanguageKey];
714                 $replacedLabels[$formattedLanguageKey] = $this->replaceSingleLabel($oldStringValue, $replacementLabels, $entry);
715             }
716         }
717
718         //Save all entries
719         ParserLabel::addLabels($this->selectedLanguage, $replacedLabels, $moduleName);
720         $this->renamedModules[$moduleName] = true;
721     }
722
723     /**
724      * Format our dynamic keys containing module strings to a valid key depending on the module.
725      *
726      * @param  string $unformatedKey
727      * @param  string $replacementStrings
728      * @return string
729      */
730     private function formatModuleLanguageKey($unformatedKey, $replacementStrings)
731     {
732         $unformatedKey = str_replace('###MODULE_SINGULAR###', strtoupper($replacementStrings['key_singular']), $unformatedKey);
733         return str_replace('###MODULE_PLURAL###', strtoupper($replacementStrings['key_plural']), $unformatedKey);
734
735     }
736
737     /**
738      * Replace a label with a new value based on metadata which specifies the label as either singular or plural.
739      *
740      * @param  string $oldStringValue
741      * @param  string $replacementLabels
742      * @param  array $replacementMetaData
743      * @return string
744      */
745     private function replaceSingleLabel($oldStringValue, $replacementLabels, $replacementMetaData)
746     {
747         $replaceKey = 'prev_' . $replacementMetaData['type'];
748         return str_replace($replacementLabels[$replaceKey] , $replacementLabels[$replacementMetaData['type']], $oldStringValue);
749     }
750
751
752     /**
753      * Save changes to the module names to the app string entries for both the moduleList and moduleListSingular entries.
754      *
755      * @return RenameModules
756      */
757     private function changeAppStringEntries()
758     {
759         $GLOBALS['log']->debug('Begining to save app string entries');
760         //Save changes to the moduleList app string entry
761         DropDownHelper::saveDropDown($_REQUEST);
762
763         //Save changes to the moduleListSingular app string entry
764         $newParams = array();
765         $newParams['dropdown_name'] = 'moduleListSingular';
766         $newParams['dropdown_lang'] = isset($_REQUEST['dropdown_lang']) ? $_REQUEST['dropdown_lang'] : '';
767         $newParams['use_push'] = true;
768         DropDownHelper::saveDropDown($this->createModuleListSingularPackage($newParams, $this->changedModules));
769         return $this;
770     }
771
772     /**
773      * Create an array entry that can be passed to the DropDownHelper:saveDropDown function so we can re-utilize
774      * the save logic.
775      *
776      * @param  array $params
777      * @param  array $changedModules
778      * @return
779      */
780     private function createModuleListSingularPackage($params, $changedModules)
781     {
782         $count = 0;
783         foreach($changedModules as $moduleName => $package)
784         {
785             $singularString = $package['singular'];
786
787             $params['slot_' . $count] = $count;
788             $params['key_' . $count] = $moduleName;
789             $params['value_' . $count] = $singularString;
790             $params['delete_' . $count] = '';
791
792             $count++;
793         }
794
795         return $params;
796
797     }
798
799     /**
800      * Determine which modules have been updated and return an array with the module name as the key
801      * and the singular/plural entries as the value.
802      *
803      * @return array
804      */
805     private function getChangedModules()
806     {
807         $count = 0;
808         $allModuleEntries = array();
809         $results = array();
810         $params = $_REQUEST;
811
812         $selected_lang = (!empty($params['dropdown_lang'])?$params['dropdown_lang']:$_SESSION['authenticated_user_language']);
813         $current_app_list_string = return_app_list_strings_language($selected_lang);
814
815         while(isset($params['slot_' . $count]))
816         {
817             $index = $params['slot_' . $count];
818             $key = (isset($params['key_' . $index]))?remove_xss(from_html($params['key_' . $index])): 'BLANK';
819             $value = (isset($params['value_' . $index]))?remove_xss(from_html($params['value_' . $index])): '';
820             $svalue = (isset($params['svalue_' . $index]))?remove_xss(from_html($params['svalue_' . $index])): $value;
821             if($key == 'BLANK')
822                $key = '';
823
824             $key = trim($key);
825             $value = trim($value);
826             $svalue = trim($svalue);
827
828             //If the module key dne then do not continue with this rename.
829             if( isset($current_app_list_string['moduleList'][$key]) )
830                 $allModuleEntries[$key] = array('s' => $svalue, 'p' => $value);
831             else
832                 $_REQUEST['delete_' . $count] = TRUE;
833
834
835            $count++;
836         }
837
838
839         foreach($allModuleEntries as $k => $e)
840         {
841             $svalue = $e['s'];
842             $pvalue = $e['p'];
843             $prev_plural = $current_app_list_string['moduleList'][$k];
844             $prev_singular = isset($current_app_list_string['moduleListSingular'][$k]) ? $current_app_list_string['moduleListSingular'][$k] : $prev_plural;
845             if( strcmp($prev_plural, $pvalue) != 0 || (strcmp($prev_singular, $svalue) != 0) )
846             {
847                 $results[$k] = array('singular' => $svalue, 'plural' => $pvalue, 'prev_singular' => $prev_singular, 'prev_plural' => $prev_plural,
848                                      'key_plural' => $k, 'key_singular' => $this->getModuleSingularKey($k)
849                 );
850             }
851
852         }
853
854         return $results;
855     }
856
857
858     /**
859      * Return the 'singular' name of a module (Eg. Opportunity for Opportunities) given a moduleName which is a key
860      * in the app string moduleList array.  If no entry is found, simply return the moduleName as this is consistant with modules
861      * built by moduleBuilder.
862      *
863      * @param  string $moduleName
864      * @return string The 'singular' name of a module.
865      */
866     private function getModuleSingularKey($moduleName)
867     {
868         $className = isset($GLOBALS['beanList'][$moduleName]) ? $GLOBALS['beanList'][$moduleName] : null;
869         if( is_null($className) || ! class_exists($className) )
870         {
871             $GLOBALS['log']->error("Unable to get module singular key for class: $className");
872             return $moduleName;
873         }
874
875         $tmp = new $className();
876         if( property_exists($tmp, 'object_name') )
877             return $tmp->object_name;
878         else
879             return $moduleName;
880     }
881
882     /**
883      * Return an array of the modules whos mod_strings have been modified.
884      *
885      * @return array
886      */
887     public function getRenamedModules()
888     {
889         return $this->renamedModules;
890     }
891 }
892
893
894