mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-app.git
synced 2026-06-17 04:30:31 +00:00
9f8fda294a
- 🔒 Restrict event creation, editing, and deletion to Meetup leaders (`is_leader`) and creators for consistency across APIs, frontend, and MCP. - ➕ Add new APIs for leader delegation: assign/remove Meetup leaders via `meetup_user.is_leader`. - 🛠️ Replace loose member checks with specific leadership checks in policies, controllers, and views. - 🧪 Add exhaustive tests to ensure only eligible leaders execute critical actions (e.g., event creation/edit, Meetup updates). - 🔄 Refactor pivot relationships and models (`leadByMe`, `isLeader`) for explicit leadership handling. - ✨ Introduce artisan command `meetups:promote-existing-leaders` to transition legacy data.
66 lines
2.5 KiB
PHP
66 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands\Database;
|
|
|
|
use App\Models\Meetup;
|
|
use Illuminate\Console\Attributes\Description;
|
|
use Illuminate\Console\Attributes\Signature;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/**
|
|
* Einmalige Fixierung des Ist-Zustands beim Wechsel auf das Leader-Modell:
|
|
* Bisher durfte jedes „Meine Meetups"-Mitglied (meetup_user) ein Meetup über
|
|
* das Portal bearbeiten. Dieser Kreis gilt als historisch legitimiert und wird
|
|
* zu echten Leadern (meetup_user.is_leader = true) befördert. Zusätzlich wird
|
|
* sichergestellt, dass jeder Ersteller Leader seines Meetups ist (auch bei
|
|
* Alt-Meetups, die vor dem created-Hook angelegt wurden).
|
|
*
|
|
* Nach diesem Lauf berechtigt nur noch is_leader = true zum Bearbeiten; frisch
|
|
* über addToMine hinzugefügte Mitglieder bleiben is_leader = false.
|
|
*/
|
|
#[Signature('meetups:promote-existing-leaders {--dry-run : Nur anzeigen, nichts schreiben}')]
|
|
#[Description('Befördert alle bestehenden meetup_user-Mitglieder zu Leadern (Ist-Zustand fixieren) und sichert die Ersteller-Leaderschaft.')]
|
|
class PromoteExistingLeaders extends Command
|
|
{
|
|
public function handle(): int
|
|
{
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
|
|
$memberRows = DB::table('meetup_user')->where('is_leader', false)->count();
|
|
|
|
$missingCreators = Meetup::query()
|
|
->whereNotNull('created_by')
|
|
->whereDoesntHave('users', function ($query): void {
|
|
$query->whereColumn('users.id', 'meetups.created_by');
|
|
})
|
|
->count();
|
|
|
|
if ($dryRun) {
|
|
$this->info("Dry-Run: {$memberRows} Mitglieder würden zu Leadern befördert.");
|
|
$this->info("Dry-Run: {$missingCreators} fehlende Ersteller-Mitgliedschaften würden ergänzt (als Leader).");
|
|
|
|
return Command::SUCCESS;
|
|
}
|
|
|
|
DB::table('meetup_user')->where('is_leader', false)->update(['is_leader' => true]);
|
|
|
|
$ensured = 0;
|
|
Meetup::query()
|
|
->whereNotNull('created_by')
|
|
->chunkById(200, function ($meetups) use (&$ensured): void {
|
|
foreach ($meetups as $meetup) {
|
|
$meetup->users()->syncWithoutDetaching([
|
|
$meetup->created_by => ['is_leader' => true],
|
|
]);
|
|
$ensured++;
|
|
}
|
|
});
|
|
|
|
$this->info("{$memberRows} Mitglieder zu Leadern befördert.");
|
|
$this->info("Ersteller-Leaderschaft für {$ensured} Meetups sichergestellt.");
|
|
|
|
return Command::SUCCESS;
|
|
}
|
|
}
|