diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f9c8cd9..a43be598 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## Unreleased + +### Fixed + +- Fix an issue where data are not formatted when coming from a field plugin's custom field. + ## [2.15.10] - 2026-08-07 ### Fixed diff --git a/inc/commoninjectionlib.class.php b/inc/commoninjectionlib.class.php index 501fae9e..0464274a 100644 --- a/inc/commoninjectionlib.class.php +++ b/inc/commoninjectionlib.class.php @@ -1098,6 +1098,14 @@ private function reformatThirdPass() //Get search option associated with the field $option = self::findSearchOption($searchOptions, $field); + if ($option && !isset($option['checktype'])) { + // In some cases, float datatype is wrongly detected as string, decimal or number, so we check that first. + //Regex matches all decimal formats accepted by glpi + $option['checktype'] = (preg_match($this->getFloatDetectionRegex(), $value) !== 0) + ? "float" + : $option['datatype']; + } + // Check some types switch ($option['checktype'] ?? 'text') { case "date": @@ -1299,6 +1307,13 @@ private static function reformatMacAddress($mac) return $mac; } + //--------------------------------------------------// + //----------- Utility methods -----------------------// + //------------------------------------------------// + public function getFloatDetectionRegex(): string + { + return '/^(?:(?:\d{1,3}(?: \d{3})+|\d+)[.,]|\d{1,3}(?:,\d{3})+\.)\d+$/'; + } //--------------------------------------------------// //----------- Check methods -----------------------// @@ -1397,7 +1412,7 @@ private function checkType($injectionClass, $option, $field_name, $data, $mandat { if (!empty($option)) { - $field_type = ($option['checktype'] ?? 'text'); + $field_type = ($option['checktype'] ?? $option['datatype'] ?? 'text'); //If no data provided AND this mapping is not mandatory if ( diff --git a/tests/unit/CommonInjectionLibFloatDetectionTest.php b/tests/unit/CommonInjectionLibFloatDetectionTest.php new file mode 100644 index 00000000..a02587c2 --- /dev/null +++ b/tests/unit/CommonInjectionLibFloatDetectionTest.php @@ -0,0 +1,100 @@ +. + * ------------------------------------------------------------------------- + * @copyright Copyright (C) 2007-2023 by DataInjection plugin team. + * @license GPLv2 https://www.gnu.org/licenses/gpl-2.0.html + * @link https://github.com/pluginsGLPI/datainjection + * ------------------------------------------------------------------------- + */ + +namespace GlpiPlugin\Datainjection\Tests\Unit; + +use Glpi\Tests\DbTestCase; +use PluginDatainjectionCommonInjectionLib; +use PluginDatainjectionComputerInjection; + +require_once dirname(__DIR__, 2) . '/inc/injectioninterface.class.php'; +require_once dirname(__DIR__, 2) . '/inc/commoninjectionlib.class.php'; +require_once dirname(__DIR__, 2) . '/inc/computerinjection.class.php'; + +/** + * Covers the float-detection regex used by reformatThirdPass() to derive + * $option['checktype'] when a search option (e.g. a custom field coming from + * a third-party plugin) only has a 'datatype' and no explicit 'checktype'. + * + * The regex under test is not hardcoded here: it is retrieved from + * PluginDatainjectionCommonInjectionLib::getFloatDetectionRegex(), the same + * shared source reformatThirdPass() itself calls, so this test can never + * drift out of sync with the actual pattern shipped in the code. + */ +final class CommonInjectionLibFloatDetectionTest extends DbTestCase +{ + private static function getFloatDetectionRegex(): string + { + $lib = new PluginDatainjectionCommonInjectionLib(new PluginDatainjectionComputerInjection()); + + return $lib->getFloatDetectionRegex(); + } + + public static function floatDetectionProvider(): array + { + return [ + // Accepted formats + 'plain dot decimal' => ['1234.56', true], + 'plain comma decimal' => ['1234,56', true], + 'leading zero decimal' => ['0.5', true], + 'space-grouped thousands, dot decimal' => ['1 234.56', true], + 'space-grouped thousands, comma decimal' => ['1 234,56', true], + 'multiple space groups, comma decimal' => ['12 345 678,90', true], + 'comma-grouped thousands, dot decimal' => ['1,234.56', true], + 'multiple comma groups, dot decimal' => ['12,345,678.90', true], + 'basic comma decimal, no grouping' => ['12,34', true], + + // Rejected formats (should fall back to $option['datatype']) + 'plain integer, no separator' => ['1234', false], + 'empty string' => ['', false], + 'non numeric text' => ['abc', false], + 'multiple dots (malformed)' => ['1234.56.78', false], + 'comma-grouped integer, no decimal part' => ['1,234,567', false], + 'space-grouped integer, no decimal part' => ['1 234', false], + 'leading dot, no integer part' => ['.56', false], + 'negative float (unsupported by regex)' => ['-1234.56', false], + 'malformed space grouping (2nd group not 3 digits)' => ['12 34.56', false], + 'malformed comma grouping (1st group over 3 digits)' => ['1234,567.89', false], + 'leading whitespace' => [' 1234.56', false], + 'trailing whitespace' => ['1234.56 ', false], + 'scientific notation (unsupported by regex)' => ['1.5e10', false], + ]; + } + + /** + * @dataProvider floatDetectionProvider + */ + public function testFloatDetectionRegex(string $value, bool $expected_match): void + { + $regex = self::getFloatDetectionRegex(); + + self::assertSame($expected_match, preg_match($regex, $value) !== 0); + } +}