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\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