diff --git a/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/ServiceController.php b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/ServiceController.php new file mode 100644 index 000000000..2bb3b41e9 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/ServiceController.php @@ -0,0 +1,66 @@ +request->isPost()) { + // close session for long running action + $this->sessionClose(); + + $backend = new Backend(); + // the ipfw rules need to know about all the zones, so we need to reload ipfw for the portal to work + $backend->configdRun("template reload OPNsense.IPFW"); + $bckresult = trim($backend->configdRun("ipfw reload")); + if ($bckresult == "OK") { + // TODO: implement portal webservers restart/reconfigure + $status = "ok"; + } else { + $status = "error reloading captive portal (".$bckresult.")"; + } + + return array("status" => $status); + } else { + return array("status" => "failed"); + } + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/SettingsController.php b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/SettingsController.php new file mode 100644 index 000000000..3cfd0572b --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/Api/SettingsController.php @@ -0,0 +1,224 @@ +"failed","validations" => array()); + // perform validation + $valMsgs = $mdlShaper->performValidation(); + foreach ($valMsgs as $field => $msg) { + // replace absolute path to attribute for relative one at uuid. + if ($node != null) { + $fieldnm = str_replace($node->__reference, $reference, $msg->getField()); + $result["validations"][$fieldnm] = $msg->getMessage(); + } else { + $result["validations"][$msg->getField()] = $msg->getMessage(); + } + } + + // serialize model to config and save when there are no validation errors + if (count($result['validations']) == 0) { + // save config if validated correctly + $mdlShaper->serializeToConfig(); + + Config::getInstance()->save(); + $result = array("result" => "saved"); + } + + return $result; + } + + /** + * retrieve zone settings or return defaults + * @param $uuid item unique id + * @return array + */ + public function getZoneAction($uuid = null) + { + $mdlCP = new CaptivePortal(); + if ($uuid != null) { + $node = $mdlCP->getNodeByReference('zones.zone.'.$uuid); + if ($node != null) { + // return node + return array("zone" => $node->getNodes()); + } + } else { + // generate new node, but don't save to disc + $node = $mdlCP->zones->zone->add() ; + return array("zone" => $node->getNodes()); + } + return array(); + } + + /** + * update zone with given properties + * @param $uuid item unique id + * @return array + */ + public function setZoneAction($uuid) + { + if ($this->request->isPost() && $this->request->hasPost("zone")) { + $mdlCP = new CaptivePortal(); + if ($uuid != null) { + $node = $mdlCP->getNodeByReference('zones.zone.'.$uuid); + if ($node != null) { + $node->setNodes($this->request->getPost("zone")); + return $this->save($mdlCP, $node, "zone"); + } + } + } + return array("result"=>"failed"); + } + + /** + * add new zone and set with attributes from post + * @return array + */ + public function addZoneAction() + { + $result = array("result"=>"failed"); + if ($this->request->isPost() && $this->request->hasPost("zone")) { + $mdlCP = new CaptivePortal(); + $node = $mdlCP->zones->zone->Add(); + $node->setNodes($this->request->getPost("zone")); + return $this->save($mdlCP, $node, "zone"); + } + return $result; + } + + /** + * delete zone by uuid + * @param $uuid item unique id + * @return array status + */ + public function delZoneAction($uuid) + { + $result = array("result"=>"failed"); + if ($this->request->isPost()) { + $mdlCP = new CaptivePortal(); + if ($uuid != null) { + if ($mdlCP->zones->zone->del($uuid)) { + // if item is removed, serialize to config and save + $mdlCP->serializeToConfig(); + Config::getInstance()->save(); + $result['result'] = 'deleted'; + } else { + $result['result'] = 'not found'; + } + } + } + return $result; + } + + /** + * toggle zone by uuid (enable/disable) + * @param $uuid item unique id + * @param $enabled desired state enabled(1)/disabled(1), leave empty for toggle + * @return array status + */ + public function toggleZoneAction($uuid, $enabled = null) + { + + $result = array("result" => "failed"); + if ($this->request->isPost()) { + $mdlCP = new CaptivePortal(); + if ($uuid != null) { + $node = $mdlCP->getNodeByReference('zones.zone.' . $uuid); + if ($node != null) { + if ($enabled == "0" || $enabled == "1") { + $node->enabled = (string)$enabled; + } elseif ((string)$node->enabled == "1") { + $node->enabled = "0"; + } else { + $node->enabled = "1"; + } + $result['result'] = $node->enabled; + // if item has toggled, serialize to config and save + $mdlCP->serializeToConfig(); + Config::getInstance()->save(); + } + } + } + return $result; + } + + /** + * search captive portal zones + * @return array + */ + public function searchZonesAction() + { + if ($this->request->isPost()) { + $this->sessionClose(); + // fetch query parameters + $itemsPerPage = $this->request->getPost('rowCount', 'int', 9999); + $currentPage = $this->request->getPost('current', 'int', 1); + $sortBy = array("number"); + $sortDescending = false; + + if ($this->request->hasPost('sort') && is_array($this->request->getPost("sort"))) { + $sortBy = array_keys($this->request->getPost("sort")); + if ($this->request->getPost("sort")[$sortBy[0]] == "desc") { + $sortDescending = true; + } + } + + $searchPhrase = $this->request->getPost('searchPhrase', 'string', ''); + + // create model and fetch query resuls + $fields = array("enabled", "description", "zoneid"); + $mdlCP = new CaptivePortal(); + $grid = new UIModelGrid($mdlCP->zones->zone); + return $grid->fetch($fields, $itemsPerPage, $currentPage, $sortBy, $sortDescending, $searchPhrase); + } else { + return array(); + } + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/IndexController.php b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/IndexController.php new file mode 100644 index 000000000..f9e68dd81 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/IndexController.php @@ -0,0 +1,45 @@ +view->title = "Captive Portal"; + // link rule dialog + $this->view->formDialogZone = $this->getForm("dialogZone"); + // choose template + $this->view->pick('OPNsense/CaptivePortal/index'); + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/forms/dialogZone.xml b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/forms/dialogZone.xml new file mode 100644 index 000000000..35047fcb7 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/CaptivePortal/forms/dialogZone.xml @@ -0,0 +1,28 @@ +
+ + zone.enabled + + checkbox + enable this zone + + + zone.zoneid + + info + internal number used for this zone + + + zone.interfaces + + select_multiple + + + Type or select interface. + + + zone.description + + text + Description to identify this zone. + +
diff --git a/src/opnsense/mvc/app/models/OPNsense/CaptivePortal/CaptivePortal.php b/src/opnsense/mvc/app/models/OPNsense/CaptivePortal/CaptivePortal.php new file mode 100644 index 000000000..99a3454d0 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/CaptivePortal/CaptivePortal.php @@ -0,0 +1,39 @@ + + //OPNsense/captiveportal + + Captive portal application model + + + + + + 1 + Y + + + 0 + 19 + Maximum number of zones reached + Y + + + Y + Y + lan + + /^(?!0).*$/ + /^((?!dhcp).)*$/ + + + + Y + /^([\t\n\v\f\r 0-9a-zA-Z.,_\x{00A0}-\x{FFFF}]){1,255}$/u + Description should be a string between 1 and 255 characters + + + + + diff --git a/src/opnsense/mvc/app/views/OPNsense/CaptivePortal/index.volt b/src/opnsense/mvc/app/views/OPNsense/CaptivePortal/index.volt new file mode 100644 index 000000000..95158d6f5 --- /dev/null +++ b/src/opnsense/mvc/app/views/OPNsense/CaptivePortal/index.volt @@ -0,0 +1,117 @@ +{# + +OPNsense® is Copyright © 2014 – 2015 by Deciso B.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +#} + + + + +
+
+ + + + + + + + + + + + + + + + + + + +
{{ lang._('Enabled') }}{{ lang._('Zoneid') }}{{ lang._('Description') }}{{ lang._('Commands') }}{{ lang._('ID') }}
+ + +
+
+
+ +
+
+
+ +
+
+ + +{# include dialogs #} +{{ partial("layout_partials/base_dialog",['fields':formDialogZone,'id':'DialogZone','label':'Edit zone'])}}