diff --git a/class/Api.php b/class/Api.php
index bb41ec6..876dac5 100755
--- a/class/Api.php
+++ b/class/Api.php
@@ -59,18 +59,35 @@ class Api {
if( empty($id) ){
$this->err_msg(-1003,'The category ID cannot be empty!');
}
+ //根据fid查询这个分类是否存在
+ $count = $this->db->count("on_categorys", [
+ "id" => $fid
+ ]);
+
+ //如果fid不是0,且查询结果小于1,则认为这个父级ID是不存在的,则不允许修改
+ if( !empty($fid) && ($count < 1) ) {
+ $this->err_msg(-2000,'父级ID不存在!');
+ }
+
+ //查询fid是否是二级分类的ID,如果是,则不允许修改
+ $category = $this->db->get("on_categorys","*",[
+ "id" => $fid
+ ]);
+ //如果查询到他的父ID不是0,则是一个二级分类
+ if( intval($category['fid']) !== 0 ) {
+ $this->err_msg(-2000,'父分类不能是二级分类!');
+ }
//如果分类名为空
elseif( empty($name ) ){
$this->err_msg(-1004,'The category name cannot be empty!');
}
-
//更新数据库
else{
//根据分类ID查询改分类下面是否已经存在子分类,如果存在子分类了则不允许设置为子分类,实用情况:一级分类下存在二级分类,无法再将改一级分类修改为二级分类
$count = $this->db->count("on_categorys", [
"fid" => $id
]);
- //改分类下的子分类数量大于0,并且将父级ID修改为其它分类
+ //该分类下的子分类数量大于0,并且父级ID修改为其它分类
if( ( $count > 0 ) && ( $fid !== 0 ) ) {
$this->err_msg(-2000,'修改失败,该分类下已存在子分类!');
}
@@ -245,6 +262,26 @@ class Api {
}
}
}
+ /**
+ * 批量修改链接属性为公有或私有
+ */
+ public function set_link_attribute($data) {
+ $this->auth($token);
+ //获取链接ID,是一个数组
+ $ids = implode(',',$data['ids']);
+ $property = intval($data['property']);
+ //拼接SQL文件
+ $sql = "UPDATE on_links SET property = $property WHERE id IN ($ids)";
+ $re = $this->db->query($sql);
+ //返回影响行数
+ $row = $re->rowCount();
+ if ( $row > 0 ){
+ $this->return_json(200,"success");
+ }
+ else{
+ $this->return_json(-2000,"failed");
+ }
+ }
/**
* 批量导入链接
@@ -323,6 +360,129 @@ class Api {
];
exit(json_encode($data));
}
+ /**
+ * 批量导入链接并自动创建分类,这是新的导入接口
+ */
+ public function import_link($filename,$property = 0) {
+ //过滤$filename
+ $filename = str_replace('../','',$filename);
+ $filename = str_replace('./','',$filename);
+ $this->auth($token);
+ //检查文件是否存在
+ if ( !file_exists($filename) ) {
+ $this->err_msg(-1016,'File does not exist!');
+ }
+ //解析HTML数据
+ $content = file_get_contents($filename);
+ $HTMLs = explode("\n",$content);//分割文本
+ $data = []; //链接组
+ $categorys = []; //分类信息组
+ $categoryt = []; //分类信息表
+
+ // 遍历HTML
+ foreach( $HTMLs as $HTMLh ){
+ //匹配分类名称
+ if( preg_match("/
(.*)<\/H3>/i",$HTMLh,$category) ){
+ //匹配到文件夹名时加入数组
+ array_push($categoryt,$category[1]);
+ array_push($categorys,$category[1]);
+ }elseif( preg_match('/<\/DL>/i',$HTMLh) ){
+ //匹配到文件夹结束标记时删除一个
+ array_pop($categorys);
+ }elseif( preg_match('/
(.+)<\/A>/i',$HTMLh,$urls) ){
+ $datat['category'] = $categorys[count($categorys) -1];
+ $datat['title'] = $urls[2];
+ $datat['url'] = $urls[1];
+ array_push($data,$datat);
+ }
+ }
+ $categoryt = array_unique($categoryt);
+ //追加一个默认分类,用来存储部分链接找不到分类的情况
+ array_push($categoryt,"默认分类");
+
+
+ //批量创建分类
+ $this->batch_create_category($categoryt);
+ //查询所有分类
+ $categorys = $this->db->select("on_categorys",[
+ "name",
+ "id",
+ "fid"
+ ]);
+ // var_dump($categorys);
+ // exit;
+ //链接计数
+ $i = 0;
+ //统计链接总数
+ $count = count($data);
+ //批量导入链接
+ foreach ($data as $key => $value) {
+ $category_name = trim($value['category']);
+ //如果链接的分类是空的,则设置为默认分类
+ $category_name = empty( $category_name ) ? "默认分类" : $category_name;
+
+ foreach ($categorys as $category) {
+ if( trim( $category['name'] ) == $category_name ) {
+ $fid = intval($category['id']);
+ break;
+ }
+ }
+
+ //合并数据
+ $link_data = [
+ 'fid' => $fid,
+ 'title' => htmlspecialchars($value['title']),
+ 'url' => htmlspecialchars($value['url'],ENT_QUOTES),
+ 'add_time' => time(),
+ 'weight' => 0,
+ 'property' => $property
+ ];
+
+ //插入数据库
+ $re = $this->db->insert('on_links',$link_data);
+ //返回影响行数
+ $row = $re->rowCount();
+ if ($row) {
+ $i++;
+ }
+ }
+ //删除书签文件
+ unlink($filename);
+ $this->return_json(200,"success",[
+ "count" => $count,
+ "success" => $i,
+ "failed" => $count - $i
+ ]);
+
+ }
+ /**
+ * 批量创建分类
+ * 接收一个一维数组
+ */
+ protected function batch_create_category($category_name) {
+ $i = 0;
+ foreach ($category_name as $key => $value) {
+ $value = empty($value) ? "默认分类" : $value;
+ $data = [
+ 'name' => trim($value),
+ 'add_time' => time(),
+ 'weight' => 0,
+ 'property' => 1,
+ 'description' => "书签导入时自动创建",
+ 'fid' => 0
+ ];
+ try {
+ //插入分类目录
+ $this->db->insert("on_categorys",$data);
+ $i++;
+ } catch (\Throwable $th) {
+ continue;
+ }
+
+ }
+ return $i;
+ }
+
/**
* 书签上传
* type:上传类型,默认为上传书签,后续类型保留使用
@@ -357,6 +517,40 @@ class Api {
}
}
}
+ /**
+ * 导出HTML链接进行备份
+ */
+ public function export_link(){
+ //鉴权
+ $this->auth($token);
+ //查询所有分类
+ $categorys = $this->db->select("on_categorys","*");
+
+ //定义一个空数组用来存储查询后的数据
+ $data = [];
+
+ //遍历分类
+ foreach ($categorys as $key => $category) {
+ //查询该分类下的所有链接
+ $links = $this->db->select("on_links","*",[
+ "fid" => $category['id']
+ ]);
+ // echo $category['name'];
+ // var_dump($links);
+ // exit;
+ //组合为一个一维数组
+
+ $arr[$category['name']] = $links;
+ // var_dump();
+ // exit;
+ $data[$category['name']] = $arr[$category['name']];
+
+ //清除临时数据
+ unset($arr);
+ }
+ //返回数据
+ return $data;
+ }
/**
* name:修改链接
*/
@@ -690,7 +884,7 @@ class Api {
* 验证是否登录
*/
protected function is_login(){
- $key = md5(USER.PASSWORD.'onenav');
+ $key = md5(USER.PASSWORD.'onenav'.$_SERVER['HTTP_USER_AGENT']);
//获取session
$session = $_COOKIE['key'];
//如果已经成功登录
@@ -1118,6 +1312,13 @@ class Api {
}
}
+ /**
+ * 用户状态
+ */
+ public function check_login(){
+ $status = $this->is_login() ? "true" : "false";
+ $this->return_json(200,$status,"");
+ }
}
diff --git a/controller/admin.php b/controller/admin.php
index ebb0c0b..3957d90 100755
--- a/controller/admin.php
+++ b/controller/admin.php
@@ -2,6 +2,8 @@
/**
* 后台入口文件
*/
+// 载入辅助函数
+require('functions/helper.php');
//检查认证
check_auth($site_setting['user'],$site_setting['password']);
@@ -269,40 +271,12 @@ if ($page == 'ext_js') {
$page = $page.'.php';
-//获取访客IP
-function getIP() {
- if (getenv('HTTP_CLIENT_IP')) {
- $ip = getenv('HTTP_CLIENT_IP');
- }
- elseif (getenv('HTTP_X_FORWARDED_FOR')) {
- $ip = getenv('HTTP_X_FORWARDED_FOR');
- }
- elseif (getenv('HTTP_X_FORWARDED')) {
- $ip = getenv('HTTP_X_FORWARDED');
- }
- elseif (getenv('HTTP_FORWARDED_FOR')) {
- $ip = getenv('HTTP_FORWARDED_FOR');
- }
- elseif (getenv('HTTP_FORWARDED')) {
- $ip = getenv('HTTP_FORWARDED');
- }
- else {
- $ip = $_SERVER['REMOTE_ADDR'];
- }
- return $ip;
- }
-
/**
* 检查授权
*/
function check_auth($user,$password){
- $ip = getIP();
- $key = md5($user.$password.'onenav');
- //获取cookie
- $cookie = $_COOKIE['key'];
- //如果cookie的值和计算的key不一致,则没有权限
- if( $cookie !== $key ){
+ if ( !is_login() ) {
$msg = "";
require('templates/admin/403.php');
exit;
diff --git a/controller/api.php b/controller/api.php
index 14c3459..58f2864 100755
--- a/controller/api.php
+++ b/controller/api.php
@@ -221,6 +221,16 @@ function imp_link($api) {
$property = intval(@$_POST['property']);
$api->imp_link($token,$filename,$fid,$property);
}
+//新版书签批量导入并自动创建分类
+function import_link($api) {
+ //获取token
+ $token = $_POST['token'];
+ //获取书签路径
+ $filename = trim($_POST['filename']);
+ $fid = intval($_POST['fid']);
+ $property = intval(@$_POST['property']);
+ $api->import_link($filename,$property);
+}
//检查弱密码
function check_weak_password($api) {
//获取token
@@ -354,4 +364,47 @@ function save_theme_config($api) {
//获取主题配置信息
function get_theme_config($api) {
$api->get_theme_config();
+}
+
+//批量设置链接私有属性
+function set_link_attribute($api) {
+ $ids = $_POST['ids'];
+ $property = intval( $_POST['property'] );
+ $data = [
+ "ids" => $ids,
+ "property" => $property
+ ];
+ $api->set_link_attribute($data);
+}
+
+//导出链接数据
+function export_link($api) {
+ header('Content-Type: text/html;charset=utf8');
+ $data = $api->export_link();
+ //当前时间
+ $current = time();
+ echo <<< EOF
+
+从OneNav导出的书签
+Bookmarks
+EOF;
+ //遍历结果
+ foreach ($data as $key => $value) {
+ echo "$key
\n";
+ echo "\n";
+ foreach ($value as $link) {
+ $title = $link['title'];
+ $add_time = $link['add_time'];
+ $url = $link['url'];
+ echo "- $title
\n";
+ }
+ echo "
\n";
+ echo "\n";
+
+ }
+}
+
+//获取用户登录状态
+function check_login($api) {
+ $api->check_login();
}
\ No newline at end of file
diff --git a/controller/index.php b/controller/index.php
index 9ac9166..078079b 100755
--- a/controller/index.php
+++ b/controller/index.php
@@ -3,16 +3,30 @@
* 首页模板入口
*/
//如果已经登录,获取所有分类和链接
+// 载入辅助函数
+require('functions/helper.php');
if( is_login() ){
//查询所有分类目录
- $categorys = $db->select('on_categorys','*',[
- "ORDER" => ["weight" => "DESC"]
- ]);
+ $categorys = [];
//查询一级分类目录,分类fid为0的都是一级分类
$category_parent = $db->select('on_categorys','*',[
"fid" => 0,
"ORDER" => ["weight" => "DESC"]
]);
+ //遍历一级分类,然后获取下面的二级分类,获取到了就push
+ foreach ($category_parent as $key => $value) {
+ //把一级分类先加入到空数组
+ array_push($categorys,$value);
+ //然后查询他下面的子分类,再追加到数组
+ $category_subs = $db->select('on_categorys','*',[
+ "fid" => $value['id'],
+ "ORDER" => ["weight" => "DESC"]
+ ]);
+
+ foreach ($category_subs as $category_sub) {
+ array_push($categorys,$category_sub);
+ }
+ }
//根据分类ID查询二级分类,分类fid大于0的都是二级分类
function get_category_sub($id) {
global $db;
@@ -42,16 +56,28 @@ if( is_login() ){
//如果没有登录,只获取公有链接
else{
//查询分类目录
- $categorys = $db->select('on_categorys','*',[
- "property" => 0,
- "ORDER" => ["weight" => "DESC"]
- ]);
+ $categorys = [];
//查询一级分类目录,分类fid为0的都是一级分类
$category_parent = $db->select('on_categorys','*',[
"fid" => 0,
'property' => 0,
"ORDER" => ["weight" => "DESC"]
]);
+ //遍历一级分类,然后获取下面的二级分类,获取到了就push
+ foreach ($category_parent as $key => $value) {
+ //把一级分类先加入到空数组
+ array_push($categorys,$value);
+ //然后查询他下面的子分类,再追加到数组
+ $category_subs = $db->select('on_categorys','*',[
+ "fid" => $value['id'],
+ 'property' => 0,
+ "ORDER" => ["weight" => "DESC"]
+ ]);
+
+ foreach ($category_subs as $category_sub) {
+ array_push($categorys,$category_sub);
+ }
+ }
//根据分类ID查询二级分类,分类fid大于0的都是二级分类
function get_category_sub($id) {
global $db;
@@ -80,29 +106,6 @@ else{
$onenav['right_menu'] = 'user_menu();';
}
-
-//获取访客IP
-function getIP() {
- if (getenv('HTTP_CLIENT_IP')) {
- $ip = getenv('HTTP_CLIENT_IP');
- }
- elseif (getenv('HTTP_X_FORWARDED_FOR')) {
- $ip = getenv('HTTP_X_FORWARDED_FOR');
- }
- elseif (getenv('HTTP_X_FORWARDED')) {
- $ip = getenv('HTTP_X_FORWARDED');
- }
- elseif (getenv('HTTP_FORWARDED_FOR')) {
- $ip = getenv('HTTP_FORWARDED_FOR');
- }
- elseif (getenv('HTTP_FORWARDED')) {
- $ip = getenv('HTTP_FORWARDED');
- }
- else {
- $ip = $_SERVER['REMOTE_ADDR'];
- }
- return $ip;
- }
//获取版本号
function get_version(){
if( file_exists('version.txt') ) {
@@ -114,19 +117,7 @@ function get_version(){
return $version;
}
}
-//判断用户是否已经登录
-function is_login(){
- $key = md5(USER.PASSWORD.'onenav');
- //获取session
- $session = $_COOKIE['key'];
- //如果已经成功登录
- if($session == $key) {
- return true;
- }
- else{
- return false;
- }
-}
+
//将URL转换为base64编码
function base64($url){
$urls = parse_url($url);
diff --git a/controller/login.php b/controller/login.php
index bb4b851..b7b64f7 100755
--- a/controller/login.php
+++ b/controller/login.php
@@ -2,16 +2,20 @@
/**
* 登录入口
*/
+
+// 载入辅助函数
+require('functions/helper.php');
+
$username = $site_setting['user'];
$password = $site_setting['password'];
$ip = getIP();
//如果认证通过,直接跳转到后台管理
-$key = md5($username.$password.'onenav');
+$key = md5($username.$password.'onenav'.$_SERVER['HTTP_USER_AGENT']);
//获取cookie
$cookie = $_COOKIE['key'];
//如果已经登录,直接跳转
-if( $cookie === $key ){
+if( is_login() ){
header('location:index.php?c=admin');
exit;
}
@@ -22,7 +26,7 @@ if( $_GET['check'] == 'login' ) {
$pass = $_POST['password'];
header('Content-Type:application/json; charset=utf-8');
if( ($user === $username) && ($pass === $password) ) {
- $key = md5($username.$password.'onenav');
+ $key = md5($username.$password.'onenav'.$_SERVER['HTTP_USER_AGENT']);
//开启httponly支持
setcookie("key", $key, time()+30 * 24 * 60 * 60,"/",NULL,false,TRUE);
$data = [
@@ -56,29 +60,6 @@ if( $_GET['check'] == 'login' ) {
// header('location:index.php?c=admin');
// }
-//获取访客IP
-function getIP() {
-if (getenv('HTTP_CLIENT_IP')) {
-$ip = getenv('HTTP_CLIENT_IP');
-}
-elseif (getenv('HTTP_X_FORWARDED_FOR')) {
- $ip = getenv('HTTP_X_FORWARDED_FOR');
-}
- elseif (getenv('HTTP_X_FORWARDED')) {
- $ip = getenv('HTTP_X_FORWARDED');
-}
-elseif (getenv('HTTP_FORWARDED_FOR')) {
-$ip = getenv('HTTP_FORWARDED_FOR');
-}
-elseif (getenv('HTTP_FORWARDED')) {
-$ip = getenv('HTTP_FORWARDED');
-}
-else {
- $ip = $_SERVER['REMOTE_ADDR'];
-}
- return $ip;
-}
-
// 载入后台登录模板
require('templates/admin/login.php');
\ No newline at end of file
diff --git a/data/update.log b/data/update.log
index 5556731..756e2ac 100755
--- a/data/update.log
+++ b/data/update.log
@@ -103,4 +103,17 @@ CREATE INDEX on_options_key_IDX ON on_options ("key");
2. 后台新增根据分类查询链接
3. 离线站点图标(使用标题第一个字符)
4. 修复baisu主题修改二级分类导致分类变一级的问题
-5. 新增主题自定义参数设置
\ No newline at end of file
+5. 新增主题自定义参数设置
+
+20220507
+1. 导入链接支持自动创建分类
+2. 支持批量修改链接属性为公有或私有
+3. 修改默认主题角标大小
+
+20220509
+1. 支持 .html 链接导出
+2. 默认主题搜索支持匹配URL
+
+20220513
+1. 主题分类排序优化
+2. 修改分类优化
\ No newline at end of file
diff --git a/functions/helper.php b/functions/helper.php
index 106a6db..c89941b 100755
--- a/functions/helper.php
+++ b/functions/helper.php
@@ -24,7 +24,7 @@ function getIP() {
function is_login(){
- $key = md5(USER.PASSWORD.'onenav');
+ $key = md5(USER.PASSWORD.'onenav'.$_SERVER['HTTP_USER_AGENT']);
//获取session
$session = $_COOKIE['key'];
//如果已经成功登录
diff --git a/templates/admin/edit_category.php b/templates/admin/edit_category.php
index 63376f5..b6e3e6c 100755
--- a/templates/admin/edit_category.php
+++ b/templates/admin/edit_category.php
@@ -47,6 +47,10 @@
if ( $category['id'] == $category_one['fid'] ) {
continue;
}
+ //如果分类ID的父级ID不能是自己
+ if( $category['id'] == $id ) {
+ continue;
+ }
?>
diff --git a/templates/admin/header.php b/templates/admin/header.php
index b70e2e7..75d70b6 100755
--- a/templates/admin/header.php
+++ b/templates/admin/header.php
@@ -10,7 +10,7 @@
@@ -34,6 +34,8 @@
+
+
@@ -44,6 +46,9 @@
编辑
删除
+
+
+
diff --git a/templates/admin/static/embed.js b/templates/admin/static/embed.js
index a7a1b92..6eee47c 100755
--- a/templates/admin/static/embed.js
+++ b/templates/admin/static/embed.js
@@ -193,6 +193,28 @@ layui.use(['element','table','layer','form','upload'], function(){
}
//console.log(data);
break;
+ case "set_private":
+ //用户点击设为私有按钮
+ var data = checkStatus.data;
+ ids = [];
+ //获取链接所有ID,并拼接为数组
+ for(let i = 0;i < data.length;i++) {
+ ids.push(data[i].id);
+ }
+ //调用函数设为私有
+ set_link_attribute(ids,1);
+ break;
+ case "set_public":
+ //用户点击设为私有按钮
+ var data = checkStatus.data;
+ ids = [];
+ //获取链接所有ID,并拼接为数组
+ for(let i = 0;i < data.length;i++) {
+ ids.push(data[i].id);
+ }
+ //调用函数设为公有
+ set_link_attribute(ids,0);
+ break;
case 'isAll':
layer.msg(checkStatus.isAll ? '全选': '未全选');
break;
@@ -546,12 +568,12 @@ layui.use(['element','table','layer','form','upload'], function(){
//识别链接信息
form.on('submit(imp_link)', function(data){
//用ajax异步加载
- $.post('/index.php?c=api&method=imp_link',data.field,function(data,status){
+ $.post('/index.php?c=api&method=import_link',data.field,function(data,status){
//如果添加成功
- if(data.code == 0) {
+ if(data.code == 200) {
layer.open({
title: '导入完成'
- ,content: data.msg
+ ,content: "总数:" + data.msg.count + " 成功:" + data.msg.success + " 失败:" + data.msg.failed
});
//layer.msg('已添加!', {icon: 1});
}
@@ -745,3 +767,45 @@ function get_latest_version(){
});
}
+
+//设置链接属性,公有或私有,接收一个链接id数组和一个链接属性
+function set_link_attribute(ids,property) {
+ if( ids.length === 0 ) {
+ layer.msg("请先选择链接!",{icon:5});
+ }
+ else{
+ $.post("/index.php?c=api&method=set_link_attribute",{ids:ids,property:property},function(data,status){
+ if( data.code == 200 ){
+ layer.msg("设置已更新!",{icon:1});
+ }
+ else{
+ layer.msg("设置失败!",{icon:5});
+ }
+ });
+ }
+}
+
+//导出所有链接
+function export_link(url, fileName) {
+ layer.confirm('导出的链接可以导入到浏览器也可以再次导入到OneNav!', {icon: 3, title:'确定导出所有链接?'}, function(index){
+ var date = new Date();
+ var current_time = date.toLocaleDateString();
+ current_time = current_time.replaceAll("/",".");
+ var url = "index.php?c=api&method=export_link";
+ var fileName = "OneNav_Export_" + current_time + ".html";
+ var x = new XMLHttpRequest();
+ x.open("GET", url, true);
+ x.responseType = 'blob';
+ x.onload=function(e) {
+ var url = window.URL.createObjectURL(x.response)
+ var a = document.createElement('a');
+ a.href = url
+ a.download = fileName;
+ a.click()
+ }
+ x.send();
+
+ layer.close(index);
+ });
+
+}
\ No newline at end of file
diff --git a/templates/default/index.php b/templates/default/index.php
index da186fb..5af60d7 100755
--- a/templates/default/index.php
+++ b/templates/default/index.php
@@ -209,6 +209,9 @@
?>
+
+
+
diff --git a/templates/default/static/style.css b/templates/default/static/style.css
index 9cc96af..23a7297 100755
--- a/templates/default/static/style.css
+++ b/templates/default/static/style.css
@@ -17,7 +17,7 @@ body{
overflow: hidden;
}
.link-line .angle{
- width:70px;
+ width:50px;
height:70px;
position: absolute;
background: #FF5722;
diff --git a/version.txt b/version.txt
index d4d215f..d38f6bd 100755
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-v0.9.20-20220429
\ No newline at end of file
+v0.9.21-20220516
\ No newline at end of file