Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.14% covered (success)
93.14%
95 / 102
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SendRoleReminderCommand
93.14% covered (success)
93.14%
95 / 102
25.00% covered (danger)
25.00%
1 / 4
22.16
0.00% covered (danger)
0.00%
0 / 1
 getNotificationSubscriptionType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 autogenerateSubscriptions
96.67% covered (success)
96.67%
29 / 30
0.00% covered (danger)
0.00%
0 / 1
10
 getRoleReminderState
96.55% covered (success)
96.55%
28 / 29
0.00% covered (danger)
0.00%
0 / 1
5
 getNotifications
88.10% covered (warning)
88.10%
37 / 42
0.00% covered (danger)
0.00%
0 / 1
6.06
1<?php
2
3namespace Olz\Command\Notifications;
4
5use Olz\Command\Notifications\Common\BaseSendNotificationsCommand;
6use Olz\Command\Notifications\Common\Notification;
7use Olz\Constants\NotificationDeliveryType;
8use Olz\Constants\NotificationType;
9use Olz\Entity\NotificationSubscription;
10use Olz\Entity\Roles\Role;
11use Olz\Entity\Users\User;
12use Olz\Repository\Roles\PredefinedRole;
13use Olz\Utils\WithUtilsTrait;
14use Symfony\Component\Console\Attribute\AsCommand;
15
16#[AsCommand(name: 'olz:send-role-reminder')]
17class SendRoleReminderCommand extends BaseSendNotificationsCommand {
18    use WithUtilsTrait;
19
20    public const EXECUTION_DATE = '****-01-02';
21
22    public function getNotificationSubscriptionType(): NotificationType {
23        return NotificationType::ROLE_REMINDER;
24    }
25
26    public function autogenerateSubscriptions(): void {
27        $this->log()->info("Generating role reminder subscriptions...");
28        $role_reminder_state = $this->getRoleReminderState();
29
30        $now_datetime = new \DateTime($this->dateUtils()->getIsoNow());
31        $notification_subscription_repo = $this->entityManager()->getRepository(NotificationSubscription::class);
32        $user_repo = $this->entityManager()->getRepository(User::class);
33        foreach ($role_reminder_state as $ident => $state) {
34            [$user_id, $role_id] = array_map(fn ($part): int => intval($part), explode('-', $ident));
35            $reminder_id = $state['reminder_id'] ?? false;
36            $needs_reminder = $state['needs_reminder'] ?? false;
37            $user = $user_repo->findOneBy(['id' => $user_id]);
38            if (!$user) {
39                $this->log()->warning("No user (ID:{$user_id}) for telegram notification");
40            }
41            if ($needs_reminder && !$reminder_id && $user) {
42                $this->log()->info("Generating role ({$role_id}) reminder subscription for '{$user}'...");
43                $subscription = new NotificationSubscription();
44                $subscription->setUser($user);
45                $subscription->setDeliveryType(NotificationDeliveryType::EMAIL);
46                $subscription->setNotificationType(NotificationType::ROLE_REMINDER);
47                $subscription->setNotificationTypeArgs(json_encode([
48                    'role_id' => $role_id,
49                    'cancelled' => false,
50                ]) ?: '{}');
51                $subscription->setCreatedAt($now_datetime);
52                $this->entityManager()->persist($subscription);
53            }
54            if ($reminder_id && !$needs_reminder) {
55                $this->log()->info("Removing role ({$role_id}) reminder subscription ({$reminder_id}) for '{$user}'...");
56                $subscription = $notification_subscription_repo->findOneBy(['id' => $reminder_id]);
57                if ($subscription) {
58                    $this->entityManager()->remove($subscription);
59                }
60            }
61        }
62        $this->entityManager()->flush();
63    }
64
65    /** @return array<string, array{reminder_id?: int, needs_reminder?: bool}> */
66    protected function getRoleReminderState(): array {
67        $role_reminder_state = [];
68
69        // Find role assignees with existing role reminder notification subscriptions.
70        $notification_subscription_repo = $this->entityManager()->getRepository(NotificationSubscription::class);
71        $telegram_notification_subscriptions = $notification_subscription_repo->findBy([
72            'notification_type' => NotificationType::ROLE_REMINDER,
73        ]);
74        foreach ($telegram_notification_subscriptions as $subscription) {
75            $user_id = $subscription->getUser()->getId();
76            $args = json_decode($subscription->getNotificationTypeArgs() ?? '{}', true);
77            $role_id = $args['role_id'] ?? null;
78            if ($role_id === null) {
79                $this->log()->warning("Role reminder notification subscription ({$subscription->getId()}) without role ID");
80            }
81            $ident = "{$user_id}-{$role_id}";
82            $state = $role_reminder_state[$ident] ?? [];
83            $subscription_id = $subscription->getId();
84            $this->generalUtils()->checkNotNull($subscription_id, "No subscription ID");
85            $state['reminder_id'] = $subscription_id;
86            $role_reminder_state[$ident] = $state;
87        }
88
89        // Find role assignees who should have role reminder notification subscriptions.
90        $role_repo = $this->entityManager()->getRepository(Role::class);
91        $roles = $role_repo->findBy(['on_off' => 1]);
92        foreach ($roles as $role) {
93            $role_id = $role->getId();
94            $assignees = $role->getUsers();
95            foreach ($assignees as $assignee) {
96                $user_id = $assignee->getId();
97                $ident = "{$user_id}-{$role_id}";
98                $user_state = $role_reminder_state[$ident] ?? [];
99                $user_state['needs_reminder'] = true;
100                $role_reminder_state[$ident] = $user_state;
101            }
102        }
103
104        return $role_reminder_state;
105    }
106
107    // ---
108
109    public function getNotifications(array $args): array {
110        $today = $this->dateUtils()->getIsoToday();
111        if (substr($today, 4, 6) != substr($this::EXECUTION_DATE, 4, 6)) {
112            return [];
113        }
114
115        $role_repo = $this->entityManager()->getRepository(Role::class);
116        $role = $role_repo->findOneBy(['id' => $args['role_id']]);
117        if (!$role) {
118            return [];
119        }
120        $role_name = "{$role->getName()}";
121        $num_assignees = $role->getUsers()->count();
122        $num_others = $num_assignees - 1;
123        $num_assignees_note = $num_assignees > 1 ? " (zusammen mit {$num_others} Anderen)" : '';
124
125        $parent_role = $role;
126        $parent_role_id = $parent_role->getParentRoleId();
127        while ($parent_role_id) {
128            $new_parent_role = $role_repo->findOneBy(['id' => $parent_role_id]);
129            $parent_role_id = $new_parent_role?->getParentRoleId();
130            if ($new_parent_role) {
131                $parent_role = $new_parent_role;
132            }
133        }
134        $root_role = $parent_role;
135        $pretty_root_assignees = implode(' / ', array_map(function (User $user): string {
136            return "{$user->getFullName()}, {$user->getEmail()}";
137        }, [...$root_role->getUsers()]));
138
139        $base_href = $this->envUtils()->getBaseHref();
140        $code_href = $this->envUtils()->getCodeHref();
141        $role_url = "{$base_href}{$code_href}verein/{$role->getUsername()}";
142        $sysadmin_role = $role_repo->getPredefinedRole(PredefinedRole::Sysadmin);
143        $host = $this->envUtils()->getEmailForwardingHost();
144        $sysadmin_email = "{$sysadmin_role?->getUsername()}@{$host}";
145
146        $title = "Ressort-Erinnerung";
147        $text = <<<ZZZZZZZZZZ
148            Hallo %%userFirstName%%,
149
150            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.
151            
152            **Vielen Dank, dass du mithilfst, unseren Verein am Laufen zu halten!**
153
154            Um das Organigramm aktuell zu halten, bitten wir dich, die folgenden Punkte durchzugehen.
155            
156            **Falls etwas unklar ist, kontaktiere bitte den Website-Admin: {$sysadmin_email}!**
157
158            - 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.
159            - **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).
160            - **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.
161
162            Besten Dank für deine Mithilfe,
163
164            Der Vorstand der OL Zimmerberg
165            ZZZZZZZZZZ;
166
167        return [
168            new Notification($title, $text, [
169                'notification_type' => NotificationType::ROLE_REMINDER,
170            ]),
171        ];
172    }
173}