From dc8d739eaa40696867dff5751f66b299709521f4 Mon Sep 17 00:00:00 2001 From: Nigel Kukard <nkukard@lbsd.net> Date: Fri, 20 Sep 2013 20:25:23 +0000 Subject: [PATCH] Major interface +code reworking & cosmetic fixes --- .../plugins/webserver/pages/configmanager.pm | 340 ++++++++++++------ .../plugins/webserver/pages/limits.pm | 298 +++++++++++---- .../plugins/webserver/webserver.pm | 8 +- 3 files changed, 470 insertions(+), 176 deletions(-) diff --git a/opentrafficshaper/plugins/webserver/pages/configmanager.pm b/opentrafficshaper/plugins/webserver/pages/configmanager.pm index 7a35700..0baa423 100644 --- a/opentrafficshaper/plugins/webserver/pages/configmanager.pm +++ b/opentrafficshaper/plugins/webserver/pages/configmanager.pm @@ -1,6 +1,6 @@ # OpenTrafficShaper webserver module: configmanager page # Copyright (C) 2007-2013, AllWorldIT -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -37,7 +37,7 @@ use URI::Escape; use opentrafficshaper::logger; use opentrafficshaper::plugins; -use opentrafficshaper::utils qw( parseFormContent isUsername isIP isNumber prettyUndef ); +use opentrafficshaper::utils qw( parseFormContent parseURIQuery isUsername isIP isNumber prettyUndef ); use opentrafficshaper::plugins::configmanager qw( getOverrides getOverride getTrafficClasses getTrafficClassName isTrafficClassValid ); @@ -49,7 +49,7 @@ my $menu = { 'All Overrides' => '', }, 'Admin' => { - 'Add Override' => 'add', + 'Add Override' => 'override-add', }, }; @@ -95,12 +95,12 @@ EOF next; } - my $keyEscaped = uri_escape($override->{'Key'}); + my $idEscaped = uri_escape($override->{'ID'}); my $friendlyNameEncoded = prettyUndef(encode_entities($override->{'FriendlyName'})); my $usernameEncoded = prettyUndef(encode_entities($override->{'Username'})); my $ipAddress = prettyUndef($override->{'IP'}); - my $expiresStr = DateTime->from_epoch( epoch => $override->{'Expires'} )->iso8601(); + my $expiresStr = ($override->{'Expires'} > 0) ? DateTime->from_epoch( epoch => $override->{'Expires'} )->iso8601() : '-never-'; my $classStr = prettyUndef(getTrafficClassName($override->{'ClassID'})); my $cirStr = sprintf('%s/%s',prettyUndef($override->{'TrafficLimitTx'}),prettyUndef($override->{'TrafficLimitRx'})); @@ -120,8 +120,8 @@ EOF <td>$cirStr</td> <td>$limitStr</td> <td> - <a href="/configmanager/override-edit?key=$keyEscaped"><span class="glyphicon glyphicon-wrench" /></a> - <a href="/configmanager/override-remove?key=$keyEscaped"><span class="glyphicon glyphicon-remove" /></a> + <a href="/configmanager/override-edit?oid=$idEscaped"><span class="glyphicon glyphicon-wrench" /></a> + <a href="/configmanager/override-remove?oid=$idEscaped"><span class="glyphicon glyphicon-remove" /></a> </td> </tr> EOF @@ -147,8 +147,8 @@ EOF } -# Add action -sub add +# Add/edit action +sub override_addedit { my ($kernel,$globals,$client_session_id,$request) = @_; @@ -156,85 +156,135 @@ sub add # Setup our environment my $logger = $globals->{'logger'}; - # Errors to display + # Errors to display above the form my @errors; - # Form items - my $params = { - 'inputFriendlyName' => undef, - 'inputUsername' => undef, - 'inputIP' => undef, - 'inputTrafficClass' => undef, - 'inputTrafficClassEnabled' => undef, - 'inputLimitTx' => undef, - 'inputLimitTxEnabled' => undef, - 'inputLimitTxBurst' => undef, - 'inputLimitTxBurstEnabled' => undef, - 'inputLimitRx' => undef, - 'inputLimitRxEnabled' => undef, - 'inputLimitRxBurst' => undef, - 'inputLimitRxBurstEnabled' => undef, - 'inputExpires' => undef, - 'inputExpiresModifier' => undef, - 'inputNotes' => undef, - }; + + # Items for our form... + my @formElements = qw( + FriendlyName + Username IP + ClassID + TrafficLimitTx TrafficLimitTxBurst + TrafficLimitRx TrafficLimitRxBurst + Expires inputExpires.modifier + Notes + ); + my @formElementCheckboxes = qw( + ClassID + TrafficLimitTx TrafficLimitTxBurst + TrafficLimitRx TrafficLimitRxBurst + ); + + # Title of the form, by default its an add form + my $formType = "Add"; + my $formNoEdit = ""; + # Form data + my $formData; + + # + # Here is where we going to try load data we already have + # # If this is a form try parse it if ($request->method eq "POST") { # Parse form data - $params = parseFormContent($request->content); + $formData = parseFormContent($request->content); # If user pressed cancel, redirect - if (defined($params->{'cancel'})) { + if (defined($formData->{'cancel'})) { # Redirects to default page - return (HTTP_TEMPORARY_REDIRECT,'limits'); + return (HTTP_TEMPORARY_REDIRECT,'configmanager'); } - # Check POST data - my $friendlyName = $params->{'inputFriendlyName'}; + # Maybe we were given an override key as a parameter? this would be an edit form + } elsif ($request->method eq "GET") { + # Parse GET data + my $queryParams = parseURIQuery($request); + # We need a ID first of all... + if (defined($queryParams->{'oid'})) { + # Check if we get some data back when pulling the override from the backend + if (defined($formData = getOverride($queryParams->{'oid'}))) { + # Setup our checkboxes + foreach my $checkbox (@formElementCheckboxes) { + if (defined($formData->{$checkbox})) { + $formData->{"input$checkbox.enabled"} = "on"; + } + } + + # Work out expires modifier + # XXX - TODO + # If we didn't get any data, then something went wrong + } else { + my $encodedID = encode_entities($queryParams->{'oid'}); + push(@errors,"Override data could not be loaded using oid '$encodedID'"); + } + # Lastly if we were given a oid, this is actually an edit + $formType = "Edit"; + $formNoEdit = "readonly"; + + # Woops ... no query string? + } elsif (%{$queryParams} > 0) { + push(@errors,"No override oid in query string!"); + $formType = "Edit"; + $formNoEdit = "readonly"; + } + } + + + # + # If we already have data, lets check how valid it is... + # + + # We only do this if we have hash elements + if (ref($formData) eq "HASH") { + my $friendlyName = $formData->{'FriendlyName'}; + if (!defined($friendlyName)) { + push(@errors,"Friendly name must be specified"); + } # Make sure we have at least the username or IP - my $username = isUsername($params->{'inputUsername'}); - my $ipAddress = isIP($params->{'inputIP'}); + my $username = isUsername($formData->{'Username'}); + my $ipAddress = isIP($formData->{'IP'}); if (!defined($username) && !defined($ipAddress)) { push(@errors,"IP Address and/or Username must be specified"); } # If the traffic class is ticked, process it - my $trafficClass; - if (defined($params->{'inputTrafficClassEnabled'})) { - if (!defined($trafficClass = isTrafficClassValid($params->{'inputTrafficClass'}))) { + my $classID; + if (defined($formData->{'inputClassID.enabled'})) { + if (!defined($classID = isTrafficClassValid($formData->{'ClassID'}))) { push(@errors,"Traffic class is not valid"); } } - # Check TrafficLimitTx + # Check traffic limits my $trafficLimitTx; - if (defined($params->{'inputTrafficLimitTxEnabled'})) { - if (!defined($trafficLimitTx = isNumber($params->{'inputLimitTx'}))) { + if (defined($formData->{'inputTrafficLimitTx.enabled'})) { + if (!defined($trafficLimitTx = isNumber($formData->{'TrafficLimitTx'}))) { push(@errors,"Download CIR is not valid"); } } my $trafficLimitTxBurst; - if (defined($params->{'inputTrafficLimitTxBurstEnabled'})) { - if (!defined($trafficLimitTxBurst = isNumber($params->{'inputLimitTxBurst'}))) { + if (defined($formData->{'inputTrafficLimitTxBurst.enabled'})) { + if (!defined($trafficLimitTxBurst = isNumber($formData->{'TrafficLimitTxBurst'}))) { push(@errors,"Download limit is not valid"); } } # Check TrafficLimitRx my $trafficLimitRx; - if (defined($params->{'inputTrafficLimitRxEnabled'})) { - if (!defined($trafficLimitRx = isNumber($params->{'inputLimitRx'}))) { + if (defined($formData->{'inputTrafficLimitRx.enabled'})) { + if (!defined($trafficLimitRx = isNumber($formData->{'TrafficLimitRx'}))) { push(@errors,"Upload CIR is not valid"); } } my $trafficLimitRxBurst; - if (defined($params->{'inputTrafficLimitRxBurstEnabled'})) { - if (!defined($trafficLimitRxBurst = isNumber($params->{'inputLimitRxBurst'}))) { + if (defined($formData->{'inputTrafficLimitRxBurst.enabled'})) { + if (!defined($trafficLimitRxBurst = isNumber($formData->{'TrafficLimitRxBurst'}))) { push(@errors,"Upload limit is not valid"); } } # Check that we actually have something to override if ( - !defined($trafficClass) && + !defined($classID) && !defined($trafficLimitTx) && !defined($trafficLimitTxBurst) && !defined($trafficLimitRx) && !defined($trafficLimitRxBurst) ) { @@ -242,43 +292,48 @@ sub add } my $expires = 0; - if (defined($params->{'inputExpires'}) && $params->{'inputExpires'} ne "") { - if (!defined($expires = isNumber($params->{'inputExpires'}))) { + if (defined($formData->{'Expires'}) && $formData->{'Expires'} ne "") { + if (!defined($expires = isNumber($formData->{'Expires'}))) { push(@errors,"Expires value is not valid"); # Check the modifier } else { # Check if its defined - if (defined($params->{'inputExpiresModifier'}) && $params->{'inputExpiresModifier'} ne "") { + if (defined($formData->{'inputExpires.modifier'}) && $formData->{'inputExpires.modifier'} ne "") { # Minutes - if ($params->{'inputExpiresModifier'} eq "m") { + if ($formData->{'inputExpires.modifier'} eq "m") { $expires *= 60; # Hours - } elsif ($params->{'inputExpiresModifier'} eq "h") { + } elsif ($formData->{'inputExpires.modifier'} eq "h") { $expires *= 3600; # Days - } elsif ($params->{'inputExpiresModifier'} eq "d") { + } elsif ($formData->{'inputExpires.modifier'} eq "d") { $expires *= 86400; } else { push(@errors,"Expires modifier is not valid"); } } - # Set right time for expiry - $expires += time(); + # Base the expiry off now, plus the expiry time + if ($expires > 0) { + $expires += time(); + } } } # Grab notes - my $notes = $params->{'inputNotes'}; + my $notes = $formData->{'Notes'}; + # + # Process change if this is a POST and there are no errors + # # If there are no errors we need to push this override - if (!@errors) { + if (!@errors && $request->method eq "POST") { # Build override my $override = { 'FriendlyName' => $friendlyName, 'Username' => $username, 'IP' => $ipAddress, 'GroupID' => 1, - 'ClassID' => $trafficClass, + 'ClassID' => $classID, 'TrafficLimitTx' => $trafficLimitTx, 'TrafficLimitTxBurst' => $trafficLimitTxBurst, 'TrafficLimitRx' => $trafficLimitRx, @@ -295,7 +350,7 @@ sub add prettyUndef($username), prettyUndef($ipAddress), "", - prettyUndef($trafficClass), + prettyUndef($classID), prettyUndef($trafficLimitTx), prettyUndef($trafficLimitRx), prettyUndef($trafficLimitTxBurst), @@ -306,26 +361,29 @@ sub add } } + + # + # Sanitize all data we going to be using + # + # Handle checkboxes first and a little differently - foreach my $item ( - "inputTrafficClassEnabled", - "inputLimitTxEnabled","inputLimitTxBurstEnabled", - "inputLimitRxEnabled", "inputLimitRxBurstEnabled" - ) { - $params->{$item} = defined($params->{$item}) ? "checked" : ""; + foreach my $item (@formElementCheckboxes) { + $formData->{"input$item.enabled"} = defined($formData->{"input$item.enabled"}) ? "checked" : ""; } # Sanitize params if we need to - foreach my $item (keys %{$params}) { - $params->{$item} = defined($params->{$item}) ? encode_entities($params->{$item}) : ""; + foreach my $item (@formElements) { + $formData->{$item} = defined($formData->{$item}) ? encode_entities($formData->{$item}) : ""; } # Build content my $content = ""; + # # Form header + # $content .=<<EOF; <form role="form" method="post"> - <legend>Add Override</legend> + <legend>$formType Override</legend> EOF # Spit out errors if we have any @@ -338,54 +396,63 @@ EOF # Generate traffic class list my $trafficClasses = getTrafficClasses(); my $trafficClassStr = ""; - foreach my $classID (keys %{$trafficClasses}) { + foreach my $classID (sort keys %{$trafficClasses}) { # Process selections nicely my $selected = ""; - if ($params->{'inputTrafficClass'} ne "" && $params->{'inputTrafficClass'} eq $classID) { + if ($formData->{'ClassID'} ne "" && $formData->{'ClassID'} eq $classID) { $selected = "selected"; } # And build the options $trafficClassStr .= '<option value="'.$classID.'" '.$selected.'>'.$trafficClasses->{$classID}.'</option>'; } - # Header + # Make expires look nicer + my $expiresStr = ""; + if (defined($formData->{'Expires'}) && $formData->{'Expires'} > 0) { + $expiresStr = $formData->{'Expires'}; + } + + + # + # Page content + # $content .=<<EOF; <div class="form-group"> - <label for="inputFriendlyName" class="col-lg-2 control-label">FriendlyName</label> + <label for="FriendlyName" class="col-lg-2 control-label">FriendlyName</label> <div class="row"> <div class="col-lg-4"> <div class="input-group"> - <input name="inputFriendlyName" type="text" placeholder="Friendly Name" class="form-control" value="$params->{'inputFriendlyName'}" /> + <input name="FriendlyName" type="text" placeholder="Friendly Name" class="form-control" value="$formData->{'FriendlyName'}" /> <span class="input-group-addon">*</span> </div> </div> </div> </div> <div class="form-group"> - <label for="inputUsername" class="col-lg-2 control-label">Username</label> + <label for="Username" class="col-lg-2 control-label">Username</label> <div class="row"> <div class="col-lg-4"> - <input name="inputUsername" type="text" placeholder="Username To Override" class="form-control" value="$params->{'inputUsername'}" /> + <input name="Username" type="text" placeholder="Username To Override" class="form-control" value="$formData->{'Username'}" $formNoEdit/> </div> </div> </div> <div class="form-group"> - <label for="inputIP" class="col-lg-2 control-label">IP Address</label> + <label for="IP" class="col-lg-2 control-label">IP Address</label> <div class="row"> <div class="col-lg-4"> - <input name="inputIP" type="text" placeholder="And/Or IP Address To Override" class="form-control" value="$params->{'inputIP'}" /> + <input name="IP" type="text" placeholder="And/Or IP Address To Override" class="form-control" value="$formData->{'IP'}" $formNoEdit/> </div> </div> </div> <div class="form-group"> - <label for="inputTafficClass" class="col-lg-2 control-label">Traffic Class</label> + <label for="ClassID" class="col-lg-2 control-label">Traffic Class</label> <div class="row"> <div class="col-lg-2"> - <input name="inputTrafficClassEnabled" type="checkbox" $params->{'inputTrafficClassEnabled'}/> Override + <input name="inputClassID.enabled" type="checkbox" $formData->{'inputClassID.enabled'}/> Override </div> <div class="col-lg-2"> - <select name="inputTrafficClass" placeholder="Traffic Class" class="form-control" value="$params->{'inputTrafficClass'}"> + <select name="ClassID" class="form-control"> $trafficClassStr </select> </div> @@ -393,15 +460,15 @@ EOF </div> <div class="form-group"> - <label for="inputLimitTx" class="col-lg-2 control-label">Download CIR</label> + <label for="TrafficLimitTx" class="col-lg-2 control-label">Download CIR</label> <div class="row"> <div class="col-lg-2"> - <input name="inputLimitTxEnabled" type="checkbox" $params->{'inputLimitTxEnabled'}/> Override + <input name="inputTrafficLimitTx.enabled" type="checkbox" $formData->{'inputTrafficLimitTx.enabled'}/> Override </div> <div class="col-lg-3"> <div class="input-group"> - <input name="inputLimitTx" type="text" placeholder="Download CIR" class="form-control" value="$params->{'inputLimitTx'}" /> + <input name="TrafficLimitTx" type="text" placeholder="Download CIR" class="form-control" value="$formData->{'TrafficLimitTx'}" /> <span class="input-group-addon">Kbps<span> </div> </div> @@ -409,14 +476,14 @@ EOF </div> <div class="form-group"> - <label for="inputLimitTxBurst" class="col-lg-2 control-label">Download Limit</label> + <label for="TrafficLimitTxBurst" class="col-lg-2 control-label">Download Limit</label> <div class="row"> <div class="col-lg-2"> - <input name="inputLimitTxBurstEnabled" type="checkbox" $params->{'inputLimitTxBurstEnabled'}/> Override + <input name="inputTrafficLimitTxBurst.enabled" type="checkbox" $formData->{'inputTrafficLimitTxBurst.enabled'}/> Override </div> <div class="col-lg-3"> <div class="input-group"> - <input name="inputLimitTxBurst" type="text" placeholder="Download Limit" class="form-control" value="$params->{'inputLimitTxBurst'}" /> + <input name="TrafficLimitTxBurst" type="text" placeholder="Download Limit" class="form-control" value="$formData->{'TrafficLimitTxBurst'}" /> <span class="input-group-addon">Kbps<span> </div> </div> @@ -424,28 +491,28 @@ EOF </div> <div class="form-group"> - <label for="inputLimitRx" class="col-lg-2 control-label">Upload CIR</label> + <label for="inputTrafficLimitRx" class="col-lg-2 control-label">Upload CIR</label> <div class="row"> <div class="col-lg-2"> - <input name="inputLimitRxEnabled" type="checkbox" $params->{'inputLimitRxEnabled'}/> Override + <input name="inputTrafficLimitRx.enabled" type="checkbox" $formData->{'inputTrafficLimitRx.enabled'}/> Override </div> <div class="col-lg-3"> <div class="input-group"> - <input name="inputLimitRx" type="text" placeholder="Upload CIR" class="form-control" value="$params->{'inputLimitRx'}" /> + <input name="TrafficLimitRx" type="text" placeholder="Upload CIR" class="form-control" value="$formData->{'TrafficLimitRx'}" /> <span class="input-group-addon">Kbps<span> </div> </div> </div> </div> <div class="form-group"> - <label for="inputLimitRxBurst" class="col-lg-2 control-label">Upload Limit</label> + <label for="TrafficLimitRxBurst" class="col-lg-2 control-label">Upload Limit</label> <div class="row"> <div class="col-lg-2"> - <input name="inputLimitRxBurstEnabled" type="checkbox" $params->{'inputLimitRxBurstEnabled'}/> Override + <input name="inputTrafficLimitRxBurst.enabled" type="checkbox" $formData->{'inputTrafficLimitRxBurst.enabled'}/> Override </div> <div class="col-lg-3"> <div class="input-group"> - <input name="inputLimitRxBurst" type="text" placeholder="Upload Limit" class="form-control" value="$params->{'inputLimitRxBurst'}" /> + <input name="TrafficLimitRxBurst" type="text" placeholder="Upload Limit" class="form-control" value="$formData->{'TrafficLimitRxBurst'}" /> <span class="input-group-addon">Kbps<span> </div> </div> @@ -453,13 +520,13 @@ EOF </div> <div class="form-group"> - <label for="inputExpires" class="col-lg-2 control-label">Expires</label> + <label for="Expires" class="col-lg-2 control-label">Expires</label> <div class="row"> <div class="col-lg-2"> - <input name="inputExpires" type="text" placeholder="Expires" class="form-control" value="$params->{'inputExpires'}" /> + <input name="Expires" type="text" placeholder="Expires" class="form-control" value="$expiresStr" /> </div> <div class="col-lg-2"> - <select name="inputExpiresModifier" placeholder="Expires Modifier" class="form-control" value="$params->{'inputExpiresModifier'}"> + <select name="inputExpires.modifier" class="form-control" value="$formData->{'inputExpires.modifier'}"> <option value="m">Mins</option> <option value="h">Hours</option> <option value="d">Days</option> @@ -469,15 +536,15 @@ EOF </div> <div class="form-group"> - <label for="inputNotes" class="col-lg-2 control-label">Notes</label> + <label for="Notes" class="col-lg-2 control-label">Notes</label> <div class="row"> <div class="col-lg-4"> - <textarea name="inputNotes" placeholder="Notes" rows="3" class="form-control"></textarea> + <textarea name="Notes" placeholder="Notes" rows="3" class="form-control"></textarea> </div> </div> </div> <div class="form-group"> - <button type="submit" class="btn btn-primary">Add</button> + <button type="submit" class="btn btn-primary">$formType</button> <button name="cancel" type="submit" class="btn">Cancel</button> </div> </form> @@ -487,5 +554,78 @@ EOF } +# Remove action +sub override_remove +{ + my ($kernel,$globals,$client_session_id,$request) = @_; + + + # Content to return + my $content = ""; + + + # Pull in GET + my $queryParams = parseURIQuery($request); + # We need a key first of all... + if (!defined($queryParams->{'oid'})) { + $content = <<EOF; + <div class="alert alert-danger text-center"> + No override oid in query string! + </div> +EOF + goto END; + } + + # Grab the override + my $override = getOverride($queryParams->{'oid'}); + + # Make the oid safe for HTML + my $encodedID = encode_entities($queryParams->{'oid'}); + + # Make sure the oid was valid... we would have an override now if it was + if (!defined($override)) { + $content = <<EOF; + <div class="alert alert-danger text-center"> + Invalid override oid "$encodedID"! + </div> +EOF + goto END; + } + + # Pull in POST + my $postParams = parseFormContent($request->content); + # If this is a post, then its probably a confirmation + if (defined($postParams->{'confirm'})) { + # Check if its a success + if ($postParams->{'confirm'} eq "Yes") { + # Post the removal + $kernel->post("configmanager" => "process_override_remove" => $override); + } + return (HTTP_TEMPORARY_REDIRECT,'configmanager'); + } + + + # Make the friendly name HTML safe + my $encodedFriendlyName = encode_entities($override->{'FriendlyName'}); + + # Build our confirmation dialog + $content .= <<EOF; + <div class="alert alert-danger"> + Are you very sure you wish to remove override "$encodedFriendlyName"? + </div> + <form role="form" method="post"> + <input type="submit" class="btn btn-primary" name="confirm" value="Yes" /> + <input type="submit" class="btn btn-default" name="confirm" value="No" /> + </form> +EOF + # And here is where we return +END: + return (HTTP_OK,$content,{ 'menu' => $menu }); +} + + + + + 1; # vim: ts=4 diff --git a/opentrafficshaper/plugins/webserver/pages/limits.pm b/opentrafficshaper/plugins/webserver/pages/limits.pm index 6f5243b..39a42c1 100644 --- a/opentrafficshaper/plugins/webserver/pages/limits.pm +++ b/opentrafficshaper/plugins/webserver/pages/limits.pm @@ -1,6 +1,6 @@ # OpenTrafficShaper webserver module: limits page # Copyright (C) 2007-2013, AllWorldIT -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -51,7 +51,7 @@ my $menu = { 'Manual Limits' => './?source=plugin.webserver.limits', }, 'Admin' => { - 'Add Limit' => 'add', + 'Add Limit' => 'limit-add', }, }; @@ -144,11 +144,19 @@ EOF } } + my $lidEncoded = encode_entities($limit->{'ID'}); + my $usernameEncoded = encode_entities($limit->{'Username'}); my $usernameEscaped = uri_escape($limit->{'Username'}); my $classStr = getTrafficClassName($limit->{'ClassID'}); + # We only support removing certain sources of limits + my $removeLink = ""; + if ($limit->{'Source'} eq "plugin.webserver.limits") { + $removeLink = "<a href=\"/limits/limit-remove?lid=$lidEncoded\"><span class=\"glyphicon glyphicon-remove\"></span></a>"; + } + $content .= <<EOF; <tr class="$style"> <td>$icon</td> @@ -194,8 +202,8 @@ EOF <td>$limitStr</td> <td> <a href="/statistics/by-username?username=$usernameEscaped"><span class="glyphicon glyphicon-stats"></span></a> - <a href="/limits/limit-edit?username=$usernameEscaped"><span class="glyphicon glyphicon-wrench"></span></a> - <a href="/limits/limit-remove?username=$usernameEscaped"><span class="glyphicon glyphicon-remove"></span></a> + <a href="/limits/limit-edit?lid=$lidEncoded"><span class="glyphicon glyphicon-wrench"></span></a> + $removeLink </td> </tr> EOF @@ -246,8 +254,8 @@ EOF } -# Add action -sub add +# Add/edit action +sub limit_addedit { my ($kernel,$globals,$client_session_id,$request) = @_; @@ -255,115 +263,164 @@ sub add # Setup our environment my $logger = $globals->{'logger'}; - # Errors to display + # Errors to display above the form my @errors; - # Form items - my $params = { - 'inputFriendlyName' => undef, - 'inputUsername' => undef, - 'inputIP' => undef, - 'inputTrafficClass' => undef, - 'inputLimitTx' => undef, - 'inputLimitTxBurst' => undef, - 'inputLimitRx' => undef, - 'inputLimitRxBurst' => undef, - 'inputExpires' => undef, - 'inputExpiresModifier' => undef, - 'inputNotes' => undef, - }; + + # Items for our form... + my @formElements = qw( + FriendlyName + Username IP + ClassID + TrafficLimitTx TrafficLimitTxBurst + TrafficLimitRx TrafficLimitRxBurst + Expires inputExpires.modifier + Notes + ); + + # Title of the form, by default its an add form + my $formType = "Add"; + my $formNoEdit = ""; + # Form data + my $formData; # If this is a form try parse it if ($request->method eq "POST") { # Parse form data - $params = parseFormContent($request->content); + $formData = parseFormContent($request->content); # If user pressed cancel, redirect - if (defined($params->{'cancel'})) { + if (defined($formData->{'cancel'})) { # Redirects to default page return (HTTP_TEMPORARY_REDIRECT,'limits'); } + # Maybe we were given an override key as a parameter? this would be an edit form + } elsif ($request->method eq "GET") { + # Parse GET data + my $queryParams = parseURIQuery($request); + # We need a key first of all... + if (defined($queryParams->{'lid'})) { + # Check if we get some data back when pulling the limit from the backend + if (defined($formData = getLimit($queryParams->{'lid'}))) { + # We need to make sure we're only editing our own limits + if ($formData->{'Source'} ne "plugin.webserver.limits") { + return (HTTP_TEMPORARY_REDIRECT,'limits'); + } + + # Work out expires modifier +# XXX - TODO + # If we didn't get any data, then something went wrong + } else { + my $encodedID = encode_entities($queryParams->{'lid'}); + push(@errors,"Limit data could not be loaded using limit ID '$encodedID'"); + } + # Lastly if we were given a key, this is actually an edit + $formType = "Edit"; + $formNoEdit = "readonly"; + + # Woops ... no query string? + } elsif (%{$queryParams} > 0) { + push(@errors,"No limit ID in query string!"); + $formType = "Edit"; + $formNoEdit = "readonly"; + } + } + + + # + # If we already have data, lets check how valid it is... + # + + # We only do this if we have hash elements + if (ref($formData) eq "HASH") { # Grab friendly name - my $friendlyName = $params->{'inputFriendlyName'}; + my $friendlyName = $formData->{'FriendlyName'}; # Check POST data my $username; - if (!defined($username = isUsername($params->{'inputUsername'}))) { + if (!defined($username = isUsername($formData->{'Username'}))) { push(@errors,"Username is not valid"); } my $ipAddress; - if (!defined($ipAddress = isIP($params->{'inputIP'}))) { + if (!defined($ipAddress = isIP($formData->{'IP'}))) { push(@errors,"IP address is not valid"); } - my $trafficClass; - if (!defined($trafficClass = isTrafficClassValid($params->{'inputTrafficClass'}))) { + my $classID; + if (!defined($classID = isTrafficClassValid($formData->{'ClassID'}))) { push(@errors,"Traffic class is not valid"); } - my $trafficLimitTx = isNumber($params->{'inputLimitTx'}); - my $trafficLimitTxBurst = isNumber($params->{'inputLimitTxBurst'}); + my $trafficLimitTx = isNumber($formData->{'TrafficLimitTx'}); + my $trafficLimitTxBurst = isNumber($formData->{'TrafficLimitTxBurst'}); if (!defined($trafficLimitTx) && !defined($trafficLimitTxBurst)) { push(@errors,"A valid download CIR and/or limit is required"); } - my $trafficLimitRx = isNumber($params->{'inputLimitRx'}); - my $trafficLimitRxBurst = isNumber($params->{'inputLimitRxBurst'}); + my $trafficLimitRx = isNumber($formData->{'TrafficLimitRx'}); + my $trafficLimitRxBurst = isNumber($formData->{'TrafficLimitRxBurst'}); if (!defined($trafficLimitRx) && !defined($trafficLimitRxBurst)) { push(@errors,"A valid upload CIR and/or limit is required"); } my $expires = 0; - if (defined($params->{'inputExpires'}) && $params->{'inputExpires'} ne "") { - if (!defined($expires = isNumber($params->{'inputExpires'}))) { + if (defined($formData->{'Expires'}) && $formData->{'Expires'} ne "") { + if (!defined($expires = isNumber($formData->{'Expires'}))) { push(@errors,"Expires value is not valid"); # Check the modifier } else { # Check if its defined - if (defined($params->{'inputExpiresModifier'}) && $params->{'inputExpiresModifier'} ne "") { + if (defined($formData->{'inputExpiresModifier'}) && $formData->{'inputExpiresModifier'} ne "") { # Minutes - if ($params->{'inputExpiresModifier'} eq "m") { + if ($formData->{'inputExpiresModifier'} eq "m") { $expires *= 60; # Hours - } elsif ($params->{'inputExpiresModifier'} eq "h") { + } elsif ($formData->{'inputExpiresModifier'} eq "h") { $expires *= 3600; # Days - } elsif ($params->{'inputExpiresModifier'} eq "d") { + } elsif ($formData->{'inputExpiresModifier'} eq "d") { $expires *= 86400; } else { push(@errors,"Expires modifier is not valid"); } } - # Set right time for expiry - $expires += time(); + # Base the expiry off now, plus the expiry time + if ($expires > 0) { + $expires += time(); + } } } # Grab notes - my $notes = $params->{'inputNotes'}; + my $notes = $formData->{'Notes'}; # If there are no errors we need to push this update - if (!@errors) { + if (!@errors && $request->method eq "POST") { # Build limit my $limit = { 'FriendlyName' => $friendlyName, 'Username' => $username, 'IP' => $ipAddress, 'GroupID' => 1, - 'ClassID' => $trafficClass, + 'ClassID' => $classID, 'TrafficLimitTx' => $trafficLimitTx, 'TrafficLimitTxBurst' => $trafficLimitTxBurst, 'TrafficLimitRx' => $trafficLimitRx, 'TrafficLimitRxBurst' => $trafficLimitRxBurst, 'Expires' => $expires, 'Notes' => $notes, - 'Source' => "plugin.webserver.limits", }; - # Throw the change at the config manager + # Throw the change at the config manager after we add extra data we need + if ($formType eq "Add") { + $limit->{'Status'} = 'online'; + $limit->{'Source'} = 'plugin.webserver.limits'; + } + $kernel->post("configmanager" => "process_limit_change" => $limit); - $logger->log(LOG_INFO,'[WEBSERVER/LIMITS/ADD] User: %s, IP: %s, Group: %s, Class: %s, Limits: %s/%s, Burst: %s/%s', + $logger->log(LOG_INFO,'[WEBSERVER/LIMITS] Acount: %s, User: %s, IP: %s, Group: %s, Class: %s, Limits: %s/%s, Burst: %s/%s', + $formType, prettyUndef($username), prettyUndef($ipAddress), undef, - prettyUndef($trafficClass), + prettyUndef($classID), prettyUndef($trafficLimitTx), prettyUndef($trafficLimitRx), prettyUndef($trafficLimitTxBurst), @@ -375,8 +432,8 @@ sub add } # Sanitize params if we need to - foreach my $item (keys %{$params}) { - $params->{$item} = defined($params->{$item}) ? encode_entities($params->{$item}) : ""; + foreach my $item (@formElements) { + $formData->{$item} = defined($formData->{$item}) ? encode_entities($formData->{$item}) : ""; } # Build content @@ -385,7 +442,7 @@ sub add # Form header $content .=<<EOF; <form role="form" method="post"> - <legend>Add Manual Limit</legend> + <legend>$formType Manual Limit</legend> EOF # Spit out errors if we have any @@ -399,55 +456,69 @@ EOF my $trafficClasses = getTrafficClasses(); my $trafficClassStr = ""; foreach my $classID (sort keys %{$trafficClasses}) { - $trafficClassStr .= '<option value="'.$classID.'">'.$trafficClasses->{$classID}.'</option>'; + # Process selections nicely + my $selected = ""; + if ($formData->{'ClassID'} ne "" && $formData->{'ClassID'} eq $classID) { + $selected = "selected"; + } + # And build the options + $trafficClassStr .= '<option value="'.$classID.'" '.$selected.'>'.$trafficClasses->{$classID}.'</option>'; } - # Header + # Make expires look nicer + my $expiresStr = ""; + if (defined($formData->{'Expires'}) && $formData->{'Expires'} > 0) { + $expiresStr = $formData->{'Expires'}; + } + + # + # Page content + # $content .=<<EOF; <div class="form-group"> - <label for="inputFriendlyName" class="col-lg-2 control-label">Friendly Name</label> + <label for="FriendlyName" class="col-lg-2 control-label">Friendly Name</label> <div class="row"> <div class="col-lg-4 input-group"> - <input name="inputFriendlyName" type="text" placeholder="Opt. Friendly Name" class="form-control" value="$params->{'inputFriendlyName'}" /> + <input name="FriendlyName" type="text" placeholder="Opt. Friendly Name" class="form-control" value="$formData->{'FriendlyName'}" /> </div> </div> </div> <div class="form-group"> - <label for="inputUsername" class="col-lg-2 control-label">Username</label> + <label for="Username" class="col-lg-2 control-label">Username</label> <div class="row"> <div class="col-lg-4 input-group"> - <input name="inputUsername" type="text" placeholder="Username" class="form-control" value="$params->{'inputUsername'}" /> + <input name="Username" type="text" placeholder="Username" class="form-control" value="$formData->{'Username'}" $formNoEdit/> <span class="input-group-addon">*</span> </div> </div> </div> <div class="form-group"> - <label for="inputIP" class="col-lg-2 control-label">IP Address</label> + <label for="IP" class="col-lg-2 control-label">IP Address</label> <div class="row"> <div class="col-lg-4 input-group"> - <input name="inputIP" type="text" placeholder="IP Address" class="form-control" value="$params->{'inputIP'}" /> + <input name="IP" type="text" placeholder="IP Address" class="form-control" value="$formData->{'IP'}" $formNoEdit/> <span class="input-group-addon">*</span> </div> </div> </div> <div class="form-group"> - <label for="inputTafficClass" class="col-lg-2 control-label">Traffic Class</label> + <label for="ClassID" class="col-lg-2 control-label">Traffic Class</label> <div class="row"> <div class="col-lg-2"> - <select name="inputTrafficClass" placeholder="Traffic Class" class="form-control" value="$params->{'inputTrafficClass'}"> + <select name="ClassID" class="form-control"> $trafficClassStr </select> </div> </div> </div> <div class="form-group"> - <label for="inputExpires" class="col-lg-2 control-label">Expires</label> + <label for="Expires" class="col-lg-2 control-label">Expires</label> <div class="row"> <div class="col-lg-2"> - <input name="inputExpires" type="text" placeholder="Opt. Expires" class="form-control" value="$params->{'inputExpires'}" /> + <input name="Expires" type="text" placeholder="Opt. Expires" class="form-control" value="$expiresStr" /> </div> <div class="col-lg-2"> - <select name="inputExpiresModifier" placeholder="Expires Modifier" class="form-control" value="$params->{'inputExpiresModifier'}"> + <select name="inputExpires.modifier" class="form-control" value="$formData->{'inputExpires.modifier'}"> <option value="m">Mins</option> <option value="h">Hours</option> <option value="d">Days</option> @@ -456,19 +527,19 @@ EOF </div> </div> <div class="form-group"> - <label for="inputLimitTx" class="col-lg-2 control-label">Download CIR</label> + <label for="TrafficLimitTx" class="col-lg-2 control-label">Download CIR</label> <div class="row"> <div class="col-lg-3"> <div class="input-group"> - <input name="inputLimitTx" type="text" placeholder="Download CIR" class="form-control" value="$params->{'inputLimitTx'}" /> + <input name="TrafficLimitTx" type="text" placeholder="Download CIR" class="form-control" value="$formData->{'TrafficLimitTx'}" /> <span class="input-group-addon">Kbps *<span> </div> </div> - <label for="inputLimitTxBurst" class="col-lg-1 control-label">Limit</label> + <label for="TrafficLimitTxBurst" class="col-lg-1 control-label">Limit</label> <div class="col-lg-3"> <div class="input-group"> - <input name="inputLimitTxBurst" type="text" placeholder="Download Limit" class="form-control" value="$params->{'inputLimitTxBurst'}" /> + <input name="TrafficLimitTxBurst" type="text" placeholder="Download Limit" class="form-control" value="$formData->{'TrafficLimitTxBurst'}" /> <span class="input-group-addon">Kbps<span> </div> </div> @@ -476,34 +547,34 @@ EOF </div> <div class="form-group"> - <label for="inputLimitRx" class="col-lg-2 control-label">Upload CIR</label> + <label for="TrafficLimitRx" class="col-lg-2 control-label">Upload CIR</label> <div class="row"> <div class="col-lg-3"> <div class="input-group"> - <input name="inputLimitRx" type="text" placeholder="Upload CIR" class="form-control" value="$params->{'inputLimitRx'}" /> + <input name="TrafficLimitRx" type="text" placeholder="Upload CIR" class="form-control" value="$formData->{'TrafficLimitRx'}" /> <span class="input-group-addon">Kbps *<span> </div> </div> - <label for="inputLimitRxBurst" class="col-lg-1 control-label">Limit</label> + <label for="TrafficLimitRxBurst" class="col-lg-1 control-label">Limit</label> <div class="col-lg-3"> <div class="input-group"> - <input name="inputLimitRxBurst" type="text" placeholder="Upload Limit" class="form-control" value="$params->{'inputLimitRxBurst'}" /> + <input name="TrafficLimitRxBurst" type="text" placeholder="Upload Limit" class="form-control" value="$formData->{'TrafficLimitRxBurst'}" /> <span class="input-group-addon">Kbps<span> </div> </div> </div> </div> <div class="form-group"> - <label for="inputNotes" class="col-lg-2 control-label">Notes</label> + <label for="Notes" class="col-lg-2 control-label">Notes</label> <div class="row"> <div class="col-lg-4"> - <textarea name="inputNotes" placeholder="Opt. Notes" rows="3" class="form-control"></textarea> + <textarea name="Notes" placeholder="Opt. Notes" rows="3" class="form-control"></textarea> </div> </div> </div> <div class="form-group"> - <button type="submit" class="btn btn-primary">Add</button> + <button type="submit" class="btn btn-primary">$formType</button> <button name="cancel" type="submit" class="btn">Cancel</button> </div> </form> @@ -513,5 +584,84 @@ EOF } +# Remove action +sub limit_remove +{ + my ($kernel,$globals,$client_session_id,$request) = @_; + + + # Content to return + my $content = ""; + + + # Pull in GET + my $queryParams = parseURIQuery($request); + # We need a key first of all... + if (!defined($queryParams->{'lid'})) { + $content = <<EOF; + <div class="alert alert-danger text-center"> + No limit ID in query string! + </div> +EOF + goto END; + } + + # Grab the limit + my $limit = getLimit($queryParams->{'lid'}); + + # Make the key safe for HTML + my $encodedLID = encode_entities($queryParams->{'lid'}); + + # Make sure the limit ID is valid... we would have a limit now if it was + if (!defined($limit)) { + $content = <<EOF; + <div class="alert alert-danger text-center"> + Invalid limit ID "$encodedLID"! + </div> +EOF + goto END; + } + + # Make sure its a manual limit we're removing + if ($limit->{'Source'} ne "plugin.webserver.limits") { + $content = <<EOF; + <div class="alert alert-danger text-center"> + Only manual limits can be removed! + </div> +EOF + goto END; + } + + # Pull in POST + my $postParams = parseFormContent($request->content); + # If this is a post, then its probably a confirmation + if (defined($postParams->{'confirm'})) { + # Check if its a success + if ($postParams->{'confirm'} eq "Yes") { + # Post the removal + $kernel->post("configmanager" => "process_limit_remove" => $limit); + } + return (HTTP_TEMPORARY_REDIRECT,'limits'); + } + + + # Make the friendly name HTML safe + my $encodedUsername = encode_entities($limit->{'Username'}); + + # Build our confirmation dialog + $content .= <<EOF; + <div class="alert alert-danger"> + Are you very sure you wish to remove limit for "$encodedUsername"? + </div> + <form role="form" method="post"> + <input type="submit" class="btn btn-primary" name="confirm" value="Yes" /> + <input type="submit" class="btn btn-default" name="confirm" value="No" /> + </form> +EOF + # And here is where we return +END: + return (HTTP_OK,$content,{ 'menu' => $menu }); +} + 1; # vim: ts=4 diff --git a/opentrafficshaper/plugins/webserver/webserver.pm b/opentrafficshaper/plugins/webserver/webserver.pm index 7f6ffe2..17de751 100644 --- a/opentrafficshaper/plugins/webserver/webserver.pm +++ b/opentrafficshaper/plugins/webserver/webserver.pm @@ -86,7 +86,9 @@ my $resources = { }, 'limits' => { 'default' => \&opentrafficshaper::plugins::webserver::pages::limits::default, - 'add' => \&opentrafficshaper::plugins::webserver::pages::limits::add, + 'limit-add' => \&opentrafficshaper::plugins::webserver::pages::limits::limit_addedit, + 'limit-remove' => \&opentrafficshaper::plugins::webserver::pages::limits::limit_remove, + 'limit-edit' => \&opentrafficshaper::plugins::webserver::pages::limits::limit_addedit, }, 'statistics' => { 'by-username' => \&opentrafficshaper::plugins::webserver::pages::statistics::byusername, @@ -94,7 +96,9 @@ my $resources = { }, 'configmanager' => { 'default' => \&opentrafficshaper::plugins::webserver::pages::configmanager::default, - 'add' => \&opentrafficshaper::plugins::webserver::pages::configmanager::add, + 'override-add' => \&opentrafficshaper::plugins::webserver::pages::configmanager::override_addedit, + 'override-remove' => \&opentrafficshaper::plugins::webserver::pages::configmanager::override_remove, + 'override-edit' => \&opentrafficshaper::plugins::webserver::pages::configmanager::override_addedit, }, }, }; -- GitLab