18add1155SRico Sonntag<?php 23976b470SGreg Roach 38add1155SRico Sonntag/** 48add1155SRico Sonntag * webtrees: online genealogy 5*d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 68add1155SRico Sonntag * This program is free software: you can redistribute it and/or modify 78add1155SRico Sonntag * it under the terms of the GNU General Public License as published by 88add1155SRico Sonntag * the Free Software Foundation, either version 3 of the License, or 98add1155SRico Sonntag * (at your option) any later version. 108add1155SRico Sonntag * This program is distributed in the hope that it will be useful, 118add1155SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of 128add1155SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138add1155SRico Sonntag * GNU General Public License for more details. 148add1155SRico Sonntag * 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/>. 168add1155SRico Sonntag */ 17fcfa147eSGreg Roach 188add1155SRico Sonntagdeclare(strict_types=1); 198add1155SRico Sonntag 208add1155SRico Sonntagnamespace Fisharebest\Webtrees\Statistics\Repository; 218add1155SRico Sonntag 228add1155SRico Sonntaguse Fisharebest\Webtrees\Auth; 23e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 24e381f98dSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\MessagePage; 258add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 26c0112ce8SGreg Roachuse Fisharebest\Webtrees\Individual; 27f0c88a96SGreg Roachuse Fisharebest\Webtrees\Registry; 288cfb5e7bSGreg Roachuse Fisharebest\Webtrees\Services\MessageService; 29e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Services\UserService; 308add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\Interfaces\UserRepositoryInterface; 318add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 328add1155SRico Sonntag 3371378461SGreg Roachuse function count; 344c78e066SGreg Roachuse function e; 354c78e066SGreg Roachuse function route; 364c78e066SGreg Roachuse function view; 3771378461SGreg Roach 388add1155SRico Sonntag/** 398add1155SRico Sonntag * A repository providing methods for user related statistics. 408add1155SRico Sonntag */ 418add1155SRico Sonntagclass UserRepository implements UserRepositoryInterface 428add1155SRico Sonntag{ 434c78e066SGreg Roach private Tree $tree; 444c78e066SGreg Roach 454c78e066SGreg Roach private UserService $user_service; 468add1155SRico Sonntag 478add1155SRico Sonntag /** 488add1155SRico Sonntag * @param Tree $tree 49e5a6b4d4SGreg Roach * @param UserService $user_service 508add1155SRico Sonntag */ 51e5a6b4d4SGreg Roach public function __construct(Tree $tree, UserService $user_service) 528add1155SRico Sonntag { 538add1155SRico Sonntag $this->tree = $tree; 54e5a6b4d4SGreg Roach $this->user_service = $user_service; 558add1155SRico Sonntag } 568add1155SRico Sonntag 578add1155SRico Sonntag /** 588add1155SRico Sonntag * Who is currently logged in? 598add1155SRico Sonntag * 60c0112ce8SGreg Roach * @param string $type "list" or "nolist" 618add1155SRico Sonntag * 628add1155SRico Sonntag * @return string 638add1155SRico Sonntag */ 64f78da678SGreg Roach private function usersLoggedInQuery(string $type): string 658add1155SRico Sonntag { 668add1155SRico Sonntag $content = ''; 67c0112ce8SGreg Roach $anonymous = 0; 68c0112ce8SGreg Roach $logged_in = []; 698add1155SRico Sonntag 70e5a6b4d4SGreg Roach foreach ($this->user_service->allLoggedIn() as $user) { 711fe542e9SGreg Roach if (Auth::isAdmin() || $user->getPreference(UserInterface::PREF_IS_VISIBLE_ONLINE) === '1') { 72c0112ce8SGreg Roach $logged_in[] = $user; 738add1155SRico Sonntag } else { 74c0112ce8SGreg Roach $anonymous++; 758add1155SRico Sonntag } 768add1155SRico Sonntag } 778add1155SRico Sonntag 786ccdf4f0SGreg Roach $count_logged_in = count($logged_in); 79c0112ce8SGreg Roach 80c0112ce8SGreg Roach if ($count_logged_in === 0 && $anonymous === 0) { 81c0112ce8SGreg Roach $content .= I18N::translate('No signed-in and no anonymous users'); 828add1155SRico Sonntag } 838add1155SRico Sonntag 84c0112ce8SGreg Roach if ($anonymous > 0) { 85c0112ce8SGreg Roach $content .= '<b>' . I18N::plural('%s anonymous signed-in user', '%s anonymous signed-in users', $anonymous, I18N::number($anonymous)) . '</b>'; 868add1155SRico Sonntag } 878add1155SRico Sonntag 88c0112ce8SGreg Roach if ($count_logged_in > 0) { 89ef475b14SGreg Roach if ($anonymous !== 0) { 908add1155SRico Sonntag if ($type === 'list') { 918add1155SRico Sonntag $content .= '<br><br>'; 928add1155SRico Sonntag } else { 938add1155SRico Sonntag $content .= ' ' . I18N::translate('and') . ' '; 948add1155SRico Sonntag } 958add1155SRico Sonntag } 96c0112ce8SGreg Roach $content .= '<b>' . I18N::plural('%s signed-in user', '%s signed-in users', $count_logged_in, I18N::number($count_logged_in)) . '</b>'; 978add1155SRico Sonntag if ($type === 'list') { 988add1155SRico Sonntag $content .= '<ul>'; 998add1155SRico Sonntag } else { 1008add1155SRico Sonntag $content .= ': '; 1018add1155SRico Sonntag } 1028add1155SRico Sonntag } 1038add1155SRico Sonntag 1048add1155SRico Sonntag if (Auth::check()) { 105c0112ce8SGreg Roach foreach ($logged_in as $user) { 1068add1155SRico Sonntag if ($type === 'list') { 107c0112ce8SGreg Roach $content .= '<li>'; 1088add1155SRico Sonntag } 1098add1155SRico Sonntag 1101fe542e9SGreg Roach $individual = Registry::individualFactory()->make($this->tree->getUserPreference($user, UserInterface::PREF_TREE_ACCOUNT_XREF), $this->tree); 111c0112ce8SGreg Roach 112c0112ce8SGreg Roach if ($individual instanceof Individual && $individual->canShow()) { 113c0112ce8SGreg Roach $content .= '<a href="' . e($individual->url()) . '">' . e($user->realName()) . '</a>'; 114c0112ce8SGreg Roach } else { 115c0112ce8SGreg Roach $content .= e($user->realName()); 116c0112ce8SGreg Roach } 117c0112ce8SGreg Roach 118c0112ce8SGreg Roach $content .= ' - ' . e($user->userName()); 119c0112ce8SGreg Roach 1208cfb5e7bSGreg Roach if ($user->getPreference(UserInterface::PREF_CONTACT_METHOD) !== MessageService::CONTACT_METHOD_NONE && Auth::id() !== $user->id()) { 121e381f98dSGreg Roach $content .= '<a href="' . e(route(MessagePage::class, ['to' => $user->userName(), 'tree' => $this->tree->name()])) . '" class="btn btn-link" title="' . I18N::translate('Send a message') . '">' . view('icons/email') . '</a>'; 1228add1155SRico Sonntag } 1238add1155SRico Sonntag 1248add1155SRico Sonntag if ($type === 'list') { 1258add1155SRico Sonntag $content .= '</li>'; 1268add1155SRico Sonntag } 1278add1155SRico Sonntag } 1288add1155SRico Sonntag } 1298add1155SRico Sonntag 1308add1155SRico Sonntag if ($type === 'list') { 1318add1155SRico Sonntag $content .= '</ul>'; 1328add1155SRico Sonntag } 1338add1155SRico Sonntag 1348add1155SRico Sonntag return $content; 1358add1155SRico Sonntag } 1368add1155SRico Sonntag 1378add1155SRico Sonntag /** 1380dcd9387SGreg Roach * @return string 1398add1155SRico Sonntag */ 1408add1155SRico Sonntag public function usersLoggedIn(): string 1418add1155SRico Sonntag { 142c0112ce8SGreg Roach return $this->usersLoggedInQuery('nolist'); 1438add1155SRico Sonntag } 1448add1155SRico Sonntag 1458add1155SRico Sonntag /** 1460dcd9387SGreg Roach * @return string 1478add1155SRico Sonntag */ 1488add1155SRico Sonntag public function usersLoggedInList(): string 1498add1155SRico Sonntag { 1508add1155SRico Sonntag return $this->usersLoggedInQuery('list'); 1518add1155SRico Sonntag } 1528add1155SRico Sonntag 1538add1155SRico Sonntag /** 1548add1155SRico Sonntag * Returns true if the given user is visible to others. 1558add1155SRico Sonntag * 156e5a6b4d4SGreg Roach * @param UserInterface $user 1578add1155SRico Sonntag * 1588add1155SRico Sonntag * @return bool 1598add1155SRico Sonntag */ 160e5a6b4d4SGreg Roach private function isUserVisible(UserInterface $user): bool 1618add1155SRico Sonntag { 1621fe542e9SGreg Roach return Auth::isAdmin() || $user->getPreference(UserInterface::PREF_IS_VISIBLE_ONLINE) === '1'; 1638add1155SRico Sonntag } 1648add1155SRico Sonntag 1658add1155SRico Sonntag /** 1660dcd9387SGreg Roach * @return int 1678add1155SRico Sonntag */ 1688add1155SRico Sonntag public function usersLoggedInTotal(): int 1698add1155SRico Sonntag { 1706ccdf4f0SGreg Roach return count($this->user_service->allLoggedIn()); 1718add1155SRico Sonntag } 1728add1155SRico Sonntag 1738add1155SRico Sonntag /** 1740dcd9387SGreg Roach * @return int 1758add1155SRico Sonntag */ 1768add1155SRico Sonntag public function usersLoggedInTotalAnon(): int 1778add1155SRico Sonntag { 1788add1155SRico Sonntag $anonymous = 0; 1798add1155SRico Sonntag 180e5a6b4d4SGreg Roach foreach ($this->user_service->allLoggedIn() as $user) { 1818add1155SRico Sonntag if (!$this->isUserVisible($user)) { 1828add1155SRico Sonntag ++$anonymous; 1838add1155SRico Sonntag } 1848add1155SRico Sonntag } 1858add1155SRico Sonntag 1868add1155SRico Sonntag return $anonymous; 1878add1155SRico Sonntag } 1888add1155SRico Sonntag 1898add1155SRico Sonntag /** 1900dcd9387SGreg Roach * @return int 1918add1155SRico Sonntag */ 1928add1155SRico Sonntag public function usersLoggedInTotalVisible(): int 1938add1155SRico Sonntag { 1948add1155SRico Sonntag $visible = 0; 1958add1155SRico Sonntag 196e5a6b4d4SGreg Roach foreach ($this->user_service->allLoggedIn() as $user) { 1978add1155SRico Sonntag if ($this->isUserVisible($user)) { 1988add1155SRico Sonntag ++$visible; 1998add1155SRico Sonntag } 2008add1155SRico Sonntag } 2018add1155SRico Sonntag 2028add1155SRico Sonntag return $visible; 2038add1155SRico Sonntag } 2048add1155SRico Sonntag 2058add1155SRico Sonntag /** 2060dcd9387SGreg Roach * @return string 2078add1155SRico Sonntag */ 2088add1155SRico Sonntag public function userId(): string 2098add1155SRico Sonntag { 2108add1155SRico Sonntag return (string) Auth::id(); 2118add1155SRico Sonntag } 2128add1155SRico Sonntag 2138add1155SRico Sonntag /** 2140dcd9387SGreg Roach * @param string $visitor_text 2150dcd9387SGreg Roach * 2160dcd9387SGreg Roach * @return string 2178add1155SRico Sonntag */ 2188add1155SRico Sonntag public function userName(string $visitor_text = ''): string 2198add1155SRico Sonntag { 2208add1155SRico Sonntag if (Auth::check()) { 221e5a6b4d4SGreg Roach return e(Auth::user()->userName()); 2228add1155SRico Sonntag } 2238add1155SRico Sonntag 2248add1155SRico Sonntag // if #username:visitor# was specified, then "visitor" will be returned when the user is not logged in 2258add1155SRico Sonntag return e($visitor_text); 2268add1155SRico Sonntag } 2278add1155SRico Sonntag 2288add1155SRico Sonntag /** 2290dcd9387SGreg Roach * @return string 2308add1155SRico Sonntag */ 2318add1155SRico Sonntag public function userFullName(): string 2328add1155SRico Sonntag { 233315eb316SGreg Roach return Auth::check() ? '<bdi>' . e(Auth::user()->realName()) . '</bdi>' : ''; 2348add1155SRico Sonntag } 2358add1155SRico Sonntag 2368add1155SRico Sonntag /** 2378add1155SRico Sonntag * Returns the user count. 2388add1155SRico Sonntag * 2398add1155SRico Sonntag * @return int 2408add1155SRico Sonntag */ 2418add1155SRico Sonntag private function getUserCount(): int 2428add1155SRico Sonntag { 2436ccdf4f0SGreg Roach return count($this->user_service->all()); 2448add1155SRico Sonntag } 2458add1155SRico Sonntag 2468add1155SRico Sonntag /** 2478add1155SRico Sonntag * Returns the administrator count. 2488add1155SRico Sonntag * 2498add1155SRico Sonntag * @return int 2508add1155SRico Sonntag */ 2518add1155SRico Sonntag private function getAdminCount(): int 2528add1155SRico Sonntag { 2536ccdf4f0SGreg Roach return count($this->user_service->administrators()); 2548add1155SRico Sonntag } 2558add1155SRico Sonntag 2568add1155SRico Sonntag /** 2570dcd9387SGreg Roach * @return string 2588add1155SRico Sonntag */ 2598add1155SRico Sonntag public function totalUsers(): string 2608add1155SRico Sonntag { 2618add1155SRico Sonntag return I18N::number($this->getUserCount()); 2628add1155SRico Sonntag } 2638add1155SRico Sonntag 2648add1155SRico Sonntag /** 2650dcd9387SGreg Roach * @return string 2668add1155SRico Sonntag */ 2678add1155SRico Sonntag public function totalAdmins(): string 2688add1155SRico Sonntag { 2698add1155SRico Sonntag return I18N::number($this->getAdminCount()); 2708add1155SRico Sonntag } 2718add1155SRico Sonntag 2728add1155SRico Sonntag /** 2730dcd9387SGreg Roach * @return string 2748add1155SRico Sonntag */ 2758add1155SRico Sonntag public function totalNonAdmins(): string 2768add1155SRico Sonntag { 2778add1155SRico Sonntag return I18N::number($this->getUserCount() - $this->getAdminCount()); 2788add1155SRico Sonntag } 2798add1155SRico Sonntag} 280