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