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