Enable creation of new entries via templates

This commit is contained in:
Deon George 2025-06-11 19:28:38 +09:30
parent 820f398c2c
commit d61ecc96db
7 changed files with 96 additions and 22 deletions

View File

@ -30,15 +30,20 @@ class Template
public function __get(string $key): mixed public function __get(string $key): mixed
{ {
return match ($key) { return match ($key) {
'attributes' => array_map('strtolower',array_keys(Arr::get($this->template,$key))), 'attributes' => collect(array_map('strtolower',array_keys(Arr::get($this->template,$key)))),
'objectclasses' => array_map('strtolower',Arr::get($this->template,$key)), 'objectclasses' => collect(array_map('strtolower',Arr::get($this->template,$key))),
'enabled' => Arr::get($this->template,$key,FALSE), 'enabled' => Arr::get($this->template,$key,FALSE) && (! $this->invalid),
'icon','regexp' => Arr::get($this->template,$key), 'icon','regexp','title' => Arr::get($this->template,$key),
default => throw new \Exception('Unknown key: '.$key), default => throw new \Exception('Unknown key: '.$key),
}; };
} }
public function __isset(string $key): bool
{
return array_key_exists($key,$this->template);
}
public function __toString(): string public function __toString(): string
{ {
return $this->invalid ? '' : Arr::get($this->template,'title','No Template Name'); return $this->invalid ? '' : Arr::get($this->template,'title','No Template Name');

View File

@ -55,16 +55,24 @@ class HomeController extends Controller
$key = $this->request_key($request,collect(old())); $key = $this->request_key($request,collect(old()));
$template = NULL;
$o = new Entry; $o = new Entry;
$o->setRDNBase($key['dn']);
if (count($x=array_filter(old('objectclass',$request->objectclass)))) { if (count($x=collect(old('objectclass',$request->validated('objectclass')))->dot()->filter())) {
$o->objectclass = $x; $o->objectclass = Arr::undot($x);
// Also add in our required attributes // Also add in our required attributes
foreach($o->getAvailableAttributes()->filter(fn($item)=>$item->required) as $ao) foreach($o->getAvailableAttributes()->filter(fn($item)=>$item->required) as $ao)
$o->{$ao->name} = [Entry::TAG_NOTAG=>'']; $o->{$ao->name} = [Entry::TAG_NOTAG=>''];
$o->setRDNBase($key['dn']); } elseif ($request->validated('template')) {
$template = $o->template($request->validated('template'));
$o->objectclass = [Entry::TAG_NOTAG=>$template->objectclasses->toArray()];
// @todo We need to add aliases
foreach($o->getAvailableAttributes()->filter(fn($item)=>$template->attributes->contains($item)) as $ao)
$o->{$ao->name} = [Entry::TAG_NOTAG=>''];
} }
$step = $request->step ? $request->step+1 : old('step'); $step = $request->step ? $request->step+1 : old('step');
@ -74,6 +82,7 @@ class HomeController extends Controller
->with('bases',$this->bases()) ->with('bases',$this->bases())
->with('o',$o) ->with('o',$o)
->with('step',$step) ->with('step',$step)
->with('template',$template)
->with('container',old('container',$key['dn'])); ->with('container',old('container',$key['dn']));
} }
@ -383,7 +392,12 @@ class HomeController extends Controller
->with('bases',$this->bases()); ->with('bases',$this->bases());
// If we are rendering a DN, rebuild our object // If we are rendering a DN, rebuild our object
if ($key['dn']) { if ($key['cmd'] === 'create') {
$o = new Entry;
$o->setRDNBase($key['dn']);
} elseif ($key['dn']) {
// @todo Need to handle if DN is null, for example if the user's session expired and the ACLs dont let them retrieve $key['dn']
$o = config('server')->fetch($key['dn']); $o = config('server')->fetch($key['dn']);
foreach (collect(old())->except(['key','dn','step','_token','userpassword_hash','rdn','rdn_value']) as $attr => $value) foreach (collect(old())->except(['key','dn','step','_token','userpassword_hash','rdn','rdn_value']) as $attr => $value)
@ -393,6 +407,7 @@ class HomeController extends Controller
return match ($key['cmd']) { return match ($key['cmd']) {
'create' => $view 'create' => $view
->with('container',old('container',$key['dn'])) ->with('container',old('container',$key['dn']))
->with('o',$o)
->with('step',1), ->with('step',1),
'dn' => $view 'dn' => $view

View File

@ -66,12 +66,43 @@ class EntryAddRequest extends FormRequest
'min:1', 'min:1',
'max:1', 'max:1',
], ],
'objectclass._null_'=>[ 'objectclass._null_' => [
'required', function (string $attribute,mixed $value,\Closure $fail) {
$oc = collect($value)->dot()->filter();
// If this is step 1 and there is no objectclass, and no template, then fail
if ((! $oc->count())
&& (request()->post('step') == 1)
&& (! request()->post('template')))
{
$fail(__('Select an objectclass or a template'));
}
// Cant have both an objectclass and a template
if (request()->post('template') && $oc->count())
$fail(__('You cannot select a template and an objectclass'));
},
'array', 'array',
'min:1', 'min:1',
new HasStructuralObjectClass, new HasStructuralObjectClass,
] ],
'template' => [
function (string $attribute,mixed $value,\Closure $fail) {
$oc = collect(request()->post('objectclass'))->dot()->filter();
// If this is step 1 and there is no objectclass, and no template, then fail
if ((! collect($value)->filter()->count())
&& (request()->post('step') == 1)
&& (! $oc->count()))
{
$fail(__('Select an objectclass or a template'));
}
// Cant have both an objectclass and a template
if ($oc->count() && strlen($value))
$fail(__('You cannot select a template and an objectclass'));
},
],
]) ])
->toArray(); ->toArray();
} }

View File

@ -147,7 +147,7 @@ class Entry extends Model
// Filter out our templates specific for this entry // Filter out our templates specific for this entry
if ($this->dn && (! in_array(strtolower($this->dn),['cn=subschema']))) { if ($this->dn && (! in_array(strtolower($this->dn),['cn=subschema']))) {
$this->templates = $this->templates $this->templates = $this->templates
->filter(fn($item)=>! count(array_diff($item->objectclasses,array_map('strtolower',Arr::get($this->attributes,'objectclass'))))); ->filter(fn($item)=>! count($item->objectclasses->diff(array_map('strtolower',Arr::get($this->attributes,'objectclass')))));
} }
return $this; return $this;

View File

@ -24,6 +24,7 @@ class HasStructuralObjectClass implements ValidationRule
if ($item && config('server')->schema('objectclasses',$item)->isStructural()) if ($item && config('server')->schema('objectclasses',$item)->isStructural())
return; return;
$fail('There isnt a Structural Objectclass.'); if (collect($value)->dot()->filter()->count())
$fail(__('There isnt a Structural Objectclass.'));
} }
} }

View File

@ -9,7 +9,7 @@
@php($up=(session()->pull('updated') ?: collect())) @php($up=(session()->pull('updated') ?: collect()))
@php($attributes=$o->template($template)?->attributes) @php($attributes=$o->template($template)?->attributes)
@foreach($o->getVisibleAttributes()->filter(fn($item)=>in_array($item,$attributes)) as $ao) @foreach($o->getVisibleAttributes()->filter(fn($item)=>$attributes->contains($item)) as $ao)
<x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :updated="$up->contains($ao->name_lc)"/> <x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :updated="$up->contains($ao->name_lc)"/>
@endforeach @endforeach
</div> </div>

View File

@ -17,7 +17,7 @@
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header"> <div class="card-header">
@lang('Create New Entry') - @lang('Step') {{ $step }} @lang('Create New Entry') - @lang('Step') {{ $step }} @isset($template) <span class="ms-auto"><i class="fa fa-fw {{ $template->icon }}"></i> {{ $template->title }}</span>@endisset
</div> </div>
<div class="card-body"> <div class="card-body">
@ -30,19 +30,35 @@
@switch($step) @switch($step)
@case(1) @case(1)
<div class="row"> <div class="row">
<div class="col-12 col-md-6"> <div class="col-12 col-md-5">
<x-form.select <x-form.select
id="objectclass" id="objectclass"
name="objectclass[{{ Entry::TAG_NOTAG }}][]" name="objectclass[{{ Entry::TAG_NOTAG }}][]"
old="objectclass.{{ Entry::TAG_NOTAG }}" old="objectclass.{{ Entry::TAG_NOTAG }}"
:label="__('Select a Structural ObjectClass...')" :label="__('Select a Structural ObjectClass').'...'"
:options="($oc=$server->schema('objectclasses')) :options="($oc=$server->schema('objectclasses'))
->filter(fn($item)=>$item->isStructural()) ->filter(fn($item)=>$item->isStructural())
->sortBy(fn($item)=>$item->name_lc) ->sortBy(fn($item)=>$item->name_lc)
->map(fn($item)=>['id'=>$item->name,'value'=>$item->name])" ->map(fn($item)=>['id'=>$item->name,'value'=>$item->name])"
multiple="false" :allowclear="TRUE"
/> />
</div> </div>
@if($o->templates->count())
<div class="col-md-1">
<strong>@lang('OR')</strong>
</div>
<div class="col-12 col-md-5">
<x-form.select
name="template"
:label="__('Select a Template').'...'"
:options="$o->templates
->map(fn($item,$key)=>['id'=>$key,'value'=>$item->title])"
:allowclear="TRUE"
/>
</div>
@endif
</div> </div>
@break @break
@ -53,14 +69,12 @@
<x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :updated="FALSE"/> <x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :updated="FALSE"/>
@endforeach @endforeach
@include('fragment.dn.add_attr')
@break; @break;
@endswitch @endswitch
</form> </form>
<div class="row d-none pt-3"> <div class="row d-none pt-3">
<div class="col-12 {{ $step > 1 ? 'offset-sm-2' : '' }} col-lg-10"> <div class="col-11 {{ $step > 1 ? 'text-end' : '' }} pe-0">
<x-form.reset form="dn-create"/> <x-form.reset form="dn-create"/>
<x-form.submit :action="__('Next')" form="dn-create"/> <x-form.submit :action="__('Next')" form="dn-create"/>
</div> </div>
@ -102,7 +116,15 @@
} }
$(document).ready(function() { $(document).ready(function() {
@if($step === 2) @if($step === 1)
$('#objectclass').on('select2:open',function(){
$('#template').val(null).trigger('change');
});
$('#template').on('select2:open',function(){
$('#objectclass').val(null).trigger('change');
})
@elseif($step === 2)
$('#newattr').on('change',function(item) { $('#newattr').on('change',function(item) {
var oc = $('attribute#objectclass input[type=text]') var oc = $('attribute#objectclass input[type=text]')
.map((key,item)=>{return $(item).val()}).toArray(); .map((key,item)=>{return $(item).val()}).toArray();