mirror of
https://github.com/tznb1/TwoNav.git
synced 2025-08-10 08:51:49 +00:00
Compare commits
2 Commits
v2.0.32-20
...
v2.0.34-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ece1135ea | ||
|
|
cec87b24f2 |
@@ -26,14 +26,18 @@ TwoNav 是一款开源免费的书签(导航)管理程序,界面简洁,
|
||||
* 支持加密链接
|
||||
* 支持分享链接
|
||||
* 支持二级分类
|
||||
* 支持用户分组
|
||||
* 支持用户分组/权限管理
|
||||
* 支持Chrome/Firefox/Edge书签批量导入
|
||||
* 支持多种主题风格
|
||||
* 支持批量更新链接图标/标题/描述等信息
|
||||
* 支持链接信息自动识别
|
||||
* 支持API
|
||||
* 支持Docker部署
|
||||
* 支持uTools插件
|
||||
* 支持Chromium内核的[浏览器扩展]
|
||||
* 支持简易文章管理
|
||||
* 支持更换各种模板/支持混搭,20+个主题模板
|
||||
* 安全性支持:更换登录入口/二级密码/OTP双重验证
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
161
system/Authenticator.php
Normal file
161
system/Authenticator.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,5 @@
|
||||
<?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_categorys` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`uid` varchar(32) NOT NULL COMMENT '用户id',
|
||||
`name` varchar(64) NOT NULL COMMENT '名称',
|
||||
`weight` int(11) NOT NULL DEFAULT '0' COMMENT '权重',
|
||||
`add_time` int(10) UNSIGNED 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,
|
||||
|
||||
@@ -280,15 +280,6 @@ CREATE TABLE IF NOT EXISTS `global_icon` (
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 用户文章分类
|
||||
CREATE TABLE IF NOT EXISTS `user_article_categorys` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`uid` varchar(32) NOT NULL COMMENT '用户id',
|
||||
`name` varchar(64) NOT NULL COMMENT '名称',
|
||||
`weight` int(11) NOT NULL DEFAULT '0' COMMENT '权重',
|
||||
`add_time` int(10) UNSIGNED NOT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 用户文章列表
|
||||
CREATE TABLE IF NOT EXISTS `user_article_list` (
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
<?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_categorys" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"uid" integer(10) NOT NULL,
|
||||
"name" text NOT NULL DEFAULT "",
|
||||
"weight" integer NOT NULL,
|
||||
"add_time" integer(10) NOT NULL,
|
||||
CONSTRAINT "id" UNIQUE ("id" ASC)
|
||||
);
|
||||
|
||||
CREATE TABLE "user_article_list" (
|
||||
"id" integer PRIMARY KEY AUTOINCREMENT,
|
||||
"uid" integer(10) NOT NULL,
|
||||
|
||||
@@ -245,15 +245,7 @@ CREATE TABLE IF NOT EXISTS "global_icon" (
|
||||
"extend" text NOT NULL DEFAULT "",
|
||||
CONSTRAINT "id" UNIQUE ("id" ASC)
|
||||
);
|
||||
-- 用户文章分类
|
||||
CREATE TABLE IF NOT EXISTS "user_article_categorys" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"uid" integer(10) NOT NULL,
|
||||
"name" text NOT NULL DEFAULT "",
|
||||
"weight" integer NOT NULL,
|
||||
"add_time" integer(10) NOT NULL,
|
||||
CONSTRAINT "id" UNIQUE ("id" ASC)
|
||||
);
|
||||
|
||||
-- 用户文章列表
|
||||
CREATE TABLE "user_article_list" (
|
||||
"id" integer PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
@@ -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)){
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
111
system/api.php
111
system/api.php
@@ -4,52 +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','app_info'])){
|
||||
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)){
|
||||
@@ -63,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);
|
||||
}
|
||||
|
||||
//读分类列表
|
||||
@@ -661,7 +655,7 @@ function write_link(){
|
||||
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'){
|
||||
@@ -953,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']);
|
||||
@@ -1217,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;
|
||||
|
||||
@@ -23,7 +23,7 @@ function uploadImage(){
|
||||
|
||||
//取后缀并判断是否支持
|
||||
$suffix = strtolower(end(explode('.',$_FILES["file"]["name"])));
|
||||
if(!preg_match('/^(jpg|png|gif|bmp|jpeg|svg)$/',$suffix)){
|
||||
if(!preg_match('/^(jpg|png|gif|bmp|jpeg|svg|webp)$/',$suffix)){
|
||||
@unlink($_FILES["file"]["tmp_name"]);
|
||||
msgA(['errno'=>-1,'message'=>'文件格式不被支持']);
|
||||
}
|
||||
@@ -46,7 +46,26 @@ function uploadImage(){
|
||||
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'=>'未开放']);
|
||||
@@ -107,25 +126,26 @@ function article_list(){
|
||||
$limit = empty(intval($_REQUEST['limit'])) ? 50 : intval($_REQUEST['limit']);
|
||||
$offset = ($page - 1) * $limit; //起始行号
|
||||
$where['LIMIT'] = [$offset,$limit];
|
||||
$where['ORDER']['weight'] = 'ASC';
|
||||
|
||||
$datas = select_db('user_article_list',['id','title','category','category_name','state','password','top','add_time','up_time','browse_count','summary'],$where);
|
||||
|
||||
$categorys = select_db('user_article_categorys',['id','name'],['uid'=>UID]);
|
||||
|
||||
foreach (select_db('user_article_categorys',['id','name'],['uid'=>UID]) as $data) {
|
||||
$categorys[$data['id']] = $data['name'];
|
||||
}
|
||||
|
||||
$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']];
|
||||
$data['category_name'] = $categorys[$data['category']] ?? 'Null';
|
||||
}
|
||||
msgA(['code'=>1,'count'=>$count,'data'=>$datas]);
|
||||
}
|
||||
|
||||
//保存文章
|
||||
function save_article(){
|
||||
check_category($_POST['category']);$time = time();
|
||||
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,
|
||||
@@ -139,9 +159,10 @@ function save_article(){
|
||||
'browse_count'=>0,
|
||||
'summary'=>$_POST['summary'],
|
||||
'content'=>$_POST['content'],
|
||||
'cover'=>'',
|
||||
'cover'=>$_POST['cover_url'],
|
||||
'extend'=>''
|
||||
],[1,'保存成功']);
|
||||
],[1,'保存成功']);
|
||||
//存在id,更新文章数据
|
||||
}else{
|
||||
if(!has_db('user_article_list',['uid'=>UID,'id'=>$_POST['id']])){
|
||||
msg(-1,'文章id错误');
|
||||
@@ -153,7 +174,8 @@ function save_article(){
|
||||
'up_time'=>$time,
|
||||
'summary'=>$_POST['summary'],
|
||||
'content'=>$_POST['content'],
|
||||
],['uid'=>UID,'id'=>$_POST['id']],[1,'保存成功']);
|
||||
'cover'=>$_POST['cover_url']
|
||||
],['uid'=>UID,'id'=>$_POST['id']],[1,'保存成功']);
|
||||
}
|
||||
|
||||
|
||||
@@ -161,50 +183,40 @@ function save_article(){
|
||||
//删除文章
|
||||
function del_article(){
|
||||
$id = json_decode($_POST['id']);
|
||||
delete_db('user_article_list',['uid'=>UID,'id'=>$id],[1,'删除成功']);
|
||||
if(empty($id)) msg(-1,'参数错误');
|
||||
delete_db('user_article_list',['uid'=>UID,'id'=>$id],[1,'操作成功']);
|
||||
}
|
||||
//分类列表
|
||||
function category_list(){
|
||||
$where['uid'] = UID;
|
||||
$where['ORDER']['weight'] = 'ASC';
|
||||
$data = select_db('user_article_categorys',['id','name','weight','add_time'],$where);
|
||||
msgA(['code'=>1,'count'=>count($data),'data'=>$data]);
|
||||
}
|
||||
//添加分类
|
||||
function add_category(){
|
||||
$name = trim($_POST['name']);
|
||||
$time = time();
|
||||
if(empty($name)){
|
||||
msg(-1,'分类名称不能为空');
|
||||
}
|
||||
if(has_db('user_article_categorys',['uid'=>UID,'name'=>$name])){
|
||||
msg(-1,'分类名称已存在');
|
||||
}
|
||||
insert_db('user_article_categorys',[
|
||||
'uid'=>UID,
|
||||
'name'=>$name,
|
||||
'weight'=>0,
|
||||
'add_time'=>$time
|
||||
],[1,'添加成功']);
|
||||
msg(-1,'添加失败');
|
||||
}
|
||||
//删除分类
|
||||
function del_category(){
|
||||
check_category($_POST['id']);
|
||||
delete_db('user_article_categorys',['uid'=>UID,'id'=>$_POST['id']],[1,'删除成功']);
|
||||
}
|
||||
//保存分类
|
||||
function save_category(){
|
||||
check_category($_POST['id']);
|
||||
update_db('user_article_categorys',['name'=>$_POST['name'],'weight'=>$_POST['weight']],['uid'=>UID,'id'=>$_POST['id']],[1,'更新成功']);
|
||||
}
|
||||
//检查分类
|
||||
function check_category($id){
|
||||
if(empty($id)){
|
||||
msg(-1,'分类ID不能为空');
|
||||
}
|
||||
if(!has_db('user_article_categorys',['uid'=>UID,'id'=>$id])){
|
||||
//修改分类
|
||||
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,'保存成功']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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','app_info','del_link','global_search']) && function_exists($method) ) {
|
||||
if (function_exists($method)) {
|
||||
$method();
|
||||
}else{
|
||||
Amsg(-1,'方法未找到 >> '.$method);
|
||||
@@ -42,16 +42,55 @@ 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(-1,'id不能为空');
|
||||
msg(-1010,'链接ID不能为空');
|
||||
}
|
||||
$where['lid'] = $lid;
|
||||
$where['uid'] = UID;
|
||||
if(!has_db('user_links',$where)){
|
||||
msg(-1,'链接id不存在');
|
||||
msg(-1010,'链接id不存在');
|
||||
}
|
||||
delete_db('user_links',$where,[0,'删除成功']);
|
||||
}
|
||||
@@ -59,32 +98,19 @@ function del_link(){
|
||||
//搜索链接
|
||||
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'];
|
||||
$data = select_db('user_links',$field,$where);
|
||||
|
||||
// 查询出分类名称
|
||||
$categorys = select_db('user_categorys',['cid(id)','name'],['uid'=>UID,'status'=>1]);
|
||||
// 遍历分类,以id作为键名
|
||||
foreach ($categorys as $category) {
|
||||
$newCategorys[$category['id']] = $category['name'];
|
||||
}
|
||||
// 遍历查询的数据,然后添加父级分类名称
|
||||
foreach ($data as $key => $value) {
|
||||
$data[$key]['category_name'] = $newCategorys[$value['fid']];
|
||||
}
|
||||
|
||||
msgA(['code'=>0,'msg'=>'获取成功','count'=>count($data),'data'=>$data]);
|
||||
$datas = select_db('user_links',$field,$where);
|
||||
links_add_category_field($datas); //添加分类信息
|
||||
msgA(['code'=>0,'msg'=>'获取成功','count'=>count($datas),'data'=>$datas]);
|
||||
}
|
||||
//查询链接列表
|
||||
function link_list(){
|
||||
@@ -92,11 +118,7 @@ function link_list(){
|
||||
$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';
|
||||
@@ -105,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]);
|
||||
}
|
||||
//查询单个链接
|
||||
@@ -119,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]);
|
||||
}
|
||||
}
|
||||
//查询指定分类的链接
|
||||
@@ -135,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); //统计条数
|
||||
//权重排序(数字小的排前面)
|
||||
@@ -147,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']));
|
||||
@@ -170,14 +289,11 @@ 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;
|
||||
@@ -187,11 +303,19 @@ function app_info(){
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<?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();
|
||||
@@ -7,7 +11,9 @@ if(empty($id)){Not_Found();}
|
||||
|
||||
//查询文章
|
||||
$where['uid'] = UID;
|
||||
$where['state'] = 1; //1表示公开
|
||||
if(!is_login()){
|
||||
$where['state'] = 1; //状态筛选
|
||||
}
|
||||
$where['id'] = $id;
|
||||
$data = get_db('user_article_list','*',$where);
|
||||
|
||||
@@ -33,6 +39,7 @@ $path = $dir_path.'/index.php';
|
||||
//检查是否存在,不存在则使用默认
|
||||
if(!is_file($path)){
|
||||
$path= DIR.'/templates/article/default/index.php';
|
||||
$theme_dir = './templates/article/default';
|
||||
}
|
||||
|
||||
//统计点击数
|
||||
@@ -42,7 +49,7 @@ 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($dir_path.'/info.json'),true);
|
||||
$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();
|
||||
|
||||
|
||||
@@ -77,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;
|
||||
@@ -102,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;
|
||||
@@ -197,6 +197,7 @@ 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"])){
|
||||
@@ -207,6 +208,30 @@ function get_links($fid) {
|
||||
}
|
||||
|
||||
}
|
||||
//生成文章链接, 条件:非隐藏,且主题未声明不显示文章
|
||||
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']);
|
||||
|
||||
@@ -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";
|
||||
@@ -41,11 +40,25 @@ if(strlen($Password)!==32){
|
||||
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'){
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
v2.0.32-20230727
|
||||
v2.0.34-20230809
|
||||
@@ -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>
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">API模式的差别</div>
|
||||
<div class="layui-colla-content">
|
||||
<p>安全模式: 仅提供TwoNav自身的API接口,访客(未登录/Token为空)无法调用!</p>
|
||||
<p>兼容模式: 兼容部分OneNav的API接口,以便于其他插件调用!不允许访客调用!</p>
|
||||
<p>兼容模式+开放: 在兼容模式的基础上允许访客调用API获取共有数据!</p>
|
||||
<p>安全模式: 仅提供TwoNav自身的API接口,不兼容Onenav的API接口!</p>
|
||||
<p>兼容模式: 兼容部分OneNav的API接口,以便于其他插件调用!不支持访客调用!</p>
|
||||
<p>如果你未使用相关扩展插件,则无需修改模式并将Token删除,以提高账号的安全性!</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -24,10 +23,12 @@
|
||||
<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可用,其他版本未知 )<br />
|
||||
设置S: 1.TwoNav后台>右上角账号>安全设置>API模式>设为<兼容模式>或<兼容模式+开放> 2.在本页面获取Token<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: 对于单用户使用,确保系统设置中默认用户是当前用户即可!多用户使用时需开启二级域名功能并将域名替换成用户的二级域名,注意结尾不需要带/
|
||||
问题1: 对于单用户使用,确保系统设置中默认用户是当前用户即可!多用户使用时需开启二级域名功能并将域名替换成用户的二级域名,注意结尾不需要带/
|
||||
问题2: 因为插件非官方开发维护,能用就尽量不要更新,如果插件更新可能会导致无法正常使用,需这个更新获得兼容性!
|
||||
问题3: 因为国内环境限制,你可能无法访问谷歌,这种情况你可以在交流群获取插件(安装方法自行百度,部分浏览器可能需要开发者模式加载)
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-colla-item">
|
||||
@@ -35,7 +36,7 @@
|
||||
<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>设置S: 1.TwoNav后台>右上角账号>安全设置>API模式>设为<兼容模式> 2.在本页面获取SecretKey ( 即插件设置中的API KEY )</p>
|
||||
<p>设置C: 打开uTools中的OneNav,点击右下角小齿轮>输入网站地址/用户名/API KEY</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -18,13 +18,6 @@ if($mode == 'edit'){
|
||||
|
||||
$title = $mode == 'add' ? '添加文章' : '编辑文章';
|
||||
|
||||
function echo_article_category(){
|
||||
$where['uid'] = UID;
|
||||
foreach (select_db('user_article_categorys','*',$where) as $category) {
|
||||
echo "<option value=\"{$category['id']}\">{$category['name']}</option>";
|
||||
}
|
||||
}
|
||||
|
||||
require dirname(__DIR__).'/header.php' ?>
|
||||
<link href="<?php echo $libs?>/wangEditor/wangEditor.css" rel="stylesheet">
|
||||
<style type="text/css">
|
||||
@@ -32,6 +25,11 @@ require dirname(__DIR__).'/header.php' ?>
|
||||
#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;}
|
||||
@@ -55,7 +53,7 @@ require dirname(__DIR__).'/header.php' ?>
|
||||
<label class="layui-form-label w40">分类:</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="category" lay-search>
|
||||
<?php echo_article_category(); ?>
|
||||
<?php echo_category(true); ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,7 +73,7 @@ require dirname(__DIR__).'/header.php' ?>
|
||||
<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"><?php echo $data['summary'];?></textarea>
|
||||
<textarea name="summary" rows ="2" placeholder="文章摘要,留空时自动获取" class="layui-textarea" style="min-height: 45px;"><?php echo $data['summary'];?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -88,6 +86,17 @@ require dirname(__DIR__).'/header.php' ?>
|
||||
</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">
|
||||
@@ -180,7 +189,7 @@ const editor = createEditor({
|
||||
mode: 'default'
|
||||
})
|
||||
|
||||
const toolbarConfig = {excludeKeys: ['fullScreen','group-video']}
|
||||
const toolbarConfig = {excludeKeys: ['fullScreen','uploadVideo']}
|
||||
|
||||
const toolbar = createToolbar({
|
||||
editor,
|
||||
@@ -190,8 +199,9 @@ const toolbar = createToolbar({
|
||||
})
|
||||
|
||||
|
||||
layui.use(['form'], function () {
|
||||
var form = layui.form;
|
||||
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'];?>});
|
||||
@@ -211,6 +221,55 @@ layui.use(['form'], function () {
|
||||
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 == ''){
|
||||
|
||||
@@ -3,14 +3,11 @@ 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='文章列表';
|
||||
function echo_article_category(){
|
||||
$where['uid'] = UID;
|
||||
foreach (select_db('user_article_categorys','*',$where) as $category) {
|
||||
echo "<option value=\"{$category['id']}\">{$category['name']}</option>";
|
||||
}
|
||||
}
|
||||
require dirname(__DIR__).'/header.php' ?>
|
||||
<body>
|
||||
<div class="layuimini-container">
|
||||
@@ -22,7 +19,7 @@ require dirname(__DIR__).'/header.php' ?>
|
||||
<select name="category" lay-search>
|
||||
<option value="0" selected="">全部</option>
|
||||
<optgroup label="用户分类">
|
||||
<?php echo_article_category(); ?>
|
||||
<?php echo_category(true); ?>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
@@ -63,48 +60,77 @@ require dirname(__DIR__).'/header.php' ?>
|
||||
<!-- 表头工具栏 -->
|
||||
<script type="text/html" id="toolbar">
|
||||
<div class="layui-btn-group">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-danger layui-hide-xs" lay-event="batch_del">删除选中</button>
|
||||
<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 layui-btn-normal" lay-event="category">分类管理</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="category" style="margin-top: 18px;display:none;padding-right: 10px;padding-left: 10px;">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-submit id="to_article_list">返回</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-submit id="add_category">添加</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-submit id="refresh_category">刷新</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-submit id="category_tip">权重提示</button>
|
||||
</div>
|
||||
<table id="category_list" lay-filter="category_list"></table>
|
||||
</ul>
|
||||
|
||||
<ul class="edit_category" style="margin-top: 18px;display:none;padding-right: 10px;padding-left: 10px;">
|
||||
<form class="layui-form" lay-filter="edit_category_form">
|
||||
<input type="text" name="id" autocomplete="off" class="layui-input" style="display:none;">
|
||||
<!--批量修改分类-->
|
||||
<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" style="width: 40px;">名称</label>
|
||||
<div class="layui-input-block" style="margin-left: 70px">
|
||||
<input type="text" name="name" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" style="width: 40px;">权重</label>
|
||||
<div class="layui-input-block" style="margin-left: 70px">
|
||||
<input type="number" name="weight" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item" style="padding-top: 10px;">
|
||||
<label class="layui-form-label">父级分类</label>
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit id="save_category">保存</button>
|
||||
<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;
|
||||
@@ -117,10 +143,9 @@ layui.use(['form','table','dropdown','miniTab'], function () {
|
||||
var state_data = ["Null","公开", "私有", "草稿", "废弃"];
|
||||
var cols=[ //表头
|
||||
{type:'checkbox'} //开启复选框
|
||||
//,{field: 'id', title: 'ID', width:80, sort: true}
|
||||
,{ title:'操作', toolbar: '#tablebar',width:110}
|
||||
,{field: 'title', title: '标题', minWidth:200,templet: function(d){
|
||||
return '<a style="color:#3c78d8" target="_blank" href="/index.php?c=article&id=' +d.id + '&u=' + u + '">'+d.title+'</a>'
|
||||
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;
|
||||
@@ -153,6 +178,7 @@ layui.use(['form','table','dropdown','miniTab'], function () {
|
||||
,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){
|
||||
@@ -160,7 +186,80 @@ layui.use(['form','table','dropdown','miniTab'], function () {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//批量操作
|
||||
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', {
|
||||
@@ -171,16 +270,10 @@ layui.use(['form','table','dropdown','miniTab'], function () {
|
||||
,page: {curr: 1}
|
||||
});
|
||||
}
|
||||
|
||||
//关键字回车搜索
|
||||
$('#keyword').keydown(function (e){if(e.keyCode === 13){search();}});
|
||||
//搜索按钮点击
|
||||
$('#search').on('click', function(){search();});
|
||||
|
||||
//监听工具栏 - 文章列表
|
||||
//监听工具栏
|
||||
table.on('toolbar(table)', function (obj) {
|
||||
var btn = obj.event;
|
||||
if (btn == 'add_article') {
|
||||
if (btn == 'add_article') { //添加文章
|
||||
layer.open({
|
||||
title: false,
|
||||
type: 2,
|
||||
@@ -190,23 +283,21 @@ layui.use(['form','table','dropdown','miniTab'], function () {
|
||||
shadeClose: true,
|
||||
closeBtn:0,
|
||||
area: ['100%', '100%'],
|
||||
content: './index.php?c=admin&page=expand/article-edit&u=' + u,
|
||||
content: './?c=admin&page=expand/article-edit&u=' + u,
|
||||
end: function(){
|
||||
search();
|
||||
}
|
||||
});
|
||||
}else if(btn == 'category'){
|
||||
category_index = layer.open({type: 1,scrollbar: false,shadeClose: true,title: false ,area : ['100%', '100%'],closeBtn:0,content: $('.category'),
|
||||
success: function(layero, index, that){
|
||||
category_list();
|
||||
}
|
||||
});
|
||||
}else{
|
||||
}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);
|
||||
@@ -221,12 +312,12 @@ layui.use(['form','table','dropdown','miniTab'], function () {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
//监听行工具 - 文章列表
|
||||
|
||||
|
||||
|
||||
//监听行工具
|
||||
table.on('tool(table)', function (obj) {
|
||||
let btn = obj.event;
|
||||
let data = obj.data;
|
||||
@@ -251,117 +342,46 @@ layui.use(['form','table','dropdown','miniTab'], function () {
|
||||
shadeClose: true,
|
||||
closeBtn:0,
|
||||
area: ['100%', '100%'],
|
||||
content: './index.php?c=admin&page=expand/article-edit&id='+data.id+'&u=' + u,
|
||||
content: './?c=admin&page=expand/article-edit&id='+data.id+'&u=' + u,
|
||||
end: function(){
|
||||
search();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
//监听行工具 - 分类列表
|
||||
table.on('tool(category_list)', 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_category'),{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'){
|
||||
form.val('edit_category_form', data);
|
||||
edit_category_index = layer.open({type: 1,scrollbar: false,shadeClose: true,title: '编辑分类',area : ['auto', 'auto'],content: $('.edit_category')});
|
||||
}
|
||||
});
|
||||
//添加分类
|
||||
$('#add_category').click(function () {
|
||||
add_category_index = layer.prompt({formType: 0,value: '',title: '请输入分类名称:',shadeClose: false,"success":function(){
|
||||
$("input.layui-layer-input").on('keydown',function(e){
|
||||
if(e.which == 13) {add_category();}
|
||||
});
|
||||
}},function(){
|
||||
add_category()
|
||||
});
|
||||
});
|
||||
//返回
|
||||
$('#to_article_list').click(function () {
|
||||
layer.close(category_index);
|
||||
location.reload();
|
||||
});
|
||||
//刷新
|
||||
$('#refresh_category').click(function () {
|
||||
category_list();
|
||||
});
|
||||
//分类提示
|
||||
$('#category_tip').click(function () {
|
||||
layer.alert("权重越小越靠前",{title:'提示',anim: 2,closeBtn: 0});
|
||||
});
|
||||
|
||||
//编辑分类-保存
|
||||
$('#save_category').click(function () {
|
||||
$.post(get_api('write_article','save_category'),form.val('edit_category_form'),function(data,status){
|
||||
$("input.layui-layer-input").val("");
|
||||
//设置相关
|
||||
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) {
|
||||
category_list();
|
||||
layer.close(index);
|
||||
layer.msg('操作成功', {icon: 1});
|
||||
layer.close(edit_category_index);
|
||||
}else{
|
||||
$("input.layui-layer-input").focus();
|
||||
layer.msg(data.msg || '未知错误',{icon: 5});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
//取消按钮
|
||||
$('.cancel').click(function () {
|
||||
layer.close(index);
|
||||
return false;
|
||||
});
|
||||
|
||||
function add_category(){
|
||||
let name = $("input.layui-layer-input").val();
|
||||
if(name == ''){ return false; }
|
||||
$("*").blur();
|
||||
let loading = layer.msg('正在添加文章分类,请稍后..', {icon: 16,time: 1000*300,shadeClose: false});
|
||||
$.post(get_api('write_article','add_category'),{'name':name},function(data,status){
|
||||
layer.close(loading); layer.close(add_category_index);
|
||||
$("input.layui-layer-input").val("");
|
||||
//批量修改分类
|
||||
$('#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) {
|
||||
category_list();
|
||||
search();
|
||||
layer.close(index);
|
||||
layer.msg('操作成功', {icon: 1});
|
||||
}else{
|
||||
$("input.layui-layer-input").focus();
|
||||
layer.msg(data.msg || '未知错误',{icon: 5});
|
||||
}
|
||||
});
|
||||
}
|
||||
function category_list(){
|
||||
table.render({
|
||||
elem: '#category_list'
|
||||
,height: 'full-70'
|
||||
,url: get_api('read_article','category_list')
|
||||
,page: false
|
||||
,limit:999
|
||||
,limits: [999]
|
||||
,even:true
|
||||
,loading:true
|
||||
,id:'category_list'
|
||||
,cols: [[
|
||||
{title:'操作', toolbar: '#tablebar', width:110}
|
||||
,{field: 'name', title: '分类名称', minWidth:200,width:300}
|
||||
,{field: 'weight', title: '权重', minWidth:100,width:160}
|
||||
]]
|
||||
,method: 'post'
|
||||
,response: {statusCode: 1 }
|
||||
,done: function (res, curr, count) {
|
||||
//获取当前每页显示数量.并写入本都储存
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ $title='系统设置';require(dirname(__DIR__).'/header.php');
|
||||
<option value="1" >开启</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">简易文章管理功能 ( 请勿和专业的比,暂无前端模板支持,视情况逐渐优化 )</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>
|
||||
|
||||
@@ -2,13 +2,45 @@
|
||||
<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>[新增] 链接自定义字段类型新增up_img,该类型支持上传1M大小的图片,权限与上传图标共享</li>
|
||||
<li>[新增] 链接自定义字段新增提示内容</li>
|
||||
<li>[变更] 主页模板前置处理,若模板支持链接扩展时提供扩展信息</li>
|
||||
<li>[跟进] 支持onenav新版浏览器插件的兼容</li>
|
||||
|
||||
53
templates/article/default/config.php
Normal file
53
templates/article/default/config.php
Normal 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>
|
||||
@@ -1,145 +1,68 @@
|
||||
@charset "utf-8";
|
||||
html{height: 100%;}
|
||||
body{margin: 0;background: #0D1721;font-family:'pingfang SC','helvetica neue',arial,'hiragino sans gb','microsoft yahei ui','microsoft yahei',simsun,sans-serif;font-size:16px;line-height: 140%;color: #ddd;}
|
||||
ul, ol, li,dt,dd{margin:0; padding:0;list-style: none;}
|
||||
dl{margin-top:0; margin-bottom:0;}
|
||||
p{margin:0 0 10px;}
|
||||
img{border: 0; vertical-align:middle;max-width: 100%;}
|
||||
img.img-responsive{width: 100%;}
|
||||
input{outline: medium none;outline: none;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}
|
||||
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { color: #8A8A8A;}
|
||||
input:focus{border-color:#FF9900; -webkit-box-shadow:inset 0 1px 1px rgba(255,136,0,.075),0 0 8px rgba(255,136,0,.6);}
|
||||
|
||||
/* container */
|
||||
*{-webkit-box-sizing:border-box; -moz-box-sizing:border-box; box-sizing:border-box}
|
||||
:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}
|
||||
.container {width: 75%;position: relative;padding-right: 15px;padding-left: 15px;margin: 0 auto;}
|
||||
.row{position: relative; margin-right: -15px; margin-left: -15px;}
|
||||
.container:before,.container:after,.row:before,.row:after,.clearfix:before, .clearfix:after{ display: table; content: " "; clear: both;}
|
||||
|
||||
/* text */
|
||||
h1{ font-size:22px; line-height: 28px;}
|
||||
h2{ font-size:20px; line-height: 26px;}
|
||||
h3{ font-size:18px; line-height: 24px;}
|
||||
h4{ font-size:16px; line-height: 22px;}
|
||||
h5{ font-size:14px; line-height: 20px;}
|
||||
h6{ font-size:12px; line-height: 18px;}
|
||||
h1,h2,h3,h4,h5,h6{ font-weight: 400; margin-top:10px; margin-bottom:10px}
|
||||
a,button{ text-decoration:none; outline:none; -webkit-tap-highlight-color:rgba(0,0,0,0)}
|
||||
a,h1, h2, h3, h4, h5, h6{ color: #8A8A8A;}
|
||||
a,h1{color: #ffffff;}
|
||||
a{color: #b1edff;}
|
||||
|
||||
a:focus,a:hover,a:active{text-decoration:none;color: #FF9900;}
|
||||
|
||||
.col-pd{ padding: 15px 20px;}
|
||||
.text-muted{ color: #999;}
|
||||
.split-line{display: inline-block; margin-left: 12px; margin-right: 12px; width: 1px; height: 14px; vertical-align: -2px; background: #636060;}
|
||||
.news-title{
|
||||
border-bottom: 1px solid #4f4f4f !important;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
/* header */
|
||||
.newbui-header__top{position: relative;height: 100px;padding: 0 20px;}
|
||||
.newbui-header__logo{float:left; margin-right: 120px;}
|
||||
.newbui-header__logo{width: auto;margin-top: 20px;}
|
||||
.newbui-header__logo .logo{display:block;width: auto;height:60px;}
|
||||
.newbui-header__search{float: right;position:relative;width:280px;margin-top: 32px;}
|
||||
.newbui-header__search .form-control{display:block;width:100%;height: 35px;padding:6px 45px 6px 10px;font-size:12px;line-height:32px;border-radius: 5px;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;background: #2D2F36;color: #8A8A8A;-webkit-text-fill-color: #8A8A8A;border: 0;}
|
||||
.newbui-header__search .submit{display:block; position:absolute; top: 0; right: 0; width:35px; height:35px; border: 0; cursor: pointer; background: url(img/icon_seacrh.png) center no-repeat;}
|
||||
|
||||
.newbui-header__bar{position:relative;margin: 0;background: #223855;color: #191A20;box-shadow: 0 5px 10px #000;}
|
||||
.newbui-header__menu > li{ position: relative; float:left;}
|
||||
.newbui-header__menu > li > a{display:inline-block; padding: 15px 30px; font-size:15px; color: #fff;}
|
||||
.newbui-header__menu > li > a:hover {background: #191A20;color: #FF9900;}
|
||||
.newbui-header__menu > li.active > a{background: #191A20; color: #fff;}
|
||||
.newbui-header__menu li .dropdown{display: none;width: 100%;position: absolute;z-index: 999;top: 100%;right: 0;text-align: center;font-size: 12px;color: #999;background: #2D2F36;box-shadow: 0 2px 8px rgba(0,0,0,.1);}
|
||||
.newbui-header__menu li .dropdown li{ padding: 8px 0; border-top: 1px solid #2D2F36;}
|
||||
.newbui-header__menu li .dropdown li:first-child{ padding-top: 15px; border-top: 0;}
|
||||
.newbui-header__menu li .dropdown li:last-child{ padding-bottom: 15px;}
|
||||
.newbui-header__menu li a{ font-size: 14px;}
|
||||
.newbui-header__menu li:hover .dropdown{ display: block;}
|
||||
.newbui-header__more{float: left;padding-top: 20px;width: 250px;}
|
||||
.newbui-header__more li{margin-bottom: 5px;color: #ddd;}
|
||||
.newbui-header__more li .text{ display: inline-block; width: 60px; color: #fff;}
|
||||
|
||||
|
||||
/* pannel */
|
||||
.newbui-pannel{position: relative;margin-top: 30px;background: #0D1721;border-radius: 5px;border: 1px solid #2D2F36;}
|
||||
.newbui-pannel-left{ float: left; width: 70%;}
|
||||
.newbui-pannel-side{ float: left; width: 30%;}
|
||||
.newbui-pannel__head{padding: 15px 20px;line-height: 25px;border-bottom: 1px solid #2D2F36;}
|
||||
.newbui-pannel__head .title{ margin: 0;}
|
||||
.newbui-pannel__foot{ padding: 15px 20px;}
|
||||
|
||||
/* more */
|
||||
.margin-0{ margin: 0 !important;}
|
||||
.padding-0{ padding: 0 !important;}
|
||||
.pull-left{ float: left !important;}
|
||||
.pull-right{ float: right !important;}
|
||||
.hide,.visible-lg, .visible-md, .visible-sm, .visible-xs, .visible-mi{ display: none !important;}
|
||||
.newbui-foot{ padding: 20px; text-align: center; color: #999;}
|
||||
|
||||
|
||||
.pc{display: block!important;}
|
||||
.m{display: none!important;}
|
||||
.navbar-light {
|
||||
color: #fff !important;
|
||||
font-size: 16px;
|
||||
/*font-weight: bold;*/
|
||||
text-shadow: 0px 1px 0px #000
|
||||
}
|
||||
|
||||
@media (min-width: 1200px){
|
||||
.visible-lg { display: block !important;}
|
||||
.hidden-lg {display: none !important;}
|
||||
h1,h2,h3,h4,h5{
|
||||
color: black;
|
||||
}
|
||||
|
||||
@media (max-width: 1199px) and (min-width: 992px){
|
||||
.visible-md { display: block !important;}
|
||||
.hidden-md {display: none!important;}
|
||||
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;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) and (min-width: 768px){
|
||||
.visible-sm{ display: block !important;}
|
||||
.hidden-sm {display: none !important;}
|
||||
/*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;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px){
|
||||
.container{ width: 100%; padding-right: 30px; padding-left: 30px;}
|
||||
/*文章标题样式*/
|
||||
.mdui-typo-title, .mdui-typo-title-opacity {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
@media (max-width:767px){
|
||||
.visible-xs { display: block !important;}
|
||||
.hidden-xs {display: none!important;}
|
||||
.container{ padding: 0;}
|
||||
.row{ margin: 0;}
|
||||
.col-pd{ padding: 10px;}
|
||||
|
||||
/* header */
|
||||
.newbui-header__top{ height: 60px; padding:0 10px;}
|
||||
.newbui-header__logo{margin: 12px 0 0;padding: 0;}
|
||||
.newbui-header__logo .logo{width: 148px; height:35px;}
|
||||
.newbui-header__search{ float: none; width: auto; margin: 0; position: absolute; top: 15px; right: 10px; left: 168px;}
|
||||
.newbui-header__search .form-control{ height: 30px; line-height: 30px; border-radius: 5px; padding:6px 40px 6px 10px; border: 0;}
|
||||
.newbui-header__search .submit{ width: 30px; height: 30px;}
|
||||
.newbui-header__bar{ box-shadow: none;}
|
||||
.newbui-header__menu { position:relative; margin: 0; overflow: auto; white-space:nowrap; overflow-y:hidden;overflow-x:scroll; -webkit-overflow-scrolling:touch}
|
||||
.newbui-header__menu > li{ float: none; display: inline-block;}
|
||||
.newbui-header__menu > li > a{ padding: 10px 15px; font-size: 14px;}
|
||||
.newbui-header__menu li:hover .dropdown{ display: none;}
|
||||
|
||||
/* pannel */
|
||||
.newbui-pannel{ margin-top: 10px; border: 0; border-radius: 0; box-shadow: none;}
|
||||
.newbui-pannel__head{ padding: 10px; line-height: 25px; border-bottom: 1px solid #2D2F36;}
|
||||
.newbui-pannel__head .title{ font-size: 16px;}
|
||||
.newbui-pannel__foot{ padding: 10px;}
|
||||
|
||||
.pc{
|
||||
display: none!important;
|
||||
}
|
||||
.m{
|
||||
display: block!important;
|
||||
}
|
||||
|
||||
}
|
||||
/*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}
|
||||
46
templates/article/default/index.js
Normal file
46
templates/article/default/index.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,46 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title><?php echo $data['title'];?></title>
|
||||
<meta name="keywords" content="<?php echo $data['summary']; ?>">
|
||||
<meta name="description" content="<?php echo $data['summary']; ?>">
|
||||
<link rel="stylesheet" href="<?php echo $theme_dir?>/index.css?v=<?php echo $theme_ver; ?>" type="text/css" media="all" />
|
||||
<link rel="shortcut icon" href="<?php echo $favicon;?>">
|
||||
</head>
|
||||
<body>
|
||||
<div class="newbui-header__bar clearfix">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<ul class="newbui-header__menu clearfix">
|
||||
<li class="pc"><a class="navbar-light" href="./index.php?u=<?php echo $GLOBALS['u'];?>">首页</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="newbui-pannel clearfix">
|
||||
<div class="newbui-pannel-box clearfix">
|
||||
<div class="newbui-pannel_bd col-pd clearfix">
|
||||
<h1 class="news-title"><?php echo $data['title'];?></h1>
|
||||
<div class="news-content">
|
||||
<?php echo $data['content'];?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="newbui-foot clearfix">
|
||||
<p class="text-muted">
|
||||
<?php echo $copyright.PHP_EOL;?>
|
||||
<?php echo $ICP.PHP_EOL;?>
|
||||
</p>
|
||||
<?php echo $site['custom_footer'].PHP_EOL;?>
|
||||
<?php echo $global_config['global_footer'].PHP_EOL;?>
|
||||
</div>
|
||||
</body>
|
||||
<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"></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"></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>
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"name":"默认",
|
||||
"description":"待优化",
|
||||
"description":"系统默认的文章模板,支持代码段上色,支持自适应!",
|
||||
"homepage":"https://gitee.com/tznb/TwoNav",
|
||||
"version":"2.0.0",
|
||||
"update":"2023/07/27",
|
||||
"update":"2023/07/01",
|
||||
"author":"TwoNav",
|
||||
"screenshot":"https://s3.bmp.ovh/imgs/2022/04/17/8cac968a8cc8135c.png"
|
||||
"screenshot":"https://img.lm21.top/article_default.jpg",
|
||||
"config": {
|
||||
"container_width":""
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 86 KiB |
@@ -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']; ?>">
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user