Compare commits

..

13 Commits

Author SHA1 Message Date
MI15\Win
1ece1135ea v2.0.34-20230809 2023-08-09 14:52:37 +08:00
MI15\Win
cec87b24f2 v2.0.33-20230802 2023-08-02 15:57:19 +08:00
MI15\Win
1d379543f5 v2.0.32-20230727 2023-07-28 00:19:25 +08:00
MI15\Win
0038e27493 v2.0.31-20230720 2023-07-20 14:03:00 +08:00
MI15\Win
3646cfba93 v2.0.30-20230713 2023-07-13 13:27:27 +08:00
MI15\Win
6a418f6c7f v2.0.30-20230712 2023-07-12 23:18:52 +08:00
MI15\Win
33386c75dc v2.0.30-202307012 2023-07-12 23:10:31 +08:00
MI15\Win
0bc6f7bea5 v2.0.29-20230705 2023-07-05 16:16:21 +08:00
MI15\Win
82e8321432 v2.0.28-20230624 2023-06-24 13:58:46 +08:00
MI15\Win
4bc10f7c25 v2.0.27-20230618 2023-06-18 21:28:30 +08:00
MI15\Win
65edb94998 v2.0.26-20230611 2023-06-11 18:20:56 +08:00
MI15\Win
1733e995d6 v2.0.25-20230607 2023-06-07 20:53:30 +08:00
MI15\Win
332cd313fb v2.0.24-20230605 2023-06-06 13:45:44 +08:00
96 changed files with 3744 additions and 492 deletions

View File

@@ -1,8 +1,12 @@
# Apache配置文件
RewriteEngine On
RewriteRule ^(data|system|templates)/.*.(db|db3|sql|tar|gz|zip|info|log)$ - [F]
# 安全规则(必选)
RewriteRule ^templates/.*\.(php|tar|gz|zip|info|log|json)$ - [F]
RewriteRule ^data/.*\.(db|db3|php|sql|tar|gz|zip|info|log|json)$ - [F]
# 重写规则(可选)
RewriteRule '^login$' ./index.php?c=login [L]
RewriteRule '^admin$' ./index.php?c=admin [L]
RewriteRule '^ico/(.+)' ./index.php?c=icon&url=$1 [L]
RewriteRule '^([A-Za-z0-9]+)$' ./index.php?u=$1 [L]
RewriteRule '^(.+)/click/([A-Za-z0-9]+)$' ./index.php?c=$2&id=$3&u=$1 [L]
RewriteRule '^(.+)/click/(.+)' ./$3 [L]

View File

@@ -26,14 +26,18 @@ TwoNav 是一款开源免费的书签(导航)管理程序,界面简洁,
* 支持加密链接
* 支持分享链接
* 支持二级分类
* 支持用户分组
* 支持用户分组/权限管理
* 支持Chrome/Firefox/Edge书签批量导入
* 支持多种主题风格
* 支持批量更新链接图标/标题/描述等信息
* 支持链接信息自动识别
* 支持API
* 支持Docker部署
* 支持uTools插件
* 支持Chromium内核的[浏览器扩展]
* 支持简易文章管理
* 支持更换各种模板/支持混搭,20+个主题模板
* 安全性支持:更换登录入口/二级密码/OTP双重验证
![](https://foruda.gitee.com/images/1680680754989095293/fcc56e76_10359480.jpeg "主页预览")
![](https://foruda.gitee.com/images/1680680836189756220/8c227c34_10359480.jpeg "主题模板")

View File

@@ -34,8 +34,8 @@ if($db_config['type'] == 'sqlite'){
$global_config = unserialize( get_db("global_config", "v", ["k" => "o_config"]) ); //全局配置
$c = Get('c');
$libs = $global_config['Libs'];
$layui['js'] = $libs.'/Layui/v2.8.3/layui.js';
$layui['css'] = $libs.'/Layui/v2.8.3/css/layui.css';
$layui['js'] = $libs.'/Layui/v2.8.10/layui.js';
$layui['css'] = $libs.'/Layui/v2.8.10/css/layui.css';
define('libs',$global_config['Libs']);
define('SysVer',Get_Version());
define('Debug',$global_config['Debug'] == 1);
@@ -74,7 +74,7 @@ if(empty($c) || $c == 'index'){
require "./system/login.php";//登陆
}elseif(in_array($c,['admin','click','api','ico','icon','verify'])){
require "./system/{$c}.php";
}elseif(in_array($c,['apply','guestbook'])){
}elseif(in_array($c,['apply','guestbook','article'])){
if($global_config['Maintenance'] != 0){Amsg(-1,'网站正在进行维护,请稍后再试!');}
require "./system/expand/{$c}.php";
}else{

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 322 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -218,7 +218,7 @@ function echo_Atool(){
<head>
<meta charset="UTF-8">
<title>ATool 工具箱</title>
<link rel="stylesheet" href="../static/Layui/v2.8.3/css/layui.css">
<link rel="stylesheet" href="../static/Layui/v2.8.10/css/layui.css">
<style>
html, body {min-width: 1200px;background-color: #fff;position: relative;}
.page-wrapper {width: 1200px;margin: 0 auto;padding: 0 15px;}
@@ -269,7 +269,7 @@ function echo_Atool(){
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="set_user_name">改账号</a>
</div>
</script>
<script src="../static/Layui/v2.8.3/layui.js"></script>
<script src="../static/Layui/v2.8.10/layui.js"></script>
<script src="../static/jquery/jquery-3.6.0.min.js"></script>
<script src="../static/jquery/jquery.md5.js"></script>
<script src="../templates/admin/js/public.js?v=<?php echo time();?>"></script>
@@ -398,7 +398,7 @@ function echo_verify(){ ?>
<head>
<meta charset="UTF-8">
<title>ATool 工具箱</title>
<link rel="stylesheet" href="../static/Layui/v2.8.3/css/layui.css">
<link rel="stylesheet" href="../static/Layui/v2.8.10/css/layui.css">
<link rel="stylesheet" href="../static/Other/login.css">
</head>
<body>
@@ -423,7 +423,7 @@ function echo_verify(){ ?>
</div>
</div>
<script src = "../static/jquery/jquery-3.6.0.min.js"></script>
<script src = "../static/Layui/v2.8.3/layui.js"></script>
<script src = "../static/Layui/v2.8.10/layui.js"></script>
<script src = '../static/jquery/jquery.md5.js'></script>
<script>
layui.use(['form','jquery'], function () {

161
system/Authenticator.php Normal file
View File

@@ -0,0 +1,161 @@
<?php
class PHPGangsta_GoogleAuthenticator
{
protected $_codeLength = 6;
public function createSecret($secretLength = 16)
{
$validChars = $this->_getBase32LookupTable();
if ($secretLength < 16 || $secretLength > 128) {
throw new Exception('Bad secret length');
}
$secret = '';
$rnd = false;
if (function_exists('random_bytes')) {
$rnd = random_bytes($secretLength);
} elseif (function_exists('mcrypt_create_iv')) {
$rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM);
} elseif (function_exists('openssl_random_pseudo_bytes')) {
$rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong);
if (!$cryptoStrong) {
$rnd = false;
}
}
if ($rnd !== false) {
for ($i = 0; $i < $secretLength; ++$i) {
$secret .= $validChars[ord($rnd[$i]) & 31];
}
} else {
throw new Exception('No source of secure random');
}
return $secret;
}
public function getCode($secret, $timeSlice = null)
{
if ($timeSlice === null) {
$timeSlice = floor(time() / 30);
}
$secretkey = $this->_base32Decode($secret);
$time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
$hm = hash_hmac('SHA1', $time, $secretkey, true);
$offset = ord(substr($hm, -1)) & 0x0F;
$hashpart = substr($hm, $offset, 4);
$value = unpack('N', $hashpart);
$value = $value[1];
$value = $value & 0x7FFFFFFF;
$modulo = pow(10, $this->_codeLength);
return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
}
public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = array())
{
$width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200;
$height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200;
$level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M';
$urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
if (isset($title)) {
$urlencoded .= urlencode('&issuer='.urlencode($title));
}
return "https://api.qrserver.com/v1/create-qr-code/?data=$urlencoded&size=${width}x${height}&ecc=$level";
}
public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null)
{
if ($currentTimeSlice === null) {
$currentTimeSlice = floor(time() / 30);
}
if (strlen($code) != 6) {
return false;
}
for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
$calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
if ($this->timingSafeEquals($calculatedCode, $code)) {
return true;
}
}
return false;
}
public function setCodeLength($length)
{
$this->_codeLength = $length;
return $this;
}
protected function _base32Decode($secret)
{
if (empty($secret)) {
return '';
}
$base32chars = $this->_getBase32LookupTable();
$base32charsFlipped = array_flip($base32chars);
$paddingCharCount = substr_count($secret, $base32chars[32]);
$allowedValues = array(6, 4, 3, 1, 0);
if (!in_array($paddingCharCount, $allowedValues)) {
return false;
}
for ($i = 0; $i < 4; ++$i) {
if ($paddingCharCount == $allowedValues[$i] &&
substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) {
return false;
}
}
$secret = str_replace('=', '', $secret);
$secret = str_split($secret);
$binaryString = '';
for ($i = 0; $i < count($secret); $i = $i + 8) {
$x = '';
if (!in_array($secret[$i], $base32chars)) {
return false;
}
for ($j = 0; $j < 8; ++$j) {
$x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
}
$eightBits = str_split($x, 8);
for ($z = 0; $z < count($eightBits); ++$z) {
$binaryString .= (($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48) ? $y : '';
}
}
return $binaryString;
}
protected function _getBase32LookupTable()
{
return array(
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
'=',
);
}
private function timingSafeEquals($safeString, $userString)
{
if (function_exists('hash_equals')) {
return hash_equals($safeString, $userString);
}
$safeLen = strlen($safeString);
$userLen = strlen($userString);
if ($userLen != $safeLen) {
return false;
}
$result = 0;
for ($i = 0; $i < $userLen; ++$i) {
$result |= (ord($safeString[$i]) ^ ord($userString[$i]));
}
return $result === 0;
}
}

17
system/MySQL/20230605.php Normal file
View File

@@ -0,0 +1,17 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
$sql ="
ALTER TABLE `global_user` CHANGE `RegIP` `RegIP` VARCHAR( 64 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '注册IP';
ALTER TABLE `user_apply` CHANGE `ip` `ip` VARCHAR( 64 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'ip';
ALTER TABLE `user_apply` CHANGE `ua` `ua` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '浏览器UA';
ALTER TABLE `user_share` CHANGE `description` `description` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '备注';
ALTER TABLE `user_log` CHANGE `ip` `ip` VARCHAR( 64 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求ip';
ALTER TABLE `user_log` CHANGE `description` `description` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述';
ALTER TABLE `user_login_info` CHANGE `ip` `ip` VARCHAR( 64 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '登录IP';
ALTER TABLE `user_login_info` CHANGE `ua` `ua` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '浏览器UA';
";
if(exe_sql($sql)){
insert_db('updatadb_logs',['file_name'=>$file_name,'update_time'=>time(),'status'=>'TRUE','extra'=>'']);
}else{
msg(-1,'数据库更新失败');
}

View File

@@ -0,0 +1,9 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
$sql ="
ALTER TABLE `user_links` ADD `keywords` TEXT NOT NULL DEFAULT '' COMMENT '关键字' AFTER `weight` ;
";
if(exe_sql($sql)){
insert_db('updatadb_logs',['file_name'=>$file_name,'update_time'=>time(),'status'=>'TRUE','extra'=>'']);
}else{
msg(-1,'数据库更新失败');
}

32
system/MySQL/20230723.php Normal file
View File

@@ -0,0 +1,32 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
$sql =<<<EOF
CREATE TABLE IF NOT EXISTS `user_article_list` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uid` varchar(32) NOT NULL COMMENT '用户id',
`title` text NOT NULL COMMENT '标题',
`category` int(10) UNSIGNED NOT NULL COMMENT '分类id',
`state` int(10) UNSIGNED NOT NULL COMMENT '状态',
`password` text NOT NULL COMMENT '访问密码',
`top` int(10) UNSIGNED NOT NULL COMMENT '置顶',
`add_time` int(10) UNSIGNED NOT NULL COMMENT '创建时间',
`up_time` int(10) UNSIGNED NOT NULL COMMENT '修改时间',
`browse_count` int(10) UNSIGNED NOT NULL COMMENT '浏览次数',
`summary` text NOT NULL COMMENT '摘要',
`content` text NOT NULL COMMENT '内容',
`cover` text NOT NULL COMMENT '封面',
`extend` text NOT NULL COMMENT '扩展',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
ALTER TABLE `user_count` ADD `e` TEXT NOT NULL DEFAULT '' COMMENT '扩展';
INSERT INTO `purview_list` (`code`, `name`, `description`) VALUES
('article', '文章管理', '允许使用文章管理功能'),
('article_image', '文章图片', '允许在文章编辑器上传图片');
EOF;
if(exe_sql($sql)){
insert_db('updatadb_logs',['file_name'=>$file_name,'update_time'=>time(),'status'=>'TRUE','extra'=>'']);
}else{
msg(-1,'数据库更新失败');
}

View File

@@ -26,7 +26,8 @@ CREATE TABLE IF NOT EXISTS `user_count` (
`uid` int(10) UNSIGNED NOT NULL COMMENT '用户ID',
`k` varchar(32) NOT NULL COMMENT '',
`v` bigint(10) UNSIGNED DEFAULT '0' COMMENT '',
`t` varchar(32) NOT NULL COMMENT '类型'
`t` varchar(32) NOT NULL COMMENT '类型',
`e` text NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -45,6 +46,9 @@ CREATE TABLE IF NOT EXISTS `updatadb_logs` (
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230417.php', '1681719049', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230420.php', '1681977368', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230518.php', '1684393068', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230522.php', '1684762253', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230715.php', '1689427853', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230723.php', '1690119053', 'TRUE', '');
-- 创建用户表
DROP TABLE IF EXISTS `global_user`;
@@ -57,7 +61,7 @@ CREATE TABLE IF NOT EXISTS `global_user` (
`Email` varchar(32) NOT NULL COMMENT '邮箱',
`SecretKey` varchar(32) NOT NULL DEFAULT '' COMMENT 'SecretKey',
`Token` varchar(32) NOT NULL DEFAULT '' COMMENT 'Token',
`RegIP` varchar(15) NOT NULL COMMENT '注册IP',
`RegIP` varchar(64) NOT NULL DEFAULT '' COMMENT '注册IP',
`RegTime` int(10) UNSIGNED NOT NULL COMMENT '注册时间',
`Login` varchar(16) NOT NULL COMMENT '登录入口',
`LoginConfig` text NOT NULL COMMENT '登陆配置',
@@ -108,6 +112,7 @@ CREATE TABLE IF NOT EXISTS `user_links` (
`url` text NOT NULL COMMENT '主链接',
`url_standby` text NOT NULL COMMENT '备用链接',
`weight` int(11) NOT NULL DEFAULT '0' COMMENT '权重',
`keywords` text NOT NULL DEFAULT '' COMMENT '关键字',
`description` text NOT NULL DEFAULT '' COMMENT '描述',
`icon` text NOT NULL DEFAULT '' COMMENT '图标',
`click` int(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '点击数',
@@ -130,8 +135,8 @@ CREATE TABLE IF NOT EXISTS `user_login_info` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uid` int(10) UNSIGNED NOT NULL COMMENT '用户id',
`user` varchar(32) NOT NULL COMMENT '用户名',
`ip` varchar(15) NOT NULL COMMENT '登录IP',
`ua` varchar(256) NOT NULL COMMENT '浏览器UA',
`ip` varchar(64) NOT NULL DEFAULT '' COMMENT '登录IP',
`ua` TEXT NOT NULL DEFAULT '' COMMENT '浏览器UA',
`login_time` int(10) UNSIGNED NOT NULL COMMENT '登录时间',
`last_time` int(10) UNSIGNED NOT NULL COMMENT '最后访问时间',
`expire_time` int(10) UNSIGNED NOT NULL COMMENT '过期时间',
@@ -145,11 +150,11 @@ CREATE TABLE IF NOT EXISTS `user_log` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uid` int(10) UNSIGNED NOT NULL COMMENT '用户id',
`user` varchar(32) NOT NULL COMMENT '用户名',
`ip` varchar(15) NOT NULL COMMENT '请求ip',
`ip` varchar(64) NOT NULL DEFAULT '' COMMENT '请求ip',
`time` varchar(13) NOT NULL COMMENT '请求时间',
`type` varchar(16) NOT NULL COMMENT '日志类型',
`content` text NOT NULL COMMENT '请求内容',
`description` varchar(128) NOT NULL COMMENT '描述',
`description` text NOT NULL DEFAULT '' COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='日志';
@@ -191,7 +196,10 @@ INSERT INTO `purview_list` (`code`, `name`, `description`) VALUES
('guestbook', '留言板', '允许使用留言板功能'),
('link_extend', '链接扩展', '允许使用链接扩展字段'),
('theme_in', '主题设置', '后台显示主题设置菜单'),
('theme_set', '主题配置', '允许自定义主题配置');
('theme_set', '主题配置', '允许自定义主题配置'),
('icon_pull', '图标拉取', '允许用户拉取链接图标'),
('article', '文章管理', '允许使用文章管理功能'),
('article_image', '文章图片', '允许在文章编辑器上传图片');
-- 注册码列表
DROP TABLE IF EXISTS `regcode_list`;
@@ -228,9 +236,9 @@ CREATE TABLE IF NOT EXISTS `user_apply` (
`iconurl` varchar(512) NOT NULL COMMENT '图标url',
`title` varchar(512) NOT NULL COMMENT '标题',
`url` varchar(512) NOT NULL COMMENT '链接',
`ip` varchar(16) NOT NULL DEFAULT '' COMMENT 'ip',
`ip` varchar(64) NOT NULL DEFAULT '' COMMENT 'ip',
`email` varchar(128) NOT NULL DEFAULT '' COMMENT '邮箱',
`ua` varchar(512) NOT NULL DEFAULT '' COMMENT '浏览器UA',
`ua` TEXT NOT NULL DEFAULT '' COMMENT '浏览器UA',
`time` int(10) NOT NULL DEFAULT '0' COMMENT '时间',
`state` int(1) NOT NULL DEFAULT '0' COMMENT '状态',
`category_id` int(10) NOT NULL DEFAULT '0' COMMENT '分类id',
@@ -251,10 +259,44 @@ CREATE TABLE IF NOT EXISTS `user_share` (
`up_time` Bigint(13) NOT NULL DEFAULT '0' COMMENT '修改时间',
`expire_time` Bigint(13) UNSIGNED NOT NULL DEFAULT '0' COMMENT '到期时间',
`views` Bigint(13) NOT NULL DEFAULT '0' COMMENT '浏览数',
`description` varchar(13) NOT NULL DEFAULT '' COMMENT '备注',
`description` TEXT NOT NULL DEFAULT '' COMMENT '备注',
`type` int(1) NOT NULL COMMENT '类型',
`data` text NOT NULL COMMENT '数据',
`pv` int(1) NOT NULL COMMENT '私有可见',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 图标缓存
CREATE TABLE IF NOT EXISTS `global_icon` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`url_md5` varchar(32) NOT NULL COMMENT 'url_md5',
`url` text NOT NULL COMMENT 'url',
`ico_url` text NOT NULL COMMENT 'url_ico',
`add_time` int(10) UNSIGNED NOT NULL COMMENT '创建时间',
`update_time` int(10) UNSIGNED NOT NULL COMMENT '更新时间',
`file_name` text NOT NULL COMMENT '文件名',
`file_mime` text NOT NULL COMMENT 'MIME类型',
`extend` text NOT NULL COMMENT '预留扩展',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
-- 用户文章列表
CREATE TABLE IF NOT EXISTS `user_article_list` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uid` varchar(32) NOT NULL COMMENT '用户id',
`title` text NOT NULL COMMENT '标题',
`category` int(10) UNSIGNED NOT NULL COMMENT '分类id',
`state` int(10) UNSIGNED NOT NULL COMMENT '状态',
`password` text NOT NULL COMMENT '访问密码',
`top` int(10) UNSIGNED NOT NULL COMMENT '置顶',
`add_time` int(10) UNSIGNED NOT NULL COMMENT '创建时间',
`up_time` int(10) UNSIGNED NOT NULL COMMENT '修改时间',
`browse_count` int(10) UNSIGNED NOT NULL COMMENT '浏览次数',
`summary` text NOT NULL COMMENT '摘要',
`content` text NOT NULL COMMENT '内容',
`cover` text NOT NULL COMMENT '封面',
`extend` text NOT NULL COMMENT '扩展',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

View File

@@ -0,0 +1,9 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
$sql ='
ALTER TABLE user_links ADD keywords TEXT(128) NOT NULL DEFAULT "";
';
if(exe_sql($sql)){
insert_db('updatadb_logs',['file_name'=>$file_name,'update_time'=>time(),'status'=>'TRUE','extra'=>'']);
}else{
msg(-1,'数据库更新失败');
}

View File

@@ -0,0 +1,31 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
$sql =<<<EOF
CREATE TABLE "user_article_list" (
"id" integer PRIMARY KEY AUTOINCREMENT,
"uid" integer(10) NOT NULL,
"title" TEXT NOT NULL DEFAULT "",
"category" integer NOT NULL,
"state" integer(1) DEFAULT 0,
"password" TEXT NOT NULL DEFAULT "",
"top" integer(10),
"add_time" integer(10),
"up_time" integer(10),
"browse_count" integer DEFAULT 0,
"summary" TEXT,
"content" TEXT,
"cover" TEXT,
"extend" TEXT,
CONSTRAINT "id" UNIQUE ("id" ASC)
);
ALTER TABLE user_count ADD e TEXT NOT NULL DEFAULT "";
INSERT INTO `purview_list` (`code`, `name`, `description`) VALUES
('article', '文章管理', '允许使用文章管理功能'),
('article_image', '文章图片', '允许在文章编辑器上传图片');
EOF;
if(exe_sql($sql)){
insert_db('updatadb_logs',['file_name'=>$file_name,'update_time'=>time(),'status'=>'TRUE','extra'=>'']);
}else{
msg(-1,'数据库更新失败');
}

View File

@@ -23,7 +23,8 @@ CREATE TABLE IF NOT EXISTS "user_count" (
"uid" integer(10) NOT NULL,
"k" text(32) NOT NULL DEFAULT "",
"v" integer(10) NOT NULL DEFAULT 0,
"t" text(32) NOT NULL DEFAULT ""
"t" text(32) NOT NULL DEFAULT "",
"e" text NOT NULL DEFAULT ""
);
-- 数据库升级记录
@@ -37,6 +38,10 @@ CREATE TABLE IF NOT EXISTS "updatadb_logs" (
);
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230417.php', '1681719049', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230420.php', '1681977368', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230522.php', '1684762253', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230715.php', '1689427853', 'TRUE', '');
INSERT INTO "updatadb_logs" ("file_name", "update_time", "status", "extra") VALUES ('20230723.php', '1690119053', 'TRUE', '');
-- 创建用户表
CREATE TABLE IF NOT EXISTS "global_user" (
@@ -48,7 +53,7 @@ CREATE TABLE IF NOT EXISTS "global_user" (
"Email" TEXT(32) NOT NULL,
"SecretKey" TEXT(32) NOT NULL DEFAULT "",
"Token" TEXT(32) NOT NULL DEFAULT "",
"RegIP" TEXT(15) NOT NULL,
"RegIP" TEXT(64) NOT NULL DEFAULT "",
"RegTime" integer(10) NOT NULL,
"Login" TEXT(16) NOT NULL,
"LoginConfig" TEXT NOT NULL,
@@ -92,6 +97,7 @@ CREATE TABLE IF NOT EXISTS "user_links" (
"url" TEXT(1024) NOT NULL,
"url_standby" text NOT NULL DEFAULT "",
"weight" integer(11) NOT NULL DEFAULT 0,
"keywords" TEXT(128) NOT NULL DEFAULT "",
"description" TEXT(128) NOT NULL DEFAULT "",
"icon" text NOT NULL DEFAULT "",
"click" integer(10) NOT NULL DEFAULT 0,
@@ -107,8 +113,8 @@ CREATE TABLE IF NOT EXISTS "user_login_info" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"uid" integer(10) NOT NULL,
"user" TEXT(32) NOT NULL,
"ip" TEXT(15) NOT NULL,
"ua" TEXT(256) NOT NULL,
"ip" TEXT(64) NOT NULL,
"ua" TEXT NOT NULL,
"login_time" integer(10) NOT NULL,
"last_time" integer(10) NOT NULL,
"expire_time" integer(10) NOT NULL,
@@ -120,11 +126,11 @@ CREATE TABLE IF NOT EXISTS "user_log" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"uid" integer(10) NOT NULL,
"user" TEXT(32) NOT NULL,
"ip" TEXT(15) NOT NULL,
"ip" TEXT(64) NOT NULL,
"time" TEXT(13) NOT NULL,
"type" TEXT(16) NOT NULL,
"content" TEXT NOT NULL,
"description" TEXT(128) NOT NULL
"description" TEXT NOT NULL
);
-- 用户组
@@ -163,7 +169,10 @@ INSERT INTO `purview_list` (`code`, `name`, `description`) VALUES
('guestbook', '留言板', '允许使用留言板功能'),
('link_extend', '链接扩展', '允许使用链接扩展字段'),
('theme_in', '主题设置', '后台显示主题设置菜单'),
('theme_set', '主题配置', '允许自定义主题配置');
('theme_set', '主题配置', '允许自定义主题配置'),
('icon_pull', '图标拉取', '允许用户拉取链接图标'),
('article', '文章管理', '允许使用文章管理功能'),
('article_image', '文章图片', '允许在文章编辑器上传图片');
-- 注册码列表
CREATE TABLE IF NOT EXISTS "regcode_list" (
@@ -197,8 +206,8 @@ CREATE TABLE IF NOT EXISTS "user_apply" (
"title" TEXT(512) DEFAULT "",
"url" TEXT(512) DEFAULT "",
"email" TEXT(128) DEFAULT "",
"ip" TEXT(16) DEFAULT "",
"ua" TEXT(512) DEFAULT "",
"ip" TEXT(64) DEFAULT "",
"ua" TEXT DEFAULT "",
"time" integer DEFAULT "0",
"state" integer DEFAULT "0",
"category_id" INTEGER DEFAULT "0",
@@ -217,8 +226,41 @@ CREATE TABLE IF NOT EXISTS "user_share" (
"up_time" integer(13) DEFAULT "0",
"expire_time" integer(13) DEFAULT "0",
"views" integer(13) DEFAULT "0",
"description" TEXT(13) DEFAULT "",
"description" TEXT DEFAULT "",
"type" integer(1) NOT NULL,
"data" TEXT,
"pv" integer(1) DEFAULT "0"
);
-- 图标缓存
CREATE TABLE IF NOT EXISTS "global_icon" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"url_md5" text(32) NOT NULL DEFAULT "",
"url" text NOT NULL DEFAULT "",
"ico_url" text NOT NULL DEFAULT "",
"add_time" integer(10) NOT NULL,
"update_time" integer(10) NOT NULL,
"file_name" text NOT NULL DEFAULT "",
"file_mime" text NOT NULL DEFAULT "",
"extend" text NOT NULL DEFAULT "",
CONSTRAINT "id" UNIQUE ("id" ASC)
);
-- 用户文章列表
CREATE TABLE "user_article_list" (
"id" integer PRIMARY KEY AUTOINCREMENT,
"uid" integer(10) NOT NULL,
"title" TEXT NOT NULL DEFAULT "",
"category" integer NOT NULL,
"state" integer(1) DEFAULT 0,
"password" TEXT NOT NULL DEFAULT "",
"top" integer(10),
"add_time" integer(10),
"up_time" integer(10),
"browse_count" integer DEFAULT 0,
"summary" TEXT,
"content" TEXT,
"cover" TEXT,
"extend" TEXT,
CONSTRAINT "id" UNIQUE ("id" ASC)
);

View File

@@ -392,7 +392,7 @@ if($_GET['type'] == 'upload'){
}
//数据库清除
if(!empty($_POST['TABLE'])){
$TABLE = ["user_categorys","user_links","user_pwd_group","user_share","user_apply"];
$TABLE = ["user_categorys","user_links","user_pwd_group","user_share","user_apply","user_article_list"];
foreach($_POST['TABLE'] as $key =>$value){
if(in_array($key,$TABLE)){
delete_db($key,['uid'=>UID]);
@@ -418,7 +418,7 @@ if($_GET['type'] == 'upload'){
//文件删除
if(!empty($_POST['FILE'])){
$FILE = ["MessageBoard","favicon"];
$FILE = ["MessageBoard","favicon","upload"];
foreach($_POST['FILE'] as $key =>$value){
$path = DIR.'/data/user/'.U.'/'.$key;
if(in_array($key,$FILE) && is_dir($path)){

View File

@@ -47,7 +47,7 @@ if(!defined('DIR')){
$info['file_db'] = $info['backup_dir'] .'/'. $info['file'].'.db3';
$info['file_info'] = $info['backup_dir'] .'/'. $info['file'].'.info';
$info['file_gz'] = $info['backup_dir'] .'/'. $info['file'].'.tar';
$info['table_arr'] = ['user_config','user_categorys','user_links','user_pwd_group','user_apply','user_share'];
$info['table_arr'] = ['user_config','user_categorys','user_links','user_pwd_group','user_apply','user_share','user_article_list'];
$info['lock'] = DIR.'/data/user/'.U.'/lock.'.UID;
if (!extension_loaded('phar')) {
msg(-1,'不支持phar扩展');
@@ -167,7 +167,7 @@ if(!defined('DIR')){
}
//遍历删除用户数据
$info['table_arr'] = ['user_config','user_categorys','user_links','user_pwd_group','user_apply','user_share'];
$info['table_arr'] = ['user_config','user_categorys','user_links','user_pwd_group','user_apply','user_share','user_article_list'];
foreach($info['table_arr'] as $table_name){
//删除数据
@@ -186,8 +186,15 @@ if(!defined('DIR')){
$where['name'] = $table_name;
$where['LIMIT'] = [($page - 1) * $limit,$limit];
$datas = $MyDB->select('backup','data',$where);
foreach($datas as $data){
foreach($datas as $key => $data){
$data = unserialize($data);
//处理null
foreach ($data as $key => $value) {
if ($value === null) {
$data[$key] = '';
}
}
if(isset($data['id'])){
unset($data['id']);
}

View File

@@ -131,7 +131,7 @@ if($_POST['step'] == 2){
'add_time'=>$data['add_time'] ?? time(),
'up_time'=>$data['up_time'] ?? time(),
'weight'=>0,
'description'=>htmlspecialchars($data['description'],ENT_QUOTES),
'description'=>htmlspecialchars($data['description'],ENT_QUOTES) ?? '',
'font_icon'=> strstr($data['Icon'],'fa') ? 'fa '.$data['Icon'] : 'fa fa-folder',
'icon'=>''
]
@@ -149,7 +149,7 @@ if($_POST['step'] == 2){
'title' => $data['title'],
'url' => $data['url'],
'url_standby' => empty($data['url_standby']) ? '': [$data['url_standby']] ,
'description' => $data['description'],
'description' => $data['description']?? '',
'add_time' => $data['add_time'] ?? time(),
'up_time' => $data['up_time'] ?? time(),
'click' => $data['click'] ?? 0,

View File

@@ -64,7 +64,7 @@ if($page == 'config_home'){
$theme_config = empty($theme_config['config']) ? []:$theme_config['config'];
//读取用户主题配置
if(!in_array($_GET['fn'],['home','login','register','transit','guide'])){
if(!in_array($_GET['fn'],['home','login','register','transit','guide','article'])){
msg(-1,"参数错误");
}
$theme_config_db = get_db('user_config','v',['t'=>'theme_'.$_GET['fn'],'k'=>$theme,'uid'=>UID]);
@@ -83,7 +83,7 @@ if($page == 'config_home'){
}
//主题设置页面
if( $page == 'theme_home' || $page == 'theme_login' || $page == 'theme_transit' || $page == 'theme_register' || $page == 'theme_guide') {
if( $page == 'theme_home' || $page == 'theme_login' || $page == 'theme_transit' || $page == 'theme_register' || $page == 'theme_guide' || $page == 'theme_article') {
if(!check_purview('theme_in',1)){
require(DIR.'/templates/admin/page/404.php');
exit;
@@ -129,14 +129,22 @@ if( $page == 'theme_home' || $page == 'theme_login' || $page == 'theme_transit'
//没有缓存 或 禁止缓存 或 缓存过时
if(empty($template) || $_GET['cache'] === 'no' || time() - $data["time"] > 1800 ){
$urls = [ "https://update.lm21.top/TwoNav/{$fn}_template.json"];
$urls = [
"lm21" => "https://update.lm21.top/TwoNav/{$fn}_template.json",
"gitee" => "https://gitee.com/tznb/twonav_updata/raw/master/{$fn}_template.json"
];
$Source = $global_config['Update_Source'] ?? '';
if (!empty($Source) && isset($urls[$Source])) {
$urls = [$Source => $urls[$Source]];
}
}else{
$cache = true;
}
//读取超时参数
$overtime = !isset($global_config['Update_Overtime']) ? 3 : ($global_config['Update_Overtime'] < 3 || $global_config['Update_Overtime'] > 60 ? 3 : $global_config['Update_Overtime']);
//远程获取
foreach($urls as $url){
$Res = ccurl($url,3);
foreach($urls as $key => $url){
$Res = ccurl($url,$overtime);
$data = json_decode($Res["content"], true);
if($data["code"] == 200 ){ //如果获取成功
$data["time"] = time(); //记录当前时间
@@ -200,7 +208,9 @@ if ($page == 'menu') {
if($global_config['guestbook'] == 1 && check_purview('guestbook',1)){
array_push($extend,['title'=>'留言管理','href'=>'expand/guestbook-admin','icon'=>'fa fa-commenting-o']);
}
if($global_config['article'] == 1 && check_purview('article',1)){
array_push($extend,['title'=>'文章管理','href'=>'expand/article-list','icon'=>'fa fa-file-text-o']);
}
if(!empty($extend)){
$extend = ['title'=>'扩展功能','icon'=>'fa fa-folder-open-o','href'=>'','child'=> $extend];
array_push($menu,$extend);
@@ -213,7 +223,7 @@ if ($page == 'menu') {
[
['title'=>'系统设置','href'=>'root/sys_setting','icon'=>'fa fa-gears'],
['title'=>'授权管理','href'=>'root/vip','icon'=>'fa fa-diamond'],
['title'=>'默认设置','href'=>'root/default_setting','icon'=>'fa fa-heart-o'],
//['title'=>'默认设置','href'=>'root/default_setting','icon'=>'fa fa-heart-o'],
['title'=>'用户管理','href'=>'root/user_control','icon'=>'fa fa-user'],
['title'=>'用户分组','href'=>'root/users_control','icon'=>'fa fa-users'],
['title'=>'注册管理','href'=>'root/reg_control','icon'=>'fa fa-user-plus'],
@@ -234,6 +244,13 @@ if(empty($page)){
exit;
}
// 插件编辑链接跳转
if($page === 'edit_link' && !empty($_GET['id'])){
header("HTTP/1.1 302 Moved Permanently");
header("Location: ./index.php?c=admin&page=link_edit&u=".U."&id=".$_GET['id']);
exit;
}
//页面文件不存在时载入404
if(!empty($page)){
if(!is_file(DIR.'/templates/admin/page/'.$page.'.php')){
@@ -261,6 +278,7 @@ function load_static($type){
}elseif($type == 'js.layui'){
echo
'<script src="'.$GLOBALS['layui']['js'].'" charset="utf-8"></script>
<script src="./templates/admin/js/lay-config.js?v='.$GLOBALS['Ver'].'" charset="utf-8"></script>
<script>layui.config({version:"'.$GLOBALS['Ver'].'"})</script>
';
}

View File

@@ -4,51 +4,48 @@ header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Access-Control-Allow-Private-Network,Content-Type, AccessToken, X-CSRF-Token, Authorization, Token,X-Token,X-Cid");
AccessControl();
//鉴权验证 Cookie验证通过,验证二级密码,Cookie验证失败时尝试验证token
if(!empty(trim($_REQUEST['token']))){ $_COOKIE = []; } //兼容浏览器插件,避免干扰
//获取请求方法
$method = htmlspecialchars(trim($_GET['method']),ENT_QUOTES);
$LoginConfig = unserialize($USER_DB['LoginConfig']);
$api_model = $LoginConfig['api_model']; //API模式
if(!is_login()){
//没登录,根据API模式来限制
$api_model = $LoginConfig['api_model']; //API模式
$token = trim($_REQUEST['token']); //尝试获取令牌
if( empty($USER_DB['Token']) && $api_model != 'compatible+open' ){
Amsg(-1,'未设置token');
//没登录,尝试验证token
if( empty($USER_DB['Token']) ){
msg(-1,'鉴权失败:未登录且未设置token');
}
//获取请求token
$token = trim($_REQUEST['token']);
if(empty($token)){
if($api_model != 'compatible+open'){
Amsg(-1,'非开放模式,token不能为空!');
}
if(in_array($method,['link_list','get_a_link','q_category_link','category_list','get_a_category','check_login'])){
define('Access_Type','open'); //数据访问类型:仅开放
require 'api_compatible.php';
exit;
}else{
Amsg(-1,'token为空时不允许访问此接口');
}
msg(-1,'鉴权失败:未登录且请求未携带token');
}else{
if($token === $USER_DB['Token']){
define('Access_Type','all');
//验证通过
}else{
Amsg(-1,'token验证失败');
msg(-1,'鉴权失败:未登录且token错误');
}
}
if($api_model === 'compatible' || $api_model ==='compatible+open'){
require 'api_compatible.php';
}
//Cookie登录验证OK,验证二级密码
}elseif(Check_Password2($LoginConfig)){
// Cookie 二级密码验证成功(未设置时也认为成功)
}else{
msg(-1,'请先验证二级密码!');
}
//是否加载扩展API
//扩展API
if($global_config['api_extend'] == 1 && is_file('./system/api_extend.php')){
require './system/api_extend.php';
}
//兼容API
$compatible_list = ['add_link','edit_link','del_link','link_list','get_a_link','q_category_link','category_list','get_a_category','add_category','edit_category','app_info','check_login','global_search'];
if(in_array($api_model,['compatible','compatible+open']) && in_array($method,$compatible_list)){
require 'api_compatible.php';
exit;
}
//站长相关方法名
$root = ['write_subscribe','write_sys_settings','write_default_settings','read_user_list','write_user_info','read_purview_list','read_users_list','write_users','read_regcode_list','write_regcode','other_upsys','read_log','other_root'];
if(in_array($method,$root)){
@@ -62,9 +59,7 @@ if(in_array($method,$root)){
if ( preg_match("/^read_|^write_|^other_/",$method) && function_exists($method) ) {
$method();
}else{
if($api_model == 'security'){
Amsg(-1,'方法未找到 >> '.$method);
}
Amsg(-1,'方法未找到 >> '.$method);
}
//读分类列表
@@ -176,7 +171,7 @@ function write_category(){
'fid'=>intval($_POST['fid']??'0'),
'pid'=>intval($_POST['pwd_id']??'0'),
'status'=>1,
'property'=>$_POST['property']??'0',
'property'=>intval($_POST['property']??'0'),
'name'=>htmlspecialchars($_POST['name'],ENT_QUOTES),
'add_time'=>time(),
'up_time'=>time(),
@@ -230,7 +225,7 @@ function write_category(){
$data = [
'fid'=>$_POST['fid'],
'pid'=>intval($_POST['pwd_id']??'0'),
'property'=>$_POST['property']??'0',
'property'=>intval($_POST['property']??'0'),
'name'=>$_POST['name'],
'up_time'=>time(),
'description'=>$_POST['description'],
@@ -306,7 +301,7 @@ function read_link_list(){
$list = unserialize($list);
msgA(['code'=>1,'msg'=>'获取成功','count'=>count($list),'data'=>$list]);
}
$field = ['lid','fid','pid(pwd_id)','status','property','title','url','url_standby','weight','description','icon','click','add_time','up_time'];
$query = $_POST['query'];
$fid = intval(@$_POST['fid']); //获取分类ID
$page = empty(intval($_REQUEST['page'])) ? 1 : intval($_REQUEST['page']);
@@ -334,14 +329,20 @@ function read_link_list(){
//统计条数
$count = count_db('user_links',$where);
//权重排序(数字小的排前面)
$where['ORDER']['weight'] = 'ASC';
$where['ORDER']['lid'] = 'ASC';
//前端指定排序方式,过滤字段名和方式
if(!empty($_POST['order']) && !empty($_POST['field']) && in_array($_POST['field'],$field) && in_array($_POST['order'],['ASC','DESC'])){
$where['ORDER'][$_POST['field']] = $_POST['order'];
}else{
//默认排序方式 权重排序(数字小的排前面)
$where['ORDER']['weight'] = 'ASC';
$where['ORDER']['lid'] = 'ASC';
}
//分页
$where['LIMIT'] = [$offset,$limit];
//查询
$datas = select_db('user_links',['lid','fid','pid(pwd_id)','status','property','title','url','url_standby','weight','description','icon','click','add_time','up_time'],$where);
$datas = select_db('user_links',$field,$where);
msgA(['code'=>1,'msg'=>'获取成功','count'=>$count,'data'=>$datas]);
}
@@ -372,6 +373,7 @@ function write_link(){
$url = $_POST['url'];
$icon = empty($_POST['icon']) ? '' : $_POST['icon'];
$description = empty($_POST['description']) ? '' : $_POST['description'];
$keywords = empty($_POST['keywords']) ? '' : $_POST['keywords'];
$property = empty($_POST['property']) ? 0 : 1;
//检测链接是否合法
check_link($fid,$title,$url,$_POST['url_standby']);
@@ -384,7 +386,10 @@ function write_link(){
if($length_limit['l_desc'] > 0 && strlen($description) > $length_limit['l_desc'] ){
msg(-1,'描述长度不能大于'.$length_limit['l_desc'].'个字节');
}
//关键字长度检测
if($length_limit['l_key'] > 0 && strlen($keywords) > $length_limit['l_key'] ){
msg(-1,'关键字长度不能大于'.$length_limit['l_key'].'个字节');
}
//取最大链接ID
$lid = get_maxid('link_id');
//图标处理
@@ -410,6 +415,7 @@ function write_link(){
'title' => htmlspecialchars($title,ENT_QUOTES),
'url' => $url,
'url_standby' => $_POST['url_standby']??'',
'keywords' => htmlspecialchars($keywords,ENT_QUOTES),
'description' => htmlspecialchars($description,ENT_QUOTES),
'add_time' => time(),
'up_time' => time(),
@@ -419,7 +425,20 @@ function write_link(){
'property' => $property,
'icon' => $icon
];
//扩展字段
if($GLOBALS['global_config']['link_extend'] == 1 && check_purview('link_extend',1)){
$list = get_db("user_config","v",["k"=>"s_extend_list","uid"=>UID]);
if(!empty($list)){
$list = unserialize($list);
$extend = [];
foreach($list as $field){
$name = "_{$field['name']}";
if(isset($_POST[$name])){
$data['extend'][$name] = $_POST[$name];
}
}
}
}
//插入数据库
insert_db('user_links',$data);
msgA(['code'=>1,'msg'=>'添加成功','id'=>$lid]);
@@ -438,7 +457,10 @@ function write_link(){
@unlink($_FILES["file"]["tmp_name"]);
msg(-1,'文件格式不被支持!');
}
//限制文件大小
if(filesize($_FILES["file"]["tmp_name"]) > 1 * 1024 * 1024){
msg(-1,'文件大小超限');
}
session_start();
$sid = $_POST['page_sid'];
//添加链接
@@ -492,7 +514,39 @@ function write_link(){
}else{
msg(-1,'参数错误');
}
//扩展上传图片
}elseif($_GET['type'] == 'extend_up_img'){
//权限检测
if(!check_purview('Upload_icon',1)){
msg(-1,'您的用户组无权限上传图片');
}elseif(empty($_FILES["file"]) || $_FILES["file"]["error"] > 0){
msg(-1,'文件上传失败');
}
//取后缀并判断是否支持
$suffix = strtolower(end(explode('.',$_FILES["file"]["name"])));
if(!preg_match('/^(jpg|jpeg|png|ico|bmp|svg)$/',$suffix)){
@unlink($_FILES["file"]["tmp_name"]);
msg(-1,'文件格式不被支持!');
}
//限制文件大小
if(filesize($_FILES["file"]["tmp_name"]) > 1 * 1024 * 1024){
msg(-1,'文件大小超限');
}
//文件临时路径
$path = DIR . "/data/user/{$u}/upload";
//检测目录,不存在则创建!
if(!Check_Path($path)){
msg(-1,'创建upload目录失败,请检查权限');
}
$tmp_name = 'LE_'.uniqid().'.'.$suffix;
//移动文件
if(!move_uploaded_file($_FILES["file"]["tmp_name"],"{$path}/{$tmp_name}")) {
msg(-1,'上传失败,请检查目录权限');
}else{
msgA(['code'=>1,'msg'=>'上传成功','url'=>"./data/user/".U.'/upload/'.$tmp_name]);
}
//删除图标
}elseif($_GET['type'] === 'del_images'){
session_start();
@@ -531,6 +585,7 @@ function write_link(){
$title = $_POST['title'];
$url = $_POST['url'];
$icon = $_POST['icon'];
$keywords = empty($_POST['keywords']) ? '' : $_POST['keywords'];
$description = empty($_POST['description']) ? '' : $_POST['description'];
$property = empty($_POST['property']) ? 0 : 1;
//检测链接是否合法
@@ -540,6 +595,10 @@ function write_link(){
if($length_limit['l_desc'] > 0 && strlen($description) > $length_limit['l_desc'] ){
msg(-1,'描述长度不能大于'.$length_limit['l_desc'].'个字节');
}
//关键字长度检测
if($length_limit['l_key'] > 0 && strlen($keywords) > $length_limit['l_key'] ){
msg(-1,'关键字长度不能大于'.$length_limit['l_key'].'个字节');
}
//检查链接是否已存在
if(has_db('user_links',['uid'=>UID ,'lid[!]'=>$lid, "url" => $url])){msg(-1,'链接已存在!');}
//检查链接ID是否存在
@@ -551,6 +610,7 @@ function write_link(){
'title' => htmlspecialchars($title,ENT_QUOTES),
'url' => $url,
'url_standby' => $_POST['url_standby']??'',
'keywords' => htmlspecialchars($keywords,ENT_QUOTES),
'description' => htmlspecialchars($description,ENT_QUOTES),
'up_time' => time(),
'property' => $property,
@@ -570,7 +630,6 @@ function write_link(){
}
}
}
}
//非必须参数,未传递参数时
@@ -592,8 +651,11 @@ function write_link(){
if(!isset($_POST['pwd_id'])){
unset($data['pid']);
}
if(!isset($_POST['keywords'])){
unset($data['keywords']);
}
//更新数据
update_db('user_links',$data,['uid'=>UID,'lid'=>intval($_POST['lid']) ]);
update_db('user_links',$data,['uid'=>UID,'lid'=>$lid ]);
msgA(['code'=>1,'msg'=>'修改成功','icon' => $icon]);
//删除
}elseif($_GET['type'] === 'del'){
@@ -674,6 +736,116 @@ function write_link(){
if(empty($fid)){msg(-1,'分类ID错误');}
//加一个查找分类是否存在
update_db('user_links',['fid'=>$fid],['uid'=>UID ,"lid" => json_decode($_POST['lid']) ],[1,'设置成功']);
//检测是否满足要求
}elseif($_GET['type'] === 'msg_pull_check'){
if($global_config['offline']){
msg(-1,"离线模式不可用");
}
if(!is_subscribe('bool')){
msg(-1,"未检测到有效授权,无法使用该功能!");
}
if(intval($_POST['icon']) > 0){
if(!check_purview('icon_pull',1)){
msg(-1,'您所在的用户组,无法使用网站图标获取功能');
}
$path = DIR ."/data/user/".U."/favicon";
if(!Check_Path($path)){
msg(-1,'创建目录失败,请检查目录权限');
}
$config = unserialize( get_db("global_config", "v", ["k" => "icon_config"])) ?? [];
if($config['o_switch'] == '0'){
msg(-1,'相关服务处于关闭状态,请联系站长开启');
}
}
session_start();
$key = md5(uniqid().Get_Rand_Str(8));
$_SESSION['msg_pull']["$key"] = true;
msgA(['code'=>1,'msg'=>'success','key'=>$key]);
}elseif($_GET['type'] === 'msg_pull'){
session_start();
$key = $_POST['key'];
if(empty($key) || !$_SESSION['msg_pull']["$key"]){
msg(-1,'key验证失败,请重试!');
}elseif(empty($_POST['link_id'])){
msg(-1,'链接ID不能为空');
}
//读取信息
$link = get_db('user_links','*',['uid'=>UID ,'lid'=>$_POST['link_id'] ]);
//检查链接
if(empty($link)){
msg(-1,'链接ID不存在');
}elseif(!preg_match("/^(http:\/\/|https:\/\/).*/",$link['url'])){
msg(-1,'只支持识别http/https协议的链接!');
}elseif( !filter_var($link['url'], FILTER_VALIDATE_URL) ) {
msg(-1,'URL无效!');
}
//是否获取站点信息
if( ( intval($_POST['title']) + intval($_POST['keywords']) + intval($_POST['description']) ) > 0 ){
//读取长度限制配置
$length_limit = unserialize(get_db("global_config","v",["k"=>"length_limit"]));
//获取网站标题
$c = curl_init();
curl_setopt($c, CURLOPT_URL, $link['url']);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($c, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36');
curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($c , CURLOPT_TIMEOUT, 10);
$data = curl_exec($c);
curl_close($c);
require (DIR .'/system/get_page_info.php');
$info = get_page_info($data);
$new = [];
if(intval($_POST['title']) > 0 && !empty($info['site_title'])){
$new['title'] = $info['site_title'];
if($length_limit['l_name'] > 0 && strlen($new['title']) > $length_limit['l_name'] ){
$new['title'] = mb_substr($new['title'], 0, $length_limit['l_name'], 'utf-8');
}
}
if(intval($_POST['keywords']) > 0 && !empty($info['site_keywords'])){
$new['keywords'] = (empty($link['keywords']) || $_POST['keywords'] == '2') ? $info['site_keywords'] : $link['keywords'];
if($length_limit['l_key'] > 0 && strlen($new['keywords']) > $length_limit['l_key'] ){
$new['keywords'] = mb_substr($new['keywords'], 0, $length_limit['l_key'], 'utf-8');
}
}
if(intval($_POST['description']) > 0 && !empty($info['site_description'])){
$new['description'] = (empty($link['description']) || $_POST['description'] == '2') ? $info['site_description'] : $link['description'];
if($length_limit['l_desc'] > 0 && strlen($new['description']) > $length_limit['l_desc'] ){
$new['description'] = mb_substr($new['description'], 0, $length_limit['l_desc'], 'utf-8');
}
}
if(empty($new)){
$r['info'] = 'fail';
}else{
update_db('user_links',$new,['uid'=>UID ,"lid" => $link['lid'] ]);
$r['info'] = 'success';
}
}
//是否获取图标
if(intval($_POST['icon']) > 0){
//检查跳过已存在图标的链接
if($_POST['icon'] == '1' && !empty($link['icon'])){
$r['icon'] = 'skip';
}
$api = Get_Index_URL().'?c=icon&url='.base64_encode($link['url']);
$res = ccurl($api,30,true);
$data = get_db('global_icon','*',['url_md5'=>md5($link['url'])]);
if(empty($data)){
$r['icon'] = 'fail';
}
$new_path = "./data/user/".U.'/favicon/'.$data['file_name'];
if(copy("./data/icon/{$data['file_name']}",$new_path)){
update_db('user_links',['icon'=>$new_path],['uid'=>UID ,"lid" => $link['lid'] ]);
$r['icon'] = 'success';
}else{
$r['icon'] = 'fail';
}
}
msg(1,$r);
//图标拉取
}elseif($_GET['type'] === 'icon_pull'){
if($global_config['offline']){
@@ -705,7 +877,7 @@ function write_link(){
}
$api = Get_Index_URL().'?c=icon&url='.base64_encode($link['url']);
$res = ccurl($api);
$res = ccurl($api,30,true);
$data = get_db('global_icon','*',['url_md5'=>md5($link['url'])]);
if(empty($data)){
msg(1,'fail');
@@ -733,7 +905,7 @@ function write_link(){
if(empty($data['name']) || check_xss($data['name']) || !preg_match('/^[A-Za-z0-9]{3,18}$/',$data['name'])){
msgA( ['code' => -1,'msg' => '字段名错误,请输入长度3-18的字母/数字'] );
}
if(!in_array($data['type'],['text','textarea'])){
if(!in_array($data['type'],['text','textarea','up_img'])){
msgA( ['code' => -1,'msg' => '类型错误'] );
}
}
@@ -747,7 +919,7 @@ function write_link(){
$datas = [];
foreach ($lists as $key => $data ){
array_push($datas,['title'=>$data['title'],'name'=>$data['name'],'weight'=>$data['weight'],'type'=>$data['type'],'default'=> "{$data['default']}"]);
array_push($datas,['title'=>$data['title'],'name'=>$data['name'],'weight'=>$data['weight'],'type'=>$data['type'],'default'=> "{$data['default']}",'tip'=>$data['tip']]);
}
//根据序号排序
usort($datas, function($a, $b) {
@@ -766,7 +938,7 @@ function write_security_setting(){
global $USER_DB;
if($USER_DB['Password'] !== Get_MD5_Password($_POST['Password'],$USER_DB['RegTime'])){
msg(-1,'密码错误,请核对后再试!');
}elseif( $_POST['KeyClear'] > $_POST['Session']){
}elseif( intval($_POST['Session']) > 0 && intval($_POST['KeyClear']) > intval($_POST['Session'])){
msg(-1,'Key清理时间不能大于登录保持时间');
}
@@ -775,11 +947,11 @@ function write_security_setting(){
'HttpOnly'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'HttpOnly参数错误'],
'KeySecurity'=>['int'=>true,'min'=>0,'max'=>2,'msg'=>'Key安全参数错误'],
'KeyClear'=>['int'=>true,'min'=>1,'max'=>60,'msg'=>'Key清理参数错误'],
'api_model'=>['v'=>['security','compatible','compatible+open'],'msg'=>'API模式参数错误'],
'api_model'=>['v'=>['security','compatible'],'msg'=>'API模式参数错误'],
'login_page'=>['v'=>['admin','index','auto'],'msg'=>'登录成功参数错误'],
'Password2'=>['empty'=>true]
];
$LoginConfig = unserialize($USER_DB['LoginConfig']);
foreach ($datas as $key => $data){
if($data['int']){
$LoginConfig[$key] = ($_POST[$key] >= $data['min'] && $_POST[$key] <= $data['max'])?intval($_POST[$key]):msg(-1,$data['msg']);
@@ -821,6 +993,10 @@ function write_apply(){
$s['apply'] = intval($_POST['apply']); // 功能选项0.关闭 1.需要审核 2.无需审核
$s['Notice'] = $_POST['Notice']??''; // 公告
$s['submit_limit'] = intval($_POST['submit_limit']); //提交限制
$s['iconurl'] = $_POST['iconurl'];
$s['description'] = $_POST['description'];
$s['email'] = $_POST['email'];
if($s['apply'] < 0 || $s['apply'] > 2 ){
msg(-1,'参数错误!');
}elseif(strlen($s['Notice']) > 512){
@@ -966,8 +1142,8 @@ function write_site_setting(){
'main_link_priority'=>['int'=>true,'min'=>0,'max'=>3,'msg'=>'主链优先参数错误'],
'link_icon'=>['int'=>true,'min'=>0,'max'=>30,'msg'=>'链接图标参数错误'],
'site_icon'=>['empty'=>true],
'top_link'=>['int'=>true,'min'=>0,'max'=>20,'msg'=>'热门链接参数错误'],
'new_link'=>['int'=>true,'min'=>0,'max'=>20,'msg'=>'最新链接参数错误'],
'top_link'=>['int'=>true,'min'=>0,'max'=>100,'msg'=>'热门链接参数错误'],
'new_link'=>['int'=>true,'min'=>0,'max'=>100,'msg'=>'最新链接参数错误'],
'max_link'=>['int'=>true,'min'=>0,'max'=>100,'msg'=>'输出上限参数错误'],
'custom_header'=>['empty'=>true],
'custom_footer'=>['empty'=>true]
@@ -996,7 +1172,8 @@ function write_site_setting(){
function write_transit_setting(){
$datas = [
'visitor_stay_time'=>['int'=>true,'min'=>0,'max'=>60,'msg'=>'访客停留时间范围0-60'],
'admin_stay_time'=>['int'=>true,'min'=>0,'max'=>60,'msg'=>'管理员停留时间范围0-60']
'admin_stay_time'=>['int'=>true,'min'=>0,'max'=>60,'msg'=>'管理员停留时间范围0-60'],
'default_keywords'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'默认关键字参数错误']
];
foreach ($datas as $key => $data){
@@ -1034,6 +1211,65 @@ function write_user_password(){
msg(1,'修改成功');
}
//读双重验证
function read_totp(){
global $USER_DB;
if($USER_DB['Password'] !== Get_MD5_Password($_POST['Password'],$USER_DB['RegTime'])){
msg(-1102,'密码错误,请核对后再试!');
}
$LoginConfig = unserialize($USER_DB['LoginConfig']);
if(empty($LoginConfig['totp_key'])){
require DIR . '/system/Authenticator.php';
$totp = new PHPGangsta_GoogleAuthenticator();
$key = $totp->createSecret();
msgA(['code'=>2,'msg'=>'未开启双重验证','key'=> $key ]);
}
msgA(['code'=>1,'msg'=>'已开启双重验证']);
}
//写双重验证
function write_totp(){
global $USER_DB;
if($USER_DB['Password'] !== Get_MD5_Password($_POST['Password'],$USER_DB['RegTime'])){
msg(-1102,'密码错误,请核对后再试!');
}
if($_GET['type'] === 'delete'){ //删除双重验证
$LoginConfig = unserialize($USER_DB['LoginConfig']);
if(empty($LoginConfig['totp_key'])){
msgA(['code'=>-1,'msg'=>'未开启双重验证',]);
}
$LoginConfig['totp_key'] = '';
update_db("global_user", ["LoginConfig"=>$LoginConfig],["ID"=>UID],[1,'操作成功']);
}elseif($_GET['type'] === 'set'){ //设置双重验证
//必填项验证
if(empty($_POST['key'])){
msgA(['code'=>-1,'msg'=>'Key不能为空']);
}elseif(empty($_POST['code'])){
msgA(['code'=>-1,'msg'=>'验证码不能为空']);
}
$LoginConfig = unserialize($USER_DB['LoginConfig']);
if(!empty($LoginConfig['totp_key'])){
msgA(['code'=>-1,'msg'=>'已开启双重验证,无法继续开启!']);
}
//载入totp库
require DIR . '/system/Authenticator.php';
$totp = new PHPGangsta_GoogleAuthenticator();
$checkResult = $totp->verifyCode($_POST['key'], $_POST['code'], 2);
if(!$checkResult){
msgA(['code'=>-1,'msg'=>'验证失败,请重试']);
}
//写入数据库
$LoginConfig = unserialize($USER_DB['LoginConfig']);
$LoginConfig['totp_key'] = $_POST['key'];
update_db("global_user", ["LoginConfig"=>$LoginConfig],["ID"=>UID],[1,'操作成功']);
}else{
msg(-1,'请求参数有误');
}
}
//查Token
function read_token(){
global $USER_DB;
@@ -1117,7 +1353,7 @@ function other_testing_link(){
if ( $global_config['offline'] == '1'){ msg(-1,"离线模式无法使用此功能"); }
$code = get_http_code($_POST['url']);
if($code != 200 && $code != 302 && $code != 301){
$code = ccurl($_POST['url'])['code'];
$code = ccurl($_POST['url'],30)['code'];
}
msgA(['code' => 0 ,'StatusCode'=> $code]);
}
@@ -1125,7 +1361,7 @@ function other_testing_link(){
//主题下载/更新/删除
function write_theme(){
global $global_config;
$fn = $_POST['fn'];if($_GET['type'] != 'config' && !in_array($fn,['home','login','transit','register','guide'])){msg(-1,'fn参数错误');}
$fn = $_POST['fn'];if($_GET['type'] != 'config' && !in_array($fn,['home','login','transit','register','guide','article'])){msg(-1,'fn参数错误');}
if($_GET['type'] == 'download'){
is_root();
if($global_config['offline']){msg(-1,"离线模式禁止下载主题!");} //离线模式
@@ -1137,6 +1373,9 @@ function write_theme(){
}else{
msg(-1,"获取程序版本异常");
}
if(!is_writable('./templates')){
msg(-1,"检测到模板目录不可写<br />请检查templates目录权限<br />宝塔面板请注意所有者为www<br />其他疑问请联系技术支持");
}
//从数据库查找主题信息
$template = get_db('global_config','v',['k'=> 'theme_'.$fn.'_cache']);
if(empty($template)){
@@ -1156,7 +1395,10 @@ function write_theme(){
}
//下载主题包
if (!is_dir('./data/temp')) mkdir('./data/temp',0755,true) or msg(-1,'下载失败,创建临时[/data/temp]目录失败');
if(!is_dir('./data/temp')) mkdir('./data/temp',0755,true) or msg(-1,'下载失败,创建临时[/data/temp]目录失败');
if(!is_writable('./data/temp')){
msg(-1,"检测到临时目录不可写<br />请检查data/temp目录权限<br />宝塔面板请注意所有者为www<br />其他疑问请联系技术支持");
}
$data = $key;
foreach($data['url'] as $url){
if(downFile( $url , $file , DIR.'/data/temp/')){
@@ -1240,6 +1482,8 @@ function write_theme(){
$s_templates['login'] = $name;
}elseif($fn == 'transit'){
$s_templates['transit'] = $name;
}elseif($fn == 'article'){
$s_templates['article'] = $name;
}elseif($fn == 'register'){
$global_templates['register'] = $name;
update_db('global_config',['v'=>$global_templates],['k'=>'s_templates'],[1,'注册模板设置成功']);
@@ -1264,7 +1508,7 @@ function write_theme(){
msg(-1,"获取模板类型错误");
}
$fn = empty($GET['fn']) ? $_GET['template_type'] : $GET['fn'];
if(!in_array($fn,['home','login','register','transit','guide'])){
if(!in_array($fn,['home','login','register','transit','guide','article'])){
msg(-1,"参数错误");
}
//0420 END
@@ -1280,7 +1524,7 @@ function read_login_info(){
$limit = empty(intval($_REQUEST['limit'])) ? 50 : intval($_REQUEST['limit']);
$offset = ($page - 1) * $limit; //起始行号
$where["uid"] = UID;
$where["cookie_key[!]"] = md5($_COOKIE[U.'_key']);
//$where["cookie_key[!]"] = md5($_COOKIE[U.'_key']); //不显示当前设备
//统计条数
$count = count_db('user_login_info',$where);
//权重排序(数字小的排前面)
@@ -1289,7 +1533,10 @@ function read_login_info(){
$where['LIMIT'] = [$offset,$limit];
//查询
$datas = select_db('user_login_info',['id','ip','ua','login_time','last_time','expire_time'],$where);
msgA(['code'=>1,'msg'=>'获取成功','count'=>$count,'data'=>$datas]);
//获取当前登录ID,用于前端标记
$where["cookie_key"] = md5($_COOKIE[U.'_key']);
$current_id = get_db('user_login_info','id',$where);
msgA(['code'=>1,'msg'=>'获取成功','count'=>$count,'data'=>$datas,'current_id'=>$current_id]);
}
//写登录信息
@@ -1444,7 +1691,7 @@ function write_data_control(){
function read_data(){
global $USER_DB;
//指定类型限制仅root账号可用!
if(in_array($USER_DB['UserGroup'] != 'root' && $_GET['type'],['diagnostic_log','phpinfo'])){
if($USER_DB['UserGroup'] != 'root' && in_array( $_GET['type'],['diagnostic_log','connectivity_test','phpinfo'])){
msg(-1,'无权限');
}
@@ -1455,7 +1702,26 @@ function read_data(){
$index_count = get_db('user_count','v',['uid'=>UID,'k'=>date('Ym'),'t'=>'index_Ym'])??0;
$click_count = get_db('user_count','v',['uid'=>UID,'k'=>date('Ym'),'t'=>'click_Ym'])??0;
msgA( ['code'=>1,'data'=>[$category_count,$link_count,$index_count,$click_count] ]);
//连通测试
}elseif($_GET['type'] == 'connectivity_test'){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_POST['url']);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$start = microtime(true);
$response = curl_exec($ch);
$end = microtime(true);
$time = round(($end - $start) * 1000, 2);
if(curl_errno($ch)) {
$log .= "请求发生错误:".curl_error($ch);
} else {
$log .= "响应内容:".$response ?? 'Null' ;
$log .= ",访问耗时:{$time} 毫秒。" ;
}
curl_close($ch);
msg(1,$log);
//一键诊断
}elseif($_GET['type'] == 'diagnostic_log'){
clearstatcache(); //清除缓存
@@ -1523,7 +1789,37 @@ function read_data(){
msg(1,$log);
//输出phpinfo信息
}elseif($_GET['type'] == 'phpinfo'){
phpinfo();
session_start();
if($_SESSION['phpinfo_id'] != $_GET['pid']){
exit('验证失败,请刷新页面后重试!');
}elseif(Get_MD5_Password($_GET["p"],$GLOBALS['USER_DB']["RegTime"]) === $GLOBALS['USER_DB']["Password"]){
$_COOKIE = [];
$_SERVER['HTTP_COOKIE'] = 'privacy';
phpinfo();
}else{
exit('密码验证失败,请重试!');
}
//报表统计
}elseif($_GET['type'] == 'echarts'){
$days = isset($_GET['date']) && !empty($_GET['date']) ? $_GET['date'] : 7;
$dates = [];
for ($i = 0; $i < $days; $i++) {
$date = date('Ymd', strtotime("-$i days"));
$dates[] = $date;
}
$dates = array_reverse($dates);
$day_data = [];
array_push($day_data, ['name' => '访问量', 'type' => 'line', 'data' => []]);
array_push($day_data, ['name' => '点击量', 'type' => 'line', 'data' => []]);
array_push($day_data, ['name' => 'IP数', 'type' => 'line', 'data' => []]);
foreach ($dates as $date) {
array_push($day_data[0]['data'], get_db('user_count', 'v', ['uid' => UID, 'k' => $date, 't' => 'index_Ymd']) ?? 0);
array_push($day_data[1]['data'], get_db('user_count', 'v', ['uid' => UID, 'k' => $date, 't' => 'click_Ymd']) ?? 0);
array_push($day_data[2]['data'], get_db('user_count', 'v', ['uid' => UID, 'k' => $date, 't' => 'ip_count']) ?? 0);
}
$data = ['dates'=>$dates,'day_data'=>$day_data];
msgA(['code'=>1,'data'=>$data]);
}
}
@@ -1532,6 +1828,17 @@ function other_local_backup(){
require DIR . '/system/UseFew/local_backup.php';
exit;
}
//读文章
function read_article(){
require DIR . '/system/api_article.php';
exit;
}
//写文章
function write_article(){
require DIR . '/system/api_article.php';
exit;
}
//获取链接信息
function other_get_link_info(){
global $global_config;
@@ -1551,6 +1858,7 @@ function other_get_link_info(){
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($c, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36');
curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1); //允许重定向,解决http跳转到https无法识别
curl_setopt($c , CURLOPT_TIMEOUT, 5); //设置超时时间
$data = curl_exec($c);
@@ -1559,6 +1867,7 @@ function other_get_link_info(){
require (DIR .'/system/get_page_info.php');
$info = get_page_info($data);
$link['title'] = $info['site_title'];
$link['keywords'] = $info['site_keywords'];
$link['description'] = $info['site_description'];
msgA(['code'=>1,'data'=>$link]);
}

222
system/api_article.php Normal file
View File

@@ -0,0 +1,222 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
$type = htmlspecialchars(trim($_GET['type']),ENT_QUOTES);
if (function_exists($type) ) {
if($GLOBALS['global_config']['article'] != 1 || !check_purview('article',1)){
msg(-1,'无权限');
}
$type();
}else{
Amsg(-1,'请求类型错误 >> '.$type);
}
//上传图片
function uploadImage(){
global $u;
//权限检测
if(!check_purview('article_image',1)){
msgA(['errno'=>-1,'message'=>'您的用户组无权限上传图片']);
}elseif(empty($_FILES["file"]) || $_FILES["file"]["error"] > 0){
msgA(['errno'=>-1,'message'=>'文件上传失败']);
}
//取后缀并判断是否支持
$suffix = strtolower(end(explode('.',$_FILES["file"]["name"])));
if(!preg_match('/^(jpg|png|gif|bmp|jpeg|svg|webp)$/',$suffix)){
@unlink($_FILES["file"]["tmp_name"]);
msgA(['errno'=>-1,'message'=>'文件格式不被支持']);
}
//限制文件大小
if(filesize($_FILES["file"]["tmp_name"]) > 5 * 1024 * 1024){
msgA(['errno'=>-1,'message'=>'文件大小超限']);
}
//文件临时路径
$ym = date("Ym");
$path = DIR . "/data/user/{$u}/upload/{$ym}/";
//检测目录,不存在则创建!
if(!Check_Path($path)){
msgA(['errno'=>-1,'message'=>'创建upload目录失败,请检查权限']);
}
$tmp_name = 'AI_'.uniqid().'.'.$suffix;
//移动文件
if(!move_uploaded_file($_FILES["file"]["tmp_name"],"{$path}/{$tmp_name}")) {
msgA(['errno'=>-1,'message'=>'上传失败,请检查目录权限']);
}else{
msgA(['errno'=>0,'data'=>['url'=>"./data/user/{$u}/upload/{$ym}/$tmp_name",'alt'=>$_FILES["file"]["name"],'href'=>''],'message'=>'上传成功']);
}
}
//删除图片
function deleteImage(){
global $u;
if(empty($_POST['path'])){
msg(-1,'请求参数错误');
}
$path = $_POST['path'];
$pattern = "/^\.\/data\/user\/{$u}\/upload\/\d{6}\/AI_[A-Za-z0-9_]+\.(jpg|png|gif|bmp|jpeg|svg|webp)$/i";
if(preg_match($pattern,$path) && is_file($path)){
@unlink($path);
}else{
msg(-1,'请求参数错误');
}
//需考虑编辑文章删除封面时未点击保存的情况
if(is_file($path)){
msg(-1,'删除失败');
}else{
msg(1,'删除成功');
}
}
//上传视频
function uploadVideo(){
msgA(['errno'=>-1,'message'=>'未开放']);
global $u;
//权限检测
if(!check_purview('article_image',1)){
msgA(['errno'=>-1,'message'=>'您的用户组无权限上传视频']);
}elseif(empty($_FILES["file"]) || $_FILES["file"]["error"] > 0){
msgA(['errno'=>-1,'message'=>'文件上传失败']);
}
//取后缀并判断是否支持
$suffix = strtolower(end(explode('.',$_FILES["file"]["name"])));
if(!preg_match('/^(avi|mp4|wma|rmvb|rm|flash|3gp|flv)$/',$suffix)){
@unlink($_FILES["file"]["tmp_name"]);
msgA(['errno'=>-1,'message'=>'文件格式不被支持']);
}
//限制文件大小
if(filesize($_FILES["file"]["tmp_name"]) > 20 * 1024 * 1024){
msgA(['errno'=>-1,'message'=>'文件大小超限']);
}
//文件临时路径
$ym = date("Ym");
$path = DIR . "/data/user/{$u}/upload/{$ym}/";
//检测目录,不存在则创建!
if(!Check_Path($path)){
msgA(['errno'=>-1,'message'=>'创建upload目录失败,请检查权限']);
}
$tmp_name = 'AV_'.uniqid().'.'.$suffix;
//移动文件
if(!move_uploaded_file($_FILES["file"]["tmp_name"],"{$path}/{$tmp_name}")) {
msgA(['errno'=>-1,'message'=>'上传失败,请检查目录权限']);
}else{
msgA(['errno'=>0,'data'=>['url'=>"./data/user/{$u}/upload/{$ym}/$tmp_name",'alt'=>$_FILES["file"]["name"],'href'=>''],'message'=>'上传成功']);
}
}
//获取文章列表
function article_list(){
$where['uid'] = UID;
//分类筛选
if(intval(@$_POST['category']) > 0){
$where['AND']['category'] = intval(@$_POST['category']);
}
//状态筛选
if(intval(@$_POST['state']) > 0){
$where['AND']['state'] = intval(@$_POST['state']);
}
//关键字筛选
$query = $_POST['keyword'];
if(!empty($query)){
$where['AND']['OR'] = ["title[~]" => $query,"summary[~]" => $query,"content[~]" => $query];
}
//统计条数
$count = count_db('user_article_list',$where);
//分页
$page = empty(intval($_REQUEST['page'])) ? 1 : intval($_REQUEST['page']);
$limit = empty(intval($_REQUEST['limit'])) ? 50 : intval($_REQUEST['limit']);
$offset = ($page - 1) * $limit; //起始行号
$where['LIMIT'] = [$offset,$limit];
$datas = select_db('user_article_list',['id','title','category','state','password','top','add_time','up_time','browse_count','summary','cover'],$where);
//查询分类
$categorys = select_db('user_categorys',['cid(id)','name'],['uid'=>UID]);
$categorys = array_column($categorys,'name','id');
//为文章添加分类名称
foreach ($datas as &$data) {
$data['category_name'] = $categorys[$data['category']] ?? 'Null';
}
msgA(['code'=>1,'count'=>$count,'data'=>$datas]);
}
//保存文章
function save_article(){
if(empty($_POST['category']) || !has_db('user_categorys',['uid'=>UID,'cid'=>$_POST['category']])){
msg(-1,'分类不存在');
}
$time = time();
//id为空,添加文章
if(empty($_POST['id'])){
insert_db('user_article_list',[
'uid'=>UID,
'title'=>$_POST['title'],
'category'=>$_POST['category'],
'state'=>$_POST['state'],
'password'=>'',
'top'=>0,
'add_time'=>$time,
'up_time'=>$time,
'browse_count'=>0,
'summary'=>$_POST['summary'],
'content'=>$_POST['content'],
'cover'=>$_POST['cover_url'],
'extend'=>''
],[1,'保存成功']);
//存在id,更新文章数据
}else{
if(!has_db('user_article_list',['uid'=>UID,'id'=>$_POST['id']])){
msg(-1,'文章id错误');
}
update_db('user_article_list',[
'title'=>$_POST['title'],
'category'=>$_POST['category'],
'state'=>$_POST['state'],
'up_time'=>$time,
'summary'=>$_POST['summary'],
'content'=>$_POST['content'],
'cover'=>$_POST['cover_url']
],['uid'=>UID,'id'=>$_POST['id']],[1,'保存成功']);
}
}
//删除文章
function del_article(){
$id = json_decode($_POST['id']);
if(empty($id)) msg(-1,'参数错误');
delete_db('user_article_list',['uid'=>UID,'id'=>$id],[1,'操作成功']);
}
//修改分类
function up_category(){
$id = json_decode($_POST['id']);
if(empty($id)) msg(-1,'参数错误');
if(empty($_POST['category_id']) || !has_db('user_categorys',['uid'=>UID,'cid'=>$_POST['category_id']])){
msg(-1,'分类不存在');
}
update_db('user_article_list',['category'=>$_POST['category_id']],['uid'=>UID,'id'=>$id],[1,'操作成功']);
}
//修改状态
function up_state(){
$id = json_decode($_POST['id']);
if(empty($id)) msg(-1,'参数错误');
if(!in_array($_POST['state_id'],['1','2','3','4'])){
msg(-1,'状态参数错误');
}
update_db('user_article_list',['state'=>$_POST['state_id']],['uid'=>UID,'id'=>$id],[1,'操作成功']);
}
//保存设置 (与站点配置共享)
function save_article_set(){
//检查配置参数
if(!in_array($_POST['visual'],['0','1','2']) || !in_array($_POST['icon'],['0','1','2'])){
msg(-1,'参数错误');
}
//读取站点配置
$s_site = unserialize(get_db('user_config','v',['uid'=>UID,'k'=>'s_site']));
$s_site['article_visual'] = $_POST['visual'];
$s_site['article_icon'] = $_POST['icon'];
update_db("user_config",["v"=>$s_site],["k"=>'s_site',"uid"=>UID],[1,'保存成功']);
}

View File

@@ -1,6 +1,6 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
if ( in_array($method,['link_list','get_a_link','q_category_link','category_list','get_a_category','check_login','add_link']) && function_exists($method) ) {
if (function_exists($method)) {
$method();
}else{
Amsg(-1,'方法未找到 >> '.$method);
@@ -42,17 +42,83 @@ function add_link(){
insert_db('user_links',$data);
msgA(['code'=>0,'id'=>$lid]);
}
//编辑链接
function edit_link(){
$lid = intval(@$_POST['id']);
$fid = intval(@$_POST['fid']);
$title = $_POST['title'];
$url = $_POST['url'];
$description = empty($_POST['description']) ? '' : $_POST['description'];
$property = empty($_POST['property']) ? 0 : 1;
//检测链接是否合法
check_link($fid,$title,$url,'');
//描述长度检测
$length_limit = unserialize(get_db("global_config","v",["k"=>"length_limit"]));
if($length_limit['l_desc'] > 0 && strlen($description) > $length_limit['l_desc'] ){
msg(-1,'描述长度不能大于'.$length_limit['l_desc'].'个字节');
}
//关键字长度检测
if($length_limit['l_key'] > 0 && strlen($keywords) > $length_limit['l_key'] ){
msg(-1,'关键字长度不能大于'.$length_limit['l_key'].'个字节');
}
//检查链接是否已存在
if(has_db('user_links',['uid'=>UID ,'lid[!]'=>$lid, "url" => $url])){msg(-1011,'链接已存在!');}
//检查链接ID是否存在
if(!has_db('user_links',['uid'=>UID ,'lid'=>$lid])){msg(-1012,'链接ID不存在!');}
$data = [
'fid' => $fid,
'title' => htmlspecialchars($title,ENT_QUOTES),
'url' => $url,
'description' => htmlspecialchars($description,ENT_QUOTES),
'up_time' => time(),
'property' => $property
];
//更新数据
update_db('user_links',$data,['uid'=>UID,'lid'=>$lid ]);
msgA(['code'=>0,'msg'=>'successful']);
}
//删除链接
function del_link(){
$lid = intval(trim($_REQUEST['id']));
if(empty($lid)){
msg(-1010,'链接ID不能为空');
}
$where['lid'] = $lid;
$where['uid'] = UID;
if(!has_db('user_links',$where)){
msg(-1010,'链接id不存在');
}
delete_db('user_links',$where,[0,'删除成功']);
}
//搜索链接
function global_search(){
$keyword = htmlspecialchars($_REQUEST['keyword']);
if( strlen($keyword) < 2 ) {
msg(-2000,'关键字的长度太短');
}elseif( strlen($keyword) > 32 ) {
msg(-2000,'关键字长度过长');
}
$where['uid'] = UID;
$where['status'] = 1;
$where['AND']['OR'] = ["title[~]" => $keyword,"url[~]" => $keyword, "url_standby[~]" => $keyword,"description[~]" => $keyword];
$where['ORDER'] = ['weight'=>'DESC'];
$field = ['lid(id)','fid','status','property','title','url','url_standby','weight','description','click','add_time','up_time'];
$datas = select_db('user_links',$field,$where);
links_add_category_field($datas); //添加分类信息
msgA(['code'=>0,'msg'=>'获取成功','count'=>count($datas),'data'=>$datas]);
}
//查询链接列表
function link_list(){
$page = empty(intval($_REQUEST['page'])) ? 1 : intval($_REQUEST['page']);
$limit = empty(intval($_REQUEST['limit'])) ? 50 : intval($_REQUEST['limit']);
$offset = ($page - 1) * $limit; //起始行号
$where['uid'] = UID;
$where['AND']['status'] = 1;
if(Access_Type != 'all'){
$where['property'] = 0;
}
$where['status'] = 1;
$count = count_db('user_links',$where); //统计条数
//权重排序(数字小的排前面)
$where['ORDER']['weight'] = 'ASC';
@@ -61,6 +127,7 @@ function link_list(){
$where['LIMIT'] = [$offset,$limit];
//查询
$datas = select_db('user_links',['lid(id)','fid','property','title','url','url_standby','weight','description','icon','click','add_time','up_time'],$where);
links_add_category_field($datas); //添加分类信息
msgA(['code'=>0,'msg'=>'获取成功','count'=>$count,'data'=>$datas]);
}
//查询单个链接
@@ -68,7 +135,6 @@ function get_a_link(){
$lid = intval(trim($_REQUEST['id']));
if(empty($lid)){
msg(-1,'id不能为空');
}
$where['lid'] = $lid;
$where['uid'] = UID;
@@ -76,11 +142,7 @@ function get_a_link(){
if(empty($link_info)){
msgA(['code'=>-1,'msg'=>'没有找到链接信息','data'=>[]]);
}else{
if(Access_Type == 'all' || $link_info['property'] == 0){
msgA(['code'=>0,'data'=>$link_info]);
}else{
msgA(['code'=>-1,'msg'=>'私有链接,无权查看','data'=>[]]);
}
msgA(['code'=>0,'data'=>$link_info]);
}
}
//查询指定分类的链接
@@ -92,9 +154,6 @@ function q_category_link(){
$where['uid'] = UID;
$where['AND']['status'] = 1;
$where['AND']['fid'] = $category_id;
if(Access_Type != 'all'){
$where['property'] = 0;
}
$count = count_db('user_links',$where); //统计条数
//权重排序(数字小的排前面)
@@ -104,17 +163,120 @@ function q_category_link(){
$where['LIMIT'] = [$offset,$limit];
//查询
$datas = select_db('user_links',['lid(id)','fid','property','title','url','url_standby','weight','description','icon','click','add_time','up_time'],$where);
links_add_category_field($datas); //添加分类信息
msgA(['code'=>0,'msg'=>'获取成功','count'=>$count,'data'=>$datas]);
}
//查询分类列表
function category_list(){
$where = ['uid'=>UID,'status'=>1,'ORDER' => ['weight'=>'ASC']];
if(Access_Type != 'all'){
$where['property'] = 0;
}
$datas = select_db('user_categorys',['cid(id)','fid','property','name','add_time','up_time','weight','description','font_icon'],$where);
msgA(['code'=>0,'msg'=>'获取成功','count'=>count($datas),'data'=>$datas ]);
}
//添加分类
function add_category(){
if(empty($_POST['name'])){
msg(-1,'分类名称不能为空');
}elseif(!preg_match('/^(fa fa-|layui-icon layui-icon-)([A-Za-z0-9]|-)+$/',$_POST['font_icon'])){
$_POST['font_icon'] = 'fa fa-star-o';
}
//分类名查重
if(get_db('user_categorys','cid',['uid'=>UID ,"name" => $_POST['name']])){
msg(-1,'分类名称已存在');
}
//父分类不能是二级分类
if(intval($_POST['fid']) !=0 && get_db('user_categorys','fid',['uid'=>UID ,"cid" => intval($_POST['fid']) ]) !=0 ){
msg(-1,'父分类不能是二级分类');
}
//长度检测
$length_limit = unserialize(get_db("global_config","v",["k"=>"length_limit"]));
if($length_limit['c_name'] > 0 && strlen($_POST['name']) > $length_limit['c_name'] ){
msg(-1,'名称长度不能大于'.$length_limit['c_name'].'个字节');
}
if($length_limit['c_desc'] > 0 && strlen($_POST['description']) > $length_limit['c_desc'] ){
msg(-1,'名称长度不能大于'.$length_limit['c_desc'].'个字节');
}
//取最大CID
$cid = get_maxid('category_id');
//插入数据库
insert_db('user_categorys',[
'uid'=>UID,
'cid'=>$cid,
'fid'=>intval($_POST['fid']??'0'),
'pid'=>0,
'status'=>1,
'property'=>intval($_POST['property']??'0'),
'name'=>htmlspecialchars($_POST['name'],ENT_QUOTES),
'add_time'=>time(),
'up_time'=>time(),
'weight'=>$cid,
'description'=>htmlspecialchars($_POST['description'],ENT_QUOTES),
'font_icon'=>$_POST['font_icon'],
'icon'=>''
],[0,'添加成功']
);
}
//编辑分类
function edit_category(){
if(empty($_POST['name'])){
msg(-1,'分类名称不能为空');
}elseif(!preg_match('/^(fa fa-|layui-icon layui-icon-)([A-Za-z0-9]|-)+$/',$_POST['font_icon'])){
$_POST['font_icon'] = 'fa fa-star-o';
}
//父分类不能是自己
if($_POST['id'] == $_POST['fid']){
msg(-1,'父分类不能是自己');
}
//查CID是否存在
if(!get_db('user_categorys','cid',['uid'=>UID ,"cid" => intval($_POST['id'])])){
msg(-1,'分类不存在');
}
//分类名查重(排除自身)
if(get_db('user_categorys','cid',['uid'=>UID,'cid[!]'=>intval($_POST['id']),"name" => $_POST['name']])){
msg(-1,'分类名称已存在');
}
//父分类不能是二级分类
if(intval($_POST['fid']) !=0 && get_db('user_categorys','fid',['uid'=>UID ,"cid" => intval($_POST['fid']) ]) !=0 ){
msg(-1,'父分类不能是二级分类');
}
//分类下存在子分类,禁止修改父分类
if( $_POST['fid']!=0 && count_db('user_categorys',['uid'=>UID,'fid'=>$_POST['id']])>0){
msg(-1,'该分类下已存在子分类!');
}
//查父分类是否存在
if( $_POST['fid'] !=0 && !get_db('user_categorys','cid',['uid'=>UID ,"cid" => intval($_POST['fid'])])){
msg(-1,'父分类不存在');
}
//长度检测
$length_limit = unserialize(get_db("global_config","v",["k"=>"length_limit"]));
if($length_limit['c_name'] > 0 && strlen($_POST['name']) > $length_limit['c_name'] ){
msg(-1,'名称长度不能大于'.$length_limit['c_name'].'个字节');
}
if($length_limit['c_desc'] > 0 && strlen($_POST['description']) > $length_limit['c_desc'] ){
msg(-1,'名称长度不能大于'.$length_limit['c_desc'].'个字节');
}
//更新数据
$data = [
'fid'=>$_POST['fid'],
'property'=>intval($_POST['property']??'0'),
'name'=>$_POST['name'],
'up_time'=>time(),
'description'=>$_POST['description']??'',
'font_icon'=>$_POST['font_icon'],
];
if(!isset($_POST['fid'])){ //为空时不修改父id,避免二级变一级
unset($data['fid']);
}
if(!isset($_POST['font_icon'])){
unset($data['font_icon']);
}
update_db('user_categorys',$data,['uid'=>UID ,"cid"=>intval($_POST['id'])],[0,'successful']);
}
//查询单个分类信息
function get_a_category(){
$cid = intval(trim($_REQUEST['id']));
@@ -127,19 +289,33 @@ function get_a_category(){
if(empty($category_info)){
msgA(['code'=>-1,'msg'=>'没有找到分类信息','data'=>[]]);
}else{
if(Access_Type == 'all' || $category_info['property'] == 0){
msgA(['code'=>0,'data'=>$category_info]);
}else{
msgA(['code'=>-1,'msg'=>'私有分类,无权查看','data'=>[]]);
}
msgA(['code'=>0,'data'=>$category_info]);
}
}
//是否已登录
//获取TwoNav信息
function app_info(){
$data['php_version'] = floatval(PHP_VERSION);
$data['onenav_version'] = SysVer;
$data['cat_num'] = count_db('user_categorys',['uid'=>UID])??0;
$data['link_num'] = count_db('user_links',['uid'=>UID])??0;
$data['username'] = U;
msgA(['code'=>200,'msg'=>'success','data'=>$data]);
}
//是否已登录,由于上游已经拦截未登录状态,所以这里固定返回已登录
function check_login(){
if(Access_Type == 'open'){
msgA(['code'=>-1002,'data'=>'false','err_msg'=>'Authorization failure!']);
}else{
msgA(['code'=>200,'data'=>'true','msg'=>'success']);
msgA(['code'=>200,'data'=>'true','msg'=>'success']);
}
//给链接数组添加分类字段
function links_add_category_field(&$arr){
$where['uid'] = UID;
$where['status'] = 1;
$categorys = select_db('user_categorys',['cid(id)','name'],$where);
$newCategorys = array_column($categorys,'name','id');
foreach ($arr as &$data) {
$data['category_name'] = $newCategorys[$data['fid']];
}
}
return $arr;
}

View File

@@ -48,10 +48,20 @@ function other_upsys(){
}
//设置执行最长时间0为无限制。单位秒!
set_time_limit(5*60);
$overtime = !isset($GLOBALS['global_config']['Update_Overtime']) ? 3 : ($GLOBALS['global_config']['Update_Overtime'] < 3 || $GLOBALS['global_config']['Update_Overtime'] > 60 ? 3 : $GLOBALS['global_config']['Update_Overtime']);
//加载远程数据
$urls = [ "https://update.lm21.top/TwoNav/updata.json"];
foreach($urls as $url){
$Res = ccurl($url,3);
$urls = [
"lm21" => "https://update.lm21.top/TwoNav/updata.json",
"gitee" => "https://gitee.com/tznb/twonav_updata/raw/master/updata.json"
];
$Source = $GLOBALS['global_config']['Update_Source'] ?? '';
if (!empty($Source) && isset($urls[$Source])) {
$urls = [$Source => $urls[$Source]];
}
foreach($urls as $key => $url){
$Res = ccurl($url,$overtime);
$data = json_decode($Res["content"], true);
if($data["code"] == 200 ){ //如果获取成功
break; //跳出循环.
@@ -511,7 +521,7 @@ function write_sys_settings(){
}
//长度限制
foreach (['c_name','c_desc','l_name','l_url','l_desc'] as $name){
foreach (['c_name','c_desc','l_name','l_url','l_key','l_desc'] as $name){
$length_limit[$name] = is_subscribe('bool') ? intval($_POST[$name]) : 0;
}
write_global_config("length_limit",$length_limit,'长度限制');
@@ -537,10 +547,15 @@ function write_sys_settings(){
'global_header'=>['empty'=>true],
'global_footer'=>['empty'=>true],
'api_extend'=>['empty'=>true],
'c_code'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'自定义代码参数错误'],
//更新设置
'Update_Source'=>['empty'=>true],
'Update_Overtime'=>['int'=>true,'min'=>3,'max'=>60,'msg'=>'资源超时参数错误'],
//扩展功能-(全局开关)
'apply'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'收录管理参数错误'],
'guestbook'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'留言管理参数错误'],
'link_extend'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'链接扩展参数错误'],
'article'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'文章管理参数错误']
];
$o_config = [];
foreach ($datas as $key => $data){
@@ -561,9 +576,30 @@ function write_sys_settings(){
if($_POST['apply'] == 1){$o_config['apply'] = 0;$filter = true;}
if($_POST['guestbook'] == 1){$o_config['guestbook'] = 0;$filter = true;}
if($_POST['link_extend'] == 1){$o_config['link_extend'] = 0;$filter = true;}
if($_POST['article'] == 1){$o_config['article'] = 0;$filter = true;}
}
//检测于下载文章管理依赖资源
clearstatcache();
if($o_config['article'] == 1 && ( !is_file('./static/wangEditor/wangEditor.js') || !is_file('./static/wangEditor/wangEditor.css'))){
$filePath = "./data/temp/wangEdito.tar.gz";
if(downFile('https://update.lm21.top/TwoNav/updata/wangEdito.tar.gz','wangEdito.tar.gz','./data/temp/')){
$file_md5 = md5_file($filePath);
if($file_md5 != "95f830656ba8972cca39a1ddd6ebaeda"){
unlink($filePath);
msg(-1,'效验wangEdito失败<br/>!');
}
}else{
msg(-1,'下载wangEdito失败,请重试!<br/>如需手动安装可联系技术支持!');
}
try {
$phar = new PharData($filePath);
$phar->extractTo('./static/', null, true);
unlink($filePath);
clearstatcache();
} catch (Exception $e) {
msg(-1,'安装wangEdito失败');
}
}
update_db("global_config", ["v" => $o_config], ["k" => "o_config"],[1,($filter ?"保存成功,未检测到有效授权,带*号的配置无法为你保存":"保存成功")]);
}
@@ -574,7 +610,7 @@ function write_default_settings(){
if(!is_subscribe('bool')){
msg(-1,'未检测到有效授权');
}
if( $_POST['KeyClear'] > $_POST['Session']){
if(intval($_POST['Session']) > 0 && intval($_POST['KeyClear']) > intval($_POST['Session'])){
msg(-1,'Key清理时间不能大于登录保持时间');
}
// 安全配置(登录配置)
@@ -583,7 +619,8 @@ function write_default_settings(){
'HttpOnly'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'HttpOnly参数错误'],
'KeySecurity'=>['int'=>true,'min'=>0,'max'=>2,'msg'=>'Key安全参数错误'],
'KeyClear'=>['int'=>true,'min'=>1,'max'=>60,'msg'=>'Key清理参数错误'],
'api_model'=>['v'=>['security','compatible','compatible+open'],'msg'=>'API模式参数错误']
'api_model'=>['v'=>['security','compatible','compatible+open'],'msg'=>'API模式参数错误'],
'login_page'=>['v'=>['admin','index','auto'],'msg'=>'登录成功参数错误']
];
foreach ($datas as $key => $data){
if($data['int']){
@@ -723,6 +760,25 @@ function other_root(){
if(!is_subscribe('bool')){msg(-1,"未检测到有效授权,无法使用该功能!");}
write_global_config('icon_config',$_POST,'图标配置');
msg(1,'保存成功');
}elseif($_GET['type'] == 'write_icon_del_cache'){
//删除数据库缓存信息
if(empty(count_db('global_icon','*'))){
msg(-1,'无缓存记录..');
}
delete_db('global_icon','*');
//删除缓存目录下的所有文件
$files = glob(DIR.'/data/icon' . '/*');
if (empty($files)) {
msg(-1,'无缓存文件..');
}
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
msg(1,'操作成功');
}
}

View File

@@ -1,7 +1,8 @@
<?php if(!defined('DIR')){Not_Found();}AccessControl();
//负责过渡页/跳转/隐私保护/密码访问
$id = intval($_GET['id']);
//IP数统计
count_ip();
//如果id为空,则显示404
if(empty($id)){Not_Found();}
@@ -128,6 +129,22 @@ if($global_config['link_extend'] == 1 && check_purview('link_extend',1) && in_ar
$extend = empty($link['extend']) ? [] : unserialize($link['extend']);
}
//载入过渡页设置
$transition_page = unserialize(get_db("user_config", "v", ["uid"=>UID,"k"=>"s_transition_page"]));
//关键字处理
if(!empty($link['url_standby']) || $site['link_model'] == 'Transition'){
if(empty($link['keywords'])){
if($transition_page['default_keywords'] == '0'){
$link['keywords'] = $link['title'];
}else if($transition_page['default_keywords'] == '1'){
$link['keywords'] = $site['keywords'];
}else{
$link['keywords'] = $link['title'];
}
}
}
//如果存在备用链接,则强制载入过渡页
if(!empty($link['url_standby'])) {
$link['url_standby'] = unserialize($link['url_standby']);

View File

@@ -44,22 +44,22 @@ foreach($_POST as $key =>$value){
$title = $_POST['title'];
$url = $_POST['url'];
$iconurl = $_POST['iconurl'];
$description = $_POST['description'];
$iconurl = $_POST['iconurl'] ?? '';
$description = $_POST['description'] ?? '';
$category_id = intval ($_POST['category_id']);
$email = $_POST['email'];
$email = $_POST['email'] ?? '';
$user_ip = Get_IP();
if( !filter_var($url, FILTER_VALIDATE_URL) ) {
msg(-1,'URL无效!');
}elseif( !empty($iconurl) && !filter_var($iconurl, FILTER_VALIDATE_URL) ){
}elseif(!empty($apply['iconurl']) && !filter_var($iconurl, FILTER_VALIDATE_URL) ){
msg(-1,'网站图标无效!');
}elseif(!preg_match('/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/',$email)){
}elseif(!empty($apply['email']) && !preg_match('/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/',$email)){
msg(-1,'联系邮箱无效!');
}elseif(!isset($_POST['category_id'])){
msg(-1,'分类ID不能为空!');
}elseif(!isset($_POST['title'])){
msg(-1,'网站标题不能为空!');
}elseif(!isset($_POST['description'])){
}elseif(!empty($apply['description']) && empty($_POST['description'])){
msg(-1,'网站描述不能为空!');
}
//获取和检查分类信息

65
system/expand/article.php Normal file
View File

@@ -0,0 +1,65 @@
<?php if(!defined('DIR')){Not_Found();}AccessControl();
if($global_config['article'] == 0 | !check_purview('article',1)){
Not_Found();
}
$id = intval($_GET['id']);
//IP数统计
count_ip();
//如果id为空,则显示404
if(empty($id)){Not_Found();}
//查询文章
$where['uid'] = UID;
if(!is_login()){
$where['state'] = 1; //状态筛选
}
$where['id'] = $id;
$data = get_db('user_article_list','*',$where);
//查找失败时显示404
if(empty($data)){Not_Found();}
//var_dump($data);
//exit;
//站点设置和站点图标
$site = unserialize(get_db('user_config','v',['uid'=>UID,'k'=>'s_site']));
$site['Title'] = $site['title'].(empty($site['subtitle'])?'':' - '.$site['subtitle']);
//免费用户请保留版权,谢谢!
$copyright = empty($global_config['copyright'])?'<a target="_blank" href="https://gitee.com/tznb/TwoNav">Copyright © TwoNav</a>':$global_config['copyright'];
$ICP = empty($global_config['ICP'])?'':'<a target="_blank" href="https://beian.miit.gov.cn">'.$global_config['ICP'].'</a>';
$favicon = ( !empty($site['site_icon_file'])) ? $site['site_icon'] : './favicon.ico';
//取模板信息
require DIR ."/system/templates.php";
$dir_path = DIR.'/templates/article/'.$s_templates['article'];
$theme_dir = str_replace(DIR.'/templates/article',"./templates/article",$dir_path);
$path = $dir_path.'/index.php';
//检查是否存在,不存在则使用默认
if(!is_file($path)){
$path= DIR.'/templates/article/default/index.php';
$theme_dir = './templates/article/default';
}
//统计点击数
update_db("user_article_list", ["browse_count[+]"=>1],['uid'=>UID,'id'=>$id]);
//读取用户主题配置
$theme_config_db = unserialize(get_db('user_config','v',['t'=>'theme_article','k'=>$s_templates['article'],'uid'=>UID]));
//读取默认主题配置
$theme_info = json_decode(@file_get_contents($theme_dir.'/info.json'),true);
$theme_config = empty($theme_info['config']) ? []:$theme_info['config'];
$theme_ver = !Debug?$theme_info['version']:$theme_info['version'].'.'.time();
//合并配置数据
$theme_config = empty($theme_config_db) ? $theme_config : array_merge ($theme_config??[],$theme_config_db??[]);
require $path;
exit;
//返回404
function Not_Found() {
header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;
}

View File

@@ -11,7 +11,9 @@ foreach($_POST as $key =>$value){
if($method =='write_site_setting' && ($key =='custom_header' || $key =='custom_footer')){
continue;
}
if($method == 'write_article'){
continue;
}
if(preg_match('/<(iframe|script|body|img|layer|div|meta|style|base|object|input)/i',$value)){
$code = 2001;
}elseif(preg_match('/(onmouseover|onerror|onload)\=/i',$value)){

View File

@@ -1,5 +1,4 @@
<?php
// 来源 https://blog.mimvp.com/article/23089.html
function get_page_info($output, $friend_link = '', $curl_info=array()) {
$page_info = array();
@@ -71,16 +70,39 @@ function get_page_info($output, $friend_link = '', $curl_info=array()) {
preg_match('/<META\s+content="([\w\W]*?)"\s+scheme="([\w\W]*?)"/si', $meta_str, $res);
if(!empty($res)) $meta_array[strtolower($res[2])] = $res[1];
// 20230716 新增匹配语法
preg_match('/<META\s+content=[\'"](.*?)[\'"]\s+itemprop=[\'"](.*?)[\'"]\s+name=[\'"](.*?)[\'"]>/si', $meta_str, $res);
if(!empty($res)) $meta_array[strtolower($res[3])] = $res[1];
preg_match('/<meta\s+itemprop=[\'"](.*?)[\'"]\s+name=[\'"](.*?)[\'"]\s+content=[\'"](.*?)[\'"]>/si', $meta_str, $res);
if(!empty($res)) $meta_array[strtolower($res[2])] = $res[3];
}
//如果正则匹配失败则使用php函数尝试再次匹配
if(empty($meta_array['keywords']) || empty($meta_array['description'])){
//将html保存为临时文件
$key = md5(uniqid().Get_Rand_Str(8));
$tempFile = DIR ."/data/temp/".md5(uniqid().Get_Rand_Str(8)).".html";
file_put_contents($tempFile, $output);
$tags = get_meta_tags($tempFile);
unlink($tempFile); //删除临时文件
if(empty($meta_array['keywords']) && !empty($tags['keywords'])){
$meta_array['keywords'] = $tags['keywords'];
}
if(empty($meta_array['description']) && !empty($tags['description'])){
$meta_array['description'] = $tags['description'];
}
}
$page_info['site_keywords'] = $meta_array['keywords'];
$page_info['site_description'] = $meta_array['description'];
//$page_info['meta_array'] = $meta_array; //暂时不需要全部meta
# 判断是否存在友链
if(!empty($friend_link) && strstr($output, $friend_link) != "") {
$page_info['friend_link_status'] = 1;
}
return $page_info;
}

View File

@@ -10,6 +10,10 @@ if(!is_login && ($global_config['Privacy'] == 1 || !check_purview('Common_home',
}
//载入站点设置
$site = unserialize(get_db('user_config','v',['uid'=>UID,'k'=>'s_site']));
//如果没有权限则清除自定义代码
if(!check_purview('header',1)){$site['custom_header'] = '';}
if(!check_purview('footer',1)){$site['custom_footer'] = '';}
$site['Title'] = $site['title'].(empty($site['subtitle'])?'':' - '.$site['subtitle']);
//免费用户请保留版权,谢谢!
$copyright = empty($global_config['copyright'])?'<a target="_blank" href="https://gitee.com/tznb/TwoNav">Copyright © TwoNav</a>':$global_config['copyright'];
@@ -73,7 +77,7 @@ $theme_ver = !Debug?$theme_info['version']:$theme_info['version'].'.'.time();
$site['ex_theme'] = in_array($theme,['snail-nav','heimdall']); //例外主题,不支持热门网址/最新网址/输出上限
//分类查找条件
$categorys = []; //声明一个空数组
$content = ['cid(id)','name','property','font_icon','icon','description'];//需要的内容
$content = ['cid(id)','fid','name','property','font_icon','icon','description'];//需要的内容
$where['uid'] = UID;
$where['fid'] = 0;
$where['status'] = 1;
@@ -98,7 +102,7 @@ function get_category_sub($id) {
if(!empty($share)){
$where['cid'] = $data;
}
$content = ['cid(id)','name','property','font_icon','icon','description'];
$content = ['cid(id)','name','fid','property','font_icon','icon','description'];
$where['uid'] = UID;
$where['fid'] = intval($id);
$where['status'] = 1;
@@ -159,7 +163,7 @@ function get_links($fid) {
$where['LIMIT'] = $site['max_link'];
$max_link = true;
}
$links = select_db('user_links',['lid(id)','fid','property','title','url(real_url)','url_standby','description','icon','click','pid'],$where);
$links = select_db('user_links',['lid(id)','fid','property','title','url(real_url)','url_standby','description','icon','click','pid','extend'],$where);
foreach ($links as $key => $link) {
$click = false; $lock = false;
@@ -193,7 +197,41 @@ function get_links($fid) {
//获取图标链接
$links[$key]['ico'] = $lock ? $GLOBALS['libs'].'/Other/lock.svg' : geticourl($site['link_icon'],$link);
$links[$key]['type'] = 'link';
}
//处理扩展信息
if($GLOBALS['global_config']['link_extend'] == 1 && check_purview('link_extend',1) && in_array($GLOBALS['theme_info']['support']['link_extend'],["true","1"])){
foreach ($links as &$link) {
if(!empty($link['extend'])){
$link = array_merge ($link,unserialize($link['extend']));
}
}
}
//生成文章链接, 条件:非隐藏,且主题未声明不显示文章
if( intval($site['article_visual'] ?? '1') > 0 && $GLOBALS['theme_info']['support']['article'] != 'notdisplay'){
$articles = get_article_list($fid);
foreach ($articles['data'] as $article) {
$url = "./index.php?c=article&id={$article['id']}&u={$u}";
if($site['article_icon'] == '1'){ //站点图标
$icon = $GLOBALS['favicon'];
}elseif($site['article_icon'] == '2' && !empty($article['cover'])){ //封面
$icon = $article['cover'];
}else{ //首字
$icon = './system/ico.php?text='.mb_strtoupper(mb_substr($article['title'], 0, 1));
}
$article_link = ['type'=>'article','id'=>0,'title'=>$article['title'],'url'=>$url,'real_url'=>$url,'description'=>$article['summary'],'ico'=>$icon,'icon'=>$icon];
//判断靠前还是靠后
if($site['article_visual'] == '1'){
array_unshift($links,$article_link);
}else{
array_push($links,$article_link);
}
}
}
if($max_link && $count > $site['max_link']){
$oc_url = "./index.php?u={$u}&oc={$fid}" . (empty($_GET['theme']) ? '':"&theme={$_GET['theme']}");
array_push($links,['id'=>0,'title'=>'查看全部','url'=>$oc_url,'real_url'=>$oc_url,'description'=>'该分类共有'.$count.'条数据','ico'=>'./favicon.ico']);
@@ -284,5 +322,6 @@ if(empty($_GET['share']) && !$site['ex_theme']){
//访问统计
write_user_count(date('Ym'),'index_Ym');
write_user_count(date('Ymd'),'index_Ymd');
count_ip();
//载入模板
require($index_path);

View File

@@ -4,8 +4,8 @@ if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Fou
//初始化
session_name('TwoNav_initial');
session_start();
$layui['js'] = './static/Layui/v2.8.3/layui.js';
$layui['css'] = './static/Layui/v2.8.3/css/layui.css';
$layui['js'] = './static/Layui/v2.8.10/layui.js';
$layui['css'] = './static/Layui/v2.8.10/css/layui.css';
//判断请求类型
if($_SERVER['REQUEST_METHOD'] === 'POST'){
@@ -284,7 +284,7 @@ function Write_Config(){
//写站点配置
$o_config['Login'] = 'login'; //登录入口
$o_config['Register'] = 'register'; //注册入口
$o_config['RegOption'] = '1'; //注册配置
$o_config['RegOption'] = '0'; //注册配置
$o_config['Libs'] = './static'; //静态库路径
$o_config['Default_User'] = $_POST['User']; //默认用户
$o_config['XSS_WAF'] = '1'; //防XSS脚本
@@ -294,6 +294,7 @@ function Write_Config(){
$o_config['Maintenance'] = '0'; //维护模式
$o_config['Sub_domain'] = '0'; //二级域名
$o_config['copyright'] = ''; //版权信息
$o_config['c_code'] = '0'; //禁用默认用户使用自定义代码
insert_db("global_config", ["k" => "o_config","v" => $o_config,"d" => '网站配置']);
@@ -447,6 +448,25 @@ set_db_type(db_type);
layui.use(['form'], function(){
var form = layui.form;
//伪静态检测
var request = new XMLHttpRequest();
request.open('GET', './static/Other/login.css?t=' + new Date().getTime(), true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
var fileContent = request.responseText;
if (fileContent.startsWith('<!DOCTYPE html>')) {
layer.alert(
"系统检测到您的站点可能配置了不属于TwoNav的伪静态规则<br />通常是因为之前使用过其他程序,例如:OneNav Extend 或 OneNav <br />您需要将它清除,否则会影响到程序的正常使用 ( 如登录页异常 )<br />并在安装完成后在站长工具>生成伪静态>重新配置到站点中"
,{title:'环境异常提示',anim: 2,closeBtn: 0,btn: ['刷新页面']},function () {
location.reload();
}
);
}
}
};
request.send();
//开始安装
form.on('submit(register)', function(data){
var d = data.field;
@@ -510,12 +530,12 @@ function open_msg(u,p){
layer.open({ //弹出结果
type: 1
,title: '安装成功'
,area: ['230px', '220px']
,area: ['230px', '260px']
,maxmin: false
,shadeClose: false
,resize: false
,closeBtn: 0
,content: '<div style="padding: 15px;">管理员账号: '+u+'<br>管理员密码: '+p+'<br><h3><a href="?c=admin&u='+u+'" style="color: #0000FF;" class="fl"> <br> >>点我进入后台</a></h3><h3><a href="?u='+u+'" style="color: #0000FF;" class="fl"> <br> >>点我进入首页</a></h3></div>'
,content: '<div style="padding: 15px;">管理员账号: '+u+'<br>管理员密码: '+p+'<br><h3><a href="?c=admin&u='+u+'" style="color: #0000FF;" class="fl"> <br> >>点我进入后台</a></h3><h3><a href="?u='+u+'" style="color: #0000FF;" class="fl"> <br> >>点我进入首页</a></h3> <h3><a href="https://gitee.com/tznb/TwoNav/wikis/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/%E5%AE%89%E5%85%A8%E9%85%8D%E7%BD%AE" style="color: #0000FF;" class="fl" target="_blank"> <br> >>安全配置说明</a></h3> </div>'
});
}

View File

@@ -1,6 +1,5 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
//登录入口
require "./system/templates.php";
//如果是Get请求则载入登录模板
if($_SERVER['REQUEST_METHOD'] === 'GET'){
require DIR ."/system/templates.php";
@@ -18,29 +17,48 @@ if($_SERVER['REQUEST_METHOD'] === 'GET'){
AccessControl(); //访问控制
$User = $_POST["User"];$Password = $_POST["Password"]; //获取请求数据
if(empty($User)){
insert_db("user_log", ["uid" => '',"user"=>'',"ip"=>Get_IP(),"time"=>time(),"type" => 'login',"content"=>Get_Request_Content(),"description"=>"请求登录>账号为空"]);
msg(-1,'账号不能为空!');
}elseif($User != $USER_DB['User']){
insert_db("user_log", ["uid" => '',"user"=>$User,"ip"=>Get_IP(),"time"=>time(),"type" => 'login',"content"=>Get_Request_Content(),"description"=>"请求登录>账号不存在"]);
msg(-1,'账号不存在!');
}
//记录请求日志
insert_db("user_log", ["uid" => $USER_DB['ID'],"user"=>$USER_DB['User'],"ip"=>Get_IP(),"time"=>time(),"type" => 'login',"content"=>Get_Request_Content(),"description"=>"请求登录"]);
$log_id = $db->id();
//基础判断
if(!isset($User)){
update_db_db("user_log", ["description" => "请求登录>账号不能为空"], ["id"=>$log_id]);
msg(-1,'账号不能为空!');
}elseif(strlen($Password)!==32){
if(strlen($Password)!==32){
update_db("user_log", ["description" => "请求登录>密码错误(长度应该是32位的MD5)"], ["id"=>$log_id]);
msg(-1,'密码错误!');
}elseif($c != $global_config["Login"] && $c != $USER_DB['Login'] ){
update_db("user_log", ["description" => "请求登录>登录入口错误"], ["id"=>$log_id]);
msg(-1,"登录入口错误");
}elseif(strlen($_SERVER['HTTP_USER_AGENT'])>256){
}elseif(strlen($_SERVER['HTTP_USER_AGENT'])>1024){
update_db("user_log", ["description" => "请求登录>浏览器UA长度异常"], ["id"=>$log_id]);
msg(-1,"浏览器UA长度异常,请更换浏览器!");
}
$LoginConfig = unserialize( $USER_DB['LoginConfig'] );
//开启双重验证时验证OTP验证码
if(!empty($LoginConfig['totp_key'])){
if(empty($_POST['otp_code'])){
msgA(['code'=>-1,'msg'=>'您已开启双重验证,请输入OTP验证码']);
}
require DIR . '/system/Authenticator.php';
$totp = new PHPGangsta_GoogleAuthenticator();
$checkResult = $totp->verifyCode($LoginConfig['totp_key'], $_POST['otp_code'], 2);
if(!$checkResult){
msgA(['code'=>-1,'msg'=>'OTP验证码错误,请重试!']);
}
}
//计算请求密码和数据库的对比
if(Get_MD5_Password($Password,$USER_DB["RegTime"]) === $USER_DB["Password"]){
update_db("user_log", ["description" => "请求登录>登录成功"], ["id"=>$log_id]);
Set_key($USER_DB);
$LoginConfig = unserialize( $USER_DB['LoginConfig'] );
if(empty($LoginConfig['login_page']) || $LoginConfig['login_page'] == 'admin'){
$url = "./?c=admin&u={$USER_DB['User']}";
}elseif($LoginConfig['login_page'] == 'index'){

View File

@@ -234,7 +234,7 @@ function echo_pwds(){
}
//检查链接
function check_link($fid,$title,$url,$url_standby_s=''){
$pattern = "/^(http:\/\/|https:\/\/|ftp:\/\/|ftps:\/\/|sftp:\/\/|magnet:?|ed2k:\/\/|thunder:\/\/|tcp:\/\/|udp:\/\/|rtsp:\/\/).+/";
$pattern = "/^(http:\/\/|https:\/\/|ftp:\/\/|ftps:\/\/|sftp:\/\/|magnet:?|ed2k:\/\/|thunder:\/\/|tcp:\/\/|udp:\/\/|rtsp:\/\/|wsa:\/\/|vmrc:\/\/).+/";
$length_limit = unserialize(get_db("global_config","v",["k"=>"length_limit"]));
if (empty($fid)) msg(-1,'分类id(fid)不能为空');
if (empty($title)) msg(-1,'名称不能为空');
@@ -335,21 +335,39 @@ function Get_ExpireTime($day =30){
}
//验证登录
function is_login(){
global $USER_DB,$db;
global $USER_DB;
$time = time();
$LoginConfig = unserialize($USER_DB['LoginConfig']);
if (!function_exists('delete_expired_info')) {
function delete_expired_info($time,$LoginConfig){
global $USER_DB;
if(empty($LoginConfig['Session'])){
$where = [
"uid" => $USER_DB['ID'],
//"expire_time" => 0,
"OR" => [
"last_time[<]" => strtotime('-1 day'),
"login_time[<]" => strtotime('-15 day')
]
];
}else{
$where = [
"uid" => $USER_DB['ID'],
"OR" => [
"expire_time[<]" => $time,
"last_time[<]" => strtotime("-{$LoginConfig['KeyClear']} day")
]
];
}
//var_dump(select_db('user_login_info','*',$where),$where);exit;
delete_db("user_login_info", $where); //清理到期Key
update_db("global_user",["kct"=>$time],["User" => $USER_DB['User']]); //记录清理时间
}
}
//清理间隔30分钟(1800秒)
if( ($USER_DB['kct'] + 1800) < $time ){
$lt = $time - ($LoginConfig['KeyClear'] * 24 * 60 * 60);
$where = ["AND" =>
[
"uid" => $USER_DB['ID'],
"OR" => ["expire_time[<]" => $time,"last_time[<]" => $lt]
]
];
delete_db("user_login_info", $where); //清理到期Key
update_db("global_user",["kct"=>$time],["User" => $USER_DB['User']]); //记录清理时间
delete_expired_info($time,$LoginConfig);
}
//查询登录信息
@@ -359,26 +377,22 @@ function is_login(){
//没找到返回未登录
if(empty($info)){return false;}
//UA验证
if($LoginConfig['KeySecurity'] > 0 && $_SERVER['HTTP_USER_AGENT'] != $info['ua']){return false;}
//IP验证
if($LoginConfig['KeySecurity'] > 1 && Get_IP() != $info['ip']){return false;}
//到期验证(同时重新计算)
if( $info['expire_time'] != 0 && ($time > $info['expire_time'] || $time > ($info['login_time'] + ($LoginConfig['Session'] * 24 * 60 * 60) ) )){
delete_db("user_login_info", $where);
return false;
}
//会话Key验证(没有到期时间时如果距上次访问时间大于24小时认为无效)
if($info['expire_time'] == 0 && ($info['last_time'] + 86400) < $time){
delete_db("user_login_info", $where);
return false;
}//有到期时间,且开启了Key清理
elseif($LoginConfig['KeyClear'] != 0 && ($info['last_time'] + ($LoginConfig['KeyClear'] * 24 * 60 * 60)) < $time ){
delete_db("user_login_info", $where);
return false;
//根据登录保持选项来判断key是否有效
if(empty($LoginConfig['Session'])){ //浏览器关闭时
if($info['last_time'] < strtotime('-1 day') || $info['login_time'] < strtotime('-15 day')){ //上次访问超过1天 或 登录时间超过15天
delete_expired_info($time,$LoginConfig);
return false;
}
}else{ //保持天数(已到期或上次访问时间超时)
if($info['expire_time'] < $time || $info['last_time'] < strtotime("-{$LoginConfig['KeyClear']} day")){
delete_expired_info($time,$LoginConfig);
return false;
}
}
//Key验证
@@ -431,10 +445,10 @@ function is_subscribe($type = 'bool'){
$count = count($host);
if($count != 2){
$data['host'] = $host[$count-2].'.'.$host[$count-1];
//如果存在端口则去除
if(preg_match("/(.+):\d+/",$data['host'],$host)) {
$data['host'] = $host[1];
}
}
//如果存在端口则去除
if(preg_match("/(.+):\d+/",$data['host'],$host)) {
$data['host'] = $host[1];
}
}
if(!stristr($data['domain'],$data['host'])){
@@ -516,7 +530,7 @@ function get_http_code($url,$TIMEOUT = 10 ,$NOBODY = true) {
return $return;
}
function ccurl($url,$overtime = 3){
function ccurl($url,$overtime = 3,$Referer = false){
try {
$curl = curl_init ( $url ) ; //初始化
curl_setopt($curl, CURLOPT_TIMEOUT, $overtime ); //超时
@@ -525,6 +539,11 @@ function ccurl($url,$overtime = 3){
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
if($Referer === true){
curl_setopt($curl, CURLOPT_REFERER, $_SERVER['HTTP_REFERER']);
}elseif(!empty($Referer)){
curl_setopt($curl, CURLOPT_REFERER, $Referer);
}
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36');
$Res["content"] = curl_exec ( $curl ) ;
$Res["code"] = curl_getinfo($curl, CURLINFO_HTTP_CODE);
@@ -628,6 +647,10 @@ function is_Duplicated($array, $field){
//检查权限(有权限返回true 没有权限时根传递参数1是返回false 2是直接返回错误信息)
function check_purview($name,$return_type){
global $USER_DB;
//230705新增,禁止判断默认用户是否可以使用自定义代码
if($USER_DB['UserGroup'] == 'default' && $GLOBALS['global_config']['c_code'] != '1' && ( $name == 'header' || $name == 'footer' )){
return false;
}
if($USER_DB['UserGroup'] == 'root' || $USER_DB['UserGroup'] == 'default'){
return true;
}
@@ -716,4 +739,22 @@ function send_email($config){
msg(-1,'发送失败');
}
}
}
}
//统计访问ip数
function count_ip(){
$ip = Get_IP(); //取访客IP
$k = date('Ymd'); $t = 'ip_list';
$ip_list = get_db('user_count','e',['uid'=>UID,'k'=>$k,'t'=>$t]); //取列表
$ip_list = empty($ip_list) ? [] : unserialize($ip_list); //反序列化
//判断IP是否存在列表中
if(!in_array($ip, $ip_list)){
$ip_list[] = $ip; //加入列表
if(!has_db('user_count',['uid'=>UID,'t'=>$t,'k'=>$k])){
insert_db("user_count", ['uid'=>UID,"k"=>$k,"e"=>$ip_list,'t'=>$t]);
}else{
update_db("user_count", ["e"=>$ip_list],['uid'=>UID,'t'=>$t,'k'=>$k]);
}
write_user_count($k,'ip_count');//访问ip数+1
}
}

View File

@@ -14,7 +14,7 @@ if(empty($s_templates)){
}
//载入辅助函数
if(empty($c) || in_array($c,['index','click'])){
if(empty($c) || in_array($c,['index','click','article'])){
//将URL转换为base64编码
function base64($url){
$urls = parse_url($url);
@@ -98,3 +98,83 @@ if(empty($c) || in_array($c,['index','click'])){
}
//获取文章列表
function get_article_list($category = 0,$limit = 0){
$where['uid'] = UID;
if(!is_login()){
$where['AND']['state'] = 1; //状态筛选
}else{
$where['AND']['OR']['state'] = [1,2]; //状态筛选
}
//分类筛选
if($category > 0){
$where['AND']['category'] = $category;
}
//统计条数
$count = count_db('user_article_list',$where);
//获取条数
if($limit > 0){
$where['LIMIT'] = [0,$limit];
}
//获取文章列表
$datas = select_db('user_article_list','*',$where);
//查询分类
$categorys = select_db('user_categorys',['cid(id)','name'],['uid'=>UID]);
$categorys = array_column($categorys,'name','id');
//为文章添加分类名称
foreach ($datas as &$data) {
$data['category_name'] = $categorys[$data['category']] ?? 'Null';
}
return ['data'=>$datas,'count'=>$count];
}
//根据文章id获取内容
function get_article_content($id){
$where['uid'] = UID;
if(!is_login()){
$where['AND']['state'] = 1; //状态筛选
}else{
$where['AND']['OR']['state'] = [1,2]; //状态筛选
}
$where['id'] = $id;
$data = get_db('user_article_list','*',$where);
$data['category_name'] = get_db('user_categorys','name',['uid'=>UID,'cid'=>$data['category']]);
return $data;
}
//获取分类列表
function get_category_list($layer = false){
//查询条件
$where = [];
$where['uid'] = UID;
$where['fid'] = 0;
$where['status'] = 1;
$where['ORDER'] = ['weight'=>'ASC'];
if(!is_login()){
$where['property'] = 0;
}
//查找一级分类
$content = ['cid(id)','name','property','font_icon','icon','description'];
$category_parent = select_db('user_categorys',$content,$where);
//查找二级分类
$categorys = [];
if($layer === true){
foreach ($category_parent as $key => $category) {
$where['fid'] = $category['id'];
$category_subitem = select_db('user_categorys',$content,$where);
$category['subitem_count'] = count($category_subitem);
$category['subitem'] = $category_subitem;
array_push($categorys,$category);
}
}else{
foreach ($category_parent as $key => $category) {
$where['fid'] = $category['id'];
$category_subitem = select_db('user_categorys',$content,$where);
$category['subitem_count'] = count($category_subitem);
array_push($categorys,$category);
$categorys = array_merge ($categorys,$category_subitem);
}
}
return $categorys;
}

View File

@@ -27,7 +27,7 @@ switch ($type) {
setcookie($USER_DB['User'].'_Password2', md5($USER_DB['Password'].$_COOKIE[U.'_key'].$_POST['Password2']), 0,'','',false,true);
msg(1,'二级密码正确!');
}else{
msg(-1,'二级密码错误!'.$LoginConfig['Password2']);
msg(-1,'二级密码错误!');
}
break;
case "link_pwd":

View File

@@ -1 +1 @@
v2.0.23-20230527
v2.0.34-20230809

View File

@@ -649,7 +649,17 @@
padding: 0px;
overflow: hidden;
}
/**菜单缩放*/
.popup-tips .layui-layer-TipsG{
display: none;
}
.popup-tips.layui-layer-tips .layui-layer-content{
padding: 0;
}
.popup-tips .layui-nav-tree{
width: 150px;
border-radius: 10px;
}
/**左侧菜单字体间距*/
.layuimini-menu-left .layui-nav-item a span {
letter-spacing: 1px;
@@ -879,4 +889,4 @@
.layui-layout-admin .layui-logo {width: 155px;}
.layui-body {left: 155px;}
.layuimini-tool {left: 190px;}
.layui-nav-tree {width: 155px!important;}
.layui-nav-tree {width: 155px!important;}

View File

@@ -23,8 +23,9 @@
<div class="layui-header header">
<div class="layui-logo layuimini-logo"></div>
<div class="layuimini-header-content">
<a><div class="layuimini-tool"><i title="左侧栏展开/收缩" class="fa fa-outdent" data-side-fold="1"></i></div></a>
<a><div class="layuimini-tool"><i title="左侧栏展开/收缩" class="fa fa-outdent" style="font-size: 1.1rem;" data-side-fold="1"></i></div></a>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item" lay-unselect><a href="javascript:;" data-home="主页" title="主页"><i class="fa fa-home" style="font-size: 1.08rem;"></i></a></li>
<li class="layui-nav-item" lay-unselect><a href="javascript:;" data-refresh="刷新" title="刷新"><i class="fa fa-refresh"></i></a></li>
<li class="layui-nav-item mobile layui-hide-xs" lay-unselect><a href="javascript:;" title="全屏" data-check-screen="full"><i class="fa fa-arrows-alt"></i></a></li>
<li class="layui-nav-item layuimini-setting">

View File

@@ -82,6 +82,15 @@
}
limit = false; //取消修改限制
layer.closeAll('loading'); //关闭加载层
//加载加密分组数据
$.post(get_api('read_pwd_group_list'),{'page':'1','limit':'9999'},function(data,status){
if(data.code == 1){
pwds = [];
for(var i =0;i<data.count;i++){
pwds['pid_'+data.data[i].pid] = {'pwd':data.data[i].password,'name':data.data[i].name};
}
}
});
}
});
};

View File

@@ -23,66 +23,72 @@ layui.use(['layer','miniTab'], function(){
layer.tips("点击此处更新到最新版","#sysup",{tips: [3, "#ff5722"],time: 60*1000,anim: 6});
layer.msg(' 检测到新版本,请尽快更新 ', {offset: 'b',anim: 6,time: 60*1000});
}
}
//点击更新事件
$('#sysup').on('click', function(){
let tip = layer.open({
title:"系统更新"
,content: "1.更新有风险请备份后再更新<br />2.更新后检查主题是否可更新<br />3.更新时请勿有其他操作<br />4.更新时请勿刷新或关闭页面<br />5.确保所有文件(夹)是可写权限"
,btn: ['确定更新', '更新内容', '取消']
,yes: function(index, layero){
let fail = false;
let up_info = {'code':0};
let i=0;
layer.close(tip);
layer.load(1, {shade:[0.3,'#fff']});//加载层
let msg_id = layer.msg('正在准备更新,请勿操作.', {icon: 16,time: 1000*300});
//设置同步模式
$.ajaxSetup({ async : false });
//获取更新信息
$.post(get_api("other_upsys"),{"i":0}, function(data, status) {
up_info = data;
});
//如果失败
if(up_info.code != 1){
layer.closeAll();
layer.alert(up_info.msg || "错误代码404",{icon:2,title:'更新失败',anim: 2,shadeClose: false,closeBtn: 0,btn: ['知道了']});
return;
}
//设为异步模式
$.ajaxSetup({ async : true });
//开始请求更新
request_update(); let msg = '';
function request_update(){
if( i >= up_info.info.length){
layer.closeAll();
layer.alert('更新完毕,请刷新页面!',{icon:1,title:'更新成功',anim: 2,shadeClose: false,closeBtn: 0,btn: ['刷新页面']},function () {parent.location.reload();});
return;
}else{
i++;
}
$("#layui-layer"+ msg_id+" .layui-layer-padding").html('<i class="layui-layer-face layui-icon layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i>[ ' + i + ' / ' + up_info.info.length + ' ] ' + up_info.info[i-1]);
//点击更新事件
$('#sysup').on('click', function(){
let tip = layer.open({
title:"系统更新"
,content: "1.更新有风险请备份后再更新<br />2.更新后检查主题是否可更新<br />3.更新时请勿有其他操作<br />4.更新时请勿刷新或关闭页面<br />5.确保所有文件(夹)是可写权限"
,btn: ['确定更新', '更新内容', '取消']
,yes: function(index, layero){
let fail = false;
let up_info = {'code':0};
let i=0;
layer.close(tip);
layer.load(1, {shade:[0.3,'#fff']});//加载层
let msg_id = layer.msg('正在准备更新,请勿操作.', {icon: 16,time: 1000*300});
//设置同步模式
$.ajaxSetup({ async : false });
$.post(get_api("other_upsys"),{"i":i}, function(data, status) {
if (data.code == 1) {
request_update();
}else{
layer.closeAll();
layer.alert(data.msg || "未知错误,请联系开发者!",{icon:5,title:up_info.info[i-1],anim: 2,shadeClose: false,closeBtn: 0,btn: ['知道了']});
}
//获取更新信息
$.post(get_api("other_upsys"),{"i":0}, function(data, status) {
up_info = data;
});
//如果失败
if(up_info.code != 1){
layer.closeAll();
layer.alert(up_info.msg || "错误代码404",{icon:2,title:'更新失败',anim: 2,shadeClose: false,closeBtn: 0,btn: ['知道了']});
return;
}
//设为异步模式
$.ajaxSetup({ async : true });
//开始请求更新
request_update(); let msg = '';
function request_update(){
if( i >= up_info.info.length){
layer.closeAll();
layer.alert('更新完毕,请刷新页面!',{icon:1,title:'更新成功',anim: 2,shadeClose: false,closeBtn: 0,btn: ['刷新页面']},function () {parent.location.reload();});
return;
}else{
i++;
}
$("#layui-layer"+ msg_id+" .layui-layer-padding").html('<i class="layui-layer-face layui-icon layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i>[ ' + i + ' / ' + up_info.info.length + ' ] ' + up_info.info[i-1]);
$.post(get_api("other_upsys"),{"i":i}, function(data, status) {
if (data.code == 1) {
request_update();
}else{
layer.closeAll();
layer.alert(data.msg || "未知错误,请联系开发者!",{icon:5,title:up_info.info[i-1],anim: 2,shadeClose: false,closeBtn: 0,btn: ['知道了']});
}
});
}
},btn2: function(index, layero){
window.open("https://gitee.com/tznb/TwoNav/releases");
},btn3: function(index, layero){
return true;
},cancel: function(){
return true;
}
},btn2: function(index, layero){
window.open("https://gitee.com/tznb/TwoNav/releases");
},btn3: function(index, layero){
return true;
},cancel: function(){
return true;
}
});
});
});
}else{
$("#new_ver").append(' <span id="sysup" style="cursor:pointer;color: rgb(1, 170, 237);">&nbsp;更新系统</span>');
$('#sysup').on('click', function(){
layer.alert("暂无可用更新,当前为最新版本",{icon:1,title:"更新系统",anim: "slideDown",shadeClose: true,closeBtn: 0,btn: ['知道了']});
});
}
//查看更新日志
$('#ver').css({"cursor":"pointer","color":"#01AAED"}); //设置鼠标形状和字体颜色

View File

@@ -160,11 +160,51 @@ layui.define(["jquery", "miniMenu", "element","miniTab", "miniTheme"], function
},
// 监听
listen: function () {
//主页
$('body').on('click','[data-home]', function () {
top.location.href='./index.php?u='+u;
});
//刷新
$('body').on('click','[data-refresh]', function () {
$(".layui-tab-item.layui-show").find("iframe")[0].contentWindow.location.reload();
miniAdmin.success('刷新成功');
});
// 悬停菜单>移入
$("body").on("mouseenter", ".layui-nav-tree .menu-li", function () {
if (miniAdmin.checkMobile()) {
return false;
}
var classInfo = $(this).attr('class'),
tips = $(this).prop("innerHTML"),
isShow = $('.layuimini-tool i').attr('data-side-fold');
if (isShow == 0 && tips) {
tips = "<ul class='layuimini-menu-left-zoom layui-nav layui-nav-tree layui-this'><li class='layui-nav-item layui-nav-itemed'>"+tips+"</li></ul>" ;
window.openTips = layer.tips(tips, $(this), {
tips: [2, '#2f4056'],
time: 300000,
skin:"popup-tips",
success:function (el) {
var left = $(el).position().left - 10 ;
$(el).css({ left:left });
element.render();
}
});
}
});
// 悬停菜单>移出
$("body").on("mouseleave", ".popup-tips", function () {
if (miniAdmin.checkMobile()) {
return false;
}
var isShow = $('.layuimini-tool i').attr('data-side-fold');
if (isShow == 0) {
try {
layer.close(window.openTips);
} catch (e) {
console.log(e.message);
}
}
});
//全屏
$('body').on('click','[data-check-screen]', function () {
var check = $(this).attr('data-check-screen');

View File

@@ -3,12 +3,11 @@ var bak_link_id = 0;
var page_sid = '';
var link_id = '';
var load_index;
layui.use(['form','upload','miniTab'], function () {
var module = _GET('source') === 'tpl' ? ['form', 'upload'] : ['form', 'upload', 'miniTab'];
layui.use(module, function () {
var $ = layui.jquery;
var form = layui.form;
var upload = layui.upload;
var miniTab = layui.miniTab;
var edit_mode = _GET('page') == 'link_edit'; //是否编辑模式
//独立页面
if(top.location == self.location){
@@ -30,8 +29,8 @@ layui.use(['form','upload','miniTab'], function () {
if(data.path != '' ){$("#iconurl").val(data.path);}
//如果勾选连续添加
if($("#continuity").is(":checked")){
//$('#reset').click();//完全清空
form.val('form',{'url':'','title':'','description':'','icon':''});
form.val('form',{'url':'','title':'','description':'','icon':'','keywords':''});
$('form input[name^="_"]').val(''); //扩展字段清空
$('#icon_img').attr('src', blank_img);//清除缩略图
layer.msg('添加成功', {icon: 1});
$("#url").focus();//URL获取输入焦点
@@ -108,9 +107,18 @@ layui.use(['form','upload','miniTab'], function () {
if(top.location == self.location){
layer.msg('已更新!', {icon: 1});
}else{
parent.layui.table.reload('table');//刷新父页面的表格
parent.layui.layer.msg('已更新', {icon: 1});
$('#close').click();//关闭子页面
if(_GET('source') == 'tpl'){ //第三方调用时刷新父页面
layer.msg('添加成功', {icon: 1,time: 700,
end: function() {
parent.location.reload();
$('#close').click();//关闭子页面
}
});
}else{
parent.layui.table.reload('table');//刷新父页面的表格
parent.layui.layer.msg('已更新!', {icon: 1});
$('#close').click();//关闭子页面
}
}
}else{
layer.msg(data.msg, {icon: 5});
@@ -132,7 +140,9 @@ layui.use(['form','upload','miniTab'], function () {
window.close(); //关闭当前页面
}else{
parent.layer.close(parent.layer.getFrameIndex(window.name));//关闭当前页(内嵌窗口)
miniTab.deleteCurrentByIframe(); //关闭当前标签(标签窗口)
if(_GET('source') != 'tpl'){
layui.miniTab.deleteCurrentByIframe(); //关闭当前标签(标签窗口)
}
}
});
@@ -194,6 +204,28 @@ layui.use(['form','upload','miniTab'], function () {
}
}
//链接扩展上传图片
upload.render({
elem: '.extend_up_img'
,url: get_api('write_link','extend_up_img')
,exts: 'jpg|jpeg|png|ico|bmp|svg'
,acceptMime: 'image/*'
,accept: 'file'
,size: 1024
,done: function(res){
if(res.code == 1){
let inpu = this.item.closest('.layui-form-item').find('input[name^="_"]');
inpu.val(res.url);
}else{
layer.msg(res.msg || '上传失败', {icon: 5});
}
},error: function(){
layer.msg("上传异常,请刷新重试", {icon: 5});
}
});
//layui>end
});
@@ -206,7 +238,7 @@ function preview_icon(icon =''){
}else if(icon.substr(0,4) == '<svg'){
$('#icon_img').attr('src','data:image/svg+xml;base64,'+ btoa(icon.replace(/[\u00A0-\u2666]/g, function(c) {return '&#' + c.charCodeAt(0) + ';';})) );
}else{
$('#icon_img').attr('src',icon + '?t=' + Math.random() ) ;
$('#icon_img').attr('src',icon + (icon.indexOf('?') !== -1 ? '&_t=' : '?_t=') + Math.random() ) ;
}
}
}
@@ -223,9 +255,12 @@ function get_link_info() {
if(data.data.description != null) {
$("#description").val(data.data.description);
}
if(data.data.keywords != null) {
$("#keywords").val(data.data.keywords);
}
}else{
layer.msg(data.msg, {icon: 5});
}
layer.close(load_index);
});
}
}

View File

@@ -8,6 +8,7 @@ layui.use(['form','table','dropdown','miniTab'], function () {
var IDs = [];
var api = get_api('read_link_list'); //列表接口
var limit = localStorage.getItem(u + "_limit") || 50; //尝试读取本地记忆数据,没有就默认50
var link_sort = JSON.parse(localStorage.getItem(u + "_link_sort")) || {field: 'lid', type: null };
var pwds = [];
miniTab.listen();
//渲染表格
@@ -24,11 +25,11 @@ layui.use(['form','table','dropdown','miniTab'], function () {
}
});
}
var img_src;
var cols=[ //表头
{type:'checkbox'} //开启复选框
,{field: 'lid', title: 'ID', width:80, sort: true,hide:true}
,{field: 'category_name', title: '所属分类',sort:true,width:140,event: 'edit_category',templet:function(d){
,{field: 'fid', title: '所属分类',sort:true,width:140,event: 'edit_category',templet:function(d){
//检查是否存在,避免特殊情况报错
if (categorys && categorys[d.fid] && categorys[d.fid].font_icon && categorys[d.fid].name) {
return '<i class="' + categorys[d.fid].font_icon + '"></i> ' + categorys[d.fid].name;
@@ -36,24 +37,38 @@ layui.use(['form','table','dropdown','miniTab'], function () {
return 'Null';
}
}}
,{field: 'title', title: '链接标题', width:200, edit: 'text'}
,{field: 'icon', title: '图标', width:60, templet:function(d){
if(d.icon == null || d.icon == ""){
return '<img src="./templates/admin/img/ie.svg" width="28" height="28">';
}else{
if(d.icon.substr(0,5) =='data:') {
img_src = d.icon;
}else if(d.icon.substr(0,4) == '<svg'){
img_src = 'data:image/svg+xml;base64,'+ btoa(d.icon.replace(/[\u00A0-\u2666]/g, function(c) {return '&#' + c.charCodeAt(0) + ';';}));
}else{
img_src = d.icon + (d.icon.indexOf('?') !== -1 ? '&_t=' : '?_t=') + Date.now();
}
return '<img src="' + img_src + '" width="28" height="28">';
}
}}
,{field: 'title', title: '链接标题',sort:true, width:200, edit: 'text'}
,{ title:'操作', toolbar: '#tablebar',width:110}
,{field:'pwd_id',title:'密码',width:70,templet: function(d){
return d.pwd_id>0?'<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="pwd">查看</a>':'';
}}
return d.pwd_id>0?'<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="pwd">查看</a>':'';
}}
,{field: 'property', title: '私有', width: 100, sort: true,templet: function(d){
return "<input type='checkbox' value='" + d.lid + "' lay-filter='property' lay-skin='switch' lay-text='私有|公开' " + (d.property == 1?"checked":"" )+ ">";
}}
,{field: 'status', title: '状态', width: 100, sort: true,templet: function(d){
return "<input type='checkbox' value='" + d.lid + "' lay-filter='status' lay-skin='switch' lay-text='启用|禁用' " + (d.status == 1?"checked":"" )+ ">";
}}
,{field: 'url', title: 'URL',templet:function(d){
return '<a color="" target = "_blank" href = "' + d.url + '" title = "' + d.url + '" referrerpolicy="same-origin" >' + d.url + '</a>';
,{field: 'url',sort:true, title: 'URL',templet:function(d){
return '<a color="" target = "_blank" href = "' + d.url + '" title = "' + d.url + '" referrerpolicy="same-origin" >' + d.url + '</a>';
}}
,{field: 'click', title: '点击数',width:90,sort:true}
,{field: 'add_time', title: '添加时间', width:160, sort: true,templet:function(d){
var add_time = timestampToTime(d.add_time);
return add_time;
var add_time = timestampToTime(d.add_time);
return add_time;
}}
,{field: 'up_time', title: '修改时间', width:160,sort:true,templet:function(d){
return d.up_time == null ?'':timestampToTime(d.up_time);
@@ -66,21 +81,23 @@ layui.use(['form','table','dropdown','miniTab'], function () {
item.hide = local[item.field];
}
});
//渲染表格函数
var renderTable2 = function () {
where = link_sort.type === null ? {} : {field:link_sort.field,order:link_sort.type.toUpperCase()};
table.render({
elem: '#table'
,height: 'full-110' //自适应高度
,url: api //数据接口
,where: where
,page: true //开启分页
,limit:limit //默认每页显示行数
,limits: [20,50,100,300,500]
,even:true //隔行背景色
,loading:true //加载条
//,defaultToolbar:false
,toolbar: '#toolbar'
,id:'table'
,initSort: link_sort
,cols: [cols]
,method: 'post'
,response: {statusCode: 1 }
@@ -97,10 +114,9 @@ layui.use(['form','table','dropdown','miniTab'], function () {
$("[data-field='add_time']").addClass('layui-hide-xs');
$("[data-field='up_time']").addClass('layui-hide-xs');
$("[data-field='click']").addClass('layui-hide-xs');
//$(".layui-laypage .layui-laypage-count").css("padding-left","35px");
$(".layui-laypage .layui-laypage-prev").addClass('layui-hide-xs');
$(".layui-laypage .layui-laypage-curr").addClass('layui-hide-xs');
$(".layui-laypage .layui-laypage-next").addClass('layui-hide-xs');
// $(".layui-laypage .layui-laypage-prev").addClass('layui-hide-xs');
// $(".layui-laypage .layui-laypage-curr").addClass('layui-hide-xs');
// $(".layui-laypage .layui-laypage-next").addClass('layui-hide-xs');
$(".layui-laypage .layui-laypage-skip").addClass('layui-hide-xs');
$(".layui-table-tool-self").addClass('layui-hide-xs');
//加载加密分组数据
@@ -120,23 +136,28 @@ layui.use(['form','table','dropdown','miniTab'], function () {
key: input.name,value: input.checked
});
});
$('th[data-field="icon"]').attr('title', '仅显示已上传的图标');
}
});
// 监听表格排序事件
table.on('sort(table)', function(obj) {
link_sort = {field:obj.field,type:obj.type};
localStorage.setItem(u + "_link_sort",JSON.stringify(link_sort));
link_search();
});
};
function link_search(){
var fid = document.getElementById("fid").value;
var keyword = document.getElementById("link_keyword").value;//获取输入内容
var property = document.getElementById("property").value;
var status = document.getElementById("status").value;
let data = form.val('form');
if(link_sort.type != null){
data.field = link_sort.field;
data.order = link_sort.type.toUpperCase();
}
table.reload('table', {
url: api
,method: 'post'
,request: {
pageName: 'page' //页码的参数名称
,limitName: 'limit' //每页数据量的参数名
}
,where: {query:keyword,fid:fid,property:property,status:status}
,request: {pageName: 'page',limitName: 'limit'}
,where: data
,page: {curr: 1}
});
}
@@ -299,8 +320,44 @@ layui.use(['form','table','dropdown','miniTab'], function () {
layer.msg(data.msg);
}
});
}else if(event === 'msg_pull'){
index = layer.open({type: 1,scrollbar: false,shadeClose: true,title: '批量识别链接信息',area : ['100%', '100%'],content: $('.msg_pull')});
}
});
$('#start_pull').click(function () {
let lits = table.checkStatus('table').data; console.log( lits );
let config = form.val('msg_pull');
let number = 0;
let total = lits.length;
let load_id = layer.load(1, {shade:[0.5,'#fff']});//加载层
let msg_id = layer.msg('正在识别中', {icon: 16,time: 1000*300}); //进度提示
//检查是否满足条件
$.post(get_api('write_link','msg_pull_check'),config,function(data,status){
if(data.code == 1){
config.key = data.key;
start_pull(number); //开始拉取
}else{
layer.alert(data.msg || '未知错误',{icon:5,title:'错误',anim: 2,closeBtn: 0,btn: ['刷新页面']},function () {location.reload();});
}
});
function start_pull(number){
if(number >= total){
layer.closeAll();
layer.alert('处理完毕,请刷新页面!' ,{icon:1,title:'提示',anim: 2,shadeClose: false,closeBtn: 0});
return true;
}
$("#layui-layer"+ msg_id+" .layui-layer-padding").html('<i class="layui-layer-face layui-icon layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i>[ ' + number + ' / ' + total + ' ] 正在处理中..');
config.link_id = lits[number].lid;
$.post(get_api('write_link','msg_pull'),config,function(data,status){
number ++;
start_pull(number);
});
}
return false;
});
table.render({
elem: '#link_extend_list'
@@ -319,6 +376,7 @@ layui.use(['form','table','dropdown','miniTab'], function () {
,{field:'name',title:'字段名',edit:'text',width:256}
,{field:'type',title:'类型',edit:'text',width:256}
,{field:'default',title:'默认值',edit:'text',width:256}
,{field:'tip',title:'提示内容',edit:'text',width:256}
,{ title:'操作',toolbar:'#link_extend_toolbar',align:'center',width:118}
]]
});
@@ -473,7 +531,7 @@ layui.use(['form','table','dropdown','miniTab'], function () {
"title": "请输入标题",
"name":"请输入字段名(大小写字母或数字)",
"weight":(max_weight + 1),
"type":"请输入 text 或 textarea",
"type":"请输入 text 或 textarea 或 up_img",
"default":""
});
table.reload('link_extend_list', {data: data});

View File

@@ -72,7 +72,8 @@ function theme_download(dir,name,desc,fn){
layer.msg(data.msg, {icon: 1});
setTimeout(() => {location.reload();}, 500);//延迟刷新
}else{
layer.msg(data.msg, {icon: 5});
//layer.msg(data.msg, {icon: 5});
layer.alert(data.msg,{icon:5,title:"错误",anim: "slideDown",shadeClose: true,closeBtn: 0,btn: ['知道了']});
}
});
}

View File

@@ -2,7 +2,7 @@
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<!-- 操作列 -->
@@ -19,7 +19,7 @@ layui.use(['form','table'], function () {
var form = layui.form;
var api = get_api('read_login_info'); //列表接口
var limit = localStorage.getItem(u + "_limit") || 50; //尝试读取本地记忆数据,没有就默认50
var current_id = 0;
var cols=[[ //表头
{field: 'id', title: 'ID', width:60, sort: true,hide:true}
,{ title: '操作',toolbar: '#tablebar',width:70}
@@ -31,7 +31,7 @@ layui.use(['form','table'], function () {
return timestampToTime(d.last_time);;
}}
,{field: 'expire_time', title: '到期时间', width:160, sort: true,templet:function(d){
return timestampToTime(d.expire_time);;
return d.expire_time <= 0 ? '':timestampToTime(d.expire_time);
}}
,{field: 'ua', title: '浏览器UA'}
]]
@@ -50,24 +50,47 @@ layui.use(['form','table'], function () {
,method: 'post'
,response: {statusCode: 1 }
,done: function (res, curr, count) {
current_id = res.current_id;
var temp_limit = $(".layui-laypage-limits option:selected").val();
if(temp_limit > 0 && localStorage.getItem(u + "_limit") != temp_limit){
localStorage.setItem(u + "_limit",temp_limit);
}
//遍历表格数据,标记当前设备
layui.each(table.cache.table, function(index, item){
if(item.id == res.current_id){
let tr = $('.layui-table-body.layui-table-main tr[data-index="' + index + '"]');
tr.css('color', 'red');
tr.attr('title','当前设备');
return false;
}
});
}
});
table.on('tool(table)', function (obj) {
var data = obj.data;
if (obj.event === 'out') {
$.post(get_api('write_login_info','out'),{id:data.id},function(data,status){
if(data.code == 1) {
obj.del();
layer.msg(data.msg, {icon: 1});
}else{
layer.msg(data.msg, {icon: 5});
}
});
if(data.id == current_id ){
$.post('./index.php?c=admin&page=logout&u='+u,function(res,status){
if(res.code == 1) {
layer.alert("您已安全的退出登录!", function () {
top.location.href='./index.php?u='+u;
});
}else{
layer.msg(res.msg,{icon: 5});
}
});
}else{
$.post(get_api('write_login_info','out'),{id:data.id},function(res,status){
if(res.code == 1) {
obj.del();
layer.msg(res.msg, {icon: 1});
}else{
layer.msg(res.msg, {icon: 5});
}
});
}
}
});

View File

@@ -1,4 +1,6 @@
<?php $title='安全设置'; require 'header.php'; ?>
<?php $title='安全设置'; require 'header.php';
$LoginConfig = unserialize($USER_DB['LoginConfig']);
$LoginConfig['totp_key'] = empty($LoginConfig['totp_key']) ? '0':'1';?>
<body>
<div class="layuimini-container">
<div class="layuimini-main">
@@ -83,10 +85,9 @@
<select name="api_model">
<option value="security" selected>安全模式</option>
<option value="compatible">兼容模式</option>
<option value="compatible+open">兼容模式+开放</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">部分主题和插件需设为兼容+开放模式 <a href="javascript:;" layuimini-content-href="Token" data-title="Token"><font color="red"> 获取API ( Token )</font></a></div>
<div class="layui-form-mid layui-word-aux">部分主题和插件需设为兼容 <a href="javascript:;" layuimini-content-href="Token" data-title="Token"><font color="red"> 获取API ( Token )</font></a></div>
</div>
<div class="layui-form-item">
@@ -110,14 +111,58 @@
</div>
<div class="layui-form-item">
<div class="layui-input-block"><button class="layui-btn layui-btn-normal" lay-submit lay-filter="save">确认保存</button></div>
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="save">确认保存</button>
<button class="layui-btn layui-bg-purple" lay-submit lay-filter="open_totp">OTP 双重验证</button>
</div>
</div>
</div>
</form>
</div>
</div>
<ul class="ul_totp" style="margin-top:18px;display:none;padding-right: 10px;">
<form class="layui-form" lay-filter="ul_totp">
<div class="layui-form-item">
<label class="layui-form-label">二维码</label>
<div id="qr"></div><div id="qrcode"></div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">秘钥</label>
<div class="layui-input-inline">
<input type="text" name="key" id="key" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">为了您的账户安全,成功保存后无法再查看秘钥,请勿泄漏秘钥</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">验证码</label>
<div class="layui-input-inline">
<input type="text" name="code" id="code" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">请输入生成的验证码</div>
</div>
<pre class="layui-code" >
这东西叫法太多了,比如双重验证/动态密码/动态口令/动态令牌/身份验证器/双因子认证/2FA/TOTP验证码等等
原理是基于时间的动态验证码,网上客户端也大把,喜欢那个安装那个
开启后登录时需输入OTP验证码,作用是提高账号安全性
</pre>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-warm" type="button" id="close" >关闭</button>
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="save_totp" id="save_totp">保存</button>
</div>
</div>
</form>
</ul>
<script src = "<?php echo $libs;?>/jquery/jquery-3.6.0.min.js"></script>
<script src = "<?php echo $libs;?>/jquery/jquery.md5.js"></script>
<script src = "<?php echo $libs; ?>/jquery/jquery.qrcode.min.js"></script>
<script src = "./templates/admin/js/public.js?v=<?php echo $Ver;?>"></script>
<?php load_static('js');?>
<script>
@@ -127,9 +172,9 @@ layui.use(['jquery','form','miniTab'], function () {
miniTab = layui.miniTab;
miniTab.listen();
//表单赋值
form.val('form', <?php echo json_encode(unserialize( $USER_DB['LoginConfig'] ));?>);
form.val('form', <?php echo json_encode($LoginConfig);?>);
//监听提交
//保存
form.on('submit(save)', function (data) {
$("*").blur(); //失去焦点,解决按回车无限提交
data.field.Password=$.md5(data.field.Password);
@@ -137,7 +182,7 @@ layui.use(['jquery','form','miniTab'], function () {
if(data.code == 1) {
var index = layer.alert("保存成功!", function () {
layer.close(index);
//miniTab.deleteCurrentByIframe();
//miniTab.deleteCurrentByIframe(); //关闭页面
});
}else{
layer.msg(data.msg, {icon: 5});
@@ -145,6 +190,69 @@ layui.use(['jquery','form','miniTab'], function () {
});
return false;
});
//双重验证
form.on('submit(open_totp)', function (data) {
$("*").blur(); //失去焦点,解决按回车无限提交
data.field.Password=$.md5(data.field.Password);
pwd_md5 = data.field.Password;
$.post(get_api('read_totp'),data.field,function(data,status){
if(data.code == 1){
layer.confirm('已开启双重验证,是否要关闭?',{icon: 3, title:'温馨提示'}, function(index){
layer.closeAll();
$.post(get_api('write_totp','delete'),{'Password':pwd_md5},function(data,status){
if(data.code == 1) {
layer.msg(data.msg, {icon: 1});
}else{
layer.msg(data.msg, {icon: 5});
}
});
});
}else if(data.code == 2) {
layer.confirm('未开启双重验证,是否要开启?',{icon: 3, title:'温馨提示'}, function(index){
layer.closeAll();
$('#key').val(data.key);
$('#code').val('');
$("#qr").html('');//防止多次操作出现多个二维码
let content = `otpauth://totp/${u}?secret=${data.key}&issuer=TwoNav`;
$('#qr').qrcode({render: "canvas",width: 200,height: 200,text: content});
var index = layer.open({type: 1,scrollbar: false,shadeClose: true,title: '双重验证',area : ['100%', '100%'],content: $('.ul_totp')});
});
return false;
}else{
layer.msg(data.msg, {icon: 5});
}
});
return false;
});
$('#key').on('input', function() {
$("#key").html('');
let key = $('#key').val();
let content = `otpauth://totp/${u}?secret=${key}&issuer=TwoNav`;
$("#qr").html('');
$('#qr').qrcode({render: "canvas",width: 200,height: 200,text: content});
});
//保存双重验证
form.on('submit(save_totp)', function (data) {
$("*").blur(); //失去焦点,解决按回车无限提交
data.field.Password = pwd_md5;
$.post(get_api('write_totp','set'),data.field,function(data,status){
if(data.code == 1) {
layer.closeAll();
layer.msg(data.msg, {icon: 1});
}else{
layer.msg(data.msg, {icon: 5});
}
});
return false;
});
//关闭页面
$(document).on('click', '#close', function() {
layer.closeAll();
});
});
</script>
</body>

View File

@@ -84,31 +84,19 @@
</div>
<div class="layui-form-item">
<label class="layui-form-label">热门网址</label>
<label class="layui-form-label">最新网址</label>
<div class="layui-input-inline" >
<select name="top_link">
<option value="0" selected>不显示</option>
<option value="5" >显示5条</option>
<option value="10" >显示10条</option>
<option value="15" >显示15条</option>
<option value="20" >显示20条</option>
</select>
<input type="number" name="new_link" class="layui-input" value="0" placeholder="输入范围: 0-100" lay-verify="required">
</div>
<div class="layui-form-mid layui-word-aux">在主页显示热门网址(点击排行)</div>
<div class="layui-form-mid layui-word-aux">在主页显示最新的网址(创建时间)</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">最新网址</label>
<label class="layui-form-label">热门网址</label>
<div class="layui-input-inline" >
<select name="new_link">
<option value="0" selected>不显示</option>
<option value="5" >显示5条</option>
<option value="10" >显示10条</option>
<option value="15" >显示15条</option>
<option value="20" >显示20条</option>
</select>
<input type="number" name="top_link" class="layui-input" value="0" placeholder="输入范围: 0-100" lay-verify="required">
</div>
<div class="layui-form-mid layui-word-aux">在主页显示最新的网址(创建时间)</div>
<div class="layui-form-mid layui-word-aux">在主页显示热门网址(点击排行)</div>
</div>
<div class="layui-form-item">

View File

@@ -3,8 +3,46 @@
<div class="layuimini-container">
<div class="layuimini-main">
<div class="layui-form layuimini-form layui-form-pane">
<blockquote class="layui-elem-quote layui-text" style="">注意: Token(令牌),是您访问API的凭证 (不了解&不需要请勿设置) ,等同于您的账号密码,请妥善保管</blockquote>
<div class="layui-form-item">
<div class="layui-collapse" lay-accordion>
<div class="layui-colla-item" >
<div class="layui-colla-title">注意事项</div>
<div class="layui-colla-content layui-show">
<blockquote class="layui-elem-quote layui-text" style="">Token(令牌),是您访问API的凭证 (不了解&不需要请勿设置) ,等同于您的账号密码,请妥善保管</blockquote>
</div>
</div>
<div class="layui-colla-item">
<div class="layui-colla-title">API模式的差别</div>
<div class="layui-colla-content">
<p>安全模式: 仅提供TwoNav自身的API接口,不兼容Onenav的API接口!</p>
<p>兼容模式: 兼容部分OneNav的API接口,以便于其他插件调用!不支持访客调用!</p>
<p>如果你未使用相关扩展插件,则无需修改模式并将Token删除,以提高账号的安全性!</p>
</div>
</div>
<div class="layui-colla-item">
<div class="layui-colla-title">如何使用Chrome浏览器扩展 [非官方]</div>
<div class="layui-colla-content">
前言: 由于浏览器扩展插件非TwoNav所开发适配,如存在Bug或无法使用属正常现象!<br />
安装: 谷歌应用商店下载<a href="https://chrome.google.com/webstore/detail/onenav/omlkjgkogkfpjbdigianpdbjncdchdco?hl=zh-CN&authuser=0" >OneNav</a>并安装 ( 已知0.9.24 - 1.0.1可用,其他版本未知 )<br />
设置S: 1.TwoNav后台>右上角账号>安全设置>API模式>设为<兼容模式> 2.在本页面获取Token<br />
设置C: 插件API设置>填入域名和Token并保存>完成<br />
问题1: 对于单用户使用,确保系统设置中默认用户是当前用户即可!多用户使用时需开启二级域名功能并将域名替换成用户的二级域名,注意结尾不需要带/
问题2: 因为插件非官方开发维护,能用就尽量不要更新,如果插件更新可能会导致无法正常使用,需这个更新获得兼容性!
问题3: 因为国内环境限制,你可能无法访问谷歌,这种情况你可以在交流群获取插件(安装方法自行百度,部分浏览器可能需要开发者模式加载)
</div>
</div>
<div class="layui-colla-item">
<div class="layui-colla-title">如何使用uTools扩展插件 [非官方]</div>
<div class="layui-colla-content">
<p>前言: 由于uTools扩展插件非TwoNav所开发适配,如存在Bug或无法使用属正常现象!</p>
<p>安装: 在uTools插件应用市场>搜索OneNav>点击获取 </p>
<p>设置S: 1.TwoNav后台>右上角账号>安全设置>API模式>设为<兼容模式> 2.在本页面获取SecretKey ( 即插件设置中的API KEY )</p>
<p>设置C: 打开uTools中的OneNav,点击右下角小齿轮>输入网站地址/用户名/API KEY</p>
</div>
</div>
</div>
<div class="layui-form-item" style="margin-top: 15px;">
<label class="layui-form-label required">登录密码</label>
<div class="layui-input-block">
<input type="password" name="Password" lay-verify="required" lay-reqtext="请输入登录密码" placeholder="请输入登录密码" class="layui-input">
@@ -29,6 +67,7 @@
<button class="layui-btn layui-btn-danger" lay-submit lay-filter="delete">删除</button>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,14 +1,15 @@
<?php $title='添加链接'; require 'header.php'; ?>
<style>
.layui-textarea {min-height: 70px;}
body {
margin: 0px 0px 0px 0px;
background: bottom;
}
margin: 0px 0px 0px 0px;
background: bottom;
}
.layui-textarea {min-height: 70px;}
.layui-form-select dl {max-height: 190px;}
</style>
<div class="layuimini-container" style="height: 420px;">
<div class="layuimini-main" style=" margin-left: 0px; ">
<form class="layui-form layuimini-form">
<div class="layuimini-container">
<div class="layuimini-main" style="margin-left: 0px;">
<form class="layui-form layuimini-form" style="padding-bottom: 20px;">
<div class="layui-form-item">
<label class="layui-form-label required" >URL</label>
@@ -40,11 +41,17 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键字</label>
<div class="layui-input-block">
<input type="text" id="keywords" name="keywords" placeholder="可留空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea name="description" id="description" placeholder="请输入内容" class="layui-textarea"></textarea>
<textarea name="description" id="description" placeholder="可留空" class="layui-textarea"></textarea>
</div>
</div>

View File

@@ -5,7 +5,7 @@
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<!-- 操作列 -->

View File

@@ -175,7 +175,7 @@
</blockquote>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"><legend>本地备份 (订阅可用)</legend></fieldset>
<blockquote class="layui-elem-quote" style="margin-top: 10px;border-left: 2px solid #FF5722; color: #FF5722;">1.备份数据库仅保存最近20份数据<br />2.该功能仅辅助备份使用无法确保100%数据安全,因此定期对整个站点打包备份仍然是必要的</blockquote>
<blockquote class="layui-elem-quote" style="margin-top: 10px;border-left: 2px solid #FF5722; color: #FF5722;">1.备份数据库仅保存最近20份数据<br />2.该功能仅辅助备份使用无法确保100%数据安全,因此定期对整个站点打包备份仍然是必要的<br />3.不支持将新版本备份回滚到旧版本中,不建议跨数据库类型回滚</blockquote>
<!-- 数据表格 -->
<table class="layui-hide" id="list" lay-filter="list"></table>
<!--本地备份备注输入-->
@@ -233,8 +233,10 @@
<input type="checkbox" name="TABLE[user_pwd_group]" title="加密" checked>
<input type="checkbox" name="TABLE[user_share]" title="分享" checked>
<input type="checkbox" name="TABLE[user_apply]" title="收录" checked>
<input type="checkbox" name="TABLE[user_article_list]" title="文章" checked>
<input type="checkbox" name="FILE[MessageBoard]" title="留言" checked>
<input type="checkbox" name="FILE[favicon]" title="图标" checked>
<input type="checkbox" name="FILE[upload]" title="上传目录(如文章图片)" checked>
</div>
</div>
<div class="layui-form-item">

View File

@@ -20,7 +20,7 @@ $title='收录管理';$awesome=true; require dirname(__DIR__).'/header.php';
<script type="text/html" id="link_operate">
<a class="layui-btn layui-btn-xs" lay-event="operation">操作 <i class="layui-icon layui-icon-down"></i></a>
</script>
<table id="apply_list" class="layui-table" lay-filter="apply_list" style="margin: -3px 0;"></table>
<table id="apply_list" class="layui-table" lay-filter="apply_list" style="margin: 1px 0;"></table>
</div>
</div>
<!--设置-->
@@ -50,7 +50,15 @@ $title='收录管理';$awesome=true; require dirname(__DIR__).'/header.php';
</div>
<div class="layui-form-mid layui-word-aux">单位:次,指最近24小时内可以提交多少次(为了防止恶意提交,删除记录可以恢复次数)</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">必填选项</label>
<div class="layui-input-block" style="margin-left: 32px;">
<input type="checkbox" name="iconurl" title="图标" >
<input type="checkbox" name="description" title="描述" >
<input type="checkbox" name="email" title="邮箱" >
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">使用说明</label>
<div class="layui-form-mid ">部分主题没有收录入口,请自行添加到链接或者底部等你认为合适的地方!前往<a style="color:#3c78d8" target="_blank" href="./index.php?c=apply&u=<?php echo $u?>" target="_blank">申请收录</a></div>
@@ -348,7 +356,7 @@ table.on('toolbar(apply_list)', function(obj){
var data = checkStatus.data;
switch(obj.event){
case 'conf':
if(document.body.clientWidth < 768){area = ['100%' , '100%'];}else{area = ['768px' , '500px'];}
if(document.body.clientWidth < 768){area = ['100%' , '100%'];}else{area = ['768px' , '520px'];}
layer.open({
type: 1,
scrollbar: false,

View File

@@ -15,6 +15,7 @@
body{background-color:rgba(0, 0, 51, 0.8);}
.title{max-width: 400px;height: auto;margin-left: auto;margin-right: auto;margin-top:5em;}
.title h1{color:#FFFFFF;text-align: center;}
.required {color: red;margin-left: 5px;float: right;}
</style>
</head>
<body>
@@ -29,32 +30,21 @@
<div class="layui-form-item" style="color: #fbfbfb;">
<?php echo $apply['Notice'];?>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站标题</label>
<div class="layui-input-block">
<input type="text" name="title" required lay-verify="required" placeholder="例如 百度一下" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站标题<span class="required">*</span></label>
<div class="layui-input-block">
<input type="text" name="title" required lay-verify="required" placeholder="例如 百度一下" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站链接</label>
<div class="layui-input-block">
<input type="url" name="url" required lay-verify="required|url" placeholder="例如 https://www.baidu.com" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站链接<span class="required">*</span></label>
<div class="layui-input-block">
<input type="url" name="url" required lay-verify="required|url" placeholder="例如 https://www.baidu.com" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站图标</label>
<div class="layui-input-block">
<input type="url" name="iconurl" required lay-verify="url" placeholder="例如 https://www.baidu.com/favicon.ico" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站描述</label>
<div class="layui-input-block">
<input type="text" name="description" placeholder="例如 搜索引擎" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站分类</label>
<label class="layui-form-label">网站分类<span class="required">*</span></label>
<div class="layui-input-block">
<select name="category_id" lay-verify="required" lay-search>
<option ></option>
@@ -62,10 +52,22 @@
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站图标</label>
<div class="layui-input-block">
<input type="url" name="iconurl" lay-verify="url" placeholder="例如 https://www.baidu.com/favicon.ico" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站描述</label>
<div class="layui-input-block">
<input type="text" name="description" placeholder="例如 搜索引擎" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">联系邮箱</label>
<div class="layui-input-block">
<input type="text" name="email" lay-verify="required" placeholder="例如 admin@qq.com" autocomplete="off" class="layui-input">
<input type="text" name="email" placeholder="例如 admin@qq.com" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">

View File

@@ -0,0 +1,311 @@
<?php
if($global_config['article'] != 1 || !check_purview('article',1)){
require(DIR.'/templates/admin/page/404.php');
exit;
}
$article_id = Get('id');
$mode = empty($article_id) ? 'add' : 'edit' ;
if($mode == 'edit'){
if(has_db('user_article_list',['uid'=>UID,'id'=>$article_id])){
$data = get_db('user_article_list','*',['uid'=>UID,'id'=>$article_id]);
//var_dump($data);
}else{
$mode = 'add';
}
}
$title = $mode == 'add' ? '添加文章' : '编辑文章';
require dirname(__DIR__).'/header.php' ?>
<link href="<?php echo $libs?>/wangEditor/wangEditor.css" rel="stylesheet">
<style type="text/css">
#editor—wrapper { border: 1px solid #cccccc88; }
#toolbar-container { border-bottom: 1px solid #ccc; }
#editor-container { height: 400px; }
.w40{width:40px;}
.layui-upload-drag .layui-icon{font-size: 40px;color: #c2c2c2;}
.layui-upload-drag{padding: 10px;}
.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:11px;font-weight:700;color:#fff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#777;border-radius:10px}
.bg-red{background-color:#e74c3c!important}
.uploads-delete-tip{position: absolute;right: 10px;font-size: 12px;}
.layui-input-block{margin-left: 70px;}
@media screen and (max-width: 768px) {
.layui-input-block {margin-left: 12px;}
.content{display: none!important;}
.layui-form-select .layui-edge {top: 75%;}
}
</style>
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<form class="layui-form" lay-filter="form">
<input class="layui-input layui-hide" name="id" autocomplete="off" value="<?php echo $data['id'];?>">
<div class="layui-form-item ">
<label class="layui-form-label w40">标题:</label>
<div class="layui-input-block">
<input class="layui-input" name="title" placeholder='请输入文章标题' autocomplete="off" value="<?php echo $data['title'];?>">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label w40">分类:</label>
<div class="layui-input-block">
<select name="category" lay-search>
<?php echo_category(true); ?>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label w40">状态:</label>
<div class="layui-input-block">
<select name="state">
<option value="1">公开</option>
<option value="2">私有</option>
<option value="3">草稿</option>
<option value="4">废弃</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label w40">摘要:</label>
<div class="layui-input-block">
<textarea name="summary" rows ="2" placeholder="文章摘要,留空时自动获取" class="layui-textarea" style="min-height: 45px;"><?php echo $data['summary'];?></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label w40 content">正文:</label>
<div class="layui-input-block" id="editor—wrapper">
<div id="toolbar-container"></div>
<div id="editor-container"></div>
<textarea name="content" id="content" class="layui-textarea layui-hide"><?php echo $data['content'] ?? '<p><br></p>';?></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label w40">封面:</label>
<div class="layui-upload-drag">
<input type="text" name="cover_url" id="cover_url" style="display: none;" value="<?php echo $data['cover'];?>">
<small class="uploads-delete-tip bg-red badge" style="cursor:pointer;display: none;" id="del_cover_view">×</small>
<i class="up_cover layui-icon layui-icon-add-1" id="up_cover" style="padding-right: 20px; padding-left: 20px;color: #e2e2e2;"></i>
<p class="up_cover">上传封面</p>
<div id="cover_view" style="display: none;"><img src="<?php echo $data['cover'];?>" style="max-width: 196px"></div>
</div>
</div>
</form>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal layui-btn-danger" id="cancel" >取消</button>
<button class="layui-btn layui-btn-normal" id="save" >保存</button>
</div>
</div>
</div>
</div>
<script src = "<?php echo $libs;?>/jquery/jquery-3.6.0.min.js"></script>
<script src = '<?php echo $libs?>/jquery/jquery.md5.js'></script>
<script src = "./templates/admin/js/public.js?v=<?php echo $Ver;?>"></script>
<?php load_static('js');?>
<script src="<?php echo $libs?>/wangEditor/wangEditor.js"></script>
<script>
const { createEditor, createToolbar } = window.wangEditor
const editorConfig = {
placeholder: '请输入文章内容...',
MENU_CONF: {
uploadImage: {}
},
onChange(editor) {
const html = editor.getHtml();
$('#content').val(html);
}
}
editorConfig.MENU_CONF['uploadImage'] = {
base64LimitSize: 128 * 1024, //小于该值就插入base64
server: get_api('write_article','uploadImage'),
fieldName: 'file',
maxFileSize: 5 * 1024 * 1024, // 单文件限制5M
maxNumberOfFiles: 10, //最多上传10个文件
// 上传之前触发
onBeforeUpload(file) {
return file
},
// 上传进度的回调函数
onProgress(progress) {
console.log('progress', progress) //进度: 0-100
},
// 单个文件上传成功之后
onSuccess(file, res) {
parent.layer.msg('上传成功', {icon: 1});
},
// 单个文件上传失败
onFailed(file, res) {
layer.alert(`${res.message}`,{icon:5,title:`上传失败: ${file.name}`,anim: 2,closeBtn: 0});
console.log(res );
},
// 上传错误,或者触发 timeout 超时
onError(file, err, res) {
layer.alert(`${err}`,{icon:5,title:`上传错误: ${file.name}`,anim: 2,closeBtn: 0});
},
}
editorConfig.MENU_CONF['uploadVideo'] = {
base64LimitSize: 128 * 1024, //小于该值就插入base64
server: get_api('write_article','uploadVideo'),
fieldName: 'file',
maxFileSize: 20 * 1024 * 1024, // 单文件限制
maxNumberOfFiles: 10, //最多上传10个文件
// 上传之前触发
onBeforeUpload(file) {
return file
},
// 上传进度的回调函数
onProgress(progress) {
console.log('progress', progress) //进度: 0-100
},
// 单个文件上传成功之后
onSuccess(file, res) {
parent.layer.msg('上传成功', {icon: 1});
},
// 单个文件上传失败
onFailed(file, res) {
layer.alert(`${res.message}`,{icon:5,title:`上传失败: ${file.name}`,anim: 2,closeBtn: 0});
console.log(res );
},
// 上传错误,或者触发 timeout 超时
onError(file, err, res) {
layer.alert(`${err}`,{icon:5,title:`上传错误: ${file.name}`,anim: 2,closeBtn: 0});
},
}
const editor = createEditor({
selector: '#editor-container',
html: $('#content').val(),
config: editorConfig,
mode: 'default'
})
const toolbarConfig = {excludeKeys: ['fullScreen','uploadVideo']}
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default'
})
layui.use(['form','upload'], function () {
var form = layui.form,
upload = layui.upload;
<?php if($mode == 'edit'){ ?>
form.val('form',{category:<?php echo $data['category'];?>,state:<?php echo $data['state'];?>});
<?php }?>
var original_md5 = $.md5(JSON.stringify(form.val('form')));
$('#cancel').click(function () {
let data = form.val('form');
if($.md5(JSON.stringify(form.val('form'))) == original_md5){
parent.layer.close(parent.layer.getFrameIndex(window.name));
return false;
}
layer.confirm('确定取消?',{icon: 3, title:'温馨提示'}, function(index){
parent.layer.close(parent.layer.getFrameIndex(window.name));
});
return false;
});
//如果存在封面则加载
if($("#cover_url").val() != ''){
layui.$('#cover_view img').attr('src', $("#cover_url").val());
layui.$('#cover_view').show();
layui.$('#del_cover_view').show();
layui.$('.up_cover').hide();
}
//上传
var uploadInst = upload.render({
elem: '.up_cover'
,url: get_api('write_article','uploadImage')
,accept: 'images'
,acceptMime: 'image/*'
,exts:'jpg|png|gif|bmp|jpeg|svg|webp'
,size: 5*1024
,done: function(res){
if(res.errno == 0){
$("#cover_url").val(res.data.url);
layui.$('#cover_view img').attr('src', res.data.url);
layui.$('#cover_view').show();
layui.$('#del_cover_view').show();
layui.$('.up_cover').hide();
layer.msg('上传成功', {icon: 1});
}else{
layer.msg(res.message || '上传失败', {icon: 5});
}
},error: function(){
layer.msg("上传异常,请刷新重试", {icon: 5});
}
});
//删除封面
$(document).on('click', '#del_cover_view', function() {
$.post(get_api('write_article','deleteImage'),{'path':$("#cover_url").val()},function(data,status){
if(data.code == 1) {
$("#cover_url").val('');
layui.$('#cover_view').hide();
layui.$('#del_cover_view').hide();
layui.$('.up_cover').show();
uploadInst.config.elem.next()[1].value = '';
layer.msg("删除成功",{icon:1});
}else{
layer.msg(data.msg || '未知错误',{icon: 5});
}
});
});
$('#save').click(function () {
let data = form.val('form');
if(data.title == ''){
layer.msg('标题不能为空,请输入标题',{icon: 5});
return false;
}
if(data.summary == ''){
data.summary = truncateString(editor.getText(),120).replace(/\n/g, ' ');
}
let loading = layer.msg('正在处理,请稍后..', {icon: 16,time: 1000*300,shadeClose: false});
$.post(get_api('write_article','save_article'),data,function(data,status){
layer.close(loading);
if(data.code == 1) {
parent.layer.close(parent.layer.getFrameIndex(window.name));
parent.layer.msg('操作成功', {icon: 1});
}else{
layer.msg(data.msg || '未知错误',{icon: 5});
}
});
return false;
});
});
function truncateString(str,n) {
var r=/[^\x00-\xff]/g;
if(str.replace(r,"mm").length<=n){return str;}
var m=Math.floor(n/2);
for(var i=m;i<str.length;i++){
if(str.substr(0,i).replace(r,"mm").length>=n){
return str.substr(0,i)+"...";
}
}
return str;
}
</script>
</body>
</html>

View File

@@ -0,0 +1,392 @@
<?php
if($global_config['article'] != 1 || !check_purview('article',1)){
require(DIR.'/templates/admin/page/404.php');
exit;
}
//读取设置
$s_site = unserialize(get_db('user_config','v',['uid'=>UID,'k'=>'s_site']));
$set['visual'] = $s_site['article_visual'] ?? '1';
$set['icon'] = $s_site['article_icon'] ?? '1';
$title='文章列表';
require dirname(__DIR__).'/header.php' ?>
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<form class="layui-form" lay-filter="form">
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<label class="layui-form-label " style="width:60px;padding-left: 5px;padding-right: 5px;">文章筛选:</label>
<div class="layui-input-inline" style="width: 150px;">
<select name="category" lay-search>
<option value="0" selected="">全部</option>
<optgroup label="用户分类">
<?php echo_category(true); ?>
</optgroup>
</select>
</div>
</div>
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<label class="layui-form-label layui-hide-sm" style="width:60px;padding-left: 5px;padding-right: 5px;">关键字:</label>
<div class="layui-input-inline">
<input class="layui-input" name="keyword" id="keyword" placeholder='请输入标题或文章内容' autocomplete="off" >
</div>
</div>
<div class="layui-inline layui-form layui-hide-xs" style="padding-bottom: 5px;" >
<label class="layui-form-label layui-hide-sm" style="width:60px;padding-left: 5px;padding-right: 5px; ">属性筛选:</label>
<div class="layui-input-inline" style="width: 80px;">
<select name="state">
<option value="0" selected="">全部</option>
<option value="1">公开</option>
<option value="2">私有</option>
<option value="3">草稿</option>
<option value="4">废弃</option>
</select>
</div>
</div>
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<button class="layui-btn layui-btn-normal "type="button" id="search" style="height: 36px;">搜索</button>
</div>
</form>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<!-- 操作列 -->
<script type="text/html" id="tablebar">
<div class="layui-btn-group">
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit">编辑</a>
</div>
</script>
<!-- 表头工具栏 -->
<script type="text/html" id="toolbar">
<div class="layui-btn-group">
<button class="layui-btn layui-btn-sm layui-btn-danger" id="batch_operation"><span>批量操作</span><i class="layui-icon layui-icon-down layui-font-12"></i></button>
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="add_article">添加文章</button>
<button class="layui-btn layui-btn-sm " lay-event="set">设置</button>
</div>
</script>
<script src = "<?php echo $libs;?>/jquery/jquery-3.6.0.min.js"></script>
<script src = "./templates/admin/js/public.js?v=<?php echo $Ver;?>"></script>
<?php load_static('js');?>
<!--批量修改分类-->
<ul class="batch_category" style="margin-top: 18px;display:none;padding-right: 10px;padding-left: 10px;">
<form class="layui-form" lay-filter="batch_category">
<div class="layui-form-item">
<label class="layui-form-label">父级分类</label>
<div class="layui-input-block">
<select id="batch_category_fid">
<?php echo_category(true); ?>
</select>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal layui-btn-danger cancel" type="button">取消</button>
<button class="layui-btn" type="button" id="batch_category" >确定修改</button>
</div>
</div>
</form>
</ul>
<!--设置-->
<ul class="set" style="margin-top: 18px;display:none;padding-right: 10px;padding-left: 10px;">
<form class="layui-form" lay-filter="set_form">
<div class="layui-form-item">
<label class="layui-form-label">显示文章</label>
<div class="layui-input-inline">
<select name="visual">
<option value="1">显示靠前</option>
<option value="2">显示靠后</option>
<option value="0">隐藏</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">是否在主页显示文章链接</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">链接图标</label>
<div class="layui-input-inline">
<select name="icon">
<option value="0">首字图标</option>
<option value="1">站点图标</option>
<option value="2">文章封面</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">设为文章封面且无封面时显示站点图标</div>
</div>
<div class="layui-form-item" style="padding-top: 10px;">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal layui-btn-danger cancel">取消</button>
<button class="layui-btn" lay-submit id="save_set">保存</button>
</div>
</div>
<pre class="layui-code" >
小提示:
1.文章所属分类加密时不会对文章加密 (暂不支持文章加密,不想被看到可将文章设为私有)
2.文章所属分类私有且未登录时不显示文章链接 (通过文章链接访问不受限制,不想被看到可将文章设为私有)
3.上传图片支持格式:jpg|png|gif|bmp|jpeg|svg 大小限制:5M
4.编辑器中上传图片小于128KB时使用base64编码存入数据库,大于128KB时将以文件的方式上传到服务器
5.显示文章选项中的靠前/靠后是指文章链接在所属分类下的位置,隐藏则不在主页显示
</pre>
</form>
</ul>
<script>
layui.use(['form','table','dropdown','miniTab'], function () {
var $ = layui.jquery;
var table = layui.table;
var form = layui.form;
var dropdown = layui.dropdown;
var miniTab = layui.miniTab;
var api = get_api('read_article','article_list');
var limit = localStorage.getItem(u + "_limit") || 50;
var state_data = ["Null","公开", "私有", "草稿", "废弃"];
var cols=[ //表头
{type:'checkbox'} //开启复选框
,{ title:'操作', toolbar: '#tablebar',width:110}
,{field: 'title', title: '标题', minWidth:200,templet: function(d){
return '<a style="color:#3c78d8" target="_blank" href="./?c=article&id=' +d.id + '&u=' + u + '" title="' + d.summary + '">'+d.title+'</a>'
}}
,{field:'category',title:'分类',width:100,templet: function(d){
return d.category_name;
}}
,{field:'state',title:'状态',width:100,templet: function(d){
return state_data[d.state];
}}
,{field: 'browse_count', title: '浏览次数',width:120,sort:true}
,{field: 'add_time', title: '添加时间', width:170, sort: true,templet:function(d){
var add_time = timestampToTime(d.add_time);
return add_time;
}}
,{field: 'up_time', title: '修改时间', width:170,sort:true,templet:function(d){
return d.up_time == null ?'':timestampToTime(d.up_time);
}}
];
table.render({
elem: '#table'
,height: 'full-80'
,url: api
,page: true
,limit:limit
,limits: [20,50,100,300,500]
,even:true
,loading:true
,toolbar: '#toolbar'
,id:'table'
,cols: [cols]
,method: 'post'
,response: {statusCode: 1 }
,done: function (res, curr, count) {
batch_operation();//初始化批量操作菜单
//获取当前每页显示数量.并写入本都储存
var temp_limit = $(".layui-laypage-limits option:selected").val();
if(temp_limit > 0 && localStorage.getItem(u + "_limit") != temp_limit){
localStorage.setItem(u + "_limit",temp_limit);
}
}
});
//批量操作
function batch_operation(){
dropdown.render({
elem: '#batch_operation',
data: [{
title: ' 修改分类 ',
id: 'up_category'
},{
title: '修改状态',
child: [{
title: '设为公开',
id: "up_state",
value: 1
},{
title: '设为私有',
id: "up_state",
value: 2
},{
title: '设为草稿',
id: "up_state",
value: 3
},{
title: '设为废弃',
id: "up_state",
value: 4
}]
},{
title: '批量删除',
id: 'del_article'
}],
click: function(obj){
let checkStatus = table.checkStatus('table').data;
if( checkStatus.length == 0 ) {
layer.msg('未选中任何数据!');
return;
}
//获取被选ID并格式化
tableIds = checkStatus.map(function (value) {return value.id;});
tableIds = JSON.stringify(tableIds);
//删除文章
if(obj.id == 'del_article'){
layer.confirm('确认删除?',{icon: 3, title:'温馨提示'}, function(index){
$.post(get_api('write_article','del_article'),{id:tableIds},function(data,status){
if(data.code == 1) {
search();
layer.msg(data.msg, {icon: 1});
}else{
layer.msg(data.msg, {icon: 5});
}
});
});
}else if(obj.id == 'up_category'){
index = layer.open({type: 1,scrollbar: false,shadeClose: true,title: false ,area : ['100%', '100%'],closeBtn:0,content: $('.batch_category')});
}else if(obj.id == 'up_state'){
$.post(get_api('write_article','up_state'),{'id':tableIds,'state_id':obj.value},function(data,status){
if(data.code == 1) {
search();
layer.msg('操作成功', {icon: 1});
}else{
layer.msg(data.msg || '未知错误',{icon: 5});
}
});
}
}
});
}
//输入框回车事件和搜索按钮点击事件
$('#keyword, #search').on('keydown click', function(e) {
if ( (e.target.id === 'keyword' && e.keyCode === 13) || (e.target.id === 'search' && e.type === 'click') ) {
search();
}
});
//搜索
function search(){
let data = form.val('form');
table.reload('table', {
url: api
,method: 'post'
,request: {pageName: 'page',limitName: 'limit'}
,where: data
,page: {curr: 1}
});
}
//监听工具栏
table.on('toolbar(table)', function (obj) {
var btn = obj.event;
if (btn == 'add_article') { //添加文章
layer.open({
title: false,
type: 2,
scrollbar: false,
shade: 0.2,
maxmin:false,
shadeClose: true,
closeBtn:0,
area: ['100%', '100%'],
content: './?c=admin&page=expand/article-edit&u=' + u,
end: function(){
search();
}
});
}else if(btn == 'set'){ //设置
index = layer.open({type: 1,scrollbar: false,shadeClose: true,title: false ,area : ['100%', '100%'],closeBtn:0,content: $('.set')});
}else{ //综合批量操作
//取选中数据
var checkStatus = table.checkStatus(obj.config.id);
if( checkStatus.data.length == 0 && ['LAYTABLE_COLS','LAYTABLE_EXPORT','LAYTABLE_PRINT'].indexOf(btn) == -1 ) {
layer.msg('未选中任何数据!');
return;
}
//批量删除
if(btn == 'batch_del'){
tableIds = checkStatus.data.map(function (value) {return value.id;});
tableIds = JSON.stringify(tableIds);
layer.confirm('确认删除?',{icon: 3, title:'温馨提示'}, function(index){
$.post(get_api('write_article','del_article'),{id:tableIds},function(data,status){
if(data.code == 1) {
search();
layer.msg(data.msg, {icon: 1});
}else{
layer.msg(data.msg, {icon: 5});
}
});
});
}
}
});
//监听行工具
table.on('tool(table)', function (obj) {
let btn = obj.event;
let data = obj.data;
if (btn === 'del') {
layer.confirm('确认删除?',{icon: 3, title:'温馨提示'}, function(index){
$.post(get_api('write_article','del_article'),{id:'['+data.id+']'},function(data,status){
if(data.code == 1) {
obj.del();
layer.msg(data.msg, {icon: 1});
}else{
layer.msg(data.msg, {icon: 5});
}
});
});
}else if(btn === 'edit'){
layer.open({
title: false,
type: 2,
scrollbar: false,
shade: 0.2,
maxmin:false,
shadeClose: true,
closeBtn:0,
area: ['100%', '100%'],
content: './?c=admin&page=expand/article-edit&id='+data.id+'&u=' + u,
end: function(){
search();
}
});
}
});
//设置相关
form.val('set_form', <?php echo json_encode($set);?>);
$('#save_set').on('click', function(){
$.post(get_api('write_article','save_article_set'),form.val('set_form'),function(data,status){
if(data.code == 1) {
layer.close(index);
layer.msg('操作成功', {icon: 1});
}else{
layer.msg(data.msg || '未知错误',{icon: 5});
}
});
return false;
});
//取消按钮
$('.cancel').click(function () {
layer.close(index);
return false;
});
//批量修改分类
$('#batch_category').click(function () {
fid = $('#batch_category_fid').val();
$.post(get_api('write_article','up_category'),{'id':tableIds,'category_id':fid},function(data,status){
if(data.code == 1) {
search();
layer.close(index);
layer.msg('操作成功', {icon: 1});
}else{
layer.msg(data.msg || '未知错误',{icon: 5});
}
});
return false;
});
});
</script>
</body>
</html>

View File

@@ -14,14 +14,27 @@ if(!empty($Notice)){
}
//是否下载数据
if(!offline && $reload){
$Res = ccurl('https://update.lm21.top/TwoNav/Notice.json',3);
$new_data = json_decode($Res['content'], true);unset($Res);
if($new_data["code"] == 200 ){ //下载成功,写入缓存
$new_data['download_time'] = time();
write_global_config('notice',json_encode($new_data),'官方公告(缓存)');
$data = $new_data;
$overtime = !isset($global_config['Update_Overtime']) ? 3 : ($global_config['Update_Overtime'] < 3 || $global_config['Update_Overtime'] > 60 ? 3 : $global_config['Update_Overtime']);
$urls = [
"lm21" => "https://update.lm21.top/TwoNav/Notice.json",
"gitee" => "https://gitee.com/tznb/twonav_updata/raw/master/Notice.json"
];
$Source = $global_config['Update_Source'] ?? '';
if (!empty($Source) && isset($urls[$Source])) {
$urls = [$Source => $urls[$Source]];
}
foreach($urls as $key => $url){
$Res = ccurl($url,$overtime);
$new_data = json_decode($Res['content'], true);unset($Res);
if($new_data["code"] == 200 ){ //下载成功,写入缓存
$new_data['download_time'] = time();
write_global_config('notice',json_encode($new_data),'官方公告(缓存)');
$data = $new_data;
unset($new_data);
break;
}
}
unset($new_data);
}
//判断是否为空
if(empty($data['version'])){
@@ -227,9 +240,16 @@ require 'header.php';
</div>
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header"><i class="fa fa-line-chart icon"></i>报表统计</div>
<div class="layui-card-header">
<div style="display: flex; justify-content: space-between;">
<div><i class="fa fa-line-chart icon"></i>报表统计</div>
<div>
<button class="layui-btn layui-btn-primary echarts" style="border: none;display:none;"><span>最近7天</span><i class="layui-icon layui-icon-down layui-font-12"></i></button>
</div>
</div>
</div>
<div class="layui-card-body">
<div id="echarts-records" style="width: 100%;min-height:500px"></div>
<div id="echarts-records" style="width: 100%; min-height: 500px;"></div>
</div>
</div>
</div>
@@ -258,7 +278,7 @@ require 'header.php';
<?php if($USER_DB['UserGroup'] == 'root'){ ?>
<tr>
<td>最新版本</td>
<td id="new_ver"><?php echo $data['version'] ?? SysVer; ?></td>
<td id="new_ver"><a target="_blank" href="https://gitee.com/tznb/TwoNav/releases"><?php echo $data['version'] ?? SysVer; ?></a> </td>
</tr>
<tr>
<td>授权状态</td>
@@ -273,7 +293,7 @@ require 'header.php';
</tr>
<tr>
<td>技术支持</td>
<td><a target="_blank" href="tencent://message/?uin=271152681">QQ271152681</a></td>
<td><a target="_blank" href="tencent://message/?uin=271152681">QQ271152681</a></td>
</tr>
<tr>
<td>专属地址</td>
@@ -310,29 +330,61 @@ if($USER_DB['UserGroup'] == 'root'){
var $ = layui.jquery,
layer = layui.layer,
miniTab = layui.miniTab,
echarts = layui.echarts;
echarts = layui.echarts,
dropdown = layui.dropdown;
miniTab.listen();
//报表功能
var echartsRecords = echarts.init(document.getElementById('echarts-records'), 'walden');
var optionRecords = {
tooltip: {trigger: 'axis'},
legend: {data:['访问量','点击量']},
grid: {left: '3%',right: '4%',bottom: '3%',containLabel: true},
xAxis: {
type: 'category',
boundaryGap: false,
data: <?php echo json_encode($day)?>
},
yAxis: {},
series: <?php echo json_encode($day_data)?>
};
echartsRecords.setOption(optionRecords);
// echarts 窗口缩放自适应
window.onresize = function(){
echartsRecords.resize();
//报表统计下拉初始化
var home_echarts = localStorage.getItem(u + "_home_echarts") || 7 ;
$('.echarts').find('span').text(`最近${home_echarts}天`);
$('.echarts').show();
dropdown.render({
elem: '.echarts',
data: [{
title: '最近7天',
value: 7
},{
title: '最近14天',
value: 14
},{
title: '最近30天',
value: 30
}],
click: function(obj){
this.elem.find('span').text(obj.title);
localStorage.setItem(u + "_home_echarts",obj.value);
home_echarts = obj.value;
load_echarts();
}
});
//加载报表统计
function load_echarts(){
var echartsRecords = echarts.init(document.getElementById('echarts-records'), 'walden');
$.post('./index.php?c=api&method=read_data&date='+home_echarts+'&type=echarts&u='+u,function(data,status){
if(data.code == 1){
var optionRecords = {
tooltip: {trigger: 'axis'},
legend: {data:['访问量','点击量','IP数']},
grid: {left: '3%',right: '4%',bottom: '3%',containLabel: true},
xAxis: {
type: 'category',
boundaryGap: false,
data: data.data.dates
},
yAxis: {},
series: data.data.day_data
};
echartsRecords.setOption(optionRecords);
window.onresize = function(){echartsRecords.resize();} // echarts 窗口缩放自适应
return;
}
layer.alert("获取统计数据失败..",{icon:5,title:'错误',anim: 2,closeBtn: 0,btn: ['刷新页面']},function () {location.reload();});
});
}
load_echarts();
//定时刷新
setInterval(function() {
if($("#layuiminiHomeTabId",parent.document).attr('class') == 'layui-this' && document.visibilityState == 'visible'){

View File

@@ -79,14 +79,25 @@
</li>
</ul>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键字</label>
<div class="layui-input-block">
<input type="text" id="keywords" name="keywords" placeholder="可留空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea name="description" id="description" placeholder="请输入内容" class="layui-textarea"></textarea>
<textarea name="description" id="description" placeholder="可留空" class="layui-textarea"></textarea>
</div>
</div>
<?php
//判断全局是否开启扩展
if($global_config['link_extend'] && check_purview('link_extend',1)){
require 'link_extend.php';
}?>
<div class="layui-form-item">
<div class="layui-input-block">
<input type="checkbox" id="continuity" lay-skin="primary" title="连续添加">

View File

@@ -1,5 +1,5 @@
<?php
$link = get_db('user_links',['lid','fid','pid(pwd_id)','property','title','url','url_standby','description','icon'],['uid'=>UID,'lid'=>$_GET['id']]);
$link = get_db('user_links',['lid','fid','pid(pwd_id)','property','title','url','url_standby','keywords','description','icon'],['uid'=>UID,'lid'=>$_GET['id']]);
if(empty($link)){
require(DIR.'/templates/admin/page/404.php');
exit;
@@ -92,11 +92,18 @@ $title='编辑链接';$awesome=true; require 'header.php';
</li>
</ul>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键字</label>
<div class="layui-input-block">
<input type="text" id="keywords" name="keywords" placeholder="可留空" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea name="description" id="description" placeholder="请输入内容" class="layui-textarea"></textarea>
<textarea name="description" id="description" placeholder="可留空" class="layui-textarea"></textarea>
</div>
</div>
<?php

View File

@@ -13,19 +13,22 @@ if(!empty($list)){
foreach ($list as $data) {
$field = "_".$data['name'];
$data['value'] = isset($extend_data[$field]) ? $extend_data[$field] : $data['default'];
$link["_{$data['name']}"] = ''.htmlentities($data['value']);
if($data['type'] == 'text'){
echo_text($data);
}elseif($data['type'] == 'textarea'){
echo_textarea($data);
}elseif($data['type'] == 'up_img'){
echo_up_img($data);
}
}
}
function echo_text($data){ ?>
<div class="layui-form-item">
<label class="layui-form-label"><?php echo $data['title']?></label>
<label class="layui-form-label" title="<?php echo $data['name']?>"><?php echo $data['title']?></label>
<div class="layui-input-block">
<input type="text" name="_<?php echo $data['name']?>" autocomplete="off" value="<?php echo htmlentities($data['value'])?>" class="layui-input">
<input type="text" name="_<?php echo $data['name']?>" autocomplete="off" value="<?php echo htmlentities($data['value'])?>" placeholder="<?php echo htmlentities($data['tip'])?>" class="layui-input" >
</div>
</div>
<?php
@@ -33,13 +36,26 @@ function echo_text($data){ ?>
function echo_textarea($data){ ?>
<div class="layui-form-item">
<label class="layui-form-label"><?php echo $data['title']?></label>
<label class="layui-form-label" title="<?php echo $data['name']?>"><?php echo $data['title']?></label>
<div class="layui-input-block">
<textarea name="_<?php echo $data['name']?>" class="layui-textarea"><?php echo htmlentities($data['value'])?></textarea>
<textarea name="_<?php echo $data['name']?>" placeholder="<?php echo htmlentities($data['tip'])?>" class="layui-textarea"><?php echo htmlentities($data['value'])?></textarea>
</div>
</div>
<?php
}
}
function echo_up_img($data){ ?>
<div class="layui-form-item">
<label class="layui-form-label" title="<?php echo $data['name']?>"><?php echo $data['title']?></label>
<div class="layui-input-block layuimini-upload" >
<input type="text" name="_<?php echo $data['name']?>" autocomplete="off" class="layui-input obtn" value="<?php echo htmlentities($data['value'])?>" placeholder="<?php echo htmlentities($data['tip'])?>">
<div style="position: absolute; top:0px;" >
<span><a class="layui-btn layui-btn-primary extend_up_img"><i class="fa fa-upload"></i> 上传</a></span>
</div>
</div>
</div>
<?php
}
?>

View File

@@ -5,11 +5,11 @@
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<form class="layui-form" lay-filter="form">
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<label class="layui-form-label " style="width:60px;padding-left: 5px;padding-right: 5px;">分类筛选:</label>
<div class="layui-input-inline">
<select id="fid" lay-filter="fid" name="categorys" lay-search>
<select name="fid" lay-search>
<option value="0" selected="">全部</option>
<optgroup label="用户分类">
<?php echo_category(true); ?>
@@ -21,21 +21,20 @@
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<label class="layui-form-label layui-hide-sm" style="width:60px;padding-left: 5px;padding-right: 5px;">关键字:</label>
<div class="layui-input-inline">
<input class="layui-input" name="keyword" id="link_keyword" placeholder='请输入标题或描述或URL' value=''autocomplete="off" >
<input class="layui-input" name="query" id="link_keyword" placeholder='请输入标题或描述或URL' autocomplete="off" >
</div>
</div>
<div class="layui-inline layui-form layui-hide-xs" style="padding-bottom: 5px;" >
<label class="layui-form-label layui-hide-sm" style="width:60px;padding-left: 5px;padding-right: 5px; ">属性筛选:</label>
<div class="layui-input-inline" style=" width: 80px; ">
<select id="property" >
<select name="property" >
<option value="" selected>不限</option>
<option value="0" >公开</option>
<option value="1" >私有</option>
</select>
</div>
<div class="layui-input-inline" style=" width: 80px; ">
<select id="status" >
<select name="status" >
<option value="" selected>不限</option>
<option value="0" >禁用</option>
<option value="1" >启用</option>
@@ -43,12 +42,12 @@
</div>
</div>
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<button class="layui-btn layui-btn-normal " id="link_search" style="height: 36px;">搜索</button>
<button class="layui-btn layui-btn-normal "type="button" id="link_search" style="height: 36px;">搜索</button>
</div>
<span id = "testing_tip" style = "display:none;">测试中...</span>
<span id = "subscribe" style = "display:none;"><?php echo is_subscribe('bool')?'1':'0' ?></span>
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
</form>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<!-- 操作列 -->
@@ -75,9 +74,7 @@
<?php if($global_config['offline'] != 1 ){ ?>
<button class="layui-btn layui-btn-sm layui-btn-normal layui-btn-danger layui-hide-xs" lay-event="testing" id="testing">检测</button>
<?php }?>
<?php if($global_config['offline'] != 1 && check_purview('icon_pull',1)){ ?>
<button class="layui-btn layui-btn-sm layui-btn-normal layui-btn-danger layui-hide-xs" lay-event="icon_pull" id="icon_pull">图标拉取</button>
<?php }?>
<button class="layui-btn layui-btn-sm layui-btn-normal layui-btn-danger layui-hide-xs" lay-event="msg_pull" id="msg_pull">识别</button>
<button class="layui-btn layui-btn-sm layui-btn-normal layui-btn-danger" layuimini-content-href="link_sort" data-title="链接排序">排序模式</button>
</div>
</script>
@@ -116,5 +113,55 @@
</div>
</script>
</ul>
<ul class="msg_pull" style="margin-top: 18px;display:none;padding-right: 10px;padding-left: 10px;">
<form class="layui-form layuimini-form" lay-filter="msg_pull">
<pre class="layui-code" id="tip" >提示: 自动识别仅针对http/https有效,且不能保证百分百成功!未成功识别时不会对链接信息进行修改!大批量识别前建议先备份数据,效果不理想时可以回退!</pre>
<div class="layui-form-item">
<label class="layui-form-label">网站标题</label>
<div class="layui-input-inline">
<select name="title" >
<option value="0">保持不变</option>
<option value="1">获取 (覆盖)</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键字</label>
<div class="layui-input-inline">
<select name="keywords" >
<option value="0">保持不变</option>
<option value="1" selected>获取 (未填关键字时)</option>
<option value="2">获取 (覆盖)</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站描述</label>
<div class="layui-input-inline">
<select name="description" >
<option value="0">保持不变</option>
<option value="1" selected>获取 (未填描述时)</option>
<option value="2">获取 (覆盖)</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">网站图标</label>
<div class="layui-input-inline">
<select name="icon" >
<option value="0">保持不变</option>
<option value="1" selected>获取 (未上传图标时)</option>
<option value="2">获取 (覆盖)</option>
</select>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-warm" type="button" id="close" >关闭</button>
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="start_pull" id="start_pull">开始</button>
</div>
</div>
</form>
</ul>
</body>
</html>

View File

@@ -13,7 +13,7 @@
</div>
</div>
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<!-- 表头工具栏 -->

View File

@@ -5,7 +5,7 @@
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<!-- 操作列 -->

View File

@@ -9,7 +9,7 @@ $LoginConfig = unserialize( get_db("global_config", "v", ["k" => "LoginConfig"])
<div class="layuimini-main">
<form class="layui-form" lay-filter="form">
<div class="layui-form layuimini-form layui-form-pane">
<blockquote class="layui-elem-quote layui-text" style="">1.本页功能<a href="https://gitee.com/tznb/OneNav/wikis/%E8%AE%A2%E9%98%85%E6%9C%8D%E5%8A%A1%E6%8C%87%E5%BC%95" target="_blank">授权用户</a>专享<br />2.用户注册后默认使用此方案<br />3.如果您不理解选项的作用请勿乱改 </blockquote>
<blockquote class="layui-elem-quote layui-text" style="">1.本页功能<a href="https://gitee.com/tznb/OneNav/wikis/%E8%AE%A2%E9%98%85%E6%9C%8D%E5%8A%A1%E6%8C%87%E5%BC%95" target="_blank">授权用户</a>专享<br />2.用户注册后默认使用此方案<br />3.此功能不会修改现有用户的配置<br />4.如果您不理解选项的作用请勿乱改 </blockquote>
<fieldset class="layui-elem-field layui-field-title"><legend>安全设置</legend></fieldset>
<div class="layui-form-item">
@@ -77,6 +77,18 @@ $LoginConfig = unserialize( get_db("global_config", "v", ["k" => "LoginConfig"])
<div class="layui-form-mid layui-word-aux">部分主题和插件需设为开放模式!</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">登录后</label>
<div class="layui-input-inline" >
<select name="login_page">
<option value="admin" selected>进入后台</option>
<option value="index">进入主页</option>
<option value="auto">自动识别</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">自动识别:移动设备登录则进入主页,反之进入后台</div>
</div>
<fieldset class="layui-elem-field layui-field-title"><legend>站点设置</legend></fieldset>
<div class="layui-form-item">
<label class="layui-form-label">主标题</label>

View File

@@ -86,6 +86,7 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-danger" type="button" id="clean">清除缓存</button>
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="save">确认保存</button>
</div>
</div>
@@ -103,7 +104,24 @@ layui.use(['jquery','form'], function () {
//表单赋值
form.val('form', <?php echo json_encode(unserialize( get_db("global_config", "v", ["k" => "icon_config"])));?>);
//清除缓存
$('#clean').click(function() {
layer.confirm('确定要清除全部缓存吗?',{icon: 3, title:'温馨提示'}, function(index){
$.post(get_api('other_root','write_icon_del_cache'),function(data,status){
if(data.code == 1) {
if(data.msg!="操作成功"){
layer.alert(data.msg)
}else{
layer.msg(data.msg, {icon: 1});
}
}else{
layer.msg(data.msg, {icon: 5});
}
});
return false;
});
});
//监听提交
form.on('submit(save)', function (data) {
$.post(get_api('other_root','write_icon_config'),data.field,function(data,status){

View File

@@ -17,18 +17,21 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
<div class="layui-input-inline">
<input type="pass" name="user" lay-verify="required" lay-reqtext="账号不能为空" placeholder='请输入账号' autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">邮箱账号,例如: admin@qq.com</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码</label>
<div class="layui-input-inline">
<input type="password" name="pwd" lay-verify="required" lay-reqtext="密码不能为空" placeholder='请输入密码或授权码' autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">邮箱密码,也可能是独立密码或者授权码</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">服务器</label>
<div class="layui-input-inline">
<input type="text" name="host" lay-verify="required" lay-reqtext="服务器不能为空" placeholder='请输入发件服务器地址' autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">例如: smtp.qq.com</div>
</div>
<div class="layui-form-item">
@@ -36,6 +39,7 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
<div class="layui-input-inline">
<input type="number" name="port" lay-verify="required" lay-reqtext="端口不能为空" placeholder='请输入服务器端口' value="465" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">通常是: 465或587</div>
</div>
<div class="layui-form-item">
@@ -46,6 +50,7 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
<option value="TLS" >TLS</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">通常是: ssl</div>
</div>
<div class="layui-form-item">
@@ -53,6 +58,7 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
<div class="layui-input-inline">
<input type="text" name="sender" lay-verify="required" lay-reqtext="发送人邮箱不能为空" placeholder='' autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">例如: TwoNav书签&lt;test@qq.com&gt;</div>
</div>
<div class="layui-form-item">
@@ -60,6 +66,7 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
<div class="layui-input-inline">
<input type="text" name="addressee" placeholder='仅用于发件测试' autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">例如:user@qq.com</div>
</div>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"><legend>注册参数</legend></fieldset>
@@ -71,12 +78,14 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
<option value="1" >开启</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">开启时用户注册需通过邮箱接收验证码</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">发送间隔</label>
<div class="layui-input-inline">
<input type="number" name="send_interval" lay-verify="required" lay-reqtext="发送间隔不能为空" placeholder='IP发送间隔,单位秒!' value="60" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">为了避免被恶意发送,建议不低于30秒</div>
</div>
<div class="layui-form-item layui-form-text">

View File

@@ -9,7 +9,7 @@ $user_groups = select_db('user_group',['id','code','name'],'');
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<!-- 表头工具栏 -->

View File

@@ -28,7 +28,7 @@ require(dirname(__DIR__).'/header.php');
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<button class="layui-btn layui-btn-normal " id="search" style="height: 36px;">搜索</button>
</div>
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>

View File

@@ -2,9 +2,17 @@
if($USER_DB['UserGroup'] != 'root'){$content='您没有权限访问此页面'; require(DIR.'/templates/admin/page/404.php');exit;}
$title='系统设置';require(dirname(__DIR__).'/header.php');
?>
<style>
.layui-btn-container .layui-btn{border-width: 1px; border-style: solid; border-color: #FF5722!important; color: #FF5722!important;background: none;height: 30px; line-height: 30px; padding: 0 10px; font-size: 12px;}
</style>
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<div class="layui-btn-container">
<button type="button" class="layui-btn" layuimini-content-href="root/default_setting" data-title="默认设置">默认设置</button>
<button type="button" class="layui-btn" layuimini-content-href="root/mail_set" data-title="邮件配置">邮件配置</button>
<button type="button" class="layui-btn" layuimini-content-href="root/icon_set" data-title="图标配置">图标配置</button>
</div>
<form class="layui-form" lay-filter="form">
<div class="layui-form layuimini-form layui-form-pane">
<blockquote class="layui-elem-quote layui-text" style="">1.带*号的选项属<a href="https://gitee.com/tznb/OneNav/wikis/%E8%AE%A2%E9%98%85%E6%9C%8D%E5%8A%A1%E6%8C%87%E5%BC%95" target="_blank">授权用户</a>专享<br />2.原OneNav Extend的部分配置已下放到用户组配置中<br />3.如果您不理解选项的作用请勿乱改 </blockquote>
@@ -180,14 +188,35 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
</div>
</div>
<div class="layui-form-item" id="api_extend" style="display:none;">
<label class="layui-form-label required">api_extend</label>
<div class="layui-form-item layui-hide" id="api_extend">
<label class="layui-form-label">api_extend</label>
<div class="layui-input-inline">
<select name="api_extend">
<option value="0" selected="">关闭</option>
<option value="1" >开启</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">请勿开启!请勿开启!请勿开启!</div>
</div>
<div class="layui-form-item layui-hide">
<label class="layui-form-label">资源接口</label>
<div class="layui-input-inline">
<select name="Update_Source">
<option value="0" selected="">自动</option>
<option value="lm21">主线路</option>
<option value="gitee">备用线路(gitee)</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">备用资源不定期更新,非必要请勿使用!</div>
</div>
<div class="layui-form-item layui-hide">
<label class="layui-form-label">资源超时</label>
<div class="layui-input-inline">
<input type="number" name="Update_Overtime" autocomplete="off" value="3" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">默认3秒,范围3-60</div>
</div>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"><legend>扩展功能</legend></fieldset>
@@ -222,43 +251,70 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
</div>
<div class="layui-form-mid layui-word-aux">自定义链接的扩展信息(需自行添加字段,目前仅用于自定义过渡页)</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">文章管理</label>
<div class="layui-input-inline">
<select name="article">
<option value="0" selected="">关闭</option>
<option value="1" >开启</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">简易文章管理功能 ( 请勿和专业的比,首次开启时自动下载相关资源 )</div>
</div>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"><legend>长度限制</legend></fieldset>
<blockquote class="layui-elem-quote layui-text" style="">程序采用UTF8编码,一个汉字约占用3个字节!英文字母和数组占用1个字节!值为0表示不限制!</blockquote>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;"><legend>相关限制</legend></fieldset>
<blockquote class="layui-elem-quote layui-text" style="">程序采用UTF8编码,一个汉字约占用3个字节!英文字母和数组占用1个字节!值为0表示不限制!<br />添加或编辑时长度超限则不允许添加,批量识别时超限则截断</blockquote>
<div class="layui-form-item">
<label class="layui-form-label required">分类名称</label>
<div class="layui-input-inline">
<input type="number" name="c_name" autocomplete="off" value="0" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">单位:字节。</div>
<div class="layui-form-mid layui-word-aux">字符长度限制,单位:字节。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">分类描述</label>
<div class="layui-input-inline">
<input type="number" name="c_desc" autocomplete="off" value="0" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">单位:字节。</div>
<div class="layui-form-mid layui-word-aux">字符长度限制,单位:字节。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">链接名称</label>
<div class="layui-input-inline">
<input type="number" name="l_name" autocomplete="off" value="0" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">单位:字节。</div>
<div class="layui-form-mid layui-word-aux">字符长度限制,单位:字节。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">链接地址</label>
<div class="layui-input-inline">
<input type="number" name="l_url" autocomplete="off" value="0" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">单位:字节。</div>
<div class="layui-form-mid layui-word-aux">字符长度限制,单位:字节。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">链接关键字</label>
<div class="layui-input-inline">
<input type="number" name="l_key" autocomplete="off" value="0" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">字符长度限制,单位:字节。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">链接描述</label>
<div class="layui-input-inline">
<input type="number" name="l_desc" autocomplete="off" value="0" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">单位:字节。</div>
<div class="layui-form-mid layui-word-aux">字符长度限制,单位:字节。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">自定义代码</label>
<div class="layui-input-inline">
<select name="c_code" lay-filter="c_code">
<option value="0" selected="">禁止</option>
<option value="1" >允许</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">是否允许默认用户组使用自定义代码!允许存在安全隐患!</div>
</div>
<div class="layui-form-item">
@@ -268,22 +324,30 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
</form>
</div>
</div>
<script src = "<?php echo $libs;?>/jquery/jquery-3.6.0.min.js"></script>
<?php load_static('js.layui');?>
<script>
layui.use(['jquery','form'], function () {
layui.use(['jquery','form','miniTab'], function () {
var form = layui.form;
var layer = layui.layer;
var $ = layui.jquery;
var miniTab = layui.miniTab;
miniTab.listen();
//表单赋值
form.val('form', <?php echo json_encode($global_config);?>);
form.val('form', <?php echo json_encode(unserialize( get_db("global_config", "v", ["k" => "length_limit"])));?>);
//危险提示
form.on('select(c_code)', function(data){
if (data.value === '1') {
layer.alert("允许使用自定义代码存在安全隐患<br />除非您信任使用者!否则建议禁止<br />同时请避免在登录管理员账号时浏览其他用户的主页", { title: '危险提示:' })
}
});
//监听提交
form.on('submit(save)', function (data) {
layer.msg('正在保存中,请稍后...', {icon: 16,time: 1000*300,shadeClose: false});
$.post('./index.php?c=api&method=write_sys_settings&u='+u,data.field,function(data,status){
layer.closeAll();
if(data.code == 1) {
if(data.msg!="保存成功"){
layer.alert(data.msg)
@@ -299,14 +363,13 @@ layui.use(['jquery','form'], function () {
//开启隐藏功能
$('.layui-elem-field').click(function () {
if(Number( $(this).attr('click')) >= 6){
$("#api_extend").show();
}else{
let click = $(this).attr('click') ? Number($(this).attr('click')) + 1 : 0;
$(this).attr('click',click)
let clickCount = Number($(this).attr('click') || 0);
if (clickCount >= 6) {
$(".layui-hide").removeClass("layui-hide");
} else {
$(this).attr('click', clickCount + 1);
}
});
});
});
</script>

View File

@@ -1,6 +1,8 @@
<?php
if($USER_DB['UserGroup'] != 'root'){$content='您没有权限访问此页面'; require(DIR.'/templates/admin/page/404.php');exit;}
$title='站长工具';
session_start();
$_SESSION['phpinfo_id'] = Get_Rand_Str(8);
if(function_exists("opcache_reset")){
opcache_reset(); //清理PHP缓存
}
@@ -18,6 +20,7 @@ require(dirname(__DIR__).'/header.php');
<div class="layui-btn-container">
<button type="button" class="layui-btn copy_log">复制内容</button>
<button type="button" class="layui-btn diagnose">一键诊断</button>
<button type="button" class="layui-btn connectivity_test">连通测试</button>
<button type="button" class="layui-btn phpinfo">phpinfo</button>
<?php if(preg_match('/nginx/i',$_SERVER['SERVER_SOFTWARE']) ){ ?>
<button type="button" class="layui-btn rewrite">生成伪静态</button>
@@ -27,8 +30,6 @@ require(dirname(__DIR__).'/header.php');
<button type="button" class="layui-btn" layuimini-content-href="root/sys_log" data-title="系统日志">系统日志</button>
<button type="button" class="layui-btn" layuimini-content-href="updatelog" data-title="更新日志">更新日志</button>
<button type="button" class="layui-btn" layuimini-content-href="root/import_data" data-title="导入数据">导入数据</button>
<button type="button" class="layui-btn" layuimini-content-href="root/mail_set" data-title="邮件配置">邮件配置</button>
<button type="button" class="layui-btn" layuimini-content-href="root/icon_set" data-title="图标配置">图标配置</button>
</div>
<pre class="layui-code" id="console_log" >
1.功能都集中在上方的按钮了,需要那个就点击那个!
@@ -48,6 +49,7 @@ require(dirname(__DIR__).'/header.php');
<script src = "<?php echo $libs;?>/jquery/jquery-3.6.0.min.js"></script>
<script src = "./templates/admin/js/public.js?v=<?php echo $Ver;?>"></script>
<script src = "<?php echo $libs?>/Other/ClipBoard.min.js"></script>
<script src = '<?php echo $libs?>/jquery/jquery.md5.js'></script>
<?php load_static('js');?>
<script>
layui.use(['layer','form','miniTab'], function () {
@@ -75,8 +77,47 @@ layui.use(['layer','form','miniTab'], function () {
});
});
//连通测试
$('.connectivity_test').on('click', function(){
$("#console_log").text("");
$("#console_log").append("浏览器UA" + navigator.userAgent +"\n");
$("#console_log").append("客户端时间:" + timestampToTime(Math.round(new Date() / 1000) ) +"\n");
var urls = [
['主线路', 'https://update.lm21.top/connectivity_test.txt'],
['备用线路(Gitee)', 'https://gitee.com/tznb/twonav_updata/raw/master/connectivity_test.txt']
];
urls.forEach(function(route) {
var routeName = route[0];
var url = route[1];
$("#console_log").append("正在检测: " + routeName +"\n");
$.ajax({
url: get_api('read_data', 'connectivity_test'),
type: 'POST',
data: { url: url },
async: false,
success: function(data, status) {
$("#console_log").append(data.msg + "\n");
},
error: function(jqXHR, textStatus, errorThrown) {
$("#console_log").append(routeName + ": 请求 " + url + " 发生错误:" + errorThrown + "\n");
}
});
});
});
//phpinfo
$('.phpinfo').on('click', function(){
index = layer.prompt({formType: 1,value: '',title: '输入登录密码:',shadeClose: false,"success":function(){
$("input.layui-layer-input").on('keydown',function(e){if(e.which == 13) {echo_phpinfo();}});
}},function(){
echo_phpinfo()
});
});
function echo_phpinfo(){
let p = $("input.layui-layer-input").val();
if(p == ''){ return false;}
layer.close(index);
layer.open({
title: 'phpinfo',
type: 2,
@@ -85,22 +126,23 @@ layui.use(['layer','form','miniTab'], function () {
maxmin:false,
shadeClose: true,
area: ['100%', '100%'],
content: get_api('read_data','phpinfo'),
content: get_api('read_data','phpinfo')+'&p='+$.md5(p)+'&pid=<?php echo $_SESSION['phpinfo_id'] ;?>'
});
});
}
//伪静态
$('.rewrite').on('click', function(){
let pathname = window.location.pathname;
$("#console_log").text("");
$("#console_log").append(`#安全设置\n`);
$("#console_log").append(`location ~* ^${pathname}(data|system|templates)/.*.(db|db3|php|sql|tar|gz|zip|info|log)$ {\n\treturn 403;\n}\n`);
$("#console_log").append(`#伪静态\n`);
$("#console_log").append(`#安全规则(必选)\n`);
$("#console_log").append(`location ^~ ${pathname}data/ {location ~* \\.(db|db3|php|sql|tar|gz|zip|info|log|json)$ {return 403;}}\n`);
$("#console_log").append(`location ^~ ${pathname}templates/ {location ~* \\.(php|tar|gz|zip|info|log|json)$ {return 403;}}\n`);
$("#console_log").append(`#重写规则(可选)\n`);
$("#console_log").append(`rewrite ^${pathname}login$ ${pathname}index.php?c=login break;\n`);
$("#console_log").append(`rewrite ^${pathname}admin$ ${pathname}index.php?c=admin break;\n`);
$("#console_log").append(`rewrite ^${pathname}ico/(.+) ${pathname}index.php?c=icon&url=$1 break;\n`);
$("#console_log").append(`rewrite ^${pathname}([A-Za-z0-9]+)$ ${pathname}index.php?u=$1 break; #HOST/USER\n`);
$("#console_log").append(`rewrite ^${pathname}([A-Za-z0-9]+)$ ${pathname}index.php?u=$1 break;\n`);
$("#console_log").append(`rewrite ^${pathname}(.+)/(click)/([A-Za-z0-9]+)$ ${pathname}index.php?c=$2&id=$3&u=$1 break;\n`);
$("#console_log").append(`rewrite ^${pathname}(.+)/(click)/(.+) ${pathname}$3 break; #static\n`);
$("#console_log").append(`rewrite ^${pathname}(.+)/(click)/(.+) ${pathname}$3 break;\n`);
});
//清理缓存

View File

@@ -28,7 +28,7 @@ $user_groups = select_db('user_group',['id','code','name'],'');
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<button class="layui-btn layui-btn-normal " id="search" style="height: 36px;">搜索</button>
</div>
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>

View File

@@ -6,7 +6,7 @@ require(dirname(__DIR__).'/header.php');
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<script type="text/html" id="tool">

View File

@@ -10,7 +10,7 @@
<div class="layui-input-inline">
<input type="number" min="0" max="60" lay-verify="required|number" name="visitor_stay_time" value = "3" autocomplete="off" placeholder="访客停留时间单位s" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">访客停留时间单位秒</div>
<div class="layui-form-mid layui-word-aux">访客停留时间,单位秒(需模板支持)</div>
</div>
<div class="layui-form-item">
@@ -18,7 +18,18 @@
<div class="layui-input-inline">
<input type="number" min="0" max="60" lay-verify="required|number" name="admin_stay_time" value = "5" autocomplete="off" placeholder="管理员停留时间单位s" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">管理员停留时间单位秒</div>
<div class="layui-form-mid layui-word-aux">管理员停留时间,单位秒(需模板支持)</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" >默认关键字</label>
<div class="layui-input-inline" >
<select name="default_keywords" >
<option value="0">链接标题</option>
<option value="1">站点关键字</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">指链接信息未填写关键字时选择其他值作为关键字</div>
</div>
<div class="layui-form-item">

View File

@@ -38,7 +38,7 @@
<div class="layui-inline layui-form" style="padding-bottom: 5px;">
<button class="layui-btn layui-btn-normal " id="search" style="height: 36px;">搜索</button>
</div>
<table id="table" class="layui-table" lay-filter="table" style="margin: -3px 0;"></table>
<table id="table" class="layui-table" lay-filter="table" style="margin: 1px 0;"></table>
</div>
</div>
<!-- 操作列 -->

View File

@@ -0,0 +1,83 @@
<?php $title='文章模板';$awesome=true; require 'header.php';
$site = unserialize(get_db('user_config','v',['uid'=>UID,'k'=>'s_site']));
?>
<style type="text/css">
.screenshot{
width: 99%;
height: 99%;
max-width: 100%;
max-height: 100%;
aspect-ratio:16/9;
}
#default #del {display: none;}
</style>
<body>
<div class="layuimini-container">
<div class="layuimini-main">
<blockquote class="layui-elem-quote layuimini-form" style="margin-top: 0px;border-left: 5px solid <?php echo $cache?"#1e9fff":($global_config['offline']?"":"#639d11") ?>;padding: 6px;">
<span class="layui-breadcrumb" lay-separator="|">
<a href="./index.php?c=admin&page=theme_article&cache=no&u=<?php echo U;?>">刷新数据</a>
</span>
</blockquote>
<div class="layui-bg-gray" style="padding: 1px;" >
<div class="layui-row layui-col-space15">
<?php
$Space = ' ';//占位符,强迫症想让输出的源码好看点而已...
foreach ($themes as $key => $theme) {
$online = !empty($theme['info']['md5']); //在线主题!
if($s_templates['article'] == $key){
$icon ='<i class="fa fa-magic" style="color: #03a9f4;" title = "正在使用"></i> ';
}else{
$icon ='';
}
$color = ($s_templates['article'] == $key ?"color: #03a9f4;":"");
?>
<!--主题卡片-->
<div class="layui-col-xs layui-col-sm4 layui-col-md3 ">
<div class="layui-card">
<div class="layui-card-header">
<div style="float:left; cursor:pointer;<?php echo $color; ?>" title="<?php echo $key; ?>"><?php echo $icon.$theme['info']['name']; ?></div>
<div style="float:right;cursor:pointer;" title="<?php echo $theme['info']['update']; ?>"><?php echo $theme['info']['version']; ?></div>
</div>
<div class="layui-card-body">
<div class="img-list"><img class="screenshot" layer-src="<?php echo $theme['info']['screenshot']; ?>" data-original="<?php echo $theme['info']['screenshot']; ?>"></div>
</div>
<div class="layui-card-header" style="height: 1px;"></div>
<div class="layui-card-header" style="height: auto;" id="article">
<div class="layui-btn-group" id="<?php echo $key;?>">
<?php
if($online){ //如果是在线主题则显示下载
echo $Space.'<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" id="dw">下载</button>'."\n";
}elseif($theme['info']['up'] == 1){ //如果有更新则同时显示下载和使用
echo $Space.'<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" id="up">更新</button>'."\n";
echo $Space.'<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" id="set">使用</button>'."\n";
}else{ //其他情况仅显示使用
echo $Space.'<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" id="set">使用</button>'."\n";
}
echo $Space.'<button type="button" class="layui-btn layui-btn-sm layui-btn-normal" id="detail">详情</button>'."\n";
if($theme['info']['config'] == '1'){ //支持配置的主题显示配置
echo $Space.'<button type="button" class="layui-btn layui-btn-sm layui-btn-normal" id="config">配置</button>'."\n";
}
if($USER_DB['UserGroup'] === 'root' && !$online){ //管理员&本地主题>显示删除
echo $Space.'<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" id="del">删除</button>'."\n";
}
?>
</div>
</div>
</div>
</div>
<!--主题卡片End-->
<?php }?>
</div>
</div>
</div>
</div>
<script src = "<?php echo $libs;?>/jquery/jquery-3.6.0.min.js"></script>
<script src = "<?php echo $libs;?>/jquery/jquery.lazyload.min.js"></script>
<script src = "./templates/admin/js/public.js?v=<?php echo $Ver;?>"></script>
<?php load_static('js');?>
<script>var datas = <?php echo json_encode($themes)?>;</script>
<script src = "./templates/admin/js/theme.js?v=<?php echo $Ver;?>"></script>
</body>
</html>

View File

@@ -17,6 +17,7 @@
<a href="./index.php?c=admin&page=theme_home&cache=no&u=<?php echo U;?>" >刷新数据</a>
<a href="javascript:;" layuimini-content-href="theme_login" data-title="登录模板">登录模板</a>
<a href="javascript:;" layuimini-content-href="theme_transit" data-title="过渡模板">过渡模板</a>
<a href="javascript:;" layuimini-content-href="theme_article" data-title="文章模板">文章模板</a>
<?php if($USER_DB['UserGroup'] === 'root'){echo '<a href="javascript:;" layuimini-content-href="theme_register" data-title="注册模板">注册模板</a>';} ?>
<?php if($USER_DB['UserGroup'] === 'root'){echo '<a href="javascript:;" layuimini-content-href="theme_guide" data-title="引导页模板">引导页模板</a>';} ?>
</span>

View File

@@ -1,4 +1,7 @@
<?php $title='过渡模板';$awesome=true; require 'header.php'; ?>
<?php $title='过渡模板';$awesome=true; require 'header.php';
$site = unserialize(get_db('user_config','v',['uid'=>UID,'k'=>'s_site']));
$tip = $site['link_model'] == 'Transition';
?>
<style type="text/css">
.screenshot{
width: 99%;
@@ -15,7 +18,8 @@
<blockquote class="layui-elem-quote layuimini-form" style="margin-top: 0px;border-left: 5px solid <?php echo $cache?"#1e9fff":($global_config['offline']?"":"#639d11") ?>;padding: 6px;">
<span class="layui-breadcrumb" lay-separator="|">
<a href="./index.php?c=admin&page=theme_transit&cache=no&u=<?php echo U;?>">刷新数据</a>
<a href="javascript:;" layuimini-content-href="set_transit" data-title="设置过渡页面">设置</a>
<a href="javascript:;" layuimini-content-href="set_transit" data-title="设置过渡页面">设置</a><?php if(!$tip){echo '
<a href="javascript:;" layuimini-content-href="SiteSetting" data-title="站点设置">注:请将站点设置>链接模式>改为过渡页面</a>';}?>
</span>
</blockquote>
<div class="layui-bg-gray" style="padding: 1px;" >

View File

@@ -2,6 +2,171 @@
<body>
<div class="layuimini-container">
<div class="layuimini-main" style=" margin-left: 20px;">
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.34-20230809</h4>
<ul>
<li>[新增] 安全设置新增OTP双重验证</li>
<li>[模板] 所有登录模板:已开启双重验证时,支持输入OTP验证码,版本:2.0.4 </li>
<li>[警告] 如果您正在使用非默认登录模板,请立即更新登录模板,以免因模板不支持输入OTP验证码造成无法登录</li>
<li>[新增] 导出导入>清空数据>支持清空文章和上传目录(upload)</li>
<li>[新增] 导出导入>本地备份>支持备份和回滚文章列表</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.33-20230802</h4>
<ul>
<li>[新增] 文章编辑新增封面上传功能,插入网络视频功能</li>
<li>[新增] 文章列表以链接的方式展现到主页( 已登录时显示公开和私有,未登录时显示公开文章 )</li>
<li>[新增] 文章列表新增批量操作,支持批量删除,批量修改文章分类和状态</li>
<li>[修复] 文章相关功能的已知问题</li>
<li>[变更] 移除文章功能的独立分类机制,改为使用链接分类 ( 已存在的文章需手动更新分类 )</li>
<li>[变更] API接口鉴权逻辑调整,新增几个兼容API,移除API模式中的兼容+开放模式</li>
<li>[变更] 默认主页模板右键对查看全部或文章链接操作给出提示,给文章链接添加黑色角标</li>
<li>[新增] 主页模板:简约主题 ( 需将安全设置>API模式>改为兼容模式才能使用全部功能 ) 作者:涂山</li>
<li>[新增] 主页模板:花森主页( 自带文章浏览功能 ),作者:花森JioJio</li>
<li>[新增] 文章模板:挽风导航,作者:凌云</li>
<li>[优化] 默认文章模板样式</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.32-20230727</h4>
<ul>
<li>[新增] 扩展功能新增简易文章管理 [ 半成品,尚未完善 ]</li>
<li>[新增] 链接自定义字段类型新增up_img,该类型支持上传1M大小的图片,权限与上传图标共享</li>
<li>[新增] 链接自定义字段新增提示内容</li>
<li>[变更] 主页模板前置处理,若模板支持链接扩展时提供扩展信息</li>
<li>[跟进] 支持onenav新版浏览器插件的兼容</li>
<li>[修复] ip统计存在异常的问题</li>
<li>[修复] 上传链接图标后端接口未限制大小</li>
<li>[修复] 在使用CDN的情况下可能出现授权验证问题</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.31-20230720</h4>
<ul>
<li>[新增] 支持统计访问IP数,可在后台概要页报表统计展示</li>
<li>[新增] 数据库链接表新增关键字列</li>
<li>[新增] 添加/编辑链接页面新增关键字输入,用于过渡页SEO优化 (注:230715之前的过度模板固定用链接标题作为关键字)</li>
<li>[新增] 过度模板设置新增默认关键字选项 (针对未填写关键字时选择其他值作为关键字,需更新过度页模板)</li>
<li>[新增] 链接列表新增识别按钮,用于批量获取URL的标题/描述/关键字/图标</li>
<li>[新增] 系统设置中新增链接关键字长度限制</li>
<li>[新增] 链接列表排序模式支持记忆到客户端</li>
<li>[新增] 已开启链接扩展字段时,添加链接时支持填写扩展字段 (原仅编辑支持)</li>
<li>[修复] 编辑链接时重置按钮未对扩展内容重置</li>
<li>[优化] 添加/编辑链接页面识别功能支持关键字识别</li>
<li>[优化] 链接列表的图标拉取与自动识别功能合并</li>
<li>[优化] 链接识别的成功率</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.30-20230713</h4>
<ul>
<li>[修复] 登录接口的一个错误</li>
<li>[优化] 图标配置页面新增清除缓存按钮,优化图标拉取功能的成功率</li>
<li>[优化] 主题设置>过渡模板,当站点设置中链接模式不为过度页面时显示提示信息</li>
<li>[新增] 后台概要页的报表统计支持选择最近7/14/30天的统计数据 (终端记忆)</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.29-20230705</h4>
<ul>
<li>[升级] Layui v2.8.3 升级到 v2.8.10</li>
<li>[修复] 全新安装v2.0.22 - v2.0.28,未创建图标缓存表导致图标拉取失败的bug <a href="https://gitee.com/tznb/TwoNav/releases/tag/v2.0.22-20230523" target="_blank">手动修复说明</a></li>
<li>[变更] 默认设置和站长工具中邮件配置/图标配置,移入系统设置中</li>
<li>[新增] Token页面新增使用说明</li>
<li>[安全] 优化安全性,站长工具>phpinfo使用时需输入密码核验,并移除Cookie相关信息!</li>
<li>[安全] 系统设置中长度限制改为相关限制,并加入默认用户组禁止使用自定义代码的开关!默认为禁止!并在开启时提示站长存在安全隐患!</li>
<li>[安全] 系统安装后默认禁止注册,如需开启请在系统设置>注册配置>设为开放注册</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.28-20230624</h4>
<ul>
<li>[优化] 收录管理允许用户自行设置必填项</li>
<li>[优化] 可添加的链接类型新增wsa和vmrc</li>
<li>[优化] 站点设置中热门网址和最新网址由下拉选项改为直接输入,范围:0-100</li>
<li>[修复] 分类列表无法查看加密分类的bug</li>
<li>[模板] 主页模板 WebStack-Hugo, 修复开启拖拽排序造成悬停提示失效的bug,禁止拖拽查看全部</li>
<li>[模板] 过度模板可能无法设置的bug</li>
<li>[新增] 链接列表添加图标显示 (仅显示自定义图标,未定义时显示ie图标)</li>
<li>[优化] 链接列表排序由前端当前页排序改为后端全局排序</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.27-20230618</h4>
<ul>
<li>[优化] 增加在线数据冗余线路,以适应更多环境</li>
<li>[优化] 安装时检测是否存在不属于本程序的伪静态规则,存在时提醒用户处理</li>
<li>[优化] 安装成功提示内容添加安全配置说明</li>
<li>[新增] 站长工具新增连通测试,用于检测是否能与资源服务器连通! </li>
<li>[模板] 主页模板 WebStack-Hugo, 新增拖拽排序支持(默认关闭),修复使用分类个性图标时无法定位分类,优化iframe的自适应</li>
<li>[模板] 非默认登录模板无法登录的bug</li>
<li>[修复] 过度页停留时间设置无效的bug</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.26-20230611</h4>
<ul>
<li>[新增] 后台页面右上角新增主页图标用于返回主页</li>
<li>[修复] 后台左侧栏收起时无法使用二级菜单</li>
<li>[修复] 申请收录无法提交,v2.0.24更新造成</li>
<li>[修复] 二级密码输错时提示正确密码的bug</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.25-20230607</h4>
<ul>
<li>[修复] 默认设置>登录保持设为浏览器关闭时无法保存</li>
<li>[修复] 导入OneNav Extend 升级数据时,如果description存在Null值造成导入失败</li>
<li>[新增] 默认设置>可定义登录后进入后台还是主页 (注:此页面配置仅对新注册账号有效,不会修改现有用户的配置)</li>
<li>[优化] 前端主题WebStack-Hugo的适配性</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">
<h4 class="layui-timeline-title">v2.0.24-20230606</h4>
<ul>
<li>[修复] 调整数据库字段长度限制,使其能够正确记录IPV6地址/较长的浏览器UA ( 同时解决MySQL严格模式报错 )</li>
<li>[修复] 放宽登录时UA长度限制,使其能够在腾讯系列APP(微信/QQ/QQ浏览器等)的内置浏览器登录程序</li>
<li>[修复] 安全设置>登录保持设为浏览器关闭时无法保存</li>
<li>[优化] 站长工具>生成伪静态,优化配置规则提高站点安全性 ( 需站长手动将新规则写入指定位置,仅针对Nginx环境 )</li>
<li>[优化] 下载主题前检测目录是否可写,不可写时提醒用户</li>
<li>[优化] 管理员登录后台时始终显示更新系统入口 ( 避免用户不知道在哪里更新系统 )</li>
<li>[优化] 登录设备页面支持显示当前设备(字体为红色)</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis"></i>
<div class="layui-timeline-content layui-text">

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title><?php echo $theme;?> - 主题配置</title>
<link rel='stylesheet' href='<?php echo $layui['css']; ?>'>
<style>
.layui-form-item {margin-bottom: 10px;}
</style>
</style>
</head>
<body>
<div class="layui-row" style = "margin-top:18px;">
<div class="layui-container">
<div class="layui-col-lg8 layui-col-md-offset2">
<form class="layui-form" lay-filter="form">
<div class="layui-form-item">
<label class="layui-form-label">最大宽度</label>
<div class="layui-input-block">
<input type="text" name="container_width" placeholder="默认为空(全宽),输入格式80%或1280px" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item" style="padding-top: 10px;">
<div class="layui-input-block"><button class="layui-btn" lay-submit lay-filter="save">保存</button></div>
</div>
</form>
</div>
</div>
</div>
<script src="<?php echo $layui['js']; ?>"></script>
<script src="./templates/admin/js/public.js?v=<?php echo $Ver;?>"></script>
<script>
var u = _GET('u');
layui.use(['form'], function(){
var form = layui.form;var $ = layui.$;
form.val('form', <?php echo json_encode($theme_config);?>);
form.on('submit(save)', function(data){
$.post(get_api('write_theme','config') + '&t=' + _GET('theme'),data.field,function(data,status){
if(data.code == 1) {
layer.msg(data.msg, {icon: 1,time: 500,end: function() {if(_GET('source') != 'admin'){parent.location.reload();}}});
}else{
layer.msg(data.msg, {icon: 5});
}
});
return false;
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,68 @@
@charset "utf-8";
html{height: 100%;}
h1,h2,h3,h4,h5{
color: black;
}
li {
line-height: 2.2;
}
/*图片边距*/
img {
margin: 10px;
}
/*代码段样式*/
pre {
background-color: #f2f2f2;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
margin: 10px 0;
font-family: "Courier New", monospace;
font-size: 14px;
line-height: 1.5;
overflow-x: auto;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
}
/*表格样式*/
.mdui-table {
border-collapse: collapse;
width: 100%;
}
.mdui-table th,
.mdui-table td {
border: 1px solid #ccc;
padding: 8px;
text-align: center;
}
.mdui-table th::after,
.mdui-table td::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
right: -1px;
width: 1px;
background-color: #ccc;
}
/*a标签样式*/
.container a {
text-decoration: none;
color: inherit;
font-family: Arial, sans-serif;
color: #007bff;
padding: 5px 10px;
border-radius: 5px;
background-color: #f2f2f2;
}
.container a:hover {
color: #ff4500;
}
/*文章标题样式*/
.mdui-typo-title, .mdui-typo-title-opacity {
font-size: 1em;
}
/*prism 代码上色*/
code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="renderer" content="webkit"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=5.0">
<meta http-equiv="Cache-Control" content="no-siteapp"/>
<title><?php echo $data['title'];?> - <?php echo $site['title']; ?></title>
<meta name="keywords" content="<?php echo $data['summary']; ?>">
<meta name="description" content="<?php echo $data['summary']; ?>">
<link rel="shortcut icon" href="<?php echo $favicon;?>">
<link rel="stylesheet" href="<?php echo $libs?>/MDUI/v1.0.1/css/mdui.min.css">
<link rel="stylesheet" href="<?php echo $theme_dir?>/index.css">
</head>
<body class="mdui-drawer-body-left mdui-appbar-with-toolbar mdui-theme-primary-indigo mdui-theme-accent-pink mdui-theme-layout-auto">
<header class="appbar mdui-appbar mdui-appbar-fixed">
<div class="mdui-toolbar mdui-color-theme">
<span class="mdui-btn mdui-btn-icon mdui-ripple mdui-ripple-white" mdui-drawer="{target: '#main-drawer', swipe: true}">
<i class="mdui-icon material-icons">menu</i>
</span>
<a href="" class="mdui-typo-headline mdui-hidden-xs"><?php echo $site['logo'];?></a>
<a href="" class="mdui-typo-title"><?php echo $data['title'];?></a>
<div class="mdui-toolbar-spacer"></div>
</div>
</header>
<div class="mdui-drawer" id="main-drawer">
<div class="mdui-collapse-item-header mdui-list-item mdui-ripple" data_id="to_top">
<i class="mdui-list-item-icon mdui-icon material-icons mdui-text-color-blue">&#xe25a;</i>
<div class="mdui-list-item-content">文章开始</div>
</div>
<div class="mdui-collapse-item-header mdui-list-item mdui-ripple" data_id="to_bottom" id="to_bottom">
<i class="mdui-list-item-icon mdui-icon material-icons mdui-text-color-blue">&#xe258;</i>
<div class="mdui-list-item-content">文章结尾</div>
</div>
</div>
<div class="container p-download mdui-container" style="max-width: <?php echo $theme_config['container_width'];?> ;">
<?php echo $data['content'];?>
</div>
<script src="<?php echo $libs?>/MDUI/v1.0.1/js/mdui.min.js"></script>
<script src="<?php echo $libs?>/jquery/jquery-3.6.0.min.js"></script>
<script src="<?php echo $theme_dir?>/index.js"></script>
</body>
</html>

View File

@@ -0,0 +1,12 @@
{
"name":"默认",
"description":"系统默认的文章模板,支持代码段上色,支持自适应!",
"homepage":"https://gitee.com/tznb/TwoNav",
"version":"2.0.0",
"update":"2023/07/01",
"author":"TwoNav",
"screenshot":"https://img.lm21.top/article_default.jpg",
"config": {
"container_width":""
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -192,10 +192,8 @@ body{
<!--定义一个卡片-->
<div class="mdui-card link-line mdui-hoverable CBC">
<!-- 如果是私有链接,则显示角标 -->
<?php if($link['property'] == 1 ) { ?>
<div class="angle">
<span> </span>
</div>
<?php if($link['property'] == 1 || $link['type'] == 'article') { ?>
<div class="angle" <?php echo $link['type'] == 'article' ? 'style="background: #000000;"':'';?>><span> </span></div>
<?php } ?>
<!-- 角标END -->
<a class="TFC" href="<?php echo $link['url']; ?>" target="_blank" <?php echo $protectA; ?> title = "<?php echo $link['description']; ?>">

View File

@@ -2,8 +2,8 @@
"name":"默认主题(加强)",
"description":"默认主题(加强)",
"homepage":"https://gitee.com/tznb/OneNav",
"version":"2.0.2",
"update":"2023/05/20",
"version":"2.0.3",
"update":"2023/07/31",
"author":"落幕",
"screenshot":"https://s3.bmp.ovh/imgs/2022/04/17/8cac968a8cc8135c.png",
"config": {

View File

@@ -33,17 +33,19 @@ var menu = {
var link_id = $(this).attr('id');
link_id = link_id.replace('id_','');
var tempwindow=window.open('_blank');
tempwindow.location='./index.php?c=click&id='+link_id+"&u="+u;
tempwindow.location = $(this).attr('link-url');
}},
"edit": {name: "编辑", icon: "edit",callback:function(key,opt){
var link_id = $(this).attr('id');
link_id = link_id.replace('id_','');
if(link_id == '0'){mdui.alert('非常规链接,无法操作');return true;}
var tempwindow=window.open('_blank');
tempwindow.location='./index.php?c=admin&page=link_edit&id='+link_id+"&u="+u;
}},
"delete": {name: "删除", icon: "delete",callback:function(){
var link_id = $(this).attr('id');
link_id = link_id.replace('id_','');
if(link_id == '0'){mdui.alert('非常规链接,无法操作');return true;}
mdui.confirm('确认删除?'
,function(){
$.post(get_api('write_link','del'),{lid:link_id},function(data,status){

View File

@@ -1,4 +1,5 @@
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}?>
<?php if(!defined('DIR')){header('HTTP/1.1 404 Not Found');header("status: 404 Not Found");exit;}
$LoginConfig = unserialize($USER_DB['LoginConfig']);?>
<!DOCTYPE html>
<html>
<head>
@@ -29,16 +30,21 @@
<form class="layui-form login-bottom">
<div class="center">
<div class="item">
<span class="icon icon-2"></span>
<span class="icon layui-icon layui-icon-username"></span>
<input type="text" name="User" lay-verify="required" placeholder="请输入账号">
</div>
<div class="item">
<span class="icon icon-3"></span>
<span class="icon layui-icon layui-icon-password"></span>
<input type="password" name="Password" lay-verify="required" placeholder="请输入密码">
<span class="bind-password icon icon-4"></span>
</div>
<?php if(!empty($LoginConfig['totp_key'])){ ?>
<div class="item">
<span class="icon layui-icon layui-icon-vercode"></span>
<input type="text" name="otp_code" lay-verify="required" placeholder="请输入OTP验证码">
</div>
<?php }?>
</div>
<div class="tip">
<?php

View File

@@ -2,7 +2,7 @@
"name": "默认",
"description": "默认",
"homepage": "https://gitee.com/tznb/TwoNav",
"version": "2.0.2",
"update": "2023/04/25",
"version": "2.0.4",
"update": "2023/08/09",
"author": "TwoNav"
}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<title><?php echo $link['title']; ?> - <?php echo $site['title']; ?></title>
<meta name="keywords" content="<?php echo $link['title']; ?>" />
<meta name="keywords" content="<?php echo $link['keywords']; ?>" />
<meta name="description" content="<?php echo $link['description']; ?>" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="<?php echo $libs?>/bootstrap4/css/bootstrap.min.css" type="" media=""/>
@@ -110,7 +110,7 @@ foreach ($link['url_standby'] as $key => $url_standby){
<?php if( empty($link['url_standby']) ) { ?>
<div class="spinner-border"></div> 即将打开,请稍等...
<div class="spinner-border" style="margin-top: 16px;"></div> 即将打开,请稍等...
<?php }else{ ?>
<div class="alert alert-primary" style="margin-top: 16px;">
<strong>存在备用链接,请手动点击您要打开的链接!</strong>

View File

@@ -2,8 +2,8 @@
"name":"OneNav1",
"description":"OneNav旧版过渡页",
"homepage":"https://www.xiaoz.me",
"version":"2.0.1",
"update":"2023/04/05",
"version":"2.0.2",
"update":"2023/07/16",
"author":"xiaoz",
"screenshot":"https://s3.bmp.ovh/imgs/2022/04/17/8cac968a8cc8135c.png",
"config": {