Compare commits

..

8 Commits

Author SHA1 Message Date
d3c7bfcf79 Ensure form validation is displayed on template input entries, especially those marked as read-only
All checks were successful
Create Docker Image / Test Application (x86_64) (push) Successful in 27s
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 1m30s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 2m43s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
2025-06-22 09:40:45 +10:00
43c92e542c Fix regression introduce in 31e3c7 when adding a new objectclass to a new entry, newoc shouldnt be passed as a form value 2025-06-22 09:23:06 +10:00
7352293880 Fix rendering updated attributes on entries that trigger a template 2025-06-22 09:11:47 +10:00
bc4490a075 Framework and javascript dependancies update 2025-06-22 09:11:47 +10:00
05b2651ef4 Update README with v2.2 updates, as well as updating the home page 2025-06-22 09:11:47 +10:00
f6b7bff605 Enable disabling internal templates, as well as having custom templates 2025-06-22 09:11:47 +10:00
e8aaa17122 Change our internal template keys to be prefixed with an underscore for easier identification 2025-06-22 09:11:47 +10:00
ee7762d69b Working JS Template Engine with basic functionality 2025-06-22 09:11:43 +10:00
8 changed files with 54 additions and 19 deletions

View File

@ -58,6 +58,34 @@ class Template
return array_key_exists($key,$this->template); return array_key_exists($key,$this->template);
} }
/**
* If the attribute has been marked as read-only
*
* @param string $attribute
* @return bool
*/
public function attributeReadOnly(string $attribute): bool
{
return ($x=Arr::get($this->template,'attributes.'.$attribute.'.readonly')) && $x;
}
/**
* Return the title we should use for an attribute
*
* @param string $attribute
* @return string|NULL
*/
public function attributeTitle(string $attribute): string|NULL
{
return Arr::get($this->template,'attributes.'.$attribute.'.display');
}
/**
* Return the onChange JavaScript for an attribute
*
* @param string $attribute
* @return Collection|NULL
*/
public function onChange(string $attribute): Collection|NULL public function onChange(string $attribute): Collection|NULL
{ {
if (! $this->on_change_processed) if (! $this->on_change_processed)
@ -82,6 +110,9 @@ class Template
->has(strtolower($attribute)); ->has(strtolower($attribute));
} }
/**
* Process the attributes for onChange JavaScript
*/
/** /**
* Return the onchange JavaScript for attribute * Return the onchange JavaScript for attribute
* *

View File

@ -6,8 +6,11 @@
<div class="col-12 bg-light text-dark p-2 rounded-2"> <div class="col-12 bg-light text-dark p-2 rounded-2">
<span class="d-flex justify-content-between"> <span class="d-flex justify-content-between">
<span style="width: 20em;"> <span style="width: 20em;">
<strong class="align-middle"><abbr title="{{ $o->description }}">{{ $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))
<sup data-bs-toggle="tooltip" title="@lang('Input disabled by template')"><i class="fas fa-ban"></i></sup>
@endif
@if($template?->onChangeAttribute($o->name_lc)) @if($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 by template')"><i class="fas fa-keyboard"></i></sup>
@endif @endif
@ -56,7 +59,7 @@
</div> </div>
</div> </div>
<x-attribute :o="$o" :edit="$edit" :new="$new" :updated="$updated"/> <x-attribute :o="$o" :edit="(! $template?->attributeReadOnly($o->name)) && $edit" :new="$new" :updated="$updated"/>
</div> </div>
</div> </div>

View File

@ -5,9 +5,12 @@
@foreach($o->langtags as $langtag) @foreach($o->langtags as $langtag)
<span @class(['tab-pane','active'=>$loop->index === 0]) id="langtag-{{ $o->name_lc }}-{{ $langtag }}" role="tabpanel"> <span @class(['tab-pane','active'=>$loop->index === 0]) id="langtag-{{ $o->name_lc }}-{{ $langtag }}" role="tabpanel">
@foreach(Arr::get(old($o->name_lc,[$langtag=>$new ? [NULL] : $o->tagValues($langtag)]),$langtag,[]) as $key => $value) @foreach(Arr::get(old($o->name_lc,[$langtag=>$new ? [NULL] : $o->tagValues($langtag)]),$langtag,[]) as $key => $value)
@if($edit && (! $o->is_rdn))
<div class="input-group has-validation"> <div class="input-group has-validation">
@if($edit && (! $o->is_rdn))
<input type="text" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','border-focus'=>! ($tv=$o->tagValuesOld($langtag))->contains($value),'bg-success-subtle'=>$updated]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ $value }}" placeholder="{{ ! is_null($x=$tv->get($loop->index)) ? $x : '['.__('NEW').']' }}" @readonly(! $new) @disabled($o->isDynamic())> <input type="text" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','border-focus'=>! ($tv=$o->tagValuesOld($langtag))->contains($value),'bg-success-subtle'=>$updated]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ $value }}" placeholder="{{ ! is_null($x=$tv->get($loop->index)) ? $x : '['.__('NEW').']' }}" @readonly(! $new) @disabled($o->isDynamic())>
@else
<input type="text" @class(['form-control','noedit','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','bg-success-subtle'=>$updated]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ $value }}" readonly>
@endif
<div class="invalid-feedback pb-2"> <div class="invalid-feedback pb-2">
@if($e) @if($e)
@ -15,10 +18,6 @@
@endif @endif
</div> </div>
</div> </div>
@else
<input type="text" @class(['form-control','mb-1','bg-success-subtle'=>$updated]) value="{{ $value }}" disabled>
@endif
@endforeach @endforeach
</span> </span>
@endforeach @endforeach

View File

@ -1,6 +1,5 @@
<div class="row pt-2"> <div class="row pt-2">
<div @class(['col-1','d-none'=>(! $edit) && (! ($detail ?? FALSE))])></div> <div class="col-10 offset-1">
<div class="col-10">
<attribute id="{{ $o->name_lc }}"> <attribute id="{{ $o->name_lc }}">
{{ $slot }} {{ $slot }}
</attribute> </attribute>

View File

@ -38,7 +38,7 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<x-form.select id="newoc" name="newoc" :label="__('Select from').'...'"/> <x-form.select id="newoc" :label="__('Select from').'...'"/>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -106,6 +106,10 @@
if (rdn_attr && ($(this)[0].name === rdn_attr+'[]')) if (rdn_attr && ($(this)[0].name === rdn_attr+'[]'))
return; return;
// Exclude attributes marked as noedit
if ($(this).hasClass('noedit'))
return;
$(this).attr('readonly',false); $(this).attr('readonly',false);
}); });

View File

@ -101,7 +101,7 @@
<div class="tab-content"> <div class="tab-content">
@php($up=(session()->pull('updated') ?: collect())) @php($up=(session()->pull('updated') ?: collect()))
@foreach($o->getVisibleAttributes() as $ao) @foreach($o->getVisibleAttributes() as $ao)
<x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :template="$template ?? NULL" :updated="$up->contains($ao->name)"/> <x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :template="NULL" :updated="$up->contains($ao->name)"/>
@endforeach @endforeach
@include('fragment.dn.add_attr') @include('fragment.dn.add_attr')

View File

@ -51,8 +51,7 @@
"order": 6 "order": 6
}, },
"gidNumber": { "gidNumber": {
"display": "UID Number", "display": "GID Number",
"readonly": true,
"onchange": [ "onchange": [
"=autoFill(homeDirectory;/home/users/%gidNumber|0-0/T%/%uid|3-%)" "=autoFill(homeDirectory;/home/users/%gidNumber|0-0/T%/%uid|3-%)"
], ],