xref: /webtrees/app/Services/MessageService.php (revision e873f434551745f888937263ff89e80db3b0f785)
1e381f98dSGreg Roach<?php
2e381f98dSGreg Roach
3e381f98dSGreg Roach/**
4e381f98dSGreg Roach * webtrees: online genealogy
5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
6e381f98dSGreg Roach * This program is free software: you can redistribute it and/or modify
7e381f98dSGreg Roach * it under the terms of the GNU General Public License as published by
8e381f98dSGreg Roach * the Free Software Foundation, either version 3 of the License, or
9e381f98dSGreg Roach * (at your option) any later version.
10e381f98dSGreg Roach * This program is distributed in the hope that it will be useful,
11e381f98dSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12e381f98dSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13e381f98dSGreg Roach * GNU General Public License for more details.
14e381f98dSGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
16e381f98dSGreg Roach */
17e381f98dSGreg Roach
18e381f98dSGreg Roachdeclare(strict_types=1);
19e381f98dSGreg Roach
20e381f98dSGreg Roachnamespace Fisharebest\Webtrees\Services;
21e381f98dSGreg Roach
22e381f98dSGreg Roachuse Fisharebest\Webtrees\Auth;
23e381f98dSGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface;
246f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB;
25e381f98dSGreg Roachuse Fisharebest\Webtrees\I18N;
26d97083feSGreg Roachuse Fisharebest\Webtrees\Registry;
27e381f98dSGreg Roachuse Fisharebest\Webtrees\SiteUser;
28e381f98dSGreg Roachuse Fisharebest\Webtrees\Tree;
297c4add84SGreg Roachuse Fisharebest\Webtrees\User;
30e381f98dSGreg Roachuse Illuminate\Support\Collection;
31e381f98dSGreg Roach
32e381f98dSGreg Roachuse function array_filter;
33e381f98dSGreg Roachuse function in_array;
34e381f98dSGreg Roachuse function view;
35e381f98dSGreg Roach
36e381f98dSGreg Roach/**
37e381f98dSGreg Roach * Send messages between users and from visitors to the site.
38e381f98dSGreg Roach */
39e381f98dSGreg Roachclass MessageService
40e381f98dSGreg Roach{
418cfb5e7bSGreg Roach    // Users can be contacted by various methods
42*e873f434SGreg Roach    public const string CONTACT_METHOD_INTERNAL           = 'messaging';
43*e873f434SGreg Roach    public const string CONTACT_METHOD_INTERNAL_AND_EMAIL = 'messaging2';
44*e873f434SGreg Roach    public const string CONTACT_METHOD_EMAIL              = 'messaging3';
45*e873f434SGreg Roach    public const string CONTACT_METHOD_MAILTO             = 'mailto';
46*e873f434SGreg Roach    public const string CONTACT_METHOD_NONE               = 'none';
478cfb5e7bSGreg Roach
48*e873f434SGreg Roach    private const string BROADCAST_ALL   = 'all';
49*e873f434SGreg Roach    private const string BROADCAST_NEVER = 'never';
50*e873f434SGreg Roach    private const string BROADCAST_GONE  = 'gone';
51a46dd5a6SGreg Roach
52c4943cffSGreg Roach    private EmailService $email_service;
53e381f98dSGreg Roach
54c4943cffSGreg Roach    private UserService $user_service;
55e381f98dSGreg Roach
56e381f98dSGreg Roach    /**
57e381f98dSGreg Roach     * @param EmailService $email_service
58e381f98dSGreg Roach     * @param UserService  $user_service
59e381f98dSGreg Roach     */
60e381f98dSGreg Roach    public function __construct(EmailService $email_service, UserService $user_service)
61e381f98dSGreg Roach    {
62e381f98dSGreg Roach        $this->email_service = $email_service;
63e381f98dSGreg Roach        $this->user_service  = $user_service;
64e381f98dSGreg Roach    }
65e381f98dSGreg Roach
66e381f98dSGreg Roach    /**
67e381f98dSGreg Roach     * Contact messages can only be sent to the designated contacts
68e381f98dSGreg Roach     *
69e381f98dSGreg Roach     * @param Tree $tree
70e381f98dSGreg Roach     *
7109482a55SGreg Roach     * @return array<UserInterface>
72e381f98dSGreg Roach     */
73e381f98dSGreg Roach    public function validContacts(Tree $tree): array
74e381f98dSGreg Roach    {
75e381f98dSGreg Roach        $contacts = [
76e381f98dSGreg Roach            $this->user_service->find((int) $tree->getPreference('CONTACT_USER_ID')),
77e381f98dSGreg Roach            $this->user_service->find((int) $tree->getPreference('WEBMASTER_USER_ID')),
78e381f98dSGreg Roach        ];
79e381f98dSGreg Roach
80e381f98dSGreg Roach        return array_filter($contacts);
81e381f98dSGreg Roach    }
82e381f98dSGreg Roach
83e381f98dSGreg Roach    /**
84e381f98dSGreg Roach     * Add a message to a user's inbox, send it to them via email, or both.
85e381f98dSGreg Roach     *
86e381f98dSGreg Roach     * @param UserInterface $sender
87e381f98dSGreg Roach     * @param UserInterface $recipient
88e381f98dSGreg Roach     * @param string        $subject
89e381f98dSGreg Roach     * @param string        $body
90e381f98dSGreg Roach     * @param string        $url
91e381f98dSGreg Roach     * @param string        $ip
92e381f98dSGreg Roach     *
93e381f98dSGreg Roach     * @return bool
94e381f98dSGreg Roach     */
95e381f98dSGreg Roach    public function deliverMessage(UserInterface $sender, UserInterface $recipient, string $subject, string $body, string $url, string $ip): bool
96e381f98dSGreg Roach    {
97e381f98dSGreg Roach        $success = true;
98e381f98dSGreg Roach
99e381f98dSGreg Roach        // Temporarily switch to the recipient's language
100e381f98dSGreg Roach        $old_language = I18N::languageTag();
1011fe542e9SGreg Roach        I18N::init($recipient->getPreference(UserInterface::PREF_LANGUAGE));
102e381f98dSGreg Roach
103e381f98dSGreg Roach        $body_text = view('emails/message-user-text', [
104e381f98dSGreg Roach            'sender'    => $sender,
105e381f98dSGreg Roach            'recipient' => $recipient,
106e381f98dSGreg Roach            'message'   => $body,
107e381f98dSGreg Roach            'url'       => $url,
108e381f98dSGreg Roach        ]);
109e381f98dSGreg Roach
110e381f98dSGreg Roach        $body_html = view('emails/message-user-html', [
111e381f98dSGreg Roach            'sender'    => $sender,
112e381f98dSGreg Roach            'recipient' => $recipient,
113e381f98dSGreg Roach            'message'   => $body,
114e381f98dSGreg Roach            'url'       => $url,
115e381f98dSGreg Roach        ]);
116e381f98dSGreg Roach
117e381f98dSGreg Roach        // Send via the internal messaging system.
118e381f98dSGreg Roach        if ($this->sendInternalMessage($recipient)) {
119e381f98dSGreg Roach            DB::table('message')->insert([
120e381f98dSGreg Roach                'sender'     => Auth::check() ? Auth::user()->email() : $sender->email(),
121e381f98dSGreg Roach                'ip_address' => $ip,
122e381f98dSGreg Roach                'user_id'    => $recipient->id(),
123e381f98dSGreg Roach                'subject'    => $subject,
124e381f98dSGreg Roach                'body'       => $body_text,
125e381f98dSGreg Roach            ]);
126e381f98dSGreg Roach        }
127e381f98dSGreg Roach
128e381f98dSGreg Roach        // Send via email
129e381f98dSGreg Roach        if ($this->sendEmail($recipient)) {
130e381f98dSGreg Roach            $success = $this->email_service->send(
131e381f98dSGreg Roach                new SiteUser(),
132e381f98dSGreg Roach                $recipient,
133e381f98dSGreg Roach                $sender,
134e381f98dSGreg Roach                I18N::translate('webtrees message') . ' - ' . $subject,
135e381f98dSGreg Roach                $body_text,
136e381f98dSGreg Roach                $body_html
137e381f98dSGreg Roach            );
138e381f98dSGreg Roach        }
139e381f98dSGreg Roach
140e381f98dSGreg Roach        I18N::init($old_language);
141e381f98dSGreg Roach
142e381f98dSGreg Roach        return $success;
143e381f98dSGreg Roach    }
144e381f98dSGreg Roach
145e381f98dSGreg Roach    /**
146e381f98dSGreg Roach     * Should we send messages to this user via internal messaging?
147e381f98dSGreg Roach     *
148e381f98dSGreg Roach     * @param UserInterface $user
149e381f98dSGreg Roach     *
150e381f98dSGreg Roach     * @return bool
151e381f98dSGreg Roach     */
152e381f98dSGreg Roach    public function sendInternalMessage(UserInterface $user): bool
153e381f98dSGreg Roach    {
1541fe542e9SGreg Roach        return in_array($user->getPreference(UserInterface::PREF_CONTACT_METHOD), [
1558cfb5e7bSGreg Roach            self::CONTACT_METHOD_INTERNAL,
1568cfb5e7bSGreg Roach            self::CONTACT_METHOD_INTERNAL_AND_EMAIL,
1578cfb5e7bSGreg Roach            self::CONTACT_METHOD_MAILTO,
1588cfb5e7bSGreg Roach            self::CONTACT_METHOD_NONE,
159e381f98dSGreg Roach        ], true);
160e381f98dSGreg Roach    }
161e381f98dSGreg Roach
162e381f98dSGreg Roach    /**
163e381f98dSGreg Roach     * Should we send messages to this user via email?
164e381f98dSGreg Roach     *
165e381f98dSGreg Roach     * @param UserInterface $user
166e381f98dSGreg Roach     *
167e381f98dSGreg Roach     * @return bool
168e381f98dSGreg Roach     */
169e381f98dSGreg Roach    public function sendEmail(UserInterface $user): bool
170e381f98dSGreg Roach    {
1711fe542e9SGreg Roach        return in_array($user->getPreference(UserInterface::PREF_CONTACT_METHOD), [
17210e06497SGreg Roach            self::CONTACT_METHOD_INTERNAL_AND_EMAIL,
17310e06497SGreg Roach            self::CONTACT_METHOD_EMAIL,
1748cfb5e7bSGreg Roach            self::CONTACT_METHOD_MAILTO,
1758cfb5e7bSGreg Roach            self::CONTACT_METHOD_NONE,
176e381f98dSGreg Roach        ], true);
177e381f98dSGreg Roach    }
178e381f98dSGreg Roach
179e381f98dSGreg Roach    /**
180e381f98dSGreg Roach     * Convert a username (or mailing list name) into an array of recipients.
181e381f98dSGreg Roach     *
182e381f98dSGreg Roach     * @param string $to
183e381f98dSGreg Roach     *
18436779af1SGreg Roach     * @return Collection<int,User>
185e381f98dSGreg Roach     */
186e381f98dSGreg Roach    public function recipientUsers(string $to): Collection
187e381f98dSGreg Roach    {
188e381f98dSGreg Roach        switch ($to) {
189e381f98dSGreg Roach            default:
190a46dd5a6SGreg Roach            case self::BROADCAST_ALL:
191e381f98dSGreg Roach                return $this->user_service->all();
192a46dd5a6SGreg Roach            case self::BROADCAST_NEVER:
193f25fc0f9SGreg Roach                return $this->user_service->all()
194f25fc0f9SGreg Roach                    ->filter(static fn (UserInterface $user): bool => $user->getPreference(UserInterface::PREF_IS_ACCOUNT_APPROVED) === '1' && $user->getPreference(UserInterface::PREF_TIMESTAMP_REGISTERED) > $user->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE));
195a46dd5a6SGreg Roach            case self::BROADCAST_GONE:
196d97083feSGreg Roach                $six_months_ago = Registry::timestampFactory()->now()->subtractMonths(6)->timestamp();
197e381f98dSGreg Roach
198e381f98dSGreg Roach                return $this->user_service->all()->filter(static function (UserInterface $user) use ($six_months_ago): bool {
1991fe542e9SGreg Roach                    $session_time = (int) $user->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE);
200e381f98dSGreg Roach
201e381f98dSGreg Roach                    return $session_time > 0 && $session_time < $six_months_ago;
202e381f98dSGreg Roach                });
203e381f98dSGreg Roach        }
204e381f98dSGreg Roach    }
205e381f98dSGreg Roach
206e381f98dSGreg Roach    /**
207a46dd5a6SGreg Roach     * Recipients for broadcast messages
208e381f98dSGreg Roach     *
209a46dd5a6SGreg Roach     * @return array<string,string>
210e381f98dSGreg Roach     */
211a46dd5a6SGreg Roach    public function recipientTypes(): array
212e381f98dSGreg Roach    {
213a46dd5a6SGreg Roach        return [
214a46dd5a6SGreg Roach            self::BROADCAST_ALL   => I18N::translate('Send a message to all users'),
215a46dd5a6SGreg Roach            self::BROADCAST_NEVER => I18N::translate('Send a message to users who have never signed in'),
216a46dd5a6SGreg Roach            self::BROADCAST_GONE  => I18N::translate('Send a message to users who have not signed in for 6 months'),
217a46dd5a6SGreg Roach        ];
218e381f98dSGreg Roach    }
219f91b18ebSGreg Roach
220f91b18ebSGreg Roach    /**
221f91b18ebSGreg Roach     * A list of contact methods (e.g. for an edit control).
222f91b18ebSGreg Roach     *
22324f2a3afSGreg Roach     * @return array<string>
224f91b18ebSGreg Roach     */
225f91b18ebSGreg Roach    public function contactMethods(): array
226f91b18ebSGreg Roach    {
227f91b18ebSGreg Roach        return [
2288cfb5e7bSGreg Roach            self::CONTACT_METHOD_INTERNAL           => I18N::translate('Internal messaging'),
2298cfb5e7bSGreg Roach            self::CONTACT_METHOD_INTERNAL_AND_EMAIL => I18N::translate('Internal messaging with emails'),
2308cfb5e7bSGreg Roach            self::CONTACT_METHOD_EMAIL              => I18N::translate('webtrees sends emails with no storage'),
2318cfb5e7bSGreg Roach            self::CONTACT_METHOD_MAILTO             => I18N::translate('Mailto link'),
2328cfb5e7bSGreg Roach            self::CONTACT_METHOD_NONE               => I18N::translate('No contact'),
233f91b18ebSGreg Roach        ];
234f91b18ebSGreg Roach    }
235e381f98dSGreg Roach}
236