PageRenderTime 56ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/SugarCRM/include/MVC/View/SugarView.php

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