icingadb-web/library/Icingadb/Model/Behavior/FlattenedObjectVars.php
Alexander A. Klimov 3c8ed68cc6 Upgrade license from GPLv2 to GPLv2+
This was easy because only README.md and doc/01-About.md were redacted manually, everything else via:
git ls-files -z |xargs -0 perl -pi -e 's/Icinga GmbH \| GPLv2/Icinga GmbH | GPLv2+/'

This is legal because we have only merged PRs with label:cla/signed or made by Icinga staff:
https://github.com/Icinga/icingadb-web/pulls?page=1&q=is%3Apr+is%3Aclosed+-label%3Acla%2Fsigned+-author%3Anilmerg

This has no risk for us in people distributing their own version under GPLv3 only.
After all, we won't take their patches anyway, unless they sign our CLA.

This is the cleanest solution for having e.g. these in one address space:

* Icinga Web, GPLv2+
* K8s Web, AGPLv3
* Thirdparty, some LGPLv3 and Apache-2.0

Apropos, K8s Web is even v3-licensed on purpose, to have a stronger protection against cloud ops.
2025-11-21 13:31:24 +01:00

114 lines
4.9 KiB
PHP

<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2+ */
namespace Icinga\Module\Icingadb\Model\Behavior;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Model\CustomvarFlat;
use ipl\Orm\AliasedExpression;
use ipl\Orm\ColumnDefinition;
use ipl\Orm\Contract\QueryAwareBehavior;
use ipl\Orm\Contract\RewriteColumnBehavior;
use ipl\Orm\Query;
use ipl\Stdlib\Filter;
class FlattenedObjectVars implements RewriteColumnBehavior, QueryAwareBehavior
{
use Auth;
/** @var string[] The labels to rewrite for column definition */
private const LABEL_REWRITE = [
'user' => 'contact',
'usergroup' => 'contactgroup'
];
/** @var Query */
protected $query;
public function setQuery(Query $query)
{
$this->query = $query;
return $this;
}
public function rewriteCondition(Filter\Condition $condition, $relation = null)
{
$column = $condition->metaData()->get('columnName');
if ($column !== null) {
$relation = substr($relation, 0, -5) . 'customvar_flat.';
$condition->metaData()
->set('requiresTransformation', true)
->set('columnPath', $relation . $column)
->set('relationPath', substr($relation, 0, -1));
if (isset($this->query->getWith()[substr($relation, 0, -1)])) {
// In case customvar_flat is being selected, the FilterProcessor will not try to optimize the condition.
// So we can prepare the condition here and still let CustomvarFlat's behavior do the rest.
$condition->metaData()->set('columnName', $relation . $column);
return $condition;
}
// The ORM's FilterProcessor only optimizes filter conditions that are in the same level (chain).
// Previously, this behavior transformed a single condition to an ALL chain and hence the semantics
// of the level changed, since the FilterProcessor interpreted the conditions separately from there on.
// To not change the semantics of the condition it is required to delay the transformation of the condition
// until the subquery is created. Though, since this is about custom variables, and such can contain dots,
// the FilterProcessor then continues traversing the parts of the column's path, which then would include
// the dot-separated parts of the custom variable name. To prevent this, we have to signal that what we
// return a replacement here, that should be used as-is and not processed further.
$condition->metaData()->set('forceResolved', true);
// But to make it even worse: If we do that, (not transforming the condition) the FilterProcessor sees
// multiple conditions as targeting different columns, as it doesn't know that the *columns* are in fact
// custom variables. It then attempts to combine the conditions with an AND, which is not possible, since
// they refer to the same columns (flatname and flatvalue) after being transformed. So we have to make
// the condition refer to a different column, which is totally irrelevant, but since it's always the same
// column, the FilterProcessor won't attempt to combine the conditions. The literal icing on the cake.
$condition->setColumn('always_the_same_but_totally_irrelevant');
return $condition;
}
}
public function rewriteColumn($column, $relation = null)
{
$subQuery = $this->query->createSubQuery(new CustomvarFlat(), $relation)
->limit(1)
->columns('flatvalue')
->filter(Filter::equal('flatname', $column));
$this->applyRestrictions($subQuery);
$alias = $this->query->getDb()->quoteIdentifier([str_replace('.', '_', $relation) . "_$column"]);
list($select, $values) = $this->query->getDb()->getQueryBuilder()->assembleSelect($subQuery->assembleSelect());
return new AliasedExpression($alias, "($select)", null, ...$values);
}
public function rewriteColumnDefinition(ColumnDefinition $def, string $relation): void
{
$parts = explode('.', substr($relation, 0, -5));
$objectType = array_pop($parts);
$name = $def->getName();
if (substr($name, -3) === '[*]') {
// The suggestions also hide this from the label, so should this
$name = substr($name, 0, -3);
}
if (array_key_exists($objectType, self::LABEL_REWRITE)) {
$objectType = self::LABEL_REWRITE[$objectType];
}
// Programmatically translated since the full definition is available in class ObjectSuggestions
$def->setLabel(sprintf(t(ucfirst($objectType) . ' %s', '..<customvar-name>'), $name));
}
public function isSelectableColumn(string $name): bool
{
return true;
}
}