481 lines
20 KiB
PHTML
481 lines
20 KiB
PHTML
<?php
|
|
|
|
/**
|
|
* @var array $values
|
|
* @var bool $passwordRequired
|
|
* @var string|null $passwordLabel
|
|
* @var string|null $passwordLegend
|
|
* @var string|null $passwordConfirmLabel
|
|
* @var string|null $formId
|
|
* @var bool $detailsOpenAll
|
|
* @var bool $showTenants
|
|
* @var array $tenants
|
|
* @var array $selectedTenantIds
|
|
* @var bool $showRoles
|
|
* @var array $roles
|
|
* @var array $selectedRoleIds
|
|
* @var bool $showDepartments
|
|
* @var array $departments
|
|
* @var array $selectedDepartmentIds
|
|
* @var bool $isReadOnly
|
|
* @var bool $showPermissions
|
|
* @var array $permissionRows
|
|
* @var bool $showPasswordResets
|
|
* @var array $passwordResets
|
|
* @var bool $showRememberTokens
|
|
* @var array $rememberTokens
|
|
*/
|
|
|
|
use MintyPHP\Session;
|
|
use MintyPHP\Service\User\UserService;
|
|
use MintyPHP\I18n;
|
|
|
|
$values = $values ?? [];
|
|
$isOwnAccount = (int) ($_SESSION['user']['id'] ?? 0) === (int) ($values['id'] ?? 0);
|
|
$passwordRequired = $passwordRequired ?? false;
|
|
$passwordLabel = $passwordLabel ?? t('Password');
|
|
$passwordLegend = $passwordLegend ?? t('Password');
|
|
$passwordConfirmLabel = $passwordConfirmLabel ?? t('Password (again)');
|
|
$formId = $formId ?? 'user-form';
|
|
$detailsOpenAll = $detailsOpenAll ?? false;
|
|
$showTenants = $showTenants ?? false;
|
|
$tenants = $tenants ?? [];
|
|
$selectedTenantIds = $selectedTenantIds ?? [];
|
|
$showRoles = $showRoles ?? false;
|
|
$roles = $roles ?? [];
|
|
$selectedRoleIds = $selectedRoleIds ?? [];
|
|
$showDepartments = $showDepartments ?? false;
|
|
$departments = $departments ?? [];
|
|
$selectedDepartmentIds = $selectedDepartmentIds ?? [];
|
|
$primaryTenantId = (int) ($values['primary_tenant_id'] ?? 0);
|
|
if (!$primaryTenantId && count($selectedTenantIds) === 1) {
|
|
$primaryTenantId = (int) $selectedTenantIds[0];
|
|
}
|
|
$isReadOnly = $isReadOnly ?? false;
|
|
$permissionRows = $permissionRows ?? [];
|
|
$showPermissions = $showPermissions ?? false;
|
|
$passwordResets = $passwordResets ?? [];
|
|
$showPasswordResets = $showPasswordResets ?? false;
|
|
$rememberTokens = $rememberTokens ?? [];
|
|
$showRememberTokens = $showRememberTokens ?? false;
|
|
$minLength = UserService::passwordMinLength();
|
|
$requiredAttr = $passwordRequired ? 'required' : '';
|
|
$readonlyAttr = $isReadOnly ? 'readonly' : '';
|
|
$disabledAttr = $isReadOnly ? 'disabled' : '';
|
|
$dataDisabledAttr = $isReadOnly ? 'data-disabled="true"' : '';
|
|
$locales = defined('APP_LOCALES') ? APP_LOCALES : [I18n::$defaultLocale];
|
|
$primaryTenantDisabled = $isReadOnly || count($selectedTenantIds) === 1;
|
|
$primaryTenantDisabledAttr = $primaryTenantDisabled ? 'disabled' : '';
|
|
$showOrganization = $showTenants || $showDepartments || $showRoles;
|
|
|
|
?>
|
|
<form id="<?php e($formId); ?>" method="post">
|
|
<div class="app-tabs" data-tabs data-tabs-param="tab" data-tabs-storage-key="admin-user-form">
|
|
<div class="app-tabs-nav">
|
|
<button type="button" data-tab="profile" data-tab-default><?php e(t('Profile')); ?></button>
|
|
<button type="button" data-tab="access"><?php e(t('Access & password')); ?></button>
|
|
<?php if ($showOrganization): ?>
|
|
<button type="button" data-tab="organization"><?php e(t('Organization')); ?></button>
|
|
<?php endif; ?>
|
|
<button type="button" data-tab="masterdata"><?php e(t('Master data')); ?></button>
|
|
<?php if ($showPermissions): ?>
|
|
<button type="button" data-tab="permissions"><?php e(t('Permissions')); ?></button>
|
|
<?php endif; ?>
|
|
<?php if ($showPasswordResets): ?>
|
|
<button type="button" data-tab="password-resets"><?php e(t('Password resets')); ?></button>
|
|
<?php endif; ?>
|
|
<?php if ($showRememberTokens): ?>
|
|
<button type="button" data-tab="login-tokens"><?php e(t('Login tokens')); ?></button>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div data-tab-panel="profile">
|
|
|
|
<div class="grid">
|
|
<label for="first_name">
|
|
<span><?php e(t('First name')); ?></span>
|
|
<input required type="text" name="first_name" id="first_name" value="<?php e($values['first_name'] ?? ''); ?>"
|
|
<?php e($readonlyAttr); ?> />
|
|
</label>
|
|
<label for="last_name">
|
|
<span><?php e(t('Last name')); ?></span>
|
|
<input required type="text" name="last_name" id="last_name" value="<?php e($values['last_name'] ?? ''); ?>"
|
|
<?php e($readonlyAttr); ?> />
|
|
</label>
|
|
</div>
|
|
<label for="email">
|
|
<span><?php e(t('Email')); ?></span>
|
|
<input required type="email" name="email" id="email" value="<?php e($values['email'] ?? ''); ?>" <?php e($readonlyAttr); ?> />
|
|
</label>
|
|
|
|
<hr>
|
|
<details name="profile-data" open>
|
|
<summary><?php e(t('Profile')); ?></summary>
|
|
<hr>
|
|
<label for="job_title">
|
|
<span><?php e(t('Job title')); ?></span>
|
|
<input type="text" name="job_title" id="job_title" value="<?php e($values['job_title'] ?? ''); ?>"
|
|
<?php e($readonlyAttr); ?> />
|
|
</label>
|
|
<label for="profile_description">
|
|
<span><?php e(t('Profile description')); ?></span>
|
|
<textarea name="profile_description" id="profile_description" rows="4"
|
|
<?php e($readonlyAttr); ?>><?php e($values['profile_description'] ?? ''); ?></textarea>
|
|
</label>
|
|
</details>
|
|
<hr>
|
|
<details name="preferences" open>
|
|
<summary><?php e(t('Preferences')); ?></summary>
|
|
<hr>
|
|
<div class="grid">
|
|
<?php if (allowUserTheme()): ?>
|
|
<label for="theme">
|
|
<span><?php e(t('Theme')); ?></span>
|
|
<?php
|
|
$themes = appThemes();
|
|
$selectedTheme = (string) ($values['theme'] ?? '');
|
|
if ($selectedTheme === '' || !isset($themes[$selectedTheme])) {
|
|
$selectedTheme = appDefaultTheme();
|
|
}
|
|
?>
|
|
<select name="theme" id="theme" <?php e($disabledAttr); ?>>
|
|
<?php foreach ($themes as $key => $label): ?>
|
|
<option value="<?php e($key); ?>" <?php if ($selectedTheme === $key) { ?>selected<?php } ?>>
|
|
<?php e(t($label)); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</label>
|
|
<?php endif; ?>
|
|
<label for="locale">
|
|
<span><?php e(t('Language')); ?></span>
|
|
<?php $selectedLocale = $values['locale'] ?? (I18n::$locale ?? I18n::$defaultLocale); ?>
|
|
<select name="locale" id="locale" <?php e($disabledAttr); ?>>
|
|
<?php foreach ($locales as $locale): ?>
|
|
<?php
|
|
$label = strtoupper($locale);
|
|
if ($locale === 'de') {
|
|
$label = t('German');
|
|
} elseif ($locale === 'en') {
|
|
$label = t('English');
|
|
}
|
|
?>
|
|
<option value="<?php e($locale); ?>" <?php if ($selectedLocale === $locale) { ?>selected<?php } ?>>
|
|
<?php e($label); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<div data-tab-panel="access">
|
|
|
|
<div class="grid">
|
|
<div>
|
|
<label for="password">
|
|
<span><?php e($passwordLabel); ?></span>
|
|
<input <?php e($requiredAttr); ?> type="password" name="password" id="password"
|
|
minlength="<?php e($minLength); ?>" autocomplete="new-password" <?php e($disabledAttr); ?> />
|
|
</label>
|
|
<label for="password2">
|
|
<span><?php e($passwordConfirmLabel); ?></span>
|
|
<input <?php e($requiredAttr); ?> type="password" name="password2" id="password2"
|
|
minlength="<?php e($minLength); ?>" autocomplete="new-password" <?php e($disabledAttr); ?> />
|
|
</label>
|
|
</div>
|
|
<div class="form-hint" data-password-hints data-password-input="#password" data-confirm-input="#password2"
|
|
data-email-input="#email" data-min-length="<?php e($minLength); ?>">
|
|
<strong><?php e(t('Password requirements')); ?></strong>
|
|
<ul class="form-hint-list">
|
|
<?php foreach (UserService::passwordHints() as $hint): ?>
|
|
<li data-rule="<?php e($hint['rule']); ?>"><?php e($hint['text']); ?></li>
|
|
<?php endforeach; ?>
|
|
<li data-rule="match"><?php e(t('Passwords must match')); ?></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<?php if (!$isOwnAccount): ?>
|
|
<label for="active">
|
|
<input type="checkbox" name="active" id="active" value="1" <?php e(($values['active'] ?? 0) ? 'checked' : ''); ?>
|
|
<?php e($disabledAttr); ?> />
|
|
<span><?php e(t('Active')); ?></span>
|
|
</label>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php if ($showOrganization): ?>
|
|
<div data-tab-panel="organization">
|
|
<?php if ($showTenants): ?>
|
|
<details name="assignments-tenants" open>
|
|
<summary><?php e(t('Tenants')); ?></summary>
|
|
<hr>
|
|
<label>
|
|
<span>
|
|
<?php e(t('Primary tenant')); ?>
|
|
</span>
|
|
</label>
|
|
<?php if ($primaryTenantDisabled && $primaryTenantId > 0): ?>
|
|
<input type="hidden" name="primary_tenant_id" value="<?php e((string) $primaryTenantId); ?>">
|
|
<?php endif; ?>
|
|
<select id="user-primary-tenant" name="primary_tenant_id" <?php e($primaryTenantDisabledAttr); ?>
|
|
<?php e($disabledAttr); ?>>
|
|
<?php if (count($selectedTenantIds) !== 1): ?>
|
|
<option value="">
|
|
<?php e(t('Select primary tenant')); ?>
|
|
</option>
|
|
<?php endif; ?>
|
|
<?php foreach ($tenants as $tenant): ?>
|
|
<?php
|
|
$tenantId = (int) ($tenant['id'] ?? 0);
|
|
if (!in_array($tenantId, $selectedTenantIds ?? [], true)) {
|
|
continue;
|
|
}
|
|
$selected = $tenantId === $primaryTenantId ? 'selected' : '';
|
|
?>
|
|
<option value="<?php e((string) $tenantId); ?>" <?php e($selected); ?>>
|
|
<?php e($tenant['description'] ?? ''); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php multiSelectForm('user-tenants', 'tenant_ids', 'Assigned tenants', 'Select tenants', $tenants, $selectedTenantIds, $isReadOnly); ?>
|
|
</details>
|
|
<hr>
|
|
<?php endif; ?>
|
|
<?php if ($showDepartments || $showRoles): ?>
|
|
<details name="assignments-structure" open>
|
|
<summary><?php e(t('Departments & roles')); ?></summary>
|
|
<hr>
|
|
<div class="grid">
|
|
<?php if ($showDepartments): ?>
|
|
<div>
|
|
<?php multiSelectForm('user-departments', 'department_ids', 'Assigned departments', 'Select departments', $departments, $selectedDepartmentIds, $isReadOnly); ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php if ($showRoles): ?>
|
|
<div>
|
|
<?php multiSelectForm('user-roles', 'role_ids', 'Assigned roles', 'Select roles', $roles, $selectedRoleIds, $isReadOnly); ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</details>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div data-tab-panel="masterdata">
|
|
<details name="contact-data" open>
|
|
<summary><?php e(t('Contact')); ?></summary>
|
|
<hr>
|
|
<div class="grid">
|
|
<label for="phone">
|
|
<span><?php e(t('Phone')); ?></span>
|
|
<input type="tel" name="phone" id="phone" autocomplete="tel" value="<?php e($values['phone'] ?? ''); ?>"
|
|
<?php e($readonlyAttr); ?> />
|
|
</label>
|
|
<label for="mobile">
|
|
<span><?php e(t('Mobile')); ?></span>
|
|
<input type="tel" name="mobile" id="mobile" autocomplete="tel" value="<?php e($values['mobile'] ?? ''); ?>"
|
|
<?php e($readonlyAttr); ?> />
|
|
</label>
|
|
<label for="short_dial">
|
|
<span><?php e(t('Short dial')); ?></span>
|
|
<input type="text" name="short_dial" id="short_dial" inputmode="numeric"
|
|
value="<?php e($values['short_dial'] ?? ''); ?>" <?php e($readonlyAttr); ?> />
|
|
</label>
|
|
</div>
|
|
</details>
|
|
<hr>
|
|
<details name="address-data" open>
|
|
<summary><?php e(t('Address')); ?></summary>
|
|
<hr>
|
|
<label for="address">
|
|
<span><?php e(t('Address')); ?></span>
|
|
<input type="text" name="address" id="address" value="<?php e($values['address'] ?? ''); ?>" <?php e($readonlyAttr); ?> />
|
|
</label>
|
|
<div class="grid">
|
|
<label for="postal_code">
|
|
<span><?php e(t('Postal code')); ?></span>
|
|
<input type="text" name="postal_code" id="postal_code" value="<?php e($values['postal_code'] ?? ''); ?>" <?php e($readonlyAttr); ?> />
|
|
</label>
|
|
<label for="city">
|
|
<span><?php e(t('City')); ?></span>
|
|
<input type="text" name="city" id="city" value="<?php e($values['city'] ?? ''); ?>" <?php e($readonlyAttr); ?> />
|
|
</label>
|
|
</div>
|
|
<div class="grid">
|
|
<label for="region">
|
|
<span><?php e(t('Region')); ?></span>
|
|
<input type="text" name="region" id="region" value="<?php e($values['region'] ?? ''); ?>" <?php e($readonlyAttr); ?> />
|
|
</label>
|
|
<label for="country">
|
|
<span><?php e(t('Country')); ?></span>
|
|
<input type="text" name="country" id="country" value="<?php e($values['country'] ?? ''); ?>" <?php e($readonlyAttr); ?> />
|
|
</label>
|
|
</div>
|
|
</details>
|
|
<hr>
|
|
<details name="employment-data" open>
|
|
<summary><?php e(t('Employment')); ?></summary>
|
|
<hr>
|
|
<label for="hire_date">
|
|
<span><?php e(t('Hire date')); ?></span>
|
|
<input type="date" name="hire_date" id="hire_date" value="<?php e($values['hire_date'] ?? ''); ?>" <?php e($readonlyAttr); ?> />
|
|
</label>
|
|
</details>
|
|
</div>
|
|
<?php if ($showPermissions): ?>
|
|
<div data-tab-panel="permissions">
|
|
<div class="overflow-auto white-space-nowrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th><?php e(t('Description')); ?></th>
|
|
<th><?php e(t('Permission key')); ?></th>
|
|
<th><?php e(t('Permission origin')); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($permissionRows as $permissionRow): ?>
|
|
<?php
|
|
$permissionKey = (string) ($permissionRow['key'] ?? '');
|
|
$labelKey = 'perm.' . $permissionKey;
|
|
$label = t($labelKey);
|
|
if ($label === $labelKey) {
|
|
$label = (string) ($permissionRow['description'] ?? '');
|
|
if ($label === '') {
|
|
$label = $permissionKey;
|
|
}
|
|
}
|
|
$rolesList = [];
|
|
if (!empty($permissionRow['roles']) && is_array($permissionRow['roles'])) {
|
|
$rolesList = $permissionRow['roles'];
|
|
}
|
|
?>
|
|
<tr>
|
|
<td><?php e($label); ?></td>
|
|
<td><code><?php e($permissionKey); ?></code></td>
|
|
<td>
|
|
<?php if ($rolesList): ?>
|
|
<?php foreach ($rolesList as $roleLabel): ?>
|
|
<span class="badge" data-variant="neutral"><?php e($roleLabel); ?></span>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php if ($showPasswordResets): ?>
|
|
<div data-tab-panel="password-resets">
|
|
<div class="overflow-auto white-space-nowrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th><?php e(t('Requested at')); ?></th>
|
|
<th><?php e(t('Expires at')); ?></th>
|
|
<th><?php e(t('Used at')); ?></th>
|
|
<th><?php e(t('Attempts')); ?></th>
|
|
<th><?php e(t('Status')); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($passwordResets as $reset): ?>
|
|
<?php
|
|
$expiresAt = (string) ($reset['expires_at'] ?? '');
|
|
$usedAt = (string) ($reset['used_at'] ?? '');
|
|
$status = t('Active');
|
|
$statusVariant = 'success';
|
|
if ($usedAt !== '') {
|
|
$status = t('Used');
|
|
$statusVariant = 'neutral';
|
|
} elseif ($expiresAt !== '') {
|
|
try {
|
|
$expired = (new \DateTimeImmutable($expiresAt)) <= new \DateTimeImmutable('now');
|
|
if ($expired) {
|
|
$status = t('Expired');
|
|
$statusVariant = 'warning';
|
|
}
|
|
} catch (\Exception $e) {
|
|
// fallback: keep Active
|
|
}
|
|
}
|
|
?>
|
|
<tr>
|
|
<td><?php e(dt($reset['created'] ?? '')); ?></td>
|
|
<td><?php e($expiresAt !== '' ? dt($expiresAt) : '-'); ?></td>
|
|
<td><?php e($usedAt !== '' ? dt($usedAt) : '-'); ?></td>
|
|
<td><?php e((string) ($reset['attempts'] ?? 0)); ?></td>
|
|
<td>
|
|
<span class="badge" data-variant="<?php e($statusVariant); ?>">
|
|
<?php e($status); ?>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php if ($showRememberTokens): ?>
|
|
<div data-tab-panel="login-tokens">
|
|
<div class="overflow-auto white-space-nowrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th><?php e(t('Created at')); ?></th>
|
|
<th><?php e(t('Expires at')); ?></th>
|
|
<th><?php e(t('Last used')); ?></th>
|
|
<th><?php e(t('Status')); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($rememberTokens as $token): ?>
|
|
<?php
|
|
$expiresAt = (string) ($token['expires_at'] ?? '');
|
|
$expiredByAdminAt = (string) ($token['expired_by_admin_at'] ?? '');
|
|
$lastUsed = (string) ($token['last_used'] ?? '');
|
|
$status = t('Active');
|
|
$statusVariant = 'success';
|
|
if ($expiresAt !== '') {
|
|
try {
|
|
$expired = (new \DateTimeImmutable($expiresAt)) <= new \DateTimeImmutable('now');
|
|
if ($expired) {
|
|
if ($expiredByAdminAt !== '') {
|
|
$status = t('Expired by admin');
|
|
$statusVariant = 'warning';
|
|
} else {
|
|
$status = t('Expired');
|
|
$statusVariant = 'warning';
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
// fallback: keep Active
|
|
}
|
|
}
|
|
?>
|
|
<tr>
|
|
<td><?php e(dt($token['created'] ?? '')); ?></td>
|
|
<td><?php e($expiresAt !== '' ? dt($expiresAt) : '-'); ?></td>
|
|
<td><?php e($lastUsed !== '' ? dt($lastUsed) : '-'); ?></td>
|
|
<td>
|
|
<span class="badge" data-variant="<?php e($statusVariant); ?>">
|
|
<?php e($status); ?>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php Session::getCsrfInput(); ?>
|
|
</form>
|