diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Base/ApiMutableModelControllerBase.php b/src/opnsense/mvc/app/controllers/OPNsense/Base/ApiMutableModelControllerBase.php index 89299b378..c519d0c49 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Base/ApiMutableModelControllerBase.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Base/ApiMutableModelControllerBase.php @@ -90,6 +90,83 @@ abstract class ApiMutableModelControllerBase extends ApiControllerBase return true; } + /** + * check if a specific token is in use, either in a list of options (xxx,yyy) or as exact match + * @param string $token token to search recursive + * @param bool $contains exact match or in list + * @param bool $only_mvc only report (versioned) models + * @throws UserException containing additional information + */ + protected function checkAndThrowValueInUse($token, $contains=True, $only_mvc=True) + { + if ($contains) { + $xpath = "//text()[contains(.,'{$token}')]"; + } else { + $xpath = "//*[text() = '{$token}']"; + } + $usages = []; + // find uuid's in our config.xml + foreach (Config::getInstance()->object()->xpath($xpath) as $node) { + $referring_node = $node->xpath("..")[0]; + if (!empty($referring_node->attributes()['uuid'])) { + // this looks like a model node, try to find module name (first tag with version attribute) + $item_path = array($referring_node->getName()); + $item_uuid = $referring_node->attributes()['uuid']; + $parent_node = $referring_node; + do { + $parent_node = $parent_node->xpath(".."); + $parent_node = $parent_node != null ? $parent_node[0] : null; + if ($parent_node != null) { + $item_path[] = $parent_node->getName(); + } + } while ($parent_node != null && !isset($parent_node->attributes()['version'])); + if ($parent_node != null) { + // construct usage info and add to usages if this looks like a model + $item_path = array_reverse($item_path); + $item_description = ""; + foreach (["description", "descr", "name"] as $key) { + if (!empty($referring_node->$key)) { + $item_description = (string)$referring_node->$key; + break; + } + } + $usages[] = [ + "reference" => implode(".", $item_path) . "." . $item_uuid, + "module" => $item_path[0], + "description" => $item_description + ]; + } + } elseif (!$only_mvc) { + $referring_node = $node->xpath("..")[0]; + $item_description = ""; + foreach (["description", "descr", "name"] as $key) { + if (!empty($referring_node->$key)) { + $item_description = (string)$referring_node->$key; + break; + } + } + $usages[] = [ + "reference" => $referring_node->getName(), + "module" => $referring_node->getName(), + "description" => $item_description + ]; + } + } + if (!empty($usages)) { + // render exception message + $message = ""; + foreach ($usages as $usage) { + $message .= sprintf( + gettext("%s - %s {%s}"), + $usage['module'], + $usage['description'], + $usage['reference'] + ) . "\n"; + } + throw new UserException($message, gettext("Item in use by")); + } + } + /** * Check if item can be safely deleted if $internalModelUseSafeDelete is enabled. * Throws a user exception when the $uuid seems to be used in some other config section. @@ -99,54 +176,7 @@ abstract class ApiMutableModelControllerBase extends ApiControllerBase private function checkAndThrowSafeDelete($uuid) { if (static::$internalModelUseSafeDelete) { - $configObj = Config::getInstance()->object(); - $usages = []; - // find uuid's in our config.xml - foreach ($configObj->xpath("//text()[contains(.,'{$uuid}')]") as $node) { - $referring_node = $node->xpath("..")[0]; - if (!empty($referring_node->attributes()['uuid'])) { - // this looks like a model node, try to find module name (first tag with version attribute) - $item_path = array($referring_node->getName()); - $item_uuid = $referring_node->attributes()['uuid']; - $parent_node = $referring_node; - do { - $parent_node = $parent_node->xpath(".."); - $parent_node = $parent_node != null ? $parent_node[0] : null; - if ($parent_node != null) { - $item_path[] = $parent_node->getName(); - } - } while ($parent_node != null && !isset($parent_node->attributes()['version'])); - if ($parent_node != null) { - // construct usage info and add to usages if this looks like a model - $item_path = array_reverse($item_path); - $item_description = ""; - foreach (["description", "descr", "name"] as $key) { - if (!empty($referring_node->$key)) { - $item_description = (string)$referring_node->$key; - break; - } - } - $usages[] = array( - "reference" => implode(".", $item_path) . "." . $item_uuid, - "module" => $item_path[0], - "description" => $item_description - ); - } - } - } - if (!empty($usages)) { - // render exception message - $message = ""; - foreach ($usages as $usage) { - $message .= sprintf( - gettext("%s - %s {%s}"), - $usage['module'], - $usage['description'], - $usage['reference'] - ) . "\n"; - } - throw new UserException($message, gettext("Item in use by")); - } + $this->checkAndThrowValueInUse($uuid); } }