Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d1e4f73
Feat: Added <select> to control enabling players on Monitor page (mon…
IgorA100 Aug 26, 2025
182360f
Added "ZMSEnabled" (Monitor.php)
IgorA100 Aug 26, 2025
0220604
Disabled the ability to click on the element "input.disabled" (skin.js)
IgorA100 Aug 26, 2025
7f24e0e
Added 'ZMSEnabled' (monitor.php)
IgorA100 Aug 26, 2025
d408688
Added support for "input.disabled" (skin.css)
IgorA100 Aug 26, 2025
b8c9842
Fix Eslint (monitor.js)
IgorA100 Aug 26, 2025
cf0f0a4
Added <select> to select players (monitor.php)
IgorA100 Aug 29, 2025
a5c6e02
Avoid SQL error because we are not using the "ZMSEnabled" field yet(M…
IgorA100 Sep 2, 2025
d218910
Merge branch 'ZoneMinder:master' into patch-439137
IgorA100 Sep 17, 2025
1e336bf
Merge branch 'ZoneMinder:master' into patch-439137
IgorA100 Oct 29, 2025
80c5957
Merge branch 'ZoneMinder:master' into patch-439137
IgorA100 Feb 5, 2026
8d2ca14
Instead of the variable "i," we use "j."Update web/skins/classic/view…
IgorA100 Mar 5, 2026
a012e41
Commented out the unused line 'ZMSEnabled' => 0 (monitor.php)
IgorA100 Mar 8, 2026
ce0974c
event listener to prevent Space/Enter for ".disabled" (web/skins/clas…
IgorA100 Mar 8, 2026
eef799e
Merge branch 'ZoneMinder:master' into patch-439137
IgorA100 Mar 8, 2026
67374da
Use "data-none-exists" instead of the "none-exists" attribute. (monit…
IgorA100 Mar 8, 2026
fbf824e
Use "data-none-exists" instead of the "none-exists" attribute. (web/s…
IgorA100 Mar 8, 2026
658db62
Added the $enableDisableZMS variable, a getter for it, and "construct…
IgorA100 Mar 9, 2026
06bf008
Added the condition "if (ZM\Monitor::getEnableDisableZMS()) $types['Z…
IgorA100 Mar 9, 2026
5c3521d
Added analysis for ZM\Monitor::getEnableDisableZMS() (monitor.php)
IgorA100 Mar 9, 2026
4208fcf
Avoid PHP warnings (monitor.php)
IgorA100 Mar 9, 2026
c1657c7
Merge branch 'ZoneMinder:master' into patch-439137
IgorA100 Mar 30, 2026
5826cd2
Merge branch 'ZoneMinder:master' into patch-439137
IgorA100 Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions web/includes/Monitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ class Monitor extends ZM_Object {
private $shm_id = null;
private $connected = false;

private static $enableDisableZMS = false; // This setting is for future use, in case we decide to disable ALL players, including ZMS.
// To be able to disable/enable the ZMS player, you need to run the following code in the database:
// "ALTER TABLE `Monitors` ADD COLUMN `ZMSEnabled` BOOLEAN NOT NULL default true AFTER `Decoding`"

public function __construct($IdOrRow = NULL) {
parent::__construct($IdOrRow);
if (self::$enableDisableZMS) $this->defaults['ZMSEnabled'] = array('type'=>'integer','default'=>1);
}

public static function getEnableDisableZMS() {
return self::$enableDisableZMS;
}

private $shm_offsets = ['SharedData' => [
'size' => [ 'type'=>'uint32', 'offset'=>0, 'size'=>4 ],
'last_write_index' => [ 'type'=>'int32', 'offset'=>4, 'size'=>4 ],
Expand Down
1 change: 1 addition & 0 deletions web/includes/actions/monitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
'SectionLengthWarn' => 0,
'SOAP_wsa_compl' => 0
);
if (ZM\Monitor::getEnableDisableZMS()) $types['ZMSEnabled'] = 0;

# Checkboxes don't return an element in the POST data, so won't be present in newMonitor.
# So force a value for these fields
Expand Down
20 changes: 14 additions & 6 deletions web/skins/classic/css/base/skin.css
Original file line number Diff line number Diff line change
Expand Up @@ -1395,22 +1395,29 @@ button.btn.btn-view-watch:hover {
--d-t: 0.6s;
--d-t-e: cubic-bezier(0.2, 0.85, 0.32, 1.2);
}
input[type='checkbox']:disabled, input[type='radio']:disabled {
input[type='checkbox'].disabled:not(.input-switch):after {
border-color: var(--active);
}
input[type='checkbox']:disabled, input[type='radio']:disabled,
input[type='checkbox'].disabled, input[type='radio'].disabled {
--b: var(--disabled);
cursor: not-allowed;
opacity: 0.9;
}
input[type='checkbox']:disabled:checked, input[type='radio']:disabled:checked {
input[type='checkbox']:disabled:checked, input[type='radio']:disabled:checked,
input[type='checkbox'].disabled:checked, input[type='radio'].disabled:checked {
--b: var(--disabled-inner);
--bc: var(--border);
}
input[type='checkbox']:disabled + label, input[type='radio']:disabled + label {
input[type='checkbox']:disabled + label, input[type='radio']:disabled + label,
input[type='checkbox'].disabled + label, input[type='radio'].disabled + label {
cursor: not-allowed;
}
input[type='checkbox']:hover:not(:checked):not(:disabled), input[type='radio']:hover:not(:checked):not(:disabled) {
input[type='checkbox']:hover:not(:checked):not(:disabled), input[type='radio']:hover:not(:checked):not(:disabled),
input[type='checkbox']:hover:not(:checked):not(.disabled), input[type='radio']:hover:not(:checked):not(.disabled) {
--bc: var(--border-hover);
}
input[type='checkbox']:focus, input[type='radio']:focus {
input[type='checkbox']:not(.disabled):focus, input[type='radio']:not(.disabled):focus {
box-shadow: 0 0 0 var(--focus);
}
input[type='checkbox']:not(.input-switch), input[type='radio']:not(.input-switch) {
Expand Down Expand Up @@ -1466,7 +1473,8 @@ button.btn.btn-view-watch:hover {
--ab: var(--active-inner);
--x: 17px;
}
input[type='checkbox'].input-switch:disabled:not(:checked):after {
input[type='checkbox'].input-switch:disabled:not(:checked):after,
input[type='checkbox'].input-switch.disabled:not(:checked):after {
opacity: 0.6;
}
input[type='radio'] {
Expand Down
14 changes: 14 additions & 0 deletions web/skins/classic/js/skin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2595,6 +2595,20 @@ function initPageGeneral() {
}
});
});

document.querySelectorAll('input.disabled').forEach(function(el) {
el.addEventListener("click", function clickInputDisabled(event) {
event.preventDefault();
return;
});
el.addEventListener("keydown", function keydownInputDisabled(event) {
var key = event.key || event.keyCode;
if (key === ' ' || key === 'Spacebar' || key === 'Enter' || key === 32 || key === 13) {
event.preventDefault();
event.stopPropagation();
}
});
});
}

// Called when monitor filters change - refreshes table via AJAX instead of full page reload
Expand Down
87 changes: 87 additions & 0 deletions web/skins/classic/views/js/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ function initPage() {
if (!isMobile()) initThumbAnimation();

manageChannelStream();
disableCheckboxesPlayerSelection();
} // end function initPage()

function saveMonitorData(href = '') {
Expand Down Expand Up @@ -896,6 +897,92 @@ function ControlList_onClick() {
window.location = '?view=options&tab=control';
}

function selectPlayersSetZMS(select) {
for (var i = 0; i < select.length; i++) {
var option_ = select.options[i];
if (option_.value == 'zms') {
option_.selected = true;
break;
}
}
selectPlayersChangeCheckBox('zms', 'set');
}

function disableCheckboxesPlayerSelection() {
// Now all control is via multi selector #SelectPlayers
let checkBox;
checkBox = document.querySelector('input[name="newMonitor[ZMSEnabled]"]');
if (checkBox) checkBox.classList.add('disabled');

checkBox = document.querySelector('input[name="newMonitor[Go2RTCEnabled]"]');
if (checkBox) checkBox.classList.add('disabled');

checkBox = document.querySelector('input[name="newMonitor[RTSP2WebEnabled]"]');
if (checkBox) checkBox.classList.add('disabled');

checkBox = document.querySelector('input[name="newMonitor[JanusEnabled]"]');
if (checkBox) checkBox.classList.add('disabled');

const sel = document.querySelector('select[name="SelectPlayers"]');
if (sel) sel.noneExists = sel.getAttribute('data-none-exists');
}

function selectPlayersChangeCheckBox(selectedPlayer, action) {
let checkBox;
if (selectedPlayer == 'zms') {
checkBox = document.querySelector('input[name="newMonitor[ZMSEnabled]"]');
} else if (selectedPlayer == 'go2rtc') {
checkBox = document.querySelector('input[name="newMonitor[Go2RTCEnabled]"]');
} else if (selectedPlayer == 'rtsp2web') {
checkBox = document.querySelector('input[name="newMonitor[RTSP2WebEnabled]"]');
} else if (selectedPlayer == 'janus') {
checkBox = document.querySelector('input[name="newMonitor[JanusEnabled]"]');
}
if (checkBox) {
checkBox.checked = (action == 'set') ? true : false;
// Need to call the "change" event trigger
const event = new Event("change");
checkBox.dispatchEvent(event);
}
}

function selectPlayers(e) {
var select = e.target;
var count = 0;
for (var i = 0; i < select.length; i++) {
var option = select.options[i];
if (option.selected) {
count++;
if (option.value == 'none') {
if (select.noneExists) { //Previously 'none' was already added, and now we are changing something
select.noneExists = false;
option.selected = false;
selectPlayersSetZMS(select);
} else { //Adding for the first time
select.noneExists = true;
for (let j = 0; j < select.length; j++) {
var option_ = select.options[j];
if (option_.value != 'none') {
option_.selected = false;
selectPlayersChangeCheckBox(option_.value, 'remove');
//option_.removeAttribute('selected');
}
}
}
} else { //Let's set a checkbox for the corresponding player
selectPlayersChangeCheckBox(option.value, 'set');
}
} else { //Let's reset the checkbox for the corresponding player
selectPlayersChangeCheckBox(option.value, 'remove');
}
}
if (count === 0) {
select.noneExists = false;
selectPlayersSetZMS(select);
}
applyChosen();
}

window.addEventListener('DOMContentLoaded', initPage);

// Clear password field values when navigating away from the page so
Expand Down
34 changes: 34 additions & 0 deletions web/skins/classic/views/monitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,24 @@ class="nav-link<?php echo ($tab == $name ? ' active' : '') . ' ' . (($name == 'z
break;
}
case 'viewing' :
$selectPlayers = array(
'go2rtc' => 'Go2RTC',
'rtsp2web' => 'RTSP2Web',
'janus' => 'Janus',
'zms' => ['Name' => 'ZMS MJPEG', 'disabled' => 'disabled'],
);
if (ZM\Monitor::getEnableDisableZMS()) $selectPlayers = array_merge($selectPlayers, array('none' => 'None')); // Add an option to disable all players
$selectedPlayers = [];
$noneExists = false; // All players are disabled
if ((ZM\Monitor::getEnableDisableZMS() && !$monitor->ZMSEnabled()) && (!$monitor->Go2RTCEnabled()) && (!$monitor->RTSP2WebEnabled()) && (!$monitor->JanusEnabled())) {
$selectedPlayers[] = 'none';
$noneExists = true;
} else {
if (!ZM\Monitor::getEnableDisableZMS() || (ZM\Monitor::getEnableDisableZMS() && $monitor->ZMSEnabled())) $selectedPlayers[] = 'zms';
if ($monitor->Go2RTCEnabled()) $selectedPlayers[] = 'go2rtc';
if ($monitor->RTSP2WebEnabled()) $selectedPlayers[] = 'rtsp2web';
if ($monitor->JanusEnabled()) $selectedPlayers[] = 'janus';
}
?>
<li class="RTSPServer">
<label><?php echo translate('RTSPServer'); echo makeHelpLink('OPTIONS_RTSPSERVER') ?></label>
Expand All @@ -1257,6 +1275,22 @@ class="nav-link<?php echo ($tab == $name ? ' active' : '') . ' ' . (($name == 'z
<label><?php echo translate('RTSPStreamName'); echo makeHelpLink('OPTIONS_RTSPSTREAMNAME') ?></label>
<input type="text" name="newMonitor[RTSPStreamName]" value="<?php echo validHtmlStr($monitor->RTSPStreamName()) ?>"/>
</li>
<li id="SelectPlayers" class="SelectPlayers">
<label><?php echo translate('Select players'); echo makeHelpLink('OPTIONS_SELECTPLAYERS') ?> </label>
<?php echo htmlSelect('SelectPlayers', $selectPlayers, $selectedPlayers, ['class'=>'chosen chosen-full-width', 'multiple'=>'', 'data-on-change'=>'selectPlayers', 'data-none-exists'=>$noneExists]); ?>
</li>
<?php
if (ZM\Monitor::getEnableDisableZMS()) {
$checkedZMSEnabled = ($monitor->ZMSEnabled()) ? ' checked="checked"' : '';
$strZMSEnabled = '
<li id="FunctionZMSEnabled">
<label>' . translate('ZMS MJPEG') . '</label>
<input type="checkbox" name="newMonitor[ZMSEnabled]" value="1"'. $checkedZMSEnabled .' />
</li>
';
echo $strZMSEnabled;
}
?>
<li id="FunctionGo2RTCEnabled">
<label><?php echo translate('Go2RTC Live Stream') ?></label>
<input type="checkbox" name="newMonitor[Go2RTCEnabled]" value="1"<?php echo $monitor->Go2RTCEnabled() ? ' checked="checked"' : '' ?> on_click="update_players"/>
Expand Down
Loading