Files
awo-hamburg-intranet/module/tasks/endpoints/admin/task_upsert.php

419 lines
15 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
require_once dirname(dirname(__DIR__)) . '/bootstrap.php';
if (!function_exists('task_cert_append_query_params')) {
function task_cert_append_query_params(string $url, array $params): string
{
if ($url === '' || $params === []) {
return $url;
}
$queryString = http_build_query($params);
if ($queryString === '') {
return $url;
}
$separator = strpos($url, '?') === false ? '?' : '&';
return $url . $separator . $queryString;
}
}
if (!TaskCertRequest::isPost()) {
TaskCertResponse::error('Methode nicht erlaubt', 405);
}
$auth = TaskCertAuthContext::fromGlobals();
if (!$auth->hasPermission('r-task-cert-admin')) {
TaskCertResponse::error('Keine Berechtigung', 403);
}
TaskCertCsrf::assertValidFromRequest(true);
$services = task_cert_services();
$taskService = $services['taskDefinitionService'];
$parseCsvOptions = static function (string $raw): array {
$result = [];
$parts = preg_split('/[,\n]/', $raw) ?: [];
foreach ($parts as $part) {
$item = trim((string) $part);
if ($item === '') {
continue;
}
$result[] = [
'value' => $item,
'label' => $item,
];
}
return $result;
};
$slugifyFieldKey = static function (string $label): string {
$map = [
'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'ß' => 'ss',
'Ä' => 'ae', 'Ö' => 'oe', 'Ü' => 'ue',
];
$slug = strtr(mb_strtolower($label, 'UTF-8'), $map);
$slug = preg_replace('/[^a-z0-9]+/', '_', $slug) ?? '';
return trim($slug, '_');
};
$buildMethods = static function () use ($parseCsvOptions, $slugifyFieldKey): array {
$methodEnabled = TaskCertRequest::postArray('method_enabled');
$orderedTypes = TaskCertConstants::METHOD_TYPES;
$methods = [];
$sortOrder = 0;
foreach ($orderedTypes as $methodType) {
if ((int) ($methodEnabled[$methodType] ?? 0) !== 1) {
continue;
}
$method = [
'method_type' => $methodType,
'is_required' => 1,
'sort_order' => $sortOrder,
'active' => 1,
'settings' => [],
];
if ($methodType === 'knowledge_read') {
$knowledgePosts = [];
$postIds = TaskCertRequest::postIntArray('knowledge_post_ids');
foreach ($postIds as $index => $postId) {
$knowledgePosts[] = [
'knowledge_post_id' => $postId,
'required' => 1,
'sort_order' => $index,
];
}
$method['knowledge_posts'] = $knowledgePosts;
}
if ($methodType === 'form_submit') {
$formFields = [];
$rows = TaskCertRequest::postArray('form_fields');
foreach ($rows as $idx => $row) {
if (!is_array($row)) {
continue;
}
$label = trim((string) ($row['label'] ?? ''));
if ($label === '') {
continue;
}
$fieldKey = trim((string) ($row['field_key'] ?? ''));
if ($fieldKey === '') {
$base = $slugifyFieldKey($label);
if ($base === '') {
$base = 'field';
}
$fieldKey = $base;
$usedKeys = array_column($formFields, 'field_key');
$suffix = 2;
while (in_array($fieldKey, $usedKeys, true)) {
$fieldKey = $base . '_' . $suffix;
$suffix++;
}
}
$fieldType = (string) ($row['field_type'] ?? 'text');
if (!TaskCertConstants::isValidFieldType($fieldType)) {
$fieldType = 'text';
}
$formFields[] = [
'field_key' => $fieldKey,
'label' => $label,
'field_type' => $fieldType,
'is_required' => (int) (($row['is_required'] ?? 0) ? 1 : 0),
'options' => $parseCsvOptions((string) ($row['options_text'] ?? '')),
'placeholder' => trim((string) ($row['placeholder'] ?? '')),
'prefill_column' => ContactPrefillColumns::sanitize(trim((string) ($row['prefill_column'] ?? ''))),
'sort_order' => count($formFields),
'active' => 1,
];
}
$method['form_fields'] = $formFields;
}
if ($methodType === 'quiz') {
$quizQuestions = [];
$rows = TaskCertRequest::postArray('quiz_questions');
foreach ($rows as $row) {
if (!is_array($row)) {
continue;
}
$questionText = trim((string) ($row['question_text'] ?? ''));
if ($questionText === '') {
continue;
}
$correctOption = strtolower(trim((string) ($row['correct_option'] ?? 'a')));
if (!in_array($correctOption, ['a', 'b', 'c', 'd'], true)) {
$correctOption = 'a';
}
$optionsMap = [
'a' => trim((string) ($row['option_a'] ?? '')),
'b' => trim((string) ($row['option_b'] ?? '')),
'c' => trim((string) ($row['option_c'] ?? '')),
'd' => trim((string) ($row['option_d'] ?? '')),
];
$options = [];
foreach (['a', 'b', 'c', 'd'] as $position => $letter) {
$optionText = $optionsMap[$letter];
if ($optionText === '') {
continue;
}
$options[] = [
'option_text' => $optionText,
'is_correct' => $letter === $correctOption ? 1 : 0,
'sort_order' => $position,
];
}
if ($options === []) {
continue;
}
$quizQuestions[] = [
'question_text' => $questionText,
'sort_order' => count($quizQuestions),
'active' => 1,
'options' => $options,
];
}
$method['quiz_questions'] = $quizQuestions;
}
$methods[] = $method;
$sortOrder++;
}
return $methods;
};
$buildTargetRules = static function (): array {
$mandantIds = array_values(array_unique(TaskCertRequest::postIntArray('target_mandant_ids')));
$departmentIds = array_values(array_unique(TaskCertRequest::postIntArray('target_department_ids')));
$roleIds = array_values(array_unique(TaskCertRequest::postIntArray('target_role_ids')));
$mandantValues = $mandantIds === [] ? [0] : $mandantIds;
$departmentValues = $departmentIds === [] ? [0] : $departmentIds;
$roleValues = $roleIds === [] ? [0] : $roleIds;
$rules = [];
$count = 0;
$maxRules = 5000;
foreach ($mandantValues as $mandantId) {
foreach ($departmentValues as $departmentId) {
foreach ($roleValues as $roleId) {
if ((int) $mandantId === 0 && (int) $departmentId === 0 && (int) $roleId === 0) {
continue;
}
$rules[] = [
'main_mandant_id' => (int) $mandantId ?: null,
'main_department_id' => (int) $departmentId ?: null,
'main_role_id' => (int) $roleId ?: null,
'active' => 1,
];
$count++;
if ($count > $maxRules) {
throw new RuntimeException('Zu viele Regelkombinationen. Bitte Auswahl einschränken.');
}
}
}
}
return $rules;
};
$buildTargetOverrides = static function (): array {
$includeReason = TaskCertRequest::postString('override_include_reason', '');
$excludeReason = TaskCertRequest::postString('override_exclude_reason', '');
$includeIds = array_values(array_unique(TaskCertRequest::postIntArray('override_include_contact_ids')));
$excludeIds = array_values(array_unique(TaskCertRequest::postIntArray('override_exclude_contact_ids')));
// exclude wins when the same contact is selected in both lists
$includeIds = array_values(array_diff($includeIds, $excludeIds));
$overrides = [];
foreach ($includeIds as $contactId) {
$overrides[] = [
'main_contact_id' => $contactId,
'override_type' => 'include',
'reason' => $includeReason,
];
}
foreach ($excludeIds as $contactId) {
$overrides[] = [
'main_contact_id' => $contactId,
'override_type' => 'exclude',
'reason' => $excludeReason,
];
}
return $overrides;
};
try {
$methods = $buildMethods();
if ($methods === []) {
throw new RuntimeException('Mindestens eine Erledigungsart muss aktiviert sein.');
}
$deadlineRuleType = TaskCertRequest::postString('deadline_rule_type', 'cycle_end');
if (!in_array($deadlineRuleType, ['cycle_end', 'offset_days', 'weekday', 'monthday'], true)) {
$deadlineRuleType = 'cycle_end';
}
$deadlineOffsetDays = null;
$deadlineWeekday = null;
$deadlineMonthday = null;
if ($deadlineRuleType === 'offset_days') {
$deadlineOffsetDays = TaskCertRequest::postInt('deadline_offset_days', 0);
if ($deadlineOffsetDays <= 0) {
throw new RuntimeException('Bitte Frist in Tagen größer 0 angeben.');
}
}
if ($deadlineRuleType === 'weekday') {
$deadlineWeekday = TaskCertRequest::postInt('deadline_weekday', 0);
if ($deadlineWeekday < 1 || $deadlineWeekday > 7) {
throw new RuntimeException('Bitte einen gültigen Wochentag (1-7) auswählen.');
}
}
if ($deadlineRuleType === 'monthday') {
$deadlineMonthday = TaskCertRequest::postInt('deadline_monthday', 0);
if ($deadlineMonthday < 1 || $deadlineMonthday > 31) {
throw new RuntimeException('Bitte einen gültigen Monatstag (1-31) auswählen.');
}
}
$payload = [
'actor_id' => $auth->contactId(),
'title' => TaskCertRequest::postString('title'),
'description' => TaskCertHtml::sanitize(TaskCertRequest::postString('description')),
'task_category_id' => TaskCertRequest::postInt('task_category_id', 0),
'frequency_type' => TaskCertRequest::postString('frequency_type', 'once'),
'recurrence_basis' => TaskCertRequest::postString('recurrence_basis', 'calendar'),
'recurrence_unit' => TaskCertRequest::postString('recurrence_unit', ''),
'recurrence_interval' => TaskCertRequest::postInt('recurrence_interval', 1),
'start_date' => TaskCertRequest::postString('start_date', date('Y-m-d')),
'deadline_rule_type' => $deadlineRuleType,
'deadline_offset_days' => $deadlineOffsetDays,
'deadline_weekday' => $deadlineWeekday,
'deadline_monthday' => $deadlineMonthday,
'fulfillment_mode' => TaskCertRequest::postString('fulfillment_mode', 'all'),
'certificate_enabled' => TaskCertRequest::postInt('certificate_enabled', 1),
'certificate_title' => TaskCertRequest::postString('certificate_title', ''),
'certificate_validity_mode' => TaskCertRequest::postString('certificate_validity_mode', ''),
'certificate_validity_days' => TaskCertRequest::postString('certificate_validity_days', ''),
'show_cycle_period' => TaskCertRequest::postInt('show_cycle_period', 0) === 1 ? 1 : 0,
'active' => TaskCertRequest::postInt('active', 1),
'methods' => $methods,
'target_rules' => $buildTargetRules(),
'target_overrides' => $buildTargetOverrides(),
'responsible_contact_ids' => array_values(array_unique(TaskCertRequest::postIntArray('responsible_contact_ids'))),
'reminder_policy' => [
'base_mode' => 'due_date',
'first_escalation_days' => TaskCertRequest::postInt('reminder_first_escalation_days', 0),
'step_days' => TaskCertRequest::postInt('reminder_step_days', 7),
'max_level' => TaskCertRequest::postInt('reminder_max_level', 3),
'grace_days' => TaskCertRequest::postInt('reminder_grace_days', 7),
'active' => TaskCertRequest::postInt('reminder_active', 0) === 1 ? 1 : 0,
],
];
$taskId = TaskCertRequest::postInt('task_id', 0);
$resetExistingRecords = $taskId > 0 && TaskCertRequest::postInt('reset_existing_records', 0) === 1;
$resetStats = [
'cycle_key' => '',
'assignment_count' => 0,
'submission_count' => 0,
'certificate_count' => 0,
];
if ($taskId > 0) {
$taskService->updateTask($taskId, $payload);
if ($resetExistingRecords) {
$resetStats = $taskService->resetCurrentCycleProgress($taskId, $auth->contactId());
}
} else {
$taskId = $taskService->createTask($payload);
}
$afterSave = TaskCertRequest::postString('after_save', 'stay');
if (!in_array($afterSave, ['stay', 'close'], true)) {
$afterSave = 'stay';
}
$redirectTo = TaskCertRequest::postString('redirect_to', '');
$redirectToStay = TaskCertRequest::postString('redirect_to_stay', '');
$redirectToClose = TaskCertRequest::postString('redirect_to_close', '');
if ($redirectToStay === '') {
$redirectToStay = task_cert_view_url('admin.task_form', [
'id' => $taskId,
'saved' => 1,
]);
}
if ($redirectToClose === '') {
$redirectToClose = task_cert_view_url('admin.task_list');
}
$resolvedRedirect = $redirectTo;
if ($resolvedRedirect === '') {
$resolvedRedirect = $afterSave === 'close' ? $redirectToClose : $redirectToStay;
}
if ($resolvedRedirect !== '') {
$resolvedRedirect = str_replace('{task_id}', (string) $taskId, $resolvedRedirect);
if ($resetExistingRecords) {
$resolvedRedirect = task_cert_append_query_params($resolvedRedirect, [
'reset' => 1,
'reset_assignments' => (int) ($resetStats['assignment_count'] ?? 0),
'reset_submissions' => (int) ($resetStats['submission_count'] ?? 0),
'reset_certificates' => (int) ($resetStats['certificate_count'] ?? 0),
]);
}
TaskCertResponse::redirect($resolvedRedirect);
}
TaskCertResponse::json([
'success' => true,
'task_id' => $taskId,
'redirect' => $resolvedRedirect,
'reset' => $resetExistingRecords,
'reset_stats' => $resetStats,
]);
} catch (Throwable $throwable) {
TaskCertResponse::json([
'success' => false,
'error' => $throwable->getMessage(),
], 400);
}