Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added

- Add Transifex translations link to README
- Inject generic assets, including their type, model, comment and custom fields

## [2.16.0] - 2026-10-05

Expand Down
50 changes: 42 additions & 8 deletions inc/commoninjectionlib.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
use Glpi\DBAL\QuerySubQuery;
use Glpi\Exception\Http\HttpException;
use Glpi\Features\AssignableItem;
use Glpi\Asset\Asset;
use GlpiPlugin\Datainjection\AssetInjection;

use function Safe\preg_match;
use function Safe\preg_replace;
Expand Down Expand Up @@ -316,7 +318,10 @@ public static function isFieldADropdown($field_type)
public static function getItemtypeInstanceByInjection($injectionClassName)
{

if (!is_a($injectionClassName, PluginDatainjectionInjectionInterface::class, true)) {
if (
!is_a($injectionClassName, PluginDatainjectionInjectionInterface::class, true)
|| is_a($injectionClassName, AssetInjection::class, true)
) {
throw new HttpException(500, 'Class ' . $injectionClassName . ' is not a valid class');
}

Expand All @@ -338,7 +343,8 @@ public static function getItemtypeInstanceByInjection($injectionClassName)
*/
public static function getItemtypeByInjection($injectionClassName)
{
if (!is_a($injectionClassName, CommonDBTM::class, true)) {
// A generic asset injection class needs its itemtype: it cannot be instanciated from its name
if (!is_a($injectionClassName, CommonDBTM::class, true) || is_a($injectionClassName, AssetInjection::class, true)) {
throw new HttpException(500, 'Class ' . $injectionClassName . ' is not a valid class');
}

Expand All @@ -355,6 +361,10 @@ public static function getItemtypeByInjection($injectionClassName)
*/
public static function getItemtypeByInjectionClass($injectionClass)
{
// All generic assets share the same table
if ($injectionClass instanceof AssetInjection) {
return $injectionClass->itemtype;
}

return Toolbox::ucfirst(getItemTypeForTable($injectionClass->getTable()));
}
Expand All @@ -369,6 +379,9 @@ public static function getItemtypeByInjectionClass($injectionClass)
*/
public static function getInjectionClassInstance($itemtype)
{
if (is_a($itemtype, Asset::class, true)) {
return AssetInjection::forItemtype($itemtype);
}

if (!isPluginItemType($itemtype)) {
$itemtype = (new ReflectionClass($itemtype))->getShortName(); // get shortname of class when full namespaced class given (from GLPI)
Expand Down Expand Up @@ -527,8 +540,12 @@ private function getFloatFormat()
private function getItemInstance()
{

$classname = $this->injectionClass::class;
return self::getItemtypeInstanceByInjection($classname);
$itemtype = $this->primary_type;
if (!is_a($itemtype, CommonDBTM::class, true)) {
throw new HttpException(500, 'Class ' . $itemtype . ' is not a valid class');
}

return new $itemtype();
}


Expand Down Expand Up @@ -626,7 +643,8 @@ private function getFieldValue(

case 'dropdown':
case 'relation':
$tmptype = getItemTypeForTable($searchOption['table']);
// Generic assets types and models, or custom dropdowns, share their table with other itemtypes
$tmptype = $searchOption['itemtype'] ?? getItemTypeForTable($searchOption['table']);
if (!is_a($tmptype, CommonDBTM::class, true)) {
return;
}
Expand Down Expand Up @@ -900,6 +918,7 @@ private function findSingle($item, $searchOption, $entity, $value)
}

$where[$searchOption['field']] = $value;
$where += $item::getSystemSQLCriteria();

$result = $DB->request([
'SELECT' => ['id'],
Expand Down Expand Up @@ -1010,7 +1029,7 @@ private function setValueForItemtype($itemtype, $field, $value, $fromdb = false)
'is_active',
];
if (empty($value) && $value !== 0 && $value !== '0') {
if (isForeignKeyField($field) || (str_contains($field, 'is_')) || (method_exists($injectionClass, 'isNullable') && !$injectionClass->isNullable($field))) {
if (isForeignKeyField($field) || (str_starts_with($field, 'is_')) || (method_exists($injectionClass, 'isNullable') && !$injectionClass->isNullable($field))) {
//If the field concernes groupds, unseting it instead of setting it to 0 in order to avoid associating the item to a non existing group (id 0)
$group_fields = ['groups_id_tech', 'groups_id', 'groups_id_normal'];
if (in_array($field, $group_fields) && Toolbox::hasTrait($itemtype, AssignableItem::class)) {
Expand Down Expand Up @@ -1053,7 +1072,7 @@ private function getTemplateIDByName($itemtype, $name)
'WHERE' => [
'is_template' => 1,
'template_name' => $name,
],
] + $itemtype::getSystemSQLCriteria(),
]);

if (count($result) > 0) {
Expand Down Expand Up @@ -2128,6 +2147,11 @@ private function dataAlreadyInDB($injectionClass, $itemtype)
'FROM' => 'glpi_useremails',
'WHERE' => ['email' => $this->getValueByItemtypeAndName($itemtype, $field)],
]);
} elseif (
$injectionClass instanceof AssetInjection
&& ($criterion = $injectionClass->getCustomFieldCriterion($field, $this->getValueByItemtypeAndName($itemtype, $field))) instanceof QueryExpression
) {
$where[] = $criterion;
} else {
$where[$field] = $this->getValueByItemtypeAndName($itemtype, $field);
}
Expand Down Expand Up @@ -2165,13 +2189,20 @@ private function dataAlreadyInDB($injectionClass, $itemtype)
}
}

// Restrict to the items of the itemtype when its table is shared (generic assets)
$where += $item::getSystemSQLCriteria();

$result = $DB->request([
'FROM' => $injectionClass->getTable(),
'WHERE' => $where,
]);

if (count($result) > 0) {
$db_fields = $result->current();
if ($injectionClass instanceof AssetInjection) {
$db_fields += $injectionClass->getCustomFieldValues($db_fields);
}

foreach ($db_fields as $key => $value) {
$this->setValueForItemtype($itemtype, $key, $value, true);
}
Expand Down Expand Up @@ -2447,8 +2478,11 @@ public static function addToSearchOptions(
);

$main_itemtype = self::getItemtypeByInjectionClass($injectionClass);
$main_table = getTableForItemType($main_itemtype);
foreach ($type_searchOptions as &$value) {
$value_itemtype = getItemTypeForTable($value['table']);
$value_itemtype = $value['table'] === $main_table
? $main_itemtype
: $value['itemtype'] ?? getItemTypeForTable($value['table']);
$value_itemtype_name_singular = $value_itemtype::getTypeName(1);
$value_itemtype_name_plural = $value_itemtype::getTypeName(Session::getPluralNumber());
if (
Expand Down
2 changes: 1 addition & 1 deletion inc/info.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ public static function showAdditionalInformation(
}

Dropdown::show(
getItemTypeForTable($option['table']),
$option['itemtype'] ?? getItemTypeForTable($option['table']),
['name' => $name,
'value' => $value,
],
Expand Down
8 changes: 8 additions & 0 deletions inc/injectiontype.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* -------------------------------------------------------------------------
*/
use Glpi\Exception\Http\HttpException;
use Glpi\Asset\AssetDefinitionManager;

use function Safe\json_decode;
use function Safe\json_encode;
Expand Down Expand Up @@ -85,6 +86,13 @@ public static function getItemtypes($only_primary = false)
}
}

foreach (AssetDefinitionManager::getInstance()->getDefinitions(true) as $definition) {
$itemtype = $definition->getAssetClassName();
if ($itemtype::canCreate()) {
$values[$itemtype] = $definition->getTranslatedName();
}
}

asort($values);
return $values;
}
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ parameters:
- ajax
- front
- inc
- src
- hook.php
- setup.php
scanDirectories:
Expand Down
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<directory name="ajax" />
<directory name="front" />
<directory name="inc" />
<directory name="src" />
<file name="hook.php" />
<file name="setup.php" />
</projectFiles>
Expand Down
1 change: 1 addition & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
__DIR__ . '/ajax',
__DIR__ . '/front',
__DIR__ . '/inc',
__DIR__ . '/src',
__DIR__ . '/tests',
]);

Expand Down
Loading
Loading