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