133c34396SGreg Roach<?php 23976b470SGreg Roach 333c34396SGreg Roach/** 433c34396SGreg Roach * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 633c34396SGreg Roach * This program is free software: you can redistribute it and/or modify 733c34396SGreg Roach * it under the terms of the GNU General Public License as published by 833c34396SGreg Roach * the Free Software Foundation, either version 3 of the License, or 933c34396SGreg Roach * (at your option) any later version. 1033c34396SGreg Roach * This program is distributed in the hope that it will be useful, 1133c34396SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 1233c34396SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1333c34396SGreg Roach * GNU General Public License for more details. 1433c34396SGreg Roach * 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/>. 1633c34396SGreg Roach */ 17fcfa147eSGreg Roach 1833c34396SGreg Roachdeclare(strict_types=1); 1933c34396SGreg Roach 2033c34396SGreg Roachnamespace Fisharebest\Webtrees\Module; 2133c34396SGreg Roach 22*6f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB; 238bfab638SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\FamilyPage; 248bfab638SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\IndividualPage; 258bfab638SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\MediaPage; 268bfab638SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\NotePage; 278bfab638SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\RepositoryPage; 288bfab638SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\SourcePage; 291e1acee3SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\SubmitterPage; 308e0e1b25SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\TreePage; 318e0e1b25SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\UserPage; 3233c34396SGreg Roachuse Fisharebest\Webtrees\I18N; 3333c34396SGreg Roachuse Fisharebest\Webtrees\Session; 3433c34396SGreg Roachuse Fisharebest\Webtrees\Tree; 35b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator; 366ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface; 376ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 386ccdf4f0SGreg Roachuse Psr\Http\Server\MiddlewareInterface; 396ccdf4f0SGreg Roachuse Psr\Http\Server\RequestHandlerInterface; 4033c34396SGreg Roach 4133c34396SGreg Roach/** 420c8c69d4SGreg Roach * Class HitCountFooterModule - show the number of page hits in the footer. 4333c34396SGreg Roach */ 440c8c69d4SGreg Roachclass HitCountFooterModule extends AbstractModule implements ModuleFooterInterface, MiddlewareInterface 4533c34396SGreg Roach{ 4633c34396SGreg Roach use ModuleFooterTrait; 4733c34396SGreg Roach 48f1d4b4a2SGreg Roach // Which routes do we count? 4933c34396SGreg Roach // For historical reasons, we record the names of the original webtrees script and parameter. 5033c34396SGreg Roach protected const PAGE_NAMES = [ 518bfab638SGreg Roach FamilyPage::class => 'family.php', 528bfab638SGreg Roach IndividualPage::class => 'individual.php', 538bfab638SGreg Roach MediaPage::class => 'mediaviewer.php', 548bfab638SGreg Roach NotePage::class => 'note.php', 558bfab638SGreg Roach RepositoryPage::class => 'repo.php', 568bfab638SGreg Roach SourcePage::class => 'source.php', 571e1acee3SGreg Roach SubmitterPage::class => 'submitter', 588e0e1b25SGreg Roach TreePage::class => 'index.php', 598e0e1b25SGreg Roach UserPage::class => 'index.php', 6033c34396SGreg Roach ]; 6133c34396SGreg Roach 6233c746f1SGreg Roach // Count of visits to the current page 6333c746f1SGreg Roach protected int $page_hits = 0; 6433c34396SGreg Roach 6533c34396SGreg Roach /** 66d4c04956SGreg Roach * How should this module be labelled on tabs, footers, etc.? 67d4c04956SGreg Roach * 68d4c04956SGreg Roach * @return string 69d4c04956SGreg Roach */ 70d4c04956SGreg Roach public function title(): string 71d4c04956SGreg Roach { 72d4c04956SGreg Roach /* I18N: Name of a module */ 73d4c04956SGreg Roach return I18N::translate('Hit counters'); 74d4c04956SGreg Roach } 75d4c04956SGreg Roach 76d4c04956SGreg Roach public function description(): string 77d4c04956SGreg Roach { 78d4c04956SGreg Roach /* I18N: Description of the “Hit counters” module */ 79d4c04956SGreg Roach return I18N::translate('Count the visits to each page'); 80d4c04956SGreg Roach } 81d4c04956SGreg Roach 82d4c04956SGreg Roach /** 8333c34396SGreg Roach * The default position for this footer. It can be changed in the control panel. 8433c34396SGreg Roach * 8533c34396SGreg Roach * @return int 8633c34396SGreg Roach */ 8733c34396SGreg Roach public function defaultFooterOrder(): int 8833c34396SGreg Roach { 8933c34396SGreg Roach return 3; 9033c34396SGreg Roach } 9133c34396SGreg Roach 9233c34396SGreg Roach /** 9333c34396SGreg Roach * A footer, to be added at the bottom of every page. 9433c34396SGreg Roach * 95a992e8c1SGreg Roach * @param ServerRequestInterface $request 960c8c69d4SGreg Roach * 9733c34396SGreg Roach * @return string 9833c34396SGreg Roach */ 99a992e8c1SGreg Roach public function getFooter(ServerRequestInterface $request): string 10033c34396SGreg Roach { 1010c8c69d4SGreg Roach if ($this->page_hits === 0) { 10233c34396SGreg Roach return ''; 10333c34396SGreg Roach } 10433c34396SGreg Roach 10533c34396SGreg Roach $digits = '<span class="odometer">' . I18N::digits($this->page_hits) . '</span>'; 10633c34396SGreg Roach 10733c34396SGreg Roach return view('modules/hit-counter/footer', [ 10833c34396SGreg Roach 'hit_counter' => I18N::plural('This page has been viewed %s time.', 'This page has been viewed %s times.', $this->page_hits, $digits), 10933c34396SGreg Roach ]); 11033c34396SGreg Roach } 11133c34396SGreg Roach 11233c34396SGreg Roach /** 1136ccdf4f0SGreg Roach * @param ServerRequestInterface $request 1146ccdf4f0SGreg Roach * @param RequestHandlerInterface $handler 1156ccdf4f0SGreg Roach * 1166ccdf4f0SGreg Roach * @return ResponseInterface 1176ccdf4f0SGreg Roach */ 1186ccdf4f0SGreg Roach public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 1196ccdf4f0SGreg Roach { 120b55cbc6bSGreg Roach $route = Validator::attributes($request)->route(); 121b55cbc6bSGreg Roach $tree = Validator::attributes($request)->treeOptional(); 122b55cbc6bSGreg Roach $user = Validator::attributes($request)->user(); 1236ccdf4f0SGreg Roach 1246ccdf4f0SGreg Roach if ($tree instanceof Tree && $tree->getPreference('SHOW_COUNTER')) { 125de2aa325SGreg Roach $page_name = self::PAGE_NAMES[$route->name] ?? ''; 1266ccdf4f0SGreg Roach 127de2aa325SGreg Roach switch ($route->name) { 1288bfab638SGreg Roach case FamilyPage::class: 1298bfab638SGreg Roach case IndividualPage::class: 1308bfab638SGreg Roach case MediaPage::class: 1318bfab638SGreg Roach case NotePage::class: 1328bfab638SGreg Roach case RepositoryPage::class: 1338bfab638SGreg Roach case SourcePage::class: 1341e1acee3SGreg Roach case SubmitterPage::class: 135b55cbc6bSGreg Roach $xref = Validator::attributes($request)->isXref()->string('xref'); 136b55cbc6bSGreg Roach $this->page_hits = $this->countHit($tree, $page_name, $xref); 1376ccdf4f0SGreg Roach break; 1386ccdf4f0SGreg Roach 1398e0e1b25SGreg Roach case TreePage::class: 1406ccdf4f0SGreg Roach $this->page_hits = $this->countHit($tree, $page_name, 'gedcom:' . $tree->id()); 1416ccdf4f0SGreg Roach break; 1426ccdf4f0SGreg Roach 1438e0e1b25SGreg Roach case UserPage::class: 1446ccdf4f0SGreg Roach $this->page_hits = $this->countHit($tree, $page_name, 'user:' . $user->id()); 1456ccdf4f0SGreg Roach break; 1466ccdf4f0SGreg Roach } 1476ccdf4f0SGreg Roach } 1486ccdf4f0SGreg Roach 1496ccdf4f0SGreg Roach return $handler->handle($request); 1506ccdf4f0SGreg Roach } 1516ccdf4f0SGreg Roach 1526ccdf4f0SGreg Roach /** 15333c34396SGreg Roach * Increment the page count. 15433c34396SGreg Roach * 1550c8c69d4SGreg Roach * @param Tree $tree 15633c34396SGreg Roach * @param string $page 15733c34396SGreg Roach * @param string $parameter 15833c34396SGreg Roach * 15933c34396SGreg Roach * @return int 16033c34396SGreg Roach */ 1610c8c69d4SGreg Roach protected function countHit(Tree $tree, string $page, string $parameter): int 16233c34396SGreg Roach { 16333c34396SGreg Roach // Don't increment the counter while we stay on the same page. 16433c34396SGreg Roach if ( 16533c34396SGreg Roach Session::get('last_page_name') === $page && 166a992e8c1SGreg Roach Session::get('last_page_parameter') === $parameter && 167a992e8c1SGreg Roach Session::get('last_gedcom_id') === $tree->id() 16833c34396SGreg Roach ) { 1690c8c69d4SGreg Roach return (int) Session::get('last_count'); 17033c34396SGreg Roach } 17133c34396SGreg Roach 17233c34396SGreg Roach $count = (int) DB::table('hit_counter') 1730c8c69d4SGreg Roach ->where('gedcom_id', '=', $tree->id()) 17433c34396SGreg Roach ->where('page_name', '=', $page) 17533c34396SGreg Roach ->where('page_parameter', '=', $parameter) 1768491737aSGreg Roach ->value('page_count'); 17733c34396SGreg Roach 17833c34396SGreg Roach $count++; 17933c34396SGreg Roach 18033c34396SGreg Roach DB::table('hit_counter')->updateOrInsert([ 1810c8c69d4SGreg Roach 'gedcom_id' => $tree->id(), 18233c34396SGreg Roach 'page_name' => $page, 18333c34396SGreg Roach 'page_parameter' => $parameter, 18433c34396SGreg Roach ], [ 18533c34396SGreg Roach 'page_count' => $count, 18633c34396SGreg Roach ]); 18733c34396SGreg Roach 1880c8c69d4SGreg Roach Session::put('last_gedcom_id', $tree->id()); 18933c34396SGreg Roach Session::put('last_page_name', $page); 19033c34396SGreg Roach Session::put('last_page_parameter', $parameter); 19133c34396SGreg Roach Session::put('last_count', $count); 19233c34396SGreg Roach 19333c34396SGreg Roach return $count; 19433c34396SGreg Roach } 19533c34396SGreg Roach} 196