Working JS Template Engine with a basic functionality
This commit is contained in:
parent
fac560750e
commit
d6ec080ecf
@ -3,12 +3,16 @@
|
||||
namespace App\Classes;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Template
|
||||
{
|
||||
private string $file;
|
||||
private const LOGKEY = 'T--';
|
||||
|
||||
private(set) string $file;
|
||||
private array $template;
|
||||
private(set) bool $invalid = FALSE;
|
||||
private(set) string $reason = '';
|
||||
@ -48,4 +52,137 @@ class Template
|
||||
{
|
||||
return array_key_exists($key,$this->template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the onchange JavaScript for attribute
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return Collection
|
||||
*/
|
||||
public function onChange(string $attribute): Collection
|
||||
{
|
||||
$attr = collect($this->template['attributes'])
|
||||
->filter(fn($item,$key)=>! strcasecmp($key,$attribute))
|
||||
->pop();
|
||||
|
||||
if (! $attr)
|
||||
return collect();
|
||||
|
||||
$result = collect();
|
||||
|
||||
foreach (Arr::get($attr,'onchange',[]) as $item) {
|
||||
list($command,$args) = preg_split('/^=([a-zA-Z]+)\((.+)\)$/',$item,-1,PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
switch ($command) {
|
||||
case 'autoFill':
|
||||
$result->push($this->autofill($args));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* autoFill - javascript to have one attribute fill the value of another
|
||||
*
|
||||
* args: is a literal string, with two parts, delimited by a semi-colon ;
|
||||
* + The first part is the attribute that will be populated
|
||||
* + The second part may contain many fields like %attr|start-end/flags|additionalcontrolchar%
|
||||
* to substitute values read from other fields.
|
||||
* + |start-end is optional, but must be present if the k flag is used
|
||||
* + /flags is optional
|
||||
* + |additionalcontrolchar is optional, and specific to a flag being used
|
||||
*
|
||||
* + flags may be:
|
||||
* T:(?) Read display text from selection item (drop-down list), otherwise, read the value of the field
|
||||
* For fields that aren't selection items, /T shouldn't be used, and the field value will always be read
|
||||
* k:(?) Tokenize:
|
||||
* If the "k" flag is not given:
|
||||
* + A |start-end instruction will perform a sub-string operation upon the value of the attr, passing
|
||||
* character positions start-end through
|
||||
* + start can be 0 for first character, or any other integer
|
||||
* + end can be 0 for last character, or any other integer for a specific position
|
||||
* If the "k" flag is given:
|
||||
* + The string read will be split into fields, using : as a delimiter
|
||||
* + start indicates which field number to pass through
|
||||
*
|
||||
* If additionalcontrolchar is given, it will be used as delimiter (e.g. this allows for splitting
|
||||
* e-mail addresses into domain and domain-local part)
|
||||
* l: Make the result lower case
|
||||
* U: Make the result upper case
|
||||
* A:(?) Remap special characters to their corresponding ASCII value
|
||||
*
|
||||
* @note Attributes rendered on the page are lowercase, eg: <attribute id="gidnumber"> for gidNumber
|
||||
* @note JavaScript generated here depends on js/template.js
|
||||
* (?) = to test
|
||||
*/
|
||||
private function autofill(string $arg): string
|
||||
{
|
||||
if (! preg_match('/;/',$arg)) {
|
||||
Log::alert(sprintf('%s:Invalid argument given to autofill [%s]',self::LOGKEY,$arg));
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
// $attr has our attribute to update, $string is the format to use when updating it
|
||||
list($attr,$string) = preg_split('(([^,]+);(.*))',$arg,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
$output = $string;
|
||||
//$result .= sprintf("\n// %s\n",$arg);
|
||||
|
||||
$m = [];
|
||||
// MATCH : 0 = highlevel match, 1 = attr, 2 = subst, 3 = mod, 4 = delimiter
|
||||
preg_match_all('/%(\w+)(?:\|([0-9]*-[0-9])+)?(?:\/([klTUA]+))?(?:\|(.)?)?%/U',$string,$m);
|
||||
|
||||
foreach ($m[0] as $index => $null) {
|
||||
$match_attr = strtolower($m[1][$index]);
|
||||
$match_subst = $m[2][$index];
|
||||
$match_mod = $m[3][$index];
|
||||
$match_delim = $m[4][$index];
|
||||
|
||||
$substrarray = [];
|
||||
|
||||
$result .= sprintf("var %s;\n",$match_attr);
|
||||
|
||||
if (str_contains($match_mod,'k')) {
|
||||
preg_match_all('/([0-9]+)/',trim($match_subst),$substrarray);
|
||||
|
||||
$delimiter = ($match_delim === '') ? ' ' : preg_quote($match_delim);
|
||||
$result .= sprintf(" %s = %s.split('%s')[%s];\n",$match_attr,$match_attr,$delimiter,$substrarray[1][0] ?? '0');
|
||||
|
||||
} else {
|
||||
// Work out the start and end chars needed from this value if we have a range specifier
|
||||
preg_match_all('/([0-9]*)-([0-9]+)/',$match_subst,$substrarray);
|
||||
if ((isset($substrarray[1][0]) && $substrarray[1][0]) || (isset($substrarray[2][0]) && $substrarray[2][0])) {
|
||||
$result .= sprintf("%s = get_attribute('%s',%d,%s);\n",
|
||||
$match_attr,$match_attr,
|
||||
$substrarray[1][0] ?? '0',
|
||||
$substrarray[2][0] ?: sprintf('%s.length',$match_attr));
|
||||
} else {
|
||||
$result .= sprintf("%s = get_attribute('%s');\n",$match_attr,$match_attr);
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($match_mod,'l'))
|
||||
$result .= sprintf("%s = %s.toLowerCase();\n",$match_attr,$match_attr);
|
||||
|
||||
if (str_contains($match_mod,'U'))
|
||||
$result .= sprintf("%s = %s.toUpperCase();\n",$match_attr,$match_attr);
|
||||
|
||||
if (str_contains($match_mod,'A'))
|
||||
$result .= sprintf("%s = toAscii(%s);\n",$match_attr,$match_attr);
|
||||
|
||||
// For debugging
|
||||
//$result .= sprintf("console.log('%s will return:'+%s);\n",$match_attr,$match_attr);
|
||||
|
||||
// Reformat out output into JS variables
|
||||
$output = preg_replace('/'.preg_quote($m[0][$index],'/').'/','\'+'.$match_attr.'+\'',$output);
|
||||
}
|
||||
|
||||
$result .= sprintf("put_attribute('%s','%s');\n",strtolower($attr),$output);
|
||||
$result .= "\n";
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -46,18 +46,12 @@ class HomeController extends Controller
|
||||
$o = new Entry;
|
||||
$o->setRDNBase($key['dn']);
|
||||
|
||||
foreach (collect(old())->except(['_token','key','step','rdn','rdn_value','userpassword_hash']) as $old => $value)
|
||||
foreach (collect(old())->except(['_token','key','step','rdn','rdn_value','_template','userpassword_hash']) as $old => $value)
|
||||
$o->{$old} = array_filter($value);
|
||||
|
||||
if (count($x=collect(old('objectclass',$request->validated('objectclass')))->dot()->filter())) {
|
||||
$o->objectclass = Arr::undot($x);
|
||||
if (old('_template',$request->validated('template'))) {
|
||||
$template = $o->template(old('_template',$request->validated('template')));
|
||||
|
||||
// Also add in our required attributes
|
||||
foreach ($o->getAvailableAttributes()->filter(fn($item)=>$item->is_must) as $ao)
|
||||
$o->{$ao->name} = [Entry::TAG_NOTAG=>''];
|
||||
|
||||
} elseif ($request->validated('template')) {
|
||||
$template = $o->template($request->validated('template'));
|
||||
$o->objectclass = [Entry::TAG_NOTAG=>$template->objectclasses->toArray()];
|
||||
|
||||
foreach ($o->getAvailableAttributes()
|
||||
@ -66,6 +60,13 @@ class HomeController extends Controller
|
||||
{
|
||||
$o->{$ao->name} = [Entry::TAG_NOTAG=>''];
|
||||
}
|
||||
|
||||
} elseif (count($x=collect(old('objectclass',$request->validated('objectclass')))->dot()->filter())) {
|
||||
$o->objectclass = Arr::undot($x);
|
||||
|
||||
// Also add in our required attributes
|
||||
foreach ($o->getAvailableAttributes()->filter(fn($item)=>$item->is_must) as $ao)
|
||||
$o->{$ao->name} = [Entry::TAG_NOTAG=>''];
|
||||
}
|
||||
|
||||
$step = $request->step ? $request->step+1 : old('step');
|
||||
@ -104,6 +105,7 @@ class HomeController extends Controller
|
||||
return $view
|
||||
->with('o',$o)
|
||||
->with('langtag',Entry::TAG_NOTAG)
|
||||
->with('template',NULL)
|
||||
->with('updated',FALSE);
|
||||
}
|
||||
|
||||
@ -116,7 +118,7 @@ class HomeController extends Controller
|
||||
$o = new Entry;
|
||||
$o->setDn($dn);
|
||||
|
||||
foreach ($request->except(['_token','key','step','rdn','rdn_value','userpassword_hash']) as $key => $value)
|
||||
foreach ($request->except(['_token','key','step','rdn','rdn_value','_template','userpassword_hash']) as $key => $value)
|
||||
$o->{$key} = array_filter($value);
|
||||
|
||||
try {
|
||||
@ -368,6 +370,7 @@ class HomeController extends Controller
|
||||
* @param Request $request
|
||||
* @param Collection|null $old
|
||||
* @return \Illuminate\View\View
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function frame(Request $request,?Collection $old=NULL): \Illuminate\View\View
|
||||
{
|
||||
|
23
public/js/template.js
vendored
Normal file
23
public/js/template.js
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/* JavaScript template engine abstraction layer */
|
||||
/* Currently implemented for jquery */
|
||||
|
||||
// Get a value from an attribute
|
||||
function get_attribute(attribute,start,end) {
|
||||
var val = $('#'+attribute).find('input').val();
|
||||
|
||||
return ((start !== undefined) && (end !== undefined))
|
||||
? val.substring(start,end)
|
||||
: val;
|
||||
}
|
||||
|
||||
// Put a value to an attribute
|
||||
function put_attribute(attribute,result) {
|
||||
// Get the value, if the value hasnt changed, then we dont need to do anything
|
||||
if (get_attribute(attribute) === result)
|
||||
return;
|
||||
|
||||
$('#'+attribute)
|
||||
.find('input')
|
||||
.val(result)
|
||||
.trigger('change');
|
||||
}
|
81
public/js/toAscii.js
vendored
Normal file
81
public/js/toAscii.js
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// Purpose of this file is to remap characters as ASCII characters
|
||||
//
|
||||
//
|
||||
|
||||
var to_ascii_array = new Array();
|
||||
to_ascii_array['Ã '] = 'a';
|
||||
to_ascii_array['á'] = 'a';
|
||||
to_ascii_array['â'] = 'a';
|
||||
to_ascii_array['À'] = 'a';
|
||||
to_ascii_array['ã'] = 'a';
|
||||
to_ascii_array['Ã¥'] = 'a';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã
'] = 'A';
|
||||
to_ascii_array['é'] = 'e';
|
||||
to_ascii_array['Ú'] = 'e';
|
||||
to_ascii_array['ë'] = 'e';
|
||||
to_ascii_array['ê'] = 'e';
|
||||
to_ascii_array['â¬'] = 'E';
|
||||
to_ascii_array['ï'] = 'i';
|
||||
to_ascii_array['î'] = 'i';
|
||||
to_ascii_array['ì'] = 'i';
|
||||
to_ascii_array['Ã'] = 'i';
|
||||
to_ascii_array['Ã'] = 'I';
|
||||
to_ascii_array['Ã'] = 'I';
|
||||
to_ascii_array['Ã'] = 'I';
|
||||
to_ascii_array['Ã'] = 'I';
|
||||
to_ascii_array['ò'] = 'o';
|
||||
to_ascii_array['ó'] = 'o';
|
||||
to_ascii_array['ÃŽ'] = 'o';
|
||||
to_ascii_array['õ'] = 'o';
|
||||
to_ascii_array['ö'] = 'o';
|
||||
to_ascii_array['Þ'] = 'o';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['ù'] = 'u';
|
||||
to_ascii_array['ú'] = 'u';
|
||||
to_ascii_array['Ì'] = 'u';
|
||||
to_ascii_array['û'] = 'u';
|
||||
to_ascii_array['Ã'] = 'U';
|
||||
to_ascii_array['Ã'] = 'U';
|
||||
to_ascii_array['Ã'] = 'U';
|
||||
to_ascii_array['Ã'] = 'U';
|
||||
to_ascii_array['Ê'] = 'ae';
|
||||
to_ascii_array['Ã'] = 'AE';
|
||||
to_ascii_array['Ü'] = 'y';
|
||||
to_ascii_array['ÿ'] = 'y';
|
||||
to_ascii_array['Ã'] = 'SS';
|
||||
to_ascii_array['Ã'] = 'C';
|
||||
to_ascii_array['ç'] = 'c';
|
||||
to_ascii_array['Ã'] = 'N';
|
||||
to_ascii_array['ñ'] = 'n';
|
||||
to_ascii_array['¢'] = 'c';
|
||||
to_ascii_array['©'] = '(C)';
|
||||
to_ascii_array['®'] = '(R)';
|
||||
to_ascii_array['«'] = '<<';
|
||||
to_ascii_array['»'] = '>>';
|
||||
|
||||
function toAscii(text) {
|
||||
//var text = field.value;
|
||||
var output = '';
|
||||
|
||||
for (position=0; position < text.length; position++) {
|
||||
var tmp = text.substring(position,position+1);
|
||||
|
||||
if (to_ascii_array[tmp] !== undefined)
|
||||
tmp = to_ascii_array[tmp];
|
||||
|
||||
output += tmp;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
@ -18,3 +18,8 @@
|
||||
<!-- Any Custom JS -->
|
||||
<script src="{{ asset('js/custom.js') }}"></script>
|
||||
@endif
|
||||
|
||||
@if(file_exists('js/template.js'))
|
||||
<!-- Template Engine JS -->
|
||||
<script src="{{ asset('js/template.js') }}"></script>
|
||||
@endif
|
@ -7,6 +7,10 @@
|
||||
<span class="d-flex justify-content-between">
|
||||
<span style="width: 20em;">
|
||||
<strong class="align-middle"><abbr title="{{ $o->description }}">{{ $o->name }}</abbr></strong>
|
||||
@if($new && $template?->onChange($o->name)->count())
|
||||
<sup data-bs-toggle="tooltip" title="@lang('Value calculated by template')"><i class="fas fa-wand-magic-sparkles"></i></sup>
|
||||
@endif
|
||||
|
||||
@if($o->hints->count())
|
||||
<sup>
|
||||
[
|
||||
|
@ -34,3 +34,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</x-attribute.layout>
|
||||
|
||||
@if($new && ($x=$template?->onChange($o->name))?->count())
|
||||
@section('page-scripts')
|
||||
<!-- START: ONCHANGE PROCESSING {{ $o->name }} -->
|
||||
<script type="text/javascript">
|
||||
$('#{{ $o->name_lc }}').on('change',function() {
|
||||
{!! $x->join('') !!}
|
||||
});
|
||||
</script>
|
||||
<!-- END: ONCHANGE PROCESSING {{ $o->name }} -->
|
||||
@append
|
||||
@endif
|
@ -63,10 +63,11 @@
|
||||
@break
|
||||
|
||||
@case(2)
|
||||
<x-attribute-type :o="$o->getObject('rdn')" :edit="TRUE" :new="FALSE" :updated="FALSE"/>
|
||||
<input type="hidden" name="_template" value="{{ $template?->file }}">
|
||||
<x-attribute-type :o="$o->getObject('rdn')" :edit="TRUE" :new="TRUE" :template="$template" :updated="FALSE"/>
|
||||
|
||||
@foreach($o->getVisibleAttributes() as $ao)
|
||||
<x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :template="$template" :updated="FALSE"/>
|
||||
<x-attribute-type :o="$ao" :edit="TRUE" :new="TRUE" :template="$template" :updated="FALSE"/>
|
||||
@endforeach
|
||||
|
||||
@if(! $template)
|
||||
|
@ -101,7 +101,7 @@
|
||||
<div class="tab-content">
|
||||
@php($up=(session()->pull('updated') ?: collect()))
|
||||
@foreach($o->getVisibleAttributes() 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" :template="$template ?? NULL" :updated="$up->contains($ao->name_lc)"/>
|
||||
@endforeach
|
||||
|
||||
@include('fragment.dn.add_attr')
|
||||
@ -124,7 +124,7 @@
|
||||
<!-- Internal Attributes -->
|
||||
<div class="tab-pane mt-3" id="internal" role="tabpanel">
|
||||
@foreach($o->getInternalAttributes() as $ao)
|
||||
<x-attribute-type :o="$ao" :edit="FALSE" :new="FALSE" :updated="FALSE"/>
|
||||
<x-attribute-type :o="$ao" :edit="FALSE" :new="FALSE" :template="$template ?? NULL" :updated="FALSE"/>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
"enabled": true,
|
||||
"icon": "fa-user",
|
||||
"rdn": "cn",
|
||||
"regexp": "/^ou=.+,o=.+,?/",
|
||||
"regexp": "/^ou=.+,?/",
|
||||
|
||||
"objectclasses": [
|
||||
"inetOrgPerson",
|
||||
@ -15,7 +15,7 @@
|
||||
"givenName": {
|
||||
"display": "First Name",
|
||||
"onchange": [
|
||||
"=autoFill(cn;%givenName% %sn%)",
|
||||
"=autoFill(cn;%givenName% %sn/U%)",
|
||||
"=autoFill(uid;%givenName|0-1/l%%sn/l%)"
|
||||
],
|
||||
"order": 1
|
||||
@ -23,7 +23,7 @@
|
||||
"sn": {
|
||||
"display": "Last Name",
|
||||
"onchange": [
|
||||
"=autoFill(cn;%givenName% %sn%)",
|
||||
"=autoFill(cn;%givenName% %sn/U%)",
|
||||
"=autoFill(uid;%givenName|0-1/l%%sn/l%)"
|
||||
],
|
||||
"order": 2
|
||||
|
Loading…
x
Reference in New Issue
Block a user