PageRenderTime 69ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/include/MVC/View/SugarView.php

https://bitbucket.org/cviolette/sugarcrm
PHP | 1626 lines | 1086 code | 211 blank | 329 comment | 236 complexity | ed5c6cb004cb28478d409b806f6bd6ce MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  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' => true, '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. foreach ( $history as $key => $row ) {
  394. $history[$key]['item_summary_short'] = getTrackerSubstring($row['item_summary']);
  395. $history[$key]['image'] = SugarThemeRegistry::current()
  396. ->getImage($row['module_name'],'border="0" align="absmiddle"',null,null,'.gif',$row['item_summary']);
  397. }
  398. $ss->assign("recentRecords",$history);
  399. }
  400. $bakModStrings = $mod_strings;
  401. if (isset($_SESSION["authenticated_user_id"]) ) {
  402. // get the module list
  403. $moduleTopMenu = array();
  404. $max_tabs = $current_user->getPreference('max_tabs');
  405. // Attempt to correct if max tabs count is extremely high.
  406. if ( !isset($max_tabs) || $max_tabs <= 0 || $max_tabs > 10 ) {
  407. $max_tabs = $GLOBALS['sugar_config']['default_max_tabs'];
  408. $current_user->setPreference('max_tabs', $max_tabs, 0, 'global');
  409. }
  410. $moduleTab = $this->_getModuleTab();
  411. $ss->assign('MODULE_TAB',$moduleTab);
  412. // See if they are using grouped tabs or not (removed in 6.0, returned in 6.1)
  413. $user_navigation_paradigm = $current_user->getPreference('navigation_paradigm');
  414. if ( !isset($user_navigation_paradigm) ) {
  415. $user_navigation_paradigm = $GLOBALS['sugar_config']['default_navigation_paradigm'];
  416. }
  417. // Get the full module list for later use
  418. foreach ( query_module_access_list($current_user) as $module ) {
  419. // Bug 25948 - Check for the module being in the moduleList
  420. if ( isset($app_list_strings['moduleList'][$module]) ) {
  421. $fullModuleList[$module] = $app_list_strings['moduleList'][$module];
  422. }
  423. }
  424. if(!should_hide_iframes()) {
  425. $iFrame = new iFrame();
  426. $frames = $iFrame->lookup_frames('tab');
  427. foreach($frames as $key => $values){
  428. $fullModuleList[$key] = $values;
  429. }
  430. }
  431. elseif (isset($fullModuleList['iFrames'])) {
  432. unset($fullModuleList['iFrames']);
  433. }
  434. if ( $user_navigation_paradigm == 'gm' && isset($themeObject->group_tabs) && $themeObject->group_tabs) {
  435. // We are using grouped tabs
  436. require_once('include/GroupedTabs/GroupedTabStructure.php');
  437. $groupedTabsClass = new GroupedTabStructure();
  438. $modules = query_module_access_list($current_user);
  439. //handle with submoremodules
  440. $max_tabs = $current_user->getPreference('max_tabs');
  441. // If the max_tabs isn't set incorrectly, set it within the range, to the default max sub tabs size
  442. if ( !isset($max_tabs) || $max_tabs <= 0 || $max_tabs > 10){
  443. // We have a default value. Use it
  444. if(isset($GLOBALS['sugar_config']['default_max_tabs'])){
  445. $max_tabs = $GLOBALS['sugar_config']['default_max_tabs'];
  446. }
  447. else{
  448. $max_tabs = 8;
  449. }
  450. }
  451. $subMoreModules = false;
  452. $groupTabs = $groupedTabsClass->get_tab_structure(get_val_array($modules));
  453. // We need to put this here, so the "All" group is valid for the user's preference.
  454. $groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules'] = $fullModuleList;
  455. // Setup the default group tab.
  456. $allGroup = $app_strings['LBL_TABGROUP_ALL'];
  457. $ss->assign('currentGroupTab',$allGroup);
  458. $currentGroupTab = $allGroup;
  459. $usersGroup = $current_user->getPreference('theme_current_group');
  460. // Figure out which tab they currently have selected (stored as a user preference)
  461. if ( !empty($usersGroup) && isset($groupTabs[$usersGroup]) ) {
  462. $currentGroupTab = $usersGroup;
  463. } else {
  464. $current_user->setPreference('theme_current_group',$currentGroupTab);
  465. }
  466. $ss->assign('currentGroupTab',$currentGroupTab);
  467. $usingGroupTabs = true;
  468. } else {
  469. // Setup the default group tab.
  470. $ss->assign('currentGroupTab',$app_strings['LBL_TABGROUP_ALL']);
  471. $usingGroupTabs = false;
  472. $groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules'] = $fullModuleList;
  473. }
  474. $topTabList = array();
  475. // Now time to go through each of the tab sets and fix them up.
  476. foreach ( $groupTabs as $tabIdx => $tabData ) {
  477. $topTabs = $tabData['modules'];
  478. if ( ! is_array($topTabs) ) {
  479. $topTabs = array();
  480. }
  481. $extraTabs = array();
  482. // Split it in to the tabs that go across the top, and the ones that are on the extra menu.
  483. if ( count($topTabs) > $max_tabs ) {
  484. $extraTabs = array_splice($topTabs,$max_tabs);
  485. }
  486. // Make sure the current module is accessable through one of the top tabs
  487. if ( !isset($topTabs[$moduleTab]) ) {
  488. // Nope, we need to add it.
  489. // First, take it out of the extra menu, if it's there
  490. if ( isset($extraTabs[$moduleTab]) ) {
  491. unset($extraTabs[$moduleTab]);
  492. }
  493. if ( count($topTabs) >= $max_tabs - 1 ) {
  494. // We already have the maximum number of tabs, so we need to shuffle the last one
  495. // from the top to the first one of the extras
  496. $lastElem = array_splice($topTabs,$max_tabs-1);
  497. $extraTabs = $lastElem + $extraTabs;
  498. }
  499. if ( !empty($moduleTab) ) {
  500. $topTabs[$moduleTab] = $app_list_strings['moduleList'][$moduleTab];
  501. }
  502. }
  503. /*
  504. // This was removed, but I like the idea, so I left the code in here in case we decide to turn it back on
  505. // If we are using group tabs, add all the "hidden" tabs to the end of the extra menu
  506. if ( $usingGroupTabs ) {
  507. foreach($fullModuleList as $moduleKey => $module ) {
  508. if ( !isset($topTabs[$moduleKey]) && !isset($extraTabs[$moduleKey]) ) {
  509. $extraTabs[$moduleKey] = $module;
  510. }
  511. }
  512. }
  513. */
  514. // Get a unique list of the top tabs so we can build the popup menus for them
  515. foreach ( $topTabs as $moduleKey => $module ) {
  516. $topTabList[$moduleKey] = $module;
  517. }
  518. $groupTabs[$tabIdx]['modules'] = $topTabs;
  519. $groupTabs[$tabIdx]['extra'] = $extraTabs;
  520. }
  521. }
  522. if ( isset($topTabList) && is_array($topTabList) ) {
  523. // Adding shortcuts array to menu array for displaying shortcuts associated with each module
  524. $shortcutTopMenu = array();
  525. foreach($topTabList as $module_key => $label) {
  526. global $mod_strings;
  527. $mod_strings = return_module_language($current_language, $module_key);
  528. foreach ( $this->getMenu($module_key) as $key => $menu_item ) {
  529. $shortcutTopMenu[$module_key][$key] = array(
  530. "URL" => $menu_item[0],
  531. "LABEL" => $menu_item[1],
  532. "MODULE_NAME" => $menu_item[2],
  533. "IMAGE" => $themeObject
  534. ->getImage($menu_item[2],"border='0' align='absmiddle'",null,null,'.gif',$menu_item[1]),
  535. "ID" => $menu_item[2]."_link",
  536. );
  537. }
  538. }
  539. $ss->assign("groupTabs",$groupTabs);
  540. $ss->assign("shortcutTopMenu",$shortcutTopMenu);
  541. $ss->assign('USE_GROUP_TABS',$usingGroupTabs);
  542. // This is here for backwards compatibility, someday, somewhere, it will be able to be removed
  543. $ss->assign("moduleTopMenu",$groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules']);
  544. $ss->assign("moduleExtraMenu",$groupTabs[$app_strings['LBL_TABGROUP_ALL']]['extra']);
  545. }
  546. if ( isset($extraTabs) && is_array($extraTabs) ) {
  547. // Adding shortcuts array to extra menu array for displaying shortcuts associated with each module
  548. $shortcutExtraMenu = array();
  549. foreach($extraTabs as $module_key => $label) {
  550. global $mod_strings;
  551. $mod_strings = return_module_language($current_language, $module_key);
  552. foreach ( $this->getMenu($module_key) as $key => $menu_item ) {
  553. $shortcutExtraMenu[$module_key][$key] = array(
  554. "URL" => $menu_item[0],
  555. "LABEL" => $menu_item[1],
  556. "MODULE_NAME" => $menu_item[2],
  557. "IMAGE" => $themeObject
  558. ->getImage($menu_item[2],"border='0' align='absmiddle'",null,null,'.gif',$menu_item[1]),
  559. "ID" => $menu_item[2]."_link",
  560. );
  561. }
  562. }
  563. $ss->assign("shortcutExtraMenu",$shortcutExtraMenu);
  564. }
  565. if(!empty($current_user)){
  566. $ss->assign("max_tabs", $current_user->getPreference("max_tabs"));
  567. }
  568. $imageURL = SugarThemeRegistry::current()->getImageURL("dashboard.png");
  569. $homeImage = "<img src='$imageURL'>";
  570. $ss->assign("homeImage",$homeImage);
  571. global $mod_strings;
  572. $mod_strings = $bakModStrings;
  573. $headerTpl = $themeObject->getTemplate('header.tpl');
  574. if (inDeveloperMode() )
  575. $ss->clear_compiled_tpl($headerTpl);
  576. if ($retModTabs)
  577. {
  578. return $ss->fetch($themeObject->getTemplate('_headerModuleList.tpl'));
  579. } else {
  580. $ss->display($headerTpl);
  581. $this->includeClassicFile('modules/Administration/DisplayWarnings.php');
  582. $errorMessages = SugarApplication::getErrorMessages();
  583. if ( !empty($errorMessages)) {
  584. foreach ( $errorMessages as $error_message ) {
  585. echo('<p class="error">' . $error_message.'</p>');
  586. }
  587. }
  588. }
  589. }
  590. function getModuleMenuHTML()
  591. {
  592. }
  593. /**
  594. * If the view is classic then this method will include the file and
  595. * setup any global variables.
  596. *
  597. * @param string $file
  598. */
  599. public function includeClassicFile(
  600. $file
  601. )
  602. {
  603. global $sugar_config, $theme, $current_user, $sugar_version, $sugar_flavor, $mod_strings, $app_strings, $app_list_strings, $action;
  604. global $gridline, $request_string, $modListHeader, $dashletData, $authController, $locale, $currentModule, $import_bean_map, $image_path, $license;
  605. global $user_unique_key, $server_unique_key, $barChartColors, $modules_exempt_from_availability_check, $dictionary, $current_language, $beanList, $beanFiles, $sugar_build, $sugar_codename;
  606. global $timedate, $login_error; // cn: bug 13855 - timedate not available to classic views.
  607. if (!empty($this->module))
  608. $currentModule = $this->module;
  609. require_once ($file);
  610. }
  611. protected function _displayLoginJS()
  612. {
  613. global $sugar_config, $timedate;
  614. if(isset($this->bean->module_dir)){
  615. echo "<script>var module_sugar_grp1 = '{$this->bean->module_dir}';</script>";
  616. }
  617. if(isset($_REQUEST['action'])){
  618. echo "<script>var action_sugar_grp1 = '{$_REQUEST['action']}';</script>";
  619. }
  620. echo '<script>jscal_today = 1000*' . $timedate->asUserTs($timedate->getNow()) . '; if(typeof app_strings == "undefined") app_strings = new Array();</script>';
  621. if (!is_file(sugar_cached("include/javascript/sugar_grp1.js"))) {
  622. $_REQUEST['root_directory'] = ".";
  623. require_once("jssource/minify_utils.php");
  624. ConcatenateFiles(".");
  625. }
  626. echo getVersionedScript('cache/include/javascript/sugar_grp1_jquery.js');
  627. echo getVersionedScript('cache/include/javascript/sugar_grp1_yui.js');
  628. echo getVersionedScript('cache/include/javascript/sugar_grp1.js');
  629. echo getVersionedScript('include/javascript/calendar.js');
  630. echo <<<EOQ
  631. <script>
  632. if ( typeof(SUGAR) == 'undefined' ) {SUGAR = {}};
  633. if ( typeof(SUGAR.themes) == 'undefined' ) SUGAR.themes = {};
  634. </script>
  635. EOQ;
  636. if(isset( $sugar_config['disc_client']) && $sugar_config['disc_client'])
  637. echo getVersionedScript('modules/Sync/headersync.js');
  638. }
  639. /**
  640. * Get JS validation code for views
  641. */
  642. public static function getJavascriptValidation()
  643. {
  644. global $timedate;
  645. $cal_date_format = $timedate->get_cal_date_format();
  646. $timereg = $timedate->get_regular_expression($timedate->get_time_format());
  647. $datereg = $timedate->get_regular_expression($timedate->get_date_format());
  648. $date_pos = '';
  649. foreach ($datereg['positions'] as $type => $pos) {
  650. if (empty($date_pos)) {
  651. $date_pos .= "'$type': $pos";
  652. } else {
  653. $date_pos .= ",'$type': $pos";
  654. }
  655. }
  656. $time_separator = $timedate->timeSeparator();
  657. $hour_offset = $timedate->getUserUTCOffset() * 60;
  658. // Add in the number formatting styles here as well, we have been handling this with individual modules.
  659. require_once ('modules/Currencies/Currency.php');
  660. list ($num_grp_sep, $dec_sep) = get_number_seperators();
  661. $the_script = "<script type=\"text/javascript\">\n" . "\tvar time_reg_format = '" .
  662. $timereg['format'] . "';\n" . "\tvar date_reg_format = '" .
  663. $datereg['format'] . "';\n" . "\tvar date_reg_positions = { $date_pos };\n" .
  664. "\tvar time_separator = '$time_separator';\n" .
  665. "\tvar cal_date_format = '$cal_date_format';\n" .
  666. "\tvar time_offset = $hour_offset;\n" . "\tvar num_grp_sep = '$num_grp_sep';\n" .
  667. "\tvar dec_sep = '$dec_sep';\n" . "</script>";
  668. return $the_script;
  669. }
  670. /**
  671. * Called from process(). This method will display the correct javascript.
  672. */
  673. protected function _displayJavascript()
  674. {
  675. global $locale, $sugar_config, $timedate;
  676. if ($this->_getOption('show_javascript')) {
  677. if (!$this->_getOption('show_header')) {
  678. $langHeader = get_language_header();
  679. echo <<<EOHTML
  680. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  681. <html {$langHeader}>
  682. <head>
  683. EOHTML;
  684. }
  685. $js_vars = array(
  686. "sugar_cache_dir" => "cache/",
  687. );
  688. if(isset($this->bean->module_dir)){
  689. $js_vars['module_sugar_grp1'] = $this->bean->module_dir;
  690. }
  691. if(isset($_REQUEST['action'])){
  692. $js_vars['action_sugar_grp1'] = $_REQUEST['action'];
  693. }
  694. echo '<script>jscal_today = 1000*' . $timedate->asUserTs($timedate->getNow()) . '; if(typeof app_strings == "undefined") app_strings = new Array();</script>';
  695. 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"))) {
  696. $_REQUEST['root_directory'] = ".";
  697. require_once("jssource/minify_utils.php");
  698. ConcatenateFiles(".");
  699. }
  700. echo getVersionedScript('cache/include/javascript/sugar_grp1_jquery.js');
  701. echo getVersionedScript('cache/include/javascript/sugar_grp1_yui.js');
  702. echo getVersionedScript('cache/include/javascript/sugar_grp1.js');
  703. echo getVersionedScript('include/javascript/calendar.js');
  704. // output necessary config js in the top of the page
  705. $config_js = $this->getSugarConfigJS();
  706. if(!empty($config_js)){
  707. echo "<script>\n".implode("\n", $config_js)."</script>\n";
  708. }
  709. if ( isset($sugar_config['email_sugarclient_listviewmaxselect']) ) {
  710. echo "<script>SUGAR.config.email_sugarclient_listviewmaxselect = {$GLOBALS['sugar_config']['email_sugarclient_listviewmaxselect']};</script>";
  711. }
  712. $image_server = (defined('TEMPLATE_URL'))?TEMPLATE_URL . '/':'';
  713. echo '<script type="text/javascript">SUGAR.themes.image_server="' . $image_server . '";</script>'; // cn: bug 12274 - create session-stored key to defend against CSRF
  714. echo '<script type="text/javascript">var name_format = "' . $locale->getLocaleFormatMacro() . '";</script>';
  715. echo self::getJavascriptValidation();
  716. if (!is_file(sugar_cached('jsLanguage/') . $GLOBALS['current_language'] . '.js')) {
  717. require_once ('include/language/jsLanguage.php');
  718. jsLanguage::createAppStringsCache($GLOBALS['current_language']);
  719. }
  720. echo getVersionedScript('cache/jsLanguage/'. $GLOBALS['current_language'] . '.js', $GLOBALS['sugar_config']['js_lang_version']);
  721. echo $this->_getModLanguageJS();
  722. if(isset( $sugar_config['disc_client']) && $sugar_config['disc_client'])
  723. echo getVersionedScript('modules/Sync/headersync.js');
  724. //echo out the $js_vars variables as javascript variables
  725. echo "<script type='text/javascript'>\n";
  726. foreach($js_vars as $var=>$value)
  727. {
  728. echo "var {$var} = '{$value}';\n";
  729. }
  730. echo "</script>\n";
  731. }
  732. }
  733. protected function _getModLanguageJS(){
  734. if (!is_file(sugar_cached('jsLanguage/') . $this->module . '/' . $GLOBALS['current_language'] . '.js')) {
  735. require_once ('include/language/jsLanguage.php');
  736. jsLanguage::createModuleStringsCache($this->module, $GLOBALS['current_language']);
  737. }
  738. return getVersionedScript("cache/jsLanguage/{$this->module}/". $GLOBALS['current_language'] . '.js', $GLOBALS['sugar_config']['js_lang_version']);
  739. }
  740. /**
  741. * Called from process(). This method will display the footer on the page.
  742. */
  743. public function displayFooter()
  744. {
  745. if (empty($this->responseTime)) {
  746. $this->_calculateFooterMetrics();
  747. }
  748. global $sugar_config;
  749. global $app_strings;
  750. global $mod_strings;
  751. $themeObject = SugarThemeRegistry::current();
  752. //decide whether or not to show themepicker, default is to show
  753. $showThemePicker = true;
  754. if (isset($sugar_config['showThemePicker'])) {
  755. $showThemePicker = $sugar_config['showThemePicker'];
  756. }
  757. echo "<!-- crmprint -->";
  758. $jsalerts = new jsAlerts();
  759. if ( !isset($_SESSION['isMobile']) )
  760. echo $jsalerts->getScript();
  761. $ss = new Sugar_Smarty();
  762. $ss->assign("AUTHENTICATED",isset($_SESSION["authenticated_user_id"]));
  763. $ss->assign('MOD',return_module_language($GLOBALS['current_language'], 'Users'));
  764. $bottomLinkList = array();
  765. if (isset($this->action) && $this->action != "EditView") {
  766. $bottomLinkList['print'] = array($app_strings['LNK_PRINT'] => getPrintLink());
  767. }
  768. $bottomLinkList['backtotop'] = array($app_strings['LNK_BACKTOTOP'] => 'javascript:SUGAR.util.top();');
  769. $bottomLinksStr = "";
  770. foreach($bottomLinkList as $key => $value) {
  771. foreach($value as $text => $link) {
  772. $href = $link;
  773. if(substr($link, 0, 11) == "javascript:") {
  774. $onclick = " onclick=\"".substr($link,11)."\"";
  775. $href = "javascript:void(0)";
  776. } else {
  777. $onclick = "";
  778. }
  779. $imageURL = SugarThemeRegistry::current()->getImageURL($key.'.gif');
  780. $bottomLinksStr .= "<a href=\"{$href}\"";
  781. $bottomLinksStr .= (isset($onclick)) ? $onclick : "";
  782. $bottomLinksStr .= "><img src='{$imageURL}' alt=''>"; //keeping alt blank on purpose for 508 (text will be read instead)
  783. $bottomLinksStr .= " ".$text."</a>";
  784. }
  785. }
  786. $ss->assign("BOTTOMLINKS",$bottomLinksStr);
  787. if (SugarConfig::getInstance()->get('calculate_response_time', false))
  788. $ss->assign('STATISTICS',$this->_getStatistics());
  789. // Under the License referenced above, you are required to leave in all copyright statements in both
  790. // the code and end-user application.
  791. $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>';
  792. // The interactive user interfaces in modified source and object code
  793. // versions of this program must display Appropriate Legal Notices, as
  794. // required under Section 5 of the GNU General Public License version
  795. // 3. In accordance with Section 7(b) of the GNU General Public License
  796. // version 3, these Appropriate Legal Notices must retain the display
  797. // of the "Powered by SugarCRM" logo. If the display of the logo is
  798. // not reasonably feasible for technical reasons, the Appropriate
  799. // Legal Notices must display the words "Powered by SugarCRM".
  800. $attribLinkImg = "<img style='margin-top: 2px' border='0' width='120' height='34' src='include/images/poweredby_sugarcrm_65.png' alt='Powered By SugarCRM'>\n";
  801. // handle resizing of the company logo correctly on the fly
  802. $companyLogoURL = $themeObject->getImageURL('company_logo.png');
  803. $companyLogoURL_arr = explode('?', $companyLogoURL);
  804. $companyLogoURL = $companyLogoURL_arr[0];
  805. $company_logo_attributes = sugar_cache_retrieve('company_logo_attributes');
  806. if(!empty($company_logo_attributes)) {
  807. $ss->assign("COMPANY_LOGO_MD5", $company_logo_attributes[0]);
  808. $ss->assign("COMPANY_LOGO_WIDTH", $company_logo_attributes[1]);
  809. $ss->assign("COMPANY_LOGO_HEIGHT", $company_logo_attributes[2]);
  810. }
  811. else {
  812. // Always need to md5 the file
  813. $ss->assign("COMPANY_LOGO_MD5", md5_file($companyLogoURL));
  814. list($width,$height) = getimagesize($companyLogoURL);
  815. if ( $width > 212 || $height > 40 ) {
  816. $resizePctWidth = ($width - 212)/212;
  817. $resizePctHeight = ($height - 40)/40;
  818. if ( $resizePctWidth > $resizePctHeight )
  819. $resizeAmount = $width / 212;
  820. else
  821. $resizeAmount = $height / 40;
  822. $ss->assign("COMPANY_LOGO_WIDTH", round($width * (1/$resizeAmount)));
  823. $ss->assign("COMPANY_LOGO_HEIGHT", round($height * (1/$resizeAmount)));
  824. }
  825. else {
  826. $ss->assign("COMPANY_LOGO_WIDTH", $width);
  827. $ss->assign("COMPANY_LOGO_HEIGHT", $height);
  828. }
  829. // Let's cache the results
  830. sugar_cache_put('company_logo_attributes',
  831. array(
  832. $ss->get_template_vars("COMPANY_LOGO_MD5"),
  833. $ss->get_template_vars("COMPANY_LOGO_WIDTH"),
  834. $ss->get_template_vars("COMPANY_LOGO_HEIGHT")
  835. )
  836. );
  837. }
  838. $ss->assign("COMPANY_LOGO_URL",getJSPath($companyLogoURL)."&logo_md5=".$ss->get_template_vars("COMPANY_LOGO_MD5"));
  839. // Bug 38594 - Add in Trademark wording
  840. $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 />';
  841. //rrs bug: 20923 - if this image does not exist as per the license, then the proper image will be displayed regardless, so no need
  842. //to display an empty image here.
  843. if(file_exists('include/images/poweredby_sugarcrm_65.png')){
  844. $copyright .= $attribLinkImg;
  845. }
  846. // End Required Image
  847. $ss->assign('COPYRIGHT',$copyright);
  848. $ss->display(SugarThemeRegistry::current()->getTemplate('footer.tpl'));
  849. }
  850. /**
  851. * Called from process(). This method will display subpanels.
  852. */
  853. protected function _displaySubPanels()
  854. {
  855. 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'))) {
  856. $GLOBALS['focus'] = $this->bean;
  857. require_once ('include/SubPanel/SubPanelTiles.php');
  858. $subpanel = new SubPanelTiles($this->bean, $this->module);
  859. echo $subpanel->display();
  860. }
  861. }
  862. protected function _buildModuleList()
  863. {
  864. if (!empty($GLOBALS['current_user']) && empty($GLOBALS['modListHeader']))
  865. $GLOBALS['modListHeader'] = query_module_access_list($GLOBALS['current_user']);
  866. }
  867. /**
  868. * private method used in process() to determine the value of a passed in option
  869. *
  870. * @param string option - the option that we want to know the valye of
  871. * @param bool default - what the default value should be if we do not find the option
  872. *
  873. * @return bool - the value of the option
  874. */
  875. protected function _getOption(
  876. $option,
  877. $default = false
  878. )
  879. {
  880. if (!empty($this->options) && isset($this->options['show_all'])) {
  881. return $this->options['show_all'];
  882. } elseif (!empty($this->options) && isset($this->options[$option])) {
  883. return $this->options[$option];
  884. } else return $default;
  885. }
  886. /**
  887. * track
  888. * Private function to track information about the view request
  889. */
  890. private function _track()
  891. {
  892. if (empty($this->responseTime)) {
  893. $this->_calculateFooterMetrics();
  894. }
  895. if (empty($GLOBALS['current_user']->id)) {
  896. return;
  897. }
  898. $trackerManager = TrackerManager::getInstance();
  899. $trackerManager->save();
  900. }
  901. /**
  902. * Checks to see if the module name passed is valid; dies if it is not
  903. */
  904. protected function _checkModule()
  905. {
  906. if(!empty($this->module) && !file_exists('modules/'.$this->module)){
  907. $error = str_replace("[module]", "$this->module", $GLOBALS['app_strings']['ERR_CANNOT_FIND_MODULE']);
  908. $GLOBALS['log']->fatal($error);
  909. echo $error;
  910. die();
  911. }
  912. }
  913. public function renderJavascript()
  914. {
  915. if ($this->action !== 'Login')
  916. $this->_displayJavascript();
  917. else
  918. $this->_displayLoginJS();
  919. }
  920. private function _calculateFooterMetrics()
  921. {
  922. $endTime = microtime(true);
  923. $deltaTime = $endTime - $GLOBALS['startTime'];
  924. $this->responseTime = number_format(round($deltaTime, 2), 2);
  925. // Print out the resources used in constructing the page.
  926. $this->fileResources = count(get_included_files());
  927. }
  928. private function _getStatistics()
  929. {
  930. $endTime = microtime(true);
  931. $deltaTime = $endTime - $GLOBALS['startTime'];
  932. $response_time_string = $GLOBALS['app_strings']['LBL_SERVER_RESPONSE_TIME'] . ' ' . number_format(round($deltaTime, 2), 2) . ' ' . $GLOBALS['app_strings']['LBL_SERVER_RESPONSE_TIME_SECONDS'];
  933. $return = $response_time_string;
  934. // $return .= '<br />';
  935. if (!empty($GLOBALS['sugar_config']['show_page_resources'])) {
  936. // Print out the resources used in constructing the page.
  937. $included_files = get_included_files();
  938. // take all of the included files and make a list that does not allow for duplicates based on case
  939. // I believe the full get_include_files result set appears to have one entry for each file in real
  940. // case, and one entry in all lower case.
  941. $list_of_files_case_insensitive = array();
  942. foreach($included_files as $key => $name) {
  943. // preserve the first capitalization encountered.
  944. $list_of_files_case_insensitive[mb_strtolower($name) ] = $name;
  945. }
  946. $return .= $GLOBALS['app_strings']['LBL_SERVER_RESPONSE_RESOURCES'] . '(' . DBManager::getQueryCount() . ',' . sizeof($list_of_files_case_insensitive) . ')<br>';
  947. // Display performance of the internal and external caches....
  948. $cacheStats = SugarCache::instance()->getCacheStats();
  949. $return .= "External cache (hits/total=ratio) local ({$cacheStats['localHits']}/{$cacheStats['requests']}=" . round($cacheStats['localHits']*100/$cacheStats['requests'], 0) . "%)";
  950. $return .= " external ({$cacheStats['externalHits']}/{$cacheStats['requests']}=" . round($cacheStats['externalHits']*100/$cacheStats['requests'], 0) . "%)<br />";
  951. $return .= " misses ({$cacheStats['misses']}/{$cacheStats['requests']}=" . round($cacheStats['misses']*100/$cacheStats['requests'], 0) . "%)<br />";
  952. }
  953. $return .= $this->logMemoryStatistics();
  954. return $return;
  955. }
  956. /**
  957. * logMemoryStatistics
  958. *
  959. * This function returns a string message containing the memory statistics as well as writes to the memory_usage.log
  960. * file the memory statistics for the SugarView invocation.
  961. *
  962. * @param $newline String of newline character to use (defaults to </ br>)
  963. * @return $message String formatted message about memory statistics
  964. */
  965. protected function logMemoryStatistics($newline='<br>')
  966. {
  967. $log_message = '';
  968. if(!empty($GLOBALS['sugar_config']['log_memory_usage']))
  969. {
  970. if(function_exists('memory_get_usage'))
  971. {
  972. $memory_usage = memory_get_usage();
  973. $bytes = $GLOBALS['app_strings']['LBL_SERVER_MEMORY_BYTES'];
  974. $data = array($memory_usage, $bytes);
  975. $log_message = string_format($GLOBALS['app_strings']['LBL_SERVER_MEMORY_USAGE'], $data) . $newline;
  976. }
  977. if(function_exists('memory_get_peak_usage'))
  978. {
  979. $memory_peak_usage = memory_get_peak_usage();
  980. $bytes = $GLOBALS['app_strings']['LBL_SERVER_MEMORY_BYTES'];
  981. $data = array($memory_peak_usage, $bytes);
  982. $log_message .= string_format($GLOBALS['app_strings']['LBL_SERVER_PEAK_MEMORY_USAGE'], $data) . $newline;
  983. }
  984. if(!empty($log_message))
  985. {
  986. $data = array
  987. (
  988. !empty($this->module) ? $this->module : $GLOBALS['app_strings']['LBL_LINK_NONE'],
  989. !empty($this->action) ? $this->action : $GLOBALS['app_strings']['LBL_LINK_NONE'],
  990. );
  991. $output = string_format($GLOBALS['app_strings']['LBL_SERVER_MEMORY_LOG_MESSAGE'], $data) . $newline;
  992. $output .= $log_message;
  993. $fp = fopen("memory_usage.log", "ab");
  994. fwrite($fp, $output);
  995. fclose($fp);
  996. }
  997. }
  998. return $log_message;
  999. }
  1000. /**
  1001. * Loads the module shortcuts menu
  1002. *
  1003. * @param $module string optional, can specify module to retrieve menu for if not the current one
  1004. * @return array module menu
  1005. */
  1006. public function getMenu(
  1007. $module = null
  1008. )
  1009. {
  1010. global $current_language, $current_user, $mod_strings, $app_strings, $module_menu;
  1011. if ( empty($module) )
  1012. $module = $this->module;
  1013. //Need to make sure the mod_strings match the requested module or Menus may fail
  1014. $curr_mod_strings = $mod_strings;
  1015. $mod_strings = return_module_language ( $current_language, $module ) ;
  1016. $module_menu = array();
  1017. if (file_exists('modules/' . $module . '/Menu.php')) {
  1018. require('modules/' . $module . '/Menu.php');
  1019. }
  1020. if (file_exists('custom/modules/' . $module . '/Ext/Menus/menu.ext.php')) {
  1021. require('custom/modules/' . $module . '/Ext/Menus/menu.ext.php');
  1022. }
  1023. if (!file_exists('modules/' . $module . '/Menu.php')
  1024. && !file_exists('custom/modules/' . $module . '/Ext/Menus/menu.ext.php')
  1025. && !empty($GLOBALS['mod_strings']['LNK_NEW_RECORD'])) {
  1026. $module_menu[] = array("index.php?module=$module&action=EditView&return_module=$module&return_action=DetailView",
  1027. $GLOBALS['mod_strings']['LNK_NEW_RECORD'],"{$GLOBALS['app_strings']['LBL_CREATE_BUTTON_LABEL']}$module" ,$module );
  1028. $module_menu[] = array("index.php?module=$module&action=index", $GLOBALS['mod_strings']['LNK_LIST'],
  1029. $module, $module);
  1030. if ( ($this->bean instanceOf SugarBean) && !empty($this->bean->importable) )
  1031. if ( !empty($mod_strings['LNK_IMPORT_'.strtoupper($module)]) )
  1032. $module_menu[] = array("index.php?module=Import&action=Step1&import_module=$module&return_module=$module&return_action=index",
  1033. $mod_strings['LNK_IMPORT_'.strtoupper($module)], "Import", $module);
  1034. else
  1035. $module_menu[] = array("index.php?module=Import&action=Step1&import_module=$module&return_module=$module&return_action=index",
  1036. $app_strings['LBL_IMPORT'], "Import", $module);
  1037. }
  1038. if (file_exists('custom/application/Ext/Menus/menu.ext.php')) {
  1039. require('custom/application/Ext/Menus/menu.ext.php');
  1040. }
  1041. $mod_strings = $curr_mod_strings;
  1042. $builtModuleMenu = $module_menu;
  1043. unset($module_menu);
  1044. return $builtModuleMenu;
  1045. }
  1046. /**
  1047. * Returns the module name which should be highlighted in the module menu
  1048. */
  1049. protected function _getModuleTab()
  1050. {
  1051. global $app_list_strings, $moduleTabMap, $current_user;
  1052. $userTabs = query_module_access_list($current_user);
  1053. //If the home tab is in the user array use it as the default tab, otherwise use the first element in the tab array
  1054. $defaultTab = (in_array("Home",$userTabs)) ? "Home" : key($userTabs);
  1055. // Need to figure out what tab this module belongs to, most modules have their own tabs, but there are exceptions.
  1056. if ( !empty($_REQUEST['module_tab']) )
  1057. return $_REQUEST['module_tab'];
  1058. elseif ( isset($moduleTabMap[$this->module]) )
  1059. return $moduleTabMap[$this->module];
  1060. // Special cases
  1061. elseif ( $this->module == 'MergeRecords' )
  1062. return !empty($_REQUEST['merge_module']) ? $_REQUEST['merge_module'] : $_REQUEST['return_module'];
  1063. elseif ( $this->module == 'Users' && $this->action == 'SetTimezone' )
  1064. return $defaultTab;
  1065. // Default anonymous pages to be under Home
  1066. elseif ( !isset($app_list_strings['moduleList'][$this->module]) )
  1067. return $defaultTab;
  1068. elseif ( isset($_REQUEST['action']) && $_REQUEST['action'] == "ajaxui" )
  1069. return $defaultTab;
  1070. else
  1071. return $this->module;
  1072. }
  1073. /**
  1074. * Return the "breadcrumbs" to display at the top of the page
  1075. *
  1076. * @param bool $show_help optional, true if we show the help links
  1077. * @return HTML string containing breadcrumb title
  1078. */
  1079. public function getModuleTitle(
  1080. $show_help = true
  1081. )
  1082. {
  1083. global $sugar_version, $sugar_flavor, $server_unique_key, $current_language, $action;
  1084. $theTitle = "<div class='moduleTitle'>\n";
  1085. $module = preg_replace("/ /","",$this->module);
  1086. $params = $this->_getModuleTitleParams();
  1087. $index = 0;
  1088. if(SugarThemeRegistry::current()->directionality == "rtl") {
  1089. $params = array_reverse($params);
  1090. }
  1091. if(count($params) > 1) {
  1092. array_shift($params);
  1093. }
  1094. $count = count($params);
  1095. $paramString = '';
  1096. foreach($params as $parm){
  1097. $index++;
  1098. $paramString .= $parm;
  1099. if($index < $count){
  1100. $paramString .= $this->getBreadCrumbSymbol();
  1101. }
  1102. }
  1103. if(!empty($paramString)){
  1104. $theTitle .= "<h2> $paramString </h2>\n";
  1105. }
  1106. $theTitle .= "<span class='utils'>";
  1107. $createImageURL = SugarThemeRegistry::current()->getImageURL('create-record.gif');
  1108. $url = ajaxLink("index.php?module=$module&action=EditView&return_module=$module&return_action=DetailView");
  1109. $theTitle .= <<<EOHTML
  1110. &nbsp;
  1111. <a id="create_image" href="{$url}" class="utilsLink">
  1112. <img src='{$createImageURL}' alt='{$GLOBALS['app_strings']['LNK_CREATE']}'></a>
  1113. <a id="create_link" href="{$url}" class="utilsLink">
  1114. {$GLOBALS['app_strings']['LNK_CREATE']}
  1115. </a>
  1116. EOHTML;
  1117. $theTitle .= "</span><div class='clear'></div></div>\n";
  1118. return $theTitle;
  1119. }
  1120. /**
  1121. * Return the metadata file that will be used by this view.
  1122. *
  1123. * @return string File location of the metadata file.
  1124. */
  1125. public function getMetaDataFile()
  1126. {
  1127. $metadataFile = null;
  1128. $foundViewDefs = false;
  1129. $viewDef = strtolower($this->type) . 'viewdefs';
  1130. $coreMetaPath = 'modules/'.$this->module.'/metadata/' . $viewDef . '.php';
  1131. if(file_exists('custom/' .$coreMetaPath )){
  1132. $metadataFile = 'custom/' . $coreMetaPath;
  1133. $foundViewDefs = true;
  1134. }else{
  1135. if(file_exists('custom/modules/'.$this->module.'/metadata/metafiles.php')){
  1136. require_once('custom/modules/'.$this->module.'/metadata/metafiles.php');
  1137. if(!empty($metafiles[$this->module][$viewDef])){
  1138. $metadataFile = $metafiles[$this->module][$viewDef];
  1139. $foundViewDefs = true;
  1140. }
  1141. }elseif(file_exists('modules/'.$this->module.'/metadata/metafiles.php')){
  1142. require_once('modules/'.$this->module.'/metadata/metafiles.php');
  1143. if(!empty($metafiles[$this->module][$viewDef])){
  1144. $metadataFile = $metafiles[$this->module][$viewDef];
  1145. $foundViewDefs = true;
  1146. }
  1147. }
  1148. }
  1149. if(!$foundViewDefs && file_exists($coreMetaPath)){
  1150. $metadataFile = $coreMetaPath;
  1151. }
  1152. $GLOBALS['log']->debug("metadatafile=". $metadataFile);
  1153. return $metadataFile;
  1154. }
  1155. /**
  1156. * Returns an array composing of the breadcrumbs to use for the module title
  1157. *
  1158. * @param bool $browserTitle true if the returned string is being used for the browser title, meaning
  1159. * there should be no HTML in the string
  1160. * @return array
  1161. */
  1162. protected function _getModuleTitleParams($browserTitle = false)
  1163. {
  1164. $params = array($this->_getModuleTitleListParam($browserTitle));
  1165. //$params = array();
  1166. if (isset($this->action)){
  1167. switch ($this->action) {
  1168. case 'EditView':
  1169. if(!empty($this->bean->id) && (empty($_REQUEST['isDuplicate']) || $_REQUEST['isDuplicate'] === 'false')) {
  1170. $params[] = "<a href='index.php?module={$this->module}&action=DetailView&record={$this->bean->id}'>".$this->bean->get_summary_text()."</a>";
  1171. $params[] = $GLOBALS['app_strings']['LBL_EDIT_BUTTON_LABEL'];
  1172. }
  1173. else
  1174. $params[] = $GLOBALS['app_strings']['LBL_CREATE_BUTTON_LABEL'];
  1175. break;
  1176. case 'DetailView':
  1177. $beanName = $this->bean->get_summary_text();
  1178. $params[] = $beanName;
  1179. break;
  1180. }
  1181. }
  1182. return $params;
  1183. }
  1184. /**
  1185. * Returns the portion of the array that will represent the listview in the breadcrumb
  1186. *
  1187. * @param bool $browserTitle true if the returned string is being used for the browser title, meaning
  1188. * there should be no HTML in the string
  1189. * @return string
  1190. */
  1191. protected function _getModuleTitleListParam( $browserTitle = false )
  1192. {
  1193. global $current_user;
  1194. global $app_strings;
  1195. if(!empty($GLOBALS['app_list_strings']['moduleList'][$this->module]))
  1196. $firstParam = $GLOBALS['app_list_strings']['moduleList'][$this->module];
  1197. else
  1198. $firstParam = $this->module;
  1199. $iconPath = $this->getModuleTitleIconPath($this->module);
  1200. if($this->action == "ListView" || $this->action == "index") {
  1201. if (!empty($iconPath) && !$browserTitle) {
  1202. if (SugarThemeRegistry::current()->directionality == "ltr") {
  1203. return $app_strings['LBL_SEARCH']."&nbsp;"
  1204. . "$firstParam";
  1205. } else {
  1206. return "$firstParam"
  1207. . "&nbsp;".$app_strings['LBL_SEARCH'];
  1208. }
  1209. } else {
  1210. return $firstParam;
  1211. }
  1212. }
  1213. else {
  1214. if (!empty($iconPath) && !$browserTitle) {
  1215. //return "<a href='index.php?module={$this->module}&action=index'>$this->module</a>";
  1216. } else {
  1217. return $firstParam;
  1218. }
  1219. }
  1220. }
  1221. protected function getModuleTitleIconPath($module)
  1222. {
  1223. $iconPath = "";
  1224. if(is_file(SugarThemeRegistry::current()->getImageURL('icon_'.$module.'_32.png',false))) {
  1225. $iconPath = SugarThemeRegistry::current()->getImageURL('icon_'.$module.'_32.png');
  1226. }
  1227. else if (is_file(SugarThemeRegistry::current()->getImageURL('icon_'.ucfirst($module).'_32.png',false))) {
  1228. $iconPath = SugarThemeRegistry::current()->getImageURL('icon_'.ucfirst($module).'_32.png');
  1229. }
  1230. return $iconPath;
  1231. }
  1232. /**
  1233. * Returns the string which will be shown in the browser's title; defaults to using the same breadcrumb
  1234. * as in the module title
  1235. *
  1236. * @return string
  1237. */
  1238. public function getBrowserTitle()
  1239. {
  1240. global $app_strings;
  1241. $browserTitle = $app_strings['LBL_BROWSER_TITLE'];
  1242. if ( $this->module == 'Users' && ($this->action == 'SetTimezone' || $this->action == 'Login') )
  1243. return $browserTitle;
  1244. $params = $this->_getModuleTitleParams(true);
  1245. foreach ($params as $value )
  1246. $browserTitle = strip_tags($value) . ' &raquo; ' . $browserTitle;
  1247. return $browserTitle;
  1248. }
  1249. /**
  1250. * Returns the correct breadcrumb symbol according to theme's directionality setting
  1251. *
  1252. * @return string
  1253. */
  1254. public function getBreadCrumbSymbol()
  1255. {
  1256. if(SugarThemeRegistry::current()->directionality == "ltr") {
  1257. return "<span class='pointer'>&raquo;</span>";
  1258. }
  1259. else {
  1260. return "<span class='pointer'>&laquo;</span>";
  1261. }
  1262. }
  1263. /**
  1264. * Fetch config values to be put into an array for JavaScript
  1265. *
  1266. * @return array
  1267. */
  1268. protected function getSugarConfigJS(){
  1269. global $sugar_config;
  1270. // Set all the config parameters in the JS config as necessary
  1271. $config_js = array();
  1272. // AjaxUI stock banned modules
  1273. $config_js[] = "SUGAR.config.stockAjaxBannedModules = ".json_encode(ajaxBannedModules()).";";
  1274. if ( isset($sugar_config['quicksearch_querydelay']) ) {
  1275. $config_js[] = $this->prepareConfigVarForJs('quicksearch_querydelay', $sugar_config['quicksearch_querydelay']);
  1276. }
  1277. if ( empty($sugar_config['disableAjaxUI']) ) {
  1278. $config_js[] = "SUGAR.config.disableAjaxUI = false;";
  1279. }
  1280. else{
  1281. $config_js[] = "SUGAR.config.disableAjaxUI = true;";
  1282. }
  1283. if ( !empty($sugar_config['addAjaxBannedModules']) ){
  1284. $config_js[] = $this->prepareConfigVarForJs('addAjaxBannedModules', $sugar_config['addAjaxBannedModules']);
  1285. }
  1286. if ( !empty($sugar_config['overrideAjaxBannedModules']) ){
  1287. $config_js[] = $this->prepareConfigVarForJs('overrideAjaxBannedModules', $sugar_config['overrideAjaxBannedModules']);
  1288. }
  1289. if (!empty($sugar_config['js_available']) && is_array ($sugar_config['js_available']))
  1290. {
  1291. foreach ($sugar_config['js_available'] as $configKey)
  1292. {
  1293. if (isset($sugar_config[$configKey]))
  1294. {
  1295. $jsVariableStatement = $this->prepareConfigVarForJs($configKey, $sugar_config[$configKey]);
  1296. if (!array_search($jsVariableStatement, $config_js))
  1297. {
  1298. $config_js[] = $jsVariableStatement;
  1299. }
  1300. }
  1301. }
  1302. }
  1303. return $config_js;
  1304. }
  1305. /**
  1306. * Utility method to convert sugar_config values into a JS acceptable format.
  1307. *
  1308. * @param string $key Config Variable Name
  1309. * @param string $value Config Variable Value
  1310. * @return string
  1311. */
  1312. protected function prepareConfigVarForJs($key, $value)
  1313. {
  1314. $value = json_encode($value);
  1315. return "SUGAR.config.{$key} = {$value};";
  1316. }
  1317. /**
  1318. * getHelpText
  1319. *
  1320. * This is a protected function that returns the help text portion. It is called from getModuleTitle.
  1321. *
  1322. * @param $module String the formatted module name
  1323. * @return $theTitle String the HTML for the help text
  1324. */
  1325. protected function getHelpText($module)
  1326. {
  1327. $createImageURL = SugarThemeRegistry::current()->getImageURL('create-record.gif');
  1328. $url = ajaxLink("index.php?module=$module&action=EditView&return_module=$module&return_action=DetailView");
  1329. $theTitle = <<<EOHTML
  1330. &nbsp;
  1331. <img src='{$createImageURL}' alt='{$GLOBALS['app_strings']['LNK_CREATE']}'>
  1332. <a href="{$url}" class="utilsLink">
  1333. {$GLOBALS['app_strings']['LNK_CREATE']}
  1334. </a>
  1335. EOHTML;
  1336. return $theTitle;
  1337. }
  1338. /**
  1339. * Retrieves favicon corresponding to currently requested module
  1340. *
  1341. * @return array
  1342. */
  1343. protected function getFavicon()
  1344. {
  1345. // get favicon
  1346. if(isset($GLOBALS['sugar_config']['default_module_favicon']))
  1347. $module_favicon = $GLOBALS['sugar_config']['default_module_favicon'];
  1348. else
  1349. $module_favicon = false;
  1350. $themeObject = SugarThemeRegistry::current();
  1351. $favicon = '';
  1352. if ( $module_favicon )
  1353. $favicon = $themeObject->getImageURL($this->module.'.gif',false);
  1354. if ( !sugar_is_file($favicon) || !$module_favicon )
  1355. $favicon = $themeObject->getImageURL('sugar_icon.ico',false);
  1356. $extension = pathinfo($favicon, PATHINFO_EXTENSION);
  1357. switch ($extension)
  1358. {
  1359. case 'png':
  1360. $type = 'image/png';
  1361. break;
  1362. case 'ico':
  1363. // fall through
  1364. default:
  1365. $type = 'image/x-icon';
  1366. break;
  1367. }
  1368. return array(
  1369. 'url' => getJSPath($favicon),
  1370. 'type' => $type,
  1371. );
  1372. }
  1373. /**
  1374. * getCustomFilePathIfExists
  1375. *
  1376. * This function wraps a call to get_custom_file_if_exists from include/utils.php
  1377. *
  1378. * @param $file String of filename to check
  1379. * @return $file String of filename including custom directory if found
  1380. */
  1381. protected function getCustomFilePathIfExists($file)
  1382. {
  1383. return get_custom_file_if_exists($file);
  1384. }
  1385. /**
  1386. * fetchTemplate
  1387. *
  1388. * This function wraps the call to the fetch function of the Smarty variable for the view
  1389. *
  1390. * @param $file String path of the file to fetch
  1391. * @return $content String content from resulting Smarty fetch operation on template
  1392. */
  1393. protected function fetchTemplate($file)
  1394. {
  1395. return $this->ss->fetch($file);
  1396. }
  1397. /**
  1398. * Determines whether the state of the post global array indicates there was an error uploading a
  1399. * file that exceeds the post_max_size setting. Such an error can be detected if:
  1400. * 1. The Server['REQUEST_METHOD'] will still point to POST
  1401. * 2. POST and FILES global arrays will be returned empty despite the request method
  1402. * This also results in a redirect to the home page (due to lack of module and action in POST)
  1403. *
  1404. * @return boolean indicating true or false
  1405. */
  1406. public function checkPostMaxSizeError(){
  1407. //if the referrer is post, and the post array is empty, then an error has occurred, most likely
  1408. //while uploading a file that exceeds the post_max_size.
  1409. if(empty($_FILES) && empty($_POST) && isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) == 'post'){
  1410. $GLOBALS['log']->fatal($GLOBALS['app_strings']['UPLOAD_ERROR_HOME_TEXT']);
  1411. return true;
  1412. }
  1413. return false;
  1414. }
  1415. }