Implement getNextNumber() to populate template->values for attributes, where the attribute is determined after evaulating whats in the directory
Some checks failed
Create Docker Image / Test Application (x86_64) (push) Successful in 28s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 3m1s
Create Docker Image / Final Docker Image Manifest (push) Has been cancelled
Create Docker Image / Build Docker Image (x86_64) (push) Has been cancelled
Some checks failed
Create Docker Image / Test Application (x86_64) (push) Successful in 28s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 3m1s
Create Docker Image / Final Docker Image Manifest (push) Has been cancelled
Create Docker Image / Build Docker Image (x86_64) (push) Has been cancelled
This commit is contained in:
parent
c8d1122ff6
commit
553368c7b9
@ -4,13 +4,18 @@ namespace App\Classes;
|
|||||||
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
class Template
|
class Template
|
||||||
{
|
{
|
||||||
private const LOGKEY = 'T--';
|
private const LOGKEY = 'T--';
|
||||||
|
private const LOCK_TIME = 600;
|
||||||
|
|
||||||
private(set) string $file;
|
private(set) string $file;
|
||||||
private Collection $template;
|
private Collection $template;
|
||||||
@ -115,6 +120,154 @@ class Template
|
|||||||
return $this->attribute($attribute)?->get('type');
|
return $this->attribute($attribute)?->get('type');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function attributeValue(string $attribute): string|NULL
|
||||||
|
{
|
||||||
|
if ($x=$this->attribute($attribute)->get('value')) {
|
||||||
|
list($command,$args) = preg_split('/^=([a-zA-Z]+)\((.+)\)$/',$x,-1,PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
||||||
|
|
||||||
|
return match ($command) {
|
||||||
|
'getNextNumber' => $this->getNextNumber($args),
|
||||||
|
default => NULL,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get next number for an attribute
|
||||||
|
*
|
||||||
|
* As part of getting the next number, we'll use a lock to avoid any potential clashes. The lock is obtained by
|
||||||
|
* two lock files:
|
||||||
|
* a: Read a session lock (our session id), use that number if it exists, otherwise,
|
||||||
|
* b: Query the ldap server for the attribute, sort by number
|
||||||
|
* c: Read a system lock, if it exists, and use that as our start base (otherwise use a config() base)
|
||||||
|
* d: Starting at base, find the next free number
|
||||||
|
* e: When number identified, put it in the system lock with our session id
|
||||||
|
* f: Put the number in our session lock, with a timeout
|
||||||
|
* g: Read the system lock, make sure our session id is still in it, if not, go to (d) with our number as the base
|
||||||
|
* h: Remove our session id from the system lock (our number is unique)
|
||||||
|
*
|
||||||
|
* When using the number to create an entry:
|
||||||
|
* + Read our session lock, confirm the number is still in it, if not fail validation and bounce back
|
||||||
|
* + Create the entry
|
||||||
|
* + Delete our session lock
|
||||||
|
*
|
||||||
|
* @param string $arg
|
||||||
|
* @return int|NULL
|
||||||
|
*/
|
||||||
|
private function getNextNumber(string $arg): int|NULL
|
||||||
|
{
|
||||||
|
if (! preg_match('/;/',$arg)) {
|
||||||
|
Log::alert(sprintf('%s:Invalid argument given to getNextNumber [%s]',self::LOGKEY,$arg));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($start,$attr) = preg_split('(([^,]+);(\w+))',$arg,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||||
|
$attr = strtolower($attr);
|
||||||
|
|
||||||
|
// If we recently got a number, return it
|
||||||
|
if ($number=Cache::get($attr.':'.Session::id()))
|
||||||
|
return $number;
|
||||||
|
|
||||||
|
$cache = Cache::get($attr.':system');
|
||||||
|
Log::debug(sprintf('%s:System Cache has',self::LOGKEY),['cache'=>$cache]);
|
||||||
|
|
||||||
|
if (! Arr::get($cache,'number'))
|
||||||
|
$number = config('pla.template.getnextnumber.'.$attr,0);
|
||||||
|
else
|
||||||
|
$number = Arr::get($cache,'number')+1;
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:Starting with [%d] for [%s]',self::LOGKEY,$number,$attr));
|
||||||
|
|
||||||
|
$o = config('server');
|
||||||
|
$bases = ($start === '/') ? $o->baseDNs() : collect($start);
|
||||||
|
|
||||||
|
$result = collect();
|
||||||
|
$complete = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
$sizelimit = FALSE;
|
||||||
|
|
||||||
|
// Get the current numbers
|
||||||
|
foreach ($bases as $base) {
|
||||||
|
if (Arr::get($complete,$dn=$base->getDN()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$query = Entry::query()
|
||||||
|
->setDN($base)
|
||||||
|
->select([$attr])
|
||||||
|
->where($attr,'*')
|
||||||
|
->notFilter(fn($q)=>$q->where($attr,'<=',$number-1));
|
||||||
|
|
||||||
|
if ($result->count())
|
||||||
|
$query->notFilter(fn($q)=>$q->where($attr,'>=',$result->min()));
|
||||||
|
|
||||||
|
$result = $result->merge(($x=$query
|
||||||
|
->search()
|
||||||
|
->orderBy($attr)
|
||||||
|
->get())
|
||||||
|
->pluck($attr)
|
||||||
|
->flatten());
|
||||||
|
|
||||||
|
// If we hit a sizelimit on this run
|
||||||
|
$base_sizelimit = $query->getModel()->hasMore();
|
||||||
|
Log::debug(sprintf('%s:Query in [%s] returned [%d] entries and has more [%s]',self::LOGKEY,$base,$x->count(),$base_sizelimit ? 'TRUE' : 'FALSE'));
|
||||||
|
|
||||||
|
if (! $base_sizelimit)
|
||||||
|
$complete[$dn] = TRUE;
|
||||||
|
else
|
||||||
|
Log::info(sprintf('%s:Size Limit alert for [%s]',self::LOGKEY,$dn));
|
||||||
|
|
||||||
|
$sizelimit = $sizelimit || $base_sizelimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $result
|
||||||
|
->sort()
|
||||||
|
->unique();
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:Result has [%s]',self::LOGKEY,$result->join('|')));
|
||||||
|
|
||||||
|
if ($result->count())
|
||||||
|
foreach ($result as $item) {
|
||||||
|
Log::debug(sprintf('%s:Checking [%d] against [%s]',self::LOGKEY,$number,$item));
|
||||||
|
if ($number < $item)
|
||||||
|
break;
|
||||||
|
|
||||||
|
$number += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
$number += 1;
|
||||||
|
|
||||||
|
// Remove redundant entries
|
||||||
|
$result = $result->filter(fn($item)=>$item>$number);
|
||||||
|
|
||||||
|
if ($sizelimit)
|
||||||
|
Log::debug(sprintf('%s:We got a sizelimit.',self::LOGKEY),['number'=>$number,'result_min'=>$result->min(),'result_count'=>$result->count()]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @todo This might need some additional work:
|
||||||
|
* EG: if sizelimit is 5
|
||||||
|
* if result has 1,2,3,4,20 [size limit]
|
||||||
|
* we re-enquire (4=>20) and get 7,8,9,10,11 [size limit]
|
||||||
|
* we re-enquire (4=>7) and get 5,6 [no size limit]
|
||||||
|
* we calculate 12, and accept it because no size limit, but we didnt test for 12
|
||||||
|
*/
|
||||||
|
} while ($sizelimit);
|
||||||
|
|
||||||
|
// We found our number
|
||||||
|
Log::debug(sprintf('%s:Autovalue for Attribute [%s] in Session [%s] Storing [%d]',self::LOGKEY,$attr,Session::id(),$number));
|
||||||
|
Cache::put($attr.':system',['number'=>$number,'session'=>Session::id(),self::LOCK_TIME*2]);
|
||||||
|
Cache::put($attr.':'.Session::id(),$number,self::LOCK_TIME);
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
// If the session still has our session ID, then our number is ours
|
||||||
|
return (Arr::get(Cache::get($attr.':system'),'session') === Session::id())
|
||||||
|
? $number
|
||||||
|
: NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the onChange JavaScript for an attribute
|
* Return the onChange JavaScript for an attribute
|
||||||
*
|
*
|
||||||
@ -242,7 +395,7 @@ class Template
|
|||||||
|
|
||||||
$m = [];
|
$m = [];
|
||||||
// MATCH : 0 = highlevel match, 1 = attr, 2 = subst, 3 = mod, 4 = delimiter
|
// MATCH : 0 = highlevel match, 1 = attr, 2 = subst, 3 = mod, 4 = delimiter
|
||||||
preg_match_all('/%(\w+)(?:\|([0-9]*-[0-9])+)?(?:\/([klTUA]+))?(?:\|(.)?)?%/U',$string,$m);
|
preg_match_all('/%(\w+)(?:\|(\d*-\d)+)?(?:\/([klTUA]+))?(?:\|(.)?)?%/U',$string,$m);
|
||||||
|
|
||||||
foreach ($m[0] as $index => $null) {
|
foreach ($m[0] as $index => $null) {
|
||||||
$match_attr = strtolower($m[1][$index]);
|
$match_attr = strtolower($m[1][$index]);
|
||||||
@ -255,14 +408,14 @@ class Template
|
|||||||
$result .= sprintf("var %s;\n",$match_attr);
|
$result .= sprintf("var %s;\n",$match_attr);
|
||||||
|
|
||||||
if (str_contains($match_mod,'k')) {
|
if (str_contains($match_mod,'k')) {
|
||||||
preg_match_all('/([0-9]+)/',trim($match_subst),$substrarray);
|
preg_match_all('/(\d+)/',trim($match_subst),$substrarray);
|
||||||
|
|
||||||
$delimiter = ($match_delim === '') ? ' ' : preg_quote($match_delim);
|
$delimiter = ($match_delim === '') ? ' ' : preg_quote($match_delim);
|
||||||
$result .= sprintf(" %s = %s.split('%s')[%s];\n",$match_attr,$match_attr,$delimiter,$substrarray[1][0] ?? '0');
|
$result .= sprintf(" %s = %s.split('%s')[%s];\n",$match_attr,$match_attr,$delimiter,$substrarray[1][0] ?? '0');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Work out the start and end chars needed from this value if we have a range specifier
|
// 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);
|
preg_match_all('/(\d*)-(\d+)/',$match_subst,$substrarray);
|
||||||
if ((isset($substrarray[1][0]) && $substrarray[1][0]) || (isset($substrarray[2][0]) && $substrarray[2][0])) {
|
if ((isset($substrarray[1][0]) && $substrarray[1][0]) || (isset($substrarray[2][0]) && $substrarray[2][0])) {
|
||||||
$result .= sprintf("%s = get_attribute('%s',%d,%s);\n",
|
$result .= sprintf("%s = get_attribute('%s',%d,%s);\n",
|
||||||
$match_attr,$match_attr,
|
$match_attr,$match_attr,
|
||||||
|
@ -6,17 +6,18 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Redirect;
|
use Illuminate\Support\Facades\Redirect;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
use LdapRecord\Exceptions\InsufficientAccessException;
|
use LdapRecord\Exceptions\InsufficientAccessException;
|
||||||
use LdapRecord\LdapRecordException;
|
use LdapRecord\LdapRecordException;
|
||||||
use LdapRecord\Query\ObjectNotFoundException;
|
use LdapRecord\Query\ObjectNotFoundException;
|
||||||
use Nette\NotImplementedException;
|
use Nette\NotImplementedException;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute\{Factory,Password};
|
use App\Classes\LDAP\Attribute\{Factory,Password};
|
||||||
use App\Classes\LDAP\Server;
|
|
||||||
use App\Classes\LDAP\Import\LDIF as LDIFImport;
|
use App\Classes\LDAP\Import\LDIF as LDIFImport;
|
||||||
use App\Classes\LDAP\Export\LDIF as LDIFExport;
|
use App\Classes\LDAP\Export\LDIF as LDIFExport;
|
||||||
use App\Exceptions\Import\{GeneralException,VersionException};
|
use App\Exceptions\Import\{GeneralException,VersionException};
|
||||||
@ -28,7 +29,7 @@ class HomeController extends Controller
|
|||||||
{
|
{
|
||||||
private const LOGKEY = 'CHc';
|
private const LOGKEY = 'CHc';
|
||||||
|
|
||||||
private const INTERNAL_POST = ['_key','_rdn','_rdn_value','_step','_template','_token','_userpassword_hash'];
|
private const INTERNAL_POST = ['_auto_value','_key','_rdn','_rdn_value','_step','_template','_token','_userpassword_hash'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new object in the LDAP server
|
* Create a new object in the LDAP server
|
||||||
@ -151,6 +152,12 @@ class HomeController extends Controller
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there are an _auto_value attributes, we need to invalid those
|
||||||
|
foreach ($request->get('_auto_value') as $attr => $value) {
|
||||||
|
Log::debug(sprintf('%s:Removing auto_value attr [%s]',self::LOGKEY,$attr));
|
||||||
|
Cache::delete($attr.':'.Session::id());
|
||||||
|
}
|
||||||
|
|
||||||
return Redirect::to('/')
|
return Redirect::to('/')
|
||||||
->withFragment($o->getDNSecure());
|
->withFragment($o->getDNSecure());
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,17 @@
|
|||||||
namespace App\Http\Requests;
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
|
||||||
use App\Rules\{DNExists,HasStructuralObjectClass};
|
use App\Rules\{DNExists,HasStructuralObjectClass};
|
||||||
|
|
||||||
class EntryAddRequest extends FormRequest
|
class EntryAddRequest extends FormRequest
|
||||||
{
|
{
|
||||||
|
private const LOGKEY = 'EAR';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the error messages for the defined validation rules.
|
* Get the error messages for the defined validation rules.
|
||||||
*
|
*
|
||||||
@ -112,6 +117,23 @@ class EntryAddRequest extends FormRequest
|
|||||||
$fail(__('You cannot select a template and an objectclass'));
|
$fail(__('You cannot select a template and an objectclass'));
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'_auto_value' => 'nullable|array|min:1',
|
||||||
|
'_auto_value.*' => [
|
||||||
|
'nullable',
|
||||||
|
function (string $attribute,mixed $value,\Closure $fail) {
|
||||||
|
$attr = preg_replace('/^_auto_value\./','',$attribute);
|
||||||
|
|
||||||
|
// If the value has been overritten, then our auto_value is invalid
|
||||||
|
if (! collect(request()->get($attr))->dot()->contains($value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$cache = Cache::get($attr.':'.Session::id());
|
||||||
|
Log::debug(sprintf('%s:Autovalue for Attribute [%s] in Session [%s] Retrieved [%d](%d)',self::LOGKEY,$attr,Session::id(),$cache,$value));
|
||||||
|
|
||||||
|
if ($cache !== (int)$value)
|
||||||
|
$fail(__('Lock expired, please re-submit.'));
|
||||||
|
}
|
||||||
|
]
|
||||||
])
|
])
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
@ -465,6 +465,21 @@ class Entry extends Model
|
|||||||
->has($key);
|
->has($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Did this query generate a size limit exception
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws \LdapRecord\ContainerException
|
||||||
|
*/
|
||||||
|
public function hasMore(): bool
|
||||||
|
{
|
||||||
|
return $this->getConnectionContainer()
|
||||||
|
->getConnection()
|
||||||
|
->getLdapConnection()
|
||||||
|
->getDetailedError()
|
||||||
|
?->getErrorCode() === 4;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an icon for a DN based on objectClass
|
* Return an icon for a DN based on objectClass
|
||||||
*
|
*
|
||||||
|
@ -95,5 +95,9 @@ return [
|
|||||||
'template' => [
|
'template' => [
|
||||||
'dir' => env('LDAP_TEMPLATE_DRIVER','templates'),
|
'dir' => env('LDAP_TEMPLATE_DRIVER','templates'),
|
||||||
'exclude_system' => env('LDAP_TEMPLATE_EXCLUDE_SYSTEM',FALSE),
|
'exclude_system' => env('LDAP_TEMPLATE_EXCLUDE_SYSTEM',FALSE),
|
||||||
|
'getnexnumber' => [
|
||||||
|
'gidnumber' => env('LDAP_TEMPLATE_GIDNUMBER_START', 1000),
|
||||||
|
'uidnumber' => env('LDAP_TEMPLATE_UIDNUMBER_START', 1000),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
@ -5,13 +5,26 @@
|
|||||||
@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)
|
||||||
<div class="input-group has-validation">
|
<!-- AutoValue Lock -->
|
||||||
@if($edit && (! $o->is_rdn))
|
@if($new && $template && ($av=$template->attributeValue($o->name_lc)))
|
||||||
<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="hidden" name="_auto_value[{{ $o->name_lc }}]" value="{{ $av }}">
|
||||||
@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
|
@endif
|
||||||
|
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
<input type="text"
|
||||||
|
@class([
|
||||||
|
'form-control',
|
||||||
|
'noedit'=>(! $edit) || ($o->is_rdn),
|
||||||
|
'is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)) || ($e=$errors->get('_auto_value.'.$o->name_lc)),
|
||||||
|
'mb-1',
|
||||||
|
'border-focus'=>! ($tv=$o->tagValuesOld($langtag))->contains($value),
|
||||||
|
'bg-success-subtle'=>$updated])
|
||||||
|
name="{{ $o->name_lc }}[{{ $langtag }}][]"
|
||||||
|
value="{{ $value ?: ($av ?? '') }}"
|
||||||
|
placeholder="{{ ! is_null($x=$tv->get($loop->index)) ? $x : '['.__('NEW').']' }}"
|
||||||
|
readonly
|
||||||
|
@disabled($o->isDynamic())>
|
||||||
|
|
||||||
<div class="invalid-feedback pb-2">
|
<div class="invalid-feedback pb-2">
|
||||||
@if($e)
|
@if($e)
|
||||||
{{ join('|',$e) }}
|
{{ join('|',$e) }}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
"uidNumber": {
|
"uidNumber": {
|
||||||
"display": "UID Number",
|
"display": "UID Number",
|
||||||
"readonly": true,
|
"readonly": true,
|
||||||
"value": "=php.GetNextNumber(/;uidNumber)",
|
"value": "=getNextNumber(/;uidNumber)",
|
||||||
"order": 6
|
"order": 6
|
||||||
},
|
},
|
||||||
"gidNumber": {
|
"gidNumber": {
|
||||||
@ -55,8 +55,7 @@
|
|||||||
"onchange": [
|
"onchange": [
|
||||||
"=autoFill(homeDirectory;/home/users/%gidNumber|0-0/T%/%uid|3-%)"
|
"=autoFill(homeDirectory;/home/users/%gidNumber|0-0/T%/%uid|3-%)"
|
||||||
],
|
],
|
||||||
"value": "=php.GetNextNumber(/;uidNumber)",
|
"value": "=getNextNumber(/;gidNumber)",
|
||||||
"value": "=php.PickList(/;(&(objectClass=posixGroup));gidNumber;%cn%;;;;cn)",
|
|
||||||
"order": 7
|
"order": 7
|
||||||
},
|
},
|
||||||
"homeDirectory": {
|
"homeDirectory": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user