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 " 3) Send e-post pa nytt til mislykkede\n";
|
||||||
echo " 4) Send bekreftelse pa nytt til ubekreftede\n";
|
echo " 4) Send bekreftelse pa nytt til ubekreftede\n";
|
||||||
echo " 5) Marker oppforinger som ignorert\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";
|
echo "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -642,6 +644,313 @@ function resendToUnconfirmed(): void {
|
||||||
printColor("Ferdig: {$success} vellykket, {$failures} mislykket\n", $failures > 0 ? 'yellow' : 'green');
|
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
|
* Interactive ignore marking
|
||||||
*/
|
*/
|
||||||
|
|
@ -823,13 +1132,19 @@ function main(): void {
|
||||||
markAsIgnored();
|
markAsIgnored();
|
||||||
break;
|
break;
|
||||||
case '6':
|
case '6':
|
||||||
|
manuallyConfirmSignatures();
|
||||||
|
break;
|
||||||
|
case '7':
|
||||||
|
manuallyDeleteSignatures();
|
||||||
|
break;
|
||||||
|
case '8':
|
||||||
case 'q':
|
case 'q':
|
||||||
case 'quit':
|
case 'quit':
|
||||||
case 'exit':
|
case 'exit':
|
||||||
echo "Ha det!\n";
|
echo "Ha det!\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
default:
|
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