Merge branch 'edit_menu_navbar'
commit
6a33a8ccb0
|
|
@ -1348,6 +1348,39 @@ CREATE TABLE `Notifications` (
|
|||
CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
--
|
||||
-- Table structure for table `Menu_Items`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `Menu_Items`;
|
||||
CREATE TABLE `Menu_Items` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`MenuKey` varchar(32) NOT NULL,
|
||||
`Enabled` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`Label` varchar(64) DEFAULT NULL,
|
||||
`SortOrder` smallint NOT NULL DEFAULT 0,
|
||||
`Icon` varchar(128) DEFAULT NULL,
|
||||
`IconType` enum('material','fontawesome','image','none') NOT NULL DEFAULT 'material',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `Menu_Items_MenuKey_idx` (`MenuKey`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
INSERT INTO `Menu_Items` (`MenuKey`, `Enabled`, `SortOrder`) VALUES
|
||||
('Console', 1, 10),
|
||||
('Montage', 1, 20),
|
||||
('MontageReview', 1, 30),
|
||||
('Events', 1, 40),
|
||||
('Options', 1, 50),
|
||||
('Log', 1, 60),
|
||||
('Devices', 1, 70),
|
||||
('IntelGpu', 1, 80),
|
||||
('Groups', 1, 90),
|
||||
('Filters', 1, 100),
|
||||
('Snapshots', 1, 110),
|
||||
('Reports', 1, 120),
|
||||
('ReportEventAudit', 1, 130),
|
||||
('Map', 1, 140);
|
||||
|
||||
source @PKGDATADIR@/db/Object_Types.sql
|
||||
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
|
||||
source @PKGDATADIR@/db/triggers.sql
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
--
|
||||
-- Add Menu_Items table for customizable navbar/sidebar menu
|
||||
--
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Menu_Items'
|
||||
) > 0,
|
||||
"SELECT 'Table Menu_Items already exists'",
|
||||
"CREATE TABLE `Menu_Items` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`MenuKey` varchar(32) NOT NULL,
|
||||
`Enabled` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`Label` varchar(64) DEFAULT NULL,
|
||||
`SortOrder` smallint NOT NULL DEFAULT 0,
|
||||
`Icon` varchar(128) DEFAULT NULL,
|
||||
`IconType` enum('material','fontawesome','image','none') NOT NULL DEFAULT 'material',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `Menu_Items_MenuKey_idx` (`MenuKey`)
|
||||
) ENGINE=InnoDB"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
--
|
||||
-- Seed default menu items if table is empty
|
||||
--
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM `Menu_Items`) > 0,
|
||||
"SELECT 'Menu_Items already has data'",
|
||||
"INSERT INTO `Menu_Items` (`MenuKey`, `Enabled`, `SortOrder`) VALUES
|
||||
('Console', 1, 10),
|
||||
('Montage', 1, 20),
|
||||
('MontageReview', 1, 30),
|
||||
('Events', 1, 40),
|
||||
('Options', 1, 50),
|
||||
('Log', 1, 60),
|
||||
('Devices', 1, 70),
|
||||
('IntelGpu', 1, 80),
|
||||
('Groups', 1, 90),
|
||||
('Filters', 1, 100),
|
||||
('Snapshots', 1, 110),
|
||||
('Reports', 1, 120),
|
||||
('ReportEventAudit', 1, 130),
|
||||
('Map', 1, 140)"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
--
|
||||
-- Add Icon and IconType columns if they don't exist
|
||||
--
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Menu_Items' AND column_name = 'Icon'
|
||||
) > 0,
|
||||
"SELECT 'Column Icon already exists'",
|
||||
"ALTER TABLE `Menu_Items` ADD `Icon` varchar(128) DEFAULT NULL AFTER `SortOrder`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Menu_Items' AND column_name = 'IconType'
|
||||
) > 0,
|
||||
"SELECT 'Column IconType already exists'",
|
||||
"ALTER TABLE `Menu_Items` ADD `IconType` enum('material','fontawesome','image','none') NOT NULL DEFAULT 'material' AFTER `Icon`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
namespace ZM;
|
||||
require_once('database.php');
|
||||
require_once('Object.php');
|
||||
|
||||
class MenuItem extends ZM_Object {
|
||||
protected static $table = 'Menu_Items';
|
||||
|
||||
protected $defaults = array(
|
||||
'Id' => null,
|
||||
'MenuKey' => '',
|
||||
'Enabled' => 1,
|
||||
'Label' => null,
|
||||
'SortOrder' => 0,
|
||||
'Icon' => null,
|
||||
'IconType' => 'material',
|
||||
);
|
||||
|
||||
// Default material icons for each menu key
|
||||
public static $defaultIcons = array(
|
||||
'Console' => 'dashboard',
|
||||
'Montage' => 'live_tv',
|
||||
'MontageReview' => 'movie',
|
||||
'Events' => 'event',
|
||||
'Options' => 'settings',
|
||||
'Log' => 'notification_important',
|
||||
'Devices' => 'devices_other',
|
||||
'IntelGpu' => 'memory',
|
||||
'Groups' => 'group',
|
||||
'Filters' => 'filter_alt',
|
||||
'Snapshots' => 'preview',
|
||||
'Reports' => 'report',
|
||||
'ReportEventAudit' => 'shield',
|
||||
'Map' => 'language',
|
||||
);
|
||||
|
||||
public function effectiveIcon() {
|
||||
if ($this->{'Icon'} !== null && $this->{'Icon'} !== '') {
|
||||
return $this->{'Icon'};
|
||||
}
|
||||
return isset(self::$defaultIcons[$this->{'MenuKey'}]) ? self::$defaultIcons[$this->{'MenuKey'}] : 'menu';
|
||||
}
|
||||
|
||||
public function effectiveIconType() {
|
||||
if ($this->{'IconType'} == 'none') {
|
||||
return 'none';
|
||||
}
|
||||
if ($this->{'Icon'} !== null && $this->{'Icon'} !== '') {
|
||||
return $this->{'IconType'};
|
||||
}
|
||||
return 'material';
|
||||
}
|
||||
|
||||
public static function find($parameters = array(), $options = array()) {
|
||||
return ZM_Object::_find(self::class, $parameters, $options);
|
||||
}
|
||||
|
||||
public static function find_one($parameters = array(), $options = array()) {
|
||||
return ZM_Object::_find_one(self::class, $parameters, $options);
|
||||
}
|
||||
|
||||
public function displayLabel() {
|
||||
if ($this->{'Label'} !== null && $this->{'Label'} !== '') {
|
||||
return $this->{'Label'};
|
||||
}
|
||||
return translate($this->{'MenuKey'});
|
||||
}
|
||||
}
|
||||
|
|
@ -189,5 +189,95 @@ if ( $action == 'delete' ) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if ($action == 'menuitems') {
|
||||
if (!canEdit('System')) {
|
||||
ZM\Warning('Need System permission to edit menu items');
|
||||
} else if (isset($_REQUEST['items'])) {
|
||||
require_once('includes/MenuItem.php');
|
||||
$allItems = ZM\MenuItem::find();
|
||||
foreach ($allItems as $item) {
|
||||
$id = $item->Id();
|
||||
$enabled = isset($_REQUEST['items'][$id]['Enabled']) ? 1 : 0;
|
||||
$label = isset($_REQUEST['items'][$id]['Label']) ? trim($_REQUEST['items'][$id]['Label']) : null;
|
||||
$sortOrder = isset($_REQUEST['items'][$id]['SortOrder']) ? intval($_REQUEST['items'][$id]['SortOrder']) : $item->SortOrder();
|
||||
if ($label === '') $label = null;
|
||||
|
||||
$iconType = isset($_REQUEST['items'][$id]['IconType']) ? $_REQUEST['items'][$id]['IconType'] : $item->IconType();
|
||||
if (!in_array($iconType, ['material', 'fontawesome', 'image', 'none'])) $iconType = 'material';
|
||||
$icon = isset($_REQUEST['items'][$id]['Icon']) ? trim($_REQUEST['items'][$id]['Icon']) : $item->Icon();
|
||||
if ($icon === '') $icon = null;
|
||||
|
||||
// Handle image upload
|
||||
if (isset($_FILES['items']['name'][$id]['IconFile'])
|
||||
&& $_FILES['items']['error'][$id]['IconFile'] == UPLOAD_ERR_OK) {
|
||||
$uploadDir = ZM_PATH_WEB.'/graphics/menu/';
|
||||
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
|
||||
|
||||
$tmpName = $_FILES['items']['tmp_name'][$id]['IconFile'];
|
||||
$origName = basename($_FILES['items']['name'][$id]['IconFile']);
|
||||
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION));
|
||||
$allowedExts = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico'];
|
||||
if (in_array($ext, $allowedExts)) {
|
||||
// Validate it's actually an image (except SVG/ICO)
|
||||
if ($ext == 'svg' || $ext == 'ico' || getimagesize($tmpName) !== false) {
|
||||
$safeName = 'menu_'.$id.'_'.time().'.'.$ext;
|
||||
$destPath = $uploadDir.$safeName;
|
||||
if (move_uploaded_file($tmpName, $destPath)) {
|
||||
// Remove old uploaded icon if it exists
|
||||
if ($item->IconType() == 'image' && $item->Icon() && file_exists(ZM_PATH_WEB.'/'.$item->Icon())) {
|
||||
unlink(ZM_PATH_WEB.'/'.$item->Icon());
|
||||
}
|
||||
$icon = 'graphics/menu/'.$safeName;
|
||||
$iconType = 'image';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If user cleared icon, reset to default
|
||||
if ($iconType != 'image' && ($icon === null || $icon === '')) {
|
||||
$icon = null;
|
||||
}
|
||||
|
||||
$item->save([
|
||||
'Enabled' => $enabled,
|
||||
'Label' => $label,
|
||||
'SortOrder' => $sortOrder,
|
||||
'Icon' => $icon,
|
||||
'IconType' => $iconType,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$redirect = '?view=options&tab=menu';
|
||||
} else if ($action == 'resetmenu') {
|
||||
if (!canEdit('System')) {
|
||||
ZM\Warning('Need System permission to reset menu items');
|
||||
} else {
|
||||
// Clean up any uploaded icon files
|
||||
require_once('includes/MenuItem.php');
|
||||
$oldItems = ZM\MenuItem::find();
|
||||
foreach ($oldItems as $item) {
|
||||
if ($item->IconType() == 'image' && $item->Icon() && file_exists(ZM_PATH_WEB.'/'.$item->Icon())) {
|
||||
unlink(ZM_PATH_WEB.'/'.$item->Icon());
|
||||
}
|
||||
}
|
||||
dbQuery('DELETE FROM Menu_Items');
|
||||
dbQuery("INSERT INTO `Menu_Items` (`MenuKey`, `Enabled`, `SortOrder`) VALUES
|
||||
('Console', 1, 10),
|
||||
('Montage', 1, 20),
|
||||
('MontageReview', 1, 30),
|
||||
('Events', 1, 40),
|
||||
('Options', 1, 50),
|
||||
('Log', 1, 60),
|
||||
('Devices', 1, 70),
|
||||
('IntelGpu', 1, 80),
|
||||
('Groups', 1, 90),
|
||||
('Filters', 1, 100),
|
||||
('Snapshots', 1, 110),
|
||||
('Reports', 1, 120),
|
||||
('ReportEventAudit', 1, 130),
|
||||
('Map', 1, 140)");
|
||||
}
|
||||
$redirect = '?view=options&tab=menu';
|
||||
} // end if object vs action
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -179,48 +179,139 @@ function getBodyTopHTML() {
|
|||
}
|
||||
} // end function getBodyTopHTML
|
||||
|
||||
function buildMenuItem($viewItemName, $id, $itemName, $href, $icon, $classNameForTag_A = '', $subMenu = '') {
|
||||
function renderMenuIcon($icon, $iconType = 'material') {
|
||||
if ($iconType == 'none') {
|
||||
return '';
|
||||
} else if ($iconType == 'fontawesome') {
|
||||
return '<i class="fa '.htmlspecialchars($icon).'"></i>';
|
||||
} else if ($iconType == 'image') {
|
||||
return '<img src="'.htmlspecialchars($icon).'" alt="" style="width:24px;height:24px;object-fit:contain;"/>';
|
||||
}
|
||||
return '<i class="material-icons">'.htmlspecialchars($icon).'</i>';
|
||||
}
|
||||
|
||||
// Returns icon HTML from the current $menuIconOverride, or empty string if not set
|
||||
function getNavbarIcon() {
|
||||
global $menuIconOverride;
|
||||
if (!isset($menuIconOverride)) return '';
|
||||
$html = renderMenuIcon($menuIconOverride['icon'], $menuIconOverride['iconType']);
|
||||
if ($html !== '') $html .= ' ';
|
||||
return $html;
|
||||
}
|
||||
|
||||
function buildMenuItem($viewItemName, $id, $itemName, $href, $icon, $classNameForTag_A = '', $subMenu = '', $skipTranslate = false, $iconType = 'material') {
|
||||
global $view;
|
||||
global $menuIconOverride;
|
||||
|
||||
// Check for icon override from renderMenuItems
|
||||
if (isset($menuIconOverride)) {
|
||||
$icon = $menuIconOverride['icon'];
|
||||
$iconType = $menuIconOverride['iconType'];
|
||||
}
|
||||
|
||||
/* Highlighting the active menu section */
|
||||
if ($viewItemName == 'watch') {
|
||||
$activeClass = ($view == $viewItemName && (isset($_REQUEST['cycle']) && $_REQUEST['cycle'] == "true")) ? ' active' : '';
|
||||
} else {
|
||||
$activeClass = $view == $viewItemName ? ' active' : '';
|
||||
}
|
||||
$itemName = translate($itemName);
|
||||
if (!$skipTranslate) $itemName = translate($itemName);
|
||||
$result = '
|
||||
<li id="' . $id . '" class="menu-item '.$activeClass.'">
|
||||
<a href="' . $href . '" class="' . $classNameForTag_A . '">
|
||||
<span class="menu-icon"><i class="material-icons">' . $icon . '</i></span>
|
||||
<span class="menu-title">'.$itemName.'</span>
|
||||
<span class="menu-icon">'.renderMenuIcon($icon, $iconType).'</span>
|
||||
<span class="menu-title">'.htmlspecialchars($itemName).'</span>
|
||||
</a>
|
||||
</li>'.PHP_EOL;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function getMenuItemFunctions() {
|
||||
return [
|
||||
'Console' => 'getConsoleHTML',
|
||||
'Montage' => 'getMontageHTML',
|
||||
'MontageReview' => 'getMontageReviewHTML',
|
||||
'Events' => 'getEventsHTML',
|
||||
'Options' => 'getOptionsHTML',
|
||||
'Log' => 'getLogHTML',
|
||||
'Devices' => 'getDevicesHTML',
|
||||
'IntelGpu' => 'getIntelGpuHTML',
|
||||
'Groups' => 'getGroupsHTML',
|
||||
'Filters' => 'getFilterHTML',
|
||||
'Snapshots' => 'getSnapshotsHTML',
|
||||
'Reports' => 'getReportsHTML',
|
||||
'ReportEventAudit' => 'getRprtEvntAuditHTML',
|
||||
'Map' => 'getMapHTML',
|
||||
];
|
||||
}
|
||||
|
||||
function getMenuItems() {
|
||||
static $cached = null;
|
||||
if ($cached === null) {
|
||||
require_once('includes/MenuItem.php');
|
||||
$cached = ZM\MenuItem::find([], ['order' => 'SortOrder ASC']);
|
||||
}
|
||||
return $cached;
|
||||
}
|
||||
|
||||
// Functions that take only ($forLeftBar, $customLabel) - no $view parameter
|
||||
function getMenuFuncsNoView() {
|
||||
return ['getConsoleHTML', 'getOptionsHTML', 'getLogHTML', 'getDevicesHTML', 'getIntelGpuHTML'];
|
||||
}
|
||||
|
||||
function renderMenuItems($forLeftBar = false) {
|
||||
global $view;
|
||||
$menuItems = getMenuItems();
|
||||
$funcMap = getMenuItemFunctions();
|
||||
$result = '';
|
||||
|
||||
if (empty($menuItems)) {
|
||||
// Fallback: no DB rows yet, render all in default order
|
||||
foreach ($funcMap as $key => $funcName) {
|
||||
$noViewFuncs = getMenuFuncsNoView();
|
||||
if (in_array($funcName, $noViewFuncs)) {
|
||||
$result .= $funcName($forLeftBar);
|
||||
} else {
|
||||
$result .= $funcName($view, $forLeftBar);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
global $menuIconOverride;
|
||||
$noViewFuncs = getMenuFuncsNoView();
|
||||
foreach ($menuItems as $item) {
|
||||
if (!$item->Enabled()) continue;
|
||||
$key = $item->MenuKey();
|
||||
if (!isset($funcMap[$key])) continue;
|
||||
|
||||
$funcName = $funcMap[$key];
|
||||
$customLabel = ($item->Label() !== null && $item->Label() !== '') ? $item->displayLabel() : null;
|
||||
|
||||
// Set icon override for buildMenuItem/getOptionsHTML to pick up
|
||||
$effIcon = $item->effectiveIcon();
|
||||
$effIconType = $item->effectiveIconType();
|
||||
$menuIconOverride = ['icon' => $effIcon, 'iconType' => $effIconType];
|
||||
|
||||
if (in_array($funcName, $noViewFuncs)) {
|
||||
$result .= $funcName($forLeftBar, $customLabel);
|
||||
} else {
|
||||
$result .= $funcName($view, $forLeftBar, $customLabel);
|
||||
}
|
||||
}
|
||||
$menuIconOverride = null;
|
||||
}
|
||||
|
||||
$result .= getAdditionalLinksHTML($view, $forLeftBar);
|
||||
return $result;
|
||||
}
|
||||
|
||||
function buildSidebarMenu() {
|
||||
global $view;
|
||||
global $user;
|
||||
if ( $user and $user->Username() ) {
|
||||
$menuForAuthUser = '
|
||||
<li class="menu-header"><span> GENERAL </span></li> ' .
|
||||
getConsoleHTML($forLeftBar = true) .
|
||||
getMontageHTML($view, $forLeftBar = true) .
|
||||
getCycleHTML($view, $forLeftBar = true) .
|
||||
getMontageReviewHTML($view, $forLeftBar = true) .
|
||||
getEventsHTML($view, $forLeftBar = true) .
|
||||
getOptionsHTML($forLeftBar = true) .
|
||||
getLogHTML($forLeftBar = true) .
|
||||
getDevicesHTML($forLeftBar = true) .
|
||||
getIntelGpuHTML($forLeftBar = true) .
|
||||
getGroupsHTML($view, $forLeftBar = true) .
|
||||
getFilterHTML($view, $forLeftBar = true) .
|
||||
getSnapshotsHTML($view, $forLeftBar = true) .
|
||||
getReportsHTML($view, $forLeftBar = true) .
|
||||
getRprtEvntAuditHTML($view, $forLeftBar = true) .
|
||||
getMapHTML($view, $forLeftBar = true) .
|
||||
getAdditionalLinksHTML($view, $forLeftBar = true)
|
||||
renderMenuItems($forLeftBar = true)
|
||||
;
|
||||
} else { // USER IS NOT AUTHORIZED!
|
||||
$menuForAuthUser = '';
|
||||
|
|
@ -511,21 +602,7 @@ function getNormalNavBarHTML($running, $user, $bandwidth_options, $view, $skin)
|
|||
|
||||
// *** Build the navigation bar menu items ***
|
||||
echo '<ul class="nav navbar-nav align-self-start justify-content-center">';
|
||||
echo getConsoleHTML();
|
||||
echo getOptionsHTML();
|
||||
echo getLogHTML();
|
||||
echo getDevicesHTML();
|
||||
echo getIntelGpuHTML();
|
||||
echo getGroupsHTML($view);
|
||||
echo getFilterHTML($view);
|
||||
echo getCycleHTML($view);
|
||||
echo getMontageHTML($view);
|
||||
echo getMontageReviewHTML($view);
|
||||
echo getSnapshotsHTML($view);
|
||||
echo getReportsHTML($view);
|
||||
echo getRprtEvntAuditHTML($view);
|
||||
echo getMapHTML($view);
|
||||
echo getAdditionalLinksHTML($view);
|
||||
echo renderMenuItems($forLeftBar = false);
|
||||
echo getHeaderFlipHTML();
|
||||
echo '</ul></div><div id="accountstatus">';
|
||||
echo '<ul class="nav navbar-nav justify-content-end align-self-start flex-grow-1">';
|
||||
|
|
@ -669,21 +746,7 @@ function getCollapsedNavBarHTML($running, $user, $bandwidth_options, $view, $ski
|
|||
<?php
|
||||
if ( $user and $user->Username() ) {
|
||||
echo '<ul class="navbar-nav">';
|
||||
echo getConsoleHTML();
|
||||
echo getOptionsHTML();
|
||||
echo getLogHTML();
|
||||
echo getDevicesHTML();
|
||||
echo getIntelGpuHTML();
|
||||
echo getGroupsHTML($view);
|
||||
echo getFilterHTML($view);
|
||||
echo getCycleHTML($view);
|
||||
echo getMontageHTML($view);
|
||||
echo getMontageReviewHTML($view);
|
||||
echo getSnapshotsHTML($view);
|
||||
echo getReportsHTML($view);
|
||||
echo getRprtEvntAuditHTML($view);
|
||||
echo getMapHTML($view);
|
||||
echo getAdditionalLinksHTML($view);
|
||||
echo renderMenuItems($forLeftBar = false);
|
||||
echo '</ul>';
|
||||
}
|
||||
?>
|
||||
|
|
@ -963,23 +1026,26 @@ function getNavBrandHTML() {
|
|||
}
|
||||
|
||||
// Returns the html representing the Console menu item
|
||||
function getConsoleHTML($forLeftBar = false) {
|
||||
function getConsoleHTML($forLeftBar = false, $customLabel = null) {
|
||||
global $user;
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'Console';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if (count($user->viewableMonitorIds()) or !ZM\Monitor::find_one()) {
|
||||
if ($forLeftBar) {
|
||||
$result .= buildMenuItem(
|
||||
$viewItemName = 'console',
|
||||
$id = 'getConsoleHTML',
|
||||
$itemName = 'Console',
|
||||
$itemName = $label,
|
||||
$href = '?view=console',
|
||||
$icon = 'dashboard',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getConsoleHTML" class="nav-item"><a class="nav-link" href="?view=console">'.translate('Console').'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getConsoleHTML" class="nav-item"><a class="nav-link" href="?view=console">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -987,9 +1053,10 @@ function getConsoleHTML($forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the Options menu item
|
||||
function getOptionsHTML($forLeftBar = false) {
|
||||
function getOptionsHTML($forLeftBar = false, $customLabel = null) {
|
||||
global $zmMenu;
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : translate('Options');
|
||||
|
||||
// Sorting order of the "Options" submenu items. If a submenu item is in the DB but is not here, it will be automatically added to the end of the list.
|
||||
$categoryDisplayOrder = [
|
||||
|
|
@ -1026,7 +1093,8 @@ function getOptionsHTML($forLeftBar = false) {
|
|||
'privacy',
|
||||
'MQTT',
|
||||
'telemetry',
|
||||
'version'
|
||||
'version',
|
||||
'menu'
|
||||
]);
|
||||
$zmMenu::buildSubMenuOptions($categoryDisplayOrder);
|
||||
|
||||
|
|
@ -1055,16 +1123,23 @@ function getOptionsHTML($forLeftBar = false) {
|
|||
</div>
|
||||
';
|
||||
|
||||
$optIcon = 'settings';
|
||||
$optIconType = 'material';
|
||||
if (isset($menuIconOverride)) {
|
||||
$optIcon = $menuIconOverride['icon'];
|
||||
$optIconType = $menuIconOverride['iconType'];
|
||||
}
|
||||
|
||||
$result .= '
|
||||
<li id="getOptionsHTML" class="menu-item sub-menu '.($view == "options" ? ' open' : '').'">
|
||||
<a href="#<!--?view='.$view_.'&tab=system-->">
|
||||
<span class="menu-icon"><i class="material-icons">settings</i></span>
|
||||
<span class="menu-title">'.translate('Options').'</span>
|
||||
<span class="menu-icon">'.renderMenuIcon($optIcon, $optIconType).'</span>
|
||||
<span class="menu-title">'.htmlspecialchars($label).'</span>
|
||||
</a>
|
||||
' . $subMenuOptions . '
|
||||
</li>'.PHP_EOL;
|
||||
} else {
|
||||
$result .= '<li id="getOptionsHTML" class="nav-item"><a class="nav-link" href="?view=options">'.translate('Options').'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getOptionsHTML" class="nav-item"><a class="nav-link" href="?view=options">'.getNavbarIcon().htmlspecialchars($label).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1072,9 +1147,11 @@ function getOptionsHTML($forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the Log menu item
|
||||
function getLogHTML($forLeftBar = false) {
|
||||
function getLogHTML($forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
|
||||
$label = $customLabel !== null ? $customLabel : 'Log';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if ( canView('System') ) {
|
||||
if ( ZM\logToDatabase() > ZM\Logger::NOLOG ) {
|
||||
$logstate = logState();
|
||||
|
|
@ -1083,18 +1160,19 @@ function getLogHTML($forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'log',
|
||||
$id = 'getLogHTML',
|
||||
$itemName = 'Log',
|
||||
$itemName = $label,
|
||||
$href = '?view=log',
|
||||
$icon = 'notification_important',
|
||||
$classNameForTag_A = $class,
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getLogHTML" class="nav-item"><a class="nav-link '.$class.'" href="?view=log">'.translate('Log').'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getLogHTML" class="nav-item"><a class="nav-link '.$class.'" href="?view=log">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
@ -1116,22 +1194,25 @@ function getLogIconHTML() {
|
|||
}
|
||||
|
||||
// Returns the html representing the X10 Devices menu item
|
||||
function getDevicesHTML($forLeftBar = false) {
|
||||
function getDevicesHTML($forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'Devices';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if ( ZM_OPT_X10 && canView('Devices') ) {
|
||||
if ($forLeftBar) {
|
||||
$result .= buildMenuItem(
|
||||
$viewItemName = 'devices',
|
||||
$id = 'getDevicesHTML',
|
||||
$itemName = 'Devices',
|
||||
$itemName = $label,
|
||||
$href = '?view=devices',
|
||||
$icon = 'devices_other',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getDevicesHTML" class="nav-item"><a class="nav-link" href="?view=devices">'.translate('Devices').'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getDevicesHTML" class="nav-item"><a class="nav-link" href="?view=devices">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1139,8 +1220,10 @@ function getDevicesHTML($forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the Intel GPU status menu item
|
||||
function getIntelGpuHTML($forLeftBar = false) {
|
||||
function getIntelGpuHTML($forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'Intel GPU';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
// Only show if intel_gpu_top is available and user can view System
|
||||
if (canView('System')) {
|
||||
|
|
@ -1151,14 +1234,15 @@ function getIntelGpuHTML($forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'intelgpu',
|
||||
$id = 'getIntelGpuHTML',
|
||||
$itemName = 'Intel GPU',
|
||||
$itemName = $label,
|
||||
$href = '?view=intelgpu',
|
||||
$icon = 'memory',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getIntelGpuHTML" class="nav-item"><a class="nav-link" href="?view=intelgpu">Intel GPU</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getIntelGpuHTML" class="nav-item"><a class="nav-link" href="?view=intelgpu">'.getNavbarIcon().htmlspecialchars($label).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1167,80 +1251,63 @@ function getIntelGpuHTML($forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the Groups menu item
|
||||
function getGroupsHTML($view, $forLeftBar = false) {
|
||||
function getGroupsHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
if ( !canView('Groups') ) return $result;
|
||||
$label = $customLabel !== null ? $customLabel : 'Groups';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
$class = $view == 'groups' ? ' selected' : '';
|
||||
if ($forLeftBar) {
|
||||
$result .= buildMenuItem(
|
||||
$viewItemName = 'groups',
|
||||
$id = 'getGroupsHTML',
|
||||
$itemName = 'Groups',
|
||||
$itemName = $label,
|
||||
$href = '?view=groups',
|
||||
$icon = 'group',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getGroupsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=groups">'. translate('Groups') .'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getGroupsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=groups">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Returns the html representing the Filter menu item
|
||||
function getFilterHTML($view, $forLeftBar = false) {
|
||||
function getFilterHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
if ( !canView('Events') ) return $result;
|
||||
|
||||
$label = $customLabel !== null ? $customLabel : 'Filters';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
$class = $view == 'filter' ? ' selected' : '';
|
||||
if ($forLeftBar) {
|
||||
$result .= buildMenuItem(
|
||||
$viewItemName = 'filter',
|
||||
$id = 'getFilterHTML',
|
||||
$itemName = 'Filters',
|
||||
$itemName = $label,
|
||||
$href = '?view=filter',
|
||||
$icon = 'filter_alt',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getFilterHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=filter">'.translate('Filters').'</a></li>'.PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Returns the html representing the Cycle menu item
|
||||
function getCycleHTML($view, $forLeftBar = false) {
|
||||
$result = '';
|
||||
|
||||
if ( canView('Stream') ) {
|
||||
$class = $view == 'cycle' ? ' selected' : '';
|
||||
if ($forLeftBar) {
|
||||
$result .= buildMenuItem(
|
||||
$viewItemName = 'watch',
|
||||
$id = 'getCycleHTML',
|
||||
$itemName = 'Cycle',
|
||||
$href = '?view=watch&cycle=true',
|
||||
//$icon = 'cyclone',
|
||||
$icon = 'repeat',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getCycleHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=watch&cycle=true">' .translate('Cycle'). '</a></li>'.PHP_EOL;
|
||||
}
|
||||
$result .= '<li id="getFilterHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=filter">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Returns the html representing the Montage menu item
|
||||
function getMontageHTML($view, $forLeftBar = false) {
|
||||
function getMontageHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
global $user;
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'Montage';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if (canView('Stream') and count($user->viewableMonitorIds())) {
|
||||
$class = $view == 'montage' ? ' selected' : '';
|
||||
|
|
@ -1248,14 +1315,15 @@ function getMontageHTML($view, $forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'montage',
|
||||
$id = 'getMontageHTML',
|
||||
$itemName = 'Montage',
|
||||
$itemName = $label,
|
||||
$href = '?view=montage',
|
||||
$icon = 'live_tv',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getMontageHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=montage">' .translate('Montage'). '</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getMontageHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=montage">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1263,9 +1331,11 @@ function getMontageHTML($view, $forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the MontageReview menu item
|
||||
function getMontageReviewHTML($view, $forLeftBar = false) {
|
||||
function getMontageReviewHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
|
||||
$label = $customLabel !== null ? $customLabel : 'MontageReview';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if ( canView('Events') ) {
|
||||
if ( isset($_REQUEST['filter']['Query']['terms']['attr']) ) {
|
||||
$terms = $_REQUEST['filter']['Query']['terms'];
|
||||
|
|
@ -1287,14 +1357,15 @@ function getMontageReviewHTML($view, $forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'montagereview',
|
||||
$id = 'getMontageReviewHTML',
|
||||
$itemName = 'MontageReview',
|
||||
$itemName = $label,
|
||||
$href = '?view=montagereview' .$live,
|
||||
$icon = 'movie',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getMontageReviewHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=montagereview' .$live. '">'.translate('MontageReview').'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getMontageReviewHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=montagereview' .$live. '">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1302,8 +1373,10 @@ function getMontageReviewHTML($view, $forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the Montage menu item
|
||||
function getSnapshotsHTML($view, $forLeftBar = false) {
|
||||
function getSnapshotsHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'Snapshots';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if (defined('ZM_FEATURES_SNAPSHOTS') and ZM_FEATURES_SNAPSHOTS and canView('Snapshots')) {
|
||||
$class = $view == 'snapshots' ? ' selected' : '';
|
||||
|
|
@ -1311,14 +1384,15 @@ function getSnapshotsHTML($view, $forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'snapshots',
|
||||
$id = 'getSnapshotsHTML',
|
||||
$itemName = 'Snapshots',
|
||||
$itemName = $label,
|
||||
$href = '?view=snapshots',
|
||||
$icon = 'preview',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getSnapshotsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=snapshots">' .translate('Snapshots'). '</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getSnapshotsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=snapshots">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1326,9 +1400,11 @@ function getSnapshotsHTML($view, $forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the Events menu item
|
||||
function getEventsHTML($view, $forLeftBar = false) {
|
||||
function getEventsHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
global $user;
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'Events';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if (canView('Events')) {
|
||||
$class = $view == 'events' ? ' selected' : '';
|
||||
|
|
@ -1336,22 +1412,25 @@ function getEventsHTML($view, $forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'events',
|
||||
$id = 'getEventsHTML',
|
||||
$itemName = 'Events',
|
||||
$itemName = $label,
|
||||
$href = '?view=events',
|
||||
$icon = 'event',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getEventsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=events">' .translate('Events'). '</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getEventsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=events">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function getReportsHTML($view, $forLeftBar = false) {
|
||||
function getReportsHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'Reports';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if (canView('Events')) {
|
||||
$class = ($view == 'reports' or $view == 'report') ? ' selected' : '';
|
||||
|
|
@ -1359,14 +1438,15 @@ function getReportsHTML($view, $forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'reports',
|
||||
$id = 'getReportsHTML',
|
||||
$itemName = 'Reports',
|
||||
$itemName = $label,
|
||||
$href = '?view=reports',
|
||||
$icon = 'report',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getReportsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=reports">'.translate('Reports').'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getReportsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=reports">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1374,8 +1454,10 @@ function getReportsHTML($view, $forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the Audit Events Report menu item
|
||||
function getRprtEvntAuditHTML($view, $forLeftBar = false) {
|
||||
function getRprtEvntAuditHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'ReportEventAudit';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if ( canView('Events') ) {
|
||||
$class = $view == 'report_event_audit' ? ' selected' : '';
|
||||
|
|
@ -1383,14 +1465,15 @@ function getRprtEvntAuditHTML($view, $forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'report_event_audit',
|
||||
$id = 'getRprtEvntAuditHTML',
|
||||
$itemName = 'ReportEventAudit',
|
||||
$itemName = $label,
|
||||
$href = '?view=report_event_audit',
|
||||
$icon = 'shield',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getRprtEvntAuditHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=report_event_audit">'.translate('ReportEventAudit').'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getRprtEvntAuditHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=report_event_audit">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1398,8 +1481,10 @@ function getRprtEvntAuditHTML($view, $forLeftBar = false) {
|
|||
}
|
||||
|
||||
// Returns the html representing the Audit Events Report menu item
|
||||
function getMapHTML($view, $forLeftBar = false) {
|
||||
function getMapHTML($view, $forLeftBar = false, $customLabel = null) {
|
||||
$result = '';
|
||||
$label = $customLabel !== null ? $customLabel : 'Map';
|
||||
$skipTranslate = $customLabel !== null;
|
||||
|
||||
if (defined('ZM_OPT_USE_GEOLOCATION') and ZM_OPT_USE_GEOLOCATION) {
|
||||
$class = $view == 'map' ? ' selected' : '';
|
||||
|
|
@ -1407,14 +1492,15 @@ function getMapHTML($view, $forLeftBar = false) {
|
|||
$result .= buildMenuItem(
|
||||
$viewItemName = 'map',
|
||||
$id = 'getMapHTML',
|
||||
$itemName = 'Map',
|
||||
$itemName = $label,
|
||||
$href = '?view=map',
|
||||
$icon = 'language',
|
||||
$classNameForTag_A = '',
|
||||
$subMenu = ''
|
||||
$subMenu = '',
|
||||
$skipTranslate
|
||||
);
|
||||
} else {
|
||||
$result .= '<li id="getMapHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=map">'.translate('Map').'</a></li>'.PHP_EOL;
|
||||
$result .= '<li id="getMapHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=map">'.getNavbarIcon().htmlspecialchars($skipTranslate ? $label : translate($label)).'</a></li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
require_once('includes/MenuItem.php');
|
||||
$menuItems = ZM\MenuItem::find([], ['order' => 'SortOrder ASC']);
|
||||
$canEdit = canEdit('System');
|
||||
?>
|
||||
<form name="menuItemsForm" method="post" action="?" enctype="multipart/form-data">
|
||||
<input type="hidden" name="view" value="options"/>
|
||||
<input type="hidden" name="tab" value="menu"/>
|
||||
<input type="hidden" name="action" value="menuitems"/>
|
||||
<div id="options">
|
||||
<div class="row pb-2">
|
||||
<div class="col">
|
||||
<div id="contentButtons">
|
||||
<?php if ($canEdit) { ?>
|
||||
<button type="submit" class="btn btn-primary"><?php echo translate('Save') ?></button>
|
||||
<button type="button" id="sortMenuBtn" data-on-click-this="sortMenuItems">
|
||||
<i class="material-icons" title="<?php echo translate('Click and drag rows to change order') ?>">swap_vert</i>
|
||||
<span class="text"><?php echo translate('Sort') ?></span>
|
||||
</button>
|
||||
<button type="submit" name="action" value="resetmenu" class="btn btn-warning"
|
||||
onclick="return confirm('<?php echo addslashes(translate('Reset menu items to defaults?')) ?>');"
|
||||
><?php echo translate('Reset') ?></button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper-scroll-table">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table table-striped" id="menuItemsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left"><?php echo translate('Enabled') ?></th>
|
||||
<th class="text-left"><?php echo translate('Name') ?></th>
|
||||
<th class="text-left"><?php echo translate('Custom Label') ?></th>
|
||||
<th class="text-left"><?php echo translate('Icon') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="menuItemsBody">
|
||||
<?php foreach ($menuItems as $item) {
|
||||
$id = $item->Id();
|
||||
$effIcon = $item->effectiveIcon();
|
||||
$effIconType = $item->effectiveIconType();
|
||||
$hasCustomIcon = ($item->Icon() !== null && $item->Icon() !== '');
|
||||
?>
|
||||
<tr id="menuItem-<?php echo $id ?>">
|
||||
<td>
|
||||
<input type="hidden" name="items[<?php echo $id ?>][Id]" value="<?php echo $id ?>"/>
|
||||
<input type="hidden" name="items[<?php echo $id ?>][SortOrder]" class="sortOrderInput" value="<?php echo $item->SortOrder() ?>"/>
|
||||
<input type="checkbox" name="items[<?php echo $id ?>][Enabled]" value="1"
|
||||
<?php echo $item->Enabled() ? 'checked' : '' ?>
|
||||
<?php echo !$canEdit ? 'disabled' : '' ?>
|
||||
/>
|
||||
</td>
|
||||
<td class="text-left"><?php echo htmlspecialchars(translate($item->MenuKey())) ?></td>
|
||||
<td>
|
||||
<input type="text" name="items[<?php echo $id ?>][Label]"
|
||||
value="<?php echo htmlspecialchars($item->Label() ?? '') ?>"
|
||||
placeholder="<?php echo htmlspecialchars(translate($item->MenuKey())) ?>"
|
||||
<?php echo !$canEdit ? 'disabled' : '' ?>
|
||||
/>
|
||||
</td>
|
||||
<td class="text-left">
|
||||
<div class="d-flex align-items-center" style="gap:6px;">
|
||||
<span class="menuIconPreview" id="iconPreview-<?php echo $id ?>">
|
||||
<?php if ($effIconType == 'fontawesome') { ?>
|
||||
<i class="fa <?php echo htmlspecialchars($effIcon) ?>"></i>
|
||||
<?php } else if ($effIconType == 'image') { ?>
|
||||
<img src="<?php echo htmlspecialchars($effIcon) ?>" style="width:24px;height:24px;object-fit:contain;" alt=""/>
|
||||
<?php } else { ?>
|
||||
<i class="material-icons"><?php echo htmlspecialchars($effIcon) ?></i>
|
||||
<?php } ?>
|
||||
</span>
|
||||
<select name="items[<?php echo $id ?>][IconType]" class="form-control form-control-sm iconTypeSelect"
|
||||
data-item-id="<?php echo $id ?>"
|
||||
style="width:auto;display:inline-block;"
|
||||
<?php echo !$canEdit ? 'disabled' : '' ?>
|
||||
>
|
||||
<option value="material" <?php echo $effIconType == 'material' ? 'selected' : '' ?>>Material</option>
|
||||
<option value="fontawesome" <?php echo $effIconType == 'fontawesome' ? 'selected' : '' ?>>Font Awesome</option>
|
||||
<option value="image" <?php echo $effIconType == 'image' ? 'selected' : '' ?>>Image</option>
|
||||
<option value="none" <?php echo $effIconType == 'none' ? 'selected' : '' ?>>None</option>
|
||||
</select>
|
||||
<input type="text" name="items[<?php echo $id ?>][Icon]" class="form-control form-control-sm iconNameInput"
|
||||
id="iconName-<?php echo $id ?>"
|
||||
value="<?php echo htmlspecialchars($hasCustomIcon ? $item->Icon() : '') ?>"
|
||||
placeholder="<?php echo htmlspecialchars($effIcon) ?>"
|
||||
style="width:140px;<?php echo ($effIconType == 'image' || $effIconType == 'none') ? 'display:none;' : '' ?>"
|
||||
<?php echo !$canEdit ? 'disabled' : '' ?>
|
||||
/>
|
||||
<input type="file" name="items[<?php echo $id ?>][IconFile]" class="form-control-file form-control-sm iconFileInput"
|
||||
id="iconFile-<?php echo $id ?>"
|
||||
accept="image/*"
|
||||
style="width:200px;<?php echo $effIconType != 'image' ? 'display:none;' : '' ?>"
|
||||
<?php echo !$canEdit ? 'disabled' : '' ?>
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -267,14 +267,14 @@ echo $navbar ?>
|
|||
<?php if ( ZM_WEB_LIST_THUMBS ) { ?>
|
||||
<th data-sortable="false" data-field="Thumbnail" class="colThumbnail"><?php echo translate('Thumbnail') ?></th>
|
||||
<?php } ?>
|
||||
<th data-sortable="true" data-field="Name" class="colName"><i class="material-icons">videocam</i> <?php echo translate('Name') ?></th>
|
||||
<th data-sortable="true" data-field="Name" class="colName"><?php echo translate('Name') ?></th>
|
||||
<th data-sortable="true" data-visible="false" data-field="Manufacturer" class="colName"><?php echo translate('Manufacturer') ?></th>
|
||||
<th data-sortable="true" data-visible="false" data-field="Model" class="colName"><?php echo translate('Model') ?></th>
|
||||
<th data-sortable="true" data-field="Function" class="colFunction"><?php echo translate('Function') ?></th>
|
||||
<?php if ( count($Servers) ) { ?>
|
||||
<th data-sortable="true" data-field="Server" class="colServer"><?php echo translate('Server') ?></th>
|
||||
<?php } ?>
|
||||
<th data-sortable="true" data-field="Source" class="colSource"><i class="material-icons">settings</i> <?php echo translate('Source') ?></th>
|
||||
<th data-sortable="true" data-field="Source" class="colSource"><?php echo translate('Source') ?></th>
|
||||
<?php if ( $show_storage_areas ) { ?>
|
||||
<th data-sortable="true" data-field="Storage" class="colStorage"><?php echo translate('Storage') ?></th>
|
||||
<?php }
|
||||
|
|
@ -293,13 +293,14 @@ echo $navbar ?>
|
|||
: array('cnj'=>'and', 'attr'=>'Monitor')
|
||||
);
|
||||
parseFilter($filter);
|
||||
echo '<th data-sortable="true" data-field="'.$i.'Events" class="colEvents"><a '
|
||||
.(canView('Events') ? 'href="?view='.ZM_WEB_EVENTS_VIEW.'&page=1'.$filter['querystring'].'">' : '')
|
||||
.$eventCounts[$i]['title']
|
||||
.'</a></th>'.PHP_EOL;
|
||||
$eventsLink = canView('Events') ? '?view='.ZM_WEB_EVENTS_VIEW.'&page=1'.$filter['querystring'] : '';
|
||||
echo '<th data-sortable="true" data-field="'.$i.'Events" class="colEvents"'
|
||||
.'>'
|
||||
.htmlspecialchars($eventCounts[$i]['title'])
|
||||
.'</th>'.PHP_EOL;
|
||||
} // end foreach eventCounts
|
||||
?>
|
||||
<th data-sortable="true" data-field="ZoneCount" class="colZones"><a href="?view=zones"><?php echo translate('Zones') ?></a></th>
|
||||
<th data-sortable="true" data-field="ZoneCount" class="colZones"><?php echo translate('Zones') ?></th>
|
||||
<th data-sortable="true" data-visible="false" data-field="Sequence" class="Sequence"><?php echo translate('Sequence') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
|||
|
|
@ -552,6 +552,22 @@ function initPage() {
|
|||
|
||||
// Make the table visible after initialization
|
||||
table.show();
|
||||
|
||||
// Add icons to column headers after bootstrap-table init so they don't
|
||||
// leak into the Columns dropdown (which uses the plain text title).
|
||||
var headerIcons = {Name: 'videocam', Source: 'settings'};
|
||||
table.find('thead th').each(function() {
|
||||
var field = $j(this).data('field');
|
||||
var inner = $j(this).find('.th-inner');
|
||||
if (!inner.length) return;
|
||||
if (headerIcons[field]) {
|
||||
inner.prepend('<i class="material-icons">' + headerIcons[field] + '</i> ');
|
||||
}
|
||||
if (field === 'ZoneCount') {
|
||||
var text = inner.text();
|
||||
inner.html('<a href="?view=zones">' + text + '</a>');
|
||||
}
|
||||
});
|
||||
} // end function initPage
|
||||
|
||||
function sortMonitors(button) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,19 @@ function AddNewRole(el) {
|
|||
window.location.assign(url);
|
||||
}
|
||||
|
||||
function sortMenuItems(button) {
|
||||
if (button.classList.contains('btn-success')) {
|
||||
$j('#menuItemsBody').sortable('disable');
|
||||
// Update hidden sort order fields based on new row positions
|
||||
$j('#menuItemsBody tr').each(function(index) {
|
||||
$j(this).find('.sortOrderInput').val((index + 1) * 10);
|
||||
});
|
||||
} else {
|
||||
$j('#menuItemsBody').sortable('enable');
|
||||
}
|
||||
button.classList.toggle('btn-success');
|
||||
}
|
||||
|
||||
function initPage() {
|
||||
const NewStorageBtn = $j('#NewStorageBtn');
|
||||
const NewServerBtn = $j('#NewServerBtn');
|
||||
|
|
@ -65,6 +78,36 @@ function initPage() {
|
|||
NewServerBtn.prop('disabled', !canEdit.System);
|
||||
|
||||
$j('.bootstraptable').bootstrapTable({icons: icons}).show();
|
||||
|
||||
// Menu items tab: sortable drag-and-drop and icon type toggle
|
||||
if ($j('#menuItemsBody').length) {
|
||||
$j('#menuItemsBody').sortable({
|
||||
disabled: true,
|
||||
axis: 'y',
|
||||
cursor: 'move',
|
||||
update: function() {
|
||||
$j('#menuItemsBody tr').each(function(index) {
|
||||
$j(this).find('.sortOrderInput').val((index + 1) * 10);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle between text input and file input based on icon type
|
||||
$j('.iconTypeSelect').on('change', function() {
|
||||
const id = $j(this).data('item-id');
|
||||
const type = $j(this).val();
|
||||
if (type === 'image') {
|
||||
$j('#iconName-' + id).hide();
|
||||
$j('#iconFile-' + id).show();
|
||||
} else if (type === 'none') {
|
||||
$j('#iconName-' + id).hide();
|
||||
$j('#iconFile-' + id).hide();
|
||||
} else {
|
||||
$j('#iconName-' + id).show();
|
||||
$j('#iconFile-' + id).hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$j(document).ready(function() {
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@ foreach (array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as $
|
|||
include('_options_roles.php');
|
||||
} else if ($tab == 'API') {
|
||||
include('_options_api.php');
|
||||
} // $tab == API
|
||||
} else if ($tab == 'menu') {
|
||||
include('_options_menu.php');
|
||||
} // $tab == API/menu
|
||||
else {
|
||||
$config = array();
|
||||
$configCats = array();
|
||||
|
|
|
|||
Loading…
Reference in New Issue