forked from salesagility/SuiteCRM
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/develop' into feature/TrackerModule
- Loading branch information
Showing
175 changed files
with
20,872 additions
and
1,149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
<?php | ||
/** | ||
* This file is part of SinergiaCRM. | ||
* SinergiaCRM is a work developed by SinergiaTIC Association, based on SuiteCRM. | ||
* Copyright (C) 2013 - 2023 SinergiaTIC Association | ||
* | ||
* This program is free software; you can redistribute it and/or modify it under | ||
* the terms of the GNU Affero General Public License version 3 as published by the | ||
* Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | ||
* details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License along with | ||
* this program; if not, see http://www.gnu.org/licenses or write to the Free | ||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
* 02110-1301 USA. | ||
* | ||
* You can contact SinergiaTIC Association at email address [email protected]. | ||
*/ | ||
|
||
/** | ||
* Generates an HTML menu from a list of items, filtering out invalid nodes. | ||
* | ||
* This function builds an HTML list for a navigation menu using a recursive | ||
* structure. It filters out menu items that don't correspond to valid modules | ||
* and don't have valid children. It also handles custom URLs embedded in menu items. | ||
* The function relies on global variables to obtain the corresponding texts for | ||
* the menu items and respects configuration for displaying icons and the "All" menu. | ||
* | ||
* @param array $items The menu items to process, each item can contain subitems and custom URLs. | ||
* @param bool $isFirstLevel Indicates if it's the top level of the menu. | ||
* @param array|null $validTabs Array of valid tabs/modules for the current user. | ||
* @return string The generated HTML code for the menu. | ||
*/ | ||
function generateMenu($items, $isFirstLevel = true, $validTabs = null) | ||
{ | ||
global $app_list_strings, $app_strings, $current_user, $sugar_config; | ||
|
||
require_once 'modules/ModuleBuilder/Module/IconRepository.php'; | ||
|
||
if ($_REQUEST['lang'] && is_string($_REQUEST['lang'])) { | ||
$app_strings = return_application_language($_REQUEST['lang']); | ||
} | ||
|
||
// Initialize valid tabs if not provided | ||
if ($validTabs === null) { | ||
require_once 'modules/MySettings/TabController.php'; | ||
$controller = new TabController(); | ||
$validTabs = $controller->get_tabs($current_user)[0]; | ||
foreach ($validTabs as $key => $value) { | ||
$validTabs[$key] = $app_list_strings['moduleList'][$key]; | ||
} | ||
asort($validTabs); | ||
} | ||
|
||
$html = ''; | ||
$validItemsCount = 0; | ||
|
||
$module = $_REQUEST['module']; | ||
$moduleLabel = $app_list_strings['moduleList'][$module]; | ||
|
||
$recents = array_slice(getUserModuleRecents($current_user->id, $module), 0, 5); | ||
$favs = array_slice(getUserModuleFavs($current_user->id, $module), 0, 5); | ||
|
||
// Add the new menu icon as the first item if it's the first level | ||
if ($isFirstLevel) { | ||
$html .= '<li class="dropdown"> | ||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> | ||
<i class="glyphicon glyphicon-menu-hamburger"></i> | ||
</a> | ||
<ul class="dropdown-menu" id="actions-area"> | ||
<li class="divider"></li>'; | ||
|
||
if (!empty($recents)) { | ||
$html .= '<li class="dropdown-header" id="recents-area">Recientes</li>'; | ||
foreach ($recents as $recent) { | ||
$text = strlen($recent['item_summary']) > 70 ? $recent['item_summary_short'] : $recent['item_summary']; | ||
$html .= "<li style='display:flex;justify-content: space-between;'> | ||
<a style='width:80%' href='index.php?module=$module&action=DetailView&record={$recent['item_id']}' title='{$recent['item_summary']}'>$text</a> | ||
<a style='width:20%' href='index.php?module=$module&action=EditView&record={$recent['item_id']}' title='{$recent['item_summary']}'><i class='glyphicon glyphicon-pencil' aria-hidden='true'></i></a> | ||
</li>"; | ||
|
||
} | ||
$html .= '<li class="divider"></li>'; | ||
} | ||
if (!empty($favs)) { | ||
$html .= '<li class="dropdown-header" id="recents-area">Favoritos</li>'; | ||
foreach ($favs as $fav) { | ||
$text = strlen($fav['item_summary']) > 70 ? $fav['item_summary_short'] : $fav['item_summary']; | ||
$html .= "<li style='display:flex;justify-content: space-between;'> | ||
<a style='width:80%' href='index.php?module=$module&action=DetailView&record={$fav['id']}' title='{$fav['item_summary']}'>$text</a> | ||
<a style='width:20%' href='index.php?module=$module&action=DetailView&record={$fav['id']}' title='{$fav['item_summary']}'><i class='glyphicon glyphicon-pencil' aria-hidden='true'></i></a> | ||
</li>"; | ||
} | ||
$html .= '<li class="divider"></li>'; | ||
} | ||
|
||
$html .= '</ul></li>'; | ||
$validItemsCount++; | ||
} | ||
|
||
foreach ($items as $item) { | ||
|
||
$cleanId = preg_replace('/_\d+$/', '', $item['id']); | ||
|
||
// Get the display text for the menu item | ||
$text = ($app_list_strings['moduleList'][$cleanId] ?? $app_strings[$cleanId] ?? $app_strings[$item['id']] ?? str_replace('_', ' ', $cleanId)); | ||
|
||
$hasChildren = isset($item['children']) && is_array($item['children']) && !empty($item['children']); | ||
$isValidModule = array_key_exists($cleanId, $validTabs); | ||
|
||
// Recursively generate HTML for child items | ||
$childrenHtml = ''; | ||
if ($hasChildren) { | ||
$childrenHtml = generateMenu($item['children'], false, $validTabs); | ||
} | ||
|
||
// Only include valid modules, items with valid children, or items with custom URLs | ||
if ($isValidModule || !empty($childrenHtml) || $item['url']) { | ||
$validItemsCount++; | ||
$itemHtml = '<li' . ($hasChildren ? ' class="dropdown"' : '') . '>'; | ||
|
||
if ($isValidModule) { | ||
// Generate link for valid modules | ||
$lowerModule = str_replace('_', '-', strtolower($cleanId)); | ||
$moduleIconName = IconRepository::getIconName($cleanId); | ||
// Include icon if enabled in configuration | ||
$iconString = $sugar_config['stic_advanced_menu_icons'] ? "<span class='suitepicon suitepicon-module-{$moduleIconName}'></span>" : ''; | ||
$itemHtml .= "<a href='index.php?module={$cleanId}&action=index'>$iconString $text </a>"; | ||
} elseif ($item['url']) { | ||
// Generate external link for items with custom URLs | ||
$itemHtml .= "<a title='{$item['url']}' target='_blank' href='{$item['url']}'><span class='glyphicon glyphicon-link'></span> $text </a>"; | ||
} elseif ($hasChildren) { | ||
// Generate dropdown toggle for items with children | ||
$itemHtml .= "<a href='#' class='no-link'>" . $text . '</a>'; | ||
} | ||
|
||
$itemHtml .= $childrenHtml; | ||
$itemHtml .= '</li>'; | ||
$html .= $itemHtml; | ||
} | ||
} | ||
|
||
// Wrap the menu items in a <ul> if there are valid items or it's the first level | ||
if ($validItemsCount > 0 || $isFirstLevel) { | ||
$menuHtml = $isFirstLevel ? '<ul id="stic-menu" class="sm sm-stic">' : '<ul>'; | ||
if ($isFirstLevel) { | ||
// Add home link at the first level | ||
$menuHtml .= '<li><a href="index.php?module=Home&action=index" onclick="window.location.href=this.href; return false;"><i class="glyphicon glyphicon-home"></i></a></li>'; | ||
} | ||
$menuHtml .= $html; | ||
|
||
// Add the "All" menu if it's the first level and enabled in configuration | ||
if ($isFirstLevel && $sugar_config['stic_advanced_menu_all']) { | ||
$menuHtml .= '<li class="dropdown">'; | ||
$menuHtml .= "<a href='#' class='no-link'>{$app_strings['LBL_TABGROUP_ALL']} </a>"; | ||
$menuHtml .= '<ul>'; | ||
$menuHtml .= '<li><input type="text" id="search-all" placeholder="' . $app_strings['LBL_SEARCH'] . '"></input></li>'; | ||
foreach ($validTabs as $key => $value) { | ||
$lowerModule = str_replace('_', '-', strtolower($key)); | ||
$moduleIconName = IconRepository::getIconName($key); | ||
$iconString = $sugar_config['stic_advanced_menu_icons'] ? "<span class='suitepicon suitepicon-module-{$moduleIconName}'></span>" : ''; | ||
$menuHtml .= "<li><a href='index.php?module={$key}&action=index'> $iconString $value </a></li>"; | ||
} | ||
$menuHtml .= '</ul>'; | ||
$menuHtml .= '</li>'; | ||
} | ||
|
||
$menuHtml .= '</ul>'; | ||
return $menuHtml; | ||
} | ||
|
||
return ''; | ||
} | ||
|
||
/** | ||
* Adds text properties to menu items based on their IDs. | ||
* | ||
* This function recursively processes an array of menu items, adding a 'text' | ||
* property to each item based on its 'id'. It uses global string lists to | ||
* find the appropriate text for each menu item. | ||
* | ||
* @param array &$array The array of menu items to process. | ||
*/ | ||
function addMenuProperties(&$array, $currentLangStrings = null) | ||
{ | ||
|
||
include_once 'modules/MySettings/TabController.php'; | ||
$controller = new TabController(); | ||
$currentTabs = $controller->get_system_tabs(); | ||
global $app_list_strings, $app_strings; | ||
|
||
if ($_REQUEST['lang'] && is_string($_REQUEST['lang'])) { | ||
$app_strings = return_application_language($_REQUEST['lang']); | ||
} | ||
|
||
foreach ($array as $key => &$value) { | ||
if (is_array($value)) { | ||
if (isset($value['id'])) { | ||
$cleanValueId = preg_replace('/_\d+$/', '', $value['id']); | ||
|
||
$value['text'] = ($app_list_strings['moduleList'][$cleanValueId] ?? $app_strings[$cleanValueId] ?? $app_strings[$value['id']] ?? str_replace('_', ' ', $cleanValueId)); | ||
// Set disabled property if module is disabled | ||
if (!in_array($cleanValueId, $currentTabs) && isset($app_list_strings['moduleList'][$cleanValueId])) { | ||
$value['disabled'] = true; | ||
} | ||
if (empty($value['text'])) { | ||
$value['text'] = $app_strings[$cleanValueId]; | ||
} | ||
if (empty($value['text'])) { | ||
$value['text'] = str_replace('_', ' ', $cleanValueId); | ||
} | ||
} | ||
addMenuProperties($value); // Recursively process sub-arrays | ||
} | ||
} | ||
} | ||
|
||
function getUserModuleRecents($userId, $module) | ||
{ | ||
$tracker = BeanFactory::getBean('Trackers'); | ||
$history = $tracker->get_recently_viewed($userId); | ||
|
||
$history = array_filter($history, function ($item) use ($module) { | ||
return $item['module_name'] == $module; | ||
}); | ||
|
||
foreach ($history as $key => $row) { | ||
$history[$key]['item_summary_short'] = | ||
to_html(getTrackerSubstring($row['item_summary'])); //bug 56373 - need to re-HTML-encode | ||
$history[$key]['image'] = | ||
SugarThemeRegistry::current()->getImage( | ||
$row['module_name'], | ||
'border="0" align="absmiddle"', | ||
null, | ||
null, | ||
'.gif', | ||
$row['item_summary'] | ||
); | ||
} | ||
|
||
return $history; | ||
|
||
} | ||
function getUserModuleFavs($userId, $module) | ||
{ | ||
|
||
$favoritesBean = BeanFactory::getBean('Favorites'); | ||
$favs = $favoritesBean->getCurrentUserSidebarFavorites(); | ||
|
||
$favs = array_filter($favs, function ($item) use ($module) { | ||
return $item['module_name'] == $module; | ||
}); | ||
|
||
foreach ($favs as $key => $row) { | ||
$history[$key]['item_summary_short'] = | ||
to_html(getTrackerSubstring($row['item_summary'])); //bug 56373 - need to re-HTML-encode | ||
$history[$key]['image'] = | ||
SugarThemeRegistry::current()->getImage( | ||
$row['module_name'], | ||
'border="0" align="absmiddle"', | ||
null, | ||
null, | ||
'.gif', | ||
$row['item_summary'] | ||
); | ||
} | ||
|
||
return $favs; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* This file is part of SinergiaCRM. | ||
* SinergiaCRM is a work developed by SinergiaTIC Association, based on SuiteCRM. | ||
* Copyright (C) 2013 - 2023 SinergiaTIC Association | ||
* | ||
* This program is free software; you can redistribute it and/or modify it under | ||
* the terms of the GNU Affero General Public License version 3 as published by the | ||
* Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | ||
* details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License along with | ||
* this program; if not, see http://www.gnu.org/licenses or write to the Free | ||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
* 02110-1301 USA. | ||
* | ||
* You can contact SinergiaTIC Association at email address [email protected]. | ||
*/ | ||
|
||
|
||
$(document).ready(function() { | ||
// Initialize SmartMenus plugin with specific offsets | ||
$("#stic-menu").smartmenus({ | ||
subMenusSubOffsetX: 1, | ||
subMenusSubOffsetY: -8, | ||
showTimeout: 100 | ||
}); | ||
|
||
// Update the menu when the first item is clicked (presumably the menu toggle) | ||
$("#stic-menu > li:first-child > a").on("click", function(e) { | ||
e.preventDefault(); | ||
updateActionMenu(); | ||
}); | ||
|
||
// Update the menu on initial page load | ||
updateActionMenu(); | ||
}); | ||
|
||
/** | ||
* Builds the action menu by cloning elements from the sidebar | ||
* @returns {jQuery} A new unordered list containing the action menu items | ||
*/ | ||
function buildActionMenu() { | ||
var $newActionMenu = $("<ul>"); | ||
|
||
// Iterate through each action menu link in the sidebar | ||
$("#actionMenuSidebar ul li.actionmenulinks").each(function() { | ||
var $originalLink = $(this).find("a"); | ||
var $icon = $originalLink.find(".side-bar-action-icon span").clone(); | ||
var linkText = $originalLink.find(".actionmenulink").text(); | ||
|
||
// Create a new link with the same attributes as the original | ||
var $newLink = $("<a>") | ||
.attr("href", $originalLink.attr("href")) | ||
.attr("data-action-name", $originalLink.data("action-name")) | ||
.addClass("link-cloned"); | ||
|
||
// Append the icon and text to the new link | ||
$newLink.append($icon).append(linkText); | ||
|
||
// Wrap the new link in a list item and add it to the menu | ||
var $newListItem = $("<li>").append($newLink); | ||
$newActionMenu.append($newListItem); | ||
}); | ||
|
||
return $newActionMenu; | ||
} | ||
|
||
/** | ||
* Updates the action menu in the main navigation | ||
* This function rebuilds the action menu and inserts it into the appropriate area | ||
*/ | ||
function updateActionMenu() { | ||
var $newActionMenu = buildActionMenu(); | ||
var $actionsArea = $("#stic-menu #actions-area"); | ||
|
||
// Insert the new action menu at the beginning of the actions area, checking that the action is not being repeated | ||
if ($("#stic-menu #actions-area .link-cloned").length == 0) { | ||
$actionsArea.prepend($newActionMenu.children()); | ||
} | ||
// If there are no action items, reduce the opacity of the entire section | ||
if ($actionsArea.find("a").length == 0) { | ||
$actionsArea.closest("li").css("opacity", 0.2); | ||
} | ||
} |
Oops, something went wrong.