Beide Module wurden aus einem anderen Kundenprojekt kopiert und bereinigt: Knowledgecenter: - Bild-Cache (1.985 Dateien) geleert, Ordnerstruktur mit .gitkeep behalten - Files-Gallery-Lizenzschlüssel entfernt (config.php) - Kundenspezifische Kategorien gelöscht: Teil I/II/III QM-Struktur, nummerierte QM-Kapitel, Gremien (Präsidium/Finanzausschuss/Landesausschuss), Sitzungsservice-Serie, Protokoll-Abteilungen, Testeinträge - 56 generische Kategorien bleiben (Downloads, Onboarding, Personalthemen …) Tasks: - Alle Betriebsdaten gelöscht (114 Tasks, 914 Submissions, 579 Assignments) - Task-Definitionen und Verlinkungen geleert - Kategoriestruktur bleibt (Allgemein, IT, Personal, Buchhaltung …) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
734 lines
48 KiB
PHP
734 lines
48 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
if (!isset($assignment) && !isset($task) && !isset($error)) {
|
|
require_once dirname(dirname(__DIR__)) . '/actions/user/task_detail_action.php';
|
|
$viewData = task_cert_user_task_detail_action();
|
|
if (is_array($viewData)) {
|
|
extract($viewData, EXTR_SKIP);
|
|
}
|
|
}
|
|
|
|
$error = $error ?? null;
|
|
$assignment = $assignment ?? null;
|
|
$task = $task ?? null;
|
|
$methods = $methods ?? [];
|
|
$submissions = $submissions ?? [];
|
|
$taskCertificatesHistory = is_array($task_certificates_history ?? null) ? $task_certificates_history : [];
|
|
$taskResponsibles = is_array($responsibles ?? null) ? $responsibles : [];
|
|
$contactData = is_array($contact_data ?? null) ? $contact_data : [];
|
|
$csrfToken = $csrf_token ?? '';
|
|
$previewMode = $previewMode ?? false;
|
|
$inlineError = trim(TaskCertRequest::queryString('task_cert_error', ''));
|
|
$inlineWarning = trim(TaskCertRequest::queryString('task_cert_warning', ''));
|
|
$inlineNotice = trim(TaskCertRequest::queryString('task_cert_notice', ''));
|
|
$statusLabels = TaskCertConstants::assignmentStatusLabels();
|
|
$methodLabels = TaskCertConstants::methodLabels();
|
|
$submissionStatusLabels = TaskCertConstants::submissionStatusLabels();
|
|
$certificateStatusLabels = TaskCertConstants::certificateStatusLabels();
|
|
|
|
if (!function_exists('task_cert_render_styles')) {
|
|
require_once dirname(dirname(__DIR__)) . '/bootstrap.php';
|
|
}
|
|
task_cert_render_styles(['user']);
|
|
$taskCertBaseUrl = task_cert_base_url();
|
|
$myTasksUrl = task_cert_view_url('user.my_tasks');
|
|
$taskDetailViewUrl = $assignment
|
|
? task_cert_view_url('user.task_detail', ['assignment_id' => (int) $assignment['id']])
|
|
: task_cert_view_url('user.task_detail');
|
|
$wikiBasePath = '/' . trim(task_cert_site_code(), '/') . '/' . trim(task_cert_language_code(), '/') . '/wiki/';
|
|
$buildWikiPostUrl = static function (int $postId) use ($wikiBasePath): string {
|
|
if ($postId <= 0) {
|
|
return '';
|
|
}
|
|
|
|
return $wikiBasePath . '?action=Post&id=' . $postId . '&embedded=1';
|
|
};
|
|
|
|
$formatDateTime = static function ($value): string {
|
|
$value = trim((string) $value);
|
|
if ($value === '') {
|
|
return '-';
|
|
}
|
|
|
|
try {
|
|
return (new DateTimeImmutable($value))->format('d.m.Y H:i');
|
|
} catch (Throwable $throwable) {
|
|
return $value;
|
|
}
|
|
};
|
|
|
|
$formatDueMessage = static function ($dueRaw): string {
|
|
$dueRaw = trim((string) $dueRaw);
|
|
if ($dueRaw === '') {
|
|
return 'Kein Fälligkeitsdatum hinterlegt.';
|
|
}
|
|
|
|
try {
|
|
$dueAt = new DateTimeImmutable($dueRaw);
|
|
$now = new DateTimeImmutable();
|
|
$days = (int) floor(($dueAt->getTimestamp() - $now->getTimestamp()) / 86400);
|
|
|
|
if ($days === 0) {
|
|
return 'Heute fällig (' . $dueAt->format('d.m.Y H:i') . ')';
|
|
}
|
|
|
|
if ($days > 0) {
|
|
return 'Fällig in ' . $days . ' Tag' . ($days === 1 ? '' : 'en') . ' (' . $dueAt->format('d.m.Y H:i') . ')';
|
|
}
|
|
|
|
$overdueDays = abs($days);
|
|
return 'Seit ' . $overdueDays . ' Tag' . ($overdueDays === 1 ? '' : 'en') . ' überfällig (' . $dueAt->format('d.m.Y H:i') . ')';
|
|
} catch (Throwable $throwable) {
|
|
return $dueRaw;
|
|
}
|
|
};
|
|
|
|
$formFieldLabelsByMethod = [];
|
|
foreach ($methods as $m) {
|
|
$mId = (int) ($m['id'] ?? 0);
|
|
if ((string) ($m['method_type'] ?? '') !== 'form_submit' || $mId <= 0) {
|
|
continue;
|
|
}
|
|
$map = [];
|
|
foreach ((array) ($m['form_fields'] ?? []) as $ff) {
|
|
$fk = trim((string) ($ff['field_key'] ?? ''));
|
|
if ($fk !== '') {
|
|
$map[$fk] = trim((string) ($ff['label'] ?? $fk));
|
|
}
|
|
}
|
|
$formFieldLabelsByMethod[$mId] = $map;
|
|
}
|
|
|
|
$latestSubmissionByMethod = [];
|
|
$allSubmissionsByMethod = [];
|
|
foreach ($submissions as $submission) {
|
|
$methodId = (int) ($submission['method_id'] ?? 0);
|
|
if ($methodId <= 0) {
|
|
continue;
|
|
}
|
|
|
|
if (!isset($latestSubmissionByMethod[$methodId])) {
|
|
$latestSubmissionByMethod[$methodId] = $submission;
|
|
}
|
|
|
|
$allSubmissionsByMethod[$methodId][] = $submission;
|
|
}
|
|
|
|
$resolveMethodProgress = static function (int $methodId) use ($latestSubmissionByMethod, $submissionStatusLabels): array {
|
|
$latest = $latestSubmissionByMethod[$methodId] ?? null;
|
|
if (!is_array($latest)) {
|
|
return [
|
|
'state' => 'open',
|
|
'label' => 'Noch offen',
|
|
'note' => 'Bitte diesen Schritt ausführen.',
|
|
];
|
|
}
|
|
|
|
$status = (string) ($latest['status'] ?? '');
|
|
if (in_array($status, ['auto_passed', 'approved'], true)) {
|
|
return [
|
|
'state' => 'done',
|
|
'label' => 'Erledigt',
|
|
'note' => 'Zuletzt am ' . ($latest['submitted_at'] ?? '-') . ' eingereicht.',
|
|
];
|
|
}
|
|
|
|
if ($status === 'awaiting_approval') {
|
|
return [
|
|
'state' => 'pending',
|
|
'label' => 'Warten auf Freigabe',
|
|
'note' => 'Ihre Einreichung wird geprüft.',
|
|
];
|
|
}
|
|
|
|
if ($status === 'rejected') {
|
|
return [
|
|
'state' => 'retry',
|
|
'label' => 'Erneut erforderlich',
|
|
'note' => 'Bitte Schritt erneut bearbeiten und abschicken.',
|
|
];
|
|
}
|
|
|
|
return [
|
|
'state' => 'open',
|
|
'label' => $submissionStatusLabels[$status] ?? 'Offen',
|
|
'note' => 'Bitte diesen Schritt abschließen.',
|
|
];
|
|
};
|
|
|
|
$isMethodCompleted = static function (int $methodId) use ($latestSubmissionByMethod): bool {
|
|
$latest = $latestSubmissionByMethod[$methodId] ?? null;
|
|
if (!is_array($latest)) {
|
|
return false;
|
|
}
|
|
|
|
$status = (string) ($latest['status'] ?? '');
|
|
return in_array($status, ['auto_passed', 'approved'], true);
|
|
};
|
|
|
|
$totalRequiredSteps = 0;
|
|
$completedRequiredSteps = 0;
|
|
foreach ($methods as $method) {
|
|
if ((int) ($method['is_required'] ?? 1) !== 1) {
|
|
continue;
|
|
}
|
|
|
|
$totalRequiredSteps++;
|
|
if ($isMethodCompleted((int) ($method['id'] ?? 0))) {
|
|
$completedRequiredSteps++;
|
|
}
|
|
}
|
|
$remainingRequiredSteps = max(0, $totalRequiredSteps - $completedRequiredSteps);
|
|
$methodCount = count($methods);
|
|
?>
|
|
<div class="task-cert task-cert-user-detail">
|
|
<?php if ($error): ?>
|
|
<div class="task-cert-error"><?php echo TaskCertView::e($error); ?></div>
|
|
<?php elseif ($assignment && $task): ?>
|
|
<?php if ($inlineError !== ''): ?>
|
|
<div class="task-cert-error"><?php echo TaskCertView::e($inlineError); ?></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($inlineWarning !== ''): ?>
|
|
<div class="task-cert-warning"><?php echo TaskCertView::e($inlineWarning); ?></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($inlineNotice !== ''): ?>
|
|
<div class="task-cert-success"><?php echo TaskCertView::e($inlineNotice); ?></div>
|
|
<?php endif; ?>
|
|
|
|
<?php $assignmentStatus = (string) ($assignment['status'] ?? ''); ?>
|
|
<?php $isAssignmentLocked = in_array($assignmentStatus, ['completed', 'expired'], true); ?>
|
|
|
|
<?php if ($previewMode): ?>
|
|
<div class="task-cert-warning task-cert-preview-banner">
|
|
Vorschau – so sehen Mitarbeiter diese Aufgabe. Eingaben sind hier deaktiviert.
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="task-cert-user-detail-header">
|
|
<div class="task-cert-user-title-row">
|
|
<a href="<?php echo TaskCertView::e($myTasksUrl); ?>" class="task-cert-back-link" aria-label="Zurück zu meinen Aufgaben" title="Zurück zu meinen Aufgaben">←</a>
|
|
<h2><?php echo TaskCertView::e((string) ($task['title'] ?? 'Aufgabe')); ?></h2>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="task-cert-user-layout">
|
|
<main class="task-cert-user-main">
|
|
<?php if (trim((string) ($task['description'] ?? '')) !== ''): ?>
|
|
<div class="task-cert-block">
|
|
<h3>Das müssen Sie tun</h3>
|
|
<div class="task-cert-description-content"><?php
|
|
$descContent = (string) ($task['description'] ?? '');
|
|
if (TaskCertHtml::isHtml($descContent)) {
|
|
// HTML content from Quill — already sanitized on save
|
|
echo TaskCertHtml::sanitize($descContent);
|
|
} else {
|
|
// Legacy plain text
|
|
echo nl2br(TaskCertView::e($descContent));
|
|
}
|
|
?></div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="task-cert-block">
|
|
<h3>So erledigen Sie die Aufgabe</h3>
|
|
|
|
<?php foreach ($methods as $index => $method): ?>
|
|
<?php
|
|
$methodId = (int) ($method['id'] ?? 0);
|
|
$methodType = (string) ($method['method_type'] ?? '');
|
|
$methodTitle = $methodLabels[$methodType] ?? $methodType;
|
|
$progress = $resolveMethodProgress($methodId);
|
|
$stepTitle = $methodCount > 1
|
|
? ('Schritt ' . ((int) $index + 1) . ': ' . $methodTitle)
|
|
: $methodTitle;
|
|
?>
|
|
<section class="task-cert-user-step">
|
|
<header class="task-cert-user-step-head">
|
|
<div>
|
|
<strong><?php echo TaskCertView::e($stepTitle); ?></strong>
|
|
<div class="task-cert-hint"><?php echo TaskCertView::e((string) ($progress['note'] ?? '')); ?></div>
|
|
</div>
|
|
<span class="task-cert-user-method-state state-<?php echo TaskCertView::e((string) ($progress['state'] ?? 'open')); ?>">
|
|
<?php echo TaskCertView::e((string) ($progress['label'] ?? 'Offen')); ?>
|
|
</span>
|
|
</header>
|
|
|
|
<?php if ($methodType === 'knowledge_read'): ?>
|
|
<form action="<?php echo TaskCertView::e($taskCertBaseUrl . '/endpoints/user/submit_read.php'); ?>" method="post">
|
|
<input type="hidden" name="csrf_token" value="<?php echo TaskCertView::e($csrfToken); ?>">
|
|
<input type="hidden" name="assignment_id" value="<?php echo (int) $assignment['id']; ?>">
|
|
<input type="hidden" name="method_id" value="<?php echo (int) $method['id']; ?>">
|
|
<input type="hidden" name="redirect_to" value="<?php echo TaskCertView::e($taskDetailViewUrl); ?>">
|
|
|
|
<?php
|
|
$readLatest = $latestSubmissionByMethod[$methodId] ?? null;
|
|
$readPostIds = [];
|
|
if (is_array($readLatest)) {
|
|
$readPayload = is_array($readLatest['payload'] ?? null) ? $readLatest['payload'] : [];
|
|
foreach ((array) ($readPayload['post_ids'] ?? []) as $rpId) {
|
|
$readPostIds[(int) $rpId] = true;
|
|
}
|
|
}
|
|
?>
|
|
<div class="task-cert-user-read-posts">
|
|
<?php foreach ((array) ($method['knowledge_posts'] ?? []) as $post): ?>
|
|
<?php
|
|
$postId = (int) ($post['knowledge_post_id'] ?? 0);
|
|
$postUrl = $buildWikiPostUrl($postId);
|
|
$selectId = 'post_read_state_' . (int) ($method['id'] ?? 0) . '_' . $postId;
|
|
$alreadyRead = isset($readPostIds[$postId]);
|
|
?>
|
|
<section class="task-cert-user-read-post">
|
|
<div class="task-cert-user-read-post-head">
|
|
<div class="task-cert-user-read-post-title">
|
|
<strong><?php echo TaskCertView::e((string) ($post['title'] ?? ('Beitrag #' . $postId))); ?></strong>
|
|
<?php if ($alreadyRead): ?>
|
|
<span class="task-cert-user-read-badge">Gelesen</span>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php if ($postUrl !== ''): ?>
|
|
<a href="<?php echo TaskCertView::e($postUrl); ?>" target="_blank" rel="noopener noreferrer">Beitrag im neuen Tab öffnen</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php if ($postUrl !== ''): ?>
|
|
<details class="task-cert-user-read-post-details">
|
|
<summary>Beitrag öffnen</summary>
|
|
<div class="task-cert-user-read-post-content">
|
|
<div class="task-cert-user-read-post-iframe-wrap">
|
|
<iframe
|
|
src="<?php echo TaskCertView::e($postUrl); ?>"
|
|
title="<?php echo TaskCertView::e((string) ($post['title'] ?? ('Beitrag #' . $postId))); ?>"
|
|
loading="lazy"
|
|
referrerpolicy="strict-origin-when-cross-origin"
|
|
></iframe>
|
|
</div>
|
|
|
|
<label for="<?php echo TaskCertView::e($selectId); ?>">Lesestatus</label>
|
|
<select id="<?php echo TaskCertView::e($selectId); ?>" name="post_read_state[<?php echo $postId; ?>]">
|
|
<option value="unread" <?php echo !$alreadyRead ? 'selected' : ''; ?>>Ich habe den Beitrag nicht gelesen</option>
|
|
<option value="read" <?php echo $alreadyRead ? 'selected' : ''; ?>>Ich habe den Beitrag gelesen und verstanden</option>
|
|
</select>
|
|
</div>
|
|
</details>
|
|
<?php else: ?>
|
|
<div class="task-cert-hint">Beitragslink konnte nicht erzeugt werden.</div>
|
|
<?php endif; ?>
|
|
</section>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<button type="submit" class="btn task-cert-btn-success" <?php echo ($isAssignmentLocked || $previewMode) ? 'disabled' : ''; ?>>Lesestatus senden</button>
|
|
</form>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($methodType === 'form_submit'): ?>
|
|
<form action="<?php echo TaskCertView::e($taskCertBaseUrl . '/endpoints/user/submit_form.php'); ?>" method="post" enctype="multipart/form-data">
|
|
<input type="hidden" name="csrf_token" value="<?php echo TaskCertView::e($csrfToken); ?>">
|
|
<input type="hidden" name="assignment_id" value="<?php echo (int) $assignment['id']; ?>">
|
|
<input type="hidden" name="method_id" value="<?php echo (int) $method['id']; ?>">
|
|
<input type="hidden" name="redirect_to" value="<?php echo TaskCertView::e($taskDetailViewUrl); ?>">
|
|
|
|
<?php
|
|
// Datei-Werte der letzten Einreichung — werden am Upload-Feld
|
|
// als "aktuell hochgeladen" gezeigt; ohne Neu-Upload bleiben sie
|
|
// beim erneuten Absenden erhalten (Carry-Forward im Service).
|
|
$formLatest = $latestSubmissionByMethod[$methodId] ?? null;
|
|
$formLatestId = is_array($formLatest) ? (int) ($formLatest['id'] ?? 0) : 0;
|
|
$formLatestValues = [];
|
|
if (is_array($formLatest)) {
|
|
$formLatestPayload = is_array($formLatest['payload'] ?? null) ? $formLatest['payload'] : [];
|
|
$formLatestValues = is_array($formLatestPayload['form_values'] ?? null) ? $formLatestPayload['form_values'] : [];
|
|
}
|
|
?>
|
|
<div class="task-cert-user-stack">
|
|
<?php foreach ((array) ($method['form_fields'] ?? []) as $field): ?>
|
|
<?php
|
|
$fieldKey = (string) ($field['field_key'] ?? '');
|
|
$fieldType = (string) ($field['field_type'] ?? 'text');
|
|
$fieldLabel = (string) ($field['label'] ?? $fieldKey);
|
|
$required = (int) ($field['is_required'] ?? 0) === 1;
|
|
$options = is_array($field['options'] ?? null) ? $field['options'] : [];
|
|
?>
|
|
<div>
|
|
<label><?php echo TaskCertView::e($fieldLabel); ?><?php echo $required ? ' *' : ''; ?></label>
|
|
<?php if ($fieldType === 'textarea'): ?>
|
|
<textarea name="values[<?php echo TaskCertView::e($fieldKey); ?>]" <?php echo $required ? 'required' : ''; ?>></textarea>
|
|
<?php elseif ($fieldType === 'select'): ?>
|
|
<select name="values[<?php echo TaskCertView::e($fieldKey); ?>]" <?php echo $required ? 'required' : ''; ?>>
|
|
<option value="">Bitte wählen</option>
|
|
<?php foreach ($options as $option): ?>
|
|
<?php
|
|
$optionValue = is_array($option) ? (string) ($option['value'] ?? '') : (string) $option;
|
|
$optionLabel = is_array($option) ? (string) ($option['label'] ?? $optionValue) : (string) $option;
|
|
?>
|
|
<option value="<?php echo TaskCertView::e($optionValue); ?>"><?php echo TaskCertView::e($optionLabel); ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php elseif ($fieldType === 'radio'): ?>
|
|
<div class="task-cert-user-stack">
|
|
<?php foreach ($options as $option): ?>
|
|
<?php
|
|
$optionValue = is_array($option) ? (string) ($option['value'] ?? '') : (string) $option;
|
|
$optionLabel = is_array($option) ? (string) ($option['label'] ?? $optionValue) : (string) $option;
|
|
?>
|
|
<label class="task-cert-user-checkbox-row">
|
|
<input type="radio" name="values[<?php echo TaskCertView::e($fieldKey); ?>]" value="<?php echo TaskCertView::e($optionValue); ?>" <?php echo $required ? 'required' : ''; ?>>
|
|
<span><?php echo TaskCertView::e($optionLabel); ?></span>
|
|
</label>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php elseif ($fieldType === 'checkbox'): ?>
|
|
<div class="task-cert-user-stack">
|
|
<?php foreach ($options as $option): ?>
|
|
<?php
|
|
$optionValue = is_array($option) ? (string) ($option['value'] ?? '') : (string) $option;
|
|
$optionLabel = is_array($option) ? (string) ($option['label'] ?? $optionValue) : (string) $option;
|
|
?>
|
|
<label class="task-cert-user-checkbox-row">
|
|
<input type="checkbox" name="values[<?php echo TaskCertView::e($fieldKey); ?>][]" value="<?php echo TaskCertView::e($optionValue); ?>">
|
|
<span><?php echo TaskCertView::e($optionLabel); ?></span>
|
|
</label>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php elseif ($fieldType === 'file'): ?>
|
|
<?php
|
|
$existingFiles = is_array($formLatestValues[$fieldKey] ?? null) ? $formLatestValues[$fieldKey] : [];
|
|
$hasExistingFiles = $existingFiles !== [] && is_array(reset($existingFiles))
|
|
&& array_key_exists('file_path', reset($existingFiles));
|
|
?>
|
|
<?php if ($hasExistingFiles): ?>
|
|
<div class="task-cert-hint task-cert-user-existing-files">
|
|
Aktuell hochgeladen:
|
|
<?php
|
|
$existingLinks = [];
|
|
foreach (array_values($existingFiles) as $i => $fileMeta) {
|
|
if (!is_array($fileMeta)) {
|
|
continue;
|
|
}
|
|
$fileName = trim((string) ($fileMeta['file_name'] ?? ''));
|
|
$url = $taskCertBaseUrl . '/endpoints/admin/download_submission_file.php?' . http_build_query([
|
|
'submission_id' => $formLatestId,
|
|
'field_key' => $fieldKey,
|
|
'index' => (int) $i,
|
|
'csrf_token' => $csrfToken,
|
|
]);
|
|
$existingLinks[] = '<a href="' . TaskCertView::e($url) . '" target="_blank" rel="noopener">'
|
|
. TaskCertView::e($fileName !== '' ? $fileName : 'Datei') . '</a>';
|
|
}
|
|
echo implode(', ', $existingLinks);
|
|
?>
|
|
</div>
|
|
<?php endif; ?>
|
|
<input
|
|
type="file"
|
|
name="files[<?php echo TaskCertView::e($fieldKey); ?>][]"
|
|
multiple
|
|
accept="<?php echo TaskCertView::e(TaskUploadPolicy::acceptAttribute()); ?>"
|
|
<?php echo ($required && !$hasExistingFiles) ? 'required' : ''; ?>
|
|
>
|
|
<small class="task-cert-hint">
|
|
Erlaubt: <?php echo TaskCertView::e(TaskUploadPolicy::HUMAN_READABLE_FORMATS_SHORT); ?>
|
|
· max. <?php echo (int) TaskUploadPolicy::MAX_FILE_COUNT; ?> Dateien,
|
|
<?php echo TaskCertView::e(TaskUploadPolicy::formatBytes(TaskUploadPolicy::MAX_TOTAL_BYTES)); ?> gesamt
|
|
<?php if ($hasExistingFiles): ?>
|
|
· Leer lassen behält die aktuelle Datei, neue Auswahl ersetzt sie.
|
|
<?php endif; ?>
|
|
</small>
|
|
<?php else: ?>
|
|
<?php
|
|
$prefillCol = trim((string) ($field['prefill_column'] ?? ''));
|
|
$prefillValue = ($prefillCol !== '' && $fieldType === 'text')
|
|
? ContactPrefillColumns::resolve($contactData, $prefillCol)
|
|
: '';
|
|
?>
|
|
<input
|
|
type="<?php echo TaskCertView::e($fieldType === 'number' ? 'number' : ($fieldType === 'date' ? 'date' : 'text')); ?>"
|
|
name="values[<?php echo TaskCertView::e($fieldKey); ?>]"
|
|
placeholder="<?php echo TaskCertView::e((string) ($field['placeholder'] ?? '')); ?>"
|
|
value="<?php echo TaskCertView::e($prefillValue); ?>"
|
|
<?php echo $required ? 'required' : ''; ?>
|
|
>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<button type="submit" class="btn task-cert-btn-success" <?php echo ($isAssignmentLocked || $previewMode) ? 'disabled' : ''; ?>>Formular absenden</button>
|
|
</form>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($methodType === 'quiz'): ?>
|
|
<form action="<?php echo TaskCertView::e($taskCertBaseUrl . '/endpoints/user/submit_quiz.php'); ?>" method="post">
|
|
<input type="hidden" name="csrf_token" value="<?php echo TaskCertView::e($csrfToken); ?>">
|
|
<input type="hidden" name="assignment_id" value="<?php echo (int) $assignment['id']; ?>">
|
|
<input type="hidden" name="method_id" value="<?php echo (int) $method['id']; ?>">
|
|
<input type="hidden" name="redirect_to" value="<?php echo TaskCertView::e($taskDetailViewUrl); ?>">
|
|
|
|
<div class="task-cert-user-stack">
|
|
<?php foreach ((array) ($method['quiz_questions'] ?? []) as $question): ?>
|
|
<div class="task-cert-quiz-question">
|
|
<strong><?php echo TaskCertView::e((string) ($question['question_text'] ?? '')); ?></strong>
|
|
<?php
|
|
$questionOptions = array_values((array) ($question['options'] ?? []));
|
|
if (count($questionOptions) > 1) {
|
|
shuffle($questionOptions);
|
|
}
|
|
?>
|
|
<div class="task-cert-user-quiz-options">
|
|
<?php foreach ($questionOptions as $option): ?>
|
|
<label class="task-cert-user-checkbox-row task-cert-user-quiz-option">
|
|
<input type="checkbox" name="answers[<?php echo (int) ($question['id'] ?? 0); ?>][]" value="<?php echo (int) ($option['id'] ?? 0); ?>">
|
|
<span><?php echo TaskCertView::e((string) ($option['option_text'] ?? '')); ?></span>
|
|
</label>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<button type="submit" class="btn task-cert-btn-success" <?php echo ($isAssignmentLocked || $previewMode) ? 'disabled' : ''; ?>>Quiz einreichen</button>
|
|
</form>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($methodType === 'manual_approval'): ?>
|
|
<?php
|
|
$manualLatest = $latestSubmissionByMethod[$methodId] ?? null;
|
|
$manualLatestStatus = is_array($manualLatest) ? (string) ($manualLatest['status'] ?? '') : '';
|
|
$manualIsPending = $manualLatestStatus === 'awaiting_approval';
|
|
$manualIsApproved = in_array($manualLatestStatus, ['approved', 'auto_passed'], true);
|
|
$manualSubmittedAt = is_array($manualLatest) ? $formatDateTime($manualLatest['submitted_at'] ?? '') : '-';
|
|
$manualReviewNote = trim((string) ($manualLatest['review_note'] ?? ''));
|
|
?>
|
|
<form action="<?php echo TaskCertView::e($taskCertBaseUrl . '/endpoints/user/request_manual_approval.php'); ?>" method="post">
|
|
<input type="hidden" name="csrf_token" value="<?php echo TaskCertView::e($csrfToken); ?>">
|
|
<input type="hidden" name="assignment_id" value="<?php echo (int) $assignment['id']; ?>">
|
|
<input type="hidden" name="method_id" value="<?php echo (int) $method['id']; ?>">
|
|
<input type="hidden" name="redirect_to" value="<?php echo TaskCertView::e($taskDetailViewUrl); ?>">
|
|
|
|
<?php if ($manualIsPending): ?>
|
|
<div class="task-cert-warning">
|
|
Ihr Freigabeantrag wurde am <?php echo TaskCertView::e($manualSubmittedAt); ?> gesendet und wird aktuell geprüft.
|
|
</div>
|
|
<?php elseif ($manualIsApproved): ?>
|
|
<div class="task-cert-success">
|
|
Ihr Freigabeantrag wurde am <?php echo TaskCertView::e($manualSubmittedAt); ?> bestätigt.
|
|
</div>
|
|
<?php else: ?>
|
|
<?php if ($manualLatestStatus === 'rejected'): ?>
|
|
<div class="task-cert-warning">
|
|
Ihr letzter Antrag wurde abgelehnt. Bitte reichen Sie ihn erneut ein.
|
|
<?php if ($manualReviewNote !== ''): ?>
|
|
Hinweis: <?php echo TaskCertView::e($manualReviewNote); ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<label for="manual_note_<?php echo (int) $method['id']; ?>">Kurzer Hinweis (optional)</label>
|
|
<textarea id="manual_note_<?php echo (int) $method['id']; ?>" name="note" rows="4" placeholder="Falls nötig, kurze Ergänzung für die Freigabe."></textarea>
|
|
<button type="submit" class="btn task-cert-btn-success" <?php echo ($isAssignmentLocked || $previewMode) ? 'disabled' : ''; ?>>Antrag senden</button>
|
|
<?php endif; ?>
|
|
</form>
|
|
<?php endif; ?>
|
|
</section>
|
|
<?php endforeach; ?>
|
|
|
|
<?php if ($methods === []): ?>
|
|
<div class="task-cert-hint">Für diese Aufgabe sind aktuell keine Schritte hinterlegt.</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</main>
|
|
|
|
<aside class="task-cert-user-aside">
|
|
<div class="task-cert-user-status-compact">
|
|
<span class="task-cert-user-status-badge status-<?php echo TaskCertView::e($assignmentStatus); ?>">
|
|
<?php echo TaskCertView::e($statusLabels[$assignmentStatus] ?? $assignmentStatus); ?>
|
|
</span>
|
|
<span class="task-cert-user-status-due <?php echo $assignmentStatus === 'overdue' ? 'is-overdue' : ''; ?>">
|
|
<?php echo TaskCertView::e($formatDueMessage($assignment['due_at'] ?? '')); ?>
|
|
</span>
|
|
<?php if ($totalRequiredSteps > 0): ?>
|
|
<span class="task-cert-user-status-steps">
|
|
<?php echo (int) $completedRequiredSteps; ?>/<?php echo (int) $totalRequiredSteps; ?> Schritte
|
|
</span>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php if ($taskResponsibles !== []): ?>
|
|
<div class="task-cert-block">
|
|
<h3>Ansprechpartner</h3>
|
|
<div class="task-cert-user-responsibles">
|
|
<?php foreach ($taskResponsibles as $responsible): ?>
|
|
<?php
|
|
$respName = trim((string) ($responsible['name'] ?? ''));
|
|
$respEmail = trim((string) ($responsible['email'] ?? ''));
|
|
?>
|
|
<div class="task-cert-user-responsible-item">
|
|
<strong><?php echo TaskCertView::e($respName !== '' ? $respName : 'Kontakt #' . (int) ($responsible['main_contact_id'] ?? 0)); ?></strong>
|
|
<?php if ($respEmail !== ''): ?>
|
|
<a href="mailto:<?php echo TaskCertView::e($respEmail); ?>"><?php echo TaskCertView::e($respEmail); ?></a>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="task-cert-block">
|
|
<h3>Bisherige Einreichungen</h3>
|
|
<?php
|
|
$renderSubmissionCard = static function (
|
|
array $sub,
|
|
string $methodType,
|
|
array $fieldLabels,
|
|
array $submissionStatusLabels,
|
|
callable $formatDateTime
|
|
) use ($taskCertBaseUrl, $csrfToken): void {
|
|
$subStatus = (string) ($sub['status'] ?? '');
|
|
$submissionId = (int) ($sub['id'] ?? 0);
|
|
$downloadFileUrl = $taskCertBaseUrl . '/endpoints/admin/download_submission_file.php';
|
|
$statusLabel = $submissionStatusLabels[$subStatus] ?? $subStatus;
|
|
$submittedAt = $formatDateTime($sub['submitted_at'] ?? '');
|
|
$payload = is_array($sub['payload'] ?? null) ? $sub['payload'] : [];
|
|
$formValues = is_array($payload['form_values'] ?? null) ? $payload['form_values'] : [];
|
|
?>
|
|
<article class="task-cert-user-submission-card status-<?php echo TaskCertView::e($subStatus); ?>">
|
|
<div class="task-cert-user-submission-card-head">
|
|
<span class="task-cert-user-status-badge status-<?php echo TaskCertView::e($subStatus); ?>">
|
|
<?php echo TaskCertView::e($statusLabel); ?>
|
|
</span>
|
|
<span class="task-cert-user-submission-card-date">
|
|
<?php echo TaskCertView::e($submittedAt); ?>
|
|
</span>
|
|
</div>
|
|
<?php if ($methodType === 'form_submit' && $formValues !== []): ?>
|
|
<details class="task-cert-user-submission-details">
|
|
<summary>Antworten anzeigen</summary>
|
|
<dl class="task-cert-user-submission-answers">
|
|
<?php foreach ($formValues as $fKey => $fVal): ?>
|
|
<?php
|
|
$fKey = (string) $fKey;
|
|
$fLabel = trim((string) ($fieldLabels[$fKey] ?? $fKey));
|
|
$isFileVal = is_array($fVal) && $fVal !== [] && is_array(reset($fVal))
|
|
&& array_key_exists('file_path', reset($fVal));
|
|
?>
|
|
<div class="task-cert-user-submission-answer-row">
|
|
<dt><?php echo TaskCertView::e($fLabel); ?></dt>
|
|
<dd>
|
|
<?php if ($isFileVal): ?>
|
|
<?php $fileLinks = [];
|
|
foreach (array_values($fVal) as $i => $fileMeta):
|
|
if (!is_array($fileMeta)) { continue; }
|
|
$fileName = trim((string) ($fileMeta['file_name'] ?? ''));
|
|
$url = $downloadFileUrl . '?' . http_build_query([
|
|
'submission_id' => $submissionId,
|
|
'field_key' => $fKey,
|
|
'index' => (int) $i,
|
|
'csrf_token' => $csrfToken,
|
|
]);
|
|
$fileLinks[] = '<a href="' . TaskCertView::e($url) . '" target="_blank" rel="noopener">'
|
|
. TaskCertView::e($fileName !== '' ? $fileName : 'Datei') . '</a>';
|
|
endforeach;
|
|
echo $fileLinks === [] ? '-' : implode(', ', $fileLinks);
|
|
?>
|
|
<?php else: ?>
|
|
<?php $fDisplay = is_array($fVal) ? implode(', ', $fVal) : trim((string) $fVal); ?>
|
|
<?php echo TaskCertView::e($fDisplay !== '' ? $fDisplay : '-'); ?>
|
|
<?php endif; ?>
|
|
</dd>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</dl>
|
|
</details>
|
|
<?php endif; ?>
|
|
</article>
|
|
<?php
|
|
};
|
|
?>
|
|
|
|
<div class="task-cert-user-submissions-history">
|
|
<?php foreach ($methods as $method): ?>
|
|
<?php
|
|
$methodId = (int) ($method['id'] ?? 0);
|
|
$methodType = (string) ($method['method_type'] ?? '');
|
|
$methodSubs = $allSubmissionsByMethod[$methodId] ?? [];
|
|
$latest = $latestSubmissionByMethod[$methodId] ?? null;
|
|
$fieldLabels = $formFieldLabelsByMethod[$methodId] ?? [];
|
|
$olderSubs = array_slice($methodSubs, 1);
|
|
?>
|
|
<div class="task-cert-user-submission-group">
|
|
<strong><?php echo TaskCertView::e($methodLabels[$methodType] ?? $methodType); ?></strong>
|
|
<?php if (is_array($latest)): ?>
|
|
<?php $renderSubmissionCard($latest, $methodType, $fieldLabels, $submissionStatusLabels, $formatDateTime); ?>
|
|
|
|
<?php if ($olderSubs !== []): ?>
|
|
<details class="task-cert-user-older-submissions">
|
|
<summary>Ältere Einreichungen (<?php echo count($olderSubs); ?>)</summary>
|
|
<div class="task-cert-user-older-submissions-list">
|
|
<?php foreach ($olderSubs as $olderSub): ?>
|
|
<?php $renderSubmissionCard($olderSub, $methodType, $fieldLabels, $submissionStatusLabels, $formatDateTime); ?>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</details>
|
|
<?php endif; ?>
|
|
<?php else: ?>
|
|
<span class="task-cert-hint">Noch keine Einreichung.</span>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
|
|
<?php if ($methods === []): ?>
|
|
<div class="task-cert-hint">Es gibt noch keine Einreichungen.</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="task-cert-block">
|
|
<h3>Ihre Zertifikate zu dieser Aufgabe</h3>
|
|
|
|
<?php if ((int) ($task['certificate_enabled'] ?? 0) !== 1): ?>
|
|
<div class="task-cert-hint">Für diese Aufgabe sind keine Zertifikate vorgesehen.</div>
|
|
<?php elseif ($taskCertificatesHistory === []): ?>
|
|
<div class="task-cert-hint">Bisher keine Zertifikate zu dieser Aufgabe.</div>
|
|
<?php else: ?>
|
|
<?php
|
|
$certificateTitle = trim((string) ($task['certificate_title'] ?? ''));
|
|
if ($certificateTitle === '') {
|
|
$certificateTitle = 'Zertifikat';
|
|
}
|
|
?>
|
|
<div class="task-cert-user-certificate-cards">
|
|
<?php foreach ($taskCertificatesHistory as $certificate): ?>
|
|
<?php
|
|
$certificateStatus = (string) ($certificate['status'] ?? '');
|
|
$validUntilRaw = trim((string) ($certificate['valid_until'] ?? ''));
|
|
$cycleKey = trim((string) ($certificate['cycle_key'] ?? ''));
|
|
?>
|
|
<article class="task-cert-user-certificate-card status-<?php echo TaskCertView::e($certificateStatus); ?>">
|
|
<div class="task-cert-user-certificate-card-head">
|
|
<div class="task-cert-user-certificate-card-code">
|
|
<strong><?php echo TaskCertView::e($certificateTitle); ?></strong>
|
|
<small>Code: <?php echo TaskCertView::e((string) ($certificate['certificate_code'] ?? '-')); ?></small>
|
|
</div>
|
|
<span class="task-cert-user-status-badge status-<?php echo TaskCertView::e($certificateStatus); ?>">
|
|
<?php echo TaskCertView::e($certificateStatusLabels[$certificateStatus] ?? $certificateStatus); ?>
|
|
</span>
|
|
</div>
|
|
<div class="task-cert-user-certificate-card-meta">
|
|
<span>Ausgestellt: <?php echo TaskCertView::e($formatDateTime($certificate['issued_at'] ?? '')); ?></span>
|
|
<span>Gültig bis: <?php echo TaskCertView::e($validUntilRaw !== '' ? $formatDateTime($validUntilRaw) : 'dauerhaft'); ?></span>
|
|
<span>Zyklus: <?php echo TaskCertView::e($cycleKey !== '' ? $cycleKey : '-'); ?></span>
|
|
</div>
|
|
</article>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|