2 /*********************************************************************************
3 * SugarCRM Community Edition is a customer relationship management program developed by
4 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
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.
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
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
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.
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.
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 ********************************************************************************/
40 * This array is meant to hold an objects/data that we would like to pass between
41 * the controller and the view. The bean will automatically be set for us, but this
42 * is meant to hold anything else.
44 var $view_object_map = array();
46 * The name of the current module.
50 * The name of the current action.
57 * Sugar_Smarty. This is useful if you have a view and a subview you can
58 * share the same smarty object.
62 * Any errors that occured this can either be set by the view or the controller or the model
64 var $errors = array();
66 * Set to true if you do not want to display errors from SugarView::displayErrors(); instead they will be returned
68 var $suppressDisplayErrors = false;
71 * Options for what UI elements to hide/show/
73 var $options = array('show_header' => true, 'show_title' => true, 'show_subpanels' => false, 'show_search' => true, 'show_footer' => true, 'show_javascript' => true, 'view_print' => false,);
79 * Constructor which will peform the setup.
81 public function SugarView(
83 $view_object_map = array()
90 $view_object_map = array()
94 $this->view_object_map = $view_object_map;
95 $this->action = $GLOBALS['action'];
96 $this->module = $GLOBALS['module'];
100 protected function _initSmarty()
102 $this->ss = new Sugar_Smarty();
103 $this->ss->assign('MOD', $GLOBALS['mod_strings']);
104 $this->ss->assign('APP', $GLOBALS['app_strings']);
108 * This method will be called from the controller and is not meant to be overridden.
110 public function process()
112 LogicHook::initialize();
113 $this->_checkModule();
115 //trackView has to be here in order to track for breadcrumbs
118 if ($this->_getOption('show_header')) {
119 $this->displayHeader();
121 $this->renderJavascript();
124 $this->_buildModuleList();
126 $this->displayErrors();
128 if ( !empty($this->module) ) {
129 $GLOBALS['logic_hook']->call_custom_logic($this->module, 'after_ui_frame');
131 $GLOBALS['logic_hook']->call_custom_logic('', 'after_ui_frame');
132 if ($this->_getOption('show_subpanels')) $this->_displaySubPanels();
133 if ($this->action === 'Login') {
134 //this is needed for a faster loading login page ie won't render unless the tables are closed
137 if ($this->_getOption('show_footer')) $this->displayFooter();
138 $GLOBALS['logic_hook']->call_custom_logic('', 'after_ui_footer');
139 //Do not track if there is no module or if module is not a String
144 * This method will display the errors on the page.
146 public function displayErrors()
150 foreach($this->errors as $error) {
151 $errors .= '<span class="error">' . $error . '</span><br>';
154 if ( !$this->suppressDisplayErrors ) {
163 * [OVERRIDE] - This method is meant to overidden in a subclass. The purpose of this method is
164 * to allow a view to do some preprocessing before the display method is called. This becomes
165 * useful when you have a view defined at the application level and then within a module
166 * have a sub-view that extends from this application level view. The application level
167 * view can do the setup in preDisplay() that is common to itself and any subviews
168 * and then the subview can just override display(). If it so desires, can also override
171 public function preDisplay()
176 * [OVERRIDE] - This method is meant to overidden in a subclass. This method
177 * will handle the actual display logic of the view.
179 public function display()
187 protected function _trackView()
189 $action = strtolower($this->action);
190 //Skip save, tracked in SugarBean instead
191 if($action == 'save') {
196 $trackerManager = TrackerManager::getInstance();
197 $timeStamp = TimeDate::getInstance()->nowDb();
198 if($monitor = $trackerManager->getMonitor('tracker')){
199 $monitor->setValue('action', $action);
200 $monitor->setValue('user_id', $GLOBALS['current_user']->id);
201 $monitor->setValue('module_name', $this->module);
202 $monitor->setValue('date_modified', $timeStamp);
203 $monitor->setValue('visible', (($monitor->action == 'detailview') || ($monitor->action == 'editview')
206 if (!empty($this->bean->id)) {
207 $monitor->setValue('item_id', $this->bean->id);
208 $monitor->setValue('item_summary', $this->bean->get_summary_text());
211 //If visible is true, but there is no bean, do not track (invalid/unauthorized reference)
212 //Also, do not track save actions where there is no bean id
213 if($monitor->visible && empty($this->bean->id)) {
214 $trackerManager->unsetMonitor($monitor);
217 $trackerManager->saveMonitor($monitor, true, true);
223 * Displays the header on section of the page; basically everything before the content
225 public function displayHeader()
230 global $current_user;
231 global $sugar_config;
232 global $app_list_strings;
234 global $current_language;
236 $GLOBALS['app']->headerDisplayed = true;
238 $themeObject = SugarThemeRegistry::current();
239 $theme = $themeObject->__toString();
241 $ss = new Sugar_Smarty();
242 $ss->assign("APP", $app_strings);
243 $ss->assign("THEME", $theme);
244 $ss->assign("THEME_IE6COMPAT", $themeObject->ie6compat ? 'true':'false');
245 $ss->assign("MODULE_NAME", $this->module);
248 $ss->assign("SYSTEM_NAME", $this->getBrowserTitle());
251 $css = $themeObject->getCSS();
252 if ($this->_getOption('view_print')) {
253 $css .= '<link rel="stylesheet" type="text/css" href="'.$themeObject->getCSSURL('print.css').'" media="all" />';
255 $ss->assign("SUGAR_CSS",$css);
259 $this->renderJavascript();
261 $ss->assign("SUGAR_JS",ob_get_contents().$themeObject->getJS());
265 if(isset($GLOBALS['sugar_config']['default_module_favicon']))
266 $module_favicon = $GLOBALS['sugar_config']['default_module_favicon'];
268 $module_favicon = false;
271 if ( $module_favicon )
272 $favicon = $themeObject->getImageURL($this->module.'.gif',false);
273 if ( !sugar_is_file($favicon) || !$module_favicon )
274 $favicon = $themeObject->getImageURL('sugar_icon.ico',false);
275 $ss->assign('FAVICON_URL',getJSPath($favicon));
277 // build the shortcut menu
278 $shortcut_menu = array();
279 foreach ( $this->getMenu() as $key => $menu_item )
280 $shortcut_menu[$key] = array(
281 "URL" => $menu_item[0],
282 "LABEL" => $menu_item[1],
283 "MODULE_NAME" => $menu_item[2],
284 "IMAGE" => $themeObject
285 ->getImage($menu_item[2],"alt='".$menu_item[1]."' border='0' align='absmiddle'"),
287 $ss->assign("SHORTCUT_MENU",$shortcut_menu);
289 // handle rtl text direction
290 if(isset($_REQUEST['RTL']) && $_REQUEST['RTL'] == 'RTL'){
291 $_SESSION['RTL'] = true;
293 if(isset($_REQUEST['LTR']) && $_REQUEST['LTR'] == 'LTR'){
294 unset($_SESSION['RTL']);
296 if(isset($_SESSION['RTL']) && $_SESSION['RTL']){
297 $ss->assign("DIR", 'dir="RTL"');
300 // handle resizing of the company logo correctly on the fly
301 $companyLogoURL = $themeObject->getImageURL('company_logo.png');
302 $companyLogoURL_arr = explode('?', $companyLogoURL);
303 $companyLogoURL = $companyLogoURL_arr[0];
305 $company_logo_attributes = sugar_cache_retrieve('company_logo_attributes');
306 if(!empty($company_logo_attributes)) {
307 $ss->assign("COMPANY_LOGO_MD5", $company_logo_attributes[0]);
308 $ss->assign("COMPANY_LOGO_WIDTH", $company_logo_attributes[1]);
309 $ss->assign("COMPANY_LOGO_HEIGHT", $company_logo_attributes[2]);
312 // Always need to md5 the file
313 $ss->assign("COMPANY_LOGO_MD5", md5_file($companyLogoURL));
315 list($width,$height) = getimagesize($companyLogoURL);
316 if ( $width > 212 || $height > 40 ) {
317 $resizePctWidth = ($width - 212)/212;
318 $resizePctHeight = ($height - 40)/40;
319 if ( $resizePctWidth > $resizePctHeight )
320 $resizeAmount = $width / 212;
322 $resizeAmount = $height / 40;
323 $ss->assign("COMPANY_LOGO_WIDTH", round($width * (1/$resizeAmount)));
324 $ss->assign("COMPANY_LOGO_HEIGHT", round($height * (1/$resizeAmount)));
327 $ss->assign("COMPANY_LOGO_WIDTH", $width);
328 $ss->assign("COMPANY_LOGO_HEIGHT", $height);
331 // Let's cache the results
332 sugar_cache_put('company_logo_attributes',
334 $ss->get_template_vars("COMPANY_LOGO_MD5"),
335 $ss->get_template_vars("COMPANY_LOGO_WIDTH"),
336 $ss->get_template_vars("COMPANY_LOGO_HEIGHT")
340 $ss->assign("COMPANY_LOGO_URL",getJSPath($companyLogoURL)."&logo_md5=".$ss->get_template_vars("COMPANY_LOGO_MD5"));
342 // get the global links
344 $global_control_links = array();
345 require("include/globalControlLinks.php");
347 foreach($global_control_links as $key => $value) {
348 if ($key == 'users') { //represents logout link.
349 $ss->assign("LOGOUT_LINK", $value['linkinfo'][key($value['linkinfo'])]);
350 $ss->assign("LOGOUT_LABEL", key($value['linkinfo']));//key value for first element.
354 foreach ($value as $linkattribute => $attributevalue) {
355 // get the main link info
356 if ( $linkattribute == 'linkinfo' ) {
358 "LABEL" => key($attributevalue),
359 "URL" => current($attributevalue),
360 "SUBMENU" => array(),
362 if(substr($gcls[$key]["URL"], 0, 11) == "javascript:") {
363 $gcls[$key]["ONCLICK"] = substr($gcls[$key]["URL"],11);
364 $gcls[$key]["URL"] = "#";
367 // and now the sublinks
368 if ( $linkattribute == 'submenu' && is_array($attributevalue) ) {
369 foreach ($attributevalue as $submenulinkkey => $submenulinkinfo)
370 $gcls[$key]['SUBMENU'][$submenulinkkey] = array(
371 "LABEL" => key($submenulinkinfo),
372 "URL" => current($submenulinkinfo),
374 if(substr($gcls[$key]['SUBMENU'][$submenulinkkey]["URL"], 0, 11) == "javascript:") {
375 $gcls[$key]['SUBMENU'][$submenulinkkey]["ONCLICK"] = substr($gcls[$key]['SUBMENU'][$submenulinkkey]["URL"],11);
376 $gcls[$key]['SUBMENU'][$submenulinkkey]["URL"] = "#";
381 $ss->assign("GCLS",$gcls);
383 $ss->assign("SEARCH", isset($_REQUEST['query_string']) ? $_REQUEST['query_string'] : '');
385 if ($this->action == "EditView" || $this->action == "Login")
386 $ss->assign("ONLOAD", 'onload="set_focus()"');
388 $ss->assign("AUTHENTICATED",isset($_SESSION["authenticated_user_id"]));
390 // get other things needed for page style popup
391 if (isset($_SESSION["authenticated_user_id"])) {
392 // get the current user name and id
393 $ss->assign("CURRENT_USER", $current_user->full_name == '' || !showFullName()
394 ? $current_user->user_name : $current_user->full_name );
395 $ss->assign("CURRENT_USER_ID", $current_user->id);
397 // get the last viewed records
398 $tracker = new Tracker();
399 $history = $tracker->get_recently_viewed($current_user->id);
400 foreach ( $history as $key => $row ) {
401 $history[$key]['item_summary_short'] = getTrackerSubstring($row['item_summary']);
402 $history[$key]['image'] = SugarThemeRegistry::current()
403 ->getImage($row['module_name'],'border="0" align="absmiddle" alt="'.$row['item_summary'].'"');
405 $ss->assign("recentRecords",$history);
408 $bakModStrings = $mod_strings;
409 if (isset($_SESSION["authenticated_user_id"]) ) {
410 // get the module list
411 $moduleTopMenu = array();
413 $max_tabs = $current_user->getPreference('max_tabs');
414 // Attempt to correct if max tabs count is waaay too high.
415 if ( !isset($max_tabs) || $max_tabs <= 0 || $max_tabs > 10 ) {
416 $max_tabs = $GLOBALS['sugar_config']['default_max_tabs'];
417 $current_user->setPreference('max_tabs', $max_tabs, 0, 'global');
420 $moduleTab = $this->_getModuleTab();
421 $ss->assign('MODULE_TAB',$moduleTab);
424 // See if they are using grouped tabs or not (removed in 6.0, returned in 6.1)
425 $user_navigation_paradigm = $current_user->getPreference('navigation_paradigm');
426 if ( !isset($user_navigation_paradigm) ) {
427 $user_navigation_paradigm = $GLOBALS['sugar_config']['default_navigation_paradigm'];
431 // Get the full module list for later use
432 foreach ( query_module_access_list($current_user) as $module ) {
433 // Bug 25948 - Check for the module being in the moduleList
434 if ( isset($app_list_strings['moduleList'][$module]) ) {
435 $fullModuleList[$module] = $app_list_strings['moduleList'][$module];
440 if(!should_hide_iframes()) {
441 $iFrame = new iFrame();
442 $frames = $iFrame->lookup_frames('tab');
443 foreach($frames as $key => $values){
444 $fullModuleList[$key] = $values;
447 elseif (isset($fullModuleList['iFrames'])) {
448 unset($fullModuleList['iFrames']);
451 if ( $user_navigation_paradigm == 'gm' && isset($themeObject->group_tabs) && $themeObject->group_tabs) {
452 // We are using grouped tabs
453 require_once('include/GroupedTabs/GroupedTabStructure.php');
454 $groupedTabsClass = new GroupedTabStructure();
455 $modules = query_module_access_list($current_user);
456 //handle with submoremodules
457 $max_tabs = $current_user->getPreference('max_tabs');
458 // If the max_tabs isn't set incorrectly, set it within the range, to the default max sub tabs size
459 if ( !isset($max_tabs) || $max_tabs <= 0 || $max_tabs > 10){
460 // We have a default value. Use it
461 if(isset($GLOBALS['sugar_config']['default_max_tabs'])){
462 $max_tabs = $GLOBALS['sugar_config']['default_max_tabs'];
469 $subMoreModules = false;
470 $groupTabs = $groupedTabsClass->get_tab_structure(get_val_array($modules));
471 // We need to put this here, so the "All" group is valid for the user's preference.
472 $groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules'] = $fullModuleList;
475 // Setup the default group tab.
476 $allGroup = $app_strings['LBL_TABGROUP_ALL'];
477 $ss->assign('currentGroupTab',$allGroup);
478 $currentGroupTab = $allGroup;
479 $usersGroup = $current_user->getPreference('theme_current_group');
480 // Figure out which tab they currently have selected (stored as a user preference)
481 if ( !empty($usersGroup) && isset($groupTabs[$usersGroup]) ) {
482 $currentGroupTab = $usersGroup;
484 $current_user->setPreference('theme_current_group',$currentGroupTab);
487 $ss->assign('currentGroupTab',$currentGroupTab);
488 $usingGroupTabs = true;
491 // Setup the default group tab.
492 $ss->assign('currentGroupTab',$app_strings['LBL_TABGROUP_ALL']);
494 $usingGroupTabs = false;
496 $groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules'] = $fullModuleList;
501 $topTabList = array();
503 // Now time to go through each of the tab sets and fix them up.
504 foreach ( $groupTabs as $tabIdx => $tabData ) {
505 $topTabs = $tabData['modules'];
506 if ( ! is_array($topTabs) ) {
509 $extraTabs = array();
511 // Split it in to the tabs that go across the top, and the ones that are on the extra menu.
512 if ( count($topTabs) > $max_tabs ) {
513 $extraTabs = array_splice($topTabs,$max_tabs);
515 // Make sure the current module is accessable through one of the top tabs
516 if ( !isset($topTabs[$moduleTab]) ) {
517 // Nope, we need to add it.
518 // First, take it out of the extra menu, if it's there
519 if ( isset($extraTabs[$moduleTab]) ) {
520 unset($extraTabs[$moduleTab]);
522 if ( count($topTabs) >= $max_tabs - 1 ) {
523 // We already have the maximum number of tabs, so we need to shuffle the last one
524 // from the top to the first one of the extras
525 $lastElem = array_splice($topTabs,$max_tabs-1);
526 $extraTabs = $lastElem + $extraTabs;
528 if ( !empty($moduleTab) ) {
529 $topTabs[$moduleTab] = $app_list_strings['moduleList'][$moduleTab];
535 // This was removed, but I like the idea, so I left the code in here in case we decide to turn it back on
536 // If we are using group tabs, add all the "hidden" tabs to the end of the extra menu
537 if ( $usingGroupTabs ) {
538 foreach($fullModuleList as $moduleKey => $module ) {
539 if ( !isset($topTabs[$moduleKey]) && !isset($extraTabs[$moduleKey]) ) {
540 $extraTabs[$moduleKey] = $module;
546 // Get a unique list of the top tabs so we can build the popup menus for them
547 foreach ( $topTabs as $moduleKey => $module ) {
548 $topTabList[$moduleKey] = $module;
551 $groupTabs[$tabIdx]['modules'] = $topTabs;
552 $groupTabs[$tabIdx]['extra'] = $extraTabs;
556 if ( isset($topTabList) && is_array($topTabList) ) {
557 // Adding shortcuts array to menu array for displaying shortcuts associated with each module
558 $shortcutTopMenu = array();
559 foreach($topTabList as $module_key => $label) {
561 $mod_strings = return_module_language($current_language, $module_key);
562 foreach ( $this->getMenu($module_key) as $key => $menu_item ) {
563 $shortcutTopMenu[$module_key][$key] = array(
564 "URL" => $menu_item[0],
565 "LABEL" => $menu_item[1],
566 "MODULE_NAME" => $menu_item[2],
567 "IMAGE" => $themeObject
568 ->getImage($menu_item[2],"alt='".$menu_item[1]."' border='0' align='absmiddle'"),
572 $ss->assign("groupTabs",$groupTabs);
573 $ss->assign("shortcutTopMenu",$shortcutTopMenu);
574 $ss->assign('USE_GROUP_TABS',$usingGroupTabs);
576 // This is here for backwards compatibility, someday, somewhere, it will be able to be removed
577 $ss->assign("moduleTopMenu",$groupTabs[$app_strings['LBL_TABGROUP_ALL']]['modules']);
578 $ss->assign("moduleExtraMenu",$groupTabs[$app_strings['LBL_TABGROUP_ALL']]['extra']);
583 $mod_strings = $bakModStrings;
584 $headerTpl = $themeObject->getTemplate('header.tpl');
585 if ( isset($GLOBALS['sugar_config']['developerMode']) && $GLOBALS['sugar_config']['developerMode'] )
586 $ss->clear_compiled_tpl($headerTpl);
587 $ss->display($headerTpl);
589 $this->includeClassicFile('modules/Administration/DisplayWarnings.php');
591 $errorMessages = SugarApplication::getErrorMessages();
592 if ( !empty($errorMessages)) {
593 foreach ( $errorMessages as $error_message ) {
594 echo('<p class="error">' . $error_message.'</p>');
600 * If the view is classic then this method will include the file and
601 * setup any global variables.
603 * @param string $file
605 public function includeClassicFile(
609 global $sugar_config, $theme, $current_user, $sugar_version, $sugar_flavor, $mod_strings, $app_strings, $app_list_strings, $action, $timezones;
610 global $gridline, $request_string, $modListHeader, $dashletData, $authController, $locale, $currentModule, $import_bean_map, $image_path, $license;
611 global $user_unique_key, $server_unique_key, $barChartColors, $modules_exempt_from_availability_check, $dictionary, $current_language, $beanList, $beanFiles, $sugar_build, $sugar_codename;
612 global $timedate, $login_error; // cn: bug 13855 - timedate not available to classic views.
613 $currentModule = $this->module;
614 require_once ($file);
617 protected function _displayLoginJS()
619 global $sugar_config, $timedate;
621 if(isset($this->bean->module_dir)){
622 echo "<script>var module_sugar_grp1 = '{$this->bean->module_dir}';</script>";
624 if(isset($_REQUEST['action'])){
625 echo "<script>var action_sugar_grp1 = '{$_REQUEST['action']}';</script>";
627 echo '<script>jscal_today = ' . (1000*$timedate->asUserTs($timedate->getNow())) . '; if(typeof app_strings == "undefined") app_strings = new Array();</script>';
628 if (!is_file("include/javascript/sugar_grp1.js")) {
629 $_REQUEST['root_directory'] = ".";
630 require_once("jssource/minify_utils.php");
631 ConcatenateFiles(".");
633 echo '<script type="text/javascript" src="' . getJSPath('include/javascript/sugar_grp1_yui.js') . '"></script>';
634 echo '<script type="text/javascript" src="' . getJSPath('include/javascript/sugar_grp1.js') . '"></script>';
635 echo '<script type="text/javascript" src="' . getJSPath('include/javascript/calendar.js') . '"></script>';
638 if ( typeof(SUGAR) == 'undefined' ) {SUGAR = {}};
639 if ( typeof(SUGAR.themes) == 'undefined' ) SUGAR.themes = {};
642 if(isset( $sugar_config['disc_client']) && $sugar_config['disc_client'])
643 echo '<script type="text/javascript" src="' . getJSPath('modules/Sync/headersync.js') . '"></script>';
647 * Get JS validation code for views
649 public static function getJavascriptValidation()
652 $cal_date_format = $timedate->get_cal_date_format();
653 $timereg = $timedate->get_regular_expression($timedate->get_time_format());
654 $datereg = $timedate->get_regular_expression($timedate->get_date_format());
656 foreach ($datereg['positions'] as $type => $pos) {
657 if (empty($date_pos)) {
658 $date_pos .= "'$type': $pos";
660 $date_pos .= ",'$type': $pos";
664 $time_separator = $timedate->timeSeparator();
665 $hour_offset = $timedate->getUserUTCOffset() * 60;
667 // Add in the number formatting styles here as well, we have been handling this with individual modules.
668 require_once ('modules/Currencies/Currency.php');
669 list ($num_grp_sep, $dec_sep) = get_number_seperators();
671 $the_script = "<script type=\"text/javascript\">\n" . "\tvar time_reg_format = '" .
672 $timereg['format'] . "';\n" . "\tvar date_reg_format = '" .
673 $datereg['format'] . "';\n" . "\tvar date_reg_positions = { $date_pos };\n" .
674 "\tvar time_separator = '$time_separator';\n" .
675 "\tvar cal_date_format = '$cal_date_format';\n" .
676 "\tvar time_offset = $hour_offset;\n" . "\tvar num_grp_sep = '$num_grp_sep';\n" .
677 "\tvar dec_sep = '$dec_sep';\n" . "</script>";
683 * Called from process(). This method will display the correct javascript.
685 protected function _displayJavascript()
687 global $locale, $sugar_config, $timedate;
690 if ($this->_getOption('show_javascript')) {
691 if (!$this->_getOption('show_header'))
693 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
698 echo "<script>var sugar_cache_dir = '{$GLOBALS['sugar_config']['cache_dir']}';</script>";
699 echo "<script>var sugar_upload_dir = '{$GLOBALS['sugar_config']['upload_dir']}';</script>";
701 if(isset($this->bean->module_dir)){
702 echo "<script>var module_sugar_grp1 = '{$this->bean->module_dir}';</script>";
704 if(isset($_REQUEST['action'])){
705 echo "<script>var action_sugar_grp1 = '{$_REQUEST['action']}';</script>";
707 echo '<script>jscal_today = ' . (1000*$timedate->asUserTs($timedate->getNow())) . '; if(typeof app_strings == "undefined") app_strings = new Array();</script>';
708 if (!is_file("include/javascript/sugar_grp1.js") || !is_file("include/javascript/sugar_grp1_yui.js")) {
709 $_REQUEST['root_directory'] = ".";
710 require_once("jssource/minify_utils.php");
711 ConcatenateFiles(".");
713 echo '<script type="text/javascript" src="' . getJSPath('include/javascript/sugar_grp1_yui.js') . '"></script>';
714 echo '<script type="text/javascript" src="' . getJSPath('include/javascript/sugar_grp1.js') . '"></script>';
715 echo '<script type="text/javascript" src="' . getJSPath('include/javascript/calendar.js') . '"></script>';
717 if ( isset($sugar_config['quicksearch_querydelay']) ) {
718 echo "<script>SUGAR.config.quicksearch_querydelay = {$GLOBALS['sugar_config']['quicksearch_querydelay']};</script>";
721 $image_server = (defined('TEMPLATE_URL'))?TEMPLATE_URL . '/':'';
722 echo '<script type="text/javascript">SUGAR.themes.image_server="' . $image_server . '";</script>'; // cn: bug 12274 - create session-stored key to defend against CSRF
723 echo '<script type="text/javascript">var name_format = "' . $locale->getLocaleFormatMacro() . '";</script>';
724 echo self::getJavascriptValidation();
725 if (!is_file($GLOBALS['sugar_config']['cache_dir'] . 'jsLanguage/' . $GLOBALS['current_language'] . '.js')) {
726 require_once ('include/language/jsLanguage.php');
727 jsLanguage::createAppStringsCache($GLOBALS['current_language']);
729 echo '<script type="text/javascript" src="' . $GLOBALS['sugar_config']['cache_dir'] . 'jsLanguage/' . $GLOBALS['current_language'] . '.js?s=' . $GLOBALS['js_version_key'] . '&c=' . $GLOBALS['sugar_config']['js_custom_version'] . '&j=' . $GLOBALS['sugar_config']['js_lang_version'] . '"></script>';
730 if (!is_file($GLOBALS['sugar_config']['cache_dir'] . 'jsLanguage/' . $this->module . '/' . $GLOBALS['current_language'] . '.js')) {
731 require_once ('include/language/jsLanguage.php');
732 jsLanguage::createModuleStringsCache($this->module, $GLOBALS['current_language']);
734 echo '<script type="text/javascript" src="' . $GLOBALS['sugar_config']['cache_dir'] . 'jsLanguage/' . $this->module . '/' . $GLOBALS['current_language'] . '.js?s=' . $GLOBALS['js_version_key'] . '&c=' . $GLOBALS['sugar_config']['js_custom_version'] . '&j=' . $GLOBALS['sugar_config']['js_lang_version'] . '"></script>';
735 if(isset( $sugar_config['disc_client']) && $sugar_config['disc_client'])
736 echo '<script type="text/javascript" src="' . getJSPath('modules/Sync/headersync.js') . '"></script>';
742 * Called from process(). This method will display the footer on the page.
744 public function displayFooter()
746 if (empty($this->responseTime)) {
747 $this->_calculateFooterMetrics();
749 global $sugar_config;
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'];
758 echo "<!-- crmprint -->";
759 $jsalerts = new jsAlerts();
760 if ( !isset($_SESSION['isMobile']) )
761 echo $jsalerts->getScript();
763 $ss = new Sugar_Smarty();
764 $ss->assign("AUTHENTICATED",isset($_SESSION["authenticated_user_id"]));
765 $ss->assign('MOD',return_module_language($GLOBALS['current_language'], 'Users'));
767 $bottomLinkList = array();
768 if (isset($this->action) && $this->action != "EditView") {
769 $bottomLinkList['print'] =
770 array($app_strings['LNK_PRINT'] => 'javascript:void window.open(\'index.php?'.$GLOBALS['request_string'].'\',\'printwin\',\'menubar=1,status=0,resizable=1,scrollbars=1,toolbar=0,location=1\')');
773 $bottomLinkList['backtotop'] = array($app_strings['LNK_BACKTOTOP'] => '#top');
775 $bottomLinksStr = "";
776 foreach($bottomLinkList as $key => $value) {
777 foreach($value as $text => $link) {
779 if(substr($link, 0, 11) == "javascript:") {
780 $onclick = " onclick=\"".substr($link,11)."\"";
785 $imageURL = SugarThemeRegistry::current()->getImageURL($key.'.gif');
786 $bottomLinksStr .= "<a href=\"{$href}\"";
787 $bottomLinksStr .= (isset($onclick)) ? $onclick : "";
788 $bottomLinksStr .= "><img src='{$imageURL}' alt='{$text}'></a>";
789 $bottomLinksStr .= " <a href=\"{$href}\" class=\"bottomLink\"";
790 $bottomLinksStr .= (isset($onclick)) ? $onclick : "";
791 $bottomLinksStr .= ">".$text."</a>";
794 $ss->assign("BOTTOMLINKS",$bottomLinksStr);
795 if (SugarConfig::getInstance()->get('calculate_response_time', false))
796 $ss->assign('STATISTICS',$this->_getStatistics());
798 // Under the License referenced above, you are required to leave in all copyright statements in both
799 // the code and end-user application.
802 $copyright = '© 2004-2011 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>';
814 // The interactive user interfaces in modified source and object code
815 // versions of this program must display Appropriate Legal Notices, as
816 // required under Section 5 of the GNU General Public License version
817 // 3. In accordance with Section 7(b) of the GNU General Public License
818 // version 3, these Appropriate Legal Notices must retain the display
819 // of the "Powered by SugarCRM" logo. If the display of the logo is
820 // not reasonably feasible for technical reasons, the Appropriate
821 // Legal Notices must display the words "Powered by SugarCRM".
822 $attribLinkImg = "<img style='margin-top: 2px' border='0' width='106' height='23' src='include/images/poweredby_sugarcrm.png' alt='Powered By SugarCRM'>\n";
826 // Bug 38594 - Add in Trademark wording
827 $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 />';
829 //rrs bug: 20923 - if this image does not exist as per the license, then the proper image will be displaye regardless, so no need
830 //to display an empty image here.
831 if(file_exists('include/images/poweredby_sugarcrm.png')){
832 $copyright .= $attribLinkImg;
834 // End Required Image
835 $ss->assign('COPYRIGHT',$copyright);
836 $ss->display(SugarThemeRegistry::current()->getTemplate('footer.tpl'));
840 * Called from process(). This method will display subpanels.
842 protected function _displaySubPanels()
844 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'))) {
845 $GLOBALS['focus'] = $this->bean;
846 require_once ('include/SubPanel/SubPanelTiles.php');
847 $subpanel = new SubPanelTiles($this->bean, $this->module);
848 echo $subpanel->display();
852 protected function _buildModuleList()
854 if (!empty($GLOBALS['current_user']) && empty($GLOBALS['modListHeader']))
855 $GLOBALS['modListHeader'] = query_module_access_list($GLOBALS['current_user']);
859 * private method used in process() to determine the value of a passed in option
861 * @param string option - the option that we want to know the valye of
862 * @param bool default - what the default value should be if we do not find the option
864 * @return bool - the value of the option
866 protected function _getOption(
871 if (!empty($this->options) && isset($this->options['show_all'])) {
872 return $this->options['show_all'];
873 } elseif (!empty($this->options) && isset($this->options[$option])) {
874 return $this->options[$option];
875 } else return $default;
880 * Private function to track information about the view request
882 private function _track()
884 if (empty($this->responseTime)) {
885 $this->_calculateFooterMetrics();
887 if (empty($GLOBALS['current_user']->id)) {
892 $trackerManager = TrackerManager::getInstance();
893 $trackerManager->save();
898 * Checks to see if the module name passed is valid; dies if it is not
900 protected function _checkModule()
902 if(!empty($this->module) && !file_exists('modules/'.$this->module)){
903 $error = str_replace("[module]", "$this->module", $GLOBALS['app_strings']['ERR_CANNOT_FIND_MODULE']);
904 $GLOBALS['log']->fatal($error);
910 public function renderJavascript()
912 if ($this->action !== 'Login')
913 $this->_displayJavascript();
915 $this->_displayLoginJS();
918 private function _calculateFooterMetrics()
920 $endTime = microtime(true);
921 $deltaTime = $endTime - $GLOBALS['startTime'];
922 $this->responseTime = number_format(round($deltaTime, 2), 2);
923 // Print out the resources used in constructing the page.
924 $this->fileResources = count(get_included_files());
927 private function _getStatistics()
929 $endTime = microtime(true);
930 $deltaTime = $endTime - $GLOBALS['startTime'];
931 $response_time_string = $GLOBALS['app_strings']['LBL_SERVER_RESPONSE_TIME'] . " " . number_format(round($deltaTime, 2), 2) . " " . $GLOBALS['app_strings']['LBL_SERVER_RESPONSE_TIME_SECONDS'];
932 $return = $response_time_string;
934 if (!empty($GLOBALS['sugar_config']['show_page_resources'])) {
935 // Print out the resources used in constructing the page.
936 $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;
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 />";
958 * Loads the module shortcuts menu
960 * @param $module string optional, can specify module to retrieve menu for if not the current one
961 * @return array module menu
963 public function getMenu(
967 global $current_language, $current_user, $mod_strings, $app_strings;
969 if ( empty($module) )
970 $module = $this->module;
972 $final_module_menu = array();
974 if (file_exists('modules/' . $module . '/Menu.php')) {
975 $GLOBALS['module_menu'] = $module_menu = array();
976 require('modules/' . $module . '/Menu.php');
977 $final_module_menu = array_merge($final_module_menu,$GLOBALS['module_menu'],$module_menu);
979 if (file_exists('custom/modules/' . $module . '/Ext/Menus/menu.ext.php')) {
980 $GLOBALS['module_menu'] = $module_menu = array();
981 require('custom/modules/' . $module . '/Ext/Menus/menu.ext.php');
982 $final_module_menu = array_merge($final_module_menu,$GLOBALS['module_menu'],$module_menu);
984 if (!file_exists('modules/' . $module . '/Menu.php')
985 && !file_exists('custom/modules/' . $module . '/Ext/Menus/menu.ext.php')
986 && !empty($GLOBALS['mod_strings']['LNK_NEW_RECORD'])) {
987 $final_module_menu[] = array("index.php?module=$module&action=EditView&return_module=$module&return_action=DetailView",
988 $GLOBALS['mod_strings']['LNK_NEW_RECORD'],"{$GLOBALS['app_strings']['LBL_CREATE_BUTTON_LABEL']}$module" ,$module );
989 $final_module_menu[] = array("index.php?module=$module&action=index", $GLOBALS['mod_strings']['LNK_LIST'],
991 if ( ($this->bean instanceOf SugarBean) && !empty($this->bean->importable) )
992 if ( !empty($mod_strings['LNK_IMPORT_'.strtoupper($module)]) )
993 $final_module_menu[] = array("index.php?module=Import&action=Step1&import_module=$module&return_module=$module&return_action=index",
994 $mod_strings['LNK_IMPORT_'.strtoupper($module)], "Import", $module);
996 $final_module_menu[] = array("index.php?module=Import&action=Step1&import_module=$module&return_module=$module&return_action=index",
997 $app_strings['LBL_IMPORT'], "Import", $module);
999 if (file_exists('custom/application/Ext/Menus/menu.ext.php')) {
1000 $GLOBALS['module_menu'] = $module_menu = array();
1001 require('custom/application/Ext/Menus/menu.ext.php');
1002 $final_module_menu = array_merge($final_module_menu,$GLOBALS['module_menu'],$module_menu);
1004 $module_menu = $final_module_menu;
1006 return $module_menu;
1010 * Returns the module name which should be highlighted in the module menu
1012 protected function _getModuleTab()
1014 global $app_list_strings, $moduleTabMap;
1016 // Need to figure out what tab this module belongs to, most modules have their own tabs, but there are exceptions.
1017 if ( !empty($_REQUEST['module_tab']) )
1018 return $_REQUEST['module_tab'];
1019 elseif ( isset($moduleTabMap[$this->module]) )
1020 return $moduleTabMap[$this->module];
1022 elseif ( $this->module == 'MergeRecords' )
1023 return !empty($_REQUEST['merge_module']) ? $_REQUEST['merge_module'] : $_REQUEST['return_module'];
1024 elseif ( $this->module == 'Users' && $this->action == 'SetTimezone' )
1026 // Default anonymous pages to be under Home
1027 elseif ( !isset($app_list_strings['moduleList'][$this->module]) )
1030 return $this->module;
1034 * Return the "breadcrumbs" to display at the top of the page
1036 * @param bool $show_help optional, true if we show the help links
1037 * @return HTML string containing breadcrumb title
1039 public function getModuleTitle(
1043 global $sugar_version, $sugar_flavor, $server_unique_key, $current_language, $action;
1045 $theTitle = "<div class='moduleTitle'>\n<h2>";
1047 $module = preg_replace("/ /","",$this->module);
1049 $params = $this->_getModuleTitleParams();
1050 $count = count($params);
1053 if(SugarThemeRegistry::current()->directionality == "rtl") {
1054 $params = array_reverse($params);
1057 foreach($params as $parm){
1060 if($index < $count){
1061 $theTitle .= $this->getBreadCrumbSymbol();
1064 $theTitle .= "</h2>\n";
1067 $theTitle .= "<span class='utils'>";
1069 $createImageURL = SugarThemeRegistry::current()->getImageURL('create-record.gif');
1070 $theTitle .= <<<EOHTML
1072 <a href="index.php?module={$module}&action=EditView&return_module={$module}&return_action=DetailView" class="utilsLink">
1073 <img src='{$createImageURL}' alt='{$GLOBALS['app_strings']['LNK_CREATE']}'></a>
1074 <a href="index.php?module={$module}&action=EditView&return_module={$module}&return_action=DetailView" class="utilsLink">
1075 {$GLOBALS['app_strings']['LNK_CREATE']}
1080 $theTitle .= "</span></div>\n";
1085 * Return the metadata file that will be used by this view.
1087 * @return string File location of the metadata file.
1089 public function getMetaDataFile()
1091 $metadataFile = null;
1092 $foundViewDefs = false;
1093 $viewDef = strtolower($this->type) . 'viewdefs';
1094 $coreMetaPath = 'modules/'.$this->module.'/metadata/' . $viewDef . '.php';
1095 if(file_exists('custom/' .$coreMetaPath )){
1096 $metadataFile = 'custom/' . $coreMetaPath;
1097 $foundViewDefs = true;
1099 if(file_exists('custom/modules/'.$this->module.'/metadata/metafiles.php')){
1100 require_once('custom/modules/'.$this->module.'/metadata/metafiles.php');
1101 if(!empty($metafiles[$this->module][$viewDef])){
1102 $metadataFile = $metafiles[$this->module][$viewDef];
1103 $foundViewDefs = true;
1105 }elseif(file_exists('modules/'.$this->module.'/metadata/metafiles.php')){
1106 require_once('modules/'.$this->module.'/metadata/metafiles.php');
1107 if(!empty($metafiles[$this->module][$viewDef])){
1108 $metadataFile = $metafiles[$this->module][$viewDef];
1109 $foundViewDefs = true;
1114 if(!$foundViewDefs && file_exists($coreMetaPath)){
1115 $metadataFile = $coreMetaPath;
1117 $GLOBALS['log']->debug("metadatafile=". $metadataFile);
1119 return $metadataFile;
1124 * Returns an array composing of the breadcrumbs to use for the module title
1126 * @param bool $browserTitle true if the returned string is being used for the browser title, meaning
1127 * there should be no HTML in the string
1130 protected function _getModuleTitleParams($browserTitle = false)
1132 $params = array($this->_getModuleTitleListParam($browserTitle));
1134 if (isset($this->action)){
1135 switch ($this->action) {
1137 if(!empty($this->bean->id)) {
1138 $params[] = "<a href='index.php?module={$this->module}&action=DetailView&record={$this->bean->id}'>".$this->bean->get_summary_text()."</a>";
1139 $params[] = $GLOBALS['app_strings']['LBL_EDIT_BUTTON_LABEL'];
1142 $params[] = $GLOBALS['app_strings']['LBL_CREATE_BUTTON_LABEL'];
1145 $beanName = $this->bean->get_summary_text();
1146 $params[] = $beanName;
1155 * Returns the portion of the array that will represent the listview in the breadcrumb
1157 * @param bool $browserTitle true if the returned string is being used for the browser title, meaning
1158 * there should be no HTML in the string
1161 protected function _getModuleTitleListParam( $browserTitle = false )
1163 global $current_user;
1164 global $app_strings;
1166 if(!empty($GLOBALS['app_list_strings']['moduleList'][$this->module]))
1167 $firstParam = $GLOBALS['app_list_strings']['moduleList'][$this->module];
1169 $firstParam = $this->module;
1171 $iconPath = $this->getModuleTitleIconPath($this->module);
1172 if($this->action == "ListView" || $this->action == "index") {
1173 if (!empty($iconPath) && !$browserTitle) {
1174 if (SugarThemeRegistry::current()->directionality == "ltr") {
1175 return "<a href='index.php?module={$this->module}&action=index'>"
1176 . "<img src='{$iconPath}' alt='".$this->module."' title='".$this->module."' align='absmiddle'></a>"
1177 . $this->getBreadCrumbSymbol().$app_strings['LBL_SEARCH'];
1179 return $app_strings['LBL_SEARCH'].$this->getBreadCrumbSymbol()
1180 . "<a href='index.php?module={$this->module}&action=index'>"
1181 . "<img src='{$iconPath}' alt='".$this->module."' title='".$this->module."' align='absmiddle'></a>";
1188 if (!empty($iconPath) && !$browserTitle) {
1189 return "<a href='index.php?module={$this->module}&action=index'>"
1190 . "<img src='{$iconPath}' alt='".$this->module."' title='".$this->module."' align='absmiddle'></a>";
1192 return "{$firstParam}";
1197 protected function getModuleTitleIconPath($module)
1200 if(is_file(SugarThemeRegistry::current()->getImageURL('icon_'.$module.'_32.png',false))) {
1201 $iconPath = SugarThemeRegistry::current()->getImageURL('icon_'.$module.'_32.png');
1203 else if (is_file(SugarThemeRegistry::current()->getImageURL('icon_'.ucfirst($module).'_32.png',false))) {
1204 $iconPath = SugarThemeRegistry::current()->getImageURL('icon_'.ucfirst($module).'_32.png');
1210 * Returns the string which will be shown in the browser's title; defaults to using the same breadcrumb
1211 * as in the module title
1215 public function getBrowserTitle()
1217 global $app_strings;
1219 $browserTitle = $app_strings['LBL_BROWSER_TITLE'];
1220 if ( $this->module == 'Users' && ($this->action == 'SetTimezone' || $this->action == 'Login') )
1221 return $browserTitle;
1222 $params = $this->_getModuleTitleParams(true);
1223 foreach ($params as $value )
1224 $browserTitle = strip_tags($value) . ' » ' . $browserTitle;
1226 return $browserTitle;
1230 * Returns the correct breadcrumb symbol according to theme's directionality setting
1234 public function getBreadCrumbSymbol()
1236 if(SugarThemeRegistry::current()->directionality == "ltr") {
1237 return "<span class='pointer'>»</span>";
1240 return "<span class='pointer'>«</span>";