$value) { if (!is_string($key) || $key === '') { continue; } if ($value === null) { unset($existing[$key]); continue; } $existing[$key] = $value; } $parts['query'] = http_build_query($existing); return task_cert_sync_assignments_build_url($parts); } } if (!function_exists('task_cert_sync_assignments_build_url')) { function task_cert_sync_assignments_build_url(array $parts): string { $url = ''; if (isset($parts['scheme']) && $parts['scheme'] !== '') { $url .= $parts['scheme'] . '://'; } if (isset($parts['user']) && $parts['user'] !== '') { $url .= $parts['user']; if (isset($parts['pass']) && $parts['pass'] !== '') { $url .= ':' . $parts['pass']; } $url .= '@'; } if (isset($parts['host']) && $parts['host'] !== '') { $url .= $parts['host']; } if (isset($parts['port'])) { $url .= ':' . (int) $parts['port']; } $url .= (string) ($parts['path'] ?? ''); $query = (string) ($parts['query'] ?? ''); if ($query !== '') { $url .= '?' . $query; } $fragment = (string) ($parts['fragment'] ?? ''); if ($fragment !== '') { $url .= '#' . $fragment; } return $url; } } if (!function_exists('task_cert_sync_assignments_zero_totals')) { function task_cert_sync_assignments_zero_totals(bool $applied): array { return [ 'task_count' => 0, 'target_count' => 0, 'existing_count' => 0, 'create_count' => 0, 'update_count' => 0, 'stale_count' => 0, 'protected_completed_count' => 0, 'protected_certificate_count' => 0, 'remove_count' => 0, 'remove_with_submissions_count' => 0, 'applied' => $applied, ]; } } if (!function_exists('task_cert_sync_assignments_sum_totals')) { function task_cert_sync_assignments_sum_totals(array $results, bool $applied): array { $totals = task_cert_sync_assignments_zero_totals($applied); foreach ($results as $result) { if (!is_array($result)) { continue; } $totals['task_count']++; foreach ([ 'target_count', 'existing_count', 'create_count', 'update_count', 'stale_count', 'protected_completed_count', 'protected_certificate_count', 'remove_count', 'remove_with_submissions_count', ] as $key) { $totals[$key] += (int) ($result[$key] ?? 0); } } return $totals; } } $auth = TaskCertAuthContext::fromGlobals(); $isCli = PHP_SAPI === 'cli'; if (!$isCli && !TaskCertRequest::isPost()) { TaskCertResponse::error('Methode nicht erlaubt', 405); } if (!$isCli) { if (!$auth->hasPermission('r-task-cert-admin')) { TaskCertResponse::error('Keine Berechtigung', 403); } TaskCertCsrf::assertValidFromRequest(true); } $taskId = 0; $mode = 'dry_run'; $scope = 'current_cycle'; $redirectTo = ''; $tabIndex = 0; if ($isCli) { $options = getopt('', ['task_id::', 'mode::', 'scope::', 'apply', 'dry-run']); if ($options !== false) { if (isset($options['task_id'])) { $taskId = (int) $options['task_id']; } if (isset($options['mode']) && !is_array($options['mode'])) { $mode = trim((string) $options['mode']); } if (isset($options['scope']) && !is_array($options['scope'])) { $scope = trim((string) $options['scope']); } if (isset($options['apply'])) { $mode = 'apply'; } if (isset($options['dry-run'])) { $mode = 'dry_run'; } } } else { $taskId = TaskCertRequest::postInt('task_id', 0); $mode = TaskCertRequest::postString('mode', 'dry_run'); $scope = TaskCertRequest::postString('scope', 'current_cycle'); $redirectTo = TaskCertRequest::postString('redirect_to', ''); $tabIndex = TaskCertRequest::postInt('tab', 0); if ($taskId <= 0) { TaskCertResponse::error('Ungültige task_id', 400); } } if ($tabIndex < 0) { $tabIndex = 0; } if (!in_array($mode, ['dry_run', 'apply'], true)) { TaskCertResponse::error('Ungültiger mode', 400); } if ($scope !== 'current_cycle') { TaskCertResponse::error('Ungültiger scope', 400); } $apply = $mode === 'apply'; try { $services = task_cert_services(); $resolver = $services['assignmentResolverService'] ?? null; if (!$resolver instanceof AssignmentResolverService) { throw new RuntimeException('AssignmentResolverService nicht verfügbar.'); } $results = []; $singleResult = null; if ($taskId > 0) { $singleResult = $resolver->syncAssignmentsForTask($taskId, $apply, $scope); $results[] = $singleResult; } else { $results = $resolver->syncAssignmentsForAllActiveTasks($apply, $scope); } $totals = task_cert_sync_assignments_sum_totals($results, $apply); if ($apply && $taskId <= 0) { CronHeartbeat::record('sync_assignments', [ 'task_count' => (int) ($totals['task_count'] ?? 0), 'create_count' => (int) ($totals['create_count'] ?? 0), 'remove_count' => (int) ($totals['remove_count'] ?? 0), ]); } if (!$isCli && $redirectTo !== '' && $singleResult !== null) { $redirectTo = task_cert_sync_assignments_append_query_params($redirectTo, [ 'sync_done' => 1, 'sync_mode' => $mode, 'sync_applied' => $apply ? 1 : 0, 'sync_scope' => $scope, 'tab' => $tabIndex, 'sync_cycle_key' => (string) ($singleResult['cycle_key'] ?? ''), 'sync_target_count' => (int) ($singleResult['target_count'] ?? 0), 'sync_existing_count' => (int) ($singleResult['existing_count'] ?? 0), 'sync_create_count' => (int) ($singleResult['create_count'] ?? 0), 'sync_update_count' => (int) ($singleResult['update_count'] ?? 0), 'sync_stale_count' => (int) ($singleResult['stale_count'] ?? 0), 'sync_protected_completed_count' => (int) ($singleResult['protected_completed_count'] ?? 0), 'sync_protected_certificate_count' => (int) ($singleResult['protected_certificate_count'] ?? 0), 'sync_remove_count' => (int) ($singleResult['remove_count'] ?? 0), 'sync_remove_with_submissions_count' => (int) ($singleResult['remove_with_submissions_count'] ?? 0), ]); TaskCertResponse::redirect($redirectTo); } TaskCertResponse::json([ 'success' => true, 'mode' => $mode, 'scope' => $scope, 'task_id' => $taskId > 0 ? $taskId : null, 'result' => $singleResult, 'results' => $taskId > 0 ? [] : $results, 'totals' => $totals, ]); } catch (Throwable $throwable) { if (!$isCli && $redirectTo !== '') { $redirectTo = task_cert_sync_assignments_append_query_params($redirectTo, [ 'sync_done' => 0, 'tab' => $tabIndex, 'sync_error' => $throwable->getMessage(), ]); TaskCertResponse::redirect($redirectTo); } TaskCertResponse::json([ 'success' => false, 'error' => $throwable->getMessage(), ], 400); }