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 @@ ?>