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>
285 lines
8.5 KiB
PHP
285 lines
8.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once dirname(dirname(__DIR__)) . '/bootstrap.php';
|
|
|
|
if (!function_exists('task_cert_sync_assignments_append_query_params')) {
|
|
function task_cert_sync_assignments_append_query_params(string $url, array $params): string
|
|
{
|
|
if ($url === '' || $params === []) {
|
|
return $url;
|
|
}
|
|
|
|
$parts = parse_url($url);
|
|
if ($parts === false) {
|
|
$queryString = http_build_query($params);
|
|
if ($queryString === '') {
|
|
return $url;
|
|
}
|
|
|
|
$separator = strpos($url, '?') === false ? '?' : '&';
|
|
return $url . $separator . $queryString;
|
|
}
|
|
|
|
$path = (string) ($parts['path'] ?? '');
|
|
if ($path !== '' && substr($path, -1) !== '/') {
|
|
$parts['path'] = $path . '/';
|
|
}
|
|
|
|
$existing = [];
|
|
$rawQuery = (string) ($parts['query'] ?? '');
|
|
if ($rawQuery !== '') {
|
|
parse_str($rawQuery, $existing);
|
|
}
|
|
|
|
foreach ($params as $key => $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);
|
|
}
|