Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
81.00% covered (warning)
81.00%
81 / 100
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SendRoleReminderCommand
81.00% covered (warning)
81.00%
81 / 100
0.00% covered (danger)
0.00%
0 / 4
25.32
0.00% covered (danger)
0.00%
0 / 1
 getNotificationSubscriptionType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 autogenerateSubscriptions
83.33% covered (warning)
83.33%
25 / 30
0.00% covered (danger)
0.00%
0 / 1
10.46
 getRoleReminderState
62.07% covered (warning)
62.07%
18 / 29
0.00% covered (danger)
0.00%
0 / 1
6.36
 getNotification
95.00% covered (success)
95.00%
38 / 40
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace Olz\Command\Notifications;
4
5use Olz\Entity\NotificationSubscription;
6use Olz\Entity\Roles\Role;
7use Olz\Entity\Users\User;
8use Olz\Repository\Roles\PredefinedRole;
9use Olz\Utils\WithUtilsTrait;
10use Symfony\Component\Console\Attribute\AsCommand;
11
12#[AsCommand(name: 'olz:send-role-reminder')]
13class SendRoleReminderCommand extends BaseSendNotificationsCommand {
14    use WithUtilsTrait;
15
16    public const EXECUTION_DATE = '****-01-02';
17
18    public function getNotificationSubscriptionType(): string {
19        return NotificationSubscription::TYPE_ROLE_REMINDER;
20    }
21
22    public function autogenerateSubscriptions(): void {
23        $this->log()->info("Generating role reminder subscriptions...");
24        $role_reminder_state = $this->getRoleReminderState();
25
26        $now_datetime = new \DateTime($this->dateUtils()->getIsoNow());
27        $notification_subscription_repo = $this->entityManager()->getRepository(NotificationSubscription::class);
28        $user_repo = $this->entityManager()->getRepository(User::class);
29        foreach ($role_reminder_state as $ident => $state) {
30            [$user_id, $role_id] = array_map(fn ($part): int => intval($part), explode('-', $ident));
31            $reminder_id = $state['reminder_id'] ?? false;
32            $needs_reminder = $state['needs_reminder'] ?? false;
33            $user = $user_repo->findOneBy(['id' => $user_id]);
34            if (!$user) {
35                $this->log()->warning("No user (ID:{$user_id}) for telegram notification");
36            }
37            if ($needs_reminder && !$reminder_id && $user) {
38                $this->log()->info("Generating role ({$role_id}) reminder subscription for '{$user}'...");
39                $subscription = new NotificationSubscription();
40                $subscription->setUser($user);
41                $subscription->setDeliveryType(NotificationSubscription::DELIVERY_EMAIL);
42                $subscription->setNotificationType(NotificationSubscription::TYPE_ROLE_REMINDER);
43                $subscription->setNotificationTypeArgs(json_encode([
44                    'role_id' => $role_id,
45                    'cancelled' => false,
46                ]) ?: '{}');
47                $subscription->setCreatedAt($now_datetime);
48                $this->entityManager()->persist($subscription);
49            }
50            if ($reminder_id && !$needs_reminder) {
51                $this->log()->info("Removing role ({$role_id}) reminder subscription ({$reminder_id}) for '{$user}'...");
52                $subscription = $notification_subscription_repo->findOneBy(['id' => $reminder_id]);
53                if ($subscription) {
54                    $this->entityManager()->remove($subscription);
55                }
56            }
57        }
58        $this->entityManager()->flush();
59    }
60
61    /** @return array<string, array{reminder_id?: int, needs_reminder?: bool}> */
62    protected function getRoleReminderState(): array {
63        $role_reminder_state = [];
64
65        // Find role assignees with existing role reminder notification subscriptions.
66        $notification_subscription_repo = $this->entityManager()->getRepository(NotificationSubscription::class);
67        $telegram_notification_subscriptions = $notification_subscription_repo->findBy([
68            'notification_type' => NotificationSubscription::TYPE_ROLE_REMINDER,
69        ]);
70        foreach ($telegram_notification_subscriptions as $subscription) {
71            $user_id = $subscription->getUser()->getId();
72            $args = json_decode($subscription->getNotificationTypeArgs() ?? '{}', true);
73            $role_id = $args['role_id'] ?? null;
74            if ($role_id === null) {
75                $this->log()->warning("Role reminder notification subscription ({$subscription->getId()}) without role ID");
76            }
77            $ident = "{$user_id}-{$role_id}";
78            $state = $role_reminder_state[$ident] ?? [];
79            $subscription_id = $subscription->getId();
80            $this->generalUtils()->checkNotNull($subscription_id, "No subscription ID");
81            $state['reminder_id'] = $subscription_id;
82            $role_reminder_state[$ident] = $state;
83        }
84
85        // Find role assignees who should have role reminder notification subscriptions.
86        $role_repo = $this->entityManager()->getRepository(Role::class);
87        $roles = $role_repo->findBy(['on_off' => 1]);
88        foreach ($roles as $role) {
89            $role_id = $role->getId();
90            $assignees = $role->getUsers();
91            foreach ($assignees as $assignee) {
92                $user_id = $assignee->getId();
93                $ident = "{$user_id}-{$role_id}";
94                $user_state = $role_reminder_state[$ident] ?? [];
95                $user_state['needs_reminder'] = true;
96                $role_reminder_state[$ident] = $user_state;
97            }
98        }
99
100        return $role_reminder_state;
101    }
102
103    // ---
104
105    /** @param array<string, mixed> $args */
106    public function getNotification(array $args): ?Notification {
107        $today = $this->dateUtils()->getIsoToday();
108        if (substr($today, 4, 6) != substr($this::EXECUTION_DATE, 4, 6)) {
109            return null;
110        }
111
112        $role_repo = $this->entityManager()->getRepository(Role::class);
113        $role = $role_repo->findOneBy(['id' => $args['role_id']]);
114        if (!$role) {
115            return null;
116        }
117        $role_name = "{$role->getName()}";
118        $num_assignees = $role->getUsers()->count();
119        $num_others = $num_assignees - 1;
120        $num_assignees_note = $num_assignees > 1 ? " (zusammen mit {$num_others} Anderen)" : '';
121
122        $parent_role = $role;
123        $parent_role_id = $parent_role->getParentRoleId();
124        while ($parent_role_id) {
125            $new_parent_role = $role_repo->findOneBy(['id' => $parent_role_id]);
126            $parent_role_id = $new_parent_role?->getParentRoleId();
127            if ($new_parent_role) {
128                $parent_role = $new_parent_role;
129            }
130        }
131        $root_role = $parent_role;
132        $pretty_root_assignees = implode(' / ', array_map(function (User $user): string {
133            return "{$user->getFullName()}{$user->getEmail()}";
134        }, [...$root_role->getUsers()]));
135
136        $base_href = $this->envUtils()->getBaseHref();
137        $code_href = $this->envUtils()->getCodeHref();
138        $role_url = "{$base_href}{$code_href}verein/{$role->getUsername()}";
139        $sysadmin_role = $role_repo->getPredefinedRole(PredefinedRole::Sysadmin);
140        $host = $this->envUtils()->getEmailForwardingHost();
141        $sysadmin_email = "{$sysadmin_role?->getUsername()}@{$host}";
142
143        $title = "Ressort-Erinnerung";
144        $text = <<<ZZZZZZZZZZ
145            Hallo %%userFirstName%%,
146
147            Du bist im [OLZ-Organigramm]({$base_href}{$code_href}verein){$num_assignees_note} unter dem Ressort [**{$role_name}**]({$role_url}) eingetragen, bzw. für dieses Ressort zuständig.
148            
149            **Vielen Dank, dass du mithilfst, unseren Verein am Laufen zu halten!**
150
151            Um das Organigramm aktuell zu halten, bitten wir dich, die folgenden Punkte durchzugehen.
152            
153            **Falls etwas unklar ist, kontaktiere bitte den Website-Admin: {$sysadmin_email}!**
154
155            - Bitte schau dir die [Präsenz deines Ressorts auf olzimmerberg.ch]({$role_url}) an, und **kontrolliere, ergänze und verbessere** gegebenenfalls die Angaben. Wenn du eingeloggt bist, kannst du diese direkt bearbeiten.
156            - **Falls** du im kommenden Jahr nicht mehr für dieses Ressort zuständig sein kannst oder möchtest, bzw. nicht mehr unter diesem Ressort angezeigt werden solltest, kontaktiere bitte "deinen" Vorstand: {$pretty_root_assignees} (oder den Präsi).
157            - **Falls** du noch kein OLZ-Konto hast, erstelle doch eines ([zum Login-Dialog]({$base_href}{$code_href}#login-dialog), dann "Noch kein OLZ-Konto?" wählen). Verwende den Benutzernamen "%%userUsername%%", um automatisch Schreib-Zugriff für dein Ressort zu erhalten.
158
159            Besten Dank für deine Mithilfe,
160
161            Der Vorstand der OL Zimmerberg
162            ZZZZZZZZZZ;
163
164        return new Notification($title, $text, [
165            'notification_type' => NotificationSubscription::TYPE_ROLE_REMINDER,
166        ]);
167    }
168}