16b6f8822SAndreas Gohr<?php 2a8acb244SAndreas Gohr 3*523da372SAndreas Gohruse dokuwiki\ErrorHandler; 4d5ef99ddSAndreas Gohruse dokuwiki\Extension\Plugin; 5ba6b3b10SAndreas Gohruse dokuwiki\HTTP\DokuHTTPClient; 6d5ef99ddSAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB; 7*523da372SAndreas Gohruse dokuwiki\plugin\statistics\DummyLogger; 8*523da372SAndreas Gohruse dokuwiki\plugin\statistics\IgnoreException; 9ba6b3b10SAndreas Gohruse dokuwiki\plugin\statistics\IpResolverException; 10aab59130SAndreas Gohruse dokuwiki\plugin\statistics\Logger; 11b6632b6eSAndreas Gohruse dokuwiki\plugin\statistics\Query; 1264ba7f66SAnna Dabrowskause dokuwiki\plugin\statistics\StatisticsGraph; 13d5ef99ddSAndreas Gohr 146b6f8822SAndreas Gohr/** 156b6f8822SAndreas Gohr * Statistics Plugin 166b6f8822SAndreas Gohr * 176b6f8822SAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 186b6f8822SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 196b6f8822SAndreas Gohr */ 20d5ef99ddSAndreas Gohrclass helper_plugin_statistics extends Plugin 21a8acb244SAndreas Gohr{ 2241d1fffcSAndreas Gohr protected ?Query $oQuery = null; 23c4c84f98SAndreas Gohr protected ?StatisticsGraph $oGraph = null; 24762f4807SAndreas Gohr protected ?SQLiteDB $db = null; 25ba6b3b10SAndreas Gohr public ?DokuHTTPClient $httpClient = null; // public for testing purposes 266b6f8822SAndreas Gohr 276b6f8822SAndreas Gohr /** 28d5ef99ddSAndreas Gohr * Get SQLiteDB instance 29d5ef99ddSAndreas Gohr * 30d5ef99ddSAndreas Gohr * @return SQLiteDB|null 31483101d3SAndreas Gohr * @throws Exception when SQLite initialization failed 32d5ef99ddSAndreas Gohr */ 33483101d3SAndreas Gohr public function getDB(): ?SQLiteDB 34d5ef99ddSAndreas Gohr { 352adee4c6SAndreas Gohr if (!$this->db instanceof SQLiteDB) { 36211caa5dSAndreas Gohr if (!class_exists(SQLiteDB::class)) throw new Exception('SQLite Plugin missing'); 37762f4807SAndreas Gohr $this->db = new SQLiteDB('statistics', DOKU_PLUGIN . 'statistics/db/'); 38d5ef99ddSAndreas Gohr } 39d5ef99ddSAndreas Gohr return $this->db; 40d5ef99ddSAndreas Gohr } 41d5ef99ddSAndreas Gohr 42d5ef99ddSAndreas Gohr 43d5ef99ddSAndreas Gohr /** 446b6f8822SAndreas Gohr * Return an instance of the query class 456b6f8822SAndreas Gohr * 46b6632b6eSAndreas Gohr * @return Query 476b6f8822SAndreas Gohr */ 48211caa5dSAndreas Gohr public function getQuery(): Query 49a8acb244SAndreas Gohr { 506b6f8822SAndreas Gohr if (is_null($this->oQuery)) { 51b6632b6eSAndreas Gohr $this->oQuery = new Query($this); 526b6f8822SAndreas Gohr } 536b6f8822SAndreas Gohr return $this->oQuery; 546b6f8822SAndreas Gohr } 556b6f8822SAndreas Gohr 566b6f8822SAndreas Gohr /** 576b6f8822SAndreas Gohr * Return an instance of the logger class 586b6f8822SAndreas Gohr * 59*523da372SAndreas Gohr * When the logger cannot be created for any reason a DummyLogger is returned 60*523da372SAndreas Gohr * 61*523da372SAndreas Gohr * @return Logger|DummyLogger 626b6f8822SAndreas Gohr */ 63*523da372SAndreas Gohr public function getLogger() 64a8acb244SAndreas Gohr { 65*523da372SAndreas Gohr try { 6641d1fffcSAndreas Gohr return new Logger($this); 67*523da372SAndreas Gohr } catch (Exception $e) { 68*523da372SAndreas Gohr if (!$e instanceof IgnoreException) { 69*523da372SAndreas Gohr ErrorHandler::logException($e); 70*523da372SAndreas Gohr } 71*523da372SAndreas Gohr 72*523da372SAndreas Gohr return new DummyLogger(); 73*523da372SAndreas Gohr } 746b6f8822SAndreas Gohr } 756b6f8822SAndreas Gohr 766b6f8822SAndreas Gohr /** 776b6f8822SAndreas Gohr * Return an instance of the Graph class 786b6f8822SAndreas Gohr * 791664ba1dSAndreas Gohr * @return StatisticsGraph 806b6f8822SAndreas Gohr */ 81211caa5dSAndreas Gohr public function getGraph($from, $to, $width, $height) 82a8acb244SAndreas Gohr { 836b6f8822SAndreas Gohr if (is_null($this->oGraph)) { 84f0a4cceeSAnna Dabrowska $this->oGraph = new StatisticsGraph($this, $from, $to, $width, $height); 856b6f8822SAndreas Gohr } 866b6f8822SAndreas Gohr return $this->oGraph; 876b6f8822SAndreas Gohr } 886b6f8822SAndreas Gohr 896b6f8822SAndreas Gohr /** 904f41a2ccSAndreas Gohr * Just send a 1x1 pixel blank gif to the browser 914f41a2ccSAndreas Gohr * 9287e0f0b1SAndreas Gohr * @called from dispatch.php 934f41a2ccSAndreas Gohr * 944f41a2ccSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 954f41a2ccSAndreas Gohr * @author Harry Fuecks <fuecks@gmail.com> 964f41a2ccSAndreas Gohr */ 97a8acb244SAndreas Gohr public function sendGIF($transparent = true) 98a8acb244SAndreas Gohr { 99259897e1SAndreas Gohr if ($transparent) { 1004f41a2ccSAndreas Gohr $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7'); 101259897e1SAndreas Gohr } else { 102259897e1SAndreas Gohr $img = base64_decode('R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs='); 103259897e1SAndreas Gohr } 1044f41a2ccSAndreas Gohr header('Content-Type: image/gif'); 1054f41a2ccSAndreas Gohr header('Content-Length: ' . strlen($img)); 1064f41a2ccSAndreas Gohr header('Connection: Close'); 107a8acb244SAndreas Gohr echo $img; 1084f41a2ccSAndreas Gohr flush(); 1094f41a2ccSAndreas Gohr // Browser should drop connection after this 110211caa5dSAndreas Gohr // Thinks it got the whole image 1114f41a2ccSAndreas Gohr } 112ba6b3b10SAndreas Gohr 113ba6b3b10SAndreas Gohr /** 114ba6b3b10SAndreas Gohr * Return the location information for an IP address 115ba6b3b10SAndreas Gohr * 116ba6b3b10SAndreas Gohr * @throws IpResolverException 117ba6b3b10SAndreas Gohr * @noinspection HttpUrlsUsage 118ba6b3b10SAndreas Gohr */ 119ba6b3b10SAndreas Gohr public function resolveIP($ip) 120ba6b3b10SAndreas Gohr { 121ba6b3b10SAndreas Gohr $http = $this->httpClient ?: new DokuHTTPClient(); 122ba6b3b10SAndreas Gohr $http->timeout = 7; 123ba6b3b10SAndreas Gohr $json = $http->get('http://ip-api.com/json/' . $ip); // yes, it's HTTP only 124ba6b3b10SAndreas Gohr 125ba6b3b10SAndreas Gohr if (!$json) { 126ba6b3b10SAndreas Gohr throw new IpResolverException('Failed talk to ip-api.com.'); 127ba6b3b10SAndreas Gohr } 128ba6b3b10SAndreas Gohr try { 129ba6b3b10SAndreas Gohr $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR); 130ba6b3b10SAndreas Gohr } catch (\JsonException $e) { 131ba6b3b10SAndreas Gohr throw new IpResolverException('Failed to decode JSON from ip-api.com.', $e->getTrace(), 0, $e); 132ba6b3b10SAndreas Gohr } 133ba6b3b10SAndreas Gohr if (!isset($data['status'])) { 134ba6b3b10SAndreas Gohr throw new IpResolverException('Invalid ip-api.com result for' . $ip, $data); 135ba6b3b10SAndreas Gohr } 136ba6b3b10SAndreas Gohr // we do not check for 'success' status here. when the API can't resolve the IP we still log it 137ba6b3b10SAndreas Gohr // without location data, so we won't re-query it in the next 30 days. 138ba6b3b10SAndreas Gohr 139ba6b3b10SAndreas Gohr return $data; 140ba6b3b10SAndreas Gohr } 1416b6f8822SAndreas Gohr} 142