Merge pull request #1450 from RaspAP/feat/form-validation

Custom Bootstrap form validation for required fields
This commit is contained in:
Bill Zimmerman 2023-11-11 09:39:57 +01:00 committed by GitHub
commit 0b114ecab7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 327 additions and 48 deletions

View file

@ -16,6 +16,10 @@ License: GNU General Public License v3.0
h4.mt-3 { margin-left: 0.5rem; } h4.mt-3 { margin-left: 0.5rem; }
} }
.sidebar.toggled .nav-item .nav-link {
padding: 0.65rem 1rem;
}
.sidebar-brand-text { .sidebar-brand-text {
text-transform: none; text-transform: none;
color: #212529; color: #212529;
@ -52,7 +56,11 @@ License: GNU General Public License v3.0
.info-item-xs { .info-item-xs {
font-size: 0.7rem; font-size: 0.7rem;
margin-left: 0.3rem; margin-left: 0.75rem;
}
.sb-status {
margin-left: 0.75rem!important;
} }
.info-item-wifi { .info-item-wifi {
@ -235,3 +243,22 @@ button > i.fas {
font-size: 1.3rem; font-size: 1.3rem;
} }
.was-validated .form-control:valid,
.was-validated .form-control:invalid {
background-position: center right calc(.375em + .4875rem);
}
.was-validated .form-control:invalid {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.was-validated .form-control:valid {
background-size: calc(0.6em + 0.375rem) calc(0.6em + 0.375rem);
}
.input-group>.input-group-append:not(:last-child)>.btn {
border-top-right-radius: 0.35rem;
border-bottom-right-radius: 0.35rem;
}

View file

@ -627,3 +627,18 @@ a.scroll-to-top.rounded i.fas.fa-angle-up {
padding: 5px; padding: 5px;
box-sizing: border-box; box-sizing: border-box;
} }
.was-validated .form-control:valid,
.was-validated .form-control:invalid {
background-position: center right calc(.375em + .4875rem);
}
.was-validated .form-control:invalid {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.was-validated .form-control:valid {
background-size: calc(0.6em + 0.375rem) calc(0.6em + 0.375rem);
}

View file

@ -633,3 +633,18 @@ a.scroll-to-top.rounded i.fas.fa-angle-up {
padding: 5px; padding: 5px;
box-sizing: border-box; box-sizing: border-box;
} }
.was-validated .form-control:valid,
.was-validated .form-control:invalid {
background-position: center right calc(.375em + .4875rem);
}
.was-validated .form-control:invalid {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.was-validated .form-control:valid {
background-size: calc(0.6em + 0.375rem) calc(0.6em + 0.375rem);
}

View file

@ -241,6 +241,7 @@ function loadInterfaceDHCPSelect() {
$('#chkstatic').closest('.btn').button('toggle').blur(); $('#chkstatic').closest('.btn').button('toggle').blur();
$('#chkstatic').blur(); $('#chkstatic').blur();
$('#chkfallback').prop('disabled', true); $('#chkfallback').prop('disabled', true);
$('#dhcp-iface').removeAttr('disabled');
} else { } else {
$('#chkdhcp').closest('.btn').button('toggle'); $('#chkdhcp').closest('.btn').button('toggle');
$('#chkdhcp').closest('.btn').button('toggle').blur(); $('#chkdhcp').closest('.btn').button('toggle').blur();
@ -249,6 +250,7 @@ function loadInterfaceDHCPSelect() {
} }
if (jsonData.FallbackEnabled || $('#chkdhcp').is(':checked')) { if (jsonData.FallbackEnabled || $('#chkdhcp').is(':checked')) {
$('#dhcp-iface').prop('disabled', true); $('#dhcp-iface').prop('disabled', true);
setDhcpFieldsDisabled();
} }
}); });
} }
@ -259,6 +261,7 @@ function setDHCPToggles(state) {
} }
if ($('#dhcp-iface').is(':checked') && !state) { if ($('#dhcp-iface').is(':checked') && !state) {
$('#dhcp-iface').prop('checked', state); $('#dhcp-iface').prop('checked', state);
setDhcpFieldsDisabled();
} }
$('#chkfallback').prop('disabled', state); $('#chkfallback').prop('disabled', state);
$('#dhcp-iface').prop('disabled', !state); $('#dhcp-iface').prop('disabled', !state);
@ -365,7 +368,15 @@ $('#js-sys-reboot, #js-sys-shutdown').on('click', function (e) {
}); });
$(document).ready(function(){ $(document).ready(function(){
$("#PanelManual").hide(); $("#PanelManual").hide();
$('.ip_address').mask('0ZZ.0ZZ.0ZZ.0ZZ', {
translation: {
'Z': {
pattern: /[0-9]/, optional: true
}
},
placeholder: "___.___.___.___"
});
}); });
$('#wg-upload,#wg-manual').on('click', function (e) { $('#wg-upload,#wg-manual').on('click', function (e) {
@ -542,6 +553,77 @@ window.addEventListener('load', function() {
}); });
}, false); }, false);
// DHCP or Static IP option group
$('#chkstatic').on('change', function() {
if (this.checked) {
setStaticFieldsEnabled();
}
});
$('#chkdhcp').on('change', function() {
this.checked ? setStaticFieldsDisabled() : null;
});
$('input[name="dhcp-iface"]').change(function() {
if ($('input[name="dhcp-iface"]:checked').val() == '1') {
setDhcpFieldsEnabled();
} else {
setDhcpFieldsDisabled();
}
});
function setStaticFieldsEnabled() {
$('#txtipaddress').prop('required', true);
$('#txtsubnetmask').prop('required', true);
$('#txtgateway').prop('required', true);
$('#txtipaddress').removeAttr('disabled');
$('#txtsubnetmask').removeAttr('disabled');
$('#txtgateway').removeAttr('disabled');
}
function setStaticFieldsDisabled() {
$('#txtipaddress').prop('disabled', true);
$('#txtsubnetmask').prop('disabled', true);
$('#txtgateway').prop('disabled', true);
$('#txtipaddress').removeAttr('required');
$('#txtsubnetmask').removeAttr('required');
$('#txtgateway').removeAttr('required');
}
function setDhcpFieldsEnabled() {
$('#txtrangestart').prop('required', true);
$('#txtrangeend').prop('required', true);
$('#txtrangeleasetime').prop('required', true);
$('#cbxrangeleasetimeunits').prop('required', true);
$('#txtrangestart').removeAttr('disabled');
$('#txtrangeend').removeAttr('disabled');
$('#txtrangeleasetime').removeAttr('disabled');
$('#cbxrangeleasetimeunits').removeAttr('disabled');
$('#txtdns1').removeAttr('disabled');
$('#txtdns2').removeAttr('disabled');
$('#txtmetric').removeAttr('disabled');
}
function setDhcpFieldsDisabled() {
$('#txtrangestart').removeAttr('required');
$('#txtrangeend').removeAttr('required');
$('#txtrangeleasetime').removeAttr('required');
$('#cbxrangeleasetimeunits').removeAttr('required');
$('#txtrangestart').prop('disabled', true);
$('#txtrangeend').prop('disabled', true);
$('#txtrangeleasetime').prop('disabled', true);
$('#cbxrangeleasetimeunits').prop('disabled', true);
$('#txtdns1').prop('disabled', true);
$('#txtdns2').prop('disabled', true);
$('#txtmetric').prop('disabled', true);
}
// Static Array method // Static Array method
Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start); Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);

19
dist/jquery-mask/jquery.mask.min.js vendored Normal file
View file

@ -0,0 +1,19 @@
// jQuery Mask Plugin v1.14.16
// github.com/igorescobar/jQuery-Mask-Plugin
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,n,f){a instanceof String&&(a=String(a));for(var p=a.length,k=0;k<p;k++){var b=a[k];if(n.call(f,b,k,a))return{i:k,v:b}}return{i:-1,v:void 0}};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,n,f){a!=Array.prototype&&a!=Object.prototype&&(a[n]=f.value)};$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.polyfill=function(a,n,f,p){if(n){f=$jscomp.global;a=a.split(".");for(p=0;p<a.length-1;p++){var k=a[p];k in f||(f[k]={});f=f[k]}a=a[a.length-1];p=f[a];n=n(p);n!=p&&null!=n&&$jscomp.defineProperty(f,a,{configurable:!0,writable:!0,value:n})}};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,f){return $jscomp.findInternal(this,a,f).v}},"es6","es3");
(function(a,n,f){"function"===typeof define&&define.amd?define(["jquery"],a):"object"===typeof exports&&"undefined"===typeof Meteor?module.exports=a(require("jquery")):a(n||f)})(function(a){var n=function(b,d,e){var c={invalid:[],getCaret:function(){try{var a=0,r=b.get(0),h=document.selection,d=r.selectionStart;if(h&&-1===navigator.appVersion.indexOf("MSIE 10")){var e=h.createRange();e.moveStart("character",-c.val().length);a=e.text.length}else if(d||"0"===d)a=d;return a}catch(C){}},setCaret:function(a){try{if(b.is(":focus")){var c=
b.get(0);if(c.setSelectionRange)c.setSelectionRange(a,a);else{var g=c.createTextRange();g.collapse(!0);g.moveEnd("character",a);g.moveStart("character",a);g.select()}}}catch(B){}},events:function(){b.on("keydown.mask",function(a){b.data("mask-keycode",a.keyCode||a.which);b.data("mask-previus-value",b.val());b.data("mask-previus-caret-pos",c.getCaret());c.maskDigitPosMapOld=c.maskDigitPosMap}).on(a.jMaskGlobals.useInput?"input.mask":"keyup.mask",c.behaviour).on("paste.mask drop.mask",function(){setTimeout(function(){b.keydown().keyup()},
100)}).on("change.mask",function(){b.data("changed",!0)}).on("blur.mask",function(){f===c.val()||b.data("changed")||b.trigger("change");b.data("changed",!1)}).on("blur.mask",function(){f=c.val()}).on("focus.mask",function(b){!0===e.selectOnFocus&&a(b.target).select()}).on("focusout.mask",function(){e.clearIfNotMatch&&!k.test(c.val())&&c.val("")})},getRegexMask:function(){for(var a=[],b,c,e,t,f=0;f<d.length;f++)(b=l.translation[d.charAt(f)])?(c=b.pattern.toString().replace(/.{1}$|^.{1}/g,""),e=b.optional,
(b=b.recursive)?(a.push(d.charAt(f)),t={digit:d.charAt(f),pattern:c}):a.push(e||b?c+"?":c)):a.push(d.charAt(f).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&"));a=a.join("");t&&(a=a.replace(new RegExp("("+t.digit+"(.*"+t.digit+")?)"),"($1)?").replace(new RegExp(t.digit,"g"),t.pattern));return new RegExp(a)},destroyEvents:function(){b.off("input keydown keyup paste drop blur focusout ".split(" ").join(".mask "))},val:function(a){var c=b.is("input")?"val":"text";if(0<arguments.length){if(b[c]()!==a)b[c](a);
c=b}else c=b[c]();return c},calculateCaretPosition:function(a){var d=c.getMasked(),h=c.getCaret();if(a!==d){var e=b.data("mask-previus-caret-pos")||0;d=d.length;var g=a.length,f=a=0,l=0,k=0,m;for(m=h;m<d&&c.maskDigitPosMap[m];m++)f++;for(m=h-1;0<=m&&c.maskDigitPosMap[m];m--)a++;for(m=h-1;0<=m;m--)c.maskDigitPosMap[m]&&l++;for(m=e-1;0<=m;m--)c.maskDigitPosMapOld[m]&&k++;h>g?h=10*d:e>=h&&e!==g?c.maskDigitPosMapOld[h]||(e=h,h=h-(k-l)-a,c.maskDigitPosMap[h]&&(h=e)):h>e&&(h=h+(l-k)+f)}return h},behaviour:function(d){d=
d||window.event;c.invalid=[];var e=b.data("mask-keycode");if(-1===a.inArray(e,l.byPassKeys)){e=c.getMasked();var h=c.getCaret(),g=b.data("mask-previus-value")||"";setTimeout(function(){c.setCaret(c.calculateCaretPosition(g))},a.jMaskGlobals.keyStrokeCompensation);c.val(e);c.setCaret(h);return c.callbacks(d)}},getMasked:function(a,b){var h=[],f=void 0===b?c.val():b+"",g=0,k=d.length,n=0,p=f.length,m=1,r="push",u=-1,w=0;b=[];if(e.reverse){r="unshift";m=-1;var x=0;g=k-1;n=p-1;var A=function(){return-1<
g&&-1<n}}else x=k-1,A=function(){return g<k&&n<p};for(var z;A();){var y=d.charAt(g),v=f.charAt(n),q=l.translation[y];if(q)v.match(q.pattern)?(h[r](v),q.recursive&&(-1===u?u=g:g===x&&g!==u&&(g=u-m),x===u&&(g-=m)),g+=m):v===z?(w--,z=void 0):q.optional?(g+=m,n-=m):q.fallback?(h[r](q.fallback),g+=m,n-=m):c.invalid.push({p:n,v:v,e:q.pattern}),n+=m;else{if(!a)h[r](y);v===y?(b.push(n),n+=m):(z=y,b.push(n+w),w++);g+=m}}a=d.charAt(x);k!==p+1||l.translation[a]||h.push(a);h=h.join("");c.mapMaskdigitPositions(h,
b,p);return h},mapMaskdigitPositions:function(a,b,d){a=e.reverse?a.length-d:0;c.maskDigitPosMap={};for(d=0;d<b.length;d++)c.maskDigitPosMap[b[d]+a]=1},callbacks:function(a){var g=c.val(),h=g!==f,k=[g,a,b,e],l=function(a,b,c){"function"===typeof e[a]&&b&&e[a].apply(this,c)};l("onChange",!0===h,k);l("onKeyPress",!0===h,k);l("onComplete",g.length===d.length,k);l("onInvalid",0<c.invalid.length,[g,a,b,c.invalid,e])}};b=a(b);var l=this,f=c.val(),k;d="function"===typeof d?d(c.val(),void 0,b,e):d;l.mask=
d;l.options=e;l.remove=function(){var a=c.getCaret();l.options.placeholder&&b.removeAttr("placeholder");b.data("mask-maxlength")&&b.removeAttr("maxlength");c.destroyEvents();c.val(l.getCleanVal());c.setCaret(a);return b};l.getCleanVal=function(){return c.getMasked(!0)};l.getMaskedVal=function(a){return c.getMasked(!1,a)};l.init=function(g){g=g||!1;e=e||{};l.clearIfNotMatch=a.jMaskGlobals.clearIfNotMatch;l.byPassKeys=a.jMaskGlobals.byPassKeys;l.translation=a.extend({},a.jMaskGlobals.translation,e.translation);
l=a.extend(!0,{},l,e);k=c.getRegexMask();if(g)c.events(),c.val(c.getMasked());else{e.placeholder&&b.attr("placeholder",e.placeholder);b.data("mask")&&b.attr("autocomplete","off");g=0;for(var f=!0;g<d.length;g++){var h=l.translation[d.charAt(g)];if(h&&h.recursive){f=!1;break}}f&&b.attr("maxlength",d.length).data("mask-maxlength",!0);c.destroyEvents();c.events();g=c.getCaret();c.val(c.getMasked());c.setCaret(g)}};l.init(!b.is("input"))};a.maskWatchers={};var f=function(){var b=a(this),d={},e=b.attr("data-mask");
b.attr("data-mask-reverse")&&(d.reverse=!0);b.attr("data-mask-clearifnotmatch")&&(d.clearIfNotMatch=!0);"true"===b.attr("data-mask-selectonfocus")&&(d.selectOnFocus=!0);if(p(b,e,d))return b.data("mask",new n(this,e,d))},p=function(b,d,e){e=e||{};var c=a(b).data("mask"),f=JSON.stringify;b=a(b).val()||a(b).text();try{return"function"===typeof d&&(d=d(b)),"object"!==typeof c||f(c.options)!==f(e)||c.mask!==d}catch(w){}},k=function(a){var b=document.createElement("div");a="on"+a;var e=a in b;e||(b.setAttribute(a,
"return;"),e="function"===typeof b[a]);return e};a.fn.mask=function(b,d){d=d||{};var e=this.selector,c=a.jMaskGlobals,f=c.watchInterval;c=d.watchInputs||c.watchInputs;var k=function(){if(p(this,b,d))return a(this).data("mask",new n(this,b,d))};a(this).each(k);e&&""!==e&&c&&(clearInterval(a.maskWatchers[e]),a.maskWatchers[e]=setInterval(function(){a(document).find(e).each(k)},f));return this};a.fn.masked=function(a){return this.data("mask").getMaskedVal(a)};a.fn.unmask=function(){clearInterval(a.maskWatchers[this.selector]);
delete a.maskWatchers[this.selector];return this.each(function(){var b=a(this).data("mask");b&&b.remove().removeData("mask")})};a.fn.cleanVal=function(){return this.data("mask").getCleanVal()};a.applyDataMask=function(b){b=b||a.jMaskGlobals.maskElements;(b instanceof a?b:a(b)).filter(a.jMaskGlobals.dataMaskAttr).each(f)};k={maskElements:"input,td,span,div",dataMaskAttr:"*[data-mask]",dataMask:!0,watchInterval:300,watchInputs:!0,keyStrokeCompensation:10,useInput:!/Chrome\/[2-4][0-9]|SamsungBrowser/.test(window.navigator.userAgent)&&
k("input"),watchDataMask:!1,byPassKeys:[9,16,17,18,36,37,38,39,40,91],translation:{0:{pattern:/\d/},9:{pattern:/\d/,optional:!0},"#":{pattern:/\d/,recursive:!0},A:{pattern:/[a-zA-Z0-9]/},S:{pattern:/[a-zA-Z]/}}};a.jMaskGlobals=a.jMaskGlobals||{};k=a.jMaskGlobals=a.extend(!0,{},k,a.jMaskGlobals);k.dataMask&&a.applyDataMask();setInterval(function(){a.jMaskGlobals.watchDataMask&&a.applyDataMask()},k.watchInterval)},window.jQuery,window.Zepto);

View file

@ -32,7 +32,6 @@
.ra-wireguard:before { .ra-wireguard:before {
font-size: 1.2rem; font-size: 1.2rem;
content: "\e900"; content: "\e900";
color: #d1d3e2;
vertical-align: middle; vertical-align: middle;
} }

View file

@ -95,7 +95,9 @@ function saveDHCPConfig($status)
if (empty($errors)) { if (empty($errors)) {
$return = updateDHCPConfig($iface,$status); $return = updateDHCPConfig($iface,$status);
} else { } else {
$status->addMessage($errors, 'danger'); foreach ($errors as $error) {
$status->addMessage($error, 'danger');
}
} }
if ($return == 1) { if ($return == 1) {
$status->addMessage('Dnsmasq configuration failed to be updated.', 'danger'); $status->addMessage('Dnsmasq configuration failed to be updated.', 'danger');
@ -118,41 +120,42 @@ function saveDHCPConfig($status)
/** /**
* Validates DHCP user input from the $_POST object * Validates DHCP user input from the $_POST object
* *
* @return string $errors * @return array $errors
*/ */
function validateDHCPInput() function validateDHCPInput()
{ {
$errors = [];
define('IFNAMSIZ', 16); define('IFNAMSIZ', 16);
$iface = $_POST['interface']; $iface = $_POST['interface'];
if (!preg_match('/^[^\s\/\\0]+$/', $iface) if (!preg_match('/^[^\s\/\\0]+$/', $iface)
|| strlen($iface) >= IFNAMSIZ || strlen($iface) >= IFNAMSIZ
) { ) {
$errors .= _('Invalid interface name.').'<br />'.PHP_EOL; $errors[] = _('Invalid interface name.');
} }
if (!filter_var($_POST['StaticIP'], FILTER_VALIDATE_IP) && !empty($_POST['StaticIP'])) { if (!filter_var($_POST['StaticIP'], FILTER_VALIDATE_IP) && !empty($_POST['StaticIP'])) {
$errors .= _('Invalid static IP address.').'<br />'.PHP_EOL; $errors[] = _('Invalid static IP address.');
} }
if (!filter_var($_POST['SubnetMask'], FILTER_VALIDATE_IP) && !empty($_POST['SubnetMask'])) { if (!filter_var($_POST['SubnetMask'], FILTER_VALIDATE_IP) && !empty($_POST['SubnetMask'])) {
$errors .= _('Invalid subnet mask.').'<br />'.PHP_EOL; $errors[] = _('Invalid subnet mask.');
} }
if (!filter_var($_POST['DefaultGateway'], FILTER_VALIDATE_IP) && !empty($_POST['DefaultGateway'])) { if (!filter_var($_POST['DefaultGateway'], FILTER_VALIDATE_IP) && !empty($_POST['DefaultGateway'])) {
$errors .= _('Invalid default gateway.').'<br />'.PHP_EOL; $errors[] = _('Invalid default gateway.');
} }
if (($_POST['dhcp-iface'] == "1")) { if (($_POST['dhcp-iface'] == "1")) {
if (!filter_var($_POST['RangeStart'], FILTER_VALIDATE_IP) && !empty($_POST['RangeStart'])) { if (!filter_var($_POST['RangeStart'], FILTER_VALIDATE_IP) && !empty($_POST['RangeStart'])) {
$errors .= _('Invalid DHCP range start.').'<br />'.PHP_EOL; $errors[] = _('Invalid DHCP range start.');
} }
if (!filter_var($_POST['RangeEnd'], FILTER_VALIDATE_IP) && !empty($_POST['RangeEnd'])) { if (!filter_var($_POST['RangeEnd'], FILTER_VALIDATE_IP) && !empty($_POST['RangeEnd'])) {
$errors .= _('Invalid DHCP range end.').'<br />'.PHP_EOL; $errors[] = _('Invalid DHCP range end.');
} }
if (!ctype_digit($_POST['RangeLeaseTime']) && $_POST['RangeLeaseTimeUnits'] !== 'i') { if (!ctype_digit($_POST['RangeLeaseTime']) && $_POST['RangeLeaseTimeUnits'] !== 'i') {
$errors .= _('Invalid DHCP lease time, not a number.').'<br />'.PHP_EOL; $errors[] = _('Invalid DHCP lease time, not a number.');
} }
if (!in_array($_POST['RangeLeaseTimeUnits'], array('m', 'h', 'd', 'i'))) { if (!in_array($_POST['RangeLeaseTimeUnits'], array('m', 'h', 'd', 'i'))) {
$errors .= _('Unknown DHCP lease time unit.').'<br />'.PHP_EOL; $errors[] = _('Unknown DHCP lease time unit.');
} }
if ($_POST['Metric'] !== '' && !ctype_digit($_POST['Metric'])) { if ($_POST['Metric'] !== '' && !ctype_digit($_POST['Metric'])) {
$errors .= _('Invalid metric value, not a number.').'<br />'.PHP_EOL; $errors[] = _('Invalid metric value, not a number.');
} }
} }
return $errors; return $errors;

View file

@ -435,18 +435,25 @@ function GetDistString($input, $string, $offset, $separator)
} }
/** /**
* * Parses a configuration file
* @param array $arrConfig * Options and values are mapped with "=" characters
* Optional $wg flag is used for parsing WireGuard .conf files
* @param array $arrConfig
* @param boolean $wg
* @return $config * @return $config
*/ */
function ParseConfig($arrConfig) function ParseConfig($arrConfig, $wg = false)
{ {
$config = array(); $config = array();
foreach ($arrConfig as $line) { foreach ($arrConfig as $line) {
$line = trim($line); $line = trim($line);
if ($line == "" || $line[0] == "#") { if ($line == "" || $line[0] == "#") {
$config[$option] = null; if ($wg) {
continue; $config[$option] = null;
continue;
} else {
continue;
}
} }
if (strpos($line, "=") !== false) { if (strpos($line, "=") !== false) {

View file

@ -10,15 +10,15 @@
<img src="app/img/raspAP-logo.php" class="navbar-logo" width="64" height="64"> <img src="app/img/raspAP-logo.php" class="navbar-logo" width="64" height="64">
</div> </div>
<div class="col-xs ml-2"> <div class="col-xs ml-2">
<div class="ml-1">Status</div> <div class="ml-1 sb-status">Status</div>
<div class="info-item-xs"><span class="icon"> <div class="info-item-xs"><span class="icon">
<i class="fas fa-circle <?php echo ($hostapd_led); ?>"></i></span> <?php echo _("Hotspot").' '. _($hostapd_status); ?> <i class="fas fa-circle <?php echo ($hostapd_led); ?>"></i></span> <?php echo _("Hotspot").' '. _($hostapd_status); ?>
</div> </div>
<div class="info-item-xs"><span class="icon"> <div class="info-item-xs"><span class="icon">
<i class="fas fa-circle <?php echo ($memused_led); ?>"></i></span> <?php echo _("Memory Use").': '. htmlspecialchars(strval($memused), ENT_QUOTES); ?>% <i class="fas fa-circle <?php echo ($memused_led); ?>"></i></span> <?php echo _("Mem Use").': '. htmlspecialchars(strval($memused), ENT_QUOTES); ?>%
</div> </div>
<div class="info-item-xs"><span class="icon"> <div class="info-item-xs"><span class="icon">
<i class="fas fa-circle <?php echo ($cputemp_led); ?>"></i></span> <?php echo _("CPU Temp").': '. htmlspecialchars($cputemp, ENT_QUOTES); ?>°C <i class="fas fa-circle <?php echo ($cputemp_led); ?>"></i></span> <?php echo _("CPU").': '. htmlspecialchars($cputemp, ENT_QUOTES); ?>°C
</div> </div>
</div> </div>
</div> </div>
@ -57,7 +57,7 @@
<?php endif; ?> <?php endif; ?>
<?php if (RASPI_WIREGUARD_ENABLED) : ?> <?php if (RASPI_WIREGUARD_ENABLED) : ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="wg_conf"><span class="ra-wireguard mr-2"></span><span class="nav-label"><?php echo _("WireGuard"); ?></a> <a class="nav-link" href="wg_conf"><i class="ra-wireguard mr-2"></i><span class="nav-label"><?php echo _("WireGuard"); ?></a>
</li> </li>
<?php endif; ?> <?php endif; ?>
<?php if (RASPI_VPN_PROVIDER_ENABLED) : ?> <?php if (RASPI_VPN_PROVIDER_ENABLED) : ?>

View file

@ -46,10 +46,10 @@ if ($cputemp > 70) {
// hostapd status // hostapd status
$hostapd = $system->hostapdStatus(); $hostapd = $system->hostapdStatus();
if ($hostapd[0] ==1) { if ($hostapd[0] ==1) {
$hostapd_status = "active"; $hostapd_status = "up";
$hostapd_led = "service-status-up"; $hostapd_led = "service-status-up";
} else { } else {
$hostapd_status = "inactive"; $hostapd_status = "down";
$hostapd_led = "service-status-down"; $hostapd_led = "service-status-down";
} }

View file

@ -8,6 +8,7 @@ require_once 'config.php';
function DisplayWireGuardConfig() function DisplayWireGuardConfig()
{ {
$status = new \RaspAP\Messages\StatusMessage; $status = new \RaspAP\Messages\StatusMessage;
$parseFlag = true;
if (!RASPI_MONITOR_ENABLED) { if (!RASPI_MONITOR_ENABLED) {
$optRules = $_POST['wgRules']; $optRules = $_POST['wgRules'];
$optConf = $_POST['wgCnfOpt']; $optConf = $_POST['wgCnfOpt'];
@ -37,7 +38,7 @@ function DisplayWireGuardConfig()
// fetch server config // fetch server config
exec('sudo cat '. RASPI_WIREGUARD_CONFIG, $return); exec('sudo cat '. RASPI_WIREGUARD_CONFIG, $return);
$conf = ParseConfig($return); $conf = ParseConfig($return, $parseFlag);
$wg_srvpubkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-public.key', $return); $wg_srvpubkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-public.key', $return);
$wg_srvport = ($conf['ListenPort'] == '') ? getDefaultNetValue('wireguard','server','ListenPort') : $conf['ListenPort']; $wg_srvport = ($conf['ListenPort'] == '') ? getDefaultNetValue('wireguard','server','ListenPort') : $conf['ListenPort'];
$wg_srvipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','server','Address') : $conf['Address']; $wg_srvipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','server','Address') : $conf['Address'];
@ -49,7 +50,7 @@ function DisplayWireGuardConfig()
// fetch client config // fetch client config
exec('sudo cat '. RASPI_WIREGUARD_PATH.'client.conf', $preturn); exec('sudo cat '. RASPI_WIREGUARD_PATH.'client.conf', $preturn);
$conf = ParseConfig($preturn); $conf = ParseConfig($preturn, $parseFlag);
$wg_pipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','peer','Address') : $conf['Address']; $wg_pipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','peer','Address') : $conf['Address'];
$wg_plistenport = ($conf['ListenPort'] == '') ? getDefaultNetValue('wireguard','peer','ListenPort') : $conf['ListenPort']; $wg_plistenport = ($conf['ListenPort'] == '') ? getDefaultNetValue('wireguard','peer','ListenPort') : $conf['ListenPort'];
$wg_pendpoint = ($conf['Endpoint'] == '') ? getDefaultNetValue('wireguard','peer','Endpoint') : $conf['Endpoint']; $wg_pendpoint = ($conf['Endpoint'] == '') ? getDefaultNetValue('wireguard','peer','Endpoint') : $conf['Endpoint'];

View file

@ -148,6 +148,9 @@ initializeApp();
<!-- SB-Admin-2 JavaScript --> <!-- SB-Admin-2 JavaScript -->
<script src="dist/sb-admin-2/js/sb-admin-2.js"></script> <script src="dist/sb-admin-2/js/sb-admin-2.js"></script>
<!-- jQuery Mask plugin -->
<script src="dist/jquery-mask/jquery.mask.min.js"></script>
<!-- Custom RaspAP JS --> <!-- Custom RaspAP JS -->
<script src="app/js/custom.js"></script> <script src="app/js/custom.js"></script>

View file

@ -37,11 +37,11 @@ msgstr "WiFi client"
msgid "Hotspot" msgid "Hotspot"
msgstr "Hotspot" msgstr "Hotspot"
msgid "Memory Use" msgid "Mem Use"
msgstr "Memory Use" msgstr "Mem Use"
msgid "CPU Temp" msgid "CPU"
msgstr "CPU Temp" msgstr "CPU"
msgid "Networking" msgid "Networking"
msgstr "Networking" msgstr "Networking"
@ -98,6 +98,18 @@ msgstr "New password"
msgid "Repeat new password" msgid "Repeat new password"
msgstr "Repeat new password" msgstr "Repeat new password"
msgid "Please provide a valid username."
msgstr "Please provide a valid username."
msgid "Please enter your old password."
msgstr "Please enter your old password."
msgid "Please enter a new password."
msgstr "Please enter a new password."
msgid "Please re-enter your new password."
msgstr "Please re-enter your new password."
#: includes/configure_client.php #: includes/configure_client.php
msgid "Client settings" msgid "Client settings"
msgstr "Client settings" msgstr "Client settings"
@ -473,6 +485,57 @@ msgstr "Disable wpa_supplicant dhcp hook for this interface"
msgid "If you manage wireless connections with wpa_supplicant itself, the hook may create unwanted connection events. This option disables the hook." msgid "If you manage wireless connections with wpa_supplicant itself, the hook may create unwanted connection events. This option disables the hook."
msgstr "If you manage wireless connections with wpa_supplicant itself, the hook may create unwanted connection events. This option disables the hook." msgstr "If you manage wireless connections with wpa_supplicant itself, the hook may create unwanted connection events. This option disables the hook."
msgid "Please provide a valid IP Address."
msgstr "Please provide a valid IP Address."
msgid "Please provide a valid Default gateway."
msgstr "Please provide a valid Default gateway."
msgid "Please provide a valid Default gateway."
msgstr "Please provide a valid Default gateway."
msgid "Please provide a valid Starting IP Address."
msgstr "Please provide a valid Starting IP Address."
msgid "Please provide a valid Ending IP Address."
msgstr "Please provide a valid Ending IP Address."
msgid "Please provide a valid Lease Time."
msgstr "Please provide a valid Lease Time."
msgid "Invalid interface name."
msgstr "Invalid interface name."
msgid "Invalid interface name."
msgstr "Invalid interface name."
msgid "Invalid static IP address."
msgstr "Invalid static IP address."
msgid "Invalid default gateway."
msgstr "Invalid default gateway."
msgid "Invalid DHCP range start."
msgstr "Invalid DHCP range start."
msgid "Invalid DHCP range end."
msgstr "Invalid DHCP range end."
msgid "Invalid DHCP lease time, not a number."
msgstr "Invalid DHCP lease time, not a number."
msgid "Unknown DHCP lease time unit."
msgstr "Unknown DHCP lease time unit."
msgid "Invalid metric value, not a number."
msgstr "Invalid metric value, not a number."
msgid "DHCP configuration for %s added."
msgstr "DHCP configuration for %s added."
msgid "DHCP configuration for %s updated."
msgstr "DHCP configuration for %s updated."
#: includes/hostapd.php #: includes/hostapd.php
msgid "Basic" msgid "Basic"
msgstr "Basic" msgstr "Basic"
@ -727,6 +790,12 @@ msgstr "raw output"
msgid "Setting wireless regulatory domain to %s" msgid "Setting wireless regulatory domain to %s"
msgstr "Setting wireless regulatory domain to %s" msgstr "Setting wireless regulatory domain to %s"
msgid "Please provide a valid SSID."
msgstr "Please provide a valid SSID."
msgid "Please provide a valid PSK."
msgstr "Please provide a valid PSK."
#: includes/system.php #: includes/system.php
msgid "System Information" msgid "System Information"
msgstr "System Information" msgstr "System Information"

View file

@ -11,22 +11,28 @@
<div class="card-body"> <div class="card-body">
<?php $status->showMessages(); ?> <?php $status->showMessages(); ?>
<h4><?php echo _("Authentication settings") ;?></h4> <h4><?php echo _("Authentication settings") ;?></h4>
<form role="form" action="auth_conf" method="POST"> <form role="form" action="auth_conf" method="POST" class="needs-validation" novalidate>
<?php echo CSRFTokenFieldTag() ?> <?php echo CSRFTokenFieldTag() ?>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="username"><?php echo _("Username"); ?></label> <label for="username"><?php echo _("Username"); ?></label>
<input type="text" class="form-control" name="username" value="<?php echo htmlspecialchars($username, ENT_QUOTES); ?>"/> <input type="text" class="form-control" name="username" value="<?php echo htmlspecialchars($username, ENT_QUOTES); ?>" required />
<div class="invalid-feedback">
<?php echo _("Please provide a valid username."); ?>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<div class="mb-2"><?php echo _("Old password"); ?></div> <div class="mb-2"><?php echo _("Old password"); ?></div>
<div class="input-group"> <div class="input-group">
<input type="password" class="form-control" name="oldpass"/> <input type="password" class="form-control" name="oldpass" required />
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-light js-toggle-password" type="button" data-target="[name=oldpass]" data-toggle-with="fas fa-eye-slash"><i class="fas fa-eye mx-2"></i></button> <button class="btn btn-light js-toggle-password" type="button" data-target="[name=oldpass]" data-toggle-with="fas fa-eye-slash"><i class="fas fa-eye mx-2"></i></button>
</div> </div>
<div class="invalid-feedback">
<?php echo _("Please enter your old password."); ?>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -34,10 +40,13 @@
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<div class="mb-2"><?php echo _("New password"); ?></div> <div class="mb-2"><?php echo _("New password"); ?></div>
<div class="input-group"> <div class="input-group">
<input type="password" class="form-control" name="newpass"/> <input type="password" class="form-control" name="newpass" required />
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-light js-toggle-password" type="button" data-target="[name=newpass]" data-toggle-with="fas fa-eye-slash"><i class="fas fa-eye mx-2"></i></button> <button class="btn btn-light js-toggle-password" type="button" data-target="[name=newpass]" data-toggle-with="fas fa-eye-slash"><i class="fas fa-eye mx-2"></i></button>
</div> </div>
<div class="invalid-feedback">
<?php echo _("Please enter a new password."); ?>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -45,10 +54,13 @@
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<div class="mb-2"><?php echo _("Repeat new password"); ?></div> <div class="mb-2"><?php echo _("Repeat new password"); ?></div>
<div class="input-group"> <div class="input-group">
<input type="password" class="form-control" name="newpassagain"/> <input type="password" class="form-control" name="newpassagain" required />
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-light js-toggle-password" type="button" data-target="[name=newpassagain]" data-toggle-with="fas fa-eye-slash"><i class="fas fa-eye mx-2"></i></button> <button class="btn btn-light js-toggle-password" type="button" data-target="[name=newpassagain]" data-toggle-with="fas fa-eye-slash"><i class="fas fa-eye mx-2"></i></button>
</div> </div>
<div class="invalid-feedback">
<?php echo _("Please re-enter your new password."); ?>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -29,7 +29,7 @@
<div class="card-body"> <div class="card-body">
<?php $status->showMessages(); ?> <?php $status->showMessages(); ?>
<form method="POST" action="dhcpd_conf" class="js-dhcp-settings-form"> <form method="POST" action="dhcpd_conf" class="js-dhcp-settings-form needs-validation" novalidate>
<?php echo CSRFTokenFieldTag() ?> <?php echo CSRFTokenFieldTag() ?>
<!-- Nav tabs --> <!-- Nav tabs -->

View file

@ -35,23 +35,32 @@
<h5 class="mt-1">Static IP options</h5> <h5 class="mt-1">Static IP options</h5>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6" required>
<label for="code"><?php echo _("IP Address"); ?></label> <label for="code"><?php echo _("IP Address"); ?></label>
<input type="text" class="form-control" id="txtipaddress" name="StaticIP" /> <input type="text" class="form-control ip_address" id="txtipaddress" name="StaticIP" maxlength="15" />
<div class="invalid-feedback">
<?php echo _("Please provide a valid IP Address."); ?>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code"><?php echo _("Subnet Mask"); ?></label> <label for="code"><?php echo _("Subnet Mask"); ?></label>
<input type="text" class="form-control" id="txtsubnetmask" name="SubnetMask" /> <input type="text" class="form-control ip_address" id="txtsubnetmask" name="SubnetMask" maxlength="15" />
<div class="invalid-feedback">
<?php echo _("Please provide a valid Subnet mask."); ?>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code"><?php echo _("Default gateway"); ?></label> <label for="code"><?php echo _("Default gateway"); ?></label>
<input type="text" class="form-control" id="txtgateway" name="DefaultGateway" /> <input type="text" class="form-control ip_address" id="txtgateway" name="DefaultGateway" maxlength="15" />
<div class="invalid-feedback">
<?php echo _("Please provide a valid Default gateway."); ?>
</div>
</div> </div>
</div> </div>
@ -98,14 +107,20 @@
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code"><?php echo _("Starting IP Address"); ?></label> <label for="code"><?php echo _("Starting IP Address"); ?></label>
<input type="text" class="form-control" id="txtrangestart" name="RangeStart" /> <input type="text" class="form-control ip_address" id="txtrangestart" name="RangeStart" maxlength="15" />
<div class="invalid-feedback">
<?php echo _("Please provide a valid Starting IP Address."); ?>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="code"><?php echo _("Ending IP Address"); ?></label> <label for="code"><?php echo _("Ending IP Address"); ?></label>
<input type="text" class="form-control" id="txtrangeend" name="RangeEnd" /> <input type="text" class="form-control ip_address" id="txtrangeend" name="RangeEnd" maxlength="15" />
<div class="invalid-feedback">
<?php echo _("Please provide a valid Ending IP Address."); ?>
</div>
</div> </div>
</div> </div>
@ -113,6 +128,9 @@
<div class="form-group col-xs-3 col-sm-3"> <div class="form-group col-xs-3 col-sm-3">
<label for="code"><?php echo _("Lease Time"); ?></label> <label for="code"><?php echo _("Lease Time"); ?></label>
<input type="text" class="form-control" id="txtrangeleasetime" name="RangeLeaseTime" /> <input type="text" class="form-control" id="txtrangeleasetime" name="RangeLeaseTime" />
<div class="invalid-feedback">
<?php echo _("Please provide a valid Lease Time."); ?>
</div>
</div> </div>
<div class="col-xs-3 col-sm-3"> <div class="col-xs-3 col-sm-3">
<label for="code"><?php echo _("Interval"); ?></label> <label for="code"><?php echo _("Interval"); ?></label>
@ -122,6 +140,9 @@
<option value="d"><?php echo _("Day(s)"); ?></option> <option value="d"><?php echo _("Day(s)"); ?></option>
<option value="i"><?php echo _("Infinite"); ?></option> <option value="i"><?php echo _("Infinite"); ?></option>
</select> </select>
<div class="invalid-feedback">
<?php echo _("Please provide a valid Interval."); ?>
</div>
</div> </div>
</div> </div>

View file

@ -33,7 +33,7 @@
<input type="text" name="mac" value="" placeholder="<?php echo _("MAC address") ?>" class="form-control" autofocus="autofocus"> <input type="text" name="mac" value="" placeholder="<?php echo _("MAC address") ?>" class="form-control" autofocus="autofocus">
</div> </div>
<div class="col-md-3 col-xs-3"> <div class="col-md-3 col-xs-3">
<input type="text" name="ip" value="" placeholder="<?php echo _("IP address") ?>" class="form-control"> <input type="text" name="ip" value="" placeholder="<?php echo _("IP address") ?>" class="form-control ip_address" maxlength="15">
</div> </div>
<div class="col-md-3 col-xs-3"> <div class="col-md-3 col-xs-3">
<input type="text" name="comment" value="" placeholder="<?php echo _("Optional comment") ?>" class="form-control"> <input type="text" name="comment" value="" placeholder="<?php echo _("Optional comment") ?>" class="form-control">

View file

@ -49,7 +49,7 @@
<div class="card-body"> <div class="card-body">
<?php $status->showMessages(); ?> <?php $status->showMessages(); ?>
<form role="form" action="hostapd_conf" method="POST"> <form role="form" action="hostapd_conf" method="POST" class="needs-validation" novalidate>
<?php echo CSRFTokenFieldTag() ?> <?php echo CSRFTokenFieldTag() ?>
<!-- Nav tabs --> <!-- Nav tabs -->

View file

@ -7,9 +7,12 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <div class="form-group col-md-6" required>
<label for="txtssid"><?php echo _("SSID"); ?></label> <label for="txtssid"><?php echo _("SSID"); ?></label>
<input type="text" id="txtssid" class="form-control" name="ssid" value="<?php echo htmlspecialchars($arrConfig['ssid'], ENT_QUOTES); ?>" /> <input type="text" id="txtssid" class="form-control" name="ssid" value="<?php echo htmlspecialchars($arrConfig['ssid'], ENT_QUOTES); ?>" required />
<div class="invalid-feedback">
<?php echo _("Please provide a valid SSID."); ?>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">

View file

@ -12,10 +12,13 @@
</div> </div>
<label for="txtwpapassphrase"><?php echo _("PSK"); ?></label> <label for="txtwpapassphrase"><?php echo _("PSK"); ?></label>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" id="txtwpapassphrase" name="wpa_passphrase" value="<?php echo htmlspecialchars($arrConfig['wpa_passphrase'], ENT_QUOTES); ?>" /> <input type="text" class="form-control" id="txtwpapassphrase" name="wpa_passphrase" value="<?php echo htmlspecialchars($arrConfig['wpa_passphrase'], ENT_QUOTES); ?>" required />
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" id="gen_wpa_passphrase"><i class="fas fa-magic"></i></button> <button class="btn btn-outline-secondary" type="button" id="gen_wpa_passphrase"><i class="fas fa-magic"></i></button>
</div> </div>
<div class="invalid-feedback">
<?php echo _("Please provide a valid PSK."); ?>
</div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">