mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-31 11:03:22 +01:00
feat(navbar): introduce dynamic NavbarItem & NavbarRegistry system
- Added NavbarItem class to encapsulate navigation item configuration (key, parent, URL, label, permissions, order, visibility flags, etc.) - Implemented NavbarRegistry for centralized registration and retrieval of navigation items, including hierarchical structure support - Introduced registerNavbar() method across modules to allow modular injection of navigation entries - Refactored admin header rendering to build navigation dynamically from the registry instead of static definitions Updated modules: - FingerprintModule - MagscanModule - MinistraModule - PlexModule - TheftDetectionModule - TmdbModule - WatchModule Benefits: - Improves maintainability by removing hardcoded navigation - Enables modular and scalable extension of the admin menu - Provides consistent API for core and external modules Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
+9
-8
@@ -46,7 +46,7 @@
|
||||
| `ModuleInterface` | `ГОТОВО` | Базовый контракт присутствует |
|
||||
| `ModuleLoader::loadAll()` | `ГОТОВО` | Auto-discovery по `module.json` |
|
||||
| `ModuleLoader::registerAllCommands()` | `ГОТОВО` | CLI-команды модулей реально подключаются |
|
||||
| `ModuleLoader::bootAll()` | `ЧАСТИЧНО` | Реализован, но не встроен в `public/index.php` |
|
||||
| `ModuleLoader::bootAll()` | `ЧАСТИЧНО` | Реализован; остаются legacy-узлы web bootstrap вне модульного lifecycle |
|
||||
| `config/modules.php` | `ГОТОВО` | Overrides для enabled/class |
|
||||
| `ModuleManager` | `ГОТОВО` | Список, install, uninstall, enable, disable, update, upload |
|
||||
| Админ-страница модулей | `ГОТОВО` | `ModulesController` + `Views/admin/modules.php` |
|
||||
@@ -73,10 +73,11 @@
|
||||
- `watch`, `plex`, `fingerprint`, `theft_detection` и связанные страницы зарегистрированы статически в `public/routes/admin.php`
|
||||
- это делает модульную маршрутизацию вторичной и неавторитетной
|
||||
|
||||
### 3.3. Навигация модулей хардкодится в `header.php`
|
||||
### 3.3. Legacy-остатки в навигации и web bootstrap
|
||||
|
||||
- пункты `Plex Sync`, `Watch`, `Fingerprint` и часть связанных ссылок зашиты в `public/Views/admin/header.php`
|
||||
- модуль не может сам заявить навигацию как контракт
|
||||
- модульные пункты меню уже регистрируются через `registerNavbar()` + `NavbarRegistry`
|
||||
- рендер в `public/Views/admin/header.php` выполняется из дерева `NavbarRegistry`, без hardcoded ссылок конкретных модулей
|
||||
- остаются точки legacy-сцепления вне navbar (в отдельных route/bootstrap участках), которые ещё нужно довести до полной целевой модели
|
||||
|
||||
### 3.4. `module.json` слишком бедный
|
||||
|
||||
@@ -185,10 +186,10 @@ PHASE M-2 / ROUTE CUTOVER
|
||||
PHASE M-3 / NAVBAR API
|
||||
```
|
||||
|
||||
1. Создать extension points для header navigation.
|
||||
2. Вынести builder/render слой модульной навигации из `header.php`.
|
||||
3. Добавить декларацию navbar items на стороне модулей.
|
||||
4. Удалить hardcoded ссылки `plex`, `watch`, `fingerprint` из header runtime.
|
||||
1. Создать extension points для header navigation. ✅
|
||||
2. Вынести builder/render слой модульной навигации из `header.php`. ✅
|
||||
3. Добавить декларацию navbar items на стороне модулей. ✅
|
||||
4. Удалить hardcoded ссылки `plex`, `watch`, `fingerprint` из header runtime. ✅
|
||||
|
||||
**Результат:** ядро перестаёт знать о конкретных модулях на уровне меню.
|
||||
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CoreNavbarProvider — registers all built-in admin navigation items.
|
||||
*
|
||||
* Called once at the start of ModuleLoader::bootAll() before any module
|
||||
* registers its own items. Modules inject additional items via the same
|
||||
* NavbarRegistry::add() API at reserved order slots (60+, 170+, etc.).
|
||||
*
|
||||
* @package XC_VM_Core_Module
|
||||
* @author Divarion_D <https://github.com/Divarion-D>
|
||||
* @copyright 2025-2026 Vateron Media
|
||||
* @link https://github.com/Vateron-Media/XC_VM
|
||||
* @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*/
|
||||
|
||||
class CoreNavbarProvider {
|
||||
|
||||
/**
|
||||
* Register all core navigation items with the NavbarRegistry.
|
||||
*
|
||||
* This method acts as the main entry point, calling each category-specific
|
||||
* registration method to build the complete admin navigation structure.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register(): void {
|
||||
self::_dashboard();
|
||||
self::_servers();
|
||||
self::_users();
|
||||
self::_content();
|
||||
self::_bouquets();
|
||||
self::_management();
|
||||
self::_suppliers();
|
||||
}
|
||||
|
||||
// ── Dashboard ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Register Dashboard navigation items.
|
||||
*
|
||||
* Adds the main dashboard menu item and its live connections sub-item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _dashboard(): void {
|
||||
NavbarRegistry::add((new NavbarItem('dashboard'))
|
||||
->url('index')->label('dashboard')
|
||||
->icon('fe-activity')->noMobileSubmenu()->order(100));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('dashboard.live_connections'))
|
||||
->parent('dashboard')->url('live_connections')
|
||||
->label('live_connections')->permissions(['live_connections'])->order(10));
|
||||
}
|
||||
|
||||
// ── Servers ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Register Servers navigation items.
|
||||
*
|
||||
* Adds server management items including load balancer installation,
|
||||
* server/proxy management, ordering, and process monitoring.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _servers(): void {
|
||||
NavbarRegistry::add((new NavbarItem('servers'))
|
||||
->url('#')->label('servers')
|
||||
->icon('fas fa-server')->order(200));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('servers.install'))
|
||||
->parent('servers')->url('server_install')
|
||||
->label('install_load_balancer')->permissions(['servers'])->order(10));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('servers.manage'))
|
||||
->parent('servers')->url('servers')
|
||||
->label('manage_servers')->permissions(['servers'])->order(20));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('servers.proxies'))
|
||||
->parent('servers')->url('proxies')
|
||||
->label('manage_proxies')->permissions(['proxies'])->order(30));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('servers.order'))
|
||||
->parent('servers')->url('server_order')
|
||||
->label('server_order')->permissions(['server_order'])->order(40));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('servers.process_monitor'))
|
||||
->parent('servers')->url('process_monitor')
|
||||
->label('process_monitor')->permissions(['process_monitor'])->order(50));
|
||||
}
|
||||
|
||||
// ── Users ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Register Users navigation items.
|
||||
*
|
||||
* Adds complete user management structure including Lines, MAG devices,
|
||||
* Enigma2 devices, and Reseller management with their respective
|
||||
* add/manage/mass-edit operations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _users(): void {
|
||||
NavbarRegistry::add((new NavbarItem('users'))
|
||||
->url('#')->label('users')
|
||||
->icon('fas fa-desktop')->order(300));
|
||||
|
||||
// Lines
|
||||
NavbarRegistry::add((new NavbarItem('users.lines'))
|
||||
->parent('users')->url('#')
|
||||
->label('user_lines')->permissions(['add_user', 'users'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('users.lines.add'))
|
||||
->parent('users.lines')->url('line')
|
||||
->label('add_users')->permissions(['add_user'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('users.lines.manage'))
|
||||
->parent('users.lines')->url('lines')
|
||||
->label('manage_users')->permissions(['users'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('users.lines.mass'))
|
||||
->parent('users.lines')->url('line_mass')
|
||||
->label('mass_edit_users')->permissions(['mass_edit_lines'])->order(30));
|
||||
|
||||
// MAG
|
||||
NavbarRegistry::add((new NavbarItem('users.mag'))
|
||||
->parent('users')->url('#')
|
||||
->label('mag_devices')->permissions(['add_mag', 'manage_mag'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('users.mag.add'))
|
||||
->parent('users.mag')->url('mag')
|
||||
->label('add_mag')->permissions(['add_mag'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('users.mag.manage'))
|
||||
->parent('users.mag')->url('mags')
|
||||
->label('manage_mag_devices')->permissions(['manage_mag'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('users.mag.mass'))
|
||||
->parent('users.mag')->url('mag_mass')
|
||||
->label('mass_edit_mags')->permissions(['mass_edit_mags'])->order(30));
|
||||
|
||||
// Enigma
|
||||
NavbarRegistry::add((new NavbarItem('users.e2'))
|
||||
->parent('users')->url('#')
|
||||
->label('enigma_devices')->permissions(['add_e2', 'manage_e2'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('users.e2.add'))
|
||||
->parent('users.e2')->url('enigma')
|
||||
->label('add_enigma')->permissions(['add_e2'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('users.e2.manage'))
|
||||
->parent('users.e2')->url('enigmas')
|
||||
->label('manage_enigma_devices')->permissions(['manage_e2'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('users.e2.mass'))
|
||||
->parent('users.e2')->url('enigma_mass')
|
||||
->label('mass_edit_enigmas')->permissions(['mass_edit_enigmas'])->order(30));
|
||||
|
||||
// Reseller
|
||||
NavbarRegistry::add((new NavbarItem('users.reseller'))
|
||||
->parent('users')->url('#')
|
||||
->label('reseller')->permissions(['add_reguser', 'mng_regusers'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('users.reseller.add'))
|
||||
->parent('users.reseller')->url('user')
|
||||
->label('add_registered_user')->permissions(['add_reguser'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('users.reseller.manage'))
|
||||
->parent('users.reseller')->url('users')
|
||||
->label('manage_registered_user')->permissions(['mng_regusers'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('users.reseller.mass'))
|
||||
->parent('users.reseller')->url('user_mass')
|
||||
->label('mass_edit_resellers')->permissions(['mass_edit_users'])->order(30));
|
||||
}
|
||||
|
||||
// ── Content ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Register Content navigation items.
|
||||
*
|
||||
* Adds content management structure including Streams, Created Channels,
|
||||
* Movies, Series, Radio Stations, Recordings, and TV Guide.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _content(): void {
|
||||
NavbarRegistry::add((new NavbarItem('content'))
|
||||
->url('#')->label('content')
|
||||
->icon('fas fa-play')->order(400));
|
||||
|
||||
// Streams
|
||||
NavbarRegistry::add((new NavbarItem('content.streams'))
|
||||
->parent('content')->url('#')
|
||||
->label('streams')->permissions(['add_stream', 'streams'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('content.streams.add'))
|
||||
->parent('content.streams')->url('stream')
|
||||
->label('add_stream')->permissions(['add_stream'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('content.streams.import'))
|
||||
->parent('content.streams')->url('stream?import=1')
|
||||
->label('import_multiple_stream')->permissions(['import_streams'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('content.streams.import_review'))
|
||||
->parent('content.streams')->url('review?type=1')
|
||||
->label('import_review_stream')->permissions(['import_streams'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('content.streams.manage'))
|
||||
->parent('content.streams')->url('streams')
|
||||
->label('manage_streams')->permissions(['streams'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('content.streams.mass'))
|
||||
->parent('content.streams')->url('stream_mass')
|
||||
->label('mass_edit_streams')->permissions(['mass_edit_streams'])->order(50));
|
||||
|
||||
// Created channels
|
||||
NavbarRegistry::add((new NavbarItem('content.channels'))
|
||||
->parent('content')->url('#')
|
||||
->label('created_channels')->permissions(['create_channel', 'streams'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('content.channels.add'))
|
||||
->parent('content.channels')->url('created_channel')
|
||||
->label('create_channel')->permissions(['create_channel'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('content.channels.manage'))
|
||||
->parent('content.channels')->url('created_channels')
|
||||
->label('manage_created_channels')->permissions(['streams'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('content.channels.mass'))
|
||||
->parent('content.channels')->url('created_channel_mass')
|
||||
->label('mass_edit_created_channels')->permissions(['streams'])->order(30));
|
||||
|
||||
// Movies
|
||||
NavbarRegistry::add((new NavbarItem('content.movies'))
|
||||
->parent('content')->url('#')
|
||||
->label('movies')->permissions(['add_movie', 'import_movies', 'movies'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('content.movies.add'))
|
||||
->parent('content.movies')->url('movie')
|
||||
->label('add_movie')->permissions(['add_movie'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('content.movies.import'))
|
||||
->parent('content.movies')->url('movie?import=1')
|
||||
->label('import_multiple_movies')->permissions(['import_movies'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('content.movies.import_review'))
|
||||
->parent('content.movies')->url('review?type=2')
|
||||
->label('import_review_movies')->permissions(['import_movies'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('content.movies.manage'))
|
||||
->parent('content.movies')->url('movies')
|
||||
->label('manage_movies')->permissions(['movies'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('content.movies.mass'))
|
||||
->parent('content.movies')->url('movie_mass')
|
||||
->label('mass_edit_movies')->permissions(['mass_sedits_vod'])->order(50));
|
||||
|
||||
// Series
|
||||
NavbarRegistry::add((new NavbarItem('content.series'))
|
||||
->parent('content')->url('#')
|
||||
->label('series')->permissions(['add_series', 'series', 'episodes'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('content.series.add'))
|
||||
->parent('content.series')->url('serie')
|
||||
->label('add_series')->permissions(['add_series'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('content.series.manage'))
|
||||
->parent('content.series')->url('series')
|
||||
->label('manage_series')->permissions(['series'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('content.series.episodes'))
|
||||
->parent('content.series')->url('episodes')
|
||||
->label('manage_episodes')->permissions(['episodes'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('content.series.mass'))
|
||||
->parent('content.series')->url('series_mass')
|
||||
->label('', 'Mass Edit Series')->permissions(['mass_sedits'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('content.series.episodes_mass'))
|
||||
->parent('content.series')->url('episodes_mass')
|
||||
->label('', 'Mass Edit Episodes')->permissions(['mass_sedits'])->order(50));
|
||||
|
||||
// Radio stations
|
||||
NavbarRegistry::add((new NavbarItem('content.stations'))
|
||||
->parent('content')->url('#')
|
||||
->label('stations')->permissions(['add_radio', 'radio'])->order(50));
|
||||
NavbarRegistry::add((new NavbarItem('content.stations.add'))
|
||||
->parent('content.stations')->url('radio')
|
||||
->label('add_station')->permissions(['add_radio'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('content.stations.manage'))
|
||||
->parent('content.stations')->url('radios')
|
||||
->label('manage_stations')->permissions(['radio'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('content.stations.mass'))
|
||||
->parent('content.stations')->url('radio_mass')
|
||||
->label('mass_edit_stations')->permissions(['mass_edit_radio'])->order(30));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('content.recordings'))
|
||||
->parent('content')->url('archive')
|
||||
->label('recordings')->permissions(['movies'])->order(60));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('content.tv_guide'))
|
||||
->parent('content')->url('epg_view')
|
||||
->label('tv_guide')->permissions(['streams'])
|
||||
->desktopOnly()->order(70));
|
||||
}
|
||||
|
||||
// ── Bouquets ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Register Bouquets navigation items.
|
||||
*
|
||||
* Adds bouquet management items for adding, managing, and ordering channel bouquets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _bouquets(): void {
|
||||
NavbarRegistry::add((new NavbarItem('bouquets'))
|
||||
->url('#')->label('bouquets')
|
||||
->icon('fas fa-spa')->order(500));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('bouquets.add'))
|
||||
->parent('bouquets')->url('bouquet')
|
||||
->label('add_bouquet')->permissions(['add_bouquet'])->order(10));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('bouquets.manage'))
|
||||
->parent('bouquets')->url('bouquets')
|
||||
->label('manage_bouquets')->permissions(['bouquets'])->order(20));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('bouquets.order'))
|
||||
->parent('bouquets')->url('bouquet_order')
|
||||
->label('bouquet_order')->permissions(['bouquet_order'])
|
||||
->desktopOnly()->order(30));
|
||||
}
|
||||
|
||||
// ── Management ────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Register Management navigation items.
|
||||
*
|
||||
* Adds system management structure including Service Setup, Access Codes,
|
||||
* Security, Tools, Logs (with megamenu), and Tickets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _management(): void {
|
||||
NavbarRegistry::add((new NavbarItem('management'))
|
||||
->url('#')->label('management')
|
||||
->icon('fas fa-wrench')->order(600));
|
||||
|
||||
// Service setup
|
||||
NavbarRegistry::add((new NavbarItem('management.service_setup'))
|
||||
->parent('management')->url('#')
|
||||
->label('service_setup')->permissions(['mng_packages', 'categories', 'mng_groups', 'epg', 'tprofiles', 'folder_watch'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('management.service_setup.packages'))
|
||||
->parent('management.service_setup')->url('packages')
|
||||
->label('packages')->permissions(['mng_packages'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('management.service_setup.categories'))
|
||||
->parent('management.service_setup')->url('stream_categories')
|
||||
->label('categories')->permissions(['categories'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('management.service_setup.groups'))
|
||||
->parent('management.service_setup')->url('groups')
|
||||
->label('groups')->permissions(['mng_groups'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('management.service_setup.epg'))
|
||||
->parent('management.service_setup')->url('epgs')
|
||||
->label('epgs')->permissions(['epg'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('management.service_setup.profiles'))
|
||||
->parent('management.service_setup')->url('profiles')
|
||||
->label('transcode_profiles')->permissions(['tprofiles'])->order(50));
|
||||
// Modules inject at order 60+
|
||||
|
||||
// Access codes
|
||||
NavbarRegistry::add((new NavbarItem('management.access_codes'))
|
||||
->parent('management')->url('#')
|
||||
->label('', 'Access Codes')->permissions(['add_code'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('management.access_codes.add'))
|
||||
->parent('management.access_codes')->url('code')
|
||||
->label('add_access_codes')->permissions(['add_code'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('management.access_codes.manage'))
|
||||
->parent('management.access_codes')->url('codes')
|
||||
->label('menage_access_codes')->permissions(['add_code'])->order(20));
|
||||
|
||||
// Security
|
||||
NavbarRegistry::add((new NavbarItem('management.security'))
|
||||
->parent('management')->url('#')
|
||||
->label('', 'Security')->permissions(['block_asns', 'block_ips', 'block_isps', 'block_uas', 'add_hmac', 'rtmp'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('management.security.asns'))
|
||||
->parent('management.security')->url('asns')
|
||||
->label('blocked_asns')->permissions(['block_asns'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('management.security.ips'))
|
||||
->parent('management.security')->url('ips')
|
||||
->label('blocked_ips')->permissions(['block_ips'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('management.security.isps'))
|
||||
->parent('management.security')->url('isps')
|
||||
->label('blocked_isps')->permissions(['block_isps'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('management.security.uas'))
|
||||
->parent('management.security')->url('useragents')
|
||||
->label('blocked_uas')->permissions(['block_uas'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('management.security.hmac'))
|
||||
->parent('management.security')->url('hmacs')
|
||||
->label('hmac_keys')->permissions(['add_hmac'])->order(50));
|
||||
NavbarRegistry::add((new NavbarItem('management.security.rtmp'))
|
||||
->parent('management.security')->url('rtmp_ips')
|
||||
->label('rtmp_ips')->permissions(['rtmp'])->order(60));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('management.tools'))
|
||||
->parent('management')->url('#')
|
||||
->label('tools')->permissions(['channel_order', 'fingerprint', 'mass_delete', 'quick_tools', 'rtmp', 'stream_tools'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('management.tools.channel_order'))
|
||||
->parent('management.tools')->url('channel_order')
|
||||
->label('channel_order')->permissions(['channel_order'])->desktopOnly()->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('management.tools.fingerprint'))
|
||||
->parent('management.tools')->url('fingerprint')
|
||||
->label('fingerprint')->permissions(['fingerprint'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('management.tools.mass_delete'))
|
||||
->parent('management.tools')->url('mass_delete')
|
||||
->label('mass_delete')->permissions(['mass_delete'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('management.tools.quick_tools'))
|
||||
->parent('management.tools')->url('quick_tools')
|
||||
->label('quick_tools')->permissions(['quick_tools'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('management.tools.rtmp_monitor'))
|
||||
->parent('management.tools')->url('rtmp_monitor')
|
||||
->label('', 'RTMP Monitor')->permissions(['rtmp'])->order(50));
|
||||
NavbarRegistry::add((new NavbarItem('management.tools.stream_tools'))
|
||||
->parent('management.tools')->url('stream_tools')
|
||||
->label('stream_tools')->permissions(['stream_tools'])->order(60));
|
||||
|
||||
// Logs (megamenu)
|
||||
NavbarRegistry::add((new NavbarItem('management.logs'))
|
||||
->parent('management')->url('#')
|
||||
->label('logs')->permissions(['movies', 'streams', 'connection_logs', 'client_request_log', 'login_logs', 'panel_logs', 'credits_log', 'live_connections', 'manage_events', 'reg_userlog', 'stream_errors'])->submenuClass('megamenu')->order(50));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.activity'))
|
||||
->parent('management.logs')->url('line_activity')
|
||||
->label('activity_logs')->permissions(['connection_logs'])->order(10));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.client'))
|
||||
->parent('management.logs')->url('client_logs')
|
||||
->label('client_logs')->permissions(['client_request_log'])->order(20));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.credit'))
|
||||
->parent('management.logs')->url('credit_logs')
|
||||
->label('credit_logs')->permissions(['credits_log'])->order(30));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.queue'))
|
||||
->parent('management.logs')->url('queue')
|
||||
->label('', 'Encoding Queue')->permissions(['streams', 'episodes', 'series'])->order(40));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.line_ips'))
|
||||
->parent('management.logs')->url('line_ips')
|
||||
->label('ips_per_line')->permissions(['connection_logs'])->order(50));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.live_connections'))
|
||||
->parent('management.logs')->url('live_connections')
|
||||
->label('live_connections')->permissions(['live_connections'])->order(60));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.login'))
|
||||
->parent('management.logs')->url('login_logs')
|
||||
->label('', 'Login Logs')->permissions(['login_logs'])->order(70));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.mag_events'))
|
||||
->parent('management.logs')->url('mag_events')
|
||||
->label('mag_event_logs')->permissions(['manage_events'])->order(80));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.ondemand'))
|
||||
->parent('management.logs')->url('ondemand')
|
||||
->label('', 'On-Demand Scanner')->permissions(['streams'])->order(90));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.panel_logs'))
|
||||
->parent('management.logs')->url('panel_logs')
|
||||
->label('', 'Panel Errors')->permissions(['panel_logs'])->order(100));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.user_logs'))
|
||||
->parent('management.logs')->url('user_logs')
|
||||
->label('reseller_logs')->permissions(['reg_userlog'])->order(110));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.restream'))
|
||||
->parent('management.logs')->url('restream_logs')
|
||||
->label('', 'Restream Detection')->permissions(['restream_logs'])->order(120));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.stream_errors'))
|
||||
->parent('management.logs')->url('stream_errors')
|
||||
->label('stream_errors')->permissions(['stream_errors'])->order(130));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.stream_rank'))
|
||||
->parent('management.logs')->url('stream_rank')
|
||||
->label('', 'Stream Rank')->permissions(['streams'])->order(140));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.system'))
|
||||
->parent('management.logs')->url('mysql_syslog')
|
||||
->label('', 'System Logs')->permissions(['panel_logs'])->order(150));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.vod_theft'))
|
||||
->parent('management.logs')->url('theft_detection')
|
||||
->label('', 'VOD Theft Detection')->permissions(['movies'])->order(160));
|
||||
// Modules inject at order 170+
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('management.tickets'))
|
||||
->parent('management')->url('tickets')
|
||||
->label('tickets')->permissions(['manage_tickets'])
|
||||
->settingDisabled('show_tickets')->order(60));
|
||||
}
|
||||
|
||||
// ── Suppliers ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Register Suppliers navigation items.
|
||||
*
|
||||
* Adds stream provider/supplier management items.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function _suppliers(): void {
|
||||
NavbarRegistry::add((new NavbarItem('suppliers'))
|
||||
->url('#')->label('supplirs')
|
||||
->icon('fas fa-users')->order(700));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('suppliers.add'))
|
||||
->parent('suppliers')->url('provider')
|
||||
->label('add_providers')->permissions(['streams'])->order(10));
|
||||
|
||||
NavbarRegistry::add((new NavbarItem('suppliers.manage'))
|
||||
->parent('suppliers')->url('providers')
|
||||
->label('stream_providers')->permissions(['streams'])->order(20));
|
||||
}
|
||||
}
|
||||
@@ -110,4 +110,11 @@ interface ModuleInterface {
|
||||
* Вызывается при отключении/удалении модуля.
|
||||
*/
|
||||
public function uninstall(): void;
|
||||
|
||||
/**
|
||||
* Зарегистрировать пункты навигации через NavbarRegistry::add()
|
||||
*
|
||||
* Вызывается в bootAll() после boot() всех модулей.
|
||||
*/
|
||||
public function registerNavbar(): void;
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ class ModuleLoader {
|
||||
* @param Router|null $router HTTP-роутер (null для CLI)
|
||||
*/
|
||||
public function bootAll(ServiceContainer $container, Router $router = null) {
|
||||
CoreNavbarProvider::register();
|
||||
|
||||
foreach ($this->modules as $name => $module) {
|
||||
$module->boot($container);
|
||||
|
||||
@@ -143,6 +145,8 @@ class ModuleLoader {
|
||||
$module->registerRoutes($router);
|
||||
}
|
||||
|
||||
$module->registerNavbar();
|
||||
|
||||
$subscribers = $module->getEventSubscribers();
|
||||
if (!empty($subscribers) && $container->has('events')) {
|
||||
foreach ($subscribers as $event => $handler) {
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* NavbarItem — value object representing a single node in the admin nav tree.
|
||||
*
|
||||
* Fluent builder pattern:
|
||||
* (new NavbarItem('management.service_setup.watch'))
|
||||
* ->parent('management.service_setup')
|
||||
* ->url('watch')
|
||||
* ->label('folder_watch')
|
||||
* ->permissions(['folder_watch'])
|
||||
* ->order(60)
|
||||
*
|
||||
* Top-level items have parent === null.
|
||||
* Items with url === '#' are group headers (rendered as submenus).
|
||||
*
|
||||
* @package XC_VM_Core_Module
|
||||
* @author Divarion_D <https://github.com/Divarion-D>
|
||||
* @copyright 2025-2026 Vateron Media
|
||||
* @link https://github.com/Vateron-Media/XC_VM
|
||||
* @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*/
|
||||
|
||||
class NavbarItem {
|
||||
|
||||
/** @var string Unique dot-separated key, e.g. 'management.service_setup.watch' */
|
||||
public $key;
|
||||
/** @var string|null Parent key, null for top-level items */
|
||||
public $parent = null;
|
||||
/** @var string Target URL or '#' for group headers */
|
||||
public $url = '#';
|
||||
/** @var string Translation key passed to $language::get() */
|
||||
public $translationKey = '';
|
||||
/** @var string Fallback text used when translationKey is empty */
|
||||
public $fallbackTitle = '';
|
||||
/** @var string[] Permission names; OR-checked; empty means no permission gate */
|
||||
public $permissions = [];
|
||||
/** @var int Sort order within the same parent level */
|
||||
public $order = 100;
|
||||
/** @var string CSS class(es) for the icon element, e.g. 'fas fa-server' */
|
||||
public $icon = '';
|
||||
/** @var bool When true, item is hidden on mobile */
|
||||
public $desktopOnly = false;
|
||||
/** @var bool When true, submenu is not rendered on mobile */
|
||||
public $noMobileSubmenu = false;
|
||||
/** @var string Extra CSS class on the <ul class="submenu"> element, e.g. 'megamenu' */
|
||||
public $submenuClass = '';
|
||||
/** @var string Settings key; when non-empty and settings[key] is truthy, item is hidden */
|
||||
public $settingDisabled = '';
|
||||
/** @var bool When true, renders as a visual divider (no link) */
|
||||
public $divider = false;
|
||||
|
||||
/**
|
||||
* Create a new navbar item with the given unique key.
|
||||
*
|
||||
* @param string $key Unique dot-separated identifier, e.g. 'management.service_setup.watch'
|
||||
*/
|
||||
public function __construct(string $key) {
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent item key for hierarchical navigation.
|
||||
*
|
||||
* @param string $parentKey Dot-separated key of the parent item
|
||||
* @return self
|
||||
*/
|
||||
public function parent(string $parentKey): self {
|
||||
$this->parent = $parentKey;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target URL for this navigation item.
|
||||
*
|
||||
* Use '#' for group headers that expand to show child items.
|
||||
*
|
||||
* @param string $url Target URL or '#'
|
||||
* @return self
|
||||
*/
|
||||
public function url(string $url): self {
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the display label for this navigation item.
|
||||
*
|
||||
* The label is resolved via $language::get() using the translation key.
|
||||
* If translation key is empty, the fallback text is used directly.
|
||||
*
|
||||
* @param string $translationKey Translation key for $language::get()
|
||||
* @param string $fallback Literal fallback text when translationKey is empty
|
||||
* @return self
|
||||
*/
|
||||
public function label(string $translationKey, string $fallback = ''): self {
|
||||
$this->translationKey = $translationKey;
|
||||
$this->fallbackTitle = $fallback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set required permissions for this navigation item.
|
||||
*
|
||||
* User must have at least one of the listed permissions to see this item.
|
||||
* Empty array means no permission check.
|
||||
*
|
||||
* @param string[] $permissions List of permission names
|
||||
* @return self
|
||||
*/
|
||||
public function permissions(array $permissions): self {
|
||||
$this->permissions = $permissions;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sort order within the same parent level.
|
||||
*
|
||||
* Lower values appear first. Default is 100.
|
||||
*
|
||||
* @param int $order Sort order
|
||||
* @return self
|
||||
*/
|
||||
public function order(int $order): self {
|
||||
$this->order = $order;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CSS icon class(es) for this navigation item.
|
||||
*
|
||||
* @param string $icon CSS classes, e.g. 'fas fa-server' or 'fe-activity'
|
||||
* @return self
|
||||
*/
|
||||
public function icon(string $icon): self {
|
||||
$this->icon = $icon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this item as desktop-only.
|
||||
*
|
||||
* When true, the item is hidden on mobile devices.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function desktopOnly(): self {
|
||||
$this->desktopOnly = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the submenu on mobile devices.
|
||||
*
|
||||
* When true, child items are not rendered in mobile view.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function noMobileSubmenu(): self {
|
||||
$this->noMobileSubmenu = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an extra CSS class to the submenu <ul> element.
|
||||
*
|
||||
* Useful for mega menu layouts or custom styling.
|
||||
*
|
||||
* @param string $cls CSS class name, e.g. 'megamenu'
|
||||
* @return self
|
||||
*/
|
||||
public function submenuClass(string $cls): self {
|
||||
$this->submenuClass = $cls;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally hide this item based on a system setting.
|
||||
*
|
||||
* If the setting with the given key exists and evaluates to true,
|
||||
* this navigation item will not be displayed.
|
||||
*
|
||||
* @param string $key Setting key name
|
||||
* @return self
|
||||
*/
|
||||
public function settingDisabled(string $key): self {
|
||||
$this->settingDisabled = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this item as a visual divider.
|
||||
*
|
||||
* Dividers are non-clickable elements that separate groups of items.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function makeDivider(): self {
|
||||
$this->divider = true;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* NavbarRegistry — tree-based registry for admin navigation.
|
||||
*
|
||||
* All items (core and module) are registered via add().
|
||||
* Core items are registered by CoreNavbarProvider::register() before any
|
||||
* module's registerNavbar() is called.
|
||||
*
|
||||
* @package XC_VM_Core_Module
|
||||
* @author Divarion_D <https://github.com/Divarion-D>
|
||||
* @copyright 2025-2026 Vateron Media
|
||||
* @link https://github.com/Vateron-Media/XC_VM
|
||||
* @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*/
|
||||
|
||||
class NavbarRegistry {
|
||||
|
||||
/** @var NavbarItem[] Key → NavbarItem map */
|
||||
private static $items = [];
|
||||
|
||||
/**
|
||||
* Register a single navigation item in the registry.
|
||||
*
|
||||
* Items are stored by their unique dot-separated key.
|
||||
* Duplicate keys will overwrite previously registered items.
|
||||
*
|
||||
* @param NavbarItem $item The navigation item to register
|
||||
* @return void
|
||||
*/
|
||||
public static function add(NavbarItem $item): void {
|
||||
self::$items[$item->key] = $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all top-level navigation items.
|
||||
*
|
||||
* Top-level items are those with parent === null.
|
||||
* Items are sorted by their order property (ascending).
|
||||
*
|
||||
* @return NavbarItem[] Array of top-level items sorted by order
|
||||
*/
|
||||
public static function getTopLevel(): array {
|
||||
return self::_sorted(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns direct children of the given parent key.
|
||||
*
|
||||
* Only immediate children are returned (not descendants).
|
||||
* Items are sorted by their order property (ascending).
|
||||
*
|
||||
* @param string $parentKey The dot-separated key of the parent item
|
||||
* @return NavbarItem[] Array of child items sorted by order
|
||||
*/
|
||||
public static function getChildren(string $parentKey): array {
|
||||
return self::_sorted($parentKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a navigation item has any child items.
|
||||
*
|
||||
* @param string $key The dot-separated key of the item to check
|
||||
* @return bool True if the item has at least one child, false otherwise
|
||||
*/
|
||||
public static function hasChildren(string $key): bool {
|
||||
foreach (self::$items as $item) {
|
||||
if ($item->parent === $key) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the registry by clearing all registered items.
|
||||
*
|
||||
* Primarily useful for unit testing to ensure a clean state
|
||||
* between test cases.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function reset(): void {
|
||||
self::$items = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all navigation items belonging to a specific parent.
|
||||
*
|
||||
* Internal helper method that filters and sorts items by their
|
||||
* order property. Returns empty array if no items match.
|
||||
*
|
||||
* @param string|null $parent Parent key (null for top-level items)
|
||||
* @return NavbarItem[] Array of matching items sorted by order
|
||||
*/
|
||||
private static function _sorted(?string $parent): array {
|
||||
$result = [];
|
||||
foreach (self::$items as $item) {
|
||||
if ($item->parent === $parent) {
|
||||
$result[] = $item;
|
||||
}
|
||||
}
|
||||
usort($result, function ($a, $b) {
|
||||
return $a->order - $b->order;
|
||||
});
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -99,4 +99,10 @@ class FingerprintModule implements ModuleInterface {
|
||||
*/
|
||||
public function uninstall(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerNavbar(): void {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,4 +97,10 @@ class MagscanModule implements ModuleInterface {
|
||||
*/
|
||||
public function uninstall(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerNavbar(): void {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,4 +105,10 @@ class MinistraModule implements ModuleInterface {
|
||||
*/
|
||||
public function uninstall(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerNavbar(): void {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,4 +155,16 @@ class PlexModule implements ModuleInterface {
|
||||
*/
|
||||
public function uninstall(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerNavbar(): void {
|
||||
NavbarRegistry::add((new NavbarItem('topbar.settings.plex_settings'))
|
||||
->parent('topbar.settings')->url('settings_plex')
|
||||
->label('plex_settings')->permissions(['folder_watch_settings'])->order(55));
|
||||
NavbarRegistry::add((new NavbarItem('management.service_setup.plex'))
|
||||
->parent('management.service_setup')->url('plex')
|
||||
->label('', 'Plex Sync')->permissions(['folder_watch'])->order(70));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,4 +96,10 @@ class TheftDetectionModule implements ModuleInterface {
|
||||
*/
|
||||
public function uninstall(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerNavbar(): void {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,4 +104,10 @@ class TmdbModule implements ModuleInterface {
|
||||
*/
|
||||
public function uninstall(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerNavbar(): void {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,4 +159,21 @@ class WatchModule implements ModuleInterface {
|
||||
*/
|
||||
public function uninstall(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerNavbar(): void {
|
||||
NavbarRegistry::add((new NavbarItem('topbar.settings.divider_modules'))
|
||||
->parent('topbar.settings')->makeDivider()->order(45));
|
||||
NavbarRegistry::add((new NavbarItem('topbar.settings.watch_settings'))
|
||||
->parent('topbar.settings')->url('settings_watch')
|
||||
->label('watch_settings')->permissions(['folder_watch_settings'])->order(50));
|
||||
NavbarRegistry::add((new NavbarItem('management.service_setup.watch'))
|
||||
->parent('management.service_setup')->url('watch')
|
||||
->label('folder_watch')->permissions(['folder_watch'])->order(60));
|
||||
NavbarRegistry::add((new NavbarItem('management.logs.watch_output'))
|
||||
->parent('management.logs')->url('watch_output')
|
||||
->label('watch_folder_logs')->permissions(['folder_watch'])->order(170));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,394 +275,106 @@
|
||||
<div class="topbar-menu">
|
||||
<div class="container-fluid">
|
||||
<div id="navigation">
|
||||
<?php
|
||||
/**
|
||||
* Nav rendering helpers.
|
||||
* All nav items are registered in CoreNavbarProvider (and modules'
|
||||
* registerNavbar()) via NavbarRegistry::add(NavbarItem).
|
||||
*
|
||||
* To inject a button anywhere, add a NavbarItem with the desired
|
||||
* parent key and order, e.g.:
|
||||
* NavbarRegistry::add((new NavbarItem('management.service_setup.watch'))
|
||||
* ->parent('management.service_setup')
|
||||
* ->url('watch')->label('folder_watch')
|
||||
* ->permissions(['folder_watch'])->order(60));
|
||||
*/
|
||||
if (!function_exists('_xc_nav_visible')) {
|
||||
function _xc_nav_visible(NavbarItem $item, bool $mobile, array $settings): bool {
|
||||
if ($item->desktopOnly && $mobile) return false;
|
||||
if ($item->settingDisabled !== '' && !empty($settings[$item->settingDisabled])) return false;
|
||||
if ($item->divider) return true;
|
||||
if (!empty($item->permissions)) {
|
||||
foreach ($item->permissions as $_p) {
|
||||
if (Authorization::check('adv', $_p)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ($item->url === '#') {
|
||||
foreach (NavbarRegistry::getChildren($item->key) as $_child) {
|
||||
if (_xc_nav_visible($_child, $mobile, $settings)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('_xc_nav_label')) {
|
||||
function _xc_nav_label(NavbarItem $item, string $language): string {
|
||||
return $item->translationKey
|
||||
? $language::get($item->translationKey)
|
||||
: htmlspecialchars($item->fallbackTitle, ENT_QUOTES);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('_xc_nav_children')) {
|
||||
function _xc_nav_children(NavbarItem $parent, bool $mobile, array $settings, string $language): void {
|
||||
$visible = [];
|
||||
foreach (NavbarRegistry::getChildren($parent->key) as $_c) {
|
||||
if (_xc_nav_visible($_c, $mobile, $settings)) $visible[] = $_c;
|
||||
}
|
||||
if (empty($visible)) return;
|
||||
|
||||
$cls = 'submenu' . ($parent->submenuClass !== '' ? ' ' . $parent->submenuClass : '');
|
||||
echo '<ul class="' . $cls . '">';
|
||||
|
||||
if ($parent->submenuClass === 'megamenu') {
|
||||
$total = count($visible);
|
||||
$split = $total > 8 ? (int)ceil($total / 2) : null;
|
||||
echo '<li><ul>';
|
||||
foreach ($visible as $_i => $_c) {
|
||||
if ($_c->divider) continue;
|
||||
if ($split !== null && $_i === $split) echo '</ul></li><li><ul>';
|
||||
echo '<li><a href="' . htmlspecialchars($_c->url, ENT_QUOTES) . '">'
|
||||
. _xc_nav_label($_c, $language) . '</a></li>';
|
||||
}
|
||||
echo '</ul></li>';
|
||||
} else {
|
||||
foreach ($visible as $_c) {
|
||||
if ($_c->divider) {
|
||||
echo '<div class="dropdown-divider"></div>';
|
||||
continue;
|
||||
}
|
||||
$_hasKids = NavbarRegistry::hasChildren($_c->key) && !($_c->noMobileSubmenu && $mobile);
|
||||
echo '<li' . ($_hasKids ? ' class="has-submenu"' : '') . '>';
|
||||
echo '<a href="' . htmlspecialchars($_c->url, ENT_QUOTES) . '">'
|
||||
. _xc_nav_label($_c, $language);
|
||||
if ($_hasKids) echo ' <div class="arrow-down"></div>';
|
||||
echo '</a>';
|
||||
if ($_hasKids) _xc_nav_children($_c, $mobile, $settings, $language);
|
||||
echo '</li>';
|
||||
}
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<ul class="navigation-menu">
|
||||
<li class="has-submenu">
|
||||
<a href="index"><i class="fe-activity"></i><?= $language::get('dashboard'); ?>
|
||||
<?php if (!$rMobile): ?>
|
||||
<div class="arrow-down"></div>
|
||||
</a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'live_connections')): ?>
|
||||
<li><a href="live_connections"><?= $language::get('live_connections'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php if (Authorization::check('adv', 'servers') || Authorization::check('adv', 'process_monitor')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><i class="fas fa-server"></i><?= $language::get('servers'); ?> <div class="arrow-down">
|
||||
</div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'servers')): ?>
|
||||
<li><a href="server_install"><?= $language::get('install_load_balancer'); ?></a></li>
|
||||
<li><a href="servers"><?= $language::get('manage_servers'); ?></a></li>
|
||||
<li><a href="proxies"><?= $language::get('manage_proxies'); ?></a></li>
|
||||
<li><a href="server_order"><?= $language::get('server_order'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'process_monitor')): ?>
|
||||
<li><a href="process_monitor"><?= $language::get('process_monitor'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_user') || Authorization::check('adv', 'users') || Authorization::check('adv', 'add_mag') || Authorization::check('adv', 'manage_mag') || Authorization::check('adv', 'add_e2') || Authorization::check('adv', 'manage_e2')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"> <i class="fas fa-desktop"></i><?= $language::get('users'); ?> <div class="arrow-down">
|
||||
</div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_user') || Authorization::check('adv', 'users')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('user_lines'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_user')): ?>
|
||||
<li><a href="line"><?= $language::get('add_users'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'users')): ?>
|
||||
<li><a href="lines"><?= $language::get('manage_users'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_edit_lines')): ?>
|
||||
<li><a href="line_mass"><?= $language::get('mass_edit_users'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_mag') || Authorization::check('adv', 'manage_mag')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('mag_devices'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_mag')): ?>
|
||||
<li><a href="mag"><?= $language::get('add_mag'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'manage_mag')): ?>
|
||||
<li><a href="mags"><?= $language::get('manage_mag_devices'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_edit_mags')): ?>
|
||||
<li><a href="mag_mass">Mass Edit Mags</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_e2') || Authorization::check('adv', 'manage_e2')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('enigma_devices'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_e2')): ?>
|
||||
<li><a href="enigma"><?= $language::get('add_enigma'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'manage_e2')): ?>
|
||||
<li><a href="enigmas"><?= $language::get('manage_enigma_devices'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_edit_enigmas')): ?>
|
||||
<li><a href="enigma_mass">Mass Edit Enigmas</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_reguser') || Authorization::check('adv', 'mng_regusers')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('reseller'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_reguser')): ?>
|
||||
<li><a href="user"><?= $language::get('add_registered_user'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mng_regusers')): ?>
|
||||
<li><a href="users"><?= $language::get('manage_registered_user'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_edit_users')): ?>
|
||||
<li><a href="user_mass"><?= $language::get('mass_edit_resellers'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_stream') || Authorization::check('adv', 'create_channel') || Authorization::check('adv', 'import_streams') || Authorization::check('adv', 'streams') || Authorization::check('adv', 'add_movie') || Authorization::check('adv', 'import_movies') || Authorization::check('adv', 'movies') || Authorization::check('adv', 'series') || Authorization::check('adv', 'episodes') || Authorization::check('adv', 'add_series') || Authorization::check('adv', 'radio') || Authorization::check('adv', 'add_radio')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"> <i class="fas fa-play"></i><?= $language::get('content'); ?> <div class="arrow-down"></div>
|
||||
<?php foreach (NavbarRegistry::getTopLevel() as $_navTop): ?>
|
||||
<?php
|
||||
if (!_xc_nav_visible($_navTop, $rMobile, $rSettings)) continue;
|
||||
$_topKids = NavbarRegistry::hasChildren($_navTop->key) && !($_navTop->noMobileSubmenu && $rMobile);
|
||||
?>
|
||||
<li<?= $_topKids ? ' class="has-submenu"' : ''; ?>>
|
||||
<a href="<?= htmlspecialchars($_navTop->url, ENT_QUOTES); ?>">
|
||||
<?php if ($_navTop->icon): ?><i class="<?= htmlspecialchars($_navTop->icon, ENT_QUOTES); ?>"></i><?php endif; ?>
|
||||
<?= _xc_nav_label($_navTop, $language); ?>
|
||||
<?php if ($_topKids && !$rMobile): ?><div class="arrow-down"></div><?php endif; ?>
|
||||
</a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_stream') || Authorization::check('adv', 'import_streams') || Authorization::check('adv', 'streams')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('streams'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_stream')): ?><li><a
|
||||
href="stream"><?= $language::get('add_stream'); ?></a></li><?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'import_streams') && !$rMobile): ?>
|
||||
<li><a href="stream?import=1"><?= $language::get('import_multiple_stream'); ?></a></li>
|
||||
<li><a href="review?type=1"><?= $language::get('import_review_stream'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'streams')): ?><li><a
|
||||
href="streams"><?= $language::get('manage_streams'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'streams')): ?><li><a
|
||||
href="stream_mass"><?= $language::get('mass_edit_streams'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (Authorization::check('adv', 'create_channel') || Authorization::check('adv', 'streams')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('created_channels'); ?> <div class="arrow-down"></div>
|
||||
</a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'create_channel')): ?><li><a
|
||||
href="created_channel"><?= $language::get('create_channel'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'streams')): ?><li><a
|
||||
href="created_channels"><?= $language::get('manage_created_channels'); ?></a>
|
||||
</li><?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'streams')): ?><li><a
|
||||
href="created_channel_mass"><?= $language::get('mass_edit_created_channels'); ?></a>
|
||||
</li><?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (Authorization::check('adv', 'add_movie') || Authorization::check('adv', 'import_movies') || Authorization::check('adv', 'movies')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('movies'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_movie')): ?><li><a
|
||||
href="movie"><?= $language::get('add_movie'); ?></a></li><?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'import_movies') && !$rMobile): ?>
|
||||
<li><a href="movie?import=1"><?= $language::get('import_multiple_movies'); ?></a></li>
|
||||
<li><a href="review?type=2"><?= $language::get('import_review_movies'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'movies')): ?><li><a
|
||||
href="movies"><?= $language::get('manage_movies'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_sedits_vod')): ?>
|
||||
<li><a href="movie_mass"><?= $language::get('mass_edit_movies'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (Authorization::check('adv', 'add_series') || Authorization::check('adv', 'series') || Authorization::check('adv', 'episodes')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('series'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_series')): ?><li><a
|
||||
href="serie"><?= $language::get('add_series'); ?></a></li><?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'series')): ?><li><a
|
||||
href="series"><?= $language::get('manage_series'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'episodes')): ?><li><a
|
||||
href="episodes"><?= $language::get('manage_episodes'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_sedits')): ?>
|
||||
<li><a href="series_mass">Mass Edit Series</a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_sedits')): ?>
|
||||
<li><a href="episodes_mass">Mass Edit Episodes</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (Authorization::check('adv', 'add_radio') || Authorization::check('adv', 'radio')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('stations'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_radio')): ?><li><a
|
||||
href="radio"><?= $language::get('add_station'); ?></a></li><?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'radio')): ?><li><a
|
||||
href="radios"><?= $language::get('manage_stations'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_edit_radio')): ?>
|
||||
<li><a href="radio_mass"><?= $language::get('mass_edit_stations'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (Authorization::check('adv', 'movies')): ?><li><a href="archive"><?= $language::get('recordings'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (!$rMobile && Authorization::check('adv', 'streams')): ?><li><a href="epg_view"><?= $language::get('tv_guide'); ?></a></li><?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_bouquet') || Authorization::check('adv', 'bouquets') || Authorization::check('adv', 'edit_bouquet')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"> <i class="fas fa-spa"></i><?= $language::get('bouquets'); ?> <div class="arrow-down"></div>
|
||||
</a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_bouquet')): ?>
|
||||
<li><a href="bouquet"><?= $language::get('add_bouquet'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'bouquets')): ?>
|
||||
<li><a href="bouquets"><?= $language::get('manage_bouquets'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'edit_bouquet') && !$rMobile): ?>
|
||||
<li><a href="bouquet_order"><?= $language::get('order_bouquets'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'streams') || Authorization::check('adv', 'episodes') || Authorization::check('adv', 'series') || Authorization::check('adv', 'categories') || Authorization::check('adv', 'epg') || Authorization::check('adv', 'mng_groups') || Authorization::check('adv', 'mng_packages') || Authorization::check('adv', 'tprofiles') || Authorization::check('adv', 'folder_watch') || Authorization::check('adv', 'add_code') || Authorization::check('adv', 'block_asns') || Authorization::check('adv', 'block_ips') || Authorization::check('adv', 'block_isps') || Authorization::check('adv', 'block_uas') || Authorization::check('adv', 'rtmp') || Authorization::check('adv', 'channel_order') || Authorization::check('adv', 'fingerprint') || Authorization::check('adv', 'mass_delete') || Authorization::check('adv', 'stream_tools') || Authorization::check('adv', 'mass_edit_enigmas') || Authorization::check('adv', 'mass_edit_lines') || Authorization::check('adv', 'mass_edit_mags') || Authorization::check('adv', 'mass_sedits_vod') || Authorization::check('adv', 'mass_sedits') || Authorization::check('adv', 'mass_edit_radio') || Authorization::check('adv', 'mass_edit_streams') || Authorization::check('adv', 'mass_edit_users') || Authorization::check('adv', 'connection_logs') || Authorization::check('adv', 'client_request_log') || Authorization::check('adv', 'login_logs') || Authorization::check('adv', 'panel_logs') || Authorization::check('adv', 'credits_log') || Authorization::check('adv', 'live_connections') || Authorization::check('adv', 'manage_events') || Authorization::check('adv', 'reg_userlog') || Authorization::check('adv', 'stream_errors') || Authorization::check('adv', 'folder_watch') || Authorization::check('adv', 'add_hmac') || Authorization::check('adv', 'quick_tools') || Authorization::check('adv', 'manage_tickets')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"> <i class="fas fa-wrench"></i><?= $language::get('management'); ?> <div class="arrow-down">
|
||||
</div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'categories') || Authorization::check('adv', 'epg') || Authorization::check('adv', 'mng_groups') || Authorization::check('adv', 'mng_packages') || Authorization::check('adv', 'tprofiles') || Authorization::check('adv', 'folder_watch')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('service_setup'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'mng_packages')): ?>
|
||||
<li><a href="packages"><?= $language::get('packages'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'categories')): ?>
|
||||
<li><a href="stream_categories"><?= $language::get('categories'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mng_groups')): ?>
|
||||
<li><a href="groups"><?= $language::get('groups'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'epg')): ?>
|
||||
<li><a href="epgs"><?= $language::get('epgs'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'tprofiles')): ?>
|
||||
<li><a href="profiles"><?= $language::get('transcode_profiles'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'folder_watch')): ?>
|
||||
<li><a href="plex">Plex Sync</a></li>
|
||||
<li><a href="watch"><?= $language::get('folder_watch'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_code') || Authorization::check('adv', 'block_asns') || Authorization::check('adv', 'block_ips') || Authorization::check('adv', 'block_isps') || Authorization::check('adv', 'block_uas') || Authorization::check('adv', 'rtmp') || Authorization::check('adv', 'add_hmac')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#">Access Codes <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'add_code')): ?>
|
||||
<li><a href="code"><?= $language::get('add_access_codes'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_code')): ?>
|
||||
<li><a href="codes"><?= $language::get('menage_access_codes'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_code') || Authorization::check('adv', 'block_asns') || Authorization::check('adv', 'block_ips') || Authorization::check('adv', 'block_isps') || Authorization::check('adv', 'block_uas') || Authorization::check('adv', 'rtmp') || Authorization::check('adv', 'add_hmac')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#">Security <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'block_asns')): ?>
|
||||
<li><a href="asns"><?= $language::get('blocked_asns'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'block_ips')): ?>
|
||||
<li><a href="ips"><?= $language::get('blocked_ips'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'block_isps')): ?>
|
||||
<li><a href="isps"><?= $language::get('blocked_isps'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'block_uas')): ?>
|
||||
<li><a href="useragents"><?= $language::get('blocked_uas'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_hmac')): ?>
|
||||
<li><a href="hmacs"><?= $language::get('hmac_keys'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'rtmp')): ?>
|
||||
<li><a href="rtmp_ips"><?= $language::get('rtmp_ips'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'channel_order') || Authorization::check('adv', 'rtmp') || Authorization::check('adv', 'fingerprint') || Authorization::check('adv', 'mass_delete') || Authorization::check('adv', 'stream_tools') || Authorization::check('adv', 'quick_tools')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('tools'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'channel_order') && !$rMobile): ?>
|
||||
<li><a href="channel_order"><?= $language::get('channel_order'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'fingerprint')): ?>
|
||||
<li><a href="fingerprint"><?= $language::get('fingerprint'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'mass_delete')): ?>
|
||||
<li><a href="mass_delete"><?= $language::get('mass_delete'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'quick_tools')): ?>
|
||||
<li><a href="quick_tools"><?= $language::get('quick_tools'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'rtmp')): ?>
|
||||
<li><a href="rtmp_monitor">RTMP Monitor</a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'stream_tools')): ?>
|
||||
<li><a href="stream_tools"><?= $language::get('stream_tools'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'movies') || Authorization::check('adv', 'streams') || Authorization::check('adv', 'connection_logs') || Authorization::check('adv', 'client_request_log') || Authorization::check('adv', 'login_logs') || Authorization::check('adv', 'panel_logs') || Authorization::check('adv', 'credits_log') || Authorization::check('adv', 'live_connections') || Authorization::check('adv', 'manage_events') || Authorization::check('adv', 'reg_userlog') || Authorization::check('adv', 'streams') || Authorization::check('adv', 'episodes') || Authorization::check('adv', 'series') || Authorization::check('adv', 'stream_errors') || Authorization::check('adv', 'folder_watch')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"><?= $language::get('logs'); ?> <div class="arrow-down"></div></a>
|
||||
<ul class="submenu megamenu">
|
||||
<li>
|
||||
<ul>
|
||||
<?php
|
||||
$logs = [
|
||||
['url' => 'line_activity', 'title' => $language::get('activity_logs'), 'permissions' => ['connection_logs']],
|
||||
['url' => 'client_logs', 'title' => $language::get('client_logs'), 'permissions' => ['client_request_log']],
|
||||
['url' => 'credit_logs', 'title' => $language::get('credit_logs'), 'permissions' => ['credits_log']],
|
||||
['url' => 'queue', 'title' => 'Encoding Queue', 'permissions' => ['streams', 'episodes', 'series']],
|
||||
['url' => 'line_ips', 'title' => $language::get('ips_per_line'), 'permissions' => ['connection_logs']],
|
||||
['url' => 'live_connections', 'title' => $language::get('live_connections'), 'permissions' => ['live_connections']],
|
||||
['url' => 'login_logs', 'title' => 'Login Logs', 'permissions' => ['login_logs']],
|
||||
['url' => 'mag_events', 'title' => $language::get('mag_event_logs'), 'permissions' => ['manage_events']],
|
||||
['url' => 'ondemand', 'title' => 'On-Demand Scanner', 'permissions' => ['streams']],
|
||||
['url' => 'panel_logs', 'title' => 'Panel Errors', 'permissions' => ['panel_logs']],
|
||||
['url' => 'user_logs', 'title' => $language::get('reseller_logs'), 'permissions' => ['reg_userlog']],
|
||||
['url' => 'restream_logs', 'title' => 'Restream Detection', 'permissions' => ['restream_logs']],
|
||||
['url' => 'stream_errors', 'title' => $language::get('stream_errors'), 'permissions' => ['stream_errors']],
|
||||
['url' => 'stream_rank', 'title' => 'Stream Rank', 'permissions' => ['streams']],
|
||||
['url' => 'mysql_syslog', 'title' => 'System Logs', 'permissions' => ['panel_logs']],
|
||||
['url' => 'theft_detection', 'title' => 'VOD Theft Detection', 'permissions' => ['movies']],
|
||||
['url' => 'watch_output', 'title' => $language::get('watch_folder_logs'), 'permissions' => ['folder_watch']]
|
||||
];
|
||||
$filteredLogs = array_filter($logs, function ($log) {
|
||||
return array_reduce($log['permissions'], function ($carry, $permission) {
|
||||
return $carry || Authorization::check('adv', $permission);
|
||||
}, false);
|
||||
});
|
||||
$splitIndex = count($filteredLogs) > 8 ? ceil(count($filteredLogs) / 2) : null;
|
||||
$i = 0;
|
||||
foreach ($filteredLogs as $log) {
|
||||
if ($splitIndex && $i == $splitIndex) {
|
||||
echo '</ul></li><li><ul>';
|
||||
}
|
||||
echo '<li><a href="' . $log['url'] . '">' . $log['title'] . '</a></li>';
|
||||
$i++;
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (!$rSettings['show_tickets'] && Authorization::check('adv', 'manage_tickets')): ?>
|
||||
<li><a href="tickets"><?= $language::get('tickets'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'add_bouquet') || Authorization::check('adv', 'streams') || Authorization::check('adv', 'categories')): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="#"> <i class="fas fa-users"></i><?= $language::get('supplirs'); ?> <div class="arrow-down"></div>
|
||||
</a>
|
||||
<ul class="submenu">
|
||||
<?php if (Authorization::check('adv', 'streams')): ?>
|
||||
<li><a href="provider"><?= $language::get('add_providers'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (Authorization::check('adv', 'streams')): ?>
|
||||
<li><a href="providers"><?= $language::get('stream_providers'); ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if ($_topKids): _xc_nav_children($_navTop, $rMobile, $rSettings, $language);
|
||||
endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user