Add Icon selection formBlock

This commit is contained in:
Eric Espie
2026-01-12 11:44:31 +01:00
parent ff11aec7fe
commit b58a64e143
4 changed files with 154 additions and 0 deletions

View File

@@ -89,9 +89,17 @@ abstract class ModelReflection
* @param string $defaultValue
*
* @return \RunTimeIconSelectionField
* @deprecated since 3.3.0 replaced by GetAvailableIcons
*/
abstract public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '');
/**
* Find available icons for the current context
*
* @return array of ['value', 'label', 'icon'] where 'value' is the relative path on disk, 'label' the name to display and 'icon' is the URL to get the image
*/
abstract public function GetAvailableIcons(): array;
abstract public function GetRootClass($sClass);
abstract public function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP);
}
@@ -109,6 +117,8 @@ abstract class QueryReflection
class ModelReflectionRuntime extends ModelReflection
{
private static array $aAllIcons = [];
public function __construct()
{
}
@@ -255,6 +265,52 @@ class ModelReflectionRuntime extends ModelReflection
return new RunTimeIconSelectionField($sCode, $sLabel, $defaultValue);
}
public function GetAvailableIcons(): array
{
$aFolderList = [
APPROOT.'env-'.utils::GetCurrentEnvironment() => utils::GetAbsoluteUrlModulesRoot(),
APPROOT.'images/icons' => utils::GetAbsoluteUrlAppRoot().'images/icons',
];
if (count(self::$aAllIcons) == 0) {
foreach ($aFolderList as $sFolderPath => $sUrlPrefix) {
$aIcons = self::FindIconsOnDisk($sFolderPath);
ksort($aIcons);
foreach ($aIcons as $sFilePath) {
self::$aAllIcons[] = ['value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => $sUrlPrefix.$sFilePath];
}
}
}
return self::$aAllIcons;
}
private static function FindIconsOnDisk(string $sBaseDir, string $sDir = '', array &$aFilesSpecs = []): array
{
$aResult = [];
// Populate automatically the list of icon files
if ($hDir = @opendir($sBaseDir.'/'.$sDir)) {
while (($sFile = readdir($hDir)) !== false) {
$aMatches = [];
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile)) {
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
$aResult = array_merge($aResult, self::FindIconsOnDisk($sBaseDir, $sDirSubPath, $aFilesSpecs));
}
$sSize = filesize($sBaseDir.'/'.$sDir.'/'.$sFile);
if (isset($aFilesSpecs[$sFile]) && $aFilesSpecs[$sFile] == $sSize) {
continue;
}
if (preg_match('/\.(png|jpg|jpeg|gif|svg)$/i', $sFile, $aMatches)) { // png, jp(e)g, gif and svg are considered valid
$aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
$aFilesSpecs[$sFile] = $sSize;
}
}
closedir($hDir);
}
return $aResult;
}
public function GetRootClass($sClass)
{
return MetaModel::GetRootClass($sClass);

View File

@@ -7,7 +7,11 @@
namespace Combodo\iTop\PropertyType\ValueType\Leaf;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Forms\Block\Base\ChoiceFormBlock;
use Combodo\iTop\PropertyType\ValueType\Branch\AbstractBranchValueType;
use Combodo\iTop\Service\DependencyInjection\ServiceLocator;
use utils;
/**
* @since 3.3.0
@@ -18,4 +22,26 @@ class ValueTypeIcon extends AbstractLeafValueType
{
return ChoiceFormBlock::class;
}
public function InitFromDomNode(DesignElement $oDomNode, ?AbstractBranchValueType $oParent = null): void
{
parent::InitFromDomNode($oDomNode, $oParent);
// Search icons in iTop and extensions
/** @var \ModelReflection $oModelReflection */
$oModelReflection = ServiceLocator::GetInstance()->get('ModelReflection');
$sChoices = "[\n";
$aIcons = $oModelReflection->GetAvailableIcons();
foreach ($aIcons as $aIcon) {
$sValue = utils::QuoteForPHP($aIcon['label']);
$sCode = utils::QuoteForPHP($aIcon['value']);
$sChoices .= <<<PHP
\t\t\t\t$sCode => $sValue,\n
PHP;
}
$sChoices .= "\t\t\t]";
$this->aFormBlockOptionsForPHP['choices'] = $sChoices;
}
}

View File

@@ -593,6 +593,7 @@ abstract class ItopTestCase extends KernelTestCase
{
try {
eval($sPHPCode);
self::AssertTrue(true);
} catch (ParseError $e) {
$aLines = explode("\n", $sPHPCode);
foreach ($aLines as $iLine => $sLine) {

View File

@@ -849,4 +849,75 @@ XML,
],
];
}
public function testCompileFormForIconSelection()
{
ServiceLocator::GetInstance()->RegisterService('ModelReflection', new ModelReflectionRuntime());
$sXMLContent = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<property_type id="basic_test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyType" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
<extends>Dashlet</extends>
<definition xsi:type="Combodo-ValueType-PropertyTree">
<nodes>
<node id="icon-selection" xsi:type="Combodo-ValueType-Icon">
<label>Icon</label>
</node>
</nodes>
</definition>
</property_type>
XML;
$sExpectedStart = <<<PHP
class FormFor__basic_test extends Combodo\iTop\Forms\Block\Base\FormBlock
{
protected function BuildForm(): void
{
\$this->Add('icon-selection', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [
'label' => 'Icon',
'choices' => [
PHP;
$sProducedPHP = PropertyTypeCompiler::GetInstance()->CompileFormFromXML($sXMLContent);
$this->AssertPHPCodeIsValid($sProducedPHP);
$this->assertStringStartsWith($sExpectedStart, $sProducedPHP);
}
public function testCompileFormForClassSelection()
{
ServiceLocator::GetInstance()->RegisterService('ModelReflection', new ModelReflectionRuntime());
$sXMLContent = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<property_type id="basic_test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyType" xsi:noNamespaceSchemaLocation = "https://www.combodo.com/itop-schema/3.3">
<extends>Dashlet</extends>
<definition xsi:type="Combodo-ValueType-PropertyTree">
<nodes>
<node id="class-selection" xsi:type="Combodo-ValueType-Class">
<label>Class</label>
</node>
</nodes>
</definition>
</property_type>
XML;
$sExpectedStart = <<<PHP
class FormFor__basic_test extends Combodo\iTop\Forms\Block\Base\FormBlock
{
protected function BuildForm(): void
{
\$this->Add('class-selection', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [
'label' => 'Class',
'choices' => [
PHP;
$sProducedPHP = PropertyTypeCompiler::GetInstance()->CompileFormFromXML($sXMLContent);
$this->AssertPHPCodeIsValid($sProducedPHP);
$this->assertStringStartsWith($sExpectedStart, $sProducedPHP);
}
}