Merge branch 'latest' of forge.dmz.skyfritt.net:stopplidelsen/innhold into latest
This commit is contained in:
commit
80f308d186
1 changed files with 317 additions and 2 deletions
|
|
@ -407,7 +407,9 @@ function showMenu(): void {
|
|||
echo " 3) Send e-post pa nytt til mislykkede\n";
|
||||
echo " 4) Send bekreftelse pa nytt til ubekreftede\n";
|
||||
echo " 5) Marker oppforinger som ignorert\n";
|
||||
echo " 6) Avslutt\n";
|
||||
echo " 6) Manuell bekreftelse av signaturer\n";
|
||||
echo " 7) Slett signaturer\n";
|
||||
echo " 8) Avslutt\n";
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
|
|
@ -642,6 +644,313 @@ function resendToUnconfirmed(): void {
|
|||
printColor("Ferdig: {$success} vellykket, {$failures} mislykket\n", $failures > 0 ? 'yellow' : 'green');
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically modify petition CSV file with a callback function.
|
||||
* The callback receives the rows array (without header) by reference and can modify it.
|
||||
* Returns the result from the callback, or null on file errors.
|
||||
*
|
||||
* @param string $petitionId The petition ID
|
||||
* @param callable $callback Function that receives (&$rows, $petitionId) and returns a result
|
||||
* @return mixed|null The callback's return value, or null on error
|
||||
*/
|
||||
function modifyPetitionFile(string $petitionId, callable $callback): mixed {
|
||||
$csvPath = PETITIONS_DIR . '/' . $petitionId . '.csv';
|
||||
if (!file_exists($csvPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Open with r+ to allow reading and writing, acquire exclusive lock immediately
|
||||
// This matches the pattern used in petition-form.php to prevent race conditions
|
||||
$fp = fopen($csvPath, 'r+');
|
||||
if (!$fp) {
|
||||
printColor(" Advarsel: Kunne ikke apne {$petitionId}.csv\n", 'yellow');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!flock($fp, LOCK_EX)) {
|
||||
fclose($fp);
|
||||
printColor(" Advarsel: Kunne ikke lase {$petitionId}.csv\n", 'yellow');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Read all rows while holding the lock
|
||||
$header = fgetcsv($fp, null, ',', '"', '');
|
||||
if ($header === false) {
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
return null;
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
while (($row = fgetcsv($fp, null, ',', '"', '')) !== false) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
// Call the callback to modify rows
|
||||
$originalCount = count($rows);
|
||||
$result = $callback($rows, $petitionId);
|
||||
|
||||
// Write back if rows were modified (count changed or callback signals modification)
|
||||
$modified = (count($rows) !== $originalCount);
|
||||
if (!$modified && is_array($result) && isset($result['modified'])) {
|
||||
$modified = $result['modified'];
|
||||
}
|
||||
|
||||
if ($modified) {
|
||||
rewind($fp);
|
||||
ftruncate($fp, 0);
|
||||
fputcsv($fp, $header, ',', '"', '');
|
||||
foreach ($rows as $row) {
|
||||
fputcsv($fp, $row, ',', '"', '');
|
||||
}
|
||||
}
|
||||
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually confirm signatures by email address
|
||||
*/
|
||||
function manuallyConfirmSignatures(): void {
|
||||
echo "\n";
|
||||
printColor("Manuell bekreftelse av signaturer\n", 'cyan');
|
||||
echo "\n";
|
||||
|
||||
$input = prompt("Skriv inn e-postadresse(r) (kommaseparert): ");
|
||||
if (empty(trim($input))) {
|
||||
echo "Ingen e-postadresser oppgitt.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse and clean email addresses
|
||||
$emails = array_map('trim', explode(',', $input));
|
||||
$emails = array_filter($emails, fn($e) => !empty($e));
|
||||
$emails = array_map('strtolower', $emails);
|
||||
|
||||
if (empty($emails)) {
|
||||
echo "Ingen gyldige e-postadresser oppgitt.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
echo "Soker etter " . count($emails) . " e-postadresse(r)...\n\n";
|
||||
|
||||
$found = [];
|
||||
|
||||
// Search through all petition files
|
||||
foreach (getPetitionFiles() as $petitionId) {
|
||||
$result = modifyPetitionFile($petitionId, function(&$rows, $petitionId) use ($emails) {
|
||||
$found = [];
|
||||
$modified = false;
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
if (!isset($row[1]) || !isset($row[6])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rowEmail = strtolower($row[1]);
|
||||
if (in_array($rowEmail, $emails)) {
|
||||
if ($row[6] === 'pending') {
|
||||
$row[6] = 'confirmed';
|
||||
$modified = true;
|
||||
$found[] = [
|
||||
'email' => $row[1],
|
||||
'name' => ($row[2] ?? '') . ' ' . ($row[3] ?? ''),
|
||||
'petition_id' => $petitionId,
|
||||
'was_pending' => true
|
||||
];
|
||||
} else {
|
||||
$found[] = [
|
||||
'email' => $row[1],
|
||||
'name' => ($row[2] ?? '') . ' ' . ($row[3] ?? ''),
|
||||
'petition_id' => $petitionId,
|
||||
'was_pending' => false,
|
||||
'status' => $row[6]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($row);
|
||||
|
||||
return ['found' => $found, 'modified' => $modified];
|
||||
});
|
||||
|
||||
if ($result && !empty($result['found'])) {
|
||||
$found = array_merge($found, $result['found']);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which emails were not found
|
||||
$foundEmails = array_map(fn($f) => strtolower($f['email']), $found);
|
||||
$notFound = [];
|
||||
foreach ($emails as $email) {
|
||||
if (!in_array($email, $foundEmails)) {
|
||||
$notFound[] = $email;
|
||||
}
|
||||
}
|
||||
|
||||
// Display results
|
||||
if (!empty($found)) {
|
||||
printColor("Funnet:\n", 'green');
|
||||
foreach ($found as $entry) {
|
||||
$status = $entry['was_pending']
|
||||
? "BEKREFTET"
|
||||
: "allerede " . $entry['status'];
|
||||
echo " - {$entry['email']} ({$entry['name']}) - {$entry['petition_id']}: ";
|
||||
if ($entry['was_pending']) {
|
||||
printColor("{$status}\n", 'green');
|
||||
} else {
|
||||
printColor("{$status}\n", 'yellow');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($notFound)) {
|
||||
echo "\n";
|
||||
printColor("Ikke funnet:\n", 'red');
|
||||
foreach ($notFound as $email) {
|
||||
echo " - {$email}\n";
|
||||
}
|
||||
}
|
||||
|
||||
$confirmedCount = count(array_filter($found, fn($f) => $f['was_pending']));
|
||||
echo "\n";
|
||||
printColor("Ferdig: {$confirmedCount} signatur(er) bekreftet\n", 'green');
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually delete signatures by email address
|
||||
*/
|
||||
function manuallyDeleteSignatures(): void {
|
||||
echo "\n";
|
||||
printColor("Slett signaturer\n", 'cyan');
|
||||
echo "\n";
|
||||
|
||||
$input = prompt("Skriv inn e-postadresse(r) (kommaseparert): ");
|
||||
if (empty(trim($input))) {
|
||||
echo "Ingen e-postadresser oppgitt.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse and clean email addresses
|
||||
$emails = array_map('trim', explode(',', $input));
|
||||
$emails = array_filter($emails, fn($e) => !empty($e));
|
||||
$emails = array_map('strtolower', $emails);
|
||||
|
||||
if (empty($emails)) {
|
||||
echo "Ingen gyldige e-postadresser oppgitt.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
echo "Soker etter " . count($emails) . " e-postadresse(r)...\n\n";
|
||||
|
||||
$found = [];
|
||||
|
||||
// First pass: find all matching signatures (read-only)
|
||||
foreach (getPetitionFiles() as $petitionId) {
|
||||
$csvPath = PETITIONS_DIR . '/' . $petitionId . '.csv';
|
||||
if (!file_exists($csvPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fp = fopen($csvPath, 'r');
|
||||
if (!$fp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!flock($fp, LOCK_SH)) {
|
||||
fclose($fp);
|
||||
continue;
|
||||
}
|
||||
|
||||
fgetcsv($fp, null, ',', '"', ''); // Skip header
|
||||
while (($row = fgetcsv($fp, null, ',', '"', '')) !== false) {
|
||||
if (!isset($row[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rowEmail = strtolower($row[1]);
|
||||
if (in_array($rowEmail, $emails)) {
|
||||
$found[] = [
|
||||
'email' => $row[1],
|
||||
'name' => ($row[2] ?? '') . ' ' . ($row[3] ?? ''),
|
||||
'petition_id' => $petitionId,
|
||||
'status' => $row[6] ?? 'unknown',
|
||||
'timestamp' => (int)($row[0] ?? 0)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
// Determine which emails were not found
|
||||
$foundEmails = array_map(fn($f) => strtolower($f['email']), $found);
|
||||
$notFound = [];
|
||||
foreach ($emails as $email) {
|
||||
if (!in_array($email, $foundEmails)) {
|
||||
$notFound[] = $email;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($notFound)) {
|
||||
printColor("Ikke funnet:\n", 'red');
|
||||
foreach ($notFound as $email) {
|
||||
echo " - {$email}\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
if (empty($found)) {
|
||||
echo "Ingen signaturer a slette.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Show what will be deleted
|
||||
printColor("Folgende signaturer vil bli slettet:\n", 'yellow');
|
||||
foreach ($found as $entry) {
|
||||
$date = formatDate($entry['timestamp']);
|
||||
echo " - {$entry['email']} ({$entry['name']}) - {$entry['petition_id']} [{$entry['status']}] {$date}\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
if (!confirm("Er du sikker pa at du vil slette " . count($found) . " signatur(er)?")) {
|
||||
echo "Avbrutt.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Second pass: delete the signatures
|
||||
$deleted = 0;
|
||||
$emailsToDelete = array_map(fn($f) => strtolower($f['email']), $found);
|
||||
|
||||
foreach (getPetitionFiles() as $petitionId) {
|
||||
$result = modifyPetitionFile($petitionId, function(&$rows, $petitionId) use ($emailsToDelete) {
|
||||
$deletedCount = 0;
|
||||
$rows = array_filter($rows, function($row) use ($emailsToDelete, &$deletedCount) {
|
||||
if (isset($row[1]) && in_array(strtolower($row[1]), $emailsToDelete)) {
|
||||
$deletedCount++;
|
||||
return false; // Remove this row
|
||||
}
|
||||
return true; // Keep this row
|
||||
});
|
||||
$rows = array_values($rows); // Re-index
|
||||
return ['deleted' => $deletedCount];
|
||||
});
|
||||
|
||||
if ($result && isset($result['deleted'])) {
|
||||
$deleted += $result['deleted'];
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
printColor("Ferdig: {$deleted} signatur(er) slettet\n", 'green');
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive ignore marking
|
||||
*/
|
||||
|
|
@ -823,13 +1132,19 @@ function main(): void {
|
|||
markAsIgnored();
|
||||
break;
|
||||
case '6':
|
||||
manuallyConfirmSignatures();
|
||||
break;
|
||||
case '7':
|
||||
manuallyDeleteSignatures();
|
||||
break;
|
||||
case '8':
|
||||
case 'q':
|
||||
case 'quit':
|
||||
case 'exit':
|
||||
echo "Ha det!\n";
|
||||
exit(0);
|
||||
default:
|
||||
printColor("Ugyldig valg. Velg 1-6.\n", 'red');
|
||||
printColor("Ugyldig valg. Velg 1-8.\n", 'red');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue