xref: /webtrees/app/Statistics/Repository/UserRepository.php (revision 1270d2767576ed4a83917769b0ee3613e3b010bf)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2023 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\Statistics\Repository;
21
22use Fisharebest\Webtrees\Auth;
23use Fisharebest\Webtrees\Contracts\UserInterface;
24use Fisharebest\Webtrees\Http\RequestHandlers\MessagePage;
25use Fisharebest\Webtrees\I18N;
26use Fisharebest\Webtrees\Individual;
27use Fisharebest\Webtrees\Registry;
28use Fisharebest\Webtrees\Services\MessageService;
29use Fisharebest\Webtrees\Services\UserService;
30use Fisharebest\Webtrees\Statistics\Repository\Interfaces\UserRepositoryInterface;
31use Fisharebest\Webtrees\Tree;
32
33use function count;
34use function e;
35use function route;
36use function view;
37
38/**
39 * A repository providing methods for user related statistics.
40 */
41class UserRepository implements UserRepositoryInterface
42{
43    private Tree $tree;
44
45    private UserService $user_service;
46
47    /**
48     * @param Tree        $tree
49     * @param UserService $user_service
50     */
51    public function __construct(Tree $tree, UserService $user_service)
52    {
53        $this->tree         = $tree;
54        $this->user_service = $user_service;
55    }
56
57    /**
58     * Who is currently logged in?
59     *
60     * @param string $type "list" or "nolist"
61     *
62     * @return string
63     */
64    private function usersLoggedInQuery(string $type): string
65    {
66        $content   = '';
67        $anonymous = 0;
68        $logged_in = [];
69
70        foreach ($this->user_service->allLoggedIn() as $user) {
71            if (Auth::isAdmin() || $user->getPreference(UserInterface::PREF_IS_VISIBLE_ONLINE) === '1') {
72                $logged_in[] = $user;
73            } else {
74                $anonymous++;
75            }
76        }
77
78        $count_logged_in = count($logged_in);
79
80        if ($count_logged_in === 0 && $anonymous === 0) {
81            $content .= I18N::translate('No signed-in and no anonymous users');
82        }
83
84        if ($anonymous > 0) {
85            $content .= '<b>' . I18N::plural('%s anonymous signed-in user', '%s anonymous signed-in users', $anonymous, I18N::number($anonymous)) . '</b>';
86        }
87
88        if ($count_logged_in > 0) {
89            if ($anonymous !== 0) {
90                if ($type === 'list') {
91                    $content .= '<br><br>';
92                } else {
93                    $content .= ' ' . I18N::translate('and') . ' ';
94                }
95            }
96            $content .= '<b>' . I18N::plural('%s signed-in user', '%s signed-in users', $count_logged_in, I18N::number($count_logged_in)) . '</b>';
97            if ($type === 'list') {
98                $content .= '<ul>';
99            } else {
100                $content .= ': ';
101            }
102        }
103
104        if (Auth::check()) {
105            foreach ($logged_in as $user) {
106                if ($type === 'list') {
107                    $content .= '<li>';
108                }
109
110                $individual = Registry::individualFactory()->make($this->tree->getUserPreference($user, UserInterface::PREF_TREE_ACCOUNT_XREF), $this->tree);
111
112                if ($individual instanceof Individual && $individual->canShow()) {
113                    $content .= '<a href="' . e($individual->url()) . '">' . e($user->realName()) . '</a>';
114                } else {
115                    $content .= e($user->realName());
116                }
117
118                $content .= ' - ' . e($user->userName());
119
120                if ($user->getPreference(UserInterface::PREF_CONTACT_METHOD) !== MessageService::CONTACT_METHOD_NONE && Auth::id() !== $user->id()) {
121                    $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>';
122                }
123
124                if ($type === 'list') {
125                    $content .= '</li>';
126                }
127            }
128        }
129
130        if ($type === 'list') {
131            $content .= '</ul>';
132        }
133
134        return $content;
135    }
136
137    /**
138     * @return string
139     */
140    public function usersLoggedIn(): string
141    {
142        return $this->usersLoggedInQuery('nolist');
143    }
144
145    /**
146     * @return string
147     */
148    public function usersLoggedInList(): string
149    {
150        return $this->usersLoggedInQuery('list');
151    }
152
153    /**
154     * Returns true if the given user is visible to others.
155     *
156     * @param UserInterface $user
157     *
158     * @return bool
159     */
160    private function isUserVisible(UserInterface $user): bool
161    {
162        return Auth::isAdmin() || $user->getPreference(UserInterface::PREF_IS_VISIBLE_ONLINE) === '1';
163    }
164
165    /**
166     * @return int
167     */
168    public function usersLoggedInTotal(): int
169    {
170        return count($this->user_service->allLoggedIn());
171    }
172
173    /**
174     * @return int
175     */
176    public function usersLoggedInTotalAnon(): int
177    {
178        $anonymous = 0;
179
180        foreach ($this->user_service->allLoggedIn() as $user) {
181            if (!$this->isUserVisible($user)) {
182                ++$anonymous;
183            }
184        }
185
186        return $anonymous;
187    }
188
189    /**
190     * @return int
191     */
192    public function usersLoggedInTotalVisible(): int
193    {
194        $visible = 0;
195
196        foreach ($this->user_service->allLoggedIn() as $user) {
197            if ($this->isUserVisible($user)) {
198                ++$visible;
199            }
200        }
201
202        return $visible;
203    }
204
205    /**
206     * @return string
207     */
208    public function userId(): string
209    {
210        return (string) Auth::id();
211    }
212
213    /**
214     * @param string $visitor_text
215     *
216     * @return string
217     */
218    public function userName(string $visitor_text = ''): string
219    {
220        if (Auth::check()) {
221            return e(Auth::user()->userName());
222        }
223
224        // if #username:visitor# was specified, then "visitor" will be returned when the user is not logged in
225        return e($visitor_text);
226    }
227
228    /**
229     * @return string
230     */
231    public function userFullName(): string
232    {
233        return Auth::check() ? '<bdi>' . e(Auth::user()->realName()) . '</bdi>' : '';
234    }
235
236    /**
237     * Returns the user count.
238     *
239     * @return int
240     */
241    private function getUserCount(): int
242    {
243        return count($this->user_service->all());
244    }
245
246    /**
247     * Returns the administrator count.
248     *
249     * @return int
250     */
251    private function getAdminCount(): int
252    {
253        return count($this->user_service->administrators());
254    }
255
256    /**
257     * @return string
258     */
259    public function totalUsers(): string
260    {
261        return I18N::number($this->getUserCount());
262    }
263
264    /**
265     * @return string
266     */
267    public function totalAdmins(): string
268    {
269        return I18N::number($this->getAdminCount());
270    }
271
272    /**
273     * @return string
274     */
275    public function totalNonAdmins(): string
276    {
277        return I18N::number($this->getUserCount() - $this->getAdminCount());
278    }
279}
280