Skip to content

Commit 1deb1cb

Browse files
Merge pull request #4728 from voyo/onvif-manual-camera-url
feat(onvif): add manual camera URL entry to ONVIF probe
2 parents 1485a7e + c145939 commit 1deb1cb

5 files changed

Lines changed: 139 additions & 9 deletions

File tree

web/lang/en_gb.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,11 @@
471471
'ObjDetect' => 'ObjDetect',
472472
'OnvifProbe' => 'ONVIF',
473473
'OnvifProbeIntro' => 'The list below shows detected ONVIF cameras and whether they are already being used or available for selection.<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>',
474-
'OnvifCredentialsIntro' => 'Please supply user name and password for the selected camera.<br/>If no user has been created for the camera then the user given here will be created with the given password.<br/><br/>',
474+
'OnvifCredentialsIntro' => 'Optionally supply user name and password for the selected camera. Leave blank if the camera does not require authentication.<br/>If no user has been created for the camera then the user given here will be created with the given password.<br/><br/>',
475+
'OnvifManualOr' => 'Or connect to an ONVIF camera directly:',
476+
'OnvifManualLabel' => 'Camera IP / URL',
477+
'OnvifManualPlaceholder' => 'e.g. 192.168.1.100 or http://192.168.1.100/onvif/device_service',
478+
'OnvifManualConnect' => 'Connect ONVIF',
475479
'ONVIF' => 'ONVIF',
476480
'ONVIF_Alarm_Text' => 'ONVIF Alarm Text',
477481
'ONVIF_Event_Listener' => 'ONVIF Event Listener',

web/skins/classic/views/js/monitorprobe.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,23 @@ function submitCamera( element ) {
99

1010
function configureButtons( element ) {
1111
var form = element.form;
12-
form.saveBtn.disabled = (form.probe.selectedIndex==0);
12+
form.saveBtn.disabled = (form.probe.selectedIndex == 0);
13+
var manualUrlEl = form.elements.namedItem('manual_url');
14+
var hasManualUrl = manualUrlEl && manualUrlEl.value.trim().length > 0;
15+
if (form.elements.namedItem('onvifBtn')) {
16+
form.onvifBtn.disabled = !hasManualUrl;
17+
}
18+
}
19+
20+
function connectOnvif( element ) {
21+
var form = element.form;
22+
var manualUrl = form.manual_url ? form.manual_url.value.trim() : '';
23+
if ( !manualUrl ) return;
24+
var mid = (form.mid && form.mid.value) ? form.mid.value : '';
25+
window.location.assign(
26+
'?view=onvifprobe&mid=' + encodeURIComponent(mid) +
27+
'&manual_url=' + encodeURIComponent(manualUrl)
28+
);
1329
}
1430

1531
function changeInterface(element) {

web/skins/classic/views/js/onvifprobe.js

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,70 @@ function gotoStep1( element ) {
1313
form.submit();
1414
}
1515

16+
function normalizeOnvifManualUrl( manualUrl ) {
17+
var normalizedUrl = manualUrl.trim();
18+
19+
if ( normalizedUrl.length === 0 ) {
20+
return '';
21+
}
22+
23+
if ( !/^[a-z][a-z0-9+.-]*:\/\//i.test(normalizedUrl) ) {
24+
normalizedUrl = 'http://' + normalizedUrl;
25+
}
26+
27+
try {
28+
var parsedUrl = new URL(normalizedUrl);
29+
30+
if ( parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:' ) {
31+
return '';
32+
}
33+
34+
if ( !parsedUrl.pathname || parsedUrl.pathname === '/' ) {
35+
parsedUrl.pathname = '/onvif/device_service';
36+
}
37+
38+
return parsedUrl.toString();
39+
} catch ( e ) {
40+
return '';
41+
}
42+
}
43+
1644
function gotoStep2( element ) {
1745
var form = element.form;
46+
var manualUrlEl = form.elements.namedItem('manual_url');
47+
var manualUrl = manualUrlEl ? normalizeOnvifManualUrl(manualUrlEl.value) : '';
48+
49+
if ( manualUrl.length > 0 ) {
50+
var cameraData = {
51+
Function: 'Monitor',
52+
Type: 'Ffmpeg',
53+
Host: manualUrl,
54+
SOAP: '1.1',
55+
ConfigURL: manualUrl,
56+
ConfigOptions: 'SOAP1.1',
57+
Notes: ''
58+
};
59+
60+
var encoded = btoa(JSON.stringify(cameraData));
61+
var option = null;
62+
var i;
63+
64+
for ( i = 0; i < form.probe.options.length; i++ ) {
65+
if ( form.probe.options[i].value === encoded ) {
66+
option = form.probe.options[i];
67+
break;
68+
}
69+
}
70+
71+
if ( !option ) {
72+
option = document.createElement('option');
73+
option.value = encoded;
74+
form.probe.appendChild(option);
75+
}
76+
77+
option.selected = true;
78+
}
79+
1880
form.target = self.name;
1981
form.view.value = 'onvifprobe';
2082
form.step.value = '2';
@@ -23,13 +85,15 @@ function gotoStep2( element ) {
2385

2486
function configureButtons(element) {
2587
var form = element.form;
88+
var manualUrlEl = form.elements.namedItem('manual_url');
89+
var hasManualUrl = manualUrlEl && manualUrlEl.value.trim().length > 0;
90+
var hasProbe = form.probe && (form.probe.selectedIndex != 0);
91+
2692
if (form.elements.namedItem('nextBtn')) {
27-
form.nextBtn.disabled = (form.probe.selectedIndex==0) ||
28-
(form.Username == '') || (form.Username == null) ||
29-
(form.Password == '') || (form.Password == null);
93+
form.nextBtn.disabled = !(hasManualUrl || hasProbe);
3094
}
3195
if (form.elements.namedItem('saveBtn')) {
32-
form.saveBtn.disabled = (form.probe.selectedIndex==0);
96+
form.saveBtn.disabled = (form.probe.selectedIndex == 0);
3397
}
3498
}
3599

web/skins/classic/views/monitorprobe.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@
2323
return;
2424
}
2525

26+
// Compatibility shim: get_networks() was added to functions.php in a later
27+
// release. Older installations need this fallback so the page renders.
28+
if (!function_exists('get_networks')) {
29+
function get_networks() {
30+
// Minimal stub: no interface list available on this ZM version.
31+
// The manual ONVIF URL entry below works without interface selection.
32+
return array('default' => '');
33+
}
34+
}
35+
2636
// Probe Local Cameras
2737
function probeV4L() {
2838

@@ -463,9 +473,22 @@ function probeNetwork() {
463473
<label for="probe"><?php echo translate('DetectedCameras') ?></label>
464474
<?php echo htmlSelect('probe', $cameras, null, array('data-on-change-this'=>'configureButtons')); ?>
465475
</p>
476+
<hr/>
477+
<p><?php echo translate('OnvifManualOr') ?></p>
478+
<p>
479+
<label for="manual_url"><?php echo translate('OnvifManualLabel') ?></label>
480+
<input type="text" name="manual_url" id="manual_url"
481+
placeholder="<?php echo translate('OnvifManualPlaceholder') ?>"
482+
value="<?php echo isset($_REQUEST['manual_url']) ? htmlspecialchars($_REQUEST['manual_url']) : '' ?>"
483+
data-on-change-this="configureButtons"
484+
data-on-input-this="configureButtons"
485+
size="40"/>
486+
</p>
466487
<div id="contentButtons">
467488
<button type="button" name="saveBtn" value="Save" data-on-click-this="submitCamera" disabled="disabled">
468489
<?php echo translate('Save') ?></button>
490+
<button type="button" name="onvifBtn" data-on-click-this="connectOnvif" disabled="disabled">
491+
<?php echo translate('OnvifManualConnect') ?></button>
469492
<button type="button" data-on-click="backWindow"><?php echo translate('Cancel') ?></button>
470493
</div>
471494
</form>

web/skins/classic/views/onvifprobe.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@
2323
return;
2424
}
2525

26+
// Compatibility shim: get_networks() was added to functions.php in a later
27+
// release. Older installations need this fallback so the page renders.
28+
if (!function_exists('get_networks')) {
29+
function get_networks() {
30+
// Minimal stub: no interface list available on this ZM version.
31+
// The manual ONVIF URL entry below works without interface selection.
32+
return array('default' => '');
33+
}
34+
}
35+
2636
$cameras = array();
2737
$cameras[0] = translate('ChooseDetectedCamera');
2838

@@ -183,12 +193,12 @@ function probeProfiles($device_ep, $soapversion, $username, $password) {
183193
<?php echo translate('OnvifProbeIntro') ?>
184194
</p>
185195
<p><label for="interface"><?php echo translate('Interface') ?></label>
186-
<?php
196+
<?php
187197
$interfaces = get_networks();
188198
$default_interface = $interfaces['default'];
189199
unset($interfaces['default']);
190200

191-
echo htmlSelect('interface', $interfaces,
201+
echo htmlSelect('interface', $interfaces,
192202
(isset($_REQUEST['interface']) ? $_REQUEST['interface'] : $default_interface),
193203
array('data-on-change-this'=>'changeInterface') );
194204
?>
@@ -198,6 +208,18 @@ function probeProfiles($device_ep, $soapversion, $username, $password) {
198208
<label for="probe"><?php echo translate('DetectedCameras') ?></label>
199209
<?php echo htmlSelect('probe', $cameras, null, array('data-on-change-this'=>'configureButtons')); ?>
200210
</p>
211+
<p><?php echo translate('OnvifManualOr') ?></p>
212+
<p>
213+
<label for="manual_url"><?php echo translate('OnvifManualLabel') ?></label>
214+
<input type="text" name="manual_url" id="manual_url"
215+
placeholder="<?php echo translate('OnvifManualPlaceholder') ?>"
216+
value="<?php echo isset($_REQUEST['manual_url']) ? htmlspecialchars($_REQUEST['manual_url']) : '' ?>"
217+
data-on-change-this="configureButtons"
218+
data-on-input-this="configureButtons"
219+
oninput="(function(el){var nb=el.form.elements.namedItem('nextBtn');if(nb)nb.disabled=el.value.trim().length===0;})(this)"
220+
onchange="(function(el){var nb=el.form.elements.namedItem('nextBtn');if(nb)nb.disabled=el.value.trim().length===0;})(this)"
221+
style="width: 40ch"/>
222+
</p>
201223
<p>
202224
<?php echo translate('OnvifCredentialsIntro') ?>
203225
</p>
@@ -213,7 +235,8 @@ function probeProfiles($device_ep, $soapversion, $username, $password) {
213235
</div>
214236
<div id="contentButtons">
215237
<button type="button" data-on-click="backWindow"><?php echo translate('Cancel') ?></button>
216-
<button type="button" name="nextBtn" data-on-click-this="gotoStep2" disabled="disabled"><?php echo translate('Next') ?></button>
238+
<button type="button" name="nextBtn" data-on-click-this="gotoStep2"
239+
<?php echo (empty($_REQUEST['manual_url']) ? 'disabled="disabled"' : '') ?>><?php echo translate('Next') ?></button>
217240
</div>
218241
</form>
219242
</div>

0 commit comments

Comments
 (0)