]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/MVC/View/SugarView.php
Release 6.5.15
[Github/sugarcrm.git] / include / MVC / View / SugarView.php
1 <?php
2 /*********************************************************************************
3  * SugarCRM Community Edition is a customer relationship management program developed by
4  * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
5  * 
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  * 
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
16  * details.
17  * 
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  * 
23  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
25  * 
26  * The interactive user interfaces in modified source and object code versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  * 
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the "Powered by
32  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33  * technical reasons, the Appropriate Legal Notices must display the words
34  * "Powered by SugarCRM".
35  ********************************************************************************/
36
37 /**
38  * Base Sugar view
39  * @api
40  */
41 class SugarView
42 {
43     /**
44      * This array is meant to hold an objects/data that we would like to pass between
45      * the controller and the view.  The bean will automatically be set for us, but this
46      * is meant to hold anything else.
47      */
48     var $view_object_map = array();
49     /**
50      * The name of the current module.
51      */
52     var $module = '';
53     /**
54      * The name of the current action.
55      */
56     var $action = '';
57     /**
58      */
59     var $bean = null;
60     /**
61      * @var Sugar_Smarty
62      * Sugar_Smarty. This is useful if you have a view and a subview you can
63      * share the same smarty object.
64      */
65     var $ss = null;
66     /**
67      * Any errors that occured this can either be set by the view or the controller or the model
68      */
69     var $errors = array();
70     /**
71      * Set to true if you do not want to display errors from SugarView::displayErrors(); instead they will be returned
72      */
73     var $suppressDisplayErrors = false;
74
75     /**
76      * Options for what UI elements to hide/show/
77      */
78     var $options = array('show_header' => true, 'show_title' => true, 'show_subpanels' => false, 'show_search' => true, 'show_footer' => true, 'show_javascript' => true, 'view_print' => false,);
79     var $type = null;
80     var $responseTime;
81     var $fileResources;
82
83     /**
84      * Constructor which will peform the setup.
85      */
86     public function SugarView(
87         $bean = null,
88         $view_object_map = array()
89         )
90     {
91     }
92
93     public function init(
94         $bean = null,
95         $view_object_map = array()
96         )
97     {
98         $this->bean = $bean;
99         $this->view_object_map = $view_object_map;
100         $this->action = $GLOBALS['action'];
101         $this->module = $GLOBALS['module'];
102         $this->_initSmarty();
103     }
104
105     protected function _initSmarty()
106     {
107         $this->ss = new Sugar_Smarty();
108         $this->ss->assign('MOD', $GLOBALS['mod_strings']);
109         $this->ss->assign('APP', $GLOBALS['app_strings']);
110     }
111
112     /**
113      * This method will be called from the controller and is not meant to be overridden.
114      */
115     public function process()
116     {
117         LogicHook::initialize();
118         $this->_checkModule();
119
120         //trackView has to be here in order to track for breadcrumbs
121         $this->_trackView();
122
123         //For the ajaxUI, we need to use output buffering to return the page in an ajax friendly format
124         if ($this->_getOption('json_output')){
125                         ob_start();
126                         if(!empty($_REQUEST['ajax_load']) && !empty($_REQUEST['loadLanguageJS'])) {
127                                 echo $this->_getModLanguageJS();
128                         }
129                 }
130
131         if ($this->_getOption('show_header')) {
132             $this->displayHeader();
133         } else {
134             $this->renderJavascript();
135         }
136
137         $this->_buildModuleList();
138         $this->preDisplay();
139         $this->displayErrors();
140         $this->display();
141         if ( !empty($this->module) ) {
142             $GLOBALS['logic_hook']->call_custom_logic($this->module, 'after_ui_frame');
143         } else {
144             $GLOBALS['logic_hook']->call_custom_logic('', 'after_ui_frame');
145         }
146
147         // We have to update jsAlerts as soon as possible
148         if (
149             !isset($_SESSION['isMobile'])
150             &&
151             (
152                 $this instanceof ViewList
153                 || $this instanceof ViewDetail
154                 || $this instanceof ViewEdit
155             )
156         ) {
157             $jsAlerts = new jsAlerts();
158             echo $jsAlerts->getScript();
159         }
160
161         if ($this->_getOption('show_subpanels') && !empty($_REQUEST['record'])) $this->_displaySubPanels();
162
163         if ($this->action === 'Login') {
164             //this is needed for a faster loading login page ie won't render unless the tables are closed
165             ob_flush();
166         }
167         if ($this->_getOption('show_footer')) $this->displayFooter();
168         $GLOBALS['logic_hook']->call_custom_logic('', 'after_ui_footer');
169         if ($this->_getOption('json_output'))
170         {
171             $content = ob_get_clean();
172             $module = $this->module;
173             $ajax_ret = array(
174                 'content' => mb_detect_encoding($content) == "UTF-8" ? $content : utf8_encode($content),
175                  'menu' => array(
176                      'module' => $module,
177                      'label' => translate($module),
178                      $this->getMenu($module),
179                  ),
180                 'title' => $this->getBrowserTitle(),
181                 'action' => isset($_REQUEST['action']) ? $_REQUEST['action'] : "",
182                 'record' => isset($_REQUEST['record']) ? $_REQUEST['record'] : "",
183                 'favicon' => $this->getFavicon(),
184             );
185
186             if(SugarThemeRegistry::current()->name == 'Classic')
187                 $ajax_ret['moduleList'] = $this->displayHeader(true);
188
189             if(empty($this->responseTime))
190                 $this->_calculateFooterMetrics();
191             $ajax_ret['responseTime'] = $this->responseTime;
192             $json = getJSONobj();
193             echo $json->encode($ajax_ret);
194             $GLOBALS['app']->headerDisplayed = false;
195             ob_flush();
196         }
197         //Do not track if there is no module or if module is not a String
198         $this->_track();
199     }
200
201     /**
202      * This method will display the errors on the page.
203      */
204     public function displayErrors()
205     {
206         $errors = '';
207
208         foreach($this->errors as $error) {
209             $errors .= '<span class="error">' . $error . '</span><br>';
210         }
211
212         if ( !$this->suppressDisplayErrors ) {
213             echo $errors;
214         }
215         else {
216             return $errors;
217         }
218     }
219
220     /**
221      * [OVERRIDE] - This method is meant to overidden in a subclass. The purpose of this method is
222      * to allow a view to do some preprocessing before the display method is called. This becomes
223      * useful when you have a view defined at the application level and then within a module
224      * have a sub-view that extends from this application level view.  The application level
225      * view can do the setup in preDisplay() that is common to itself and any subviews
226      * and then the subview can just override display(). If it so desires, can also override
227      * preDisplay().
228      */
229     public function preDisplay()
230     {
231     }
232
233     /**
234      * [OVERRIDE] - This method is meant to overidden in a subclass. This method
235      * will handle the actual display logic of the view.
236      */
237     public function display()
238     {
239     }
240
241
242     /**
243      * trackView
244      */
245     protected function _trackView()
246     {
247         $action = strtolower($this->action);
248         //Skip save, tracked in SugarBean instead
249         if($action == 'save') {
250         return;
251         }
252
253
254         $trackerManager = TrackerManager::getInstance();
255         $timeStamp = TimeDate::getInstance()->nowDb();
256         if($monitor = $trackerManager->getMonitor('tracker')){
257             $monitor->setValue('action', $action);
258             $monitor->setValue('user_id', $GLOBALS['current_user']->id);
259             $monitor->setValue('module_name', $this->module);
260             $monitor->setValue('date_modified', $timeStamp);
261             $monitor->setValue('visible', (($monitor->action == 'detailview') || ($monitor->action == 'editview')
262                                             ) ? 1 : 0);
263
264             if (!empty($this->bean->id)) {
265                 $monitor->setValue('item_id', $this->bean->id);
266                 $monitor->setValue('item_summary', $this->bean->get_summary_text());
267             }
268
269             //If visible is true, but there is no bean, do not track (invalid/unauthorized reference)
270             //Also, do not track save actions where there is no bean id
271             if($monitor->visible && empty($this->bean->id)) {
272             $trackerManager->unsetMonitor($monitor);
273             return;
274             }
275             $trackerManager->saveMonitor($monitor, true, true);
276         }
277     }
278
279
280     /**
281      * Displays the header on section of the page; basically everything before the content
282      */
283     public function displayHeader($retModTabs=false)
284     {
285         global $theme;
286         global $max_tabs;
287         global $app_strings;
288         global $current_user;
289         global $sugar_config;
290         global $app_list_strings;
291         global $mod_strings;
292         global $current_language;
293
294         $GLOBALS['app']->headerDisplayed = true;
295
296         $themeObject = SugarThemeRegistry::current();
297         $theme = $themeObject->__toString();
298
299         $ss = new Sugar_Smarty();
300         $ss->assign("APP", $app_strings);
301         $ss->assign("THEME", $theme);
302         $ss->assign("THEME_IE6COMPAT", $themeObject->ie6compat ? 'true':'false');
303         $ss->assign("MODULE_NAME", $this->module);
304         $ss->assign("langHeader", get_language_header());
305
306         // set ab testing if exists
307         $testing = (isset($_REQUEST["testing"]) ? $_REQUEST['testing'] : "a");
308         $ss->assign("ABTESTING", $testing);
309
310         // get browser title
311         $ss->assign("SYSTEM_NAME", $this->getBrowserTitle());
312
313         // get css
314         $css = $themeObject->getCSS();
315         if ($this->_getOption('view_print')) {
316             $css .= '<link rel="stylesheet" type="text/css" href="'.$themeObject->getCSSURL('print.css').'" media="all" />';
317         }
318         $ss->assign("SUGAR_CSS",$css);
319
320         // get javascript
321         ob_start();
322         $this->renderJavascript();
323
324         $ss->assign("SUGAR_JS",ob_get_contents().$themeObject->getJS());
325         ob_end_clean();
326
327         // get favicon
328         if(isset($GLOBALS['sugar_config']['default_module_favicon']))
329             $module_favicon = $GLOBALS['sugar_config']['default_module_favicon'];
330         else
331             $module_favicon = false;
332
333         $favicon = $this->getFavicon();
334         $ss->assign('FAVICON_URL', $favicon['url']);
335
336         // build the shortcut menu
337         $shortcut_menu = array();
338         foreach ( $this->getMenu() as $key => $menu_item )
339             $shortcut_menu[$key] = array(
340                 "URL"         => $menu_item[0],
341                 "LABEL"       => $menu_item[1],
342                 "MODULE_NAME" => $menu_item[2],
343                 "IMAGE"       => $themeObject
344                     ->getImage($menu_item[2],"border='0' align='absmiddle'",null,null,'.gif',$menu_item[1]),
345                 );
346         $ss->assign("SHORTCUT_MENU",$shortcut_menu);
347
348         // handle rtl text direction
349         if(isset($_REQUEST['RTL']) && $_REQUEST['RTL'] == 'RTL'){
350             $_SESSION['RTL'] = true;
351         }
352         if(isset($_REQUEST['LTR']) && $_REQUEST['LTR'] == 'LTR'){
353             unset($_SESSION['RTL']);
354         }
355         if(isset($_SESSION['RTL']) && $_SESSION['RTL']){
356             $ss->assign("DIR", 'dir="RTL"');
357         }
358
359         // handle resizing of the company logo correctly on the fly
360         $companyLogoURL = $themeObject->getImageURL('company_logo.png');
361         $companyLogoURL_arr = explode('?', $companyLogoURL);
362         $companyLogoURL = $companyLogoURL_arr[0];
363
364         $company_logo_attributes = sugar_cache_retrieve('company_logo_attributes');
365         if(!empty($company_logo_attributes)) {
366             $ss->assign("COMPANY_LOGO_MD5", $company_logo_attributes[0]);
367             $ss->assign("COMPANY_LOGO_WIDTH", $company_logo_attributes[1]);
368             $ss->assign("COMPANY_LOGO_HEIGHT", $company_logo_attributes[2]);
369         }
370         else {
371             // Always need to md5 the file
372             $ss->assign("COMPANY_LOGO_MD5", md5_file($companyLogoURL));
373
374             list($width,$height) = getimagesize($companyLogoURL);
375             if ( $width > 212 || $height > 40 ) {
376                 $resizePctWidth  = ($width - 212)/212;
377                 $resizePctHeight = ($height - 40)/40;
378                 if ( $resizePctWidth > $resizePctHeight )
379                     $resizeAmount = $width / 212;
380                 else
381                     $resizeAmount = $height / 40;
382                 $ss->assign("COMPANY_LOGO_WIDTH", round($width * (1/$resizeAmount)));
383                 $ss->assign("COMPANY_LOGO_HEIGHT", round($height * (1/$resizeAmount)));
384             }
385             else {
386                 $ss->assign("COMPANY_LOGO_WIDTH", $width);
387                 $ss->assign("COMPANY_LOGO_HEIGHT", $height);
388             }
389
390             // Let's cache the results
391             sugar_cache_put('company_logo_attributes',
392                             array(
393                                 $ss->get_template_vars("COMPANY_LOGO_MD5"),
394                                 $ss->get_template_vars("COMPANY_LOGO_WIDTH"),
395                                 $ss->get_template_vars("COMPANY_LOGO_HEIGHT")
396                                 )
397             );
398         }
399         $ss->assign("COMPANY_LOGO_URL",getJSPath($companyLogoURL)."&logo_md5=".$ss->get_template_vars("COMPANY_LOGO_MD5"));
400
401         // get the global links
402         $gcls = array();
403         $global_control_links = array();
404         require("include/globalControlLinks.php");
405
406         foreach($global_control_links as $key => $value) {
407             if ($key == 'users')  {   //represents logout link.
408                 $ss->assign("LOGOUT_LINK", $value['linkinfo'][key($value['linkinfo'])]);
409                 $ss->assign("LOGOUT_LABEL", key($value['linkinfo']));//key value for first element.
410                 continue;
411             }
412
413             foreach ($value as $linkattribute => $attributevalue) {
414                 // get the main link info
415                 if ( $linkattribute == 'linkinfo' ) {
416                     $gcls[$key] = array(
417                         "LABEL" => key($attributevalue),
418                         "URL"   => current($attributevalue),
419                         "SUBMENU" => array(),
420                         );
421                    if(substr($gcls[$key]["URL"], 0, 11) == "javascript:") {
422                        $gcls[$key]["ONCLICK"] = substr($gcls[$key]["URL"],11);
423                        $gcls[$key]["URL"] = "javascript:void(0)";
424                    }
425                 }
426                 // and now the sublinks
427                 if ( $linkattribute == 'submenu' && is_array($attributevalue) ) {
428                     foreach ($attributevalue as $submenulinkkey => $submenulinkinfo)
429                         $gcls[$key]['SUBMENU'][$submenulinkkey] = array(
430                             "LABEL" => key($submenulinkinfo),
431                             "URL"   => current($submenulinkinfo),
432                         );
433                        if(substr($gcls[$key]['SUBMENU'][$submenulinkkey]["URL"], 0, 11) == "javascript:") {
434                            $gcls[$key]['SUBMENU'][$submenulinkkey]["ONCLICK"] = substr($gcls[$key]['SUBMENU'][$submenulinkkey]["URL"],11);
435                            $gcls[$key]['SUBMENU'][$submenulinkkey]["URL"] = "javascript:void(0)";
436                        }
437                 }
438             }
439         }
440         $ss->assign("GCLS",$gcls);
441
442         $ss->assign("SEARCH", isset($_REQUEST['query_string']) ? $_REQUEST['query_string'] : '');
443
444         if ($this->action == "EditView" || $this->action == "Login")
445             $ss->assign("ONLOAD", 'onload="set_focus()"');
446
447         $ss->assign("AUTHENTICATED",isset($_SESSION["authenticated_user_id"]));
448
449         // get other things needed for page style popup
450         if (isset($_SESSION["authenticated_user_id"])) {
451             // get the current user name and id
452             $ss->assign("CURRENT_USER", $current_user->full_name == '' || !showFullName()
453                 ? $current_user->user_name : $current_user->full_name );
454             $ss->assign("CURRENT_USER_ID", $current_user->id);
455
456             // get the last viewed records
457             $tracker = new Tracker();
458             $history = $tracker->get_recently_viewed($current_user->id);
459             $ss->assign("recentRecords",$this->processRecentRecords($history));
460         }
461
462         $bakModStrings = $mod_strings;
463         if (isset($_SESSION["authenticated_user_id"]) ) {
464             // get the module list
465             $moduleTopMenu = array();
466
467             $max_tabs = $current_user->getPreference('max_tabs');
468             // Attempt to correct if max tabs count is extremely high.
469             if ( !isset($max_tabs) || $max_tabs <= 0 || $max_tabs > 10 ) {
470                 $max_tabs = $GLOBALS['sugar_config']['default_max_tabs'];
471                 $current_user->setPreference('max_tabs', $max_tabs, 0, 'global');
472             }
473
474             $moduleTab = $this->_getModuleTab();
475             $ss->assign('MODULE_TAB',$moduleTab);
476
477
478             // See if they are using grouped tabs or not (removed in 6.0, returned in 6.1)
479             $user_navigation_paradigm = $current_user->getPreference('navigation_paradigm');
480             if ( !isset($user_navigation_paradigm) ) {
481                 $user_navigation_paradigm = $GLOBALS['sugar_config']['default_navigation_paradigm'];
482             }
483
484
485             // Get the full module list for later use
486             foreach ( query_module_access_list($current_user) as $module ) {
487                 // Bug 25948 - Check for the module being in the moduleList
488                 if ( isset($app_list_strings['moduleList'][$module]) ) {
489                     $fullModuleList[$module] = $app_list_strings['moduleList'][$module];
490                 }
491             }
492
493
494             if(!should_hide_iframes()) {
495                 $iFrame = new iFrame();
496                 $frames = $iFrame->lookup_frames('tab');
497                 foreach($frames as $key => $values){
498                         $fullModuleList[$key] = $values;
499                 }
500             }
501             elseif (isset($fullModuleList['iFrames'])) {
502                 unset($fullModuleList['iFrames']);
503             }
504
505             if ( $user_navigation_paradigm == 'gm' && isset($themeObject->group_tabs) && $themeObject->group_tabs) {
506                 // We are using grouped tabs
507                 require_once('include/GroupedTabs/GroupedTabStructure.php');
508                 $groupedTabsClass = new GroupedTabStructure();
509                 $modules = query_module_access_list($current_user);
510                 //handle with submoremodules
511                 $max_tabs = $current_user->getPreference('max_tabs');
512                 // If the max_tabs isn't set incorrectly, set it within the range, to the default max sub tabs size
513                 if ( !isset($max_tabs) || $max_tabs <= 0 || $max_tabs > 10){
514                     // We have a default value. Use it
515                     if(isset($GLOBALS['sugar_config']['default_max_tabs'])){
516                         $max_tabs = $GLOBALS['sugar_config']['default_max_tabs'];
517                     }
518                     else{
519                         $max_tabs = 8;
520                     }
521                 }
522
523                 $subMoreModules = false;
524                 $groupTabs = $groupedTabsClass->get_tab_structure(get_val_array($modules));
525                 // We need to put this here, so the "All" group is valid for the user's preference.
526                 $groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules'] = $fullModuleList;
527
528
529                 // Setup the default group tab.
530                 $allGroup = $app_strings['LBL_TABGROUP_ALL'];
531                 $ss->assign('currentGroupTab',$allGroup);
532                 $currentGroupTab = $allGroup;
533                 $usersGroup = $current_user->getPreference('theme_current_group');
534                 // Figure out which tab they currently have selected (stored as a user preference)
535                 if ( !empty($usersGroup) && isset($groupTabs[$usersGroup]) ) {
536                     $currentGroupTab = $usersGroup;
537                 } else {
538                     $current_user->setPreference('theme_current_group',$currentGroupTab);
539                 }
540
541                 $ss->assign('currentGroupTab',$currentGroupTab);
542                 $usingGroupTabs = true;
543
544             } else {
545                 // Setup the default group tab.
546                 $ss->assign('currentGroupTab',$app_strings['LBL_TABGROUP_ALL']);
547
548                 $usingGroupTabs = false;
549
550                 $groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules'] = $fullModuleList;
551
552             }
553
554
555             $topTabList = array();
556
557             // Now time to go through each of the tab sets and fix them up.
558             foreach ( $groupTabs as $tabIdx => $tabData ) {
559                 $topTabs = $tabData['modules'];
560                 if ( ! is_array($topTabs) ) {
561                     $topTabs = array();
562                 }
563                 $extraTabs = array();
564
565                 // Split it in to the tabs that go across the top, and the ones that are on the extra menu.
566                 if ( count($topTabs) > $max_tabs ) {
567                     $extraTabs = array_splice($topTabs,$max_tabs);
568                 }
569                 // Make sure the current module is accessable through one of the top tabs
570                 if ( !isset($topTabs[$moduleTab]) ) {
571                     // Nope, we need to add it.
572                     // First, take it out of the extra menu, if it's there
573                     if ( isset($extraTabs[$moduleTab]) ) {
574                         unset($extraTabs[$moduleTab]);
575                     }
576                     if ( count($topTabs) >= $max_tabs - 1 ) {
577                         // We already have the maximum number of tabs, so we need to shuffle the last one
578                         // from the top to the first one of the extras
579                         $lastElem = array_splice($topTabs,$max_tabs-1);
580                         $extraTabs = $lastElem + $extraTabs;
581                     }
582                     if ( !empty($moduleTab) ) {
583                         $topTabs[$moduleTab] = $app_list_strings['moduleList'][$moduleTab];
584                     }
585                 }
586
587
588                 /*
589                 // This was removed, but I like the idea, so I left the code in here in case we decide to turn it back on
590                 // If we are using group tabs, add all the "hidden" tabs to the end of the extra menu
591                 if ( $usingGroupTabs ) {
592                     foreach($fullModuleList as $moduleKey => $module ) {
593                         if ( !isset($topTabs[$moduleKey]) && !isset($extraTabs[$moduleKey]) ) {
594                             $extraTabs[$moduleKey] = $module;
595                         }
596                     }
597                 }
598                 */
599
600                 // Get a unique list of the top tabs so we can build the popup menus for them
601                 foreach ( $topTabs as $moduleKey => $module ) {
602                     $topTabList[$moduleKey] = $module;
603                 }
604
605                 $groupTabs[$tabIdx]['modules'] = $topTabs;
606                 $groupTabs[$tabIdx]['extra'] = $extraTabs;
607             }
608         }
609
610         if ( isset($topTabList) && is_array($topTabList) ) {
611             // Adding shortcuts array to menu array for displaying shortcuts associated with each module
612             $shortcutTopMenu = array();
613             foreach($topTabList as $module_key => $label) {
614                 global $mod_strings;
615                 $mod_strings = return_module_language($current_language, $module_key);
616                 foreach ( $this->getMenu($module_key) as $key => $menu_item ) {
617                     $shortcutTopMenu[$module_key][$key] = array(
618                         "URL"         => $menu_item[0],
619                         "LABEL"       => $menu_item[1],
620                         "MODULE_NAME" => $menu_item[2],
621                         "IMAGE"       => $themeObject
622                         ->getImage($menu_item[2],"border='0' align='absmiddle'",null,null,'.gif',$menu_item[1]),
623                         "ID"          => $menu_item[2]."_link",
624                         );
625                 }
626             }
627             $ss->assign("groupTabs",$groupTabs);
628             $ss->assign("shortcutTopMenu",$shortcutTopMenu);
629             $ss->assign('USE_GROUP_TABS',$usingGroupTabs);
630
631             // This is here for backwards compatibility, someday, somewhere, it will be able to be removed
632             $ss->assign("moduleTopMenu",$groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules']);
633             $ss->assign("moduleExtraMenu",$groupTabs[$app_strings['LBL_TABGROUP_ALL']]['extra']);
634
635
636         }
637         
638         if ( isset($extraTabs) && is_array($extraTabs) ) {
639             // Adding shortcuts array to extra menu array for displaying shortcuts associated with each module
640             $shortcutExtraMenu = array();
641             foreach($extraTabs as $module_key => $label) {
642                 global $mod_strings;
643                 $mod_strings = return_module_language($current_language, $module_key);
644                 foreach ( $this->getMenu($module_key) as $key => $menu_item ) {
645                     $shortcutExtraMenu[$module_key][$key] = array(
646                         "URL"         => $menu_item[0],
647                         "LABEL"       => $menu_item[1],
648                         "MODULE_NAME" => $menu_item[2],
649                         "IMAGE"       => $themeObject
650                         ->getImage($menu_item[2],"border='0' align='absmiddle'",null,null,'.gif',$menu_item[1]),
651                         "ID"          => $menu_item[2]."_link",
652                         );
653                 }
654             }
655             $ss->assign("shortcutExtraMenu",$shortcutExtraMenu);
656         }
657        
658        if(!empty($current_user)){
659         $ss->assign("max_tabs", $current_user->getPreference("max_tabs"));
660        } 
661       
662        
663         $imageURL = SugarThemeRegistry::current()->getImageURL("dashboard.png");
664         $homeImage = "<img src='$imageURL'>";
665                 $ss->assign("homeImage",$homeImage);
666         global $mod_strings;
667         $mod_strings = $bakModStrings;
668         $headerTpl = $themeObject->getTemplate('header.tpl');
669         if (inDeveloperMode() )
670             $ss->clear_compiled_tpl($headerTpl);
671
672         if ($retModTabs)
673         {
674             return $ss->fetch($themeObject->getTemplate('_headerModuleList.tpl'));
675         } else {
676             $ss->display($headerTpl);
677
678             $this->includeClassicFile('modules/Administration/DisplayWarnings.php');
679
680             $errorMessages = SugarApplication::getErrorMessages();
681             if ( !empty($errorMessages)) {
682                 foreach ( $errorMessages as $error_message ) {
683                     echo('<p class="error">' . $error_message.'</p>');
684                 }
685             }
686         }
687
688     }
689
690     function getModuleMenuHTML()
691     {
692
693     }
694
695     /**
696      * If the view is classic then this method will include the file and
697      * setup any global variables.
698      *
699      * @param string $file
700      */
701     public function includeClassicFile(
702         $file
703         )
704     {
705         global $sugar_config, $theme, $current_user, $sugar_version, $sugar_flavor, $mod_strings, $app_strings, $app_list_strings, $action;
706         global $gridline, $request_string, $modListHeader, $dashletData, $authController, $locale, $currentModule, $import_bean_map, $image_path, $license;
707         global $user_unique_key, $server_unique_key, $barChartColors, $modules_exempt_from_availability_check, $dictionary, $current_language, $beanList, $beanFiles, $sugar_build, $sugar_codename;
708         global $timedate, $login_error; // cn: bug 13855 - timedate not available to classic views.
709         if (!empty($this->module))
710             $currentModule = $this->module;
711         require_once ($file);
712     }
713
714     protected function _displayLoginJS()
715     {
716         global $sugar_config, $timedate;
717
718         if(isset($this->bean->module_dir)){
719             echo "<script>var module_sugar_grp1 = '{$this->bean->module_dir}';</script>";
720         }
721         if(isset($_REQUEST['action'])){
722             echo "<script>var action_sugar_grp1 = '{$_REQUEST['action']}';</script>";
723         }
724         echo '<script>jscal_today = 1000*' . $timedate->asUserTs($timedate->getNow()) . '; if(typeof app_strings == "undefined") app_strings = new Array();</script>';
725         if (!is_file(sugar_cached("include/javascript/sugar_grp1.js"))) {
726             $_REQUEST['root_directory'] = ".";
727             require_once("jssource/minify_utils.php");
728             ConcatenateFiles(".");
729         }
730         echo getVersionedScript('cache/include/javascript/sugar_grp1_jquery.js');
731         echo getVersionedScript('cache/include/javascript/sugar_grp1_yui.js');
732         echo getVersionedScript('cache/include/javascript/sugar_grp1.js');
733         echo getVersionedScript('include/javascript/calendar.js');
734         echo <<<EOQ
735         <script>
736             if ( typeof(SUGAR) == 'undefined' ) {SUGAR = {}};
737             if ( typeof(SUGAR.themes) == 'undefined' ) SUGAR.themes = {};
738         </script>
739 EOQ;
740         if(isset( $sugar_config['disc_client']) && $sugar_config['disc_client'])
741             echo getVersionedScript('modules/Sync/headersync.js');
742     }
743
744     /**
745      * Get JS validation code for views
746      */
747     public static function getJavascriptValidation()
748     {
749         global $timedate;
750         $cal_date_format = $timedate->get_cal_date_format();
751         $timereg = $timedate->get_regular_expression($timedate->get_time_format());
752         $datereg = $timedate->get_regular_expression($timedate->get_date_format());
753         $date_pos = '';
754         foreach ($datereg['positions'] as $type => $pos) {
755             if (empty($date_pos)) {
756                 $date_pos .= "'$type': $pos";
757             } else {
758                 $date_pos .= ",'$type': $pos";
759             }
760         }
761
762         $time_separator = $timedate->timeSeparator();
763         $hour_offset = $timedate->getUserUTCOffset() * 60;
764
765         // Add in the number formatting styles here as well, we have been handling this with individual modules.
766         require_once ('modules/Currencies/Currency.php');
767         list ($num_grp_sep, $dec_sep) = get_number_seperators();
768
769         $the_script = "<script type=\"text/javascript\">\n" . "\tvar time_reg_format = '" .
770              $timereg['format'] . "';\n" . "\tvar date_reg_format = '" .
771              $datereg['format'] . "';\n" . "\tvar date_reg_positions = { $date_pos };\n" .
772              "\tvar time_separator = '$time_separator';\n" .
773              "\tvar cal_date_format = '$cal_date_format';\n" .
774              "\tvar time_offset = $hour_offset;\n" . "\tvar num_grp_sep = '$num_grp_sep';\n" .
775              "\tvar dec_sep = '$dec_sep';\n" . "</script>";
776
777         return $the_script;
778     }
779
780     /**
781      * Called from process(). This method will display the correct javascript.
782      */
783     protected function _displayJavascript()
784     {
785         global $locale, $sugar_config, $timedate;
786
787
788         if ($this->_getOption('show_javascript')) {
789             if (!$this->_getOption('show_header')) {
790                 $langHeader = get_language_header();
791
792                 echo <<<EOHTML
793 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
794 <html {$langHeader}>
795 <head>
796 EOHTML;
797             }
798
799             $js_vars = array(
800                 "sugar_cache_dir" => "cache/",
801                 );
802
803             if(isset($this->bean->module_dir)){
804                 $js_vars['module_sugar_grp1'] = $this->bean->module_dir;
805             }
806             if(isset($_REQUEST['action'])){
807                 $js_vars['action_sugar_grp1'] = $_REQUEST['action'];
808             }
809             echo '<script>jscal_today = 1000*' . $timedate->asUserTs($timedate->getNow()) . '; if(typeof app_strings == "undefined") app_strings = new Array();</script>';
810             if (!is_file(sugar_cached("include/javascript/sugar_grp1.js")) || !is_file(sugar_cached("include/javascript/sugar_grp1_yui.js")) || !is_file(sugar_cached("include/javascript/sugar_grp1_jquery.js"))) {
811                 $_REQUEST['root_directory'] = ".";
812                 require_once("jssource/minify_utils.php");
813                 ConcatenateFiles(".");
814             }
815             echo getVersionedScript('cache/include/javascript/sugar_grp1_jquery.js');
816             echo getVersionedScript('cache/include/javascript/sugar_grp1_yui.js');
817             echo getVersionedScript('cache/include/javascript/sugar_grp1.js');
818             echo getVersionedScript('include/javascript/calendar.js');
819
820             // output necessary config js in the top of the page
821             $config_js = $this->getSugarConfigJS();
822             if(!empty($config_js)){
823                 echo "<script>\n".implode("\n", $config_js)."</script>\n";
824             }
825
826             if ( isset($sugar_config['email_sugarclient_listviewmaxselect']) ) {
827                 echo "<script>SUGAR.config.email_sugarclient_listviewmaxselect = {$GLOBALS['sugar_config']['email_sugarclient_listviewmaxselect']};</script>";
828             }
829
830             $image_server = (defined('TEMPLATE_URL'))?TEMPLATE_URL . '/':'';
831             echo '<script type="text/javascript">SUGAR.themes.image_server="' . $image_server . '";</script>'; // cn: bug 12274 - create session-stored key to defend against CSRF
832             echo '<script type="text/javascript">var name_format = "' . $locale->getLocaleFormatMacro() . '";</script>';
833             echo self::getJavascriptValidation();
834             if (!is_file(sugar_cached('jsLanguage/') . $GLOBALS['current_language'] . '.js')) {
835                 require_once ('include/language/jsLanguage.php');
836                 jsLanguage::createAppStringsCache($GLOBALS['current_language']);
837             }
838             echo getVersionedScript('cache/jsLanguage/'. $GLOBALS['current_language'] . '.js', $GLOBALS['sugar_config']['js_lang_version']);
839
840                         echo $this->_getModLanguageJS();
841
842             if(isset( $sugar_config['disc_client']) && $sugar_config['disc_client'])
843                 echo getVersionedScript('modules/Sync/headersync.js');
844
845
846             //echo out the $js_vars variables as javascript variables
847             echo "<script type='text/javascript'>\n";
848             foreach($js_vars as $var=>$value)
849             {
850                 echo "var {$var} = '{$value}';\n";
851             }
852             echo "</script>\n";
853         }
854     }
855
856         protected function _getModLanguageJS(){
857                 if (!is_file(sugar_cached('jsLanguage/') . $this->module . '/' . $GLOBALS['current_language'] . '.js')) {
858                         require_once ('include/language/jsLanguage.php');
859                         jsLanguage::createModuleStringsCache($this->module, $GLOBALS['current_language']);
860                 }
861                 return getVersionedScript("cache/jsLanguage/{$this->module}/". $GLOBALS['current_language'] . '.js', $GLOBALS['sugar_config']['js_lang_version']);
862         }
863
864     /**
865      * Called from process(). This method will display the footer on the page.
866      */
867     public function displayFooter()
868     {
869         if (empty($this->responseTime)) {
870             $this->_calculateFooterMetrics();
871         }
872         global $sugar_config;
873         global $app_strings;
874         global $mod_strings;
875                 $themeObject = SugarThemeRegistry::current();
876         //decide whether or not to show themepicker, default is to show
877         $showThemePicker = true;
878         if (isset($sugar_config['showThemePicker'])) {
879             $showThemePicker = $sugar_config['showThemePicker'];
880         }
881
882         $ss = new Sugar_Smarty();
883         $ss->assign("AUTHENTICATED",isset($_SESSION["authenticated_user_id"]));
884         $ss->assign('MOD',return_module_language($GLOBALS['current_language'], 'Users'));
885
886                 $bottomLinkList = array();
887                  if (isset($this->action) && $this->action != "EditView") {
888                          $bottomLinkList['print'] = array($app_strings['LNK_PRINT'] => getPrintLink());
889                 }
890                 $bottomLinkList['backtotop'] = array($app_strings['LNK_BACKTOTOP'] => 'javascript:SUGAR.util.top();');
891
892                 $bottomLinksStr = "";
893                 foreach($bottomLinkList as $key => $value) {
894                         foreach($value as $text => $link) {
895                                    $href = $link;
896                                    if(substr($link, 0, 11) == "javascript:") {
897                        $onclick = " onclick=\"".substr($link,11)."\"";
898                        $href = "javascript:void(0)";
899                    } else {
900                                 $onclick = "";
901                         }
902                 $imageURL = SugarThemeRegistry::current()->getImageURL($key.'.gif');
903                                 $bottomLinksStr .= "<a href=\"{$href}\"";
904                                 $bottomLinksStr .= (isset($onclick)) ? $onclick : "";
905                                 $bottomLinksStr .= "><img src='{$imageURL}' alt=''>"; //keeping alt blank on purpose for 508 (text will be read instead)
906                                 $bottomLinksStr .= " ".$text."</a>";
907                         }
908                 }
909                 $ss->assign("BOTTOMLINKS",$bottomLinksStr);
910         if (SugarConfig::getInstance()->get('calculate_response_time', false))
911             $ss->assign('STATISTICS',$this->_getStatistics());
912
913         // Under the License referenced above, you are required to leave in all copyright statements in both
914         // the code and end-user application.
915
916
917         $copyright = '&copy; 2004-2013 SugarCRM Inc. The Program is provided AS IS, without warranty.  Licensed under <a href="LICENSE.txt" target="_blank" class="copyRightLink">AGPLv3</a>.<br>This program is free software; you can redistribute it and/or modify it under the terms of the <br><a href="LICENSE.txt" target="_blank" class="copyRightLink"> GNU Affero General Public License version 3</a> as published by the Free Software Foundation, including the additional permission set forth in the source code header.<br>';
918
919
920
921
922
923
924
925
926
927
928
929         // The interactive user interfaces in modified source and object code
930         // versions of this program must display Appropriate Legal Notices, as
931         // required under Section 5 of the GNU General Public License version
932         // 3. In accordance with Section 7(b) of the GNU General Public License
933         // version 3, these Appropriate Legal Notices must retain the display
934         // of the "Powered by SugarCRM" logo. If the display of the logo is
935         // not reasonably feasible for technical reasons, the Appropriate
936         // Legal Notices must display the words "Powered by SugarCRM".
937         $attribLinkImg = "<img style='margin-top: 2px' border='0' width='120' height='34' src='include/images/poweredby_sugarcrm_65.png' alt='Powered By SugarCRM'>\n";
938
939
940                 // handle resizing of the company logo correctly on the fly
941         $companyLogoURL = $themeObject->getImageURL('company_logo.png');
942         $companyLogoURL_arr = explode('?', $companyLogoURL);
943         $companyLogoURL = $companyLogoURL_arr[0];
944
945         $company_logo_attributes = sugar_cache_retrieve('company_logo_attributes');
946         if(!empty($company_logo_attributes)) {
947             $ss->assign("COMPANY_LOGO_MD5", $company_logo_attributes[0]);
948             $ss->assign("COMPANY_LOGO_WIDTH", $company_logo_attributes[1]);
949             $ss->assign("COMPANY_LOGO_HEIGHT", $company_logo_attributes[2]);
950         }
951         else {
952             // Always need to md5 the file
953             $ss->assign("COMPANY_LOGO_MD5", md5_file($companyLogoURL));
954
955             list($width,$height) = getimagesize($companyLogoURL);
956             if ( $width > 212 || $height > 40 ) {
957                 $resizePctWidth  = ($width - 212)/212;
958                 $resizePctHeight = ($height - 40)/40;
959                 if ( $resizePctWidth > $resizePctHeight )
960                     $resizeAmount = $width / 212;
961                 else
962                     $resizeAmount = $height / 40;
963                 $ss->assign("COMPANY_LOGO_WIDTH", round($width * (1/$resizeAmount)));
964                 $ss->assign("COMPANY_LOGO_HEIGHT", round($height * (1/$resizeAmount)));
965             }
966             else {
967                 $ss->assign("COMPANY_LOGO_WIDTH", $width);
968                 $ss->assign("COMPANY_LOGO_HEIGHT", $height);
969             }
970
971             // Let's cache the results
972             sugar_cache_put('company_logo_attributes',
973                             array(
974                                 $ss->get_template_vars("COMPANY_LOGO_MD5"),
975                                 $ss->get_template_vars("COMPANY_LOGO_WIDTH"),
976                                 $ss->get_template_vars("COMPANY_LOGO_HEIGHT")
977                                 )
978             );
979         }
980         $ss->assign("COMPANY_LOGO_URL",getJSPath($companyLogoURL)."&logo_md5=".$ss->get_template_vars("COMPANY_LOGO_MD5"));
981
982         // Bug 38594 - Add in Trademark wording
983         $copyright .= 'SugarCRM is a trademark of SugarCRM, Inc. All other company and product names may be trademarks of the respective companies with which they are associated.<br />';
984
985         //rrs bug: 20923 - if this image does not exist as per the license, then the proper image will be displayed regardless, so no need
986         //to display an empty image here.
987         if(file_exists('include/images/poweredby_sugarcrm_65.png')){
988             $copyright .= $attribLinkImg;
989         }
990         // End Required Image
991         $ss->assign('COPYRIGHT',$copyright);
992
993         // here we allocate the help link data
994         $help_actions_blacklist = array('Login'); // we don't want to show a context help link here
995         if (!in_array($this->action,$help_actions_blacklist)) {
996             $url = 'javascript:void(window.open(\'index.php?module=Administration&action=SupportPortal&view=documentation&version='.$GLOBALS['sugar_version'].'&edition='.$GLOBALS['sugar_flavor'].'&lang='.$GLOBALS['current_language'].
997                         '&help_module='.$this->module.'&help_action='.$this->action.'&key='.$GLOBALS['server_unique_key'].'\'))';
998             $label = (isset($GLOBALS['app_list_strings']['moduleList'][$this->module]) ?
999                         $GLOBALS['app_list_strings']['moduleList'][$this->module] : $this->module). ' '.$app_strings['LNK_HELP'];
1000             $ss->assign('HELP_LINK',SugarThemeRegistry::current()->getLink($url, $label, "id='help_link_two'",
1001                 'help-dashlet.png', 'class="icon"',null,null,'','left'));
1002         }
1003         // end
1004
1005
1006         $ss->display(SugarThemeRegistry::current()->getTemplate('footer.tpl'));
1007     }
1008
1009     /**
1010      * Called from process(). This method will display subpanels.
1011      */
1012     protected function _displaySubPanels()
1013     {
1014         if (isset($this->bean) && !empty($this->bean->id) && (file_exists('modules/' . $this->module . '/metadata/subpaneldefs.php') || file_exists('custom/modules/' . $this->module . '/metadata/subpaneldefs.php') || file_exists('custom/modules/' . $this->module . '/Ext/Layoutdefs/layoutdefs.ext.php'))) {
1015             $GLOBALS['focus'] = $this->bean;
1016             require_once ('include/SubPanel/SubPanelTiles.php');
1017             $subpanel = new SubPanelTiles($this->bean, $this->module);
1018             echo $subpanel->display();
1019         }
1020     }
1021
1022     protected function _buildModuleList()
1023     {
1024         if (!empty($GLOBALS['current_user']) && empty($GLOBALS['modListHeader']))
1025             $GLOBALS['modListHeader'] = query_module_access_list($GLOBALS['current_user']);
1026     }
1027
1028     /**
1029      * private method used in process() to determine the value of a passed in option
1030      *
1031      * @param string option - the option that we want to know the valye of
1032      * @param bool default - what the default value should be if we do not find the option
1033      *
1034      * @return bool - the value of the option
1035      */
1036     protected function _getOption(
1037         $option,
1038         $default = false
1039         )
1040     {
1041         if (!empty($this->options) && isset($this->options['show_all'])) {
1042             return $this->options['show_all'];
1043         } elseif (!empty($this->options) && isset($this->options[$option])) {
1044             return $this->options[$option];
1045         } else return $default;
1046     }
1047
1048     /**
1049      * track
1050      * Private function to track information about the view request
1051      */
1052     private function _track()
1053     {
1054         if (empty($this->responseTime)) {
1055             $this->_calculateFooterMetrics();
1056         }
1057         if (empty($GLOBALS['current_user']->id)) {
1058             return;
1059         }
1060
1061
1062         $trackerManager = TrackerManager::getInstance();
1063             $trackerManager->save();
1064
1065     }
1066
1067     /**
1068      * Checks to see if the module name passed is valid; dies if it is not
1069      */
1070     protected function _checkModule()
1071     {
1072         if(!empty($this->module) && !file_exists('modules/'.$this->module)){
1073             $error = str_replace("[module]", "$this->module", $GLOBALS['app_strings']['ERR_CANNOT_FIND_MODULE']);
1074             $GLOBALS['log']->fatal($error);
1075             echo $error;
1076             die();
1077         }
1078     }
1079
1080     public function renderJavascript()
1081     {
1082         if ($this->action !== 'Login')
1083             $this->_displayJavascript();
1084         else
1085             $this->_displayLoginJS();
1086     }
1087
1088     private function _calculateFooterMetrics()
1089     {
1090         $endTime = microtime(true);
1091         $deltaTime = $endTime - $GLOBALS['startTime'];
1092         $this->responseTime = number_format(round($deltaTime, 2), 2);
1093         // Print out the resources used in constructing the page.
1094         $this->fileResources = count(get_included_files());
1095     }
1096
1097     private function _getStatistics()
1098     {
1099         $endTime = microtime(true);
1100         $deltaTime = $endTime - $GLOBALS['startTime'];
1101         $response_time_string = $GLOBALS['app_strings']['LBL_SERVER_RESPONSE_TIME'] . ' ' . number_format(round($deltaTime, 2), 2) . ' ' . $GLOBALS['app_strings']['LBL_SERVER_RESPONSE_TIME_SECONDS'];
1102         $return = $response_time_string;
1103        // $return .= '<br />';
1104         if (!empty($GLOBALS['sugar_config']['show_page_resources'])) {
1105             // Print out the resources used in constructing the page.
1106             $included_files = get_included_files();
1107
1108             // take all of the included files and make a list that does not allow for duplicates based on case
1109             // I believe the full get_include_files result set appears to have one entry for each file in real
1110             // case, and one entry in all lower case.
1111             $list_of_files_case_insensitive = array();
1112             foreach($included_files as $key => $name) {
1113                 // preserve the first capitalization encountered.
1114                 $list_of_files_case_insensitive[mb_strtolower($name) ] = $name;
1115             }
1116             $return .= $GLOBALS['app_strings']['LBL_SERVER_RESPONSE_RESOURCES'] . '(' . DBManager::getQueryCount() . ',' . sizeof($list_of_files_case_insensitive) . ')<br>';
1117             // Display performance of the internal and external caches....
1118             $cacheStats = SugarCache::instance()->getCacheStats();
1119             $return .= "External cache (hits/total=ratio) local ({$cacheStats['localHits']}/{$cacheStats['requests']}=" . round($cacheStats['localHits']*100/$cacheStats['requests'], 0) . "%)";
1120             $return .= " external ({$cacheStats['externalHits']}/{$cacheStats['requests']}=" . round($cacheStats['externalHits']*100/$cacheStats['requests'], 0) . "%)<br />";
1121             $return .= " misses ({$cacheStats['misses']}/{$cacheStats['requests']}=" . round($cacheStats['misses']*100/$cacheStats['requests'], 0) . "%)<br />";
1122         }
1123
1124         $return .= $this->logMemoryStatistics();
1125
1126         return $return;
1127     }
1128
1129     /**
1130      * logMemoryStatistics
1131      *
1132      * This function returns a string message containing the memory statistics as well as writes to the memory_usage.log
1133      * file the memory statistics for the SugarView invocation.
1134      *
1135      * @param $newline String of newline character to use (defaults to </ br>)
1136      * @return $message String formatted message about memory statistics
1137      */
1138     protected function logMemoryStatistics($newline='<br>')
1139     {
1140         $log_message = '';
1141
1142         if(!empty($GLOBALS['sugar_config']['log_memory_usage']))
1143         {
1144             if(function_exists('memory_get_usage'))
1145             {
1146                 $memory_usage = memory_get_usage();
1147                 $bytes = $GLOBALS['app_strings']['LBL_SERVER_MEMORY_BYTES'];
1148                 $data = array($memory_usage, $bytes);
1149                 $log_message = string_format($GLOBALS['app_strings']['LBL_SERVER_MEMORY_USAGE'], $data) . $newline;
1150             }
1151
1152             if(function_exists('memory_get_peak_usage'))
1153             {
1154                 $memory_peak_usage = memory_get_peak_usage();
1155                 $bytes = $GLOBALS['app_strings']['LBL_SERVER_MEMORY_BYTES'];
1156                 $data = array($memory_peak_usage, $bytes);
1157                 $log_message .= string_format($GLOBALS['app_strings']['LBL_SERVER_PEAK_MEMORY_USAGE'], $data) . $newline;
1158             }
1159
1160             if(!empty($log_message))
1161             {
1162                 $data = array
1163                 (
1164                    !empty($this->module) ? $this->module : $GLOBALS['app_strings']['LBL_LINK_NONE'],
1165                    !empty($this->action) ? $this->action : $GLOBALS['app_strings']['LBL_LINK_NONE'],
1166                 );
1167
1168                 $output = string_format($GLOBALS['app_strings']['LBL_SERVER_MEMORY_LOG_MESSAGE'], $data) . $newline;
1169                 $output .= $log_message;
1170                 $fp = fopen("memory_usage.log", "ab");
1171                 fwrite($fp, $output);
1172                 fclose($fp);
1173             }
1174         }
1175
1176         return $log_message;
1177     }
1178
1179
1180     /**
1181      * Loads the module shortcuts menu
1182      *
1183      * @param  $module string optional, can specify module to retrieve menu for if not the current one
1184      * @return array module menu
1185      */
1186     public function getMenu(
1187         $module = null
1188         )
1189     {
1190         global $current_language, $current_user, $mod_strings, $app_strings, $module_menu;
1191
1192         if ( empty($module) )
1193             $module = $this->module;
1194
1195         //Need to make sure the mod_strings match the requested module or Menus may fail
1196         $curr_mod_strings = $mod_strings;
1197         $mod_strings = return_module_language ( $current_language, $module ) ;
1198
1199         $module_menu = array();
1200
1201         if (file_exists('modules/' . $module . '/Menu.php')) {
1202             require('modules/' . $module . '/Menu.php');
1203         }
1204         if (file_exists('custom/modules/' . $module . '/Ext/Menus/menu.ext.php')) {
1205             require('custom/modules/' . $module . '/Ext/Menus/menu.ext.php');
1206         }
1207         if (!file_exists('modules/' . $module . '/Menu.php')
1208                 && !file_exists('custom/modules/' . $module . '/Ext/Menus/menu.ext.php')
1209                 && !empty($GLOBALS['mod_strings']['LNK_NEW_RECORD'])) {
1210             $module_menu[] = array("index.php?module=$module&action=EditView&return_module=$module&return_action=DetailView",
1211                 $GLOBALS['mod_strings']['LNK_NEW_RECORD'],"{$GLOBALS['app_strings']['LBL_CREATE_BUTTON_LABEL']}$module" ,$module );
1212             $module_menu[] = array("index.php?module=$module&action=index", $GLOBALS['mod_strings']['LNK_LIST'],
1213                 $module, $module);
1214             if ( ($this->bean instanceOf SugarBean) && !empty($this->bean->importable) )
1215                 if ( !empty($mod_strings['LNK_IMPORT_'.strtoupper($module)]) )
1216                     $module_menu[] = array("index.php?module=Import&action=Step1&import_module=$module&return_module=$module&return_action=index",
1217                         $mod_strings['LNK_IMPORT_'.strtoupper($module)], "Import", $module);
1218                 else
1219                     $module_menu[] = array("index.php?module=Import&action=Step1&import_module=$module&return_module=$module&return_action=index",
1220                         $app_strings['LBL_IMPORT'], "Import", $module);
1221         }
1222         if (file_exists('custom/application/Ext/Menus/menu.ext.php')) {
1223             require('custom/application/Ext/Menus/menu.ext.php');
1224         }
1225
1226         $mod_strings = $curr_mod_strings;
1227         $builtModuleMenu = $module_menu;
1228         unset($module_menu);
1229
1230         return $builtModuleMenu;
1231     }
1232
1233     /**
1234     * Returns the module name which should be highlighted in the module menu
1235      */
1236     protected function _getModuleTab()
1237     {
1238         global $app_list_strings, $moduleTabMap, $current_user;
1239
1240                 $userTabs = query_module_access_list($current_user);
1241                 //If the home tab is in the user array use it as the default tab, otherwise use the first element in the tab array
1242                 $defaultTab = (in_array("Home",$userTabs)) ? "Home" : key($userTabs);
1243                 
1244         // Need to figure out what tab this module belongs to, most modules have their own tabs, but there are exceptions.
1245         if ( !empty($_REQUEST['module_tab']) )
1246             return $_REQUEST['module_tab'];
1247         elseif ( isset($moduleTabMap[$this->module]) )
1248             return $moduleTabMap[$this->module];
1249         // Special cases
1250         elseif ( $this->module == 'MergeRecords' )
1251             return !empty($_REQUEST['merge_module']) ? $_REQUEST['merge_module'] : $_REQUEST['return_module'];
1252         elseif ( $this->module == 'Users' && $this->action == 'SetTimezone' )
1253             return $defaultTab;
1254         // Default anonymous pages to be under Home
1255         elseif ( !isset($app_list_strings['moduleList'][$this->module]) )
1256             return $defaultTab;
1257         elseif ( isset($_REQUEST['action']) && $_REQUEST['action'] == "ajaxui" )
1258                 return $defaultTab;
1259         else
1260             return $this->module;
1261     }
1262
1263    /**
1264     * Return the "breadcrumbs" to display at the top of the page
1265     *
1266     * @param  bool $show_help optional, true if we show the help links
1267     * @return HTML string containing breadcrumb title
1268     */
1269     public function getModuleTitle(
1270         $show_help = true
1271         )
1272     {
1273         global $sugar_version, $sugar_flavor, $server_unique_key, $current_language, $action;
1274
1275         $theTitle = "<div class='moduleTitle'>\n";
1276
1277         $module = preg_replace("/ /","",$this->module);
1278
1279         $params = $this->_getModuleTitleParams();
1280         $index = 0;
1281
1282                 if(SugarThemeRegistry::current()->directionality == "rtl") {
1283                         $params = array_reverse($params);
1284                 }
1285                 if(count($params) > 1) {
1286                         array_shift($params);
1287                 }
1288                 $count = count($params);
1289         $paramString = '';
1290         foreach($params as $parm){
1291             $index++;
1292             $paramString .= $parm;
1293             if($index < $count){
1294                 $paramString .= $this->getBreadCrumbSymbol();
1295             }
1296         }
1297
1298         if(!empty($paramString)){
1299                $theTitle .= "<h2> $paramString </h2>\n";
1300            }
1301
1302
1303         // bug 56131 - restore conditional so that link doesn't appear where it shouldn't
1304         if($show_help) {
1305             $theTitle .= "<span class='utils'>";
1306             $createImageURL = SugarThemeRegistry::current()->getImageURL('create-record.gif');
1307             $url = ajaxLink("index.php?module=$module&action=EditView&return_module=$module&return_action=DetailView");
1308             $theTitle .= <<<EOHTML
1309 &nbsp;
1310 <a id="create_image" href="{$url}" class="utilsLink">
1311 <img src='{$createImageURL}' alt='{$GLOBALS['app_strings']['LNK_CREATE']}'></a>
1312 <a id="create_link" href="{$url}" class="utilsLink">
1313 {$GLOBALS['app_strings']['LNK_CREATE']}
1314 </a>
1315 EOHTML;
1316             $theTitle .= "</span>";
1317         }
1318
1319         $theTitle .= "<div class='clear'></div></div>\n";
1320         return $theTitle;
1321     }
1322
1323     /**
1324      * Return the metadata file that will be used by this view.
1325      *
1326      * @return string File location of the metadata file.
1327      */
1328     public function getMetaDataFile()
1329     {
1330         $metadataFile = null;
1331         $foundViewDefs = false;
1332         $viewDef = strtolower($this->type) . 'viewdefs';
1333         $coreMetaPath = 'modules/'.$this->module.'/metadata/' . $viewDef . '.php';
1334         if(file_exists('custom/' .$coreMetaPath )){
1335             $metadataFile = 'custom/' . $coreMetaPath;
1336             $foundViewDefs = true;
1337         }else{
1338             if(file_exists('custom/modules/'.$this->module.'/metadata/metafiles.php')){
1339                 require_once('custom/modules/'.$this->module.'/metadata/metafiles.php');
1340                 if(!empty($metafiles[$this->module][$viewDef])){
1341                     $metadataFile = $metafiles[$this->module][$viewDef];
1342                     $foundViewDefs = true;
1343                 }
1344             }elseif(file_exists('modules/'.$this->module.'/metadata/metafiles.php')){
1345                 require_once('modules/'.$this->module.'/metadata/metafiles.php');
1346                 if(!empty($metafiles[$this->module][$viewDef])){
1347                     $metadataFile = $metafiles[$this->module][$viewDef];
1348                     $foundViewDefs = true;
1349                 }
1350             }
1351         }
1352
1353         if(!$foundViewDefs && file_exists($coreMetaPath)){
1354                 $metadataFile = $coreMetaPath;
1355         }
1356         $GLOBALS['log']->debug("metadatafile=". $metadataFile);
1357
1358         return $metadataFile;
1359     }
1360
1361
1362     /**
1363      * Returns an array composing of the breadcrumbs to use for the module title
1364      *
1365      * @param bool $browserTitle true if the returned string is being used for the browser title, meaning
1366      *                           there should be no HTML in the string
1367      * @return array
1368      */
1369     protected function _getModuleTitleParams($browserTitle = false)
1370     {
1371         $params = array($this->_getModuleTitleListParam($browserTitle));
1372                 //$params = array();
1373         if (isset($this->action)){
1374             switch ($this->action) {
1375             case 'EditView':
1376                 if(!empty($this->bean->id) && (empty($_REQUEST['isDuplicate']) || $_REQUEST['isDuplicate'] === 'false')) {
1377                     $params[] = "<a href='index.php?module={$this->module}&action=DetailView&record={$this->bean->id}'>".$this->bean->get_summary_text()."</a>";
1378                     $params[] = $GLOBALS['app_strings']['LBL_EDIT_BUTTON_LABEL'];
1379                 }
1380                 else
1381                     $params[] = $GLOBALS['app_strings']['LBL_CREATE_BUTTON_LABEL'];
1382                 break;
1383             case 'DetailView':
1384                 $beanName = $this->bean->get_summary_text();
1385                 $params[] = $beanName;
1386                 break;
1387             }
1388         }
1389
1390         return $params;
1391     }
1392
1393     /**
1394      * Returns the portion of the array that will represent the listview in the breadcrumb
1395      *
1396      * @param bool $browserTitle true if the returned string is being used for the browser title, meaning
1397      *                           there should be no HTML in the string
1398      * @return string
1399      */
1400     protected function _getModuleTitleListParam( $browserTitle = false )
1401     {
1402         global $current_user;
1403         global $app_strings;
1404
1405         if(!empty($GLOBALS['app_list_strings']['moduleList'][$this->module]))
1406                 $firstParam = $GLOBALS['app_list_strings']['moduleList'][$this->module];
1407         else
1408                 $firstParam = $this->module;
1409
1410         $iconPath = $this->getModuleTitleIconPath($this->module);
1411         if($this->action == "ListView" || $this->action == "index") {
1412             if (!empty($iconPath) && !$browserTitle) {
1413                 if (SugarThemeRegistry::current()->directionality == "ltr") {
1414                         return $app_strings['LBL_SEARCH']."&nbsp;"
1415                                  . "$firstParam";
1416
1417                 } else {
1418                                         return "$firstParam"
1419                                              . "&nbsp;".$app_strings['LBL_SEARCH'];
1420                 }
1421                         } else {
1422                                 return $firstParam;
1423                         }
1424         }
1425         else {
1426                     if (!empty($iconPath) && !$browserTitle) {
1427                                 //return "<a href='index.php?module={$this->module}&action=index'>$this->module</a>";
1428                         } else {
1429                                 return $firstParam;
1430                         }
1431         }
1432     }
1433
1434     protected function getModuleTitleIconPath($module)
1435     {
1436         $iconPath = "";
1437         if(is_file(SugarThemeRegistry::current()->getImageURL('icon_'.$module.'_32.png',false))) {
1438                 $iconPath = SugarThemeRegistry::current()->getImageURL('icon_'.$module.'_32.png');
1439         }
1440         else if (is_file(SugarThemeRegistry::current()->getImageURL('icon_'.ucfirst($module).'_32.png',false))) {
1441                 $iconPath = SugarThemeRegistry::current()->getImageURL('icon_'.ucfirst($module).'_32.png');
1442         }
1443         return $iconPath;
1444     }
1445
1446     /**
1447      * Returns the string which will be shown in the browser's title; defaults to using the same breadcrumb
1448      * as in the module title
1449      *
1450      * @return string
1451      */
1452     public function getBrowserTitle()
1453     {
1454         global $app_strings;
1455
1456         $browserTitle = $app_strings['LBL_BROWSER_TITLE'];
1457         if ( $this->module == 'Users' && ($this->action == 'SetTimezone' || $this->action == 'Login') )
1458             return $browserTitle;
1459         $params = $this->_getModuleTitleParams(true);
1460         foreach ($params as $value )
1461             $browserTitle = strip_tags($value) . ' &raquo; ' . $browserTitle;
1462
1463         return $browserTitle;
1464     }
1465
1466     /**
1467      * Returns the correct breadcrumb symbol according to theme's directionality setting
1468      *
1469      * @return string
1470      */
1471     public function getBreadCrumbSymbol()
1472     {
1473         if(SugarThemeRegistry::current()->directionality == "ltr") {
1474                 return "<span class='pointer'>&raquo;</span>";
1475         }
1476         else {
1477                 return "<span class='pointer'>&laquo;</span>";
1478         }
1479     }
1480
1481     /**
1482      * Fetch config values to be put into an array for JavaScript
1483      *
1484      * @return array
1485      */
1486     protected function getSugarConfigJS(){
1487         global $sugar_config;
1488
1489         // Set all the config parameters in the JS config as necessary
1490         $config_js = array();
1491         // AjaxUI stock banned modules
1492         $config_js[] = "SUGAR.config.stockAjaxBannedModules = ".json_encode(ajaxBannedModules()).";";
1493         if ( isset($sugar_config['quicksearch_querydelay']) ) {
1494             $config_js[] = $this->prepareConfigVarForJs('quicksearch_querydelay', $sugar_config['quicksearch_querydelay']);
1495         }
1496         if ( empty($sugar_config['disableAjaxUI']) ) {
1497             $config_js[] = "SUGAR.config.disableAjaxUI = false;";
1498         }
1499         else{
1500             $config_js[] = "SUGAR.config.disableAjaxUI = true;";
1501         }
1502         if ( !empty($sugar_config['addAjaxBannedModules']) ){
1503             $config_js[] = $this->prepareConfigVarForJs('addAjaxBannedModules', $sugar_config['addAjaxBannedModules']);
1504         }
1505         if ( !empty($sugar_config['overrideAjaxBannedModules']) ){
1506             $config_js[] = $this->prepareConfigVarForJs('overrideAjaxBannedModules', $sugar_config['overrideAjaxBannedModules']);
1507         }
1508         if (!empty($sugar_config['js_available']) && is_array ($sugar_config['js_available']))
1509         {
1510             foreach ($sugar_config['js_available'] as $configKey)
1511             {
1512                 if (isset($sugar_config[$configKey]))
1513                 {
1514                     $jsVariableStatement = $this->prepareConfigVarForJs($configKey, $sugar_config[$configKey]);
1515                     if (!array_search($jsVariableStatement, $config_js))
1516                     {
1517                         $config_js[] = $jsVariableStatement;
1518                     }
1519                 }
1520             }
1521         }
1522
1523         return $config_js;
1524     }
1525
1526     /**
1527      * Utility method to convert sugar_config values into a JS acceptable format.
1528      *
1529      * @param string $key       Config Variable Name
1530      * @param string $value     Config Variable Value
1531      * @return string
1532      */
1533     protected function prepareConfigVarForJs($key, $value)
1534     {
1535         $value = json_encode($value);
1536         return "SUGAR.config.{$key} = {$value};";
1537     }
1538
1539     /**
1540      * getHelpText
1541      *
1542      * This is a protected function that returns the help text portion.  It is called from getModuleTitle.
1543      *
1544      * @param $module String the formatted module name
1545      * @return $theTitle String the HTML for the help text
1546      */
1547     protected function getHelpText($module)
1548     {
1549         $createImageURL = SugarThemeRegistry::current()->getImageURL('create-record.gif');
1550         $url = ajaxLink("index.php?module=$module&action=EditView&return_module=$module&return_action=DetailView");
1551         $theTitle = <<<EOHTML
1552 &nbsp;
1553 <img src='{$createImageURL}' alt='{$GLOBALS['app_strings']['LNK_CREATE']}'>
1554 <a href="{$url}" class="utilsLink">
1555 {$GLOBALS['app_strings']['LNK_CREATE']}
1556 </a>
1557 EOHTML;
1558         return $theTitle;
1559     }
1560
1561     /**
1562      * Retrieves favicon corresponding to currently requested module
1563      *
1564      * @return array
1565      */
1566     protected function getFavicon()
1567     {
1568         // get favicon
1569         if(isset($GLOBALS['sugar_config']['default_module_favicon']))
1570             $module_favicon = $GLOBALS['sugar_config']['default_module_favicon'];
1571         else
1572             $module_favicon = false;
1573
1574         $themeObject = SugarThemeRegistry::current();
1575
1576         $favicon = '';
1577         if ( $module_favicon )
1578             $favicon = $themeObject->getImageURL($this->module.'.gif',false);
1579         if ( !sugar_is_file($favicon) || !$module_favicon )
1580             $favicon = $themeObject->getImageURL('sugar_icon.ico',false);
1581
1582         $extension = pathinfo($favicon, PATHINFO_EXTENSION);
1583         switch ($extension)
1584         {
1585             case 'png':
1586                 $type = 'image/png';
1587                 break;
1588             case 'ico':
1589                 // fall through
1590             default:
1591                 $type = 'image/x-icon';
1592                 break;
1593         }
1594
1595         return array(
1596             'url'  => getJSPath($favicon),
1597             'type' => $type,
1598         );
1599     }
1600
1601
1602     /**
1603      * getCustomFilePathIfExists
1604      *
1605      * This function wraps a call to get_custom_file_if_exists from include/utils.php
1606      *
1607      * @param $file String of filename to check
1608      * @return $file String of filename including custom directory if found
1609      */
1610     protected function getCustomFilePathIfExists($file)
1611     {
1612         return get_custom_file_if_exists($file);
1613     }
1614
1615
1616     /**
1617      * fetchTemplate
1618      *
1619      * This function wraps the call to the fetch function of the Smarty variable for the view
1620      *
1621      * @param $file String path of the file to fetch
1622      * @return $content String content from resulting Smarty fetch operation on template
1623      */
1624     protected function fetchTemplate($file)
1625     {
1626         return $this->ss->fetch($file);
1627     }
1628
1629     /**
1630      * handles the tracker output, and adds a link and a shortened name.
1631      * given html safe input, it will preserve html safety
1632      *
1633      * @param array $history - returned from the tracker
1634      * @return array augmented history with image link and shortened name
1635      */
1636     protected function processRecentRecords($history) {
1637         foreach ( $history as $key => $row ) {
1638             $history[$key]['item_summary_short'] = to_html(getTrackerSubstring($row['item_summary'])); //bug 56373 - need to re-HTML-encode
1639             $history[$key]['image'] = SugarThemeRegistry::current()
1640                 ->getImage($row['module_name'],'border="0" align="absmiddle"',null,null,'.gif',$row['item_summary']);
1641         }
1642         return $history;
1643     }
1644
1645     /**
1646          * Determines whether the state of the post global array indicates there was an error uploading a
1647      * file that exceeds the post_max_size setting.  Such an error can be detected if:
1648      *  1. The Server['REQUEST_METHOD'] will still point to POST
1649      *  2. POST and FILES global arrays will be returned empty despite the request method
1650      * This also results in a redirect to the home page (due to lack of module and action in POST)
1651      *
1652          * @return boolean indicating true or false
1653          */
1654     public function checkPostMaxSizeError(){
1655          //if the referrer is post, and the post array is empty, then an error has occurred, most likely
1656          //while uploading a file that exceeds the post_max_size.
1657          if(empty($_FILES) && empty($_POST) && isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) == 'post'){
1658              $GLOBALS['log']->fatal($GLOBALS['app_strings']['UPLOAD_ERROR_HOME_TEXT']);
1659              return true;
1660         }
1661         return false;
1662     }
1663
1664
1665 }