2026-02-04 23:31:53 +01:00
<?php
/**
* @var array $values
* @var string|null $formId
* @var bool $detailsOpenAll
* @var bool $isReadOnly
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
* @var bool $showDangerZone
* @var string|null $dangerZoneDeleteFormId
* @var string|null $dangerZoneWarning
* @var string|null $dangerZoneActionLabel
* @var bool $showCustomFieldsTab
* @var bool $showSsoTab
* @var string|null $customFieldTenantUuid
* @var array $customFieldDefinitions
* @var array $ssoUiState
2026-02-04 23:31:53 +01:00
*/
use MintyPHP\Session;
2026-03-04 15:56:58 +01:00
$tenantSsoService = app(\MintyPHP\Service\Auth\TenantSsoService::class);
2026-02-04 23:31:53 +01:00
$values = $values ?? [];
$formId = $formId ?? 'tenant-form';
$detailsOpenAll = $detailsOpenAll ?? false;
$isReadOnly = $isReadOnly ?? false;
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
$showDangerZone = (bool) ($showDangerZone ?? false);
$dangerZoneDeleteFormId = (string) ($dangerZoneDeleteFormId ?? '');
$dangerZoneWarning = (string) ($dangerZoneWarning ?? t('This action cannot be undone.'));
$dangerZoneActionLabel = (string) ($dangerZoneActionLabel ?? t('Delete'));
$showCustomFieldsTab = (bool) ($showCustomFieldsTab ?? false);
$showSsoTab = (bool) ($showSsoTab ?? false);
$customFieldTenantUuid = trim((string) ($customFieldTenantUuid ?? ''));
$customFieldDefinitions = is_array($customFieldDefinitions ?? null) ? $customFieldDefinitions : [];
2026-02-04 23:31:53 +01:00
$readonlyAttr = $isReadOnly ? 'readonly' : '';
$disabledAttr = $isReadOnly ? 'disabled' : '';
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
$themes = appThemes();
2026-02-04 23:31:53 +01:00
$primaryColor = (string) ($values['primary_color'] ?? '');
$defaultPrimaryColor = appSetting('app_primary_color') ?? '#2fa4a4';
$useDefaultPrimaryColor = $primaryColor === '';
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
$tenantDefaultTheme = strtolower(trim((string) ($values['default_theme'] ?? '')));
if ($tenantDefaultTheme !== '' & & !isset($themes[$tenantDefaultTheme])) {
$tenantDefaultTheme = '';
}
$rawAllowUserThemeMode = $values['allow_user_theme_mode'] ?? ($values['allow_user_theme'] ?? null);
if ($rawAllowUserThemeMode === null || $rawAllowUserThemeMode === '') {
$tenantAllowUserThemeMode = '';
} elseif (in_array(strtolower((string) $rawAllowUserThemeMode), ['1', 'true', 'yes', 'on'], true)) {
$tenantAllowUserThemeMode = '1';
} else {
$tenantAllowUserThemeMode = '0';
}
$microsoftEnabledRaw = $values['microsoft_enabled'] ?? $values['enabled'] ?? false;
$microsoftEnabled = in_array(strtolower(trim((string) $microsoftEnabledRaw)), ['1', 'true', 'yes', 'on'], true);
$microsoftEnforceRaw = $values['enforce_microsoft_login'] ?? false;
$microsoftEnforce = in_array(strtolower(trim((string) $microsoftEnforceRaw)), ['1', 'true', 'yes', 'on'], true);
$entraTenantId = strtolower(trim((string) ($values['entra_tenant_id'] ?? '')));
$allowedDomains = trim((string) ($values['allowed_domains'] ?? ''));
$useSharedAppRaw = $values['use_shared_app'] ?? 1;
$useSharedApp = !in_array(strtolower(trim((string) $useSharedAppRaw)), ['0', 'false', 'no', 'off'], true);
$clientIdOverride = trim((string) ($values['client_id_override'] ?? ''));
$syncProfileOnLoginRaw = $values['sync_profile_on_login'] ?? false;
$syncProfileOnLogin = in_array(strtolower(trim((string) $syncProfileOnLoginRaw)), ['1', 'true', 'yes', 'on'], true);
2026-02-23 12:58:19 +01:00
$syncFieldOptions = $tenantSsoService->profileSyncFieldOptions();
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
$syncProfileFieldsRaw = $values['sync_profile_fields_list'] ?? ($values['sync_profile_fields'] ?? []);
2026-02-23 12:58:19 +01:00
$syncProfileFields = $tenantSsoService->normalizeProfileSyncFields($syncProfileFieldsRaw);
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
$tenantId = (int) ($values['id'] ?? 0);
$ssoUiState = is_array($ssoUiState ?? null)
? $ssoUiState
2026-02-23 12:58:19 +01:00
: $tenantSsoService->buildMicrosoftUiState($tenantId, $values);
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
$ssoConfigComplete = !empty($ssoUiState['config_complete']);
$ssoConfigErrorLabel = trim((string) ($ssoUiState['config_error_label'] ?? ''));
2026-03-12 10:01:24 +01:00
$ssoPasswordMode = (string) ($ssoUiState['password_mode'] ?? ($microsoftEnforce ? 'microsoft_only' : ($microsoftEnabled ? 'local_and_microsoft' : 'local_only')));
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
$ssoCredentialSource = (string) ($ssoUiState['credential_source'] ?? ($useSharedApp ? 'shared' : 'override'));
$ssoSyncNeedsGraph = !empty($ssoUiState['sync_needs_graph']);
2026-03-12 10:01:24 +01:00
$ssoPasswordModeLabel = match ($ssoPasswordMode) {
'microsoft_only' => t('Microsoft only'),
'local_and_microsoft' => t('Local password + Microsoft'),
default => t('Local password only'),
};
$ssoCredentialSourceLabel = $ssoCredentialSource === 'shared' ? t('Shared app') : t('Tenant override');
$ssoStatusTiles = [
[
'label' => t('Microsoft login'),
'value' => $microsoftEnabled ? t('Active') : t('Inactive'),
'tone' => $microsoftEnabled ? 'success' : 'neutral',
],
[
'label' => t('Configuration complete'),
'value' => $ssoConfigComplete ? t('Yes') : t('No'),
'tone' => $ssoConfigComplete ? 'success' : 'warning',
],
[
'label' => t('Password login mode'),
'value' => $ssoPasswordModeLabel,
'tone' => match ($ssoPasswordMode) {
'microsoft_only' => 'warning',
'local_and_microsoft' => 'info',
default => 'neutral',
},
],
[
'label' => t('Credentials source'),
'value' => $ssoCredentialSourceLabel,
'tone' => $ssoCredentialSource === 'shared' ? 'info' : 'neutral',
],
];
2026-03-12 14:09:06 +01:00
$allowedDomainsList = preg_split('/[\s,;]+/', strtolower($allowedDomains), -1, PREG_SPLIT_NO_EMPTY);
$allowedDomainsList = is_array($allowedDomainsList) ? array_values(array_unique($allowedDomainsList)) : [];
$allowedDomainsCount = count($allowedDomainsList);
$syncFieldCount = count($syncProfileFields);
$ssoSetupBadgeVariant = $microsoftEnabled ? 'success' : 'neutral';
$ssoSetupBadgeLabel = sprintf(
'%s: %s',
t('Microsoft login'),
$microsoftEnabled ? t('Active') : t('Inactive')
);
$ssoShowConfigBadge = $microsoftEnabled;
$ssoConfigBadgeVariant = $microsoftEnabled ? ($ssoConfigComplete ? 'success' : 'warning') : 'neutral';
$ssoConfigBadgeLabel = sprintf(
'%s: %s',
t('Configuration status'),
$ssoConfigComplete ? t('Complete') : t('Incomplete')
);
$ssoPolicyBadgeVariant = match ($ssoPasswordMode) {
'microsoft_only' => 'warning',
'local_and_microsoft' => 'info',
default => 'neutral',
};
$ssoSyncBadgeVariant = $syncProfileOnLogin ? 'info' : 'neutral';
$ssoSyncBadgeLabel = $syncProfileOnLogin ? t('Active') : t('Inactive');
$ssoDomainsBadgeVariant = $allowedDomainsCount > 0 ? 'info' : 'neutral';
$ssoDomainsBadgeLabel = $allowedDomainsCount > 0
? sprintf('%d %s', $allowedDomainsCount, t('Allowed email domains'))
: t('No domain restrictions');
$ssoCredentialsBadgeVariant = $ssoCredentialSource === 'shared' ? 'info' : 'warning';
$ssoCredentialsBadgeLabel = $ssoCredentialSourceLabel;
$openSetupCard = $detailsOpenAll || !$microsoftEnabled || ($microsoftEnabled & & !$ssoConfigComplete);
$openPolicyCard = $detailsOpenAll || ($microsoftEnabled & & $microsoftEnforce);
$openAccessCard = $detailsOpenAll || ($microsoftEnabled & & ($allowedDomainsCount > 0 || $syncProfileOnLogin));
$openOverrideCard = $detailsOpenAll;
2026-02-04 23:31:53 +01:00
?>
2026-03-04 15:56:58 +01:00
< form id = " <?php e ( $formId ); ?> " method = "post" data-standard-detail-form = "1" >
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
< div class = "app-tabs" data-tabs data-app-component = "tabs" data-tabs-param = "tab" data-tabs-storage-key = "admin-tenant-form" <?php if ( ! empty ( $ignoreTabStorage )) { echo ' data-tabs-ignore-storage' ; } ?> >
2026-02-04 23:31:53 +01:00
< div class = "app-tabs-nav" >
< button type = "button" data-tab = "basic" data-tab-default > <?php e ( t ( 'Master data' )); ?> </ button >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php if ( $showCustomFieldsTab ) : ?>
< button type = "button" data-tab = "custom_fields" > <?php e ( t ( 'User custom fields' )); ?> </ button >
<?php endif ; ?>
<?php if ( $showSsoTab ) : ?>
< button type = "button" data-tab = "sso" > <?php e ( t ( 'SSO' )); ?> </ button >
<?php endif ; ?>
< button type = "button" data-tab = "visibility" > <?php e ( t ( 'Visibility' )); ?> </ button >
<?php if ( $showDangerZone && $dangerZoneDeleteFormId !== '' ) : ?>
< button type = "button" data-tab = "danger" > <?php e ( t ( 'Danger zone' )); ?> </ button >
<?php endif ; ?>
2026-02-04 23:31:53 +01:00
< / div >
< div data-tab-panel = "basic" >
2026-02-11 19:28:12 +01:00
< div class = "grid" >
< label for = "description" >
< span > <?php e ( t ( 'Description' )); ?> </ span >
< input required type = "text" name = "description" id = "description" value = " <?php e ( $values [ 'description' ] ?? '' ); ?> "
<?php e ( $readonlyAttr ); ?> />
< / label >
< / div >
2026-02-04 23:31:53 +01:00
< 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 = "country" >
< span > <?php e ( t ( 'Country' )); ?> </ span >
< input type = "text" name = "country" id = "country" value = " <?php e ( $values [ 'country' ] ?? '' ); ?> " <?php e ( $readonlyAttr ); ?> />
< / label >
< 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 >
< / div >
< div class = "grid" >
< label for = "vat_id" >
< span > <?php e ( t ( 'VAT ID' )); ?> </ span >
< input type = "text" name = "vat_id" id = "vat_id" value = " <?php e ( $values [ 'vat_id' ] ?? '' ); ?> " <?php e ( $readonlyAttr ); ?> />
< / label >
< label for = "tax_number" >
< span > <?php e ( t ( 'Tax number' )); ?> </ span >
< input type = "text" name = "tax_number" id = "tax_number" value = " <?php e ( $values [ 'tax_number' ] ?? '' ); ?> " <?php e ( $readonlyAttr ); ?> />
< / label >
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< hr >
< details name = "tenant-master-contact" <?php e ( $detailsOpenAll ? '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" value = " <?php e ( $values [ 'phone' ] ?? '' ); ?> " <?php e ( $readonlyAttr ); ?> />
< / label >
< label for = "fax" >
< span > <?php e ( t ( 'Fax' )); ?> </ span >
< input type = "tel" name = "fax" id = "fax" value = " <?php e ( $values [ 'fax' ] ?? '' ); ?> " <?php e ( $readonlyAttr ); ?> />
< / label >
< / div >
< div class = "grid" >
< label for = "email" >
< span > <?php e ( t ( 'Email' )); ?> </ span >
< input type = "email" name = "email" id = "email" value = " <?php e ( $values [ 'email' ] ?? '' ); ?> " <?php e ( $readonlyAttr ); ?> />
< / label >
< label for = "website" >
< span > <?php e ( t ( 'Website' )); ?> </ span >
< input type = "url" name = "website" id = "website" value = " <?php e ( $values [ 'website' ] ?? '' ); ?> " <?php e ( $readonlyAttr ); ?> />
< / label >
< / div >
< div class = "grid" >
< label for = "support_email" >
< span > <?php e ( t ( 'Support email' )); ?> </ span >
< input type = "email" name = "support_email" id = "support_email" value = " <?php e ( $values [ 'support_email' ] ?? '' ); ?> "
<?php e ( $readonlyAttr ); ?> />
< / label >
< label for = "support_phone" >
< span > <?php e ( t ( 'Support phone' )); ?> </ span >
< input type = "tel" name = "support_phone" id = "support_phone" value = " <?php e ( $values [ 'support_phone' ] ?? '' ); ?> "
<?php e ( $readonlyAttr ); ?> />
< / label >
< / div >
< label for = "billing_email" >
< span > <?php e ( t ( 'Billing email' )); ?> </ span >
< input type = "email" name = "billing_email" id = "billing_email" value = " <?php e ( $values [ 'billing_email' ] ?? '' ); ?> "
<?php e ( $readonlyAttr ); ?> />
2026-02-04 23:31:53 +01:00
< / label >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / details >
< hr >
< details name = "tenant-master-legal" <?php e ( $detailsOpenAll ? 'open' : '' ); ?> >
< summary > <?php e ( t ( 'Legal' )); ?> </ summary >
< hr >
< div class = "grid" >
< label for = "privacy_url" >
< span > <?php e ( t ( 'Privacy URL' )); ?> </ span >
< input type = "url" name = "privacy_url" id = "privacy_url" value = " <?php e ( $values [ 'privacy_url' ] ?? '' ); ?> "
<?php e ( $readonlyAttr ); ?> />
< / label >
< label for = "imprint_url" >
< span > <?php e ( t ( 'Imprint URL' )); ?> </ span >
< input type = "url" name = "imprint_url" id = "imprint_url" value = " <?php e ( $values [ 'imprint_url' ] ?? '' ); ?> "
<?php e ( $readonlyAttr ); ?> />
< / label >
< / div >
< / details >
2026-02-04 23:31:53 +01:00
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php if ( $showCustomFieldsTab ) : ?>
< div data-tab-panel = "custom_fields" >
<?php if ( $customFieldTenantUuid === '' ) : ?>
2026-03-13 09:49:11 +01:00
<?php
$emptyState = [
'message' => t('Save tenant first to manage custom fields'),
'size' => 'compact',
];
if (!$isReadOnly) {
$emptyState['action'] = [
'label' => t('Create'),
'form' => $formId,
];
}
require templatePath('partials/app-empty-state.phtml');
?>
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php else : ?>
< blockquote data-variant = "info" >
<?php e ( t ( 'Define custom fields here. They appear per user in the "Custom fields" tab and can be maintained there.' )); ?>
< / blockquote >
2026-03-12 12:11:31 +01:00
< details class = "app-details-card" name = "tenant-custom-fields-create" >
< summary >
< span class = "app-details-card-summary-title" >
<?php e ( t ( 'Add custom field' )); ?>
< / span >
< / summary >
< div class = "app-details-card-container" >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< div class = "grid" >
2026-03-12 12:11:31 +01:00
< label for = "tenant-custom-field-label" >
< span > <?php e ( t ( 'Label' )); ?> </ span >
< input
id="tenant-custom-field-label"
form="tenant-custom-field-create-form"
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
required
type="text"
2026-03-12 12:11:31 +01:00
name="label"
<?php e ( $disabledAttr ); ?> >
< small class = "muted" > <?php e ( t ( 'The label is shown in user custom fields and in address book filters.' )); ?> </ small >
< / label >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< label for = "tenant-custom-field-type" >
< span > <?php e ( t ( 'Field type' )); ?> </ span >
< select
id="tenant-custom-field-type"
form="tenant-custom-field-create-form"
name="type"
data-custom-field-type-source
data-custom-field-group="tenant-custom-field-create"
2026-03-12 12:11:31 +01:00
<?php e ( $disabledAttr ); ?> >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< option value = "text" > <?php e ( t ( 'Text' )); ?> </ option >
< option value = "textarea" > <?php e ( t ( 'Textarea' )); ?> </ option >
< option value = "select" > <?php e ( t ( 'Select' )); ?> </ option >
< option value = "multiselect" > <?php e ( t ( 'Multiselect' )); ?> </ option >
< option value = "boolean" > <?php e ( t ( 'Boolean' )); ?> </ option >
< option value = "date" > <?php e ( t ( 'Date' )); ?> </ option >
< / select >
< small class = "muted" > <?php e ( t ( 'The field type controls input and filter behavior.' )); ?> </ small >
< / label >
< / div >
< div class = "grid" >
< fieldset data-custom-field-filterable-target data-custom-field-group = "tenant-custom-field-create" >
< label class = "app-field" >
2026-03-12 09:41:18 +01:00
< input form = "tenant-custom-field-create-form" role = "switch" type = "checkbox" name = "is_filterable" value = "1"
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
data-custom-field-filterable-input
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Filterable' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'Filterable fields are available in address book filters.' )); ?> </ small >
< / fieldset >
< fieldset >
< label class = "app-field" >
2026-03-12 09:41:18 +01:00
< input form = "tenant-custom-field-create-form" role = "switch" type = "checkbox" name = "active" value = "1" checked
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Active' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'Inactive fields are hidden in forms and filters.' )); ?> </ small >
< / fieldset >
< / div >
< div data-custom-field-options-target data-custom-field-group = "tenant-custom-field-create" >
< label for = "tenant-custom-field-options-text" >
< span > <?php e ( t ( 'Options (one per line)' )); ?> </ span >
< textarea
id="tenant-custom-field-options-text"
form="tenant-custom-field-create-form"
name="options_text"
rows="5"
placeholder="<?php e ( t ( 'Example: manager|Manager' )); ?> "
2026-03-12 12:11:31 +01:00
<?php e ( $readonlyAttr ); ?> ></ textarea >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< small class = "muted" > <?php e ( t ( 'One option per line. Optional format: key|Label.' )); ?> </ small >
< / label >
< / div >
2026-03-22 16:48:57 +01:00
< button type = "submit" class = "app-action-success" form = "tenant-custom-field-create-form" <?php e ( $disabledAttr ); ?> >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php e ( t ( 'Add custom field' )); ?>
< / button >
2026-03-12 12:11:31 +01:00
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / details >
<?php if ( ! $customFieldDefinitions ) : ?>
2026-03-11 21:38:15 +01:00
<?php
$emptyState = [
'message' => t('No custom fields configured yet'),
'size' => 'compact',
];
require templatePath('partials/app-empty-state.phtml');
?>
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php endif ; ?>
<?php foreach ( $customFieldDefinitions as $definition ) : ?>
<?php
$definitionId = (int) ($definition['id'] ?? 0);
$definitionUuid = (string) ($definition['uuid'] ?? '');
if ($definitionId < = 0 || $definitionUuid === '') {
continue;
}
$updateFormId = 'tenant-custom-field-update-form-' . $definitionId;
$deleteFormId = 'tenant-custom-field-delete-form-' . $definitionId;
$typeGroupId = 'tenant-custom-field-edit-' . $definitionId;
$definitionType = strtolower((string) ($definition['type'] ?? 'text'));
$definitionTypeLabel = t(ucfirst($definitionType));
$definitionOptions = is_array($definition['options'] ?? null) ? $definition['options'] : [];
$definitionOptionsText = '';
foreach ($definitionOptions as $option) {
$optionKey = trim((string) ($option['option_key'] ?? ''));
$optionLabel = trim((string) ($option['label'] ?? ''));
if ($optionLabel === '') {
continue;
}
$definitionOptionsText .= ($definitionOptionsText === '' ? '' : "\n")
. ($optionKey !== '' ? ($optionKey . '|') : '')
. $optionLabel;
}
?>
2026-03-12 12:11:31 +01:00
< details class = "app-details-card" name = "tenant-custom-fields" >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< summary >
2026-03-12 12:11:31 +01:00
< span class = "app-details-card-summary-title" >
<?php e (( string ) ( $definition [ 'label' ] ?? '' )); ?>
< / span >
< span class = "app-details-card-summary-meta" >
< span class = "badge" data-variant = "neutral" > <?php e ( $definitionTypeLabel ); ?> </ span >
< / span >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / summary >
2026-03-12 12:11:31 +01:00
< div class = "app-details-card-container" >
< div class = "grid" >
< label >
< span > <?php e ( t ( 'Label' )); ?> </ span >
< input
type="text"
form="<?php e ( $updateFormId ); ?> "
name="label"
required
value="<?php e (( string ) ( $definition [ 'label' ] ?? '' )); ?> "
<?php e ( $readonlyAttr ); ?> >
< small class = "muted" > <?php e ( t ( 'The label is shown in user custom fields and in address book filters.' )); ?> </ small >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / label >
2026-03-12 12:11:31 +01:00
< label >
< span > <?php e ( t ( 'Field type' )); ?> </ span >
< select
form="<?php e ( $updateFormId ); ?> "
name="type"
data-custom-field-type-source
data-custom-field-group="<?php e ( $typeGroupId ); ?> "
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php e ( $disabledAttr ); ?> >
2026-03-12 12:11:31 +01:00
<?php foreach ([ 'text' , 'textarea' , 'select' , 'multiselect' , 'boolean' , 'date' ] as $typeOption ) : ?>
< option value = " <?php e ( $typeOption ); ?> " <?php e ( $definitionType === $typeOption ? 'selected' : '' ); ?> >
<?php e ( t ( ucfirst ( $typeOption ))); ?>
< / option >
<?php endforeach ; ?>
< / select >
< small class = "muted" > <?php e ( t ( 'The field type controls input and filter behavior.' )); ?> </ small >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / label >
2026-03-12 12:11:31 +01:00
< / div >
< div class = "grid" >
< fieldset data-custom-field-filterable-target data-custom-field-group = " <?php e ( $typeGroupId ); ?> " >
< label class = "app-field" >
< input type = "checkbox" role = "switch" form = " <?php e ( $updateFormId ); ?> " name = "is_filterable" value = "1"
data-custom-field-filterable-input
<?php e ( ! empty ( $definition [ 'is_filterable' ]) ? 'checked' : '' ); ?>
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Filterable' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'Filterable fields are available in address book filters.' )); ?> </ small >
< / fieldset >
< fieldset >
< label class = "app-field" >
< input type = "hidden" form = " <?php e ( $updateFormId ); ?> " name = "active" value = "0" >
< input type = "checkbox" role = "switch" form = " <?php e ( $updateFormId ); ?> " name = "active" value = "1"
<?php e ( ! empty ( $definition [ 'active' ]) ? 'checked' : '' ); ?>
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Active' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'Inactive fields are hidden in forms and filters.' )); ?> </ small >
< / fieldset >
< / div >
< div data-custom-field-options-target data-custom-field-group = " <?php e ( $typeGroupId ); ?> " >
< label >
< span > <?php e ( t ( 'Options (one per line)' )); ?> </ span >
< textarea
form="<?php e ( $updateFormId ); ?> "
name="options_text"
rows="5"
placeholder="<?php e ( t ( 'Example: manager|Manager' )); ?> "
<?php e ( $readonlyAttr ); ?> ><?php e ( $definitionOptionsText ); ?> </ textarea >
< / label >
< small class = "muted" > <?php e ( t ( 'One option per line. Optional format: key|Label.' )); ?> </ small >
< / div >
< hr >
< div class = "grid" >
2026-03-22 16:48:57 +01:00
< button type = "submit" class = "app-action-success" form = " <?php e ( $updateFormId ); ?> " <?php e ( $disabledAttr ); ?> >
2026-03-12 12:11:31 +01:00
<?php e ( t ( 'Save' )); ?>
< / button >
< button type = "submit" class = "danger" form = " <?php e ( $deleteFormId ); ?> " <?php e ( $disabledAttr ); ?> >
<?php e ( t ( 'Delete custom field' )); ?>
< / button >
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / div >
< / details >
<?php endforeach ; ?>
<?php endif ; ?>
2026-02-04 23:31:53 +01:00
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php endif ; ?>
2026-02-04 23:31:53 +01:00
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php if ( $showSsoTab ) : ?>
< div data-tab-panel = "sso" data-tenant-sso-root >
2026-03-12 10:01:24 +01:00
< small class = "app-form-info-tiles-title" > <?php e ( t ( 'SSO configuration status' )); ?> </ small >
< div class = "app-form-info-tiles" >
<?php foreach ( $ssoStatusTiles as $ssoStatusTile ) : ?>
<?php
$infoTileLabel = (string) ($ssoStatusTile['label'] ?? '');
$infoTileValue = (string) ($ssoStatusTile['value'] ?? '');
$infoTileTone = (string) ($ssoStatusTile['tone'] ?? 'neutral');
require templatePath('partials/app-form-info-tile.phtml');
?>
<?php endforeach ; ?>
< / div >
2026-03-12 14:09:06 +01:00
<?php if ( $microsoftEnabled && ! $ssoConfigComplete ) : ?>
< blockquote data-variant = "warning" >
< strong > <?php e ( t ( 'SSO setup is incomplete' )); ?> </ strong >< br >
<?php if ( $ssoConfigErrorLabel !== '' ) : ?>
<?php e ( $ssoConfigErrorLabel ); ?>
<?php else : ?>
<?php e ( t ( 'Complete the required Microsoft configuration before enforcing Microsoft-only login.' )); ?>
<?php endif ; ?>
< / blockquote >
2026-03-12 10:01:24 +01:00
<?php endif ; ?>
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
2026-03-12 14:09:06 +01:00
< details class = "app-details-card" name = "tenant-sso-setup" <?php e ( $openSetupCard ? 'open' : '' ); ?> >
< summary >
< span class = "app-details-card-summary-title" > <?php e ( t ( 'Required Microsoft configuration' )); ?> </ span >
< span class = "app-details-card-summary-meta" >
< span class = "badge" data-variant = " <?php e ( $ssoSetupBadgeVariant ); ?> " > <?php e ( $ssoSetupBadgeLabel ); ?> </ span >
<?php if ( $ssoShowConfigBadge ) : ?>
< span class = "badge" data-variant = " <?php e ( $ssoConfigBadgeVariant ); ?> " > <?php e ( $ssoConfigBadgeLabel ); ?> </ span >
<?php endif ; ?>
< / span >
< / summary >
< div class = "app-details-card-container" >
< label class = "app-field" >
< input
type="checkbox"
role="switch"
name="microsoft_enabled"
value="1"
data-tenant-sso-enabled
<?php e ( $microsoftEnabled ? 'checked' : '' ); ?>
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Enable Microsoft login for this tenant' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'Required for Microsoft login.' )); ?> </ small >
< div data-tenant-sso-when-enabled >
< hr >
< label >
< span > <?php e ( t ( 'Entra tenant ID (tid)' )); ?> </ span >
< input
type="text"
name="entra_tenant_id"
value="<?php e ( $entraTenantId ); ?> "
placeholder="00000000-0000-0000-0000-000000000000"
<?php e ( $readonlyAttr ); ?> >
< / label >
< / div >
< / div >
< / details >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
2026-03-12 14:09:06 +01:00
< details class = "app-details-card" name = "tenant-sso-policy" data-tenant-sso-when-enabled <?php e ( $openPolicyCard ? 'open' : '' ); ?> >
< summary >
< span class = "app-details-card-summary-title" > <?php e ( t ( 'Login policy' )); ?> </ span >
< span class = "app-details-card-summary-meta" >
< span class = "badge" data-variant = " <?php e ( $ssoPolicyBadgeVariant ); ?> " > <?php e ( $ssoPasswordModeLabel ); ?> </ span >
< / span >
< / summary >
< div class = "app-details-card-container" >
< label class = "app-field" >
< input
type="checkbox"
role="switch"
name="enforce_microsoft_login"
value="1"
<?php e ( $microsoftEnforce ? 'checked' : '' ); ?>
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Allow Microsoft-only login (disable local password login)' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'Microsoft-only login requires a complete configuration' )); ?> </ small >
< hr >
< label class = "app-field" >
< span > <?php e ( t ( 'Remember login after Microsoft sign-in' )); ?> </ span >
< select name = "microsoft_auto_remember_mode" <?php e ( $readonlyAttr ); ?> >
< option value = "inherit" <?php if (( $form [ 'auto_remember_mode' ] ?? null ) === null ) echo 'selected' ; ?> > <?php e ( t ( 'Inherit global default' )); ?> </ option >
< option value = "1" <?php if (( $form [ 'auto_remember_mode' ] ?? null ) === 1 || ( $form [ 'auto_remember_mode' ] ?? null ) === '1' ) echo 'selected' ; ?> > <?php e ( t ( 'Force on' )); ?> </ option >
< option value = "0" <?php if (( $form [ 'auto_remember_mode' ] ?? null ) === 0 || ( $form [ 'auto_remember_mode' ] ?? null ) === '0' ) echo 'selected' ; ?> > <?php e ( t ( 'Force off' )); ?> </ option >
< / select >
< small class = "muted" > <?php e ( t ( 'Controls whether Microsoft logins automatically persist a remember-me token.' )); ?> </ small >
< / label >
< / div >
< / details >
2026-03-10 22:48:10 +01:00
2026-03-12 14:09:06 +01:00
< details class = "app-details-card" name = "tenant-sso-access-sync" data-tenant-sso-when-enabled <?php e ( $openAccessCard ? 'open' : '' ); ?> >
< summary >
< span class = "app-details-card-summary-title" > <?php e ( t ( 'Access and profile sync' )); ?> </ span >
< span class = "app-details-card-summary-meta" >
< span class = "badge" data-variant = " <?php e ( $ssoDomainsBadgeVariant ); ?> " > <?php e ( $ssoDomainsBadgeLabel ); ?> </ span >
< span class = "badge" data-variant = " <?php e ( $ssoSyncBadgeVariant ); ?> " >
<?php e ( t ( 'Profile sync' )); ?> : <?php e ( $ssoSyncBadgeLabel ); ?>
< / span >
<?php if ( $syncProfileOnLogin ) : ?>
< span class = "badge" data-variant = "info" >
<?php e ( t ( 'Sync fields' )); ?> : <?php e (( string ) $syncFieldCount ); ?>
< / span >
<?php endif ; ?>
< / span >
< / summary >
< div class = "app-details-card-container" >
< label >
< span > <?php e ( t ( 'Allowed email domains' )); ?> </ span >
< textarea name = "allowed_domains" rows = "3" placeholder = "example.com partner.com" <?php e ( $readonlyAttr ); ?> > <?php e ( $allowedDomains ); ?> </ textarea >
< small > <?php e ( t ( 'Optional allow-list, comma or newline separated (example.com).' )); ?> </ small >
< / label >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< hr >
2026-03-12 14:09:06 +01:00
< label class = "app-field" >
< input
type="checkbox"
role="switch"
name="sync_profile_on_login"
value="1"
data-tenant-sso-sync-toggle
<?php e ( $syncProfileOnLogin ? 'checked' : '' ); ?>
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Profile sync on Microsoft login' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'User profile fields are global and affect all tenants of this user' )); ?> </ small >
< div data-tenant-sso-sync-fields >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< hr >
2026-03-12 14:09:06 +01:00
< label >< span > <?php e ( t ( 'Sync fields' )); ?> </ span ></ label >
< div class = "grid grid-1-1" >
<?php foreach ( $syncFieldOptions as $syncFieldKey => $syncFieldLabel ) : ?>
< label class = "app-field" >
< input
type="checkbox"
name="sync_profile_fields[]"
value="<?php e ( $syncFieldKey ); ?> "
<?php e ( in_array ( $syncFieldKey , $syncProfileFields , true ) ? 'checked' : '' ); ?>
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( $syncFieldLabel )); ?> </ span >
< / label >
<?php endforeach ; ?>
< / div >
<?php if ( $ssoSyncNeedsGraph ) : ?>
< hr >
< small class = "muted" > <?php e ( t ( 'Phone/mobile/avatar sync requires Microsoft Graph (User.Read)' )); ?> </ small >
<?php endif ; ?>
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / div >
2026-03-12 14:09:06 +01:00
< / details >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
2026-03-12 14:09:06 +01:00
< details
class="app-details-card"
name="tenant-sso-advanced"
data-tenant-sso-override-details
data-tenant-sso-when-enabled
data-remember-state="admin-tenant-sso-override"
<?php e ( $openOverrideCard ? 'open' : '' ); ?> >
< summary >
< span class = "app-details-card-summary-title" > <?php e ( t ( 'App credentials (advanced)' )); ?> </ span >
< span class = "app-details-card-summary-meta" >
< span class = "badge" data-variant = " <?php e ( $ssoCredentialsBadgeVariant ); ?> " > <?php e ( $ssoCredentialsBadgeLabel ); ?> </ span >
< / span >
< / summary >
< div class = "app-details-card-container" >
< label class = "app-field" >
< input type = "hidden" name = "use_shared_app" value = "0" >
< input
type="checkbox"
role="switch"
name="use_shared_app"
value="1"
data-tenant-sso-shared-toggle
<?php e ( $useSharedApp ? 'checked' : '' ); ?>
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Use shared app credentials from settings' )); ?> </ span >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / label >
2026-03-12 14:09:06 +01:00
< small class = "muted" > <?php e ( t ( 'Override only if this tenant needs a separate Entra app.' )); ?> </ small >
< div class = "grid" data-tenant-sso-override-fields >
< label >
< span > <?php e ( t ( 'Client ID override' )); ?> </ span >
< input type = "text" name = "client_id_override" value = " <?php e ( $clientIdOverride ); ?> " <?php e ( $readonlyAttr ); ?> >
< / label >
< label >
< span > <?php e ( t ( 'Client secret override' )); ?> </ span >
< input type = "password" name = "client_secret_override" value = "" autocomplete = "new-password" <?php e ( $readonlyAttr ); ?> >
< small > <?php e ( t ( 'Write-only. Leave empty to keep current secret.' )); ?> </ small >
< / label >
< / div >
< label class = "app-field" data-tenant-sso-override-fields >
< input type = "checkbox" name = "clear_client_secret_override" value = "1" <?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Clear override secret' )); ?> </ span >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< / label >
< / div >
< / details >
2026-03-25 07:26:04 +01:00
<?php
// ── LDAP configuration section ────────────────────────────────
$ldapUiState = is_array($ldapUiState ?? null) ? $ldapUiState : [];
$ldapEnabled = !empty($values['enabled'] ?? false) ? false : (!empty($values['ldap_enabled'] ?? $ldapConfig['enabled'] ?? false));
if (isset($values['ldap_enabled'])) {
$ldapEnabled = in_array(strtolower(trim((string) $values['ldap_enabled'])), ['1', 'true', 'yes', 'on'], true);
} elseif (isset($ldapConfig['enabled'])) {
$ldapEnabled = !empty($ldapConfig['enabled']);
}
$ldapEnforce = !empty($values['enforce_ldap_login'] ?? $ldapConfig['enforce_ldap_login'] ?? false);
$ldapHost = trim((string) ($values['ldap_host'] ?? $values['host'] ?? $ldapConfig['host'] ?? ''));
$ldapPort = (int) ($values['ldap_port'] ?? $values['port'] ?? $ldapConfig['port'] ?? 389);
$ldapEncryptionMode = (string) ($values['ldap_encryption_mode'] ?? $values['encryption_mode'] ?? $ldapConfig['encryption_mode'] ?? 'starttls');
$ldapVerifyTls = !array_key_exists('verify_tls_certificate', ($ldapConfig ?? [])) || !empty($values['ldap_verify_tls_certificate'] ?? $ldapConfig['verify_tls_certificate'] ?? true);
$ldapBaseDn = trim((string) ($values['ldap_base_dn'] ?? $values['base_dn'] ?? $ldapConfig['base_dn'] ?? ''));
$ldapUserSearchFilter = trim((string) ($values['ldap_user_search_filter'] ?? $values['user_search_filter'] ?? $ldapConfig['user_search_filter'] ?? '(&(objectClass=user)(sAMAccountName=%s))'));
$ldapUserSearchScope = (string) ($values['ldap_user_search_scope'] ?? $values['user_search_scope'] ?? $ldapConfig['user_search_scope'] ?? 'sub');
$ldapBindMethod = (string) ($values['ldap_bind_method'] ?? $values['bind_method'] ?? $ldapConfig['bind_method'] ?? 'search_then_bind');
$ldapBindUsernameFormat = trim((string) ($values['ldap_bind_username_format'] ?? $values['bind_username_format'] ?? $ldapConfig['bind_username_format'] ?? ''));
$ldapUniqueIdAttribute = trim((string) ($values['ldap_unique_id_attribute'] ?? $values['unique_id_attribute'] ?? $ldapConfig['unique_id_attribute'] ?? 'objectGUID'));
$ldapAttributeMap = $ldapConfig['attribute_map'] ?? [];
if (is_string($ldapAttributeMap)) {
$ldapAttributeMap = json_decode($ldapAttributeMap, true) ?: [];
}
$ldapSyncProfileOnLogin = !empty($values['ldap_sync_profile_on_login'] ?? $ldapConfig['sync_profile_on_login'] ?? false);
$ldapSyncFieldOptions = $tenantSsoService->ldapProfileSyncFieldOptions();
$ldapSyncProfileFields = $tenantSsoService->normalizeLdapProfileSyncFields($values['ldap_sync_profile_fields'] ?? $ldapConfig['sync_profile_fields_list'] ?? []);
$ldapAllowedDomains = trim((string) ($values['ldap_allowed_domains'] ?? $ldapConfig['allowed_domains'] ?? ''));
$ldapNetworkTimeout = (int) ($values['ldap_network_timeout'] ?? $ldapConfig['network_timeout'] ?? 5);
$ldapConfigComplete = !empty($ldapUiState['config_complete']);
$ldapConfigErrorLabel = trim((string) ($ldapUiState['config_error_label'] ?? ''));
$openLdapSetupCard = $detailsOpenAll || !$ldapEnabled || ($ldapEnabled & & !$ldapConfigComplete);
?>
< hr >
< h4 > <?php e ( t ( 'LDAP Authentication' )); ?> </ h4 >
< div class = "app-form-info-tiles" >
<?php
$infoTileLabel = t('LDAP login');
$infoTileValue = $ldapEnabled ? t('Active') : t('Inactive');
$infoTileTone = $ldapEnabled ? 'success' : 'neutral';
require templatePath('partials/app-form-info-tile.phtml');
$infoTileLabel = t('Configuration complete');
$infoTileValue = $ldapConfigComplete ? t('Yes') : t('No');
$infoTileTone = $ldapConfigComplete ? 'success' : 'warning';
require templatePath('partials/app-form-info-tile.phtml');
?>
< / div >
<?php if ( $ldapEnabled && ! $ldapConfigComplete && $ldapConfigErrorLabel !== '' ) : ?>
< blockquote data-variant = "warning" >
< strong > <?php e ( t ( 'LDAP setup is incomplete' )); ?> </ strong >< br >
<?php e ( $ldapConfigErrorLabel ); ?>
< / blockquote >
<?php endif ; ?>
< details class = "app-details-card" name = "tenant-ldap-setup" <?php e ( $openLdapSetupCard ? 'open' : '' ); ?> >
< summary >
< span class = "app-details-card-summary-title" > <?php e ( t ( 'LDAP connection settings' )); ?> </ span >
< span class = "app-details-card-summary-meta" >
< span class = "badge" data-variant = " <?php e ( $ldapEnabled ? 'success' : 'neutral' ); ?> " >
<?php e ( t ( 'LDAP login' )); ?> : <?php e ( $ldapEnabled ? t ( 'Active' ) : t ( 'Inactive' )); ?>
< / span >
< / span >
< / summary >
< div class = "app-details-card-container" >
< label class = "app-field" >
< input type = "checkbox" role = "switch" name = "ldap_enabled" value = "1"
<?php e ( $ldapEnabled ? 'checked' : '' ); ?> <?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Enable LDAP login for this tenant' )); ?> </ span >
< / label >
< hr >
< div class = "grid grid-1-1" >
< label >
< span > <?php e ( t ( 'LDAP Host' )); ?> </ span >
< input type = "text" name = "ldap_host" value = " <?php e ( $ldapHost ); ?> "
placeholder="ldap.example.com" <?php e ( $readonlyAttr ); ?> >
< / label >
< label >
< span > <?php e ( t ( 'Port' )); ?> </ span >
< input type = "number" name = "ldap_port" value = " <?php e (( string ) $ldapPort ); ?> "
min="1" max="65535" <?php e ( $readonlyAttr ); ?> >
< / label >
< / div >
< div class = "grid grid-1-1" >
< label >
< span > <?php e ( t ( 'Encryption' )); ?> </ span >
< select name = "ldap_encryption_mode" <?php e ( $disabledAttr ); ?> >
< option value = "starttls" <?php e ( $ldapEncryptionMode === 'starttls' ? 'selected' : '' ); ?> > STARTTLS</ option >
< option value = "ldaps" <?php e ( $ldapEncryptionMode === 'ldaps' ? 'selected' : '' ); ?> > LDAPS (SSL)</ option >
< option value = "none" <?php e ( $ldapEncryptionMode === 'none' ? 'selected' : '' ); ?> > <?php e ( t ( 'None (not recommended)' )); ?> </ option >
< / select >
< / label >
< label class = "app-field" >
< input type = "checkbox" name = "ldap_verify_tls_certificate" value = "1"
<?php e ( $ldapVerifyTls ? 'checked' : '' ); ?> <?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Verify TLS certificate' )); ?> </ span >
< / label >
< / div >
< label >
< span > <?php e ( t ( 'Base DN' )); ?> </ span >
< input type = "text" name = "ldap_base_dn" value = " <?php e ( $ldapBaseDn ); ?> "
placeholder="DC=corp,DC=example,DC=com" <?php e ( $readonlyAttr ); ?> >
< / label >
< hr >
< label >
< span > <?php e ( t ( 'Bind DN (service account)' )); ?> </ span >
< input type = "text" name = "ldap_bind_dn" value = "" autocomplete = "off"
placeholder="CN=svc-ldap,OU=Service Accounts,DC=corp,DC=example,DC=com" <?php e ( $readonlyAttr ); ?> >
< small class = "muted" > <?php e ( t ( 'Write-only. Leave empty to keep current value.' )); ?> </ small >
< / label >
< label >
< span > <?php e ( t ( 'Bind password' )); ?> </ span >
< input type = "password" name = "ldap_bind_password" value = "" autocomplete = "new-password" <?php e ( $readonlyAttr ); ?> >
< small class = "muted" > <?php e ( t ( 'Write-only. Leave empty to keep current value.' )); ?> </ small >
< / label >
< label class = "app-field" >
< input type = "checkbox" name = "clear_ldap_bind_password" value = "1" <?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Clear bind password' )); ?> </ span >
< / label >
< hr >
< p >
< button type = "button" class = "secondary outline small" id = "ldap-test-connection-button"
data-ldap-test-url="<?php e ( lurl ( 'admin/tenants/ldap-test-connection' )); ?> "
data-tenant-id="<?php e (( string ) $tenantId ); ?> ">
< i class = "bi bi-plug" aria-hidden = "true" ></ i > <?php e ( t ( 'Test connection' )); ?>
< / button >
< span id = "ldap-test-connection-result" aria-live = "polite" > < / span >
< / p >
< / div >
< / details >
< details class = "app-details-card" name = "tenant-ldap-search" <?php e ( $detailsOpenAll ? 'open' : '' ); ?> >
< summary >
< span class = "app-details-card-summary-title" > <?php e ( t ( 'Search and bind settings' )); ?> </ span >
< / summary >
< div class = "app-details-card-container" >
< label >
< span > <?php e ( t ( 'Bind method' )); ?> </ span >
< select name = "ldap_bind_method" <?php e ( $disabledAttr ); ?> >
< option value = "search_then_bind" <?php e ( $ldapBindMethod === 'search_then_bind' ? 'selected' : '' ); ?> > <?php e ( t ( 'Search then bind (recommended)' )); ?> </ option >
< option value = "direct_bind" <?php e ( $ldapBindMethod === 'direct_bind' ? 'selected' : '' ); ?> > <?php e ( t ( 'Direct bind' )); ?> </ option >
< / select >
< small class = "muted" > <?php e ( t ( 'Search-then-bind uses a service account to find the user DN first, then binds with the user credentials.' )); ?> </ small >
< / label >
< label >
< span > <?php e ( t ( 'User search filter' )); ?> </ span >
< input type = "text" name = "ldap_user_search_filter" value = " <?php e ( $ldapUserSearchFilter ); ?> "
placeholder="(& (objectClass=user)(sAMAccountName=%s))" <?php e ( $readonlyAttr ); ?> >
< small class = "muted" > <?php e ( t ( 'Use %s as placeholder for the username. Examples: (&(objectClass=user)(sAMAccountName=%s)) for AD, (&(objectClass=inetOrgPerson)(uid=%s)) for OpenLDAP' )); ?> </ small >
< / label >
< div class = "grid grid-1-1" >
< label >
< span > <?php e ( t ( 'Search scope' )); ?> </ span >
< select name = "ldap_user_search_scope" <?php e ( $disabledAttr ); ?> >
< option value = "sub" <?php e ( $ldapUserSearchScope === 'sub' ? 'selected' : '' ); ?> > <?php e ( t ( 'Subtree (recursive)' )); ?> </ option >
< option value = "one" <?php e ( $ldapUserSearchScope === 'one' ? 'selected' : '' ); ?> > <?php e ( t ( 'One level' )); ?> </ option >
< / select >
< / label >
< label >
< span > <?php e ( t ( 'Network timeout (seconds)' )); ?> </ span >
< input type = "number" name = "ldap_network_timeout" value = " <?php e (( string ) $ldapNetworkTimeout ); ?> "
min="1" max="30" <?php e ( $readonlyAttr ); ?> >
< / label >
< / div >
< label >
< span > <?php e ( t ( 'Direct bind username format' )); ?> </ span >
< input type = "text" name = "ldap_bind_username_format" value = " <?php e ( $ldapBindUsernameFormat ); ?> "
placeholder="%s@corp.example.com" <?php e ( $readonlyAttr ); ?> >
< small class = "muted" > <?php e ( t ( 'Only for direct bind. Use %s as placeholder. Example: %s@corp.example.com or uid=%s,ou=People,dc=example,dc=com' )); ?> </ small >
< / label >
< label >
< span > <?php e ( t ( 'Unique ID attribute' )); ?> </ span >
< input type = "text" name = "ldap_unique_id_attribute" value = " <?php e ( $ldapUniqueIdAttribute ); ?> "
placeholder="objectGUID" <?php e ( $readonlyAttr ); ?> >
< small class = "muted" > <?php e ( t ( 'AD: objectGUID, OpenLDAP: entryUUID, FreeIPA: ipaUniqueID' )); ?> </ small >
< / label >
< / div >
< / details >
< details class = "app-details-card" name = "tenant-ldap-mapping" <?php e ( $detailsOpenAll ? 'open' : '' ); ?> >
< summary >
< span class = "app-details-card-summary-title" > <?php e ( t ( 'Attribute mapping and sync' )); ?> </ span >
< / summary >
< div class = "app-details-card-container" >
< small class = "muted" > <?php e ( t ( 'Map application fields to LDAP directory attributes.' )); ?> </ small >
< div class = "grid grid-1-1" >
<?php
$defaultMap = [
'first_name' => 'givenName',
'last_name' => 'sn',
'email' => 'mail',
'phone' => 'telephoneNumber',
'mobile' => 'mobile',
];
foreach ($defaultMap as $appField => $defaultLdapAttr):
$currentLdapAttr = $ldapAttributeMap[$appField] ?? $defaultLdapAttr;
?>
< label >
< span > <?php e ( t ( ucfirst ( str_replace ( '_' , ' ' , $appField )))); ?> → </ span >
< input type = "text" name = "ldap_attribute_map[ <?php e ( $appField ); ?> ]"
value="<?php e ( $currentLdapAttr ); ?> " <?php e ( $readonlyAttr ); ?> >
< / label >
<?php endforeach ; ?>
< / div >
< hr >
< label class = "app-field" >
< input type = "checkbox" role = "switch" name = "ldap_sync_profile_on_login" value = "1"
<?php e ( $ldapSyncProfileOnLogin ? 'checked' : '' ); ?> <?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Profile sync on LDAP login' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'User profile fields are global and affect all tenants of this user' )); ?> </ small >
< div class = "grid grid-1-1" >
<?php foreach ( $ldapSyncFieldOptions as $syncFieldKey => $syncFieldLabel ) : ?>
< label class = "app-field" >
< input type = "checkbox" name = "ldap_sync_profile_fields[]"
value="<?php e ( $syncFieldKey ); ?> "
<?php e ( in_array ( $syncFieldKey , $ldapSyncProfileFields , true ) ? 'checked' : '' ); ?>
<?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( $syncFieldLabel )); ?> </ span >
< / label >
<?php endforeach ; ?>
< / div >
< hr >
< label class = "app-field" >
< input type = "checkbox" role = "switch" name = "enforce_ldap_login" value = "1"
<?php e ( $ldapEnforce ? 'checked' : '' ); ?> <?php e ( $disabledAttr ); ?> >
< span > <?php e ( t ( 'Allow LDAP-only login (disable local password login)' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'LDAP-only login requires a complete configuration' )); ?> </ small >
< hr >
< label >
< span > <?php e ( t ( 'Allowed email domains' )); ?> </ span >
< textarea name = "ldap_allowed_domains" rows = "2" placeholder = "example.com" <?php e ( $readonlyAttr ); ?> > <?php e ( $ldapAllowedDomains ); ?> </ textarea >
< small > <?php e ( t ( 'Optional allow-list, comma or newline separated.' )); ?> </ small >
< / label >
< / div >
< / details >
2026-02-04 23:31:53 +01:00
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php endif ; ?>
2026-02-04 23:31:53 +01:00
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< div data-tab-panel = "visibility" >
2026-02-04 23:31:53 +01:00
< blockquote data-variant = "info" >
<?php e ( t ( 'Using the default color applies the global system appearance.' )); ?>
< / blockquote >
< div class = "grid" >
< fieldset >
< legend >< small > <?php e ( t ( 'Primary color' )); ?> </ small ></ legend >
< label for = "primary_color" >
< input type = "color" name = "primary_color" id = "primary_color"
value="<?php e ( $primaryColor !== '' ? $primaryColor : $defaultPrimaryColor ); ?> "
<?php e ( $readonlyAttr ); ?> />
2026-03-12 12:11:31 +01:00
< div >
<?php e ( t ( 'Choose color' )); ?>
< / div >
2026-02-04 23:31:53 +01:00
< / label >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< small class = "muted" > <?php e ( t ( 'The primary color affects buttons and accent elements across the app.' )); ?> </ small >
2026-02-04 23:31:53 +01:00
< / fieldset >
< fieldset >
< legend >
< small >
<?php e ( t ( 'Use default color' )); ?>
< / small >
< / legend >
< label class = "app-field" >
< input type = "hidden" name = "primary_color_use_default" value = "0" >
2026-03-12 09:41:18 +01:00
< input type = "checkbox" role = "switch" name = "primary_color_use_default" value = "1"
2026-02-04 23:31:53 +01:00
data-color-default-toggle
data-color-target="#primary_color"
data-color-default="<?php e ( $defaultPrimaryColor ); ?> "
<?php e ( $useDefaultPrimaryColor ? 'checked' : '' ); ?>
<?php e ( $readonlyAttr ); ?> >
< span > <?php e ( t ( 'Use default color' )); ?> </ span >
< / label >
< small class = "muted" > <?php e ( t ( 'Be careful - this resets the tenants color' )); ?> </ small >
< / fieldset >
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
< div class = "grid" >
< fieldset >
< legend >< small > <?php e ( t ( 'Default theme' )); ?> </ small ></ legend >
< select name = "default_theme" <?php e ( $disabledAttr ); ?> >
< option value = "" > <?php e ( t ( 'Inherit global setting' )); ?> </ option >
<?php foreach ( $themes as $themeKey => $themeLabel ) : ?>
< option value = " <?php e ( $themeKey ); ?> " <?php e ( $tenantDefaultTheme === $themeKey ? 'selected' : '' ); ?> >
<?php e ( t ( $themeLabel )); ?>
< / option >
<?php endforeach ; ?>
< / select >
< small class = "muted" > <?php e ( t ( 'If set, this tenant uses its own default theme instead of the global setting.' )); ?> </ small >
< / fieldset >
< fieldset >
< legend >< small > <?php e ( t ( 'User theme policy' )); ?> </ small ></ legend >
< select name = "allow_user_theme_mode" <?php e ( $disabledAttr ); ?> >
< option value = "" <?php e ( $tenantAllowUserThemeMode === '' ? 'selected' : '' ); ?> >
<?php e ( t ( 'Inherit global setting' )); ?>
< / option >
< option value = "1" <?php e ( $tenantAllowUserThemeMode === '1' ? 'selected' : '' ); ?> >
<?php e ( t ( 'Force allow user theme' )); ?>
< / option >
< option value = "0" <?php e ( $tenantAllowUserThemeMode === '0' ? 'selected' : '' ); ?> >
<?php e ( t ( 'Force disallow user theme' )); ?>
< / option >
< / select >
< small class = "muted" > <?php e ( t ( 'Controls whether users in this tenant can choose their own theme.' )); ?> </ small >
< / fieldset >
< / div >
<?php
$statusFieldName = 'status';
$statusFieldId = 'tenant-status';
$statusFieldValue = (string) ($values['status'] ?? 'active');
$statusFieldOptions = [
'active' => t('Active'),
'inactive' => t('Inactive'),
];
$statusFieldHint = t('Inactive tenants are excluded from user access and tenant-scoped data.');
require templatePath('partials/app-visibility-status-field.phtml');
?>
2026-02-04 23:31:53 +01:00
< / div >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php if ( $showDangerZone && $dangerZoneDeleteFormId !== '' ) : ?>
< div data-tab-panel = "danger" >
<?php
$dangerFormId = $dangerZoneDeleteFormId;
$dangerLegend = t('Delete');
$dangerWarning = $dangerZoneWarning;
$dangerButtonLabel = $dangerZoneActionLabel;
require templatePath('partials/app-danger-zone-delete-field.phtml');
?>
< / div >
<?php endif ; ?>
2026-02-04 23:31:53 +01:00
< / div >
<?php Session :: getCsrfInput (); ?>
< / form >
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php if ( $showCustomFieldsTab && $customFieldTenantUuid !== '' ) : ?>
< form id = "tenant-custom-field-create-form" method = "post"
action="admin/tenants/custom-field-create/<?php e ( $customFieldTenantUuid ); ?> " hidden>
<?php Session :: getCsrfInput (); ?>
< / form >
<?php foreach ( $customFieldDefinitions as $definition ) : ?>
<?php
$definitionId = (int) ($definition['id'] ?? 0);
$definitionUuid = (string) ($definition['uuid'] ?? '');
if ($definitionId < = 0 || $definitionUuid === '') {
continue;
}
$updateFormId = 'tenant-custom-field-update-form-' . $definitionId;
$deleteFormId = 'tenant-custom-field-delete-form-' . $definitionId;
?>
< form id = " <?php e ( $updateFormId ); ?> " method = "post"
action="admin/tenants/custom-field-update/<?php e ( $definitionUuid ); ?> " hidden>
<?php Session :: getCsrfInput (); ?>
< / form >
< form id = " <?php e ( $deleteFormId ); ?> " method = "post"
action="admin/tenants/custom-field-delete/<?php e ( $definitionUuid ); ?> " hidden
2026-03-04 15:56:58 +01:00
data-detail-confirm-message="<?php e ( t ( 'Delete custom field' )); ?> ?"
data-detail-action-kind="delete">
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
<?php Session :: getCsrfInput (); ?>
< / form >
<?php endforeach ; ?>
2026-03-12 14:09:06 +01:00
<?php endif ; ?>