xref: /webtrees/app/Services/MessageService.php (revision d11be7027e34e3121be11cc025421873364403f9)
1e381f98dSGreg Roach<?php
2e381f98dSGreg Roach
3e381f98dSGreg Roach/**
4e381f98dSGreg Roach * webtrees: online genealogy
5*d11be702SGreg 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;
24e381f98dSGreg Roachuse Fisharebest\Webtrees\I18N;
25d97083feSGreg Roachuse Fisharebest\Webtrees\Registry;
26e381f98dSGreg Roachuse Fisharebest\Webtrees\SiteUser;
27e381f98dSGreg Roachuse Fisharebest\Webtrees\Tree;
287c4add84SGreg Roachuse Fisharebest\Webtrees\User;
29e381f98dSGreg Roachuse Illuminate\Database\Capsule\Manager as DB;
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
428cfb5e7bSGreg Roach    public const CONTACT_METHOD_INTERNAL           = 'messaging';
438cfb5e7bSGreg Roach    public const CONTACT_METHOD_INTERNAL_AND_EMAIL = 'messaging2';
448cfb5e7bSGreg Roach    public const CONTACT_METHOD_EMAIL              = 'messaging3';
458cfb5e7bSGreg Roach    public const CONTACT_METHOD_MAILTO             = 'mailto';
468cfb5e7bSGreg Roach    public const CONTACT_METHOD_NONE               = 'none';
478cfb5e7bSGreg Roach
48a46dd5a6SGreg Roach    private const BROADCAST_ALL   = 'all';
49a46dd5a6SGreg Roach    private const BROADCAST_NEVER = 'never';
50a46dd5a6SGreg Roach    private const 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     * MessageService constructor.
58e381f98dSGreg Roach     *
59e381f98dSGreg Roach     * @param EmailService $email_service
60e381f98dSGreg Roach     * @param UserService  $user_service
61e381f98dSGreg Roach     */
62e381f98dSGreg Roach    public function __construct(EmailService $email_service, UserService $user_service)
63e381f98dSGreg Roach    {
64e381f98dSGreg Roach        $this->email_service = $email_service;
65e381f98dSGreg Roach        $this->user_service  = $user_service;
66e381f98dSGreg Roach    }
67e381f98dSGreg Roach
68e381f98dSGreg Roach    /**
69e381f98dSGreg Roach     * Contact messages can only be sent to the designated contacts
70e381f98dSGreg Roach     *
71e381f98dSGreg Roach     * @param Tree $tree
72e381f98dSGreg Roach     *
7309482a55SGreg Roach     * @return array<UserInterface>
74e381f98dSGreg Roach     */
75e381f98dSGreg Roach    public function validContacts(Tree $tree): array
76e381f98dSGreg Roach    {
77e381f98dSGreg Roach        $contacts = [
78e381f98dSGreg Roach            $this->user_service->find((int) $tree->getPreference('CONTACT_USER_ID')),
79e381f98dSGreg Roach            $this->user_service->find((int) $tree->getPreference('WEBMASTER_USER_ID')),
80e381f98dSGreg Roach        ];
81e381f98dSGreg Roach
82e381f98dSGreg Roach        return array_filter($contacts);
83e381f98dSGreg Roach    }
84e381f98dSGreg Roach
85e381f98dSGreg Roach    /**
86e381f98dSGreg Roach     * Add a message to a user's inbox, send it to them via email, or both.
87e381f98dSGreg Roach     *
88e381f98dSGreg Roach     * @param UserInterface $sender
89e381f98dSGreg Roach     * @param UserInterface $recipient
90e381f98dSGreg Roach     * @param string        $subject
91e381f98dSGreg Roach     * @param string        $body
92e381f98dSGreg Roach     * @param string        $url
93e381f98dSGreg Roach     * @param string        $ip
94e381f98dSGreg Roach     *
95e381f98dSGreg Roach     * @return bool
96e381f98dSGreg Roach     */
97e381f98dSGreg Roach    public function deliverMessage(UserInterface $sender, UserInterface $recipient, string $subject, string $body, string $url, string $ip): bool
98e381f98dSGreg Roach    {
99e381f98dSGreg Roach        $success = true;
100e381f98dSGreg Roach
101e381f98dSGreg Roach        // Temporarily switch to the recipient's language
102e381f98dSGreg Roach        $old_language = I18N::languageTag();
1031fe542e9SGreg Roach        I18N::init($recipient->getPreference(UserInterface::PREF_LANGUAGE));
104e381f98dSGreg Roach
105e381f98dSGreg Roach        $body_text = view('emails/message-user-text', [
106e381f98dSGreg Roach            'sender'    => $sender,
107e381f98dSGreg Roach            'recipient' => $recipient,
108e381f98dSGreg Roach            'message'   => $body,
109e381f98dSGreg Roach            'url'       => $url,
110e381f98dSGreg Roach        ]);
111e381f98dSGreg Roach
112e381f98dSGreg Roach        $body_html = view('emails/message-user-html', [
113e381f98dSGreg Roach            'sender'    => $sender,
114e381f98dSGreg Roach            'recipient' => $recipient,
115e381f98dSGreg Roach            'message'   => $body,
116e381f98dSGreg Roach            'url'       => $url,
117e381f98dSGreg Roach        ]);
118e381f98dSGreg Roach
119e381f98dSGreg Roach        // Send via the internal messaging system.
120e381f98dSGreg Roach        if ($this->sendInternalMessage($recipient)) {
121e381f98dSGreg Roach            DB::table('message')->insert([
122e381f98dSGreg Roach                'sender'     => Auth::check() ? Auth::user()->email() : $sender->email(),
123e381f98dSGreg Roach                'ip_address' => $ip,
124e381f98dSGreg Roach                'user_id'    => $recipient->id(),
125e381f98dSGreg Roach                'subject'    => $subject,
126e381f98dSGreg Roach                'body'       => $body_text,
127e381f98dSGreg Roach            ]);
128e381f98dSGreg Roach        }
129e381f98dSGreg Roach
130e381f98dSGreg Roach        // Send via email
131e381f98dSGreg Roach        if ($this->sendEmail($recipient)) {
132e381f98dSGreg Roach            $success = $this->email_service->send(
133e381f98dSGreg Roach                new SiteUser(),
134e381f98dSGreg Roach                $recipient,
135e381f98dSGreg Roach                $sender,
136e381f98dSGreg Roach                I18N::translate('webtrees message') . ' - ' . $subject,
137e381f98dSGreg Roach                $body_text,
138e381f98dSGreg Roach                $body_html
139e381f98dSGreg Roach            );
140e381f98dSGreg Roach        }
141e381f98dSGreg Roach
142e381f98dSGreg Roach        I18N::init($old_language);
143e381f98dSGreg Roach
144e381f98dSGreg Roach        return $success;
145e381f98dSGreg Roach    }
146e381f98dSGreg Roach
147e381f98dSGreg Roach    /**
148e381f98dSGreg Roach     * Should we send messages to this user via internal messaging?
149e381f98dSGreg Roach     *
150e381f98dSGreg Roach     * @param UserInterface $user
151e381f98dSGreg Roach     *
152e381f98dSGreg Roach     * @return bool
153e381f98dSGreg Roach     */
154e381f98dSGreg Roach    public function sendInternalMessage(UserInterface $user): bool
155e381f98dSGreg Roach    {
1561fe542e9SGreg Roach        return in_array($user->getPreference(UserInterface::PREF_CONTACT_METHOD), [
1578cfb5e7bSGreg Roach            self::CONTACT_METHOD_INTERNAL,
1588cfb5e7bSGreg Roach            self::CONTACT_METHOD_INTERNAL_AND_EMAIL,
1598cfb5e7bSGreg Roach            self::CONTACT_METHOD_MAILTO,
1608cfb5e7bSGreg Roach            self::CONTACT_METHOD_NONE,
161e381f98dSGreg Roach        ], true);
162e381f98dSGreg Roach    }
163e381f98dSGreg Roach
164e381f98dSGreg Roach    /**
165e381f98dSGreg Roach     * Should we send messages to this user via email?
166e381f98dSGreg Roach     *
167e381f98dSGreg Roach     * @param UserInterface $user
168e381f98dSGreg Roach     *
169e381f98dSGreg Roach     * @return bool
170e381f98dSGreg Roach     */
171e381f98dSGreg Roach    public function sendEmail(UserInterface $user): bool
172e381f98dSGreg Roach    {
1731fe542e9SGreg Roach        return in_array($user->getPreference(UserInterface::PREF_CONTACT_METHOD), [
17410e06497SGreg Roach            self::CONTACT_METHOD_INTERNAL_AND_EMAIL,
17510e06497SGreg Roach            self::CONTACT_METHOD_EMAIL,
1768cfb5e7bSGreg Roach            self::CONTACT_METHOD_MAILTO,
1778cfb5e7bSGreg Roach            self::CONTACT_METHOD_NONE,
178e381f98dSGreg Roach        ], true);
179e381f98dSGreg Roach    }
180e381f98dSGreg Roach
181e381f98dSGreg Roach    /**
182e381f98dSGreg Roach     * Convert a username (or mailing list name) into an array of recipients.
183e381f98dSGreg Roach     *
184e381f98dSGreg Roach     * @param string $to
185e381f98dSGreg Roach     *
18636779af1SGreg Roach     * @return Collection<int,User>
187e381f98dSGreg Roach     */
188e381f98dSGreg Roach    public function recipientUsers(string $to): Collection
189e381f98dSGreg Roach    {
190e381f98dSGreg Roach        switch ($to) {
191e381f98dSGreg Roach            default:
192a46dd5a6SGreg Roach            case self::BROADCAST_ALL:
193e381f98dSGreg Roach                return $this->user_service->all();
194a46dd5a6SGreg Roach            case self::BROADCAST_NEVER:
195e381f98dSGreg Roach                return $this->user_service->all()->filter(static function (UserInterface $user): bool {
1961fe542e9SGreg Roach                    return $user->getPreference(UserInterface::PREF_IS_ACCOUNT_APPROVED) === '1' && $user->getPreference(UserInterface::PREF_TIMESTAMP_REGISTERED) > $user->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE);
197e381f98dSGreg Roach                });
198a46dd5a6SGreg Roach            case self::BROADCAST_GONE:
199d97083feSGreg Roach                $six_months_ago = Registry::timestampFactory()->now()->subtractMonths(6)->timestamp();
200e381f98dSGreg Roach
201e381f98dSGreg Roach                return $this->user_service->all()->filter(static function (UserInterface $user) use ($six_months_ago): bool {
2021fe542e9SGreg Roach                    $session_time = (int) $user->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE);
203e381f98dSGreg Roach
204e381f98dSGreg Roach                    return $session_time > 0 && $session_time < $six_months_ago;
205e381f98dSGreg Roach                });
206e381f98dSGreg Roach        }
207e381f98dSGreg Roach    }
208e381f98dSGreg Roach
209e381f98dSGreg Roach    /**
210a46dd5a6SGreg Roach     * Recipients for broadcast messages
211e381f98dSGreg Roach     *
212a46dd5a6SGreg Roach     * @return array<string,string>
213e381f98dSGreg Roach     */
214a46dd5a6SGreg Roach    public function recipientTypes(): array
215e381f98dSGreg Roach    {
216a46dd5a6SGreg Roach        return [
217a46dd5a6SGreg Roach            self::BROADCAST_ALL   => I18N::translate('Send a message to all users'),
218a46dd5a6SGreg Roach            self::BROADCAST_NEVER => I18N::translate('Send a message to users who have never signed in'),
219a46dd5a6SGreg Roach            self::BROADCAST_GONE  => I18N::translate('Send a message to users who have not signed in for 6 months'),
220a46dd5a6SGreg Roach        ];
221e381f98dSGreg Roach    }
222f91b18ebSGreg Roach
223f91b18ebSGreg Roach    /**
224f91b18ebSGreg Roach     * A list of contact methods (e.g. for an edit control).
225f91b18ebSGreg Roach     *
22624f2a3afSGreg Roach     * @return array<string>
227f91b18ebSGreg Roach     */
228f91b18ebSGreg Roach    public function contactMethods(): array
229f91b18ebSGreg Roach    {
230f91b18ebSGreg Roach        return [
2318cfb5e7bSGreg Roach            self::CONTACT_METHOD_INTERNAL           => I18N::translate('Internal messaging'),
2328cfb5e7bSGreg Roach            self::CONTACT_METHOD_INTERNAL_AND_EMAIL => I18N::translate('Internal messaging with emails'),
2338cfb5e7bSGreg Roach            self::CONTACT_METHOD_EMAIL              => I18N::translate('webtrees sends emails with no storage'),
2348cfb5e7bSGreg Roach            self::CONTACT_METHOD_MAILTO             => I18N::translate('Mailto link'),
2358cfb5e7bSGreg Roach            self::CONTACT_METHOD_NONE               => I18N::translate('No contact'),
236f91b18ebSGreg Roach        ];
237f91b18ebSGreg Roach    }
238e381f98dSGreg Roach}
239