diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..43286d2 --- /dev/null +++ b/.htaccess @@ -0,0 +1,8 @@ +# Apache配置文件 +RewriteEngine On +RewriteRule ^(data|system|templates)/.*.(db|db3|sql|tar|gz|zip|info|log)$ - [F] +RewriteRule '^login$' ./index.php?c=login [L] +RewriteRule '^admin$' ./index.php?c=admin [L] +RewriteRule '^([A-Za-z0-9]+)$' ./index.php?u=$1 [L] +RewriteRule '^(.+)/click/([A-Za-z0-9]+)$' ./index.php?c=$2&id=$3&u=$1 [L] +RewriteRule '^(.+)/click/(.+)' ./$3 [L] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e9c41e9 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +TwoNav 是一款开源免费的书签(导航)管理程序,使用PHP + SQLite 3开发,界面简洁,安装简单,使用方便。TwoNav可帮助你你将浏览器书签集中式管理,解决跨设备、跨平台、跨浏览器之间同步和访问困难问题,做到一处部署,随处访问。 + +- **演示站**: [http://two.lm21.top](http://two.lm21.top) +- **仅供体验,定期清理数据** 账号密码`admin` + +### 内测版转正 +* 删除安装目录下`system`文件夹后在解压覆盖 + +### 相关文档 +* [安装教程](https://gitee.com/tznb/TwoNav/wikis/pages?sort_id=7968668&doc_id=3767990) | [使用说明](https://gitee.com/tznb/TwoNav/wikis) | [下载TwoNav](https://gitee.com/tznb/TwoNav/releases) +* [OneNav Extend 升级到 TwoNav](https://gitee.com/tznb/OneNav/wikis/pages?sort_id=7955135&doc_id=2439895) + +### 项目地址 +- [https://gitee.com/tznb/TwoNav](https://gitee.com/tznb/TwoNav) +- [https://github.com/tznb1/TwoNav](https://github.com/tznb1/TwoNav) + +### 技术支持 +- QQ: 271152681 +- QQ群: 695720839 + +### 功能特色 +* 支持后台管理 +* 支持私有链接 +* 支持加密链接 +* 支持分享链接 +* 支持二级分类 +* 支持用户分组 +* 支持Chrome/Firefox/Edge书签批量导入 +* 支持多种主题风格 +* 支持链接信息自动识别 +* 支持API +* 支持Docker部署 +* 支持uTools插件 +* 支持Chromium内核的[浏览器扩展] + + + \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..cfc5d5f Binary files /dev/null and b/favicon.ico differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..3911c48 --- /dev/null +++ b/index.php @@ -0,0 +1,78 @@ +'sqlite','database'=>$db_config['path']]); + }catch (Exception $e) { + Amsg(-1,'载入数据库失败'.$db_config['path']); + } +}elseif($db_config['type'] == 'mysql'){ + try { + $db = new Medoo\Medoo(['type' => 'mysql', + 'host' => $db_config['host'], + 'port' => $db_config['port'], + 'database' => $db_config['name'], + 'username' => $db_config['user'], + 'password' => $db_config['password'] + ]); + }catch (Exception $e) { + Amsg(-1,'链接数据库失败!'); + } +} + + +$global_config = unserialize( get_db("global_config", "v", ["k" => "o_config"]) ); //全局配置 +$c = Get('c'); +$libs = $global_config['Libs']; +define('libs',$global_config['Libs']); +define('SysVer',Get_Version()); +define('Debug',$global_config['Debug'] == 1); + +if($c !== $global_config["Register"]){ + $u = Get('u'); + if(empty($u) && $global_config['Sub_domain'] == 1 && is_subscribe('bool')){ + $cut = explode('.',$_SERVER["HTTP_HOST"]); + if(count($cut) == 3){ + $USER_DB = get_db("global_user", "*", ["User"=>reset($cut)]); + if(!empty($USER_DB) && check_purview('Sub_domain',1)){ + $_COOKIE['Default_User'] = $USER_DB['User'];unset($cut); + } + } + } + $u = !empty($u)?$u:(!empty($_COOKIE['Default_User'])?$_COOKIE['Default_User']:(!empty($global_config['Default_User'])?$global_config['Default_User']:'admin'));//优先级:Get>Host>Cookie>默认用户>admin + $USER_DB = get_db("global_user", "*", ["User"=>$u]); + //没找到账号显示404 + if(empty($USER_DB)) { + if($_SERVER['REQUEST_METHOD'] === 'POST'){ + msg(-1,'账号不存在!'); + }else{ + require(DIR.'/templates/admin/page/404.php'); + exit; + } + } + define('U',$u);define('UID',$USER_DB['ID']); +} + +session_name('TwoNavSID'); +if(empty($c) || $c == 'index'){ + require "./system/index.php";//主页 +}elseif($c == $global_config["Register"]){ + require "./system/Register.php";//注册 +}elseif($c == $global_config['Login'] || $c == $USER_DB['Login']){ + require "./system/login.php";//登陆 +}elseif(in_array($c,['admin','click','api','ico','verify'])){ + require "./system/{$c}.php"; +}elseif(in_array($c,['apply','guestbook'])){ + require "./system/expand/{$c}.php"; +}else{ + Amsg(-1,'接口错误'.$c); +} diff --git a/static/ContextMenu/2.9.2/README b/static/ContextMenu/2.9.2/README new file mode 100644 index 0000000..f64f19e --- /dev/null +++ b/static/ContextMenu/2.9.2/README @@ -0,0 +1,4 @@ +右键菜单插件 +使用 contextMenu 要引入 jquery.contextMenu.js 和 jquery.contextMenu.css +同时由于 contextMenu 依赖 jQuery(必须)和 jQuery UI position(非必须,但还是推荐使用),还必须将这两个引入进来。 +https://www.cnblogs.com/guandekuan/p/12563930.html \ No newline at end of file diff --git a/static/ContextMenu/2.9.2/font/context-menu-icons.eot b/static/ContextMenu/2.9.2/font/context-menu-icons.eot new file mode 100644 index 0000000..896c5b0 Binary files /dev/null and b/static/ContextMenu/2.9.2/font/context-menu-icons.eot differ diff --git a/static/ContextMenu/2.9.2/font/context-menu-icons.ttf b/static/ContextMenu/2.9.2/font/context-menu-icons.ttf new file mode 100644 index 0000000..6ecdf1b Binary files /dev/null and b/static/ContextMenu/2.9.2/font/context-menu-icons.ttf differ diff --git a/static/ContextMenu/2.9.2/font/context-menu-icons.woff b/static/ContextMenu/2.9.2/font/context-menu-icons.woff new file mode 100644 index 0000000..a37f855 Binary files /dev/null and b/static/ContextMenu/2.9.2/font/context-menu-icons.woff differ diff --git a/static/ContextMenu/2.9.2/font/context-menu-icons.woff2 b/static/ContextMenu/2.9.2/font/context-menu-icons.woff2 new file mode 100644 index 0000000..b3ca8d1 Binary files /dev/null and b/static/ContextMenu/2.9.2/font/context-menu-icons.woff2 differ diff --git a/static/ContextMenu/2.9.2/jquery.contextMenu.css b/static/ContextMenu/2.9.2/jquery.contextMenu.css new file mode 100644 index 0000000..6d385ca --- /dev/null +++ b/static/ContextMenu/2.9.2/jquery.contextMenu.css @@ -0,0 +1,309 @@ +@charset "UTF-8"; +/*! + * jQuery contextMenu - Plugin for simple contextMenu handling + * + * Version: v2.9.2 + * + * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://swisnl.github.io/jQuery-contextMenu/ + * + * Copyright (c) 2011-2020 SWIS BV and contributors + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * + * Date: 2020-05-13T13:55:37.023Z + */ +@-webkit-keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} +@-o-keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + -o-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + -o-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} +@keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + -o-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + -o-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} + +@font-face { + font-family: "context-menu-icons"; + font-style: normal; + font-weight: normal; + + src: url("font/context-menu-icons.eot?33lxn"); + src: url("font/context-menu-icons.eot?33lxn#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?33lxn") format("woff2"), url("font/context-menu-icons.woff?33lxn") format("woff"), url("font/context-menu-icons.ttf?33lxn") format("truetype"); +} + +.context-menu-icon-add:before { + content: "\EA01"; +} + +.context-menu-icon-copy:before { + content: "\EA02"; +} + +.context-menu-icon-cut:before { + content: "\EA03"; +} + +.context-menu-icon-delete:before { + content: "\EA04"; +} + +.context-menu-icon-edit:before { + content: "\EA05"; +} + +.context-menu-icon-loading:before { + content: "\EA06"; +} + +.context-menu-icon-paste:before { + content: "\EA07"; +} + +.context-menu-icon-quit:before { + content: "\EA08"; +} + +.context-menu-icon::before { + position: absolute; + top: 50%; + left: 0; + width: 2em; + font-family: "context-menu-icons"; + font-size: 1em; + font-style: normal; + font-weight: normal; + line-height: 1; + color: #2980b9; + text-align: center; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.context-menu-icon.context-menu-hover:before { + color: #fff; +} + +.context-menu-icon.context-menu-disabled::before { + color: #bbb; +} + +.context-menu-icon.context-menu-icon-loading:before { + -webkit-animation: cm-spin 2s infinite; + -o-animation: cm-spin 2s infinite; + animation: cm-spin 2s infinite; +} + +.context-menu-icon.context-menu-icon--fa { + display: list-item; + font-family: inherit; + line-height: inherit; +} +.context-menu-icon.context-menu-icon--fa::before { + position: absolute; + top: 50%; + left: 0; + width: 2em; + font-family: FontAwesome; + font-size: 1em; + font-style: normal; + font-weight: normal; + line-height: 1; + color: #2980b9; + text-align: center; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.context-menu-icon.context-menu-icon--fa.context-menu-hover:before { + color: #fff; +} +.context-menu-icon.context-menu-icon--fa.context-menu-disabled::before { + color: #bbb; +} + +.context-menu-icon.context-menu-icon--fa5 { + display: list-item; + font-family: inherit; + line-height: inherit; +} +.context-menu-icon.context-menu-icon--fa5 i, .context-menu-icon.context-menu-icon--fa5 svg { + position: absolute; + top: .3em; + left: .5em; + color: #2980b9; +} +.context-menu-icon.context-menu-icon--fa5.context-menu-hover > i, .context-menu-icon.context-menu-icon--fa5.context-menu-hover > svg { + color: #fff; +} +.context-menu-icon.context-menu-icon--fa5.context-menu-disabled i, .context-menu-icon.context-menu-icon--fa5.context-menu-disabled svg { + color: #bbb; +} + +.context-menu-list { + position: absolute; + display: inline-block; + min-width: 13em; + max-width: 26em; + padding: .25em 0; + margin: .3em; + font-family: inherit; + font-size: inherit; + list-style-type: none; + background: #fff; + border: 1px solid #bebebe; + border-radius: .2em; + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5); + box-shadow: 0 2px 5px rgba(0, 0, 0, .5); +} + +.context-menu-item { + position: relative; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: .2em 2em; + color: #2f2f2f; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: #fff; +} + +.context-menu-separator { + padding: 0; + margin: .35em 0; + border-bottom: 1px solid #e6e6e6; +} + +.context-menu-item > label > input, +.context-menu-item > label > textarea { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.context-menu-item.context-menu-hover { + color: #fff; + cursor: pointer; + background-color: #2980b9; +} + +.context-menu-item.context-menu-disabled { + color: #bbb; + cursor: default; + background-color: #fff; +} + +.context-menu-input.context-menu-hover { + color: #2f2f2f; + cursor: default; +} + +.context-menu-submenu:after { + position: absolute; + top: 50%; + right: .5em; + z-index: 1; + width: 0; + height: 0; + content: ''; + border-color: transparent transparent transparent #2f2f2f; + border-style: solid; + border-width: .25em 0 .25em .25em; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); +} + +/** + * Inputs + */ +.context-menu-item.context-menu-input { + padding: .3em .6em; +} + +/* vertically align inside labels */ +.context-menu-input > label > * { + vertical-align: top; +} + +/* position checkboxes and radios as icons */ +.context-menu-input > label > input[type="checkbox"], +.context-menu-input > label > input[type="radio"] { + position: relative; + top: .12em; + margin-right: .4em; +} + +.context-menu-input > label { + margin: 0; +} + +.context-menu-input > label, +.context-menu-input > label > input[type="text"], +.context-menu-input > label > textarea, +.context-menu-input > label > select { + display: block; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.context-menu-input > label > textarea { + height: 7em; +} + +.context-menu-item > .context-menu-list { + top: .3em; + /* re-positioned by js */ + right: -.3em; + display: none; +} + +.context-menu-item.context-menu-visible > .context-menu-list { + display: block; +} + +.context-menu-accesskey { + text-decoration: underline; +} diff --git a/static/ContextMenu/2.9.2/jquery.contextMenu.js b/static/ContextMenu/2.9.2/jquery.contextMenu.js new file mode 100644 index 0000000..7eb9623 --- /dev/null +++ b/static/ContextMenu/2.9.2/jquery.contextMenu.js @@ -0,0 +1,2134 @@ +/** + * jQuery contextMenu v2.9.2 - Plugin for simple contextMenu handling + * + * Version: v2.9.2 + * + * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://swisnl.github.io/jQuery-contextMenu/ + * + * Copyright (c) 2011-2020 SWIS BV and contributors + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * + * Date: 2020-05-13T13:55:36.983Z + */ + +// jscs:disable +/* jshint ignore:start */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node / CommonJS + factory(require('jquery')); + } else { + // Browser globals. + factory(jQuery); + } +})(function ($) { + + 'use strict'; + + // TODO: - + // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio + // create