1e381f98dSGreg Roach<?php 2e381f98dSGreg Roach 3e381f98dSGreg Roach/** 4e381f98dSGreg Roach * webtrees: online genealogy 55bfc6897SGreg Roach * Copyright (C) 2022 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), [ 174*10e06497SGreg Roach self::CONTACT_METHOD_INTERNAL_AND_EMAIL, 175*10e06497SGreg 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