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