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