1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2021 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\Carbon; 24use Fisharebest\Webtrees\Contracts\UserInterface; 25use Fisharebest\Webtrees\I18N; 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 EmailService $email_service; 42 43 private UserService $user_service; 44 45 /** 46 * MessageService constructor. 47 * 48 * @param EmailService $email_service 49 * @param UserService $user_service 50 */ 51 public function __construct(EmailService $email_service, UserService $user_service) 52 { 53 $this->email_service = $email_service; 54 $this->user_service = $user_service; 55 } 56 57 /** 58 * Contact messages can only be sent to the designated contacts 59 * 60 * @param Tree $tree 61 * 62 * @return array<UserInterface> 63 */ 64 public function validContacts(Tree $tree): array 65 { 66 $contacts = [ 67 $this->user_service->find((int) $tree->getPreference('CONTACT_USER_ID')), 68 $this->user_service->find((int) $tree->getPreference('WEBMASTER_USER_ID')), 69 ]; 70 71 return array_filter($contacts); 72 } 73 74 /** 75 * Add a message to a user's inbox, send it to them via email, or both. 76 * 77 * @param UserInterface $sender 78 * @param UserInterface $recipient 79 * @param string $subject 80 * @param string $body 81 * @param string $url 82 * @param string $ip 83 * 84 * @return bool 85 */ 86 public function deliverMessage(UserInterface $sender, UserInterface $recipient, string $subject, string $body, string $url, string $ip): bool 87 { 88 $success = true; 89 90 // Temporarily switch to the recipient's language 91 $old_language = I18N::languageTag(); 92 I18N::init($recipient->getPreference(UserInterface::PREF_LANGUAGE)); 93 94 $body_text = view('emails/message-user-text', [ 95 'sender' => $sender, 96 'recipient' => $recipient, 97 'message' => $body, 98 'url' => $url, 99 ]); 100 101 $body_html = view('emails/message-user-html', [ 102 'sender' => $sender, 103 'recipient' => $recipient, 104 'message' => $body, 105 'url' => $url, 106 ]); 107 108 // Send via the internal messaging system. 109 if ($this->sendInternalMessage($recipient)) { 110 DB::table('message')->insert([ 111 'sender' => Auth::check() ? Auth::user()->email() : $sender->email(), 112 'ip_address' => $ip, 113 'user_id' => $recipient->id(), 114 'subject' => $subject, 115 'body' => $body_text, 116 ]); 117 } 118 119 // Send via email 120 if ($this->sendEmail($recipient)) { 121 $success = $this->email_service->send( 122 new SiteUser(), 123 $recipient, 124 $sender, 125 I18N::translate('webtrees message') . ' - ' . $subject, 126 $body_text, 127 $body_html 128 ); 129 } 130 131 I18N::init($old_language); 132 133 return $success; 134 } 135 136 /** 137 * Should we send messages to this user via internal messaging? 138 * 139 * @param UserInterface $user 140 * 141 * @return bool 142 */ 143 public function sendInternalMessage(UserInterface $user): bool 144 { 145 return in_array($user->getPreference(UserInterface::PREF_CONTACT_METHOD), [ 146 'messaging', 147 'messaging2', 148 'mailto', 149 'none', 150 ], true); 151 } 152 153 /** 154 * Should we send messages to this user via email? 155 * 156 * @param UserInterface $user 157 * 158 * @return bool 159 */ 160 public function sendEmail(UserInterface $user): bool 161 { 162 return in_array($user->getPreference(UserInterface::PREF_CONTACT_METHOD), [ 163 'messaging2', 164 'messaging3', 165 'mailto', 166 'none', 167 ], true); 168 } 169 170 /** 171 * Convert a username (or mailing list name) into an array of recipients. 172 * 173 * @param string $to 174 * 175 * @return Collection<int,User> 176 */ 177 public function recipientUsers(string $to): Collection 178 { 179 switch ($to) { 180 default: 181 case 'all': 182 return $this->user_service->all(); 183 case 'never_logged': 184 return $this->user_service->all()->filter(static function (UserInterface $user): bool { 185 return $user->getPreference(UserInterface::PREF_IS_ACCOUNT_APPROVED) === '1' && $user->getPreference(UserInterface::PREF_TIMESTAMP_REGISTERED) > $user->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE); 186 }); 187 case 'last_6mo': 188 $six_months_ago = Carbon::now()->subMonths(6)->unix(); 189 190 return $this->user_service->all()->filter(static function (UserInterface $user) use ($six_months_ago): bool { 191 $session_time = (int) $user->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE); 192 193 return $session_time > 0 && $session_time < $six_months_ago; 194 }); 195 } 196 } 197 198 /** 199 * @param string $to 200 * 201 * @return string 202 */ 203 public function recipientDescription(string $to): string 204 { 205 switch ($to) { 206 default: 207 case 'all': 208 return I18N::translate('Send a message to all users'); 209 case 'never_logged': 210 return I18N::translate('Send a message to users who have never signed in'); 211 case 'last_6mo': 212 return 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