Refactor user permissions handling and enhance menu functionality with collapsible headers
This commit is contained in:
@@ -15,7 +15,7 @@ $user_data = $stmt->fetch();
|
|||||||
//Define User data
|
//Define User data
|
||||||
$partnerhierarchy = $user_data['partnerhierarchy'];
|
$partnerhierarchy = $user_data['partnerhierarchy'];
|
||||||
$permission = userRights($user_data['view']);
|
$permission = userRights($user_data['view']);
|
||||||
$profile= getProfile($user_data['settings'],$permission);
|
$profile= getUserPermissions($pdo, $user_data['id']);
|
||||||
$username = $user_data['username'];
|
$username = $user_data['username'];
|
||||||
$useremail = $user_data['email'];
|
$useremail = $user_data['email'];
|
||||||
$servicekey = $user_data['service'];
|
$servicekey = $user_data['service'];
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ if ($stmt->rowCount() == 1) {
|
|||||||
//Define User data
|
//Define User data
|
||||||
$partnerhierarchy = $user_data['partnerhierarchy'];
|
$partnerhierarchy = $user_data['partnerhierarchy'];
|
||||||
$permission = userRights($user_data['view']);
|
$permission = userRights($user_data['view']);
|
||||||
$profile= getProfile($user_data['settings'],$permission);
|
$profile= getUserPermissions($pdo, $user_data['id']);
|
||||||
$username = $user_data['username'];
|
$username = $user_data['username'];
|
||||||
$useremail = $user_data['email'];
|
$useremail = $user_data['email'];
|
||||||
$servicekey = $user_data['service'];
|
$servicekey = $user_data['service'];
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ switch ($permission) {
|
|||||||
//NEW ARRAY
|
//NEW ARRAY
|
||||||
$criterias = [];
|
$criterias = [];
|
||||||
$clause = '';
|
$clause = '';
|
||||||
|
$type_check = false;
|
||||||
|
|
||||||
//Check for $_GET variables and build up clause
|
//Check for $_GET variables and build up clause
|
||||||
if(isset($get_content) && $get_content!=''){
|
if(isset($get_content) && $get_content!=''){
|
||||||
@@ -117,6 +118,7 @@ if(isset($get_content) && $get_content!=''){
|
|||||||
//add new_querystring to clause
|
//add new_querystring to clause
|
||||||
$clause .= ' AND h.type IN ('.$new_querystring.')';
|
$clause .= ' AND h.type IN ('.$new_querystring.')';
|
||||||
//remove original key/value from array
|
//remove original key/value from array
|
||||||
|
$type_check = true;
|
||||||
unset($criterias[$v[0]]);
|
unset($criterias[$v[0]]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -142,6 +144,9 @@ if(isset($criterias['totals']) && $criterias['totals'] ==''){
|
|||||||
//Request for total rows
|
//Request for total rows
|
||||||
$sql ='SELECT count(h.rowID) as historyID FROM equipment_history h LEFT JOIN equipment e ON h.equipmentid = e.rowID '.$whereclause.'';
|
$sql ='SELECT count(h.rowID) as historyID FROM equipment_history h LEFT JOIN equipment e ON h.equipmentid = e.rowID '.$whereclause.'';
|
||||||
}
|
}
|
||||||
|
elseif($type_check){
|
||||||
|
$sql ='SELECT h.rowID as historyID, e.rowID as equipmentID, e.serialnumber, h.type, h.description, h.created, h.createdby FROM equipment_history h LEFT JOIN equipment e ON h.equipmentid = e.rowID '.$whereclause.' ORDER BY h.created DESC';
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
//request history
|
//request history
|
||||||
$sql ='SELECT h.rowID as historyID, e.rowID as equipmentID, e.serialnumber, h.type, h.description, h.created, h.createdby FROM equipment_history h LEFT JOIN equipment e ON h.equipmentid = e.rowID '.$whereclause.' ORDER BY h.created DESC LIMIT :page,:num_products';
|
$sql ='SELECT h.rowID as historyID, e.rowID as equipmentID, e.serialnumber, h.type, h.description, h.created, h.createdby FROM equipment_history h LEFT JOIN equipment e ON h.equipmentid = e.rowID '.$whereclause.' ORDER BY h.created DESC LIMIT :page,:num_products';
|
||||||
@@ -178,6 +183,12 @@ if(isset($criterias['totals']) && $criterias['totals']==''){
|
|||||||
$messages = $stmt->fetch();
|
$messages = $stmt->fetch();
|
||||||
$messages = $messages[0];
|
$messages = $messages[0];
|
||||||
}
|
}
|
||||||
|
elseif($type_check){
|
||||||
|
//Excute Query
|
||||||
|
$stmt->execute();
|
||||||
|
//Get results
|
||||||
|
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
$current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1;
|
$current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1;
|
||||||
$stmt->bindValue('page', ($current_page - 1) * $page_rows_history, PDO::PARAM_INT);
|
$stmt->bindValue('page', ($current_page - 1) * $page_rows_history, PDO::PARAM_INT);
|
||||||
|
|||||||
@@ -239,7 +239,6 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
|
|||||||
JOIN products_software_versions psv ON psa.software_version_id = psv.rowID
|
JOIN products_software_versions psv ON psa.software_version_id = psv.rowID
|
||||||
WHERE psa.product_id = ?
|
WHERE psa.product_id = ?
|
||||||
AND psa.status = 1
|
AND psa.status = 1
|
||||||
AND psv.latest = 1
|
|
||||||
AND (psv.hw_version = ? OR psv.hw_version IS NULL OR psv.hw_version = "")';
|
AND (psv.hw_version = ? OR psv.hw_version IS NULL OR psv.hw_version = "")';
|
||||||
|
|
||||||
$stmt = $pdo->prepare($sql);
|
$stmt = $pdo->prepare($sql);
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ $criterias = [];
|
|||||||
|
|
||||||
//ADD STANDARD PARAMETERS TO ARRAY BASED ON INSERT OR UPDATE
|
//ADD STANDARD PARAMETERS TO ARRAY BASED ON INSERT OR UPDATE
|
||||||
if ($command == 'update'){
|
if ($command == 'update'){
|
||||||
$post_content['updatedby'] = $username;;
|
$post_content['updatedby'] = $username;
|
||||||
$post_content['updated'] = $date;
|
$post_content['updated'] = $date;
|
||||||
}
|
}
|
||||||
elseif ($command == 'insert'){
|
elseif ($command == 'insert'){
|
||||||
$post_content['created'] = $date;
|
$post_content['created'] = $date;
|
||||||
$post_content['createdby'] = $username;;
|
$post_content['createdby'] = $username;
|
||||||
}
|
}
|
||||||
|
|
||||||
//CREAT NEW ARRAY AND MAP TO CLAUSE
|
//CREAT NEW ARRAY AND MAP TO CLAUSE
|
||||||
|
|||||||
@@ -20,6 +20,66 @@ document.querySelector('.responsive-toggle').onclick = event => {
|
|||||||
localStorage.setItem('admin_menu', 'closed');
|
localStorage.setItem('admin_menu', 'closed');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Menu header collapse/expand functionality
|
||||||
|
document.querySelectorAll('aside .menu-header').forEach(header => {
|
||||||
|
header.addEventListener('click', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Toggle expanded state
|
||||||
|
this.classList.toggle('expanded');
|
||||||
|
|
||||||
|
// Find the next sibling .sub element and toggle display
|
||||||
|
const submenu = this.nextElementSibling;
|
||||||
|
if (submenu && submenu.classList.contains('sub')) {
|
||||||
|
submenu.classList.toggle('expanded');
|
||||||
|
// Update inline style for display
|
||||||
|
submenu.style.display = submenu.classList.contains('expanded') ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate chevron
|
||||||
|
const chevron = this.querySelector('.menu-chevron');
|
||||||
|
if (chevron) {
|
||||||
|
chevron.style.transform = this.classList.contains('expanded') ? 'rotate(180deg)' : 'rotate(0deg)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store expanded state in localStorage for persistence
|
||||||
|
const section = this.dataset.section;
|
||||||
|
if (section) {
|
||||||
|
const expandedSections = JSON.parse(localStorage.getItem('menu_expanded') || '{}');
|
||||||
|
expandedSections[section] = this.classList.contains('expanded');
|
||||||
|
localStorage.setItem('menu_expanded', JSON.stringify(expandedSections));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore menu expanded states from localStorage on page load
|
||||||
|
(function restoreMenuState() {
|
||||||
|
const expandedSections = JSON.parse(localStorage.getItem('menu_expanded') || '{}');
|
||||||
|
|
||||||
|
document.querySelectorAll('aside .menu-header').forEach(header => {
|
||||||
|
const section = header.dataset.section;
|
||||||
|
const submenu = header.nextElementSibling;
|
||||||
|
const chevron = header.querySelector('.menu-chevron');
|
||||||
|
|
||||||
|
// If explicitly saved as expanded, apply it
|
||||||
|
if (section && expandedSections[section] === true) {
|
||||||
|
header.classList.add('expanded');
|
||||||
|
if (submenu && submenu.classList.contains('sub')) {
|
||||||
|
submenu.classList.add('expanded');
|
||||||
|
submenu.style.display = 'flex';
|
||||||
|
}
|
||||||
|
if (chevron) chevron.style.transform = 'rotate(180deg)';
|
||||||
|
}
|
||||||
|
// If has selected child, always expand (override localStorage)
|
||||||
|
if (submenu && submenu.querySelector('a.selected')) {
|
||||||
|
header.classList.add('expanded');
|
||||||
|
submenu.classList.add('expanded');
|
||||||
|
submenu.style.display = 'flex';
|
||||||
|
if (chevron) chevron.style.transform = 'rotate(180deg)';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
document.querySelectorAll('.tabs a').forEach((element, index) => {
|
document.querySelectorAll('.tabs a').forEach((element, index) => {
|
||||||
element.onclick = event => {
|
element.onclick = event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -233,20 +233,19 @@ function routes($urls) {
|
|||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
// Menu Builder
|
// Menu Builder
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
/**
|
||||||
|
* @deprecated Use filterMenuByPermissions() instead
|
||||||
|
* Filter menu items based on profile string (legacy)
|
||||||
|
*/
|
||||||
function filterMenuByProfile($menu, $profileString) {
|
function filterMenuByProfile($menu, $profileString) {
|
||||||
// Convert profile string to array
|
|
||||||
$profileArray = explode(',', $profileString);
|
$profileArray = explode(',', $profileString);
|
||||||
|
|
||||||
// Initialize result array
|
|
||||||
$filteredMenu = [];
|
$filteredMenu = [];
|
||||||
|
|
||||||
// Loop through main menu sections
|
|
||||||
foreach ($menu as $sectionKey => $section) {
|
foreach ($menu as $sectionKey => $section) {
|
||||||
$sectionIncluded = in_array($sectionKey, $profileArray);
|
$sectionIncluded = in_array($sectionKey, $profileArray);
|
||||||
$submenuFound = false;
|
$submenuFound = false;
|
||||||
$firstSubmenuItem = null;
|
$firstSubmenuItem = null;
|
||||||
|
|
||||||
// First check if any submenu items are in profile
|
|
||||||
foreach ($section as $itemKey => $item) {
|
foreach ($section as $itemKey => $item) {
|
||||||
if ($itemKey !== 'main_menu' && in_array($itemKey, $profileArray)) {
|
if ($itemKey !== 'main_menu' && in_array($itemKey, $profileArray)) {
|
||||||
$submenuFound = true;
|
$submenuFound = true;
|
||||||
@@ -255,24 +254,19 @@ function filterMenuByProfile($menu, $profileString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include this section if either section key or any submenu is in profile
|
|
||||||
if ($sectionIncluded || $submenuFound) {
|
if ($sectionIncluded || $submenuFound) {
|
||||||
$filteredMenu[$sectionKey] = [];
|
$filteredMenu[$sectionKey] = [];
|
||||||
|
|
||||||
// Add main_menu - if section not in profile but submenu found, use first submenu as main_menu
|
|
||||||
if (!$sectionIncluded && $submenuFound && $firstSubmenuItem !== null) {
|
if (!$sectionIncluded && $submenuFound && $firstSubmenuItem !== null) {
|
||||||
// Create hybrid main_menu - keep name and icon from original, but use URL and selected from submenu
|
|
||||||
$hybridMainMenu = $section['main_menu'];
|
$hybridMainMenu = $section['main_menu'];
|
||||||
$hybridMainMenu['url'] = $firstSubmenuItem['url'];
|
$hybridMainMenu['url'] = $firstSubmenuItem['url'];
|
||||||
$hybridMainMenu['selected'] = $firstSubmenuItem['selected'];
|
$hybridMainMenu['selected'] = $firstSubmenuItem['selected'];
|
||||||
|
|
||||||
$filteredMenu[$sectionKey]['main_menu'] = $hybridMainMenu;
|
$filteredMenu[$sectionKey]['main_menu'] = $hybridMainMenu;
|
||||||
} else {
|
} else {
|
||||||
$filteredMenu[$sectionKey]['main_menu'] = $section['main_menu'];
|
$filteredMenu[$sectionKey]['main_menu'] = $section['main_menu'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add allowed submenu items
|
|
||||||
foreach ($section as $itemKey => $item) {
|
foreach ($section as $itemKey => $item) {
|
||||||
if ($itemKey !== 'main_menu' && in_array($itemKey, $profileArray)) {
|
if ($itemKey !== 'main_menu' && in_array($itemKey, $profileArray)) {
|
||||||
$filteredMenu[$sectionKey][$itemKey] = $item;
|
$filteredMenu[$sectionKey][$itemKey] = $item;
|
||||||
@@ -280,17 +274,83 @@ function filterMenuByProfile($menu, $profileString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $filteredMenu;
|
return $filteredMenu;
|
||||||
}
|
}
|
||||||
function menu($selected,$selected_child){
|
|
||||||
|
/**
|
||||||
|
* Filter menu items based on user permissions array
|
||||||
|
*
|
||||||
|
* @param array $menu The full menu structure from settingsmenu.php
|
||||||
|
* @param array $permissions The permissions array from $_SESSION['authorization']['permissions']
|
||||||
|
* @return array Filtered menu with only items user has can_read permission for
|
||||||
|
*/
|
||||||
|
function filterMenuByPermissions($menu, $permissions) {
|
||||||
|
$filteredMenu = [];
|
||||||
|
|
||||||
|
foreach ($menu as $sectionKey => $section) {
|
||||||
|
// Get the main_menu's 'selected' path to check permission
|
||||||
|
$mainMenuPath = $section['main_menu']['selected'] ?? $sectionKey;
|
||||||
|
|
||||||
|
// Check if user has read permission for main menu
|
||||||
|
$mainMenuAllowed = isset($permissions[$mainMenuPath]) &&
|
||||||
|
$permissions[$mainMenuPath]['can_read'] == 1;
|
||||||
|
|
||||||
|
$allowedSubmenus = [];
|
||||||
|
$firstAllowedSubmenu = null;
|
||||||
|
|
||||||
|
// Check each submenu item for permission
|
||||||
|
foreach ($section as $itemKey => $item) {
|
||||||
|
if ($itemKey === 'main_menu') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the submenu item's 'selected' path
|
||||||
|
$submenuPath = $item['selected'] ?? $itemKey;
|
||||||
|
|
||||||
|
// Check if user has read permission for this submenu item
|
||||||
|
if (isset($permissions[$submenuPath]) &&
|
||||||
|
$permissions[$submenuPath]['can_read'] == 1) {
|
||||||
|
$allowedSubmenus[$itemKey] = $item;
|
||||||
|
if ($firstAllowedSubmenu === null) {
|
||||||
|
$firstAllowedSubmenu = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include section if main menu is allowed OR any submenu is allowed
|
||||||
|
if ($mainMenuAllowed || count($allowedSubmenus) > 0) {
|
||||||
|
$filteredMenu[$sectionKey] = [];
|
||||||
|
|
||||||
|
// Handle main_menu entry
|
||||||
|
if (!$mainMenuAllowed && $firstAllowedSubmenu !== null) {
|
||||||
|
// User doesn't have main access but has submenu access
|
||||||
|
// Create hybrid: keep name/icon from main, use URL/selected from first submenu
|
||||||
|
$hybridMainMenu = $section['main_menu'];
|
||||||
|
$hybridMainMenu['url'] = $firstAllowedSubmenu['url'];
|
||||||
|
$hybridMainMenu['selected'] = $firstAllowedSubmenu['selected'];
|
||||||
|
$filteredMenu[$sectionKey]['main_menu'] = $hybridMainMenu;
|
||||||
|
} else {
|
||||||
|
$filteredMenu[$sectionKey]['main_menu'] = $section['main_menu'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add allowed submenu items
|
||||||
|
foreach ($allowedSubmenus as $itemKey => $item) {
|
||||||
|
$filteredMenu[$sectionKey][$itemKey] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filteredMenu;
|
||||||
|
}
|
||||||
|
function menu($selected, $selected_child){
|
||||||
|
|
||||||
include dirname(__FILE__,2).'/settings/settings_redirector.php';
|
include dirname(__FILE__,2).'/settings/settings_redirector.php';
|
||||||
|
|
||||||
if(isset($_SESSION['country_code'])){
|
if(isset($_SESSION['country_code'])){
|
||||||
$api_file_language = dirname(__FILE__,2).'/settings/translations/translations_'.strtoupper($_SESSION['country_code']).'.php';
|
$api_file_language = dirname(__FILE__,2).'/settings/translations/translations_'.strtoupper($_SESSION['country_code']).'.php';
|
||||||
if (file_exists($api_file_language)){
|
if (file_exists($api_file_language)){
|
||||||
include $api_file_language; //Include the code
|
include $api_file_language;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
include dirname(__FILE__,2).'/settings/translations/translations_US.php';
|
include dirname(__FILE__,2).'/settings/translations/translations_US.php';
|
||||||
@@ -298,31 +358,70 @@ function menu($selected,$selected_child){
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
include dirname(__FILE__,2).'/settings/translations/translations_US.php';
|
include dirname(__FILE__,2).'/settings/translations/translations_US.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
//Define Menu
|
|
||||||
$menu = '';
|
$menu = '';
|
||||||
|
|
||||||
//filter the main_menu array based on profile
|
// Use permissions array if available, fallback to legacy profile string
|
||||||
$filteredMenu = filterMenuByProfile($main_menu, $_SESSION['authorization']['permissions']);
|
if (isset($_SESSION['authorization']['permissions']) && !empty($_SESSION['authorization']['permissions'])) {
|
||||||
|
$filteredMenu = filterMenuByPermissions($main_menu, $_SESSION['authorization']['permissions']);
|
||||||
|
} else {
|
||||||
|
$filteredMenu = filterMenuByProfile($main_menu, $_SESSION['authorization']['profile']);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($filteredMenu as $menu_item){
|
foreach ($filteredMenu as $menu_item) {
|
||||||
//Main Item
|
$submenuCount = count($menu_item) - 1; // Exclude main_menu
|
||||||
$menu .= '<a href="index.php?page='.$menu_item['main_menu']['url'].'"' . ($selected == $menu_item['main_menu']['selected'] ? ' class="selected"' : '') . '><i class="'.$menu_item['main_menu']['icon'].'"></i>'.ucfirst((${$menu_item['main_menu']['name']} ?? 'not specified')).'</a>';
|
$mainMenu = $menu_item['main_menu'];
|
||||||
|
$menuName = ucfirst((${$mainMenu['name']} ?? ucfirst(str_replace('menu_', '', $mainMenu['name']))));
|
||||||
if (count($menu_item) > 1){
|
$isMainSelected = ($selected == $mainMenu['selected']);
|
||||||
//SUBMENU
|
|
||||||
$menu .= '<div class="sub" ' . ($selected == $menu_item['main_menu']['selected'] ? ' class="selected"' : '') . '>';
|
|
||||||
|
|
||||||
foreach ($menu_item as $key => $item){
|
// Check if any child is selected (for expanded state)
|
||||||
//filter out main_menu
|
$hasSelectedChild = false;
|
||||||
if($key !='main_menu'){
|
foreach ($menu_item as $key => $item) {
|
||||||
$menu .= '<a href="index.php?page='.$item['url'].'"' . ($selected == $item['selected'] ? ' class="selected"' : '') . '><span>◼</span>'.ucfirst((${$item['name']}?? 'not specified')).'</a>';
|
if ($key !== 'main_menu' && $selected == $item['selected']) {
|
||||||
|
$hasSelectedChild = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$menu .= '</div>';
|
|
||||||
|
if ($submenuCount > 0) {
|
||||||
|
// HAS SUBMENUS: Render as collapsible header (not a link)
|
||||||
|
$expandedClass = ($isMainSelected || $hasSelectedChild) ? ' expanded' : '';
|
||||||
|
$selectedClass = $isMainSelected ? ' selected' : '';
|
||||||
|
|
||||||
|
$menu .= '<div class="menu-header' . $expandedClass . $selectedClass . '" data-section="' . htmlspecialchars($mainMenu['selected']) . '" style="font-size:14px !important;padding:15px 20px !important;cursor:pointer;display:flex;align-items:center;">';
|
||||||
|
$menu .= '<i class="' . $mainMenu['icon'] . '" style="display:inline-block;width:40px;text-align:left;"></i>';
|
||||||
|
$menu .= '<span style="flex:1;">' . $menuName . '</span>';
|
||||||
|
$menu .= '<i class="menu-chevron fas fa-chevron-down" style="font-size:12px;width:20px;text-align:right;"></i>';
|
||||||
|
$menu .= '</div>';
|
||||||
|
|
||||||
|
// SUBMENU container
|
||||||
|
$subExpandedClass = ($isMainSelected || $hasSelectedChild) ? ' expanded' : '';
|
||||||
|
$subDisplayStyle = ($isMainSelected || $hasSelectedChild) ? 'display:flex;' : 'display:none;';
|
||||||
|
$menu .= '<div class="sub' . $subExpandedClass . '" style="' . $subDisplayStyle . 'flex-flow:column;padding:13px 0;">';
|
||||||
|
|
||||||
|
foreach ($menu_item as $key => $item) {
|
||||||
|
if ($key !== 'main_menu') {
|
||||||
|
$itemName = ucfirst((${$item['name']} ?? ucfirst(str_replace('menu_', '', $item['name']))));
|
||||||
|
$itemSelectedClass = ($selected == $item['selected']) ? ' class="selected"' : '';
|
||||||
|
$menu .= '<a href="index.php?page=' . $item['url'] . '"' . $itemSelectedClass . ' style="font-size:14px !important;padding:4px 20px !important;display:flex;align-items:center;">';
|
||||||
|
$menu .= '<span style="display:inline-block;width:40px;font-size:12px;text-align:left;">◼</span>';
|
||||||
|
$menu .= '<span style="flex:1;">' . $itemName . '</span>';
|
||||||
|
$menu .= '</a>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$menu .= '</div>';
|
||||||
|
} else {
|
||||||
|
// NO SUBMENUS: Render as direct link
|
||||||
|
$selectedClass = $isMainSelected ? ' class="selected"' : '';
|
||||||
|
$menu .= '<a href="index.php?page=' . $mainMenu['url'] . '"' . $selectedClass . ' style="font-size:14px !important;padding:15px 20px !important;display:flex;align-items:center;">';
|
||||||
|
$menu .= '<i class="' . $mainMenu['icon'] . '" style="display:inline-block;width:40px;text-align:left;"></i>';
|
||||||
|
$menu .= '<span style="flex:1;">' . $menuName . '</span>';
|
||||||
|
$menu .= '<span style="width:20px;"></span>';
|
||||||
|
$menu .= '</a>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $menu;
|
return $menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,19 +518,6 @@ echo <<<EOT
|
|||||||
|
|
||||||
// Intercept fetch and XMLHttpRequest
|
// Intercept fetch and XMLHttpRequest
|
||||||
interceptNetworkRequests();
|
interceptNetworkRequests();
|
||||||
|
|
||||||
|
|
||||||
// Intercept all form submissions
|
|
||||||
function setupFormInterception() {
|
|
||||||
const forms = document.querySelectorAll('form');
|
|
||||||
|
|
||||||
forms.forEach(form => {
|
|
||||||
form.addEventListener('submit', function(e) {
|
|
||||||
// Show loading screen before form submission
|
|
||||||
showLoading();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intercept all network requests (fetch and XMLHttpRequest)
|
// Intercept all network requests (fetch and XMLHttpRequest)
|
||||||
function interceptNetworkRequests() {
|
function interceptNetworkRequests() {
|
||||||
@@ -1572,8 +1658,8 @@ function getProfile($profile, $permission){
|
|||||||
error_log($test, 3, $filelocation);
|
error_log($test, 3, $filelocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Check if basic_permission_level is 5 (System) - always allow
|
// 1. Check if basic_permission_level is 4 (System-admin+) - always allow
|
||||||
if ($basic_permission_level !== null && $basic_permission_level == 5) {
|
if ($basic_permission_level !== null && $basic_permission_level == 4) {
|
||||||
if(debug){
|
if(debug){
|
||||||
$test = "$date - Allowed by system permission (level 5)".PHP_EOL;
|
$test = "$date - Allowed by system permission (level 5)".PHP_EOL;
|
||||||
error_log($test, 3, $filelocation);
|
error_log($test, 3, $filelocation);
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ async function connectDeviceForSoftware() {
|
|||||||
progressBar("1", "", "");
|
progressBar("1", "", "");
|
||||||
|
|
||||||
// Check if DEBUG mode is enabled - use mock device data
|
// Check if DEBUG mode is enabled - use mock device data
|
||||||
if (typeof DEBUG !== 'undefined' && DEBUG) {
|
if (typeof DEBUG !== 'undefined' && DEBUG && typeof DEBUG_ID !== 'undefined' && DEBUG_ID) {
|
||||||
// TEST MODE: Use mock device data
|
// TEST MODE: Use mock device data
|
||||||
deviceSerialNumber = "22110095";
|
deviceSerialNumber = "22110095";
|
||||||
deviceVersion = "03e615af";
|
deviceVersion = "03e615af";
|
||||||
@@ -549,7 +549,7 @@ async function fetchSoftwareOptions() {
|
|||||||
progressBar("100", "Software options loaded", "#04AA6D");
|
progressBar("100", "Software options loaded", "#04AA6D");
|
||||||
|
|
||||||
// Show user info modal immediately (skip in debug mode)
|
// Show user info modal immediately (skip in debug mode)
|
||||||
if (typeof DEBUG === 'undefined' || !DEBUG) {
|
if (typeof DEBUG === 'undefined' || !DEBUG || typeof DEBUG_ID === 'undefined' || !DEBUG_ID) {
|
||||||
showUserInfoModal();
|
showUserInfoModal();
|
||||||
} else {
|
} else {
|
||||||
// In debug mode, reveal software options immediately
|
// In debug mode, reveal software options immediately
|
||||||
@@ -1619,7 +1619,7 @@ async function downloadAndInstallSoftware(option, customerData = null) {
|
|||||||
window.upgraded_version = option.version || "";
|
window.upgraded_version = option.version || "";
|
||||||
|
|
||||||
// DEBUG MODE: Don't auto-trigger upload, let user manually test
|
// DEBUG MODE: Don't auto-trigger upload, let user manually test
|
||||||
if (typeof DEBUG !== 'undefined' && DEBUG) {
|
if (typeof DEBUG !== 'undefined' && DEBUG && typeof DEBUG_ID !== 'undefined' && DEBUG_ID) {
|
||||||
// Show upload section and button for manual testing
|
// Show upload section and button for manual testing
|
||||||
document.getElementById("uploadSection").style.display = "block";
|
document.getElementById("uploadSection").style.display = "block";
|
||||||
const uploadBtn = document.getElementById("uploadSoftware");
|
const uploadBtn = document.getElementById("uploadSoftware");
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ $view .= '<div class="content-block">
|
|||||||
|
|
||||||
if ($update_allowed === 1){
|
if ($update_allowed === 1){
|
||||||
|
|
||||||
$view .='<a href="index.php?page=firmwaretool&equipmentID='.$responses->equipmentID.'" class="btn">'.$button_firmware.'</a>';
|
$view .='<a href="index.php?page=softwaretool" class="btn">'.$button_firmware.'</a>';
|
||||||
}
|
}
|
||||||
$view .='</div>';
|
$view .='</div>';
|
||||||
|
|
||||||
|
|||||||
93
index.php
93
index.php
@@ -108,8 +108,8 @@ if (isset($_GET['page']) && $_GET['page'] == 'logout') {
|
|||||||
|
|
||||||
//=====================================
|
//=====================================
|
||||||
//DEFINE WHERE TO SEND THE USER TO. GET first assigned view in the profile if not available use dashboard
|
//DEFINE WHERE TO SEND THE USER TO. GET first assigned view in the profile if not available use dashboard
|
||||||
//=====================================
|
/*=====================================
|
||||||
$allowed_views = explode(',',$_SESSION['authorization']['permissions']);
|
$allowed_views = explode(',',$_SESSION['authorization']['profile']);
|
||||||
$ignoreViews = ['profile','assets','sales'];
|
$ignoreViews = ['profile','assets','sales'];
|
||||||
|
|
||||||
// If dashboard is in the profile, prioritize it
|
// If dashboard is in the profile, prioritize it
|
||||||
@@ -118,16 +118,93 @@ if (in_array('dashboard', $allowed_views) && file_exists('dashboard.php')) {
|
|||||||
} else {
|
} else {
|
||||||
$allowed_views = findExistingView($allowed_views, 'dashboard', $ignoreViews);
|
$allowed_views = findExistingView($allowed_views, 'dashboard', $ignoreViews);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
// SIMPLE ROUTING SYSTEM
|
||||||
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
$page = $_GET['page'] ?? 'dashboard';
|
||||||
|
|
||||||
//=====================================
|
// Sanitize page parameter to prevent directory traversal
|
||||||
//FORWARD THE USER TO THE CORRECT PAGE
|
$page = preg_replace('/[^a-zA-Z0-9_-]/', '', $page);
|
||||||
//=====================================
|
$page_file = $page . '.php';
|
||||||
$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : $allowed_views;
|
|
||||||
|
|
||||||
// Output error variable
|
// Output error variable
|
||||||
$error = '';
|
$error = '';
|
||||||
// Include the requested page
|
|
||||||
include $page . '.php';
|
try {
|
||||||
|
$file_exists = file_exists($page_file);
|
||||||
|
$is_allowed = $file_exists ? isAllowed($page, $_SESSION['authorization']['permissions'], $_SESSION['authorization']['permission'], 'R') : 0;
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
debuglog("Routing: page={$page}, file_exists={$file_exists}, is_allowed={$is_allowed}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file_exists && $is_allowed !== 0) {
|
||||||
|
include $page_file;
|
||||||
|
} else {
|
||||||
|
// Show error page for missing files or unauthorized access
|
||||||
|
$page_exists = file_exists($page_file);
|
||||||
|
$error_title = $page_exists ? 'Access Denied' : 'Page Not Found';
|
||||||
|
$error_message = $page_exists
|
||||||
|
? 'You do not have permission to access this page.'
|
||||||
|
: 'The requested page "' . htmlspecialchars($page) . '" could not be found.';
|
||||||
|
$error_icon = $page_exists ? 'fa-solid fa-lock' : 'fa-solid fa-file-circle-xmark';
|
||||||
|
|
||||||
|
template_header($error_title, '');
|
||||||
|
echo '
|
||||||
|
<div class="content-title">
|
||||||
|
<div class="title">
|
||||||
|
<i class="' . $error_icon . '"></i>
|
||||||
|
<div class="txt">
|
||||||
|
<h2>' . $error_title . '</h2>
|
||||||
|
<p>' . $error_message . '</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-block" style="text-align: center; padding: 60px 20px;">
|
||||||
|
<div style="font-size: 64px; color: var(--gray-400, #9ca3af); margin-bottom: 20px;">
|
||||||
|
<i class="' . $error_icon . '"></i>
|
||||||
|
</div>
|
||||||
|
<p style="color: var(--gray-500, #6b7280); margin-bottom: 30px;">Please check the URL or navigate using the menu.</p>
|
||||||
|
<a href="index.php?page=dashboard" class="btn">
|
||||||
|
<i class="fa-solid fa-house"></i> Return to Dashboard
|
||||||
|
</a>
|
||||||
|
</div>';
|
||||||
|
template_footer();
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Handle any errors during page inclusion
|
||||||
|
if (debug) {
|
||||||
|
debuglog("Error loading page {$page}: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
template_header('System Error', '');
|
||||||
|
echo '
|
||||||
|
<div class="content-title">
|
||||||
|
<div class="title">
|
||||||
|
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||||
|
<div class="txt">
|
||||||
|
<h2>System Error</h2>
|
||||||
|
<p>An error occurred while loading the page.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-block" style="text-align: center; padding: 60px 20px;">
|
||||||
|
<div style="font-size: 64px; color: var(--gray-400, #9ca3af); margin-bottom: 20px;">
|
||||||
|
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||||
|
</div>
|
||||||
|
<p style="color: var(--gray-500, #6b7280); margin-bottom: 30px;">Please try again or contact the system administrator.</p>
|
||||||
|
<div style="display: flex; gap: 10px; justify-content: center;">
|
||||||
|
<a href="index.php?page=dashboard" class="btn">
|
||||||
|
<i class="fa-solid fa-house"></i> Return to Dashboard
|
||||||
|
</a>
|
||||||
|
<button onclick="location.reload()" class="btn">
|
||||||
|
<i class="fa-solid fa-rotate-right"></i> Reload Page
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
|
template_footer();
|
||||||
|
}
|
||||||
|
|
||||||
//=====================================
|
//=====================================
|
||||||
//debuglog
|
//debuglog
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ $main_menu = [
|
|||||||
],
|
],
|
||||||
"equipments" =>[
|
"equipments" =>[
|
||||||
"url" => "equipments",
|
"url" => "equipments",
|
||||||
"selected" => "assets",
|
"selected" => "equipments",
|
||||||
"icon" => "fa-solid fa-database",
|
"icon" => "fa-solid fa-database",
|
||||||
"name" => "menu_assets"
|
"name" => "menu_assets"
|
||||||
],
|
],
|
||||||
@@ -181,6 +181,12 @@ $main_menu = [
|
|||||||
"icon" => "fa-solid fa-magnifying-glass-chart",
|
"icon" => "fa-solid fa-magnifying-glass-chart",
|
||||||
"name" => "menu_report_main"
|
"name" => "menu_report_main"
|
||||||
],
|
],
|
||||||
|
"report_builder" => [
|
||||||
|
"url" => "report_builder",
|
||||||
|
"selected" => "report_builder",
|
||||||
|
"icon" => "fa-solid fa-magnifying-glass-chart",
|
||||||
|
"name" => "menu_report_main"
|
||||||
|
],
|
||||||
"report_build" => [
|
"report_build" => [
|
||||||
"url" => "report_build",
|
"url" => "report_build",
|
||||||
"selected" => "report_build",
|
"selected" => "report_build",
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ if (isset($_GET['equipmentID'])){$returnpage = 'equipment&equipmentID='.$_GET['e
|
|||||||
|
|
||||||
|
|
||||||
//SHOW BACK BUTTON ONLY FOR PORTAL USERS
|
//SHOW BACK BUTTON ONLY FOR PORTAL USERS
|
||||||
if (isAllowed('dashboard',$_SESSION['authorization']['permissions'],$_SESSION['authorization']['permission'],'R') != 0){
|
if (isAllowed($page ,$_SESSION['authorization']['permissions'],$_SESSION['authorization']['permission'],'R') != 0){
|
||||||
$view .= '
|
$view .= '
|
||||||
<div class="title-actions">
|
<div class="title-actions">
|
||||||
<a href="index.php?page='.$returnpage.'" class="btn alt mar-right-2"><i class="fa-solid fa-arrow-left"></i></a>
|
<a href="index.php?page='.$returnpage.'" class="btn alt mar-right-2"><i class="fa-solid fa-arrow-left"></i></a>
|
||||||
@@ -313,6 +313,7 @@ echo '
|
|||||||
<script>
|
<script>
|
||||||
var link = "'.$baseurl.'";
|
var link = "'.$baseurl.'";
|
||||||
var DEBUG = '.(debug ? 'true' : 'false').';
|
var DEBUG = '.(debug ? 'true' : 'false').';
|
||||||
|
var DEBUG_ID = '.(debug_id == $_SESSION['authorization']['id'] ? 'true' : 'false').';
|
||||||
var MOLLIE_ENABLED = '.(mollie_enabled ? 'true' : 'false').';
|
var MOLLIE_ENABLED = '.(mollie_enabled ? 'true' : 'false').';
|
||||||
var PAYPAL_ENABLED = '.(paypal_enabled ? 'true' : 'false').';
|
var PAYPAL_ENABLED = '.(paypal_enabled ? 'true' : 'false').';
|
||||||
var PAY_ON_DELIVERY_ENABLED = '.(pay_on_delivery_enabled ? 'true' : 'false').';
|
var PAY_ON_DELIVERY_ENABLED = '.(pay_on_delivery_enabled ? 'true' : 'false').';
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ header {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
padding-left: 260px;
|
padding-left: 230px;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 55px;
|
height: 55px;
|
||||||
@@ -149,13 +149,6 @@ aside > a i {
|
|||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside > a:hover, aside > a.selected {
|
|
||||||
background-color: var(--color-green);
|
|
||||||
color: var(--color-white);
|
|
||||||
padding: 15px 17px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
aside > a.selected + .sub {
|
aside > a.selected + .sub {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -192,6 +185,42 @@ aside .sub a:hover, aside .sub a.selected {
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Expanded submenu (via JS toggle or selected state) */
|
||||||
|
aside .sub.expanded {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Menu Header (collapsible parent with submenus) */
|
||||||
|
aside .menu-header {
|
||||||
|
font-size: 14px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-color-accent-2);
|
||||||
|
padding: 15px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside .menu-header i:first-child {
|
||||||
|
color: inherit;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside .menu-header .menu-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside .menu-header .menu-chevron {
|
||||||
|
width: auto;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside .menu-header.expanded .menu-chevron {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
aside .footer {
|
aside .footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
@@ -218,7 +247,7 @@ aside.closed {
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
padding-left: 290px;
|
padding-left: 240px;
|
||||||
padding-top: 65px;
|
padding-top: 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user