diff --git a/src/opnsense/mvc/app/models/OPNsense/Base/BaseModel.php b/src/opnsense/mvc/app/models/OPNsense/Base/BaseModel.php index 2aa8930aa..d7367c5ce 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Base/BaseModel.php +++ b/src/opnsense/mvc/app/models/OPNsense/Base/BaseModel.php @@ -153,19 +153,26 @@ abstract class BaseModel if ($fieldObject instanceof ArrayField) { // handle Array types, recurring items if ($config_section_data != null) { - $counter = 0 ; foreach ($config_section_data as $conf_section) { + // Array items are identified by a UUID, read from attribute or create a new one + if (isset($conf_section->attributes()->uuid)) { + $tagUUID = $conf_section->attributes()['uuid']->__toString(); + } else { + $tagUUID = $internal_data->generateUUID(); + } + // iterate array items from config data - $child_node = new ContainerField($fieldObject->__reference . "." . ($counter++), $tagName); + $child_node = new ContainerField($fieldObject->__reference . "." . $tagUUID, $tagName); $this->parseXml($xmlNode, $conf_section, $child_node); - $fieldObject->addChildNode(null, $child_node); + $fieldObject->addChildNode($tagUUID, $child_node); } } else { // There's no content in config.xml for this array node. - $child_node = new ContainerField($fieldObject->__reference . ".0", $tagName); + $tagUUID = $internal_data->generateUUID(); + $child_node = new ContainerField($fieldObject->__reference . ".".$tagUUID, $tagName); $child_node->setInternalIsVirtual(); $this->parseXml($xmlNode, $config_section_data, $child_node); - $fieldObject->addChildNode(null, $child_node); + $fieldObject->addChildNode($tagUUID, $child_node); } } else { // All other node types (Text,Email,...) diff --git a/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/ArrayField.php b/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/ArrayField.php index 9fab5d1fd..a464889c6 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/ArrayField.php +++ b/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/ArrayField.php @@ -34,27 +34,11 @@ namespace OPNsense\Base\FieldTypes; */ class ArrayField extends BaseField { - /** - * @var int item index - */ - private $internalArrayCounter = 0 ; - /** * @var null|BaseField node to use for copying */ private $internalTemplateNode = null; - /** - * add Childnode (list), ignore the name of this item - * @param string $name property name - * @param BaseField $node content (must be of type BaseField) - */ - public function addChildNode($name, $node) - { - $this->internalChildnodes[(string)$this->internalArrayCounter] = $node; - $this->internalArrayCounter++; - } - /** * Copy first node pointer as template node to make sure we always have a template to create new nodes from. * If the first node is virtual (no source data), remove that from the list. @@ -63,20 +47,13 @@ class ArrayField extends BaseField { // always make sure there's a node to copy our structure from if ($this->internalTemplateNode ==null) { - $this->internalTemplateNode = $this->internalChildnodes["0"]; + $firstKey = array_keys($this->internalChildnodes)[0]; + $this->internalTemplateNode = $this->internalChildnodes[$firstKey]; /** * if first node is empty, remove reference node. */ - if ($this->internalChildnodes["0"]->getInternalIsVirtual()) { - unset($this->internalChildnodes["0"]); - $this->internalArrayCounter--; - } - } - - // check if all children have a uuid, generate one if missing - foreach ($this->internalChildnodes as $nodeKey => $node) { - if (!array_key_exists('uuid', $node->getAttributes())) { - $node->setAttributeValue("uuid", $this->generateUUID()); + if ($this->internalChildnodes[$firstKey]->getInternalIsVirtual()) { + unset($this->internalChildnodes[$firstKey]); } } } @@ -97,8 +74,9 @@ class ArrayField extends BaseField $new_record[$key] = clone $node ; } + $nodeUUID = $this->generateUUID(); $container_node = new ContainerField( - $this->__reference . "." . $this->internalArrayCounter, + $this->__reference . "." . $nodeUUID, $this->internalXMLTagName ); @@ -110,17 +88,17 @@ class ArrayField extends BaseField } // make sure we have a UUID on repeating child items - $container_node->setAttributeValue("uuid", $this->generateUUID()); + $container_node->setAttributeValue("uuid", $nodeUUID); // add node to this object - $this->addChildNode(null, $container_node); + $this->addChildNode($nodeUUID, $container_node); return $container_node; } /** * remove item by id (number) - * @param $index index number + * @param string $index index number */ public function del($index) { @@ -129,38 +107,43 @@ class ArrayField extends BaseField } } - /** - * search child item by UUID - * @param $uuid item uuid - * @return BaseField|null + * @param string|array $fieldNames sort by fieldname + * @param bool $descending sort descending + * @return array */ - public function findByUUID($uuid) + public function sortedBy($fieldNames, $descending = false) { - foreach ($this->internalChildnodes as $nodeKey => $node) { - $nodeAttr = $node->getAttributes(); - if (array_key_exists('uuid', $nodeAttr) && $nodeAttr['uuid'] == $uuid) { - return $node; - } + // reserve at least X number of characters for every field to improve sorting of multiple fields + $MAX_KEY_LENGTH = 30; + + // fieldnames may be a list or a single item, always convert to a list + if (!is_array($fieldNames)) { + $fieldNames = array($fieldNames); } - return null; - } - /** - * search child item by UUID - * @param $uuid item uuid - * @return bool - */ - public function delByUUID($uuid) - { + // collect sortable data as key/value store + $sortedData=array(); foreach ($this->internalChildnodes as $nodeKey => $node) { - $nodeAttr = $node->getAttributes(); - if (array_key_exists('uuid', $nodeAttr) && $nodeAttr['uuid'] == $uuid) { - unset($this->internalChildnodes[$nodeKey]); - return true; + // populate sort key + $sortKey = ''; + foreach ($fieldNames as $fieldName) { + if (array_key_exists($fieldName, $node->internalChildnodes)) { + $sortKey .= sprintf("%".$MAX_KEY_LENGTH."s ,", $node->$fieldName) ; + } } + $sortKey .= $nodeKey; // prevent overwrite of duplicates + $sortedData[$sortKey] = $node ; } - return false; + + // sort by key on ascending or descending order + if (!$descending) { + ksort($sortedData); + } else { + krsort($sortedData); + } + + return array_values($sortedData); } } diff --git a/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/BaseField.php b/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/BaseField.php index eb449b73b..348419c4e 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/BaseField.php +++ b/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/BaseField.php @@ -93,7 +93,7 @@ abstract class BaseField /** * @return string uuid v4 number */ - protected function generateUUID() + public function generateUUID() { return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',