diff --git a/action.php b/action.php index ea65e94..34bfdda 100644 --- a/action.php +++ b/action.php @@ -9,7 +9,7 @@ if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); -require_once(DOKU_PLUGIN . 'action.php'); +// Using autoloading instead of require_once for better compatibility class action_plugin_statistics extends DokuWiki_Action_Plugin { @@ -113,13 +113,13 @@ function loglogins(Doku_Event $event, $param) { $act = $this->_act_clean($event->data); if($act == 'logout') { $type = 'o'; - } elseif($_SERVER['REMOTE_USER'] && $act == 'login') { - if($_REQUEST['r']) { + } elseif(isset($_SERVER['REMOTE_USER']) && $_SERVER['REMOTE_USER'] && $act == 'login') { + if(isset($_REQUEST['r']) && $_REQUEST['r']) { $type = 'p'; } else { $type = 'l'; } - } elseif($_REQUEST['u'] && !$_REQUEST['http_credentials'] && !$_SERVER['REMOTE_USER']) { + } elseif(isset($_REQUEST['u']) && $_REQUEST['u'] && !isset($_REQUEST['http_credentials']) && !isset($_SERVER['REMOTE_USER'])) { $type = 'f'; } if(!$type) return; diff --git a/admin.php b/admin.php index 6da1f4d..fbec131 100644 --- a/admin.php +++ b/admin.php @@ -108,11 +108,14 @@ public function getMenuSort() { * handle user request */ public function handle() { - $this->opt = preg_replace('/[^a-z]+/', '', $_REQUEST['opt']); + $this->opt = preg_replace('/[^a-z]+/', '', isset($_REQUEST['opt']) ? $_REQUEST['opt'] : ''); if(!in_array($this->opt, $this->allowedpages)) $this->opt = 'dashboard'; - $this->start = (int) $_REQUEST['s']; - $this->setTimeframe($_REQUEST['f'], $_REQUEST['t']); + $this->start = (int) (isset($_REQUEST['s']) ? $_REQUEST['s'] : 0); + $this->setTimeframe( + isset($_REQUEST['f']) ? $_REQUEST['f'] : '', + isset($_REQUEST['t']) ? $_REQUEST['t'] : '' + ); } /** diff --git a/helper.php b/helper.php index 7f33d34..94518ba 100644 --- a/helper.php +++ b/helper.php @@ -102,7 +102,10 @@ public function runSQL($sql_string) { $result = mysqli_query($link, $sql_string); if($result === false) { - dbglog('DB Error: ' . mysqli_error($link) . ' ' . hsc($sql_string), -1); + // Using modern logger instead of deprecated dbglog + if (class_exists('\dokuwiki\Logger')) { + \dokuwiki\Logger::error('DB Error: ' . mysqli_error($link) . ' ' . hsc($sql_string)); + } msg('DB Error: ' . mysqli_error($link) . ' ' . hsc($sql_string), -1); return null; } diff --git a/img.php b/img.php index 449663e..e61390b 100644 --- a/img.php +++ b/img.php @@ -15,7 +15,12 @@ $plugin = plugin_load('helper', 'statistics'); try { if(!auth_ismanager()) throw new Exception('Access denied'); - $plugin->Graph()->render($_REQUEST['img'], $_REQUEST['f'], $_REQUEST['t'], $_REQUEST['s']); + $plugin->Graph()->render( + isset($_REQUEST['img']) ? $_REQUEST['img'] : '', + isset($_REQUEST['f']) ? $_REQUEST['f'] : '', + isset($_REQUEST['t']) ? $_REQUEST['t'] : '', + isset($_REQUEST['s']) ? $_REQUEST['s'] : '' + ); } catch(Exception $e) { $plugin->sendGIF(false); } diff --git a/inc/StatisticsBrowscap.class.php b/inc/StatisticsBrowscap.class.php index c6f7bea..10a51cf 100644 --- a/inc/StatisticsBrowscap.class.php +++ b/inc/StatisticsBrowscap.class.php @@ -24,7 +24,7 @@ public function __construct() { * @return string */ protected function _getRemoteData($url) { - $http = new DokuHTTPClient($url); + $http = new \dokuwiki\HTTP\DokuHTTPClient($url); $file = $http->get($url); if(!$file) throw new Exception('Your server can\'t connect to external resources. Please update the file manually.'); diff --git a/inc/StatisticsGraph.class.php b/inc/StatisticsGraph.class.php index a2a53b0..887332a 100644 --- a/inc/StatisticsGraph.class.php +++ b/inc/StatisticsGraph.class.php @@ -44,19 +44,49 @@ protected function PieChart($data) { $Chart = new PieChart(400, 200, $Canvas); $Chart->setFontProperties(dirname(__FILE__) . '/pchart/Fonts/DroidSans.ttf', 8); - $DataSet->AddPoints(array_values($data), 'Serie1'); - $DataSet->AddPoints(array_keys($data), 'Serie2'); + // Ensure data values are numeric + $values = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, array_values($data)); + $keys = array_keys($data); + + // Ensure arrays have at least one element + if(empty($values) || empty($keys)) { + $values = array(0); + $keys = array('No Data'); + } + + // Check if all values are zero + $totalSum = array_sum($values); + if($totalSum == 0 && count($values) > 1) { + // If all values are zero, show a single "No Data" entry + $values = array(1); // Use 1 instead of 0 to avoid division by zero in chart rendering + $keys = array('No Data'); + } + + $DataSet->AddPoints($values, 'Serie1'); + $DataSet->AddPoints($keys, 'Serie2'); $DataSet->AddAllSeries(); $DataSet->SetAbscissaLabelSeries("Serie2"); + // Final data validation before drawing pie chart + $pieData = $DataSet->getData(); + if (is_array($pieData)) { + foreach ($pieData as $serieKey => $serieData) { + if (is_array($serieData)) { + $pieData[$serieKey] = array_map(function($v) { + return is_numeric($v) ? (float)$v : 0.0; + }, $serieData); + } + } + } + $Chart->drawBasicPieGraph( - $DataSet->getData(), + $pieData, $DataSet->GetDataDescription(), 120, 100, 60, PIE_PERCENTAGE ); $Chart->drawPieLegend( 230, 15, - $DataSet->GetData(), + $pieData, $DataSet->GetDataDescription(), new Color(250) ); @@ -76,14 +106,36 @@ protected function sumUpPieChart($query, $key, $max=4){ $result = $this->hlp->Query()->$query($this->tlimit, $this->start, 0, false); $data = array(); $top = 0; + + // Initialize 'other' key + if (!isset($data['other'])) { + $data['other'] = 0; + } + foreach($result as $row) { if($top < $max) { - $data[$row[$key]] = $row['cnt']; + $keyValue = isset($row[$key]) ? $row[$key] : 'unknown'; + $cntValue = isset($row['cnt']) ? (is_numeric($row['cnt']) ? (int)$row['cnt'] : 0) : 0; + $data[$keyValue] = $cntValue; } else { - $data['other'] += $row['cnt']; + $cntValue = isset($row['cnt']) ? (is_numeric($row['cnt']) ? (int)$row['cnt'] : 0) : 0; + if (!isset($data['other'])) { + $data['other'] = 0; + } + $data['other'] += $cntValue; } $top++; } + + // Remove empty 'other' category if it wasn't used + if (isset($data['other']) && $data['other'] == 0 && count($data) > 1) { + unset($data['other']); + } + + // Ensure we have at least some data + if (empty($data)) { + $data = array('No Data' => 0); + } $this->PieChart($data); } @@ -108,16 +160,30 @@ protected function history($info) { $data = array(); $times = array(); foreach($result as $row) { - $data[] = $row['cnt']; + $cntValue = isset($row['cnt']) ? (is_numeric($row['cnt']) ? (int)$row['cnt'] : 0) : 0; + $data[] = $cntValue; if($interval == 'months') { - $times[] = substr($row['time'], 0, 4) . '-' . substr($row['time'], 4, 2); + $time = isset($row['time']) ? $row['time'] : ''; + $times[] = substr($time, 0, 4) . '-' . substr($time, 4, 2); } elseif ($interval == 'weeks') { - $times[] = $row['EXTRACT(YEAR FROM dt)'] . '-' . $row['time']; + $year = isset($row['EXTRACT(YEAR FROM dt)']) ? $row['EXTRACT(YEAR FROM dt)'] : ''; + $time = isset($row['time']) ? $row['time'] : ''; + $times[] = $year . '-' . $time; }else { - $times[] = substr($row['time'], -5); + $time = isset($row['time']) ? $row['time'] : ''; + $times[] = substr($time, -5); } } + // Ensure data contains only numeric values + $data = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data); + + // Ensure arrays have at least one element + if(empty($data) || empty($times)) { + $data = array(0); + $times = array('No Data'); + } + $DataSet = new pData(); $DataSet->AddPoints($data, 'Serie1'); $DataSet->AddPoints($times, 'Times'); @@ -136,7 +202,18 @@ protected function history($info) { $DataSet, new ScaleStyle(SCALE_NORMAL, new Color(127)), 45, 1, false, ceil(count($times) / 12) ); - $Chart->drawLineGraph($DataSet->GetData(), $DataSet->GetDataDescription()); + // Final data validation before drawing + $chartData = $DataSet->GetData(); + if (is_array($chartData)) { + foreach ($chartData as $serieKey => $serieData) { + if (is_array($serieData)) { + $chartData[$serieKey] = array_map(function($v) { + return is_numeric($v) ? (float)$v : 0.0; + }, $serieData); + } + } + } + $Chart->drawLineGraph($chartData, $DataSet->GetDataDescription()); $DataSet->removeSeries('Times'); $DataSet->removeSeriesName('Times'); @@ -187,9 +264,21 @@ public function viewport() { $data3 = array(); foreach($result as $row) { - $data1[] = $row['res_x']; - $data2[] = $row['res_y']; - $data3[] = $row['cnt']; + $data1[] = isset($row['res_x']) ? (is_numeric($row['res_x']) ? (int)$row['res_x'] : 0) : 0; + $data2[] = isset($row['res_y']) ? (is_numeric($row['res_y']) ? (int)$row['res_y'] : 0) : 0; + $data3[] = isset($row['cnt']) ? (is_numeric($row['cnt']) ? (int)$row['cnt'] : 0) : 0; + } + + // Ensure all data arrays contain only numeric values + $data1 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data1); + $data2 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data2); + $data3 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data3); + + // Ensure all arrays have at least one element + if(empty($data1) || empty($data2) || empty($data3)) { + $data1 = array(0); + $data2 = array(0); + $data3 = array(0); } $DataSet = new pData; @@ -208,7 +297,23 @@ public function viewport() { 'Serie2', 'Serie1' ); - $Chart->drawXYPlotGraph($DataSet, 'Serie2', 'Serie1', 0, 20, 2, null, false, 'Serie3'); + // Validate DataSet before XY plot + $validatedDataSet = new pData; + $originalData = $DataSet->GetData(); + if (is_array($originalData)) { + foreach ($originalData as $serieKey => $serieData) { + if (is_array($serieData)) { + $cleanData = array_map(function($v) { + return is_numeric($v) ? (float)$v : 0.0; + }, $serieData); + $validatedDataSet->AddPoints($cleanData, $serieKey); + } + } + $validatedDataSet->AddAllSeries(); + } else { + $validatedDataSet = $DataSet; + } + $Chart->drawXYPlotGraph($validatedDataSet, 'Serie2', 'Serie1', 0, 20, 2, null, false, 'Serie3'); header('Content-Type: image/png'); $Chart->Render(''); } @@ -220,9 +325,21 @@ public function resolution() { $data3 = array(); foreach($result as $row) { - $data1[] = $row['res_x']; - $data2[] = $row['res_y']; - $data3[] = $row['cnt']; + $data1[] = isset($row['res_x']) ? (is_numeric($row['res_x']) ? (int)$row['res_x'] : 0) : 0; + $data2[] = isset($row['res_y']) ? (is_numeric($row['res_y']) ? (int)$row['res_y'] : 0) : 0; + $data3[] = isset($row['cnt']) ? (is_numeric($row['cnt']) ? (int)$row['cnt'] : 0) : 0; + } + + // Ensure all data arrays contain only numeric values + $data1 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data1); + $data2 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data2); + $data3 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data3); + + // Ensure all arrays have at least one element + if(empty($data1) || empty($data2) || empty($data3)) { + $data1 = array(0); + $data2 = array(0); + $data3 = array(0); } $DataSet = new pData; @@ -241,7 +358,23 @@ public function resolution() { 'Serie2', 'Serie1' ); - $Chart->drawXYPlotGraph($DataSet, 'Serie2', 'Serie1', 0, 20, 2, null, false, 'Serie3'); + // Validate DataSet before XY plot + $validatedDataSet = new pData; + $originalData = $DataSet->GetData(); + if (is_array($originalData)) { + foreach ($originalData as $serieKey => $serieData) { + if (is_array($serieData)) { + $cleanData = array_map(function($v) { + return is_numeric($v) ? (float)$v : 0.0; + }, $serieData); + $validatedDataSet->AddPoints($cleanData, $serieKey); + } + } + $validatedDataSet->AddAllSeries(); + } else { + $validatedDataSet = $DataSet; + } + $Chart->drawXYPlotGraph($validatedDataSet, 'Serie2', 'Serie1', 0, 20, 2, null, false, 'Serie3'); header('Content-Type: image/png'); $Chart->Render(''); } @@ -273,12 +406,25 @@ public function dashboardviews() { $times = array(); foreach($result as $time => $row) { - $data1[] = (int) $row['pageviews']; - $data2[] = (int) $row['sessions']; - $data3[] = (int) $row['visitors']; + $data1[] = isset($row['pageviews']) ? (is_numeric($row['pageviews']) ? (int)$row['pageviews'] : 0) : 0; + $data2[] = isset($row['sessions']) ? (is_numeric($row['sessions']) ? (int)$row['sessions'] : 0) : 0; + $data3[] = isset($row['visitors']) ? (is_numeric($row['visitors']) ? (int)$row['visitors'] : 0) : 0; $times[] = $time . ($hours ? 'h' : ''); } + // Ensure all data arrays contain only numeric values + $data1 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data1); + $data2 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data2); + $data3 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data3); + + // Ensure all arrays have the same length and at least one element + if(empty($data1) || empty($data2) || empty($data3) || empty($times)) { + $data1 = array(0); + $data2 = array(0); + $data3 = array(0); + $times = array('No Data'); + } + $DataSet = new pData(); $DataSet->AddPoints($data1, 'Serie1'); $DataSet->AddPoints($data2, 'Serie2'); @@ -300,7 +446,18 @@ public function dashboardviews() { $DataSet, new ScaleStyle(SCALE_NORMAL, new Color(127)), ($hours ? 0 : 45), 1, false, ceil(count($times) / 12) ); - $Chart->drawLineGraph($DataSet->GetData(), $DataSet->GetDataDescription()); + // Final data validation before drawing + $chartData = $DataSet->GetData(); + if (is_array($chartData)) { + foreach ($chartData as $serieKey => $serieData) { + if (is_array($serieData)) { + $chartData[$serieKey] = array_map(function($v) { + return is_numeric($v) ? (float)$v : 0.0; + }, $serieData); + } + } + } + $Chart->drawLineGraph($chartData, $DataSet->GetDataDescription()); $DataSet->removeSeries('Times'); $DataSet->removeSeriesName('Times'); @@ -323,12 +480,25 @@ public function dashboardwiki() { $times = array(); foreach($result as $time => $row) { - $data1[] = (int) $row['E']; - $data2[] = (int) $row['C']; - $data3[] = (int) $row['D']; + $data1[] = isset($row['E']) ? (is_numeric($row['E']) ? (int)$row['E'] : 0) : 0; + $data2[] = isset($row['C']) ? (is_numeric($row['C']) ? (int)$row['C'] : 0) : 0; + $data3[] = isset($row['D']) ? (is_numeric($row['D']) ? (int)$row['D'] : 0) : 0; $times[] = $time . ($hours ? 'h' : ''); } + // Ensure all data arrays contain only numeric values + $data1 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data1); + $data2 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data2); + $data3 = array_map(function($v) { return is_numeric($v) ? (int)$v : 0; }, $data3); + + // Ensure all arrays have the same length and at least one element + if(empty($data1) || empty($data2) || empty($data3) || empty($times)) { + $data1 = array(0); + $data2 = array(0); + $data3 = array(0); + $times = array('No Data'); + } + $DataSet = new pData(); $DataSet->AddPoints($data1, 'Serie1'); $DataSet->AddPoints($data2, 'Serie2'); @@ -350,7 +520,18 @@ public function dashboardwiki() { $DataSet, new ScaleStyle(SCALE_NORMAL, new Color(127)), ($hours ? 0 : 45), 1, false, ceil(count($times) / 12) ); - $Chart->drawLineGraph($DataSet->GetData(), $DataSet->GetDataDescription()); + // Final data validation before drawing + $chartData = $DataSet->GetData(); + if (is_array($chartData)) { + foreach ($chartData as $serieKey => $serieData) { + if (is_array($serieData)) { + $chartData[$serieKey] = array_map(function($v) { + return is_numeric($v) ? (float)$v : 0.0; + }, $serieData); + } + } + } + $Chart->drawLineGraph($chartData, $DataSet->GetDataDescription()); $DataSet->removeSeries('Times'); $DataSet->removeSeriesName('Times'); diff --git a/inc/StatisticsLogger.class.php b/inc/StatisticsLogger.class.php index ecc00bd..d0b5344 100644 --- a/inc/StatisticsLogger.class.php +++ b/inc/StatisticsLogger.class.php @@ -19,13 +19,13 @@ class StatisticsLogger { public function __construct(helper_plugin_statistics $hlp) { $this->hlp = $hlp; - $this->ua_agent = trim($_SERVER['HTTP_USER_AGENT']); + $this->ua_agent = trim(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''); $bc = new StatisticsBrowscap(); $ua = $bc->getBrowser($this->ua_agent); $this->ua_name = $ua->Browser; $this->ua_type = 'browser'; - if($ua->Crawler) $this->ua_type = 'robot'; - if($ua->isSyndicationReader) $this->ua_type = 'feedreader'; + if(isset($ua->Crawler) && $ua->Crawler) $this->ua_type = 'robot'; + if(isset($ua->isSyndicationReader) && $ua->isSyndicationReader) $this->ua_type = 'feedreader'; $this->ua_version = $ua->Version; $this->ua_platform = $ua->Platform; @@ -38,7 +38,7 @@ public function __construct(helper_plugin_statistics $hlp) { * get the unique user ID */ protected function getUID() { - $uid = $_REQUEST['uid']; + $uid = isset($_REQUEST['uid']) ? $_REQUEST['uid'] : null; if(!$uid) $uid = get_doku_pref('plgstats', false); if(!$uid) $uid = session_id(); return $uid; @@ -52,7 +52,7 @@ protected function getUID() { * @return string */ protected function getSession() { - $ses = $_REQUEST['ses']; + $ses = isset($_REQUEST['ses']) ? $_REQUEST['ses'] : null; if(!$ses) $ses = get_doku_pref('plgstatsses', false); if(!$ses) $ses = session_id(); return $ses; @@ -65,7 +65,7 @@ protected function getSession() { * regardless from where the log is initiated */ public function log_lastseen() { - if(empty($_SERVER['REMOTE_USER'])) return; + if(!isset($_SERVER['REMOTE_USER']) || empty($_SERVER['REMOTE_USER'])) return; $user = addslashes($_SERVER['REMOTE_USER']); $sql = "REPLACE INTO " . $this->hlp->prefix . "lastseen @@ -118,7 +118,7 @@ public function log_groups($type, $groups) { * Will not write anything if the referer isn't a search engine */ public function log_externalsearch($referer, &$type) { - $referer = utf8_strtolower($referer); + $referer = \dokuwiki\Utf8\PhpString::strtolower($referer); include(dirname(__FILE__) . '/searchengines.php'); /** @var array $SEARCHENGINES */ @@ -128,8 +128,8 @@ public function log_externalsearch($referer, &$type) { // parse the referer $urlparts = parse_url($referer); $domain = $urlparts['host']; - $qpart = $urlparts['query']; - if(!$qpart) $qpart = $urlparts['fragment']; //google does this + $qpart = isset($urlparts['query']) ? $urlparts['query'] : ''; + if(!$qpart) $qpart = isset($urlparts['fragment']) ? $urlparts['fragment'] : ''; //google does this $params = array(); parse_str($qpart, $params); @@ -168,14 +168,19 @@ public function log_externalsearch($referer, &$type) { $query = preg_replace('/^(cache|related):[^\+]+/', '', $query); // non-search queries $query = preg_replace('/ +/', ' ', $query); // ws compact $query = trim($query); - if(!utf8_check($query)) $query = utf8_encode($query); // assume latin1 if not utf8 + if(!\dokuwiki\Utf8\Clean::isUtf8($query)) { + // Convert from latin1 to UTF-8 if not already UTF-8 + $query = mb_convert_encoding($query, 'UTF-8', 'ISO-8859-1'); + } // no query? no log if(!$query) return; // log it! - $words = explode(' ', utf8_stripspecials($query, ' ', '\._\-:\*')); - $this->log_search($_REQUEST['p'], $query, $words, $name); + // Replace deprecated utf8_stripspecials with modern equivalent + $cleaned_query = preg_replace('/[^\w\s\._\-:\*]/u', '', $query); + $words = explode(' ', $cleaned_query); + $this->log_search(isset($_REQUEST['p']) ? $_REQUEST['p'] : '', $query, $words, $name); } /** @@ -244,9 +249,9 @@ public function log_ip($ip) { WHERE ip ='" . addslashes($ip) . "' AND lastupd > DATE_SUB(CURDATE(),INTERVAL 30 DAY)"; $result = $this->hlp->runSQL($sql); - if($result[0]['ip']) return; + if($result && isset($result[0]) && isset($result[0]['ip']) && $result[0]['ip']) return; - $http = new DokuHTTPClient(); + $http = new \dokuwiki\HTTP\DokuHTTPClient(); $http->timeout = 10; $data = $http->get('http://api.hostip.info/get_html.php?ip=' . $ip); @@ -273,12 +278,12 @@ public function log_ip($ip) { * called from log.php */ public function log_outgoing() { - if(!$_REQUEST['ol']) return; + if(!isset($_REQUEST['ol']) || !$_REQUEST['ol']) return; $link = addslashes($_REQUEST['ol']); $link_md5 = md5($link); $session = addslashes($this->getSession()); - $page = addslashes($_REQUEST['p']); + $page = addslashes(isset($_REQUEST['p']) ? $_REQUEST['p'] : ''); $sql = "INSERT DELAYED INTO " . $this->hlp->prefix . "outlinks SET dt = NOW(), @@ -299,13 +304,13 @@ public function log_outgoing() { * called from log.php */ public function log_access() { - if(!$_REQUEST['p']) return; + if(!isset($_REQUEST['p']) || !$_REQUEST['p']) return; global $USERINFO; # FIXME check referer against blacklist and drop logging for bad boys // handle referer - $referer = trim($_REQUEST['r']); + $referer = trim(isset($_REQUEST['r']) ? $_REQUEST['r'] : ''); if($referer) { $ref = addslashes($referer); $ref_md5 = ($ref) ? md5($referer) : ''; @@ -330,13 +335,13 @@ public function log_access() { $page = addslashes($_REQUEST['p']); $ip = addslashes(clientIP(true)); - $sx = (int) $_REQUEST['sx']; - $sy = (int) $_REQUEST['sy']; - $vx = (int) $_REQUEST['vx']; - $vy = (int) $_REQUEST['vy']; - $js = (int) $_REQUEST['js']; + $sx = (int) (isset($_REQUEST['sx']) ? $_REQUEST['sx'] : 0); + $sy = (int) (isset($_REQUEST['sy']) ? $_REQUEST['sy'] : 0); + $vx = (int) (isset($_REQUEST['vx']) ? $_REQUEST['vx'] : 0); + $vy = (int) (isset($_REQUEST['vy']) ? $_REQUEST['vy'] : 0); + $js = (int) (isset($_REQUEST['js']) ? $_REQUEST['js'] : 0); $uid = addslashes($this->uid); - $user = addslashes($_SERVER['REMOTE_USER']); + $user = addslashes(isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] : ''); $session = addslashes($this->getSession()); $sql = "INSERT DELAYED INTO " . $this->hlp->prefix . "access @@ -410,7 +415,7 @@ public function log_media($media, $mime, $inline, $size) { $ip = addslashes(clientIP(true)); $uid = addslashes($this->uid); - $user = addslashes($_SERVER['REMOTE_USER']); + $user = addslashes(isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] : ''); $session = addslashes($this->getSession()); $sql = "INSERT DELAYED INTO " . $this->hlp->prefix . "media @@ -433,7 +438,10 @@ public function log_media($media, $mime, $inline, $size) { $ok = $this->hlp->runSQL($sql); if(is_null($ok)) { global $MSG; - dbglog($MSG); + // Using modern logger instead of deprecated dbglog + if (class_exists('\dokuwiki\Logger')) { + \dokuwiki\Logger::debug($MSG); + } } } @@ -444,7 +452,7 @@ public function log_edit($page, $type) { global $USERINFO; $ip = addslashes(clientIP(true)); - $user = addslashes($_SERVER['REMOTE_USER']); + $user = addslashes(isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] : ''); $session = addslashes($this->getSession()); $uid = addslashes($this->uid); $page = addslashes($page); @@ -470,7 +478,7 @@ public function log_edit($page, $type) { * Log login/logoffs and user creations */ public function log_login($type, $user = '') { - if(!$user) $user = $_SERVER['REMOTE_USER']; + if(!$user) $user = isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] : ''; $ip = addslashes(clientIP(true)); $user = addslashes($user); diff --git a/inc/StatisticsQuery.class.php b/inc/StatisticsQuery.class.php index 774b663..cda4de1 100644 --- a/inc/StatisticsQuery.class.php +++ b/inc/StatisticsQuery.class.php @@ -37,10 +37,10 @@ public function aggregate($tlimit) { AND ua_type = 'browser'"; $result = $this->hlp->runSQL($sql); - $data['users'] = max($result[0]['users'] - 1, 0); // subtract empty user - $data['sessions'] = $result[0]['sessions']; - $data['pageviews'] = $result[0]['views']; - $data['visitors'] = $result[0]['visitors']; + $data['users'] = ($result && isset($result[0])) ? max($result[0]['users'] - 1, 0) : 0; // subtract empty user + $data['sessions'] = ($result && isset($result[0])) ? $result[0]['sessions'] : 0; + $data['pageviews'] = ($result && isset($result[0])) ? $result[0]['views'] : 0; + $data['visitors'] = ($result && isset($result[0])) ? $result[0]['visitors'] : 0; // calculate bounce rate if($data['sessions']) { @@ -49,7 +49,7 @@ public function aggregate($tlimit) { WHERE $tlimit AND views = 1"; $result = $this->hlp->runSQL($sql); - $data['bouncerate'] = $result[0]['cnt'] * 100 / $data['sessions']; + $data['bouncerate'] = ($result && isset($result[0])) ? $result[0]['cnt'] * 100 / $data['sessions'] : 0; // new visitors $result = "SELECT COUNT(*) as cnt @@ -62,7 +62,7 @@ public function aggregate($tlimit) { AND B.uid = B.uid )"; $result = $this->hlp->runSQL($sql); - $data['newvisitors'] = $result[0]['cnt'] * 100 / $data['sessions']; + $data['newvisitors'] = ($result && isset($result[0])) ? $result[0]['cnt'] * 100 / $data['sessions'] : 0; } // calculate avg. number of views per session @@ -70,7 +70,7 @@ public function aggregate($tlimit) { FROM " . $this->hlp->prefix . "session as A WHERE $tlimit"; $result = $this->hlp->runSQL($sql); - $data['avgpages'] = $result[0]['cnt']; + $data['avgpages'] = ($result && isset($result[0])) ? $result[0]['cnt'] : 0; /* not used currently $sql = "SELECT COUNT(id) as robots @@ -78,7 +78,7 @@ public function aggregate($tlimit) { WHERE $tlimit AND ua_type = 'robot'"; $result = $this->hlp->runSQL($sql); - $data['robots'] = $result[0]['robots']; + $data['robots'] = ($result && isset($result[0])) ? $result[0]['robots'] : 0; */ // average time spent on the site @@ -88,7 +88,7 @@ public function aggregate($tlimit) { AND dt != end AND DATE(dt) = DATE(end)"; $result = $this->hlp->runSQL($sql); - $data['timespent'] = $result[0]['time']; + $data['timespent'] = ($result && isset($result[0])) ? $result[0]['time'] : 0; // logins $sql = "SELECT COUNT(*) as logins @@ -96,7 +96,7 @@ public function aggregate($tlimit) { WHERE $tlimit AND (type = 'l' OR type = 'p')"; $result = $this->hlp->runSQL($sql); - $data['logins'] = $result[0]['logins']; + $data['logins'] = ($result && isset($result[0])) ? $result[0]['logins'] : 0; // registrations $sql = "SELECT COUNT(*) as registrations @@ -104,14 +104,14 @@ public function aggregate($tlimit) { WHERE $tlimit AND type = 'C'"; $result = $this->hlp->runSQL($sql); - $data['registrations'] = $result[0]['registrations']; + $data['registrations'] = ($result && isset($result[0])) ? $result[0]['registrations'] : 0; // current users $sql = "SELECT COUNT(*) as current FROM ". $this->hlp->prefix . "lastseen WHERE `dt` >= NOW() - INTERVAL 10 MINUTE"; $result = $this->hlp->runSQL($sql); - $data['current'] = $result[0]['current']; + $data['current'] = ($result && isset($result[0])) ? $result[0]['current'] : 0; return $data; } diff --git a/inc/pchart/PieChart.php b/inc/pchart/PieChart.php index 4506118..f220f6a 100644 --- a/inc/pchart/PieChart.php +++ b/inc/pchart/PieChart.php @@ -178,8 +178,17 @@ public function drawBasicPieGraph($Data, $DataDescription, $XPos, $YPos, $Radius throw new Exception("Pie chart can only accept one serie of data."); /** @todo Proper exception type needed here */ - $SpliceRatio = 360 / $PieSum; - $SplicePercent = 100 / $PieSum; + /* Check for division by zero */ + if($PieSum == 0) { + // When all data values are zero, create a single slice representing "No Data" + $SpliceRatio = 360; + $SplicePercent = 100; + // Reset iValues to have a single 1 value to represent the full circle + $iValues = array(1); + } else { + $SpliceRatio = 360 / $PieSum; + $SplicePercent = 100 / $PieSum; + } /* Calculate all polygons */ $Angle = 0; diff --git a/lang/zh-tw/lang.php b/lang/zh-tw/lang.php new file mode 100644 index 0000000..0153cda --- /dev/null +++ b/lang/zh-tw/lang.php @@ -0,0 +1,112 @@ + + */ + +// 用於管理插件,顯示在管理選單中的提示 +$lang['menu'] = '存取與使用統計'; + +$lang['more'] = '更多'; +$lang['prev'] = '上一頁'; +$lang['next'] = '下一頁'; + +// 時間選擇 +$lang['time_select'] = '選擇時間範圍:'; +$lang['time_today'] = '今天'; +$lang['time_last1'] = '昨天'; +$lang['time_last7'] = '最近7天'; +$lang['time_last30'] = '最近30天'; +$lang['time_go'] = '確定'; +$lang['days'] = '天'; +$lang['weeks'] = '週'; +$lang['months'] = '月'; + +// 不同頁面 +$lang['dashboard'] = '儀表板'; +$lang['page'] = '頁面'; +$lang['edits'] = '編輯'; +$lang['images'] = '圖片'; +$lang['downloads'] = '下載'; +$lang['referer'] = '來源連結'; +$lang['newreferer'] = '新增來源連結'; +$lang['outlinks'] = '外部連結'; +$lang['searchphrases'] = '外部搜尋詞組'; +$lang['searchwords'] = '外部搜尋關鍵字'; +$lang['internalsearchphrases'] = '內部搜尋詞組'; +$lang['internalsearchwords'] = '內部搜尋關鍵字'; +$lang['searchengines'] = '搜尋引擎'; +$lang['browsers'] = '瀏覽器'; +$lang['os'] = '作業系統'; +$lang['countries'] = '國家'; +$lang['resolution'] = '螢幕大小'; +$lang['viewport'] = '瀏覽器視窗'; +$lang['seenusers'] = '活躍用戶'; +$lang['history'] = '成長歷史'; +$lang['topuser'] = '熱門用戶'; +$lang['topeditor'] = '熱門編輯者'; +$lang['topgroup'] = '熱門群組'; +$lang['topgroupedit'] = '熱門編輯群組'; +$lang['content'] = '(內容)'; +$lang['users'] = '(用戶和群組)'; +$lang['links'] = '(連結)'; +$lang['search'] = '(搜尋)'; +$lang['technology'] = '(技術)'; +$lang['trafficsum'] = '%s 個請求產生了 %s 的流量。'; + + +// 介紹文字 +$lang['intro_dashboard'] = '此頁面為您提供在所選時間範圍內,您的維基發生了什麼的快速概覽。
欲獲得詳細資訊和圖表,請從目錄中選擇一個主題。'; +$lang['intro_page'] = '以下是在所選時間範圍內最常被瀏覽的維基頁面 — 您的熱門內容。'; +$lang['intro_edits'] = '以下是在所選時間範圍內最常被編輯的維基頁面 — 這是當前活動發生的地方。'; +$lang['intro_images'] = '以下是您維基中最常顯示的本地圖片。第三列顯示了每個項目傳輸的總位元組數。'; +$lang['intro_downloads'] = '以下是您維基中最常被下載的本地媒體項目。第三列顯示了每個項目傳輸的總位元組數。'; +$lang['intro_referer'] = '在所有 %d 次外部訪問中,%d 次(%.1f%%)是直接(或來自書籤)訪問,%d 次(%.1f%%)來自搜尋引擎,%d 次(%.1f%%)是通過其他頁面的連結而來。
這些其他頁面列在下方。'; + +$lang['intro_newreferer'] = '以下的來源連結在所選時間範圍內首次被記錄,且之前從未見過。'; +$lang['intro_outlinks'] = '以下是您維基中最常被點擊的外部網站連結。'; +$lang['intro_searchengines'] = '以下是用戶用來尋找您維基的搜尋引擎。'; +$lang['intro_searchphrases'] = '以下是人們在找到您的維基時使用的確切搜尋詞組。'; +$lang['intro_searchwords'] = '以下是人們在找到您的維基時最常使用的搜尋詞。'; +$lang['intro_internalsearchphrases'] = '以下是人們在您的維基內部搜尋時使用的確切詞組。'; +$lang['intro_internalsearchwords'] = '以下是人們在您的維基內部搜尋時最常使用的詞語。'; +$lang['intro_browsers'] = '以下是您的用戶最常使用的瀏覽器。'; +$lang['intro_os'] = '以下是您的用戶最常使用的平台。'; +$lang['intro_countries'] = '以下是您的用戶來源國家。請注意,將IP地址解析為國家並非完全精確的科學方法。'; +$lang['intro_resolution'] = '此頁面提供有關您用戶的螢幕大小(解析度)的一些資訊。這是他們擁有的螢幕空間,而非瀏覽器顯示區域可用的空間。有關後者,請參閱瀏覽器視窗頁面。所有值都四捨五入到100像素,圖表僅顯示前100個值。'; +$lang['intro_viewport'] = '以下是您的用戶瀏覽器用於渲染您維基的區域大小。所有值都四捨五入到100像素,圖表僅顯示前100個值。'; +$lang['intro_seenusers'] = '這是用戶最後在維基中出現的時間列表,按最後出現日期排序。這與所選時間範圍無關。'; +$lang['intro_history'] = '這些圖表讓您了解在指定時間範圍內,您的維基在條目數量和大小方面的成長情況。請注意,此圖表需要至少數天的時間範圍。'; +$lang['intro_topuser'] = '此頁面顯示在所選時間範圍內,哪些已登入的用戶瀏覽了最多的維基頁面。'; +$lang['intro_topeditor'] = '此頁面顯示在所選時間範圍內,哪些已登入的用戶進行了最多的編輯。'; +$lang['intro_topgroup'] = '以下是在所選時間範圍內,瀏覽最多維基頁面的已登入用戶所屬的群組。請注意,當一個用戶屬於多個群組時,其所有群組都會被計算。'; +$lang['intro_topgroupedit'] = '以下是在所選時間範圍內,進行最多編輯的已登入用戶所屬的群組。請注意,當一個用戶屬於多個群組時,其所有群組都會被計算。'; + +// 儀表板項目 +$lang['dash_pageviews'] = '%d 次頁面瀏覽'; +$lang['dash_sessions'] = '%d 次訪問(會話)'; +$lang['dash_visitors'] = '%d 位獨立訪客'; +$lang['dash_users'] = '%d 位已登入用戶'; +$lang['dash_logins'] = '%d 次用戶登入'; +$lang['dash_registrations'] = '%s 位新註冊用戶'; +$lang['dash_current'] = '%d 位當前已登入用戶'; +$lang['dash_bouncerate'] = '%.1f%% 跳出率'; +$lang['dash_timespent'] = '平均每次會話停留 %.2f 分鐘'; +$lang['dash_avgpages'] = '平均每次會話瀏覽 %.2f 頁'; +$lang['dash_newvisitors'] = '%.1f%% 新訪客'; + +$lang['dash_mostpopular'] = '最受歡迎的頁面'; +$lang['dash_newincoming'] = '熱門新增來源連結'; +$lang['dash_topsearch'] = '熱門搜尋詞組'; + +// 圖表標籤 +$lang['graph_edits'] = 'Page Edits'; +$lang['graph_creates'] = 'Page Creations'; +$lang['graph_deletions'] = 'Page Deletions'; +$lang['graph_views'] = 'Page Views'; +$lang['graph_sessions'] = 'Visits'; +$lang['graph_visitors'] = 'Visitors'; +$lang['graph_page_count'] = 'Pages'; +$lang['graph_page_size'] = 'Pagessize (MB)'; +$lang['graph_media_count'] = 'Media Items'; +$lang['graph_media_size'] = 'Media Item Size (MB)'; diff --git a/lang/zh-tw/settings.php b/lang/zh-tw/settings.php new file mode 100644 index 0000000..7dd4b3e --- /dev/null +++ b/lang/zh-tw/settings.php @@ -0,0 +1,8 @@ +Logger()->log_access(); $plugin->Logger()->log_session(1);