Browse Source

默认内置轻量级统计系统

tags/6.1.7
tianya 2 years ago
parent
commit
fa9f250662
23 changed files with 4289 additions and 144 deletions
  1. +0
    -3
      src/admin/index.php
  2. +13
    -90
      src/admin/index_body.php
  3. +0
    -19
      src/admin/templets/index.htm
  4. +198
    -23
      src/admin/templets/index_body.htm
  5. +28
    -0
      src/apps/statistics.php
  6. +1
    -1
      src/install/index.php
  7. +13
    -0
      src/static/js/chart.min.js
  8. +2
    -1
      src/system/archive/archives.class.php
  9. +2
    -1
      src/system/archive/listview.class.php
  10. +3
    -1
      src/system/archive/searchview.class.php
  11. +2
    -1
      src/system/archive/sglistview.class.php
  12. +3
    -1
      src/system/archive/taglist.class.php
  13. +1
    -1
      src/system/common.inc.php
  14. +416
    -0
      src/system/libraries/agent.class.php
  15. +185
    -0
      src/system/libraries/crawlerdetect.class.php
  16. +30
    -0
      src/system/libraries/fixtures/abstractprovider.php
  17. +1412
    -0
      src/system/libraries/fixtures/crawlers.php
  18. +71
    -0
      src/system/libraries/fixtures/exclusions.php
  19. +37
    -0
      src/system/libraries/fixtures/headers.php
  20. +1693
    -0
      src/system/libraries/mobiledetect.class.php
  21. +150
    -0
      src/system/libraries/statistics.class.php
  22. +28
    -0
      src/system/taglib/statistics.lib.php
  23. +1
    -2
      src/theme/templets/footer.htm

+ 0
- 3
src/admin/index.php View File

@@ -16,9 +16,6 @@ if (preg_match("#PHP (.*) Development Server#", $_SERVER['SERVER_SOFTWARE'])) {
}
require_once(dirname(__FILE__)."/config.php");
require_once(DEDEINC.'/dedetag.class.php');
$defaultIcoFile = DEDEDATA.'/admin/quickmenu.txt';
$myIcoFile = DEDEDATA.'/admin/quickmenu-'.$cuserLogin->getUserID().'.txt';
if (!file_exists($myIcoFile)) $myIcoFile = $defaultIcoFile;
require(DEDEADMIN.'/inc/inc_menu_map.php');
include(DEDEADMIN.'/templets/index.htm');
exit();

+ 13
- 90
src/admin/index_body.php View File

@@ -11,9 +11,6 @@
require(dirname(__FILE__).'/config.php');
require(DEDEINC.'/image.func.php');
require(DEDEINC.'/dedetag.class.php');
$defaultIcoFile = DEDEDATA.'/admin/quickmenu.txt';
$myIcoFile = DEDEDATA.'/admin/quickmenu-'.$cuserLogin->getUserID().'.txt';
if (!file_exists($myIcoFile)) $myIcoFile = $defaultIcoFile;
//默认主页
if (empty($dopost)) {
require(DEDEINC.'/inc/inc_fun_funAdmin.php');
@@ -30,92 +27,6 @@ if (empty($dopost)) {
include DedeInclude('templets/index_body.htm');
exit();
}
/*-----------------------
增加新项
function _AddNew() { }
-------------------------*/
else if ($dopost == 'addnew') {
if (empty($link) || empty($title)) {
ShowMsg("链接网址或标题不能为空", "-1");
exit();
}
$fp = fopen($myIcoFile, 'r');
$oldct = trim(fread($fp, filesize($myIcoFile)));
fclose($fp);
$link = preg_replace("#['\"]#", '`', $link);
$title = preg_replace("#['\"]#", '`', $title);
$ico = preg_replace("#['\"]#", '`', $ico);
$oldct .= "\r\n<menu:item ico=\"{$ico}\" link=\"{$link}\" title=\"{$title}\">";
$myIcoFileTrue = DEDEDATA.'/admin/quickmenu-'.$cuserLogin->getUserID().'.txt';
$fp = fopen($myIcoFileTrue, 'w');
fwrite($fp, $oldct);
fclose($fp);
ShowMsg("成功增加一个项目", "index_body.php?".time());
exit();
}
/*---------------------------
保存修改的项
function _EditSave() { }
----------------------------*/
else if ($dopost == 'editsave') {
$quickmenu = stripslashes($quickmenu);
$myIcoFileTrue = DEDEDATA.'/admin/quickmenu-'.$cuserLogin->getUserID().'.txt';
$fp = fopen($myIcoFileTrue, 'w');
fwrite($fp, $quickmenu);
fclose($fp);
ShowMsg("成功修改快捷操作项目", "index_body.php?".time());
exit();
}
/*---------------------------
保存修改的项
function _EditSave() { }
----------------------------*/
else if ($dopost == 'movesave') {
$movedata = str_replace('\\', "", $sortorder);
$movedata = json_decode($movedata, TRUE);
$movedata = serialize($movedata);
$myIcoFileTrue = DEDEDATA.'/admin/move-'.$cuserLogin->getUserID().'.txt';
$fp = fopen($myIcoFileTrue, 'w');
fwrite($fp, $movedata);
fclose($fp);
}
/*-----------------------------
显示修改表单
function _EditShow() { }
-----------------------------*/
else if ($dopost == 'editshow') {
$fp = fopen($myIcoFile, 'r');
$oldct = trim(fread($fp, filesize($myIcoFile)));
fclose($fp);
?>
<form name="editform" action="index_body.php" method="post">
<input type="hidden" name="dopost" value="editsave">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td height="30" background="images/tbg.gif">
<div style="float:left;margin-left:10px">修改快捷操作项</div>
<div style="float:right;padding:2px 10px 0 0">
<a href="javascript:CloseTab('editTab')"><img src="images/close.gif"></a>
</div>
</td>
</tr>
<tr>
<td style="height:10px;border-top:1px solid #8DA659"></td>
</tr>
<tr>
<td align="center"><textarea name="quickmenu" rows="10" cols="50"><?php echo $oldct; ?></textarea></td>
</tr>
<tr>
<td height="36" align="center">
<input type="submit" name="Submit" value="保存项目" class="np coolbg" style="width:80px;cursor:pointer">&nbsp;
<input type="reset" name="reset" value="重设" class="np coolbg" style="width:50px;cursor:pointer">
</td>
</tr>
</table>
</form>
<?php
exit();
}
/*---------------------------------
载入右边内容
function _getRightSide() { }
@@ -247,5 +158,17 @@ exit;
));
}
}
}
} elseif ($dopost == 'get_statistics') {
require_once(DEDEINC."/libraries/statistics.class.php");
//获取统计信息
$sdate = empty($sdate) ? 0 : intval($sdate);
$stat = new DedeStatistics;
$rs = $stat->GetInfoByDate($sdate);
echo json_encode(array(
"code" => 200,
"msg" => "",
"result" => $rs,
));
exit;
}
?>

+ 0
- 19
src/admin/templets/index.htm View File

@@ -90,25 +90,6 @@
<iframe src="index_body.php" id="main" name="main" frameborder="0"></iframe>
</div>
</div>
<div class="qucikmenu" id="qucikmenu">
<ul>
<?php
$dtp = new DedeTagparse();
$dtp->SetNameSpace('menu','<','>');
$dtp->LoadTemplet($myIcoFile);
if(is_array($dtp->CTags))
{
foreach($dtp->CTags as $ctag)
{
$title = $ctag->GetAtt('title');
$ico = $ctag->GetAtt('ico');
$link = $ctag->GetAtt('link');
echo "<li><a target='main' href='{$link}'>{$title}</a></li>";
}
}
?>
</ul>
</div>
<script>
function JumpFrame(url1, url2) {
jQuery('#menufra').get(0).src = url1;


+ 198
- 23
src/admin/templets/index_body.htm View File

@@ -12,18 +12,74 @@
<script src="../static/js/jquery.js"></script>
<script src="../static/js/bootstrap.bundle.js"></script>
<script src="../static/js/webajax.js"></script>
<script src="../static/js/chart.min.js"></script>
<script src="js/indexbody.js"></script>
<script src="js/main.js"></script>
<style>
.row{display:flex;flex-wrap:wrap}
.row>[class*='col-']{display:flex;flex-direction:column}
.table{margin-bottom:0}
.stattable{width: 100%;}
table.stattable td {
padding: 0 5px;
height: 25px;
line-height: 25px;
border-bottom: 1px solid #f0f0f0;
font-size: 12px;
text-align: right;
}
table.stattable td.today {
font-size: 12px;
color: #000;
height: 25px;
line-height: 25px;
font-weight: 700;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div id="__testEvn" class="col-md-12"></div>
<div class="col-md-12 mt-3">
<div class="card">
<div class="card-header">流量统计 <a title="点击查看流量统计图" href="#statChart"><i class="fa fa-line-chart" aria-hidden="true"></i></a></div>
<div class="card-body">
<table class="stattable">
<tbody>
<tr class="title">
<td width="10%"></td>
<td width="20%">浏览次数(PV)</td>
<td width="20%">独立访客(UV)</td>
<td width="20%">IP</td>
<td width="18%" >访问次数</td>
</tr>
<tr class="bg-white">
<td class="today">今日</td>
<td class="today" id="today_pv">...</td>
<td class="today" id="today_uv">...</td>
<td class="today" id="today_ip">...</td>
<td class="today" id="today_vv">...</td>
</tr>
<tr class="bg-white">
<td class="">昨日</td>
<td id="yestoday_pv">...</td>
<td id="yestoday_uv">...</td>
<td id="yestoday_ip">...</td>
<td id="yestoday_vv">...</td>
</tr>
<tr class="bg-white grey9 hide" style="display: table-row;">
<td class="">历史累计</td>
<td id="total_pv">...</td>
<td id="total_uv">...</td>
<td id="total_ip">...</td>
<td id="total_vv">...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 mt-3 updatenews">
<div class="card">
<div class="card-header">更新消息</div>
@@ -44,27 +100,6 @@
<div class="card-body" id="_systeminfo">正在载入</div>
</div>
</div>
<div class="col-md-6 mt-3">
<div class="card">
<div class="card-header">快捷操作<span class="float-right"><a href="javascript:AddNew()" class="btn btn-success btn-sm">新增</a><a href="javascript:ListAll()" class="btn btn-success btn-sm">管理</a></span></div>
<div class="card-body">
<ul class="nav">
<?php
$dtp = new DedeTagparse();
$dtp->SetNameSpace('menu','<','>');
$dtp->LoadTemplet($myIcoFile);
if(is_array($dtp->CTags)){
foreach($dtp->CTags as $ctag){
$title = $ctag->GetAtt('title');
$link = $ctag->GetAtt('link');
echo "<a href='{$link}' class='btn btn-success btn-sm'>{$title}</a>";
}
}
?>
</ul>
</div>
</div>
</div>
<div class="col-md-6 mt-3">
<div class="card">
<div class="card-header">信息统计</div>
@@ -138,9 +173,17 @@
</div>
</div>
</div>
<div class="col-md-6 mt-3">
<div class="card">
<div class="card-header"><a name="statChart">流量统计图</a></div>
<div class="card-body">
<canvas id="statChart" style="height: 260px;"></canvas>
</div>
</div>
</div>
</div>
</div>
<div class="footer">
<div class="footer mt-2">
<div id="loaddiv" style="display:none">
<p align="center"><img src="images/loadinglit.gif">请稍后,正在下载更新文件列表</p>
</div>
@@ -188,6 +231,21 @@
});
</script>
<script>
Date.prototype.Format = function (fmt) { //author: meizz
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
var dedebizInfo;
function ViewDedeBIZ(){
console.log(dedebizInfo);
@@ -280,11 +338,128 @@
}
});
}
function LoadStat() {
$.get("index_body.php?dopost=get_statistics", function(data){
let rsp = JSON.parse(data);
if (rsp.code == 200) {
var tpv = parseInt(rsp.result.pv);
var tuv = parseInt(rsp.result.uv);
var tip = parseInt(rsp.result.ip);
var tvv = parseInt(rsp.result.vv);
$("#today_pv").html(tpv);
$("#today_uv").html(tuv);
$("#today_ip").html(tip);
$("#today_vv").html(tvv);

$.get("index_body.php?dopost=get_statistics&sdate=-1", function(data){
let rsp = JSON.parse(data);
if (rsp.code == 200) {
$("#total_pv").html(parseInt(rsp.result.pv)+tpv);
$("#total_uv").html(parseInt(rsp.result.uv)+tuv);
$("#total_ip").html(parseInt(rsp.result.ip)+tip);
$("#total_vv").html(parseInt(rsp.result.vv)+tvv);
}
});
}
});

var d = new Date();
d.setDate(d.getDate() - 1);
var s = d.Format("yyyy-MM-dd");
s = s.replaceAll("-","");
$.get("index_body.php?dopost=get_statistics&sdate="+s, function(data){
let rsp = JSON.parse(data);
if (rsp.code == 200) {
$("#yestoday_pv").html(rsp.result.pv);
$("#yestoday_uv").html(rsp.result.uv);
$("#yestoday_ip").html(rsp.result.ip);
$("#yestoday_vv").html(rsp.result.vv);
}
});
}

async function LoadStatChart() {
const ctx = document.getElementById('statChart').getContext('2d');
let labels = [];
let pvs = [];
let ips = [];
let uvs = [];
let vvs = [];
for (let i = 15; i > 0; i--) {
var d = new Date();
d.setDate(d.getDate() - i);
var s = d.Format("yyyy-MM-dd");
labels.push(d.Format("MM-dd"));
s = s.replaceAll("-","");
let resp = await fetch("index_body.php?dopost=get_statistics&sdate="+s);
let data = await resp.json();
if (data.code == 200) {
pvs.push(typeof data.result.pv=="undefined"? 0 : data.result.pv);
ips.push(typeof data.result.ip=="undefined"? 0 : data.result.ip);
uvs.push(typeof data.result.uv=="undefined"? 0 : data.result.uv);
vvs.push(typeof data.result.vv=="undefined"? 0 : data.result.vv);
}
}

console.log(pvs);
console.log(ips);
console.log(uvs);
console.log(vvs);

const myChart = new Chart(ctx, {
type: 'line',
options: {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: '<?php echo $cfg_webname; ?>流量统计图'
}
}
},
data: {
labels: labels,
datasets: [{
label: 'IP',
data: ips,
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor :'rgba(255, 99, 132, 0.2)',
borderWidth: 1
},
{
label: 'PV',
data: pvs,
borderColor: 'rgba(54, 162, 235, 1)',
backgroundColor :'rgba(54, 162, 235, 0.2)',
borderWidth: 1
},{
label: 'UV',
data: uvs,
borderColor: 'rgba(255, 206, 86, 1)',
backgroundColor: 'rgba(255, 206, 86, 0.2)',
borderWidth: 1
},{
label: '访问次数',
data: vvs,
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderWidth: 1
}
]
},
});
}

$(document).ready(function(){
LoadServer();
LoadStat();
LoadStatChart();
setInterval(function(){
LoadServer();
}, 5000)
}, 60000)
});
</script>
</body>

+ 28
- 0
src/apps/statistics.php View File

@@ -0,0 +1,28 @@
<?php
/**
*
* 统计程序
*
* @version $Id: statistics.php$
* @package DedeBIZ.Site
* @copyright Copyright (c) 2022, DedeBIZ.COM
* @license https://www.dedebiz.com/license
* @link https://www.dedebiz.com
*/
require_once(dirname(__FILE__)."/../system/common.inc.php");
require_once(DEDEINC."/libraries/statistics.class.php");

if (empty($dopost)) $dopost = '';
$stat = new DedeStatistics;

if ($dopost == "stat") {
$rs = $stat->Record();
$result = array(
"code" => 200,
"data" => "success",
);
echo json_encode($result);
exit;
}
$v = $stat->GetStat();
echo $v;

+ 1
- 1
src/install/index.php View File

@@ -17,7 +17,7 @@ if(file_exists(INSLOCKFILE))

$verMsg = 'V6';
$dfDbname = 'DedeBIZ';
$cfg_version_detail = '6.1.6'; //详细版本号
$cfg_version_detail = '6.1.7beta'; //详细版本号
$errmsg = '';
if (version_compare(PHP_VERSION, '8.0.0', '>=')) {
mysqli_report(MYSQLI_REPORT_OFF);


+ 13
- 0
src/static/js/chart.min.js
File diff suppressed because it is too large
View File


+ 2
- 1
src/system/archive/archives.class.php View File

@@ -50,13 +50,14 @@ class Archives
*/
function __construct($aid)
{
global $dsql;
global $dsql,$envs;
$this->IsError = FALSE;
$this->ArcID = $aid;
$this->PreNext = array();
$this->dsql = $dsql;
$query = "SELECT channel,typeid FROM `#@__arctiny` WHERE id='$aid' ";
$arr = $this->dsql->GetOne($query);
$envs['url_type'] = 2;
if (!is_array($arr)) {
$this->IsError = TRUE;
} else {


+ 2
- 1
src/system/archive/listview.class.php View File

@@ -50,7 +50,8 @@ class ListView
*/
function __construct($typeid, $uppage = 1)
{
global $dsql;
global $dsql,$envs;
$envs['url_type'] = 1;
$this->TypeID = $typeid;
$this->dsql = &$dsql;
$this->CrossID = '';


+ 3
- 1
src/system/archive/searchview.class.php View File

@@ -76,7 +76,7 @@ class SearchView
$kwtype = 1,
$mid = 0
) {
global $cfg_search_max, $cfg_search_maxrc, $cfg_search_time;
global $cfg_search_max, $cfg_search_maxrc, $cfg_search_time,$envs;
if (empty($upagesize)) {
$upagesize = 10;
}
@@ -131,6 +131,8 @@ class SearchView
if ($this->PageNo == 1) {
$this->dsql->ExecuteNoneQuery("UPDATE `#@__search_keywords` SET result='".$this->TotalResult."' WHERE keyword='".addslashes($keyword)."'; ");
}
$envs['url_type'] = 3;
$envs['value'] = $keyword;
}
//php4构造函数
function SearchView(


+ 2
- 1
src/system/archive/sglistview.class.php View File

@@ -51,7 +51,8 @@ class SgListView
*/
function __construct($typeid, $searchArr = array())
{
global $dsql;
global $dsql,$envs;
$envs['url_type'] = 1;
$this->TypeID = $typeid;
$this->dsql = $dsql;
$this->CrossID = '';


+ 3
- 1
src/system/archive/taglist.class.php View File

@@ -45,7 +45,7 @@ class TagList
*/
function __construct($keyword, $templet)
{
global $dsql;
global $dsql,$envs;
$this->Templet = $templet;
$this->Tag = $keyword;
$this->dsql = $dsql;
@@ -90,6 +90,8 @@ class TagList
}
$this->dtp->LoadTemplate($tempfile);
$this->TempletsFile = preg_replace("#^".$GLOBALS['cfg_basedir']."#", '', $tempfile);
$envs['url_type'] = 4;
$envs['value'] = $keyword;
}
function TagPinyinExists($tag)
{


+ 1
- 1
src/system/common.inc.php View File

@@ -192,7 +192,7 @@ $cfg_soft_dir = $cfg_medias_dir.'/soft';
$cfg_other_medias = $cfg_medias_dir.'/media';
//软件摘要信息,****请不要删除本项**** 否则系统无法正确接收系统漏洞或升级信息
$cfg_version = 'V6';
$cfg_version_detail = '6.1.6'; //详细版本号
$cfg_version_detail = '6.1.7beta'; //详细版本号
$cfg_soft_lang = 'utf-8';
$cfg_soft_public = 'base';
$cfg_softname = '织梦内容管理系统';


+ 416
- 0
src/system/libraries/agent.class.php View File

@@ -0,0 +1,416 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
require_once(DEDEINC."/libraries/crawlerdetect.class.php");
require_once(DEDEINC."/libraries/mobiledetect.class.php");

// copyright https://github.com/jenssegers/agent

use BadMethodCallException;

class Agent extends Mobile_Detect
{
/**
* List of desktop devices.
* @var array
*/
protected static $desktopDevices = [
'Macintosh' => 'Macintosh',
];

/**
* List of additional operating systems.
* @var array
*/
protected static $additionalOperatingSystems = [
'Windows' => 'Windows',
'Windows NT' => 'Windows NT',
'OS X' => 'Mac OS X',
'Debian' => 'Debian',
'Ubuntu' => 'Ubuntu',
'Macintosh' => 'PPC',
'OpenBSD' => 'OpenBSD',
'Linux' => 'Linux',
'ChromeOS' => 'CrOS',
];

/**
* List of additional browsers.
* @var array
*/
protected static $additionalBrowsers = [
'Opera Mini' => 'Opera Mini',
'Opera' => 'Opera|OPR',
'Edge' => 'Edge|Edg',
'Coc Coc' => 'coc_coc_browser',
'UCBrowser' => 'UCBrowser',
'Vivaldi' => 'Vivaldi',
'Chrome' => 'Chrome',
'Firefox' => 'Firefox',
'Safari' => 'Safari',
'IE' => 'MSIE|IEMobile|MSIEMobile|Trident/[.0-9]+',
'Netscape' => 'Netscape',
'Mozilla' => 'Mozilla',
'WeChat' => 'MicroMessenger',
];

/**
* List of additional properties.
* @var array
*/
protected static $additionalProperties = [
// Operating systems
'Windows' => 'Windows NT [VER]',
'Windows NT' => 'Windows NT [VER]',
'OS X' => 'OS X [VER]',
'BlackBerryOS' => ['BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'],
'AndroidOS' => 'Android [VER]',
'ChromeOS' => 'CrOS x86_64 [VER]',

// Browsers
'Opera Mini' => 'Opera Mini/[VER]',
'Opera' => [' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]', 'Opera [VER]'],
'Netscape' => 'Netscape/[VER]',
'Mozilla' => 'rv:[VER]',
'IE' => ['IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'rv:[VER]'],
'Edge' => ['Edge/[VER]', 'Edg/[VER]'],
'Vivaldi' => 'Vivaldi/[VER]',
'Coc Coc' => 'coc_coc_browser/[VER]',
];

/**
* @var CrawlerDetect
*/
protected static $crawlerDetect;

/**
* Get all detection rules. These rules include the additional
* platforms and browsers and utilities.
* @return array
*/
public static function getDetectionRulesExtended()
{
static $rules;

if (!$rules) {
$rules = static::mergeRules(
static::$desktopDevices, // NEW
static::$phoneDevices,
static::$tabletDevices,
static::$operatingSystems,
static::$additionalOperatingSystems, // NEW
static::$browsers,
static::$additionalBrowsers, // NEW
static::$utilities
);
}

return $rules;
}

public function getRules()
{
if ($this->detectionType === static::DETECTION_TYPE_EXTENDED) {
return static::getDetectionRulesExtended();
}

return static::getMobileDetectionRules();
}

/**
* @return CrawlerDetect
*/
public function getCrawlerDetect()
{
if (static::$crawlerDetect === null) {
static::$crawlerDetect = new CrawlerDetect();
}

return static::$crawlerDetect;
}

public static function getBrowsers()
{
return static::mergeRules(
static::$additionalBrowsers,
static::$browsers
);
}

public static function getOperatingSystems()
{
return static::mergeRules(
static::$operatingSystems,
static::$additionalOperatingSystems
);
}

public static function getPlatforms()
{
return static::mergeRules(
static::$operatingSystems,
static::$additionalOperatingSystems
);
}

public static function getDesktopDevices()
{
return static::$desktopDevices;
}

public static function getProperties()
{
return static::mergeRules(
static::$additionalProperties,
static::$properties
);
}

/**
* Get accept languages.
* @param string $acceptLanguage
* @return array
*/
public function languages($acceptLanguage = null)
{
if ($acceptLanguage === null) {
$acceptLanguage = $this->getHttpHeader('HTTP_ACCEPT_LANGUAGE');
}

if (!$acceptLanguage) {
return [];
}

$languages = [];

// Parse accept language string.
foreach (explode(',', $acceptLanguage) as $piece) {
$parts = explode(';', $piece);
$language = strtolower($parts[0]);
$priority = empty($parts[1]) ? 1. : floatval(str_replace('q=', '', $parts[1]));

$languages[$language] = $priority;
}

// Sort languages by priority.
arsort($languages);

return array_keys($languages);
}

/**
* Match a detection rule and return the matched key.
* @param array $rules
* @param string|null $userAgent
* @return string|bool
*/
protected function findDetectionRulesAgainstUA(array $rules, $userAgent = null)
{
// Loop given rules
foreach ($rules as $key => $regex) {
if (empty($regex)) {
continue;
}

// Check match
if ($this->match($regex, $userAgent)) {
return $key ?: reset((array)$this->matchesArray);
}
}

return false;
}

/**
* Get the browser name.
* @param string|null $userAgent
* @return string|bool
*/
public function browser($userAgent = null)
{
return $this->findDetectionRulesAgainstUA(static::getBrowsers(), $userAgent);
}

/**
* Get the platform name.
* @param string|null $userAgent
* @return string|bool
*/
public function platform($userAgent = null)
{
return $this->findDetectionRulesAgainstUA(static::getPlatforms(), $userAgent);
}

/**
* Get the device name.
* @param string|null $userAgent
* @return string|bool
*/
public function device($userAgent = null)
{
$rules = static::mergeRules(
static::getDesktopDevices(),
static::getPhoneDevices(),
static::getTabletDevices(),
static::getUtilities()
);

return $this->findDetectionRulesAgainstUA($rules, $userAgent);
}

/**
* Check if the device is a desktop computer.
* @param string|null $userAgent deprecated
* @param array $httpHeaders deprecated
* @return bool
*/
public function isDesktop($userAgent = null, $httpHeaders = null)
{
// Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
if ($this->getUserAgent() === 'Amazon CloudFront') {
$cfHeaders = $this->getCfHeaders();
if(array_key_exists('HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER', $cfHeaders)) {
return $cfHeaders['HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER'] === 'true';
}
}

return !$this->isMobile($userAgent, $httpHeaders) && !$this->isTablet($userAgent, $httpHeaders) && !$this->isRobot($userAgent);
}

/**
* Check if the device is a mobile phone.
* @param string|null $userAgent deprecated
* @param array $httpHeaders deprecated
* @return bool
*/
public function isPhone($userAgent = null, $httpHeaders = null)
{
return $this->isMobile($userAgent, $httpHeaders) && !$this->isTablet($userAgent, $httpHeaders);
}

/**
* Get the robot name.
* @param string|null $userAgent
* @return string|bool
*/
public function robot($userAgent = null)
{
if ($this->getCrawlerDetect()->isCrawler($userAgent ?: $this->userAgent)) {
return ucfirst($this->getCrawlerDetect()->getMatches());
}

return false;
}

/**
* Check if device is a robot.
* @param string|null $userAgent
* @return bool
*/
public function isRobot($userAgent = null)
{
return $this->getCrawlerDetect()->isCrawler($userAgent ?: $this->userAgent);
}

/**
* Get the device type
* @param null $userAgent
* @param null $httpHeaders
* @return string
*/
public function deviceType($userAgent = null, $httpHeaders = null)
{
if ($this->isDesktop($userAgent, $httpHeaders)) {
return "desktop";
} elseif ($this->isPhone($userAgent, $httpHeaders)) {
return "phone";
} elseif ($this->isTablet($userAgent, $httpHeaders)) {
return "tablet";
} elseif ($this->isRobot($userAgent)) {
return "robot";
}

return "other";
}

public function version($propertyName, $type = self::VERSION_TYPE_STRING)
{
if (empty($propertyName)) {
return false;
}

// set the $type to the default if we don't recognize the type
if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
$type = self::VERSION_TYPE_STRING;
}

$properties = self::getProperties();

// Check if the property exists in the properties array.
if (true === isset($properties[$propertyName])) {

// Prepare the pattern to be matched.
// Make sure we always deal with an array (string is converted).
$properties[$propertyName] = (array) $properties[$propertyName];

foreach ($properties[$propertyName] as $propertyMatchString) {
if (is_array($propertyMatchString)) {
$propertyMatchString = implode("|", $propertyMatchString);
}

$propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);

// Identify and extract the version.
preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);

if (false === empty($match[1])) {
$version = ($type === self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);

return $version;
}
}
}

return false;
}

/**
* Merge multiple rules into one array.
* @param array $all
* @return array
*/
protected static function mergeRules(...$all)
{
$merged = [];

foreach ($all as $rules) {
foreach ($rules as $key => $value) {
if (empty($merged[$key])) {
$merged[$key] = $value;
} elseif (is_array($merged[$key])) {
$merged[$key][] = $value;
} else {
$merged[$key] .= '|' . $value;
}
}
}

return $merged;
}

/**
* @inheritdoc
*/
public function __call($name, $arguments)
{
// Make sure the name starts with 'is', otherwise
if (strpos($name, 'is') !== 0) {
throw new BadMethodCallException("No such method exists: $name");
}

$this->setDetectionType(self::DETECTION_TYPE_EXTENDED);

$key = substr($name, 2);

return $this->matchUAAgainstKey($key);
}
}

+ 185
- 0
src/system/libraries/crawlerdetect.class.php View File

@@ -0,0 +1,185 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
/*
* This file is part of Crawler Detect - the web crawler detection library.
*
* (c) Mark Beech <m@rkbee.ch>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

require_once(DEDEINC."/libraries/fixtures/crawlers.php");
require_once(DEDEINC."/libraries/fixtures/exclusions.php");
require_once(DEDEINC."/libraries/fixtures/headers.php");

class CrawlerDetect
{
/**
* The user agent.
*
* @var string|null
*/
protected $userAgent;

/**
* Headers that contain a user agent.
*
* @var array
*/
protected $httpHeaders = array();

/**
* Store regex matches.
*
* @var array
*/
protected $matches = array();

/**
* Crawlers object.
*
* @var \Jaybizzle\CrawlerDetect\Fixtures\Crawlers
*/
protected $crawlers;

/**
* Exclusions object.
*
* @var \Jaybizzle\CrawlerDetect\Fixtures\Exclusions
*/
protected $exclusions;

/**
* Headers object.
*
* @var \Jaybizzle\CrawlerDetect\Fixtures\Headers
*/
protected $uaHttpHeaders;

/**
* The compiled regex string.
*
* @var string
*/
protected $compiledRegex;

/**
* The compiled exclusions regex string.
*
* @var string
*/
protected $compiledExclusions;

/**
* Class constructor.
*/
public function __construct(array $headers = null, $userAgent = null)
{
$this->crawlers = new Crawlers();
$this->exclusions = new Exclusions();
$this->uaHttpHeaders = new Headers();

$this->compiledRegex = $this->compileRegex($this->crawlers->getAll());
$this->compiledExclusions = $this->compileRegex($this->exclusions->getAll());

$this->setHttpHeaders($headers);
$this->setUserAgent($userAgent);
}

/**
* Compile the regex patterns into one regex string.
*
* @param array
*
* @return string
*/
public function compileRegex($patterns)
{
return '('.implode('|', $patterns).')';
}

/**
* Set HTTP headers.
*
* @param array|null $httpHeaders
*/
public function setHttpHeaders($httpHeaders)
{
// Use global _SERVER if $httpHeaders aren't defined.
if (! is_array($httpHeaders) || ! count($httpHeaders)) {
$httpHeaders = $_SERVER;
}

// Clear existing headers.
$this->httpHeaders = array();

// Only save HTTP headers. In PHP land, that means
// only _SERVER vars that start with HTTP_.
foreach ($httpHeaders as $key => $value) {
if (strpos($key, 'HTTP_') === 0) {
$this->httpHeaders[$key] = $value;
}
}
}

/**
* Return user agent headers.
*
* @return array
*/
public function getUaHttpHeaders()
{
return $this->uaHttpHeaders->getAll();
}

/**
* Set the user agent.
*
* @param string|null $userAgent
*/
public function setUserAgent($userAgent)
{
if (is_null($userAgent)) {
foreach ($this->getUaHttpHeaders() as $altHeader) {
if (isset($this->httpHeaders[$altHeader])) {
$userAgent .= $this->httpHeaders[$altHeader].' ';
}
}
}

return $this->userAgent = $userAgent;
}

/**
* Check user agent string against the regex.
*
* @param string|null $userAgent
*
* @return bool
*/
public function isCrawler($userAgent = null)
{
$agent = trim(preg_replace(
"/{$this->compiledExclusions}/i",
'',
$userAgent ?: $this->userAgent ?: ''
));

if ($agent === '') {
return false;
}

return (bool) preg_match("/{$this->compiledRegex}/i", $agent, $this->matches);
}

/**
* Return the matches.
*
* @return string|null
*/
public function getMatches()
{
return isset($this->matches[0]) ? $this->matches[0] : null;
}
}

+ 30
- 0
src/system/libraries/fixtures/abstractprovider.php View File

@@ -0,0 +1,30 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
/*
* This file is part of Crawler Detect - the web crawler detection library.
*
* (c) Mark Beech <m@rkbee.ch>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

abstract class AbstractProvider
{
/**
* The data set.
*
* @var array
*/
protected $data;

/**
* Return the data set.
*
* @return array
*/
public function getAll()
{
return $this->data;
}
}

+ 1412
- 0
src/system/libraries/fixtures/crawlers.php
File diff suppressed because it is too large
View File


+ 71
- 0
src/system/libraries/fixtures/exclusions.php View File

@@ -0,0 +1,71 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
/*
* This file is part of Crawler Detect - the web crawler detection library.
*
* (c) Mark Beech <m@rkbee.ch>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
require_once(DEDEINC."/libraries/fixtures/abstractprovider.php");

class Exclusions extends AbstractProvider
{
/**
* List of strings to remove from the user agent before running the crawler regex
* Over a large list of user agents, this gives us about a 55% speed increase!
*
* @var array
*/
protected $data = array(
'Safari.[\d\.]*',
'Firefox.[\d\.]*',
' Chrome.[\d\.]*',
'Chromium.[\d\.]*',
'MSIE.[\d\.]',
'Opera\/[\d\.]*',
'Mozilla.[\d\.]*',
'AppleWebKit.[\d\.]*',
'Trident.[\d\.]*',
'Windows NT.[\d\.]*',
'Android [\d\.]*',
'Macintosh.',
'Ubuntu',
'Linux',
'[ ]Intel',
'Mac OS X [\d_]*',
'(like )?Gecko(.[\d\.]*)?',
'KHTML,',
'CriOS.[\d\.]*',
'CPU iPhone OS ([0-9_])* like Mac OS X',
'CPU OS ([0-9_])* like Mac OS X',
'iPod',
'compatible',
'x86_..',
'i686',
'x64',
'X11',
'rv:[\d\.]*',
'Version.[\d\.]*',
'WOW64',
'Win64',
'Dalvik.[\d\.]*',
' \.NET CLR [\d\.]*',
'Presto.[\d\.]*',
'Media Center PC',
'BlackBerry',
'Build',
'Opera Mini\/\d{1,2}\.\d{1,2}\.[\d\.]*\/\d{1,2}\.',
'Opera',
' \.NET[\d\.]*',
'cubot',
'; M bot',
'; CRONO',
'; B bot',
'; IDbot',
'; ID bot',
'; POWER BOT',
'OCTOPUS-CORE',
);
}

+ 37
- 0
src/system/libraries/fixtures/headers.php View File

@@ -0,0 +1,37 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
/*
* This file is part of Crawler Detect - the web crawler detection library.
*
* (c) Mark Beech <m@rkbee.ch>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

require_once(DEDEINC."/libraries/fixtures/abstractprovider.php");

class Headers extends AbstractProvider
{
/**
* All possible HTTP headers that represent the user agent string.
*
* @var array
*/
protected $data = array(
// The default User-Agent string.
'HTTP_USER_AGENT',
// Header can occur on devices using Opera Mini.
'HTTP_X_OPERAMINI_PHONE_UA',
// Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
'HTTP_X_DEVICE_USER_AGENT',
'HTTP_X_ORIGINAL_USER_AGENT',
'HTTP_X_SKYFIRE_PHONE',
'HTTP_X_BOLT_PHONE_UA',
'HTTP_DEVICE_STOCK_UA',
'HTTP_X_UCBROWSER_DEVICE_UA',
// Sometimes, bots (especially Google) use a genuine user agent, but fill this header in with their email address
'HTTP_FROM',
'HTTP_X_SCANNER', // Seen in use by Netsparker
);
}

+ 1693
- 0
src/system/libraries/mobiledetect.class.php
File diff suppressed because it is too large
View File


+ 150
- 0
src/system/libraries/statistics.class.php View File

@@ -0,0 +1,150 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');

require_once(DEDEINC."/libraries/agent.class.php");

/**
* 流量统计
* 一个轻量级流量统计功能
*
* @version $Id: statistics.class.php 1 11:42 2022年03月26日Z tianya $
* @package DedeBIZ.Libraries
* @copyright Copyright (c) 2022, DedeBIZ.COM
* @license https://www.dedebiz.com/license
* @link https://www.dedebiz.com
*/

class DedeStatistics {
function __construct()
{
}

// 获取统计JS
function GetStat()
{
global $envs,$cfg_cookie_encode;
$agent = new Agent();
$pm = array();
$pm['dduuid'] = GetCookie("DedeStUUID");
if (empty($pm['dduuid'])) {
$pm['dduuid'] = $this->_uniqidReal();
PutCookie('DedeStUUID', $pm['dduuid'], 60 * 24 * 365);
}
$pm['ssid'] = session_id();
if (empty($pm['ssid'])) {
session_start();
$pm['ssid'] = session_id();
}
$url_type = isset($_GET['url_type'])? $_GET['url_type'] : 0;
$typeid = isset($_GET['typeid'])? $_GET['typeid'] : 0;
$aid = isset($_GET['aid'])? $_GET['aid'] : 0;
$value = isset($_GET['value'])? $_GET['value'] : '';
$pm['browser'] = $agent->browser();
$pm['device'] = $agent->device();
$pm['device_type'] = $agent->deviceType();
$pm['os'] = $agent->platform();
$pm['t'] = time();
$pm['created_date'] = MyDate("Ymd",$pm['t']);
$pm['created_hour'] = MyDate("H",$pm['t']);
$pm['url_type'] = isset($envs['url_type'])? $envs['url_type'] : $url_type;
$pm['typeid'] = isset($envs['typeid'])? $envs['typeid'] : $typeid;
$pm['aid'] = isset($envs['aid'])? $envs['aid'] : $aid;
$pm['value'] = isset($envs['value'])? $envs['value'] : $value;
ksort($pm);
$pm['sign'] = sha1(http_build_query($pm).md5($cfg_cookie_encode));
$pm['dopost'] = "stat";
$url = $GLOBALS['cfg_cmspath'].'/apps/statistics.php?'.http_build_query($pm);
return <<<EOT
(function() {
let u = '{$url}';
fetch(u);
})();
EOT;
}

// 统计
function Record()
{
global $dsql,$cfg_cookie_encode;
// 进行统计
$pm = array('dduuid','ssid','browser','device','device_type','os','t','created_date','created_hour','url_type','typeid','aid','value','sign');
$pmvalue = array();
foreach ($pm as $v) {
$pmvalue[$v] = $_GET[$v];
}
ksort($pmvalue);
$sign = $pmvalue['sign'];
unset($pmvalue['sign']);

$cs = sha1(http_build_query($pmvalue).md5($cfg_cookie_encode));
if ($sign !== $cs) {
die("DedeBIZ:check sign failed");
}
$pmvalue['ip'] = GetIP();
$kstr = $vstr = array();
foreach ($pmvalue as $key => $value) {
$kstr[] = "`{$key}`";
$vstr[] = "'".addslashes($value)."'";
}
$insql = "INSERT INTO `#@__statistics_detail`(".implode(",",$kstr).") VALUES (".implode(",",$vstr).")";
return $dsql->ExecuteNoneQuery($insql);
}

// 生成uuid
function _uniqidReal($lenght = 13) {
if (function_exists("random_bytes")) {
$bytes = random_bytes(ceil($lenght / 2));
} elseif (function_exists("openssl_random_pseudo_bytes")) {
$bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
} else {
throw new Exception("no cryptographically secure random function available");
}
return substr(bin2hex($bytes), 0, $lenght);
}

// 获取某天的统计信息
function GetInfoByDate($d=0)
{
global $dsql;
if ($d == -1) {
$pv = $dsql->GetOne("SELECT SUM(pv) as total FROM `#@__statistics`");
$uv = $dsql->GetOne("SELECT SUM(uv) as total FROM `#@__statistics`");
$ip = $dsql->GetOne("SELECT SUM(ip) as total FROM `#@__statistics`");
$vv = $dsql->GetOne("SELECT SUM(vv) as total FROM `#@__statistics`");
return array(
"sdate" => $d,
"pv" => $pv['total'],
"uv" => $uv['total'],
"ip" => $ip['total'],
"vv" => $vv['total'],
);
}
$today = MyDate("Ymd",time());
if ($d==0) {
$d = $today;
}
$d = intval($d);
// 如果统计数据中存在,则直接查询统计表
$info = $dsql->GetOne("SELECT * FROM `#@__statistics` WHERE sdate = $d");
if (is_array($info)) {
return $info;
}
$pv = $dsql->GetOne("SELECT COUNT(*) as total FROM `#@__statistics_detail` WHERE created_date = $d");
$uv = $dsql->GetOne("SELECT COUNT(DISTINCT dduuid) as total FROM `#@__statistics_detail` WHERE created_date = $d");
$ip = $dsql->GetOne("SELECT COUNT(DISTINCT ip) as total FROM `#@__statistics_detail` WHERE created_date = $d");
$vv = $dsql->GetOne("SELECT COUNT(DISTINCT ssid) as total FROM `#@__statistics_detail` WHERE created_date = $d");
if ($d < intval($today)) {
$insql = "INSERT INTO `#@__statistics`(`sdate`,`pv`,`uv`,`ip`,`vv`) VALUES ('$d', '{$pv['total']}','{$uv['total']}','{$ip['total']}','{$vv['total']}')";
// var_dump($insql);
return $dsql->ExecuteNoneQuery($insql);
}
return array(
"sdate" => $d,
"pv" => $pv['total'],
"uv" => $uv['total'],
"ip" => $ip['total'],
"vv" => $vv['total'],
);
}
}

+ 28
- 0
src/system/taglib/statistics.lib.php View File

@@ -0,0 +1,28 @@
<?php
if (!defined('DEDEINC')) exit('dedebiz');
require_once(DEDEINC."/libraries/statistics.class.php");
/**
* 统计标签
*
* @version $Id: statistics.lib.php 1 9:29 2022年3月26日Z tianya $
* @package DedeBIZ.Taglib
* @copyright Copyright (c) 2022, DedeBIZ.COM
* @license https://www.dedebiz.com/license
* @link https://www.dedebiz.com
*/
function lib_statistics(&$ctag, &$refObj)
{
global $envs;
//属性处理
$attlist = "";
FillAttsDefault($ctag->CAttribute->Items, $attlist);
extract($ctag->CAttribute->Items, EXTR_SKIP);
$pms = array();
$pms['url_type'] = isset($envs['url_type'])? $envs['url_type'] : 0;
$pms['typeid'] = isset($envs['typeid'])? $envs['typeid'] : 0;
$pms['aid'] = isset($envs['aid'])? $envs['aid'] : 0;
$pms['value'] = isset($envs['value'])? $envs['value'] : '';
$revalue = '<script async src="'.$GLOBALS['cfg_cmspath'].'/apps/statistics.php?'.http_build_query($pms).'"></script>';
return $revalue;
}

+ 1
- 2
src/theme/templets/footer.htm View File

@@ -13,10 +13,9 @@
</button>
</div>
<!-- /.scroll-top -->

<script src="{dede:global.cfg_cmsurl/}/static/js/bootstrap.bundle.js"></script>
<script src="{dede:global.cfg_cmsurl/}/static/js/style.js"></script>
{dede:statistics/}
<script>
//校验是否登录
function CheckLogin() {


Loading…
Cancel
Save