Change our template attribute processing, to be collections, so we can find attributes using anycase keys

This commit is contained in:
Deon George 2025-06-22 17:27:56 +10:00
parent ee3cb395c2
commit 3ad4c446ea
5 changed files with 66 additions and 34 deletions

View File

@ -13,7 +13,7 @@ class Template
private const LOGKEY = 'T--'; private const LOGKEY = 'T--';
private(set) string $file; private(set) string $file;
private array $template; private Collection $template;
private(set) bool $invalid = FALSE; private(set) bool $invalid = FALSE;
private(set) string $reason = ''; private(set) string $reason = '';
private Collection $on_change_target; private Collection $on_change_target;
@ -31,7 +31,7 @@ class Template
try { try {
// @todo Load in the proper attribute objects and objectclass objects // @todo Load in the proper attribute objects and objectclass objects
// @todo Make sure we have a structural objectclass, or make the template invalid // @todo Make sure we have a structural objectclass, or make the template invalid
$this->template = json_decode($td->get($file),null,512,JSON_OBJECT_AS_ARRAY|JSON_THROW_ON_ERROR); $this->template = collect(json_decode($td->get($file),null,512,JSON_OBJECT_AS_ARRAY|JSON_THROW_ON_ERROR));
} catch (\JsonException $e) { } catch (\JsonException $e) {
$this->invalid = TRUE; $this->invalid = TRUE;
@ -42,12 +42,11 @@ class Template
public function __get(string $key): mixed public function __get(string $key): mixed
{ {
return match ($key) { return match ($key) {
'attributes' => collect(Arr::get($this->template,$key))->keys(), 'attributes','objectclasses' => collect($this->template->get($key)),
'enabled' => Arr::get($this->template,$key,FALSE) && (! $this->invalid), 'enabled' => $this->template->get($key,FALSE) && (! $this->invalid),
'icon','regexp','title' => Arr::get($this->template,$key), 'icon','regexp','title' => $this->template->get($key),
'name' => Str::replaceEnd('.json','',$this->file), 'name' => Str::replaceEnd('.json','',$this->file),
'objectclasses' => collect(Arr::get($this->template,$key)), 'order' => $this->attributes->map(fn($item)=>Arr::get($item,'order')),
'order' => collect(Arr::get($this->template,'attributes'))->map(fn($item)=>$item['order']),
default => throw new \Exception('Unknown key: '.$key), default => throw new \Exception('Unknown key: '.$key),
}; };
@ -55,7 +54,19 @@ class Template
public function __isset(string $key): bool public function __isset(string $key): bool
{ {
return array_key_exists($key,$this->template); return $this->template->has($key);
}
/**
* Return the configuration for an attribute
*
* @param string $attribute
* @return array|NULL
*/
public function attribute(string $attribute): Collection|NULL
{
$key = $this->attributes->search(fn($item,$key)=>! strcasecmp($key,$attribute));
return collect($this->attributes->get($key));
} }
/** /**
@ -66,7 +77,7 @@ class Template
*/ */
public function attributeReadOnly(string $attribute): bool public function attributeReadOnly(string $attribute): bool
{ {
return ($x=Arr::get($this->template,'attributes.'.$attribute.'.readonly')) && $x; return ($x=$this->attribute($attribute)?->get('readonly')) && $x;
} }
/** /**
@ -77,7 +88,18 @@ class Template
*/ */
public function attributeTitle(string $attribute): string|NULL public function attributeTitle(string $attribute): string|NULL
{ {
return Arr::get($this->template,'attributes.'.$attribute.'.display'); return $this->attribute($attribute)?->get('display');
}
/**
* Return the title we should use for an attribute
*
* @param string $attribute
* @return string|NULL
*/
public function attributeType(string $attribute): string|NULL
{
return $this->attribute($attribute)?->get('type');
} }
/** /**

View File

@ -57,7 +57,7 @@ class HomeController extends Controller
$o->objectclass = [Entry::TAG_NOTAG=>$template->objectclasses->toArray()]; $o->objectclass = [Entry::TAG_NOTAG=>$template->objectclasses->toArray()];
foreach ($o->getAvailableAttributes() foreach ($o->getAvailableAttributes()
->filter(fn($item)=>$item->names_lc->intersect($template->attributes->map('strtolower'))->count()) ->filter(fn($item)=>$item->names_lc->intersect($template->attributes->keys()->map('strtolower'))->count())
->sortBy(fn($item)=>Arr::get($template->order,$item->name)) as $ao) ->sortBy(fn($item)=>Arr::get($template->order,$item->name)) as $ao)
{ {
$o->{$ao->name} = [Entry::TAG_NOTAG=>'']; $o->{$ao->name} = [Entry::TAG_NOTAG=>''];

View File

@ -9,13 +9,16 @@
<strong class="align-middle"><abbr title="{{ (($x=$template?->attributeTitle($o->name)) ? $o->name.': ' : '').$o->description }}">{{ $x ?: $o->name }}</abbr></strong> <strong class="align-middle"><abbr title="{{ (($x=$template?->attributeTitle($o->name)) ? $o->name.': ' : '').$o->description }}">{{ $x ?: $o->name }}</abbr></strong>
@if($new) @if($new)
@if($template?->attributeReadOnly($o->name_lc)) @if($template?->attributeReadOnly($o->name_lc))
<sup data-bs-toggle="tooltip" title="@lang('Input disabled by template')"><i class="fas fa-ban"></i></sup> <sup data-bs-toggle="tooltip" title="@lang('Input disabled')"><i class="fas fa-ban"></i></sup>
@endif @endif
@if($template?->onChangeAttribute($o->name_lc)) @if($ca=$template?->onChangeAttribute($o->name_lc))
<sup data-bs-toggle="tooltip" title="@lang('Value triggers an update to another attribute by template')"><i class="fas fa-keyboard"></i></sup> <sup data-bs-toggle="tooltip" title="@lang('Value triggers an update to another attribute')"><i class="fas fa-keyboard"></i></sup>
@endif @endif
@if ($template?->onChangeTarget($o->name_lc)) @if ($ct=$template?->onChangeTarget($o->name_lc))
<sup data-bs-toggle="tooltip" title="@lang('Value calculated by template')"><i class="fas fa-wand-magic-sparkles"></i></sup> <sup data-bs-toggle="tooltip" title="@lang('Value calculated from another attribute')"><i class="fas fa-wand-magic-sparkles"></i></sup>
@endif
@if((! $ca) && (! $ct) && $template?->attribute($o->name_lc))
<sup data-bs-toggle="tooltip" title="@lang('Attribute controlled by template')"><i class="fas fa-wand-magic"></i></sup>
@endif @endif
@endif @endif
@ -59,7 +62,13 @@
</div> </div>
</div> </div>
@switch($template?->attributeType($o->name))
@case('type')
@break;
@default
<x-attribute :o="$o" :edit="(! $template?->attributeReadOnly($o->name)) && $edit" :new="$new" :updated="$updated"/> <x-attribute :o="$o" :edit="(! $template?->attributeReadOnly($o->name)) && $edit" :new="$new" :updated="$updated"/>
@endswitch
</div> </div>
</div> </div>

View File

@ -1,31 +1,32 @@
{ {
"title": "Example entry", "title": "Example entry", // Title shown when selecting tempaltes
"description": "This is the description", "description": "This is the description", // Unused, only for documenting
"enabled": false, "enabled": false, // Whether template is enabled or not
"icon": "fa-star-of-life", "icon": "fa-star-of-life", // Icon shown when rendering an existing entry that identifies as this template
"rdn": "o", "rdn": "o", // @todo not implemented
"regexp": "/^$/", "regexp": "/^$/", // Regular expression that restricts where this template cna be used
"objectclasses": [ "objectclasses": [ // Objectclasses that entries will have if they use this template
"organization" "organization"
], ],
"attributes": { "attributes": { // Attribute configuration
"attribute1": { "attribute1": { // LDAP attribute name
"display": "Attribute 1", "display": "Attribute 1", // Displayed when accepting input for this value
"hint": "This is an example", "hint": "This is an example", // @todo not implemented
"order": 1 "type": null, // Default is NULL, so use normal Attribute rendering type
"order": 1 // Order to show attributes
}, },
"attribute2": { "attribute2": {
"display": "Attribute 2", "display": "Attribute 2",
"hint": "This is an example", "hint": "This is an example",
"type": "input", // Default is input "type": "input", // Force attribute to use template input
"order": 2 "order": 2
}, },
"attribute3": { "attribute3": {
"display": "Attribute 3", "display": "Attribute 3",
"type": "select", "type": "select", // Force attribute to use template select
"options": { "options": { // Select options
"/bin/bash": "Bash", "/bin/bash": "Bash",
"/bin/csh": "C Shell", "/bin/csh": "C Shell",
"/bin/dash": "Dash", "/bin/dash": "Dash",