diff --git a/.gitignore b/.gitignore index 5fdf30ac..558b8e86 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,8 @@ target/ .project # logs file # -logs/ \ No newline at end of file +logs/ + +# database file # +tale.db +pack.sh \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9bcf9994..c4f11b7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: java jdk: - - oraclejdk8 + - oraclejdk8 \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..b8ec6b2a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Debug (Launch)-Application", + "request": "launch", + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "stopOnEntry": false, + "mainClass": "com.tale.Application", + "projectName": "tale", + "args": "" + }, + { + "type": "java", + "name": "Debug (Attach)", + "request": "attach", + "hostName": "localhost", + "port": 0 + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..e0f15db2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/LICENSE b/LICENSE index f4454b9d..a809d865 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 3a1e9b3f..b14a8f23 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Tale +# Tale Blog ![Tale](https://ooo.0o0.ooo/2017/02/27/58b43450c9182.png) @@ -10,9 +10,15 @@ demo website:https://tale.biezhi.me [![Build Status](https://img.shields.io/travis/otale/tale.svg?style=flat-square)](https://travis-ci.org/otale/tale) [![License](https://img.shields.io/badge/license-MIT-4EB1BA.svg?style=flat-square)](https://github.com/otale/tale/blob/master/LICENSE) -[![@biezhi on weibo](https://img.shields.io/badge/weibo-%40biezhi-red.svg?style=flat-square)](http://weibo.com/u/5238733773) +[![@biezhi on zhihu](https://img.shields.io/badge/zhihu-%40biezhi-red.svg?style=flat-square)](https://www.zhihu.com/people/biezhi) +[![Gitter](https://badges.gitter.im/biezhi/tale-group.svg)](https://gitter.im/tale-group) + +[QuickStart](https://github.com/otale/tale/wiki/QuickStart)  |  [Contribution](https://github.com/otale/tale/issues/new)  |  [Donate](https://github.com/otale/tale/wiki/9.-%E6%8D%90%E8%B5%A0%E6%88%91%E4%BB%AC)  |  [Video](https://github.com/otale/tale/wiki/%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B)  |  [中文](README_ZH.md) + +Here is a throughput graph of the repository for the last few weeks: + +[![Throughput Graph](https://graphs.waffle.io/otale/tale/throughput.svg)](https://waffle.io/otale/tale/metrics/throughput) -[QuickStart](https://github.com/otale/tale/wiki/QuickStart)  |  [Contribution](https://github.com/otale/tale/issues/new)  |  [Donate](donate.md)  |  [Video](video.md)  |  [中文](README_ZH.md) ## Feature @@ -44,6 +50,7 @@ demo website:https://tale.biezhi.me ## Thanks ++ [dongm2ez](https://github.com/dongm2ez) + [pkwenda](https://github.com/pkwenda) + [typecho](https://github.com/typecho/typecho) + [pinghsu](https://github.com/chakhsu/pinghsu) diff --git a/README_ZH.md b/README_ZH.md index d020a61a..7e101557 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -1,20 +1,21 @@ -# Tale +# Tale 博客系统 ![Tale](https://ooo.0o0.ooo/2017/02/27/58b43450c9182.png) > Tale 的英文含义为**故事**,我相信每个坚持写 Blog 的人都是有故事的;中文你叫它 ***塌了*** 也无所谓 🤣。 - `Tale` 使用了轻量级 mvc 框架 [Blade](https://github.com/biezhi/blade) 开发,默认主题使用了漂亮的 [pinghsu](https://github.com/chakhsu/pinghsu),如果觉得这个项目不错,请为它[点赞](https://github.com/otale/tale/stargazers)支持。 - 演示站点:https://tale.biezhi.me [![Build Status](https://img.shields.io/travis/otale/tale.svg?style=flat-square)](https://travis-ci.org/otale/tale) [![License](https://img.shields.io/badge/license-MIT-4EB1BA.svg?style=flat-square)](https://github.com/otale/tale/blob/master/LICENSE) -[![@biezhi on weibo](https://img.shields.io/badge/weibo-%40biezhi-red.svg?style=flat-square)](http://weibo.com/u/5238733773) +[![@biezhi on zhihu](https://img.shields.io/badge/zhihu-%40biezhi-red.svg?style=flat-square)](https://www.zhihu.com/people/biezhi) +[![Gitter](https://badges.gitter.im/biezhi/tale-group.svg)](https://gitter.im/tale-group) + +[开始使用](https://github.com/otale/tale/wiki)  |  [参与贡献](https://github.com/otale/tale/wiki/8.-%E5%8F%82%E4%B8%8E%E8%B4%A1%E7%8C%AE)  |  [捐赠](https://github.com/otale/tale/wiki/9.-%E6%8D%90%E8%B5%A0%E6%88%91%E4%BB%AC)  |  [视频教程](https://github.com/otale/tale/wiki/%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B)  |  [English](README.md) -[开始使用](https://github.com/otale/tale/wiki)  |  [参与贡献](contribution.md)  |  [捐赠](donate.md)  |  [视频教程](video.md)  |  [English](README.md) +[![Throughput Graph](https://graphs.waffle.io/otale/tale/throughput.svg)](https://waffle.io/otale/tale/metrics/throughput) ## 特性 @@ -46,6 +47,7 @@ ## 感谢 ++ [dongm2ez](https://github.com/dongm2ez) + [pkwenda](https://github.com/pkwenda) + [typecho](https://github.com/typecho/typecho) + [pinghsu](https://github.com/chakhsu/pinghsu) diff --git a/bin/tale.sh b/bin/tale.sh deleted file mode 100644 index cf5f65c2..00000000 --- a/bin/tale.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash -# author:biezhi -# url:https://github.com/otale/scripts - - -# Usage: sh tale.sh start "-Xms128m -Xmx128m" -# Usage: sh tale.sh stop -# Usage: sh tale.sh status -# Usage: sh tale.sh reload 10 -# Usage: sh tale.sh log - -env_args="-Xms128m -Xmx128m" -sleeptime=0 -arglen=$# - -# get tale pid -get_pid(){ - pname="`find .. -name 'tale*.jar'`" - pname=${pname:3} - pid=`ps -ef | grep $pname | grep -v grep | awk '{print $2}'` - echo "$pid" -} - -startup(){ - pid=$(get_pid) - if [ "$pid" != "" ] - then - echo "Tale already startup!" - else - jar_path=`find .. -name 'tale*.jar'` - echo "jarfile=$jar_path" - cmd="java $1 -jar $jar_path > ./tale.out < /dev/null &" - echo "cmd: $cmd" - java $1 -jar $jar_path > ./tale.out < /dev/null & - show_log - fi -} - -shut_down(){ - pid=$(get_pid) - if [ "$pid" != "" ] - then - kill -9 $pid - echo "Tale is stop!" - else - echo "Tale already stop!" - fi -} - -show_log(){ - tail -f tale.out -} - -show_help(){ - echo -e "\r\n\t欢迎使用Tale Blog" - echo -e "\r\nUsage: sh tale.sh start|stop|reload|status|log" - exit -} - -show_status(){ - pid=$(get_pid) - if [ "$pid" != "" ] - then - echo "Tale is running with pid: $pid" - else - echo "Tale is stop!" - fi -} - -if [ $arglen -eq 0 ] - then - show_help -else - if [ "$2" != "" ] - then - env_args="$2" - fi - case "$1" in - "start") - startup "$env_args" - ;; - "stop") - shut_down - ;; - "reload") - echo "reload" - ;; - "status") - show_status - ;; - "log") - show_log - ;; - esac -fi \ No newline at end of file diff --git a/bin/tool b/bin/tool new file mode 100644 index 00000000..8b401d6d --- /dev/null +++ b/bin/tool @@ -0,0 +1,154 @@ +#!/bin/sh + +APP_NAME="tale" +JAVA_OPTS="-Xms256m -Xmx256m -Dfile.encoding=UTF-8" +psid=0 + +checkpid() { + javaps=$(pgrep -f "tale-latest") + + if [ -n "$javaps" ]; then + psid=$javaps + else + psid=0 + fi +} + +start() { + checkpid + + if [ $psid -ne 0 ]; then + echo "================================" + echo "warn: $APP_NAME already started! (pid=$psid)" + echo "================================" + else + echo "Starting $APP_NAME ..." + nohup java $JAVA_OPTS -jar tale-latest.jar --app.env=prod >/dev/null 2>&1 & + sleep 1 + checkpid + if [ $psid -ne 0 ]; then + echo "(pid=$psid) [OK]" + else + echo "[Failed]" + fi + fi +} + +stop() { + checkpid + + if [ $psid -ne 0 ]; then + echo -n "Stopping $APP_NAME ...(pid=$psid) " + kill -9 $psid + + if [ $? -eq 0 ]; then + echo "[OK]" + else + echo "[Failed]" + fi + + checkpid + if [ $psid -ne 0 ]; then + stop + fi + else + echo "================================" + echo "warn: $APP_NAME is not running" + echo "================================" + fi +} + +status() { + checkpid + if [ $psid -ne 0 ]; then + echo "$APP_NAME is running! (pid=$psid)" + else + echo "$APP_NAME is not running" + fi +} + +showlog() { + tail -f logs/tale.log +} + +get_latest_release() { + curl --silent "https://api.github.com/repos/$1/releases/latest" | # Get latest release from GitHub api + grep '"tag_name":' | # Get tag line + sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value +} + +REMOVE_LOCAL_THEME=1 + +upgrade(){ + echo "是否允许覆盖本地主题 (y/n)?" + read con + case $con in + y|yes|Y|YES) + REMOVE_THEME=1 + ;; + *) + break + ;; + esac + + # 备份当前目录 + TIME=`date +%Y%m%d_%H%m` + tar czvf back_$TIME.tar.gz * + echo '备份成功' + + echo '开始下载最新版本.' + + TAG_VERSION=$(get_latest_release "otale/tale") + wget -N --no-check-certificate https://github.com/otale/tale/releases/download/$TAG_VERSION/tale.tar.gz + + mkdir upgrade_tmp + tar -zxvf $APP_NAME.tar.gz -C upgrade_tmp + sh tool stop + rm -rf lib tale-latest.jar tool resources/static resources/templates/admin resources/templates/comm resources/templates/*.html resources/*.properties + + if [ "$REMOVE_LOCAL_THEME" -eq "1" ]; then + rm -rf resources/templates/themes + mv upgrade_tmp/resources/templates/themes resources/templates + fi + + mv upgrade_tmp/lib . + mv upgrade_tmp/tale-latest.jar . + mv upgrade_tmp/tool . + chmod +x tool + mv upgrade_tmp/resources/static ./resources + mv upgrade_tmp/resources/*.properties ./resources + mv upgrade_tmp/resources/templates/admin ./resources/templates + mv upgrade_tmp/resources/templates/comm ./resources/templates + mv upgrade_tmp/resources/templates/*.html ./resources/templates + + echo '升级完毕, 请执行 sh tool restart 重启博客系统!' + + rm -rf upgrade_tmp + rm -rf tale.tar.gz +} + +case "$1" in + 'start') + start + ;; + 'stop') + stop + ;; + 'restart') + stop + start + ;; + 'status') + status + ;; + 'log') + showlog + ;; + 'upgrade') + upgrade + ;; + *) + echo "Usage: $0 {start | stop | restart | status | upgrade | log}" + exit 1 +esac +exit 0 \ No newline at end of file diff --git a/contribution.md b/contribution.md deleted file mode 100644 index 627c67c1..00000000 --- a/contribution.md +++ /dev/null @@ -1,15 +0,0 @@ -# 贡献 - -开源世界正因为有广大开源爱好者和工程师而变得更加有趣,我们提倡开放、平等、协作、分享的互联网精神,如果你也愿意那么继续浏览。 - -## 如果你是开发者? - -Tale 博客将是一个不断完善和优化的过程,如果你是一位Java开发者或者前端工程师欢迎你加入为 Tale 做出贡献,让更多人使用稳定可靠的程序并非易事,Tale 组织在向你招手 : ) - -## 如果你是设计师? - -我们相信一个优秀的博客系统必须要有良好的体验,如果你在这方面有自己的见解或者认为我们哪里可以做的更酷,Tale 期待你让更多使用者因为你的设计理念变得更加高大上。 - -## 我只是有些建议 - -我们会参考使用者的优秀建议作为后期开发和优化的重点,让 Tale 变得更加友好,点击 [这里](issues/new) 可以发布你的观点。 \ No newline at end of file diff --git a/donate.md b/donate.md deleted file mode 100644 index 55bfdaf0..00000000 --- a/donate.md +++ /dev/null @@ -1,23 +0,0 @@ -# 捐赠 - -这个项目会因为你的支持变得更加美好 :) - -## 支付宝 - -![](https://ooo.0o0.ooo/2017/02/27/58b3ff7af06db.png) - -## 微信支付 - -![](https://ooo.0o0.ooo/2017/02/27/58b4013b2769d.png) - - -## 捐赠列表 - -| 姓名 | 金额(元) | 方式 | 时间 | 留言 | -| ------| ------ | ------ | ------ | ------ | -| 微信匿名 | 18.88 | 微信 | 2017/02/27 | 无 | -| 微信匿名 | 6.66 | 微信 | 2017/02/27 | 无 | -| 微信匿名 | 23.30 | 微信 | 2017/02/27 | 我要匿名 | -| 潇哥一阵风 | 66.6 | 支付宝 | 2017/02/27 | 项目和作者都比较6,哈哈 | -| 大馍祖师 | 10.0 | 支付宝 | 2017/02/28 | 无 | -| 微信匿名 | 3.0 | 微信 | 2017/03/01 | 零钱少,能否参加项目 | diff --git a/install.sh b/install.sh new file mode 100644 index 00000000..7a2b48d1 --- /dev/null +++ b/install.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +APP_NAME="tale" +get_latest_release() { + curl --silent "https://api.github.com/repos/$1/releases/latest" | # Get latest release from GitHub api + grep '"tag_name":' | # Get tag line + sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value +} + +TAG_VERSION=$(get_latest_release "otale/tale") + +wget -N --no-check-certificate https://github.com/otale/tale/releases/download/$TAG_VERSION/tale.tar.gz + +echo '下载完毕' + +mkdir $APP_NAME +tar -zxvf $APP_NAME.tar.gz -C $APP_NAME && cd $APP_NAME +chmod +x tool + +echo '安装成功,请进入 tale 目录执行 sh tool start 启动' \ No newline at end of file diff --git a/assembly.xml b/package.xml similarity index 91% rename from assembly.xml rename to package.xml index 9953daa2..ca57edc1 100644 --- a/assembly.xml +++ b/package.xml @@ -2,9 +2,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> - customAssembly + bin - dir + tar.gz + zip false @@ -20,7 +21,7 @@ bin/ - /bin + / diff --git a/pom.xml b/pom.xml index b21aeeb9..b6bfd51b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,32 +4,27 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.junicorn + io.github.otale tale - 1.2.10 + latest UTF-8 - 3.15.1 - 1.0.24 - 1.7.1-release - 0.1.5 - 0.0.9 - 0.1.2 + 3.21.0.1 + 2.0.14.RELEASE + 0.2.5 + 0.1.3 + 0.11.0 + 1.0 + 4.0.0 com.bladejava - blade-core - ${blade.version} - - - - com.bladejava - blade-kit - 1.4.1-alpha + blade-mvc + ${blade-mvc.version} @@ -38,10 +33,11 @@ sqlite-jdbc ${sqlite.version} + - com.bladejava - blade-jdbc - ${blade-jdbc.version} + io.github.biezhi + anima + ${anima.version} @@ -55,35 +51,55 @@ com.atlassian.commonmark commonmark - 0.8.0 + ${commonmark.version} com.atlassian.commonmark commonmark-ext-gfm-tables - 0.8.0 - - - - com.bladejava - blade-embed-jetty - ${blade-embed-jetty.version} + ${commonmark.version} + rome rome - 1.0 + ${rome.version} - + + com.vdurmont emoji-java - 3.2.0 + ${emoji.version} + + + + + org.projectlombok + lombok + 1.16.18 + provided + + junit + junit + 4.12 + test + + + oss-releases + https://oss.sonatype.org/content/repositories/releases + + true + + + false + + oss-snapshots https://oss.sonatype.org/content/repositories/snapshots @@ -99,6 +115,9 @@ prod + + prod + @@ -113,6 +132,13 @@ dev + + dev + + + + true + @@ -147,16 +173,24 @@ UTF-8 - maven-assembly-plugin false - assembly.xml + package.xml ${project.build.directory}/dist/ + + + make-assembly + package + + single + + + org.apache.maven.plugins @@ -170,7 +204,6 @@ true - resources/ diff --git a/src/main/java/com/tale/Application.java b/src/main/java/com/tale/Application.java index c28bc826..fc128c7a 100644 --- a/src/main/java/com/tale/Application.java +++ b/src/main/java/com/tale/Application.java @@ -1,14 +1,20 @@ package com.tale; -import com.tale.init.TaleLoader; - -import static com.blade.Blade.$; +import com.blade.Blade; +import com.blade.security.web.csrf.CsrfMiddleware; +import com.tale.bootstrap.TaleLoader; +/** + * Tale启动类 + * + * @author biezhi + */ public class Application { - public static void main(String[] args) throws Exception { - TaleLoader.init(); - $().start(Application.class); + public static void main(String[] args) { + Blade blade = Blade.of(); + TaleLoader.init(blade); + blade.use(new CsrfMiddleware()).start(Application.class, args); } } \ No newline at end of file diff --git a/src/main/java/com/tale/annotation/SysLog.java b/src/main/java/com/tale/annotation/SysLog.java new file mode 100644 index 00000000..07f2f1a3 --- /dev/null +++ b/src/main/java/com/tale/annotation/SysLog.java @@ -0,0 +1,18 @@ +package com.tale.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author biezhi + * @date 2018/6/5 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface SysLog { + + String value(); + +} diff --git a/src/main/java/com/tale/bootstrap/Bootstrap.java b/src/main/java/com/tale/bootstrap/Bootstrap.java new file mode 100644 index 00000000..9f19def2 --- /dev/null +++ b/src/main/java/com/tale/bootstrap/Bootstrap.java @@ -0,0 +1,124 @@ +package com.tale.bootstrap; + +import com.blade.Blade; +import com.blade.Environment; +import com.blade.ioc.Ioc; +import com.blade.ioc.annotation.Bean; +import com.blade.ioc.annotation.Inject; +import com.blade.kit.JsonKit; +import com.blade.kit.StringKit; +import com.blade.loader.BladeLoader; +import com.blade.mvc.view.template.JetbrickTemplateEngine; +import com.blade.validator.Validators; +import com.tale.controller.BaseController; +import com.tale.extension.AdminCommons; +import com.tale.extension.Commons; +import com.tale.extension.JetTag; +import com.tale.extension.Theme; +import com.tale.model.dto.RememberMe; +import com.tale.model.dto.Types; +import com.tale.service.OptionsService; +import com.tale.service.SiteService; +import io.github.biezhi.anima.Anima; +import jetbrick.template.JetGlobalContext; +import jetbrick.template.resolver.GlobalResolver; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.tale.bootstrap.TaleConst.CLASSPATH; +import static com.tale.bootstrap.TaleConst.OPTION_SAFE_REMEMBER_ME; + +/** + * Tale初始化进程 + * + * @author biezhi + */ +@Bean +public class Bootstrap implements BladeLoader { + + @Inject + private OptionsService optionsService; + + @Inject + private Environment environment; + + @Override + public void preLoad(Blade blade) { + Ioc ioc = blade.ioc(); + + Validators.useChinese(); + + boolean devMode = true; + if (blade.environment().hasKey("app.dev")) { + devMode = blade.environment().getBoolean("app.dev", true); + } + if (blade.environment().hasKey("app.devMode")) { + devMode = blade.environment().getBoolean("app.devMode", true); + } + SqliteJdbc.importSql(devMode); + Anima.open(SqliteJdbc.DB_SRC); + Commons.setSiteService(ioc.getBean(SiteService.class)); + } + + @Override + public void load(Blade blade) { + JetbrickTemplateEngine templateEngine = new JetbrickTemplateEngine(); + + List macros = new ArrayList<>(8); + macros.add(File.separatorChar + "comm" + File.separatorChar + "macros.html"); + // 扫描主题下面的所有自定义宏 + String themeDir = CLASSPATH + "templates" + File.separatorChar + "themes"; + File[] dir = new File(themeDir).listFiles(); + if (null != dir) { + for (File f : dir) { + if (f.isDirectory() && Files.exists(Paths.get(f.getPath() + File.separatorChar + "macros.html"))) { + String macroName = File.separatorChar + "themes" + File.separatorChar + f.getName() + File.separatorChar + "macros.html"; + macros.add(macroName); + } + } + } + + StringBuffer sbuf = new StringBuffer(); + macros.forEach(s -> sbuf.append(',').append(s)); + templateEngine.addConfig("jetx.import.macros", sbuf.substring(1)); + + GlobalResolver resolver = templateEngine.getGlobalResolver(); + resolver.registerFunctions(Commons.class); + resolver.registerFunctions(Theme.class); + resolver.registerFunctions(AdminCommons.class); + resolver.registerTags(JetTag.class); + + JetGlobalContext context = templateEngine.getGlobalContext(); + context.set("version", environment.get("app.version", "v1.0")); + context.set("enableCdn", environment.getBoolean("app.enableCdn", false)); + + blade.templateEngine(templateEngine); + + TaleConst.ENABLED_CDN = environment.getBoolean("app.enableCdn", false); + TaleConst.MAX_FILE_SIZE = environment.getInt("app.max-file-size", 20480); + + TaleConst.OPTIONS.addAll(optionsService.getOptions()); + String ips = TaleConst.OPTIONS.get(Types.BLOCK_IPS, ""); + if (StringKit.isNotBlank(ips)) { + TaleConst.BLOCK_IPS.addAll(Arrays.asList(ips.split(","))); + } + if (Files.exists(Paths.get(CLASSPATH + "install.lock"))) { + TaleConst.INSTALLED = Boolean.TRUE; + } + + String rememberToken = optionsService.getOption(OPTION_SAFE_REMEMBER_ME); + if (StringKit.isNotEmpty(rememberToken)) { + RememberMe rememberMe = JsonKit.formJson(rememberToken, RememberMe.class); + TaleConst.REMEMBER_TOKEN = rememberMe.getToken(); + } + + BaseController.THEME = "themes/" + Commons.site_option("site_theme"); + + TaleConst.BCONF = environment; + } +} diff --git a/src/main/java/com/tale/bootstrap/SqliteJdbc.java b/src/main/java/com/tale/bootstrap/SqliteJdbc.java new file mode 100644 index 00000000..655f0816 --- /dev/null +++ b/src/main/java/com/tale/bootstrap/SqliteJdbc.java @@ -0,0 +1,77 @@ +package com.tale.bootstrap; + +import com.blade.mvc.Const; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.stream.Collectors; + +/** + * SQLite 数据库操作 + *

+ * Created by biezhi on 2017/3/4. + */ +@Slf4j +@NoArgsConstructor +public final class SqliteJdbc { + + public static final String DB_NAME = "tale.db"; + public static String DB_PATH; + public static String DB_SRC; + + static { + try { + Class.forName("org.sqlite.JDBC"); + } catch (Exception e) { + log.error("load sqlite driver error", e); + } + } + + /** + * 测试连接并导入数据库 + */ + public static void importSql(boolean devMode) { + try { + + DB_PATH = Const.CLASSPATH + File.separatorChar + DB_NAME; + DB_SRC = "jdbc:sqlite://" + DB_PATH; + + if (devMode) { + DB_PATH = System.getProperty("user.dir") + "/" + DB_NAME; + DB_SRC = "jdbc:sqlite://" + DB_PATH; + } + + log.info("blade dev mode: {}", devMode); + log.info("load sqlite database path [{}]", DB_PATH); + log.info("load sqlite database src [{}]", DB_SRC); + + Connection con = DriverManager.getConnection(DB_SRC); + Statement statement = con.createStatement(); + ResultSet rs = statement.executeQuery("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='t_options'"); + int count = rs.getInt(1); + if (count == 0) { + String cp = SqliteJdbc.class.getClassLoader().getResource("").getPath(); + InputStreamReader isr = new InputStreamReader(new FileInputStream(cp + "schema.sql"), "UTF-8"); + + String sql = new BufferedReader(isr).lines().collect(Collectors.joining("\n")); + int r = statement.executeUpdate(sql); + log.info("initialize import database - {}", r); + } + rs.close(); + statement.close(); + con.close(); + log.info("database path is: {}", DB_PATH); + } catch (Exception e) { + log.error("initialize database fail", e); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/tale/bootstrap/TaleConst.java b/src/main/java/com/tale/bootstrap/TaleConst.java new file mode 100644 index 00000000..317d5572 --- /dev/null +++ b/src/main/java/com/tale/bootstrap/TaleConst.java @@ -0,0 +1,120 @@ +package com.tale.bootstrap; + + +import com.blade.Environment; +import com.tale.controller.admin.AdminApiController; +import com.tale.model.dto.PluginMenu; + +import java.io.File; +import java.util.*; + +/** + * Tale 常量存储 + * + * @author biezhi + */ +public class TaleConst { + + public static final String CLASSPATH = new File(AdminApiController.class.getResource("/").getPath()).getPath() + File.separatorChar; + + public static final String REMEMBER_IN_COOKIE = "remember_me"; + public static final String LOGIN_ERROR_COUNT = "login_error_count"; + public static String LOGIN_SESSION_KEY = "login_user"; + public static String REMEMBER_TOKEN = ""; + public static Environment OPTIONS = Environment.of(new HashMap<>()); + public static Boolean INSTALLED = false; + public static Boolean ENABLED_CDN = true; + public static Environment BCONF = null; + + /** + * 最大页码 + */ + public static final int MAX_PAGE = 100; + + /** + * 最大获取文章条数 + */ + public static final int MAX_POSTS = 9999; + + /** + * 文章最多可以输入的文字数 + */ + public static final int MAX_TEXT_COUNT = 200000; + + /** + * 文章标题最多可以输入的文字个数 + */ + public static final int MAX_TITLE_COUNT = 200; + + /** + * 插件菜单 + */ + public static final List PLUGIN_MENUS = new ArrayList<>(); + + /** + * 上传文件最大20M + */ + public static Integer MAX_FILE_SIZE = 204800; + + /** + * 要过滤的ip列表 + */ + public static final Set BLOCK_IPS = new HashSet<>(16); + + public static final String SLUG_HOME = "/"; + public static final String SLUG_ARCHIVES = "archives"; + public static final String SLUG_CATEGRORIES = "categories"; + public static final String SLUG_TAGS = "tags"; + + /** + * 静态资源URI + */ + public static final String STATIC_URI = "/static"; + + /** + * 安装页面URI + */ + public static final String INSTALL_URI = "/install"; + + /** + * 后台URI前缀 + */ + public static final String ADMIN_URI = "/admin"; + + /** + * 后台登录地址 + */ + public static final String LOGIN_URI = "/admin/login"; + + /** + * 插件菜单 Attribute Name + */ + public static final String PLUGINS_MENU_NAME = "plugin_menus"; + + public static final String ENV_SUPPORT_163_MUSIC = "app.support_163_music"; + public static final String ENV_SUPPORT_GIST = "app.support_gist"; + public static final String MP3_PREFIX = "[mp3:"; + public static final String MUSIC_IFRAME = ""; + public static final String MUSIC_REG_PATTERN = "\\[mp3:(\\d+)\\]"; + public static final String GIST_PREFIX_URL = "https://gist.github.com/"; + public static final String GIST_REG_PATTERN = "<script src=\"https://gist.github.com/(\\w+)/(\\w+)\\.js\"></script>"; + public static final String GIST_REPLATE_PATTERN = ""; + + + public static final String SQL_QUERY_METAS = "select a.*, count(b.cid) as count from t_metas a left join `t_relationships` b on a.mid = b.mid " + + "where a.type = ? and a.name = ? group by a.mid"; + + public static final String SQL_QUERY_ARTICLES = "select a.* from t_contents a left join t_relationships b on a.cid = b.cid " + + "where b.mid = ? and a.status = 'publish' and a.type = 'post' order by a.created desc"; + + public static final String COMMENT_APPROVED = "approved"; + public static final String COMMENT_NO_AUDIT = "no_audit"; + + public static final String OPTION_CDN_URL = "cdn_url"; + public static final String OPTION_SITE_THEME = "site_theme"; + public static final String OPTION_ALLOW_INSTALL = "allow_install"; + public static final String OPTION_ALLOW_COMMENT_AUDIT = "allow_comment_audit"; + public static final String OPTION_ALLOW_CLOUD_CDN = "allow_cloud_CDN"; + public static final String OPTION_SAFE_REMEMBER_ME = "safe_remember_me"; + +} \ No newline at end of file diff --git a/src/main/java/com/tale/bootstrap/TaleLoader.java b/src/main/java/com/tale/bootstrap/TaleLoader.java new file mode 100644 index 00000000..cc8c6c4c --- /dev/null +++ b/src/main/java/com/tale/bootstrap/TaleLoader.java @@ -0,0 +1,77 @@ +package com.tale.bootstrap; + +import com.blade.Blade; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static com.tale.bootstrap.TaleConst.CLASSPATH; + + +/** + * Created by biezhi on 2017/3/1. + */ +public final class TaleLoader { + + private TaleLoader() { + } + + private static Blade blade; + + public static void init(Blade blade) { + TaleLoader.blade = blade; + loadPlugins(); + loadThemes(); + } + + public static void loadThemes() { + String themeDir = CLASSPATH + "templates" + File.separatorChar + "themes"; + File[] dir = new File(themeDir).listFiles(); + for (File f : dir) { + if (f.isDirectory() && Files.isDirectory(Paths.get(f.getPath() + "/static"))) { + String themePath = "/templates/themes/" + f.getName(); + blade.addStatics(themePath + "/style.css", themePath + "/screenshot.png", themePath + "/static/"); + } + } + } + + public static void loadTheme(String themePath) { + blade.addStatics(themePath + "/style.css", themePath + "/screenshot.png", themePath + "/static/"); + } + + public static void loadPlugins() { + File pluginDir = new File(CLASSPATH + "plugins"); + if (pluginDir.exists() && pluginDir.isDirectory()) { + File[] plugins = pluginDir.listFiles(); + for (File plugin : plugins) { + loadPlugin(plugin); + } + } + } + + /** + * 加载某个插件jar包 + * + * @param pluginFile 插件文件 + */ + public static void loadPlugin(File pluginFile) { + try { + if (pluginFile.isFile() && pluginFile.getName().endsWith(".jar")) { + URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); + Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class}); + add.setAccessible(true); + add.invoke(classLoader, pluginFile.toURI().toURL()); + + String pluginName = pluginFile.getName().substring(6); + blade.addStatics("/templates/plugins/" + pluginName + "/static/"); + } + } catch (Exception e) { + throw new RuntimeException("插件 [" + pluginFile.getName() + "] 加载失败"); + } + } + +} diff --git a/src/main/java/com/tale/controller/ArticleController.java b/src/main/java/com/tale/controller/ArticleController.java new file mode 100644 index 00000000..09f612d2 --- /dev/null +++ b/src/main/java/com/tale/controller/ArticleController.java @@ -0,0 +1,151 @@ +package com.tale.controller; + +import com.blade.exception.ValidatorException; +import com.blade.ioc.annotation.Inject; +import com.blade.kit.StringKit; +import com.blade.mvc.annotation.*; +import com.blade.mvc.http.Request; +import com.blade.mvc.http.Response; +import com.blade.mvc.ui.RestResponse; +import com.tale.bootstrap.TaleConst; +import com.tale.extension.Commons; +import com.tale.model.dto.ErrorCode; +import com.tale.model.dto.Types; +import com.tale.model.entity.Comments; +import com.tale.model.entity.Contents; +import com.tale.service.CommentsService; +import com.tale.service.ContentsService; +import com.tale.service.SiteService; +import com.tale.validators.CommonValidator; +import lombok.extern.slf4j.Slf4j; + +import java.net.URLEncoder; + +import static com.tale.bootstrap.TaleConst.COMMENT_APPROVED; +import static com.tale.bootstrap.TaleConst.COMMENT_NO_AUDIT; +import static com.tale.bootstrap.TaleConst.OPTION_ALLOW_COMMENT_AUDIT; + +/** + * @author biezhi + * @date 2018/6/4 + */ +@Path +@Slf4j +public class ArticleController extends BaseController { + + @Inject + private ContentsService contentsService; + + @Inject + private CommentsService commentsService; + + @Inject + private SiteService siteService; + + /** + * 自定义页面 + */ + @GetRoute(value = {"/:cid", "/:cid.html"}) + public String page(@PathParam String cid, Request request) { + Contents contents = contentsService.getContents(cid); + if (null == contents) { + return this.render_404(); + } + if (contents.getAllowComment()) { + int cp = request.queryInt("cp", 1); + request.attribute("cp", cp); + } + request.attribute("article", contents); + Contents temp = new Contents(); + temp.setHits(contents.getHits() + 1); + temp.updateById(contents.getCid()); + if (Types.ARTICLE.equals(contents.getType())) { + return this.render("post"); + } + if (Types.PAGE.equals(contents.getType())) { + return this.render("page"); + } + return this.render_404(); + } + + /** + * 文章页 + */ + @GetRoute(value = {"article/:cid", "article/:cid.html"}) + public String post(Request request, @PathParam String cid) { + Contents contents = contentsService.getContents(cid); + if (null == contents) { + return this.render_404(); + } + if (Types.DRAFT.equals(contents.getStatus())) { + return this.render_404(); + } + request.attribute("article", contents); + request.attribute("is_post", true); + if (contents.getAllowComment()) { + int cp = request.queryInt("cp", 1); + request.attribute("cp", cp); + } + Contents temp = new Contents(); + temp.setHits(contents.getHits() + 1); + temp.updateById(contents.getCid()); + return this.render("post"); + } + + /** + * 评论操作 + */ + @PostRoute(value = "comment") + @JSON + public RestResponse comment(Request request, Response response, + @HeaderParam String Referer, Comments comments) { + + if (StringKit.isBlank(Referer)) { + return RestResponse.fail(ErrorCode.BAD_REQUEST); + } + + if (!Referer.startsWith(Commons.site_url())) { + return RestResponse.fail("非法评论来源"); + } + + CommonValidator.valid(comments); + + String val = request.address() + ":" + comments.getCid(); + Integer count = cache.hget(Types.COMMENTS_FREQUENCY, val); + if (null != count && count > 0) { + return RestResponse.fail("您发表评论太快了,请过会再试"); + } + comments.setIp(request.address()); + comments.setAgent(request.userAgent()); + + if (TaleConst.OPTIONS.getBoolean(OPTION_ALLOW_COMMENT_AUDIT, true)) { + comments.setStatus(COMMENT_NO_AUDIT); + } else { + comments.setStatus(COMMENT_APPROVED); + } + + try { + commentsService.saveComment(comments); + response.cookie("tale_remember_author", URLEncoder.encode(comments.getAuthor(), "UTF-8"), 7 * 24 * 60 * 60); + response.cookie("tale_remember_mail", URLEncoder.encode(comments.getMail(), "UTF-8"), 7 * 24 * 60 * 60); + if (StringKit.isNotBlank(comments.getUrl())) { + response.cookie("tale_remember_url", URLEncoder.encode(comments.getUrl(), "UTF-8"), 7 * 24 * 60 * 60); + } + + // 设置对每个文章30秒可以评论一次 + cache.hset(Types.COMMENTS_FREQUENCY, val, 1, 30); + siteService.cleanCache(Types.SYS_STATISTICS); + + return RestResponse.ok(); + } catch (Exception e) { + String msg = "评论发布失败"; + if (e instanceof ValidatorException) { + msg = e.getMessage(); + } else { + log.error(msg, e); + } + return RestResponse.fail(msg); + } + } + +} diff --git a/src/main/java/com/tale/controller/BaseController.java b/src/main/java/com/tale/controller/BaseController.java index aaf188ea..062cd03e 100644 --- a/src/main/java/com/tale/controller/BaseController.java +++ b/src/main/java/com/tale/controller/BaseController.java @@ -1,7 +1,7 @@ package com.tale.controller; import com.blade.mvc.http.Request; -import com.tale.model.Users; +import com.tale.model.entity.Users; import com.tale.utils.MapCache; import com.tale.utils.TaleUtils; diff --git a/src/main/java/com/tale/controller/CategoryController.java b/src/main/java/com/tale/controller/CategoryController.java new file mode 100644 index 00000000..7e085503 --- /dev/null +++ b/src/main/java/com/tale/controller/CategoryController.java @@ -0,0 +1,130 @@ +package com.tale.controller; + +import com.blade.ioc.annotation.Inject; +import com.blade.mvc.annotation.GetRoute; +import com.blade.mvc.annotation.Param; +import com.blade.mvc.annotation.Path; +import com.blade.mvc.annotation.PathParam; +import com.blade.mvc.http.Request; +import com.tale.bootstrap.TaleConst; +import com.tale.model.dto.Types; +import com.tale.model.entity.Contents; +import com.tale.model.entity.Metas; +import com.tale.service.ContentsService; +import com.tale.service.MetasService; +import io.github.biezhi.anima.page.Page; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 分类、标签控制器 + * + * @author biezhi + * @date 2017/9/17 + */ +@Path +public class CategoryController extends BaseController { + + @Inject + private ContentsService contentsService; + + @Inject + private MetasService metasService; + + /** + * 分类列表页 + * + * @since 1.3.1 + */ + @GetRoute(value = {"categories", "categories.html"}) + public String categories(Request request) { + Map> mapping = metasService.getMetaMapping(Types.CATEGORY); + Set categories = mapping.keySet(); + request.attribute("categories", categories); + request.attribute("mapping", mapping); + return this.render("categories"); + } + + /** + * 某个分类详情页 + */ + @GetRoute(value = {"category/:keyword", "category/:keyword.html"}) + public String categories(Request request, @PathParam String keyword, @Param(defaultValue = "12") int limit) { + return this.categories(request, keyword, 1, limit); + } + + /** + * 某个分类详情页分页 + */ + @GetRoute(value = {"category/:keyword/:page", "category/:keyword/:page.html"}) + public String categories(Request request, @PathParam String keyword, + @PathParam int page, @Param(defaultValue = "12") int limit) { + + page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; + Metas metaDto = metasService.getMeta(Types.CATEGORY, keyword); + if (null == metaDto) { + return this.render_404(); + } + + Page contentsPage = contentsService.getArticles(metaDto.getMid(), page, limit); + request.attribute("articles", contentsPage); + request.attribute("meta", metaDto); + request.attribute("type", "分类"); + request.attribute("keyword", keyword); + request.attribute("is_category", true); + request.attribute("page_prefix", "/category/" + keyword); + + return this.render("page-category"); + } + + /** + * 标签列表页面 + *

+ * 渲染所有的标签和文章映射 + * + * @since 1.3.1 + */ + @GetRoute(value = {"tags", "tags.html"}) + public String tags(Request request) { + Map> mapping = metasService.getMetaMapping(Types.TAG); + Set tags = mapping.keySet(); + request.attribute("tags", tags); + request.attribute("mapping", mapping); + return this.render("tags"); + } + + /** + * 标签详情页 + * + * @param name 标签名 + */ + @GetRoute(value = {"tag/:name", "tag/:name.html"}) + public String tagPage(Request request, @PathParam String name, @Param(defaultValue = "12") int limit) { + return this.tags(request, name, 1, limit); + } + + /** + * 标签下文章分页 + */ + @GetRoute(value = {"tag/:name/:page", "tag/:name/:page.html"}) + public String tags(Request request, @PathParam String name, @PathParam int page, @Param(defaultValue = "12") int limit) { + page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; + Metas metaDto = metasService.getMeta(Types.TAG, name); + if (null == metaDto) { + return this.render_404(); + } + + Page contentsPage = contentsService.getArticles(metaDto.getMid(), page, limit); + request.attribute("articles", contentsPage); + request.attribute("meta", metaDto); + request.attribute("type", "标签"); + request.attribute("keyword", name); + request.attribute("is_tag", true); + request.attribute("page_prefix", "/tag/" + name); + + return this.render("page-category"); + } + +} \ No newline at end of file diff --git a/src/main/java/com/tale/controller/IndexController.java b/src/main/java/com/tale/controller/IndexController.java index 24a423ff..e16ae318 100644 --- a/src/main/java/com/tale/controller/IndexController.java +++ b/src/main/java/com/tale/controller/IndexController.java @@ -1,52 +1,35 @@ package com.tale.controller; import com.blade.ioc.annotation.Inject; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.IPKit; -import com.blade.kit.PatternKit; -import com.blade.kit.StringKit; +import com.blade.mvc.RouteContext; import com.blade.mvc.annotation.*; -import com.blade.mvc.http.HttpMethod; import com.blade.mvc.http.Request; import com.blade.mvc.http.Response; -import com.blade.mvc.http.wrapper.Session; -import com.blade.mvc.view.RestResponse; -import com.tale.dto.Archive; -import com.tale.dto.ErrorCode; -import com.tale.dto.MetaDto; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.ext.Commons; -import com.tale.init.TaleConst; -import com.tale.model.Comments; -import com.tale.model.Contents; -import com.tale.model.Metas; -import com.tale.service.CommentsService; -import com.tale.service.ContentsService; -import com.tale.service.MetasService; +import com.blade.mvc.http.Session; +import com.tale.bootstrap.TaleConst; +import com.tale.model.dto.Archive; +import com.tale.model.dto.Types; +import com.tale.model.entity.Contents; +import com.tale.model.params.PageParam; import com.tale.service.SiteService; import com.tale.utils.TaleUtils; -import com.vdurmont.emoji.EmojiParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.github.biezhi.anima.enums.OrderBy; +import io.github.biezhi.anima.page.Page; +import lombok.extern.slf4j.Slf4j; -import java.net.URLEncoder; import java.util.List; -@Controller -public class IndexController extends BaseController { - - private static final Logger LOGGER = LoggerFactory.getLogger(IndexController.class); - - @Inject - private ContentsService contentsService; +import static io.github.biezhi.anima.Anima.select; - @Inject - private MetasService metasService; - - @Inject - private CommentsService commentsService; +/** + * 首页、归档、Feed、评论 + * + * @author biezhi + * @since 1.3.1 + */ +@Path +@Slf4j +public class IndexController extends BaseController { @Inject private SiteService siteService; @@ -56,160 +39,32 @@ public class IndexController extends BaseController { * * @return */ - @Route(value = "/", method = HttpMethod.GET) - public String index(Request request, @QueryParam(value = "limit", defaultValue = "12") int limit) { - return this.index(request, 1, limit); - } - - /** - * 自定义页面 - */ - @Route(values = {"/:pagename", "/:pagename.html"}, method = HttpMethod.GET) - public String page(@PathParam String pagename, Request request) { - Contents contents = contentsService.getContents(pagename); - if (null == contents) { - return this.render_404(); - } - if (contents.getAllow_comment()) { - int cp = request.queryInt("cp", 1); - request.attribute("cp", cp); - } - request.attribute("article", contents); - updateArticleHit(contents.getCid(), contents.getHits()); - if (Types.ARTICLE.equals(contents.getType())) { - return this.render("post"); - } - if (Types.PAGE.equals(contents.getType())) { - return this.render("page"); - } - return this.render_404(); + @GetRoute + public String index(Request request, PageParam pageParam) { + return this.index(request, 1, pageParam.getLimit()); } /** * 首页分页 * * @param request - * @param pageIndex + * @param page * @param limit * @return */ - @Route(values = {"page/:p", "page/:pageIndex.html"}, method = HttpMethod.GET) - public String index(Request request, @PathParam int pageIndex, @QueryParam(value = "limit", defaultValue = "12") int limit) { - pageIndex = pageIndex < 0 || pageIndex > TaleConst.MAX_PAGE ? 1 : pageIndex; - Take take = new Take(Contents.class).eq("type", Types.ARTICLE).eq("status", Types.PUBLISH).page(pageIndex, limit, "created desc"); - Paginator articles = contentsService.getArticles(take); - request.attribute("articles", articles); - if (pageIndex > 1) { - this.title(request, "第" + pageIndex + "页"); + @GetRoute(value = {"page/:page", "page/:page.html"}) + public String index(Request request, @PathParam int page, @Param(defaultValue = "12") int limit) { + page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; + if (page > 1) { + this.title(request, "第" + page + "页"); } + request.attribute("page_num", page); + request.attribute("limit", limit); request.attribute("is_home", true); request.attribute("page_prefix", "/page"); return this.render("index"); } - /** - * 文章页 - */ - @Route(values = {"article/:cid", "article/:cid.html"}, method = HttpMethod.GET) - public String post(Request request, @PathParam String cid) { - Contents contents = contentsService.getContents(cid); - if (null == contents) { - return this.render_404(); - } - request.attribute("article", contents); - request.attribute("is_post", true); - if (contents.getAllow_comment()) { - int cp = request.queryInt("cp", 1); - request.attribute("cp", cp); - } - updateArticleHit(contents.getCid(), contents.getHits()); - return this.render("post"); - } - - private void updateArticleHit(Integer cid, Integer chits) { - Integer hits = cache.hget(Types.C_ARTICLE_HITS, cid.toString()); - hits = null == hits ? 1 : hits + 1; - if (hits >= TaleConst.HIT_EXCEED) { - Contents temp = new Contents(); - temp.setCid(cid); - temp.setHits(chits + hits); - contentsService.update(temp); - cache.hset(Types.C_ARTICLE_HITS, cid.toString(), 1); - } else { - cache.hset(Types.C_ARTICLE_HITS, cid.toString(), hits); - } - } - - /** - * 分类页 - * - * @return - */ - @Route(values = {"category/:keyword", "category/:keyword.html"}, method = HttpMethod.GET) - public String categories(Request request, @PathParam String keyword, @QueryParam(value = "limit", defaultValue = "12") int limit) { - return this.categories(request, keyword, 1, limit); - } - - @Route(values = {"category/:keyword/:page", "category/:keyword/:page.html"}, method = HttpMethod.GET) - public String categories(Request request, @PathParam String keyword, - @PathParam int page, @QueryParam(value = "limit", defaultValue = "12") int limit) { - page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; - MetaDto metaDto = metasService.getMeta(Types.CATEGORY, keyword); - if (null == metaDto) { - return this.render_404(); - } - - Paginator contentsPaginator = contentsService.getArticles(metaDto.getMid(), page, limit); - - request.attribute("articles", contentsPaginator); - request.attribute("meta", metaDto); - request.attribute("type", "分类"); - request.attribute("keyword", keyword); - request.attribute("is_category", true); - request.attribute("page_prefix", "/category/" + keyword); - - return this.render("page-category"); - } - - /** - * 标签页 - * - * @param name - * @return - */ - @Route(values = {"tag/:name", "tag/:name.html"}, method = HttpMethod.GET) - public String tags(Request request, @PathParam String name, @QueryParam(value = "limit", defaultValue = "12") int limit) { - return this.tags(request, name, 1, limit); - } - - /** - * 标签分页 - * - * @param request - * @param name - * @param page - * @param limit - * @return - */ - @Route(values = {"tag/:name/:page", "tag/:name/:page.html"}, method = HttpMethod.GET) - public String tags(Request request, @PathParam String name, @PathParam int page, @QueryParam(value = "limit", defaultValue = "12") int limit) { - - page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; - MetaDto metaDto = metasService.getMeta(Types.TAG, name); - if (null == metaDto) { - return this.render_404(); - } - - Paginator contentsPaginator = contentsService.getArticles(metaDto.getMid(), page, limit); - request.attribute("articles", contentsPaginator); - request.attribute("meta", metaDto); - request.attribute("type", "标签"); - request.attribute("keyword", name); - request.attribute("is_tag", true); - request.attribute("page_prefix", "/tag/" + name); - - return this.render("page-category"); - } /** * 搜索页 @@ -217,21 +72,30 @@ public String tags(Request request, @PathParam String name, @PathParam int page, * @param keyword * @return */ - @Route(values = {"search/:keyword", "search/:keyword.html"}, method = HttpMethod.GET) - public String search(Request request, @PathParam String keyword, @QueryParam(value = "limit", defaultValue = "12") int limit) { + @GetRoute(value = {"search/:keyword", "search/:keyword.html"}) + public String search(Request request, @PathParam String keyword, @Param(defaultValue = "12") int limit) { + return this.search(request, keyword, 1, limit); + } + + @GetRoute(value = {"search", "search.html"}) + public String search(Request request, @Param(defaultValue = "12") int limit) { + String keyword = request.query("s").orElse(""); return this.search(request, keyword, 1, limit); } - @Route(values = {"search/:keyword/:page", "search/:keyword/:page.html"}, method = HttpMethod.GET) - public String search(Request request, @PathParam String keyword, @PathParam int page, @QueryParam(value = "limit", defaultValue = "12") int limit) { + @GetRoute(value = {"search/:keyword/:page", "search/:keyword/:page.html"}) + public String search(Request request, @PathParam String keyword, @PathParam int page, @Param(defaultValue = "12") int limit) { page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; - Take take = new Take(Contents.class).eq("type", Types.ARTICLE).eq("status", Types.PUBLISH) - .like("title", "%" + keyword + "%").page(page, limit, "created desc"); - Paginator articles = contentsService.getArticles(take); - request.attribute("articles", articles); + Page articles = select().from(Contents.class) + .where(Contents::getType, Types.ARTICLE) + .and(Contents::getStatus, Types.PUBLISH) + .like(Contents::getTitle, "%" + keyword + "%") + .order(Contents::getCreated, OrderBy.DESC) + .page(page, limit); + request.attribute("articles", articles); request.attribute("type", "搜索"); request.attribute("keyword", keyword); request.attribute("page_prefix", "/search/" + keyword); @@ -243,7 +107,7 @@ public String search(Request request, @PathParam String keyword, @PathParam int * * @return */ - @Route(values = {"archives", "archives.html"}, method = HttpMethod.GET) + @GetRoute(value = {"archives", "archives.html"}) public String archives(Request request) { List archives = siteService.getArchives(); request.attribute("archives", archives); @@ -252,128 +116,57 @@ public String archives(Request request) { } /** - * 友链页 + * feed页 * * @return */ - @Route(values = {"links", "links.html"}, method = HttpMethod.GET) - public String links(Request request) { - List links = metasService.getMetas(Types.LINK); - request.attribute("links", links); - return this.render("links"); + @GetRoute(value = {"feed", "feed.xml", "atom.xml"}) + public void feed(Response response) { + + List articles = select().from(Contents.class) + .where(Contents::getType, Types.ARTICLE) + .and(Contents::getStatus, Types.PUBLISH) + .and(Contents::getAllowFeed, true) + .order(Contents::getCreated, OrderBy.DESC) + .all(); + + try { + String xml = TaleUtils.getRssXml(articles); + response.contentType("text/xml; charset=utf-8"); + response.body(xml); + } catch (Exception e) { + log.error("生成 rss 失败", e); + } } /** - * feed页 + * sitemap 站点地图 * * @return */ - @Route(values = {"feed", "feed.xml"}, method = HttpMethod.GET) - public void feed(Response response) { - Paginator contentsPaginator = contentsService.getArticles(new Take(Contents.class) - .eq("type", Types.ARTICLE).eq("status", Types.PUBLISH).eq("allow_feed", true).page(1, TaleConst.MAX_POSTS, "created desc")); + @GetRoute(value = {"sitemap", "sitemap.xml"}) + public void sitemap(Response response) { + List articles = select().from(Contents.class) + .where(Contents::getType, Types.ARTICLE) + .and(Contents::getStatus, Types.PUBLISH) + .and(Contents::getAllowFeed, true) + .order(Contents::getCreated, OrderBy.DESC) + .all(); try { - String xml = TaleUtils.getRssXml(contentsPaginator.getList()); - response.xml(xml); + String xml = TaleUtils.getSitemapXml(articles); + response.contentType("text/xml; charset=utf-8"); + response.body(xml); } catch (Exception e) { - LOGGER.error("生成RSS失败", e); + log.error("生成 sitemap 失败", e); } } /** * 注销 - * - * @param session - * @param response */ - @Route("logout") - public void logout(Session session, Response response) { - TaleUtils.logout(session, response); + @Route(value = "logout") + public void logout(RouteContext context) { + TaleUtils.logout(context); } - /** - * 评论操作 - */ - @Route(value = "comment", method = HttpMethod.POST) - @JSON - public RestResponse comment(Request request, Response response, - @QueryParam Integer cid, @QueryParam Integer coid, - @QueryParam String author, @QueryParam String mail, - @QueryParam String url, @QueryParam String text, @QueryParam String _csrf_token) { - - String ref = request.header("Referer"); - if (StringKit.isBlank(ref) || StringKit.isBlank(_csrf_token)) { - return RestResponse.fail(ErrorCode.BAD_REQUEST); - } - - if (!ref.startsWith(Commons.site_url())) { - return RestResponse.fail("非法评论来源"); - } - - String token = cache.hget(Types.CSRF_TOKEN, _csrf_token); - if (StringKit.isBlank(token)) { - return RestResponse.fail(ErrorCode.BAD_REQUEST); - } - - if (null == cid || StringKit.isBlank(author) || StringKit.isBlank(mail) || StringKit.isBlank(text)) { - return RestResponse.fail("请输入完整后评论"); - } - - if (author.length() > 50) { - return RestResponse.fail("姓名过长"); - } - - if (!TaleUtils.isEmail(mail)) { - return RestResponse.fail("请输入正确的邮箱格式"); - } - - if (StringKit.isNotBlank(url) && !PatternKit.isURL(url)) { - return RestResponse.fail("请输入正确的URL格式"); - } - - if (text.length() > 200) { - return RestResponse.fail("请输入200个字符以内的评论"); - } - - String val = IPKit.getIpAddrByRequest(request.raw()) + ":" + cid; - Integer count = cache.hget(Types.COMMENTS_FREQUENCY, val); - if (null != count && count > 0) { - return RestResponse.fail("您发表评论太快了,请过会再试"); - } - - author = TaleUtils.cleanXSS(author); - text = TaleUtils.cleanXSS(text); - - author = EmojiParser.parseToAliases(author); - text = EmojiParser.parseToAliases(text); - - Comments comments = new Comments(); - comments.setAuthor(author); - comments.setCid(cid); - comments.setIp(request.address()); - comments.setUrl(url); - comments.setContent(text); - comments.setMail(mail); - comments.setParent(coid); - try { - commentsService.saveComment(comments); - response.cookie("tale_remember_author", URLEncoder.encode(author, "UTF-8"), 7 * 24 * 60 * 60); - response.cookie("tale_remember_mail", URLEncoder.encode(mail, "UTF-8"), 7 * 24 * 60 * 60); - if (StringKit.isNotBlank(url)) { - response.cookie("tale_remember_url", URLEncoder.encode(url, "UTF-8"), 7 * 24 * 60 * 60); - } - // 设置对每个文章1分钟可以评论一次 - cache.hset(Types.COMMENTS_FREQUENCY, val, 1, 60); - siteService.cleanCache(Types.C_STATISTICS); - return RestResponse.ok(); - } catch (Exception e) { - String msg = "评论发布失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - } -} +} \ No newline at end of file diff --git a/src/main/java/com/tale/controller/InstallController.java b/src/main/java/com/tale/controller/InstallController.java index ded8c4a8..c86b9c43 100644 --- a/src/main/java/com/tale/controller/InstallController.java +++ b/src/main/java/com/tale/controller/InstallController.java @@ -1,31 +1,32 @@ package com.tale.controller; +import com.blade.Environment; import com.blade.ioc.annotation.Inject; -import com.blade.kit.FileKit; -import com.blade.kit.StringKit; -import com.blade.kit.base.Config; -import com.blade.mvc.annotation.Controller; +import com.blade.mvc.annotation.GetRoute; import com.blade.mvc.annotation.JSON; -import com.blade.mvc.annotation.QueryParam; -import com.blade.mvc.annotation.Route; -import com.blade.mvc.http.HttpMethod; +import com.blade.mvc.annotation.Path; +import com.blade.mvc.annotation.PostRoute; import com.blade.mvc.http.Request; -import com.blade.mvc.view.RestResponse; -import com.tale.controller.admin.AttachController; -import com.tale.exception.TipException; -import com.tale.init.TaleConst; -import com.tale.model.Users; +import com.blade.mvc.ui.RestResponse; +import com.tale.bootstrap.TaleConst; +import com.tale.model.entity.Users; +import com.tale.model.params.InstallParam; import com.tale.service.OptionsService; import com.tale.service.SiteService; import com.tale.utils.TaleUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.tale.validators.CommonValidator; +import lombok.extern.slf4j.Slf4j; -@Controller("install") -public class InstallController extends BaseController { +import java.nio.file.Files; +import java.nio.file.Paths; + +import static com.tale.bootstrap.TaleConst.CLASSPATH; +import static com.tale.bootstrap.TaleConst.OPTION_ALLOW_INSTALL; - private static final Logger LOGGER = LoggerFactory.getLogger(InstallController.class); +@Slf4j +@Path("install") +public class InstallController extends BaseController { @Inject private SiteService siteService; @@ -35,76 +36,43 @@ public class InstallController extends BaseController { /** * 安装页 - * - * @return */ - @Route(value = "/", method = HttpMethod.GET) + @GetRoute public String index(Request request) { - boolean existInstall = FileKit.exist(AttachController.CLASSPATH + "install.lock"); - int allow_reinstall = TaleConst.OPTIONS.getInt("allow_install", 0); - - if(allow_reinstall == 1){ - request.attribute("is_install", false); - } else { - request.attribute("is_install", existInstall); - } + boolean existInstall = Files.exists(Paths.get(CLASSPATH + "install.lock")); + boolean allowReinstall = TaleConst.OPTIONS.getBoolean(OPTION_ALLOW_INSTALL, false); + request.attribute("is_install", !allowReinstall && existInstall); return "install"; } - @Route(value = "/", method = HttpMethod.POST) + @PostRoute @JSON - public RestResponse doInstall(@QueryParam String site_title, @QueryParam String site_url, - @QueryParam String admin_user, @QueryParam String admin_email, - @QueryParam String admin_pwd) { - if(FileKit.exist(AttachController.CLASSPATH + "install.lock") - && TaleConst.OPTIONS.getInt("allow_install", 0)!=1){ + public RestResponse doInstall(InstallParam installParam) { + if (isRepeatInstall()) { return RestResponse.fail("请勿重复安装"); } - try { - if (StringKit.isBlank(site_title) || - StringKit.isBlank(site_url) || - StringKit.isBlank(admin_user) || - StringKit.isBlank(admin_pwd)) { - return RestResponse.fail("请确认网站信息输入完整"); - } - - if (admin_pwd.length() < 6 || admin_pwd.length() > 14) { - return RestResponse.fail("请输入6-14位密码"); - } - - if (StringKit.isNotBlank(admin_email) && !TaleUtils.isEmail(admin_email)) { - return RestResponse.fail("邮箱格式不正确"); - } - - Users users = new Users(); - users.setUsername(admin_user); - users.setPassword(admin_pwd); - users.setEmail(admin_email); - - siteService.initSite(users); - - if (site_url.endsWith("/")) { - site_url = site_url.substring(0, site_url.length() - 1); - } - if (!site_url.startsWith("http")) { - site_url = "http://".concat(site_url); - } - optionsService.saveOption("site_title", site_title); - optionsService.saveOption("site_url", site_url); - - Config config = new Config(); - config.addAll(optionsService.getOptions()); - TaleConst.OPTIONS = config; - } catch (Exception e) { - String msg = "安装失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } + + CommonValidator.valid(installParam); + + Users temp = new Users(); + temp.setUsername(installParam.getAdminUser()); + temp.setPassword(installParam.getAdminPwd()); + temp.setEmail(installParam.getAdminEmail()); + + siteService.initSite(temp); + + String siteUrl = TaleUtils.buildURL(installParam.getSiteUrl()); + optionsService.saveOption("site_title", installParam.getSiteTitle()); + optionsService.saveOption("site_url", siteUrl); + + TaleConst.OPTIONS = Environment.of(optionsService.getOptions()); + return RestResponse.ok(); } + private boolean isRepeatInstall() { + return Files.exists(Paths.get(CLASSPATH + "install.lock")) + && TaleConst.OPTIONS.getInt("allow_install", 0) != 1; + } + } diff --git a/src/main/java/com/tale/controller/admin/AdminApiController.java b/src/main/java/com/tale/controller/admin/AdminApiController.java new file mode 100644 index 00000000..011db7fd --- /dev/null +++ b/src/main/java/com/tale/controller/admin/AdminApiController.java @@ -0,0 +1,433 @@ +package com.tale.controller.admin; + +import com.blade.Environment; +import com.blade.ioc.annotation.Inject; +import com.blade.kit.JsonKit; +import com.blade.kit.StringKit; +import com.blade.mvc.Const; +import com.blade.mvc.WebContext; +import com.blade.mvc.annotation.*; +import com.blade.mvc.http.Request; +import com.blade.mvc.http.Response; +import com.blade.mvc.ui.RestResponse; +import com.tale.annotation.SysLog; +import com.tale.bootstrap.TaleConst; +import com.tale.bootstrap.TaleLoader; +import com.tale.controller.BaseController; +import com.tale.extension.Commons; +import com.tale.model.dto.ThemeDto; +import com.tale.model.dto.Types; +import com.tale.model.entity.*; +import com.tale.model.params.*; +import com.tale.service.*; +import com.tale.validators.CommonValidator; +import io.github.biezhi.anima.Anima; +import io.github.biezhi.anima.enums.OrderBy; +import io.github.biezhi.anima.page.Page; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; + +import static com.tale.bootstrap.TaleConst.*; +import static io.github.biezhi.anima.Anima.delete; +import static io.github.biezhi.anima.Anima.select; + +/** + * @author biezhi + * @date 2018/6/9 + */ +@Slf4j +@Path(value = "admin/api", restful = true) +public class AdminApiController extends BaseController { + + @Inject + private MetasService metasService; + + @Inject + private ContentsService contentsService; + + @Inject + private CommentsService commentsService; + + @Inject + private OptionsService optionsService; + + @Inject + private SiteService siteService; + + @GetRoute("logs") + public RestResponse sysLogs(PageParam pageParam) { + return RestResponse.ok(select().from(Logs.class).order(Logs::getId, OrderBy.DESC).page(pageParam.getPage(), pageParam.getLimit())); + } + + @SysLog("删除页面") + @PostRoute("page/delete/:cid") + public RestResponse deletePage(@PathParam Integer cid) { + contentsService.delete(cid); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(); + } + + @GetRoute("articles/:cid") + public RestResponse article(@PathParam String cid) { + Contents contents = contentsService.getContents(cid); + contents.setContent(""); + return RestResponse.ok(contents); + } + + @GetRoute("articles/content/:cid") + public void articleContent(@PathParam String cid, Response response) { + Contents contents = contentsService.getContents(cid); + response.text(contents.getContent()); + } + + @PostRoute("article/new") + public RestResponse newArticle(@BodyParam Contents contents) { + CommonValidator.valid(contents); + + Users users = this.user(); + contents.setType(Types.ARTICLE); + contents.setAuthorId(users.getUid()); + //将点击数设初始化为0 + contents.setHits(0); + //将评论数设初始化为0 + contents.setCommentsNum(0); + if (StringKit.isBlank(contents.getCategories())) { + contents.setCategories("默认分类"); + } + Integer cid = contentsService.publish(contents); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(cid); + } + + @PostRoute("article/delete/:cid") + public RestResponse deleteArticle(@PathParam Integer cid) { + contentsService.delete(cid); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(); + } + + @PostRoute("article/update") + public RestResponse updateArticle(@BodyParam Contents contents) { + if (null == contents || null == contents.getCid()) { + return RestResponse.fail("缺少参数,请重试"); + } + CommonValidator.valid(contents); + Integer cid = contents.getCid(); + contentsService.updateArticle(contents); + return RestResponse.ok(cid); + } + + @GetRoute("articles") + public RestResponse articleList(ArticleParam articleParam) { + articleParam.setType(Types.ARTICLE); + articleParam.setOrderBy("created desc"); + Page articles = contentsService.findArticles(articleParam); + return RestResponse.ok(articles); + } + + @GetRoute("pages") + public RestResponse pageList(ArticleParam articleParam) { + articleParam.setType(Types.PAGE); + articleParam.setOrderBy("created desc"); + Page articles = contentsService.findArticles(articleParam); + return RestResponse.ok(articles); + } + + @SysLog("发布页面") + @PostRoute("page/new") + public RestResponse newPage(@BodyParam Contents contents) { + + CommonValidator.valid(contents); + + Users users = this.user(); + contents.setType(Types.PAGE); + contents.setAllowPing(true); + contents.setAuthorId(users.getUid()); + contentsService.publish(contents); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(); + } + + @SysLog("修改页面") + @PostRoute("page/update") + public RestResponse updatePage(@BodyParam Contents contents) { + CommonValidator.valid(contents); + + if (null == contents.getCid()) { + return RestResponse.fail("缺少参数,请重试"); + } + Integer cid = contents.getCid(); + contents.setType(Types.PAGE); + contentsService.updateArticle(contents); + return RestResponse.ok(cid); + } + + @SysLog("保存分类") + @PostRoute("category/save") + public RestResponse saveCategory(@BodyParam MetaParam metaParam) { + metasService.saveMeta(Types.CATEGORY, metaParam.getCname(), metaParam.getMid()); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(); + } + + @SysLog("删除分类/标签") + @PostRoute("category/delete/:mid") + public RestResponse deleteMeta(@PathParam Integer mid) { + metasService.delete(mid); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(); + } + + @GetRoute("comments") + public RestResponse commentList(CommentParam commentParam) { + Users users = this.user(); + commentParam.setExcludeUID(users.getUid()); + + Page commentsPage = commentsService.findComments(commentParam); + return RestResponse.ok(commentsPage); + } + + @SysLog("删除评论") + @PostRoute("comment/delete/:coid") + public RestResponse deleteComment(@PathParam Integer coid) { + Comments comments = select().from(Comments.class).byId(coid); + if (null == comments) { + return RestResponse.fail("不存在该评论"); + } + commentsService.delete(coid, comments.getCid()); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(); + } + + @SysLog("修改评论状态") + @PostRoute("comment/status") + public RestResponse updateStatus(@BodyParam Comments comments) { + comments.update(); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(); + } + + @SysLog("回复评论") + @PostRoute("comment/reply") + public RestResponse replyComment(@BodyParam Comments comments, Request request) { + CommonValidator.validAdmin(comments); + + Comments c = select().from(Comments.class).byId(comments.getCoid()); + if (null == c) { + return RestResponse.fail("不存在该评论"); + } + Users users = this.user(); + comments.setAuthor(users.getUsername()); + comments.setAuthorId(users.getUid()); + comments.setCid(c.getCid()); + comments.setIp(request.address()); + comments.setUrl(users.getHomeUrl()); + + if (StringKit.isNotBlank(users.getEmail())) { + comments.setMail(users.getEmail()); + } else { + comments.setMail(""); + } + comments.setStatus(TaleConst.COMMENT_APPROVED); + comments.setParent(comments.getCoid()); + commentsService.saveComment(comments); + siteService.cleanCache(Types.SYS_STATISTICS); + return RestResponse.ok(); + } + + @GetRoute("attaches") + public RestResponse attachList(PageParam pageParam) { + + Page attachPage = select().from(Attach.class) + .order(Attach::getCreated, OrderBy.DESC) + .page(pageParam.getPage(), pageParam.getLimit()); + + return RestResponse.ok(attachPage); + } + + @SysLog("删除附件") + @PostRoute("attach/delete/:id") + public RestResponse deleteAttach(@PathParam Integer id) throws IOException { + Attach attach = select().from(Attach.class).byId(id); + if (null == attach) { + return RestResponse.fail("不存在该附件"); + } + String key = attach.getFkey(); + siteService.cleanCache(Types.SYS_STATISTICS); + String filePath = CLASSPATH.substring(0, CLASSPATH.length() - 1) + key; + java.nio.file.Path path = Paths.get(filePath); + log.info("Delete attach: [{}]", filePath); + if (Files.exists(path)) { + Files.delete(path); + } + Anima.deleteById(Attach.class, id); + return RestResponse.ok(); + } + + @GetRoute("categories") + public RestResponse categoryList() { + List categories = siteService.getMetas(Types.RECENT_META, Types.CATEGORY, TaleConst.MAX_POSTS); + return RestResponse.ok(categories); + } + + @GetRoute("tags") + public RestResponse tagList() { + List tags = siteService.getMetas(Types.RECENT_META, Types.TAG, TaleConst.MAX_POSTS); + return RestResponse.ok(tags); + } + + @GetRoute("options") + public RestResponse options() { + Map options = optionsService.getOptions(); + return RestResponse.ok(options); + } + + @SysLog("保存系统配置") + @PostRoute("options/save") + public RestResponse saveOptions(Request request) { + Map> querys = request.parameters(); + querys.forEach((k, v) -> optionsService.saveOption(k, v.get(0))); + Environment config = Environment.of(optionsService.getOptions()); + TaleConst.OPTIONS = config; + return RestResponse.ok(); + } + + @SysLog("保存高级选项设置") + @PostRoute("advanced/save") + public RestResponse saveAdvance(AdvanceParam advanceParam) { + // 清除缓存 + if (StringKit.isNotBlank(advanceParam.getCacheKey())) { + if ("*".equals(advanceParam.getCacheKey())) { + cache.clean(); + } else { + cache.del(advanceParam.getCacheKey()); + } + } + // 要过过滤的黑名单列表 + if (StringKit.isNotBlank(advanceParam.getBlockIps())) { + optionsService.saveOption(Types.BLOCK_IPS, advanceParam.getBlockIps()); + TaleConst.BLOCK_IPS.addAll(Arrays.asList(advanceParam.getBlockIps().split(","))); + } else { + optionsService.saveOption(Types.BLOCK_IPS, ""); + TaleConst.BLOCK_IPS.clear(); + } + // 处理卸载插件 + if (StringKit.isNotBlank(advanceParam.getPluginName())) { + String key = "plugin_"; + // 卸载所有插件 + if (!"*".equals(advanceParam.getPluginName())) { + key = "plugin_" + advanceParam.getPluginName(); + } else { + optionsService.saveOption(Types.ATTACH_URL, Commons.site_url()); + } + optionsService.deleteOption(key); + } + + if (StringKit.isNotBlank(advanceParam.getCdnURL())) { + optionsService.saveOption(OPTION_CDN_URL, advanceParam.getCdnURL()); + TaleConst.OPTIONS.set(OPTION_CDN_URL, advanceParam.getCdnURL()); + } + + // 是否允许重新安装 + if (StringKit.isNotBlank(advanceParam.getAllowInstall())) { + optionsService.saveOption(OPTION_ALLOW_INSTALL, advanceParam.getAllowInstall()); + TaleConst.OPTIONS.set(OPTION_ALLOW_INSTALL, advanceParam.getAllowInstall()); + } + + // 评论是否需要审核 + if (StringKit.isNotBlank(advanceParam.getAllowCommentAudit())) { + optionsService.saveOption(OPTION_ALLOW_COMMENT_AUDIT, advanceParam.getAllowCommentAudit()); + TaleConst.OPTIONS.set(OPTION_ALLOW_COMMENT_AUDIT, advanceParam.getAllowCommentAudit()); + } + + // 是否允许公共资源CDN + if (StringKit.isNotBlank(advanceParam.getAllowCloudCDN())) { + optionsService.saveOption(OPTION_ALLOW_CLOUD_CDN, advanceParam.getAllowCloudCDN()); + TaleConst.OPTIONS.set(OPTION_ALLOW_CLOUD_CDN, advanceParam.getAllowCloudCDN()); + } + return RestResponse.ok(); + } + + @GetRoute("themes") + public RestResponse getThemes() { + // 读取主题 + String themesDir = CLASSPATH + "templates/themes"; + File[] themesFile = new File(themesDir).listFiles(); + List themes = new ArrayList<>(themesFile.length); + for (File f : themesFile) { + if (f.isDirectory()) { + ThemeDto themeDto = new ThemeDto(f.getName()); + if (Files.exists(Paths.get(f.getPath() + "/setting.html"))) { + themeDto.setHasSetting(true); + } + themes.add(themeDto); + try { + WebContext.blade().addStatics("/templates/themes/" + f.getName() + "/screenshot.png"); + } catch (Exception e) { + } + } + } + return RestResponse.ok(themes); + } + + @SysLog("保存主题设置") + @PostRoute("themes/setting") + public RestResponse saveSetting(Request request) { + Map> query = request.parameters(); + + // theme_milk_options => { } + String currentTheme = Commons.site_theme(); + String key = "theme_" + currentTheme + "_options"; + + Map options = new HashMap<>(); + query.forEach((k, v) -> options.put(k, v.get(0))); + + optionsService.saveOption(key, JsonKit.toString(options)); + + TaleConst.OPTIONS = Environment.of(optionsService.getOptions()); + return RestResponse.ok(); + } + + @SysLog("激活主题") + @PostRoute("themes/active") + public RestResponse activeTheme(@BodyParam ThemeParam themeParam) { + optionsService.saveOption(OPTION_SITE_THEME, themeParam.getSiteTheme()); + delete().from(Options.class).where(Options::getName).like("theme_option_%").execute(); + + TaleConst.OPTIONS.set(OPTION_SITE_THEME, themeParam.getSiteTheme()); + BaseController.THEME = "themes/" + themeParam.getSiteTheme(); + + String themePath = "/templates/themes/" + themeParam.getSiteTheme(); + try { + TaleLoader.loadTheme(themePath); + } catch (Exception e) { + } + return RestResponse.ok(); + } + + @SysLog("保存模板") + @PostRoute("template/save") + public RestResponse saveTpl(@BodyParam TemplateParam templateParam) throws IOException { + if (StringKit.isBlank(templateParam.getFileName())) { + return RestResponse.fail("缺少参数,请重试"); + } + String content = templateParam.getContent(); + String themePath = Const.CLASSPATH + File.separatorChar + "templates" + File.separatorChar + "themes" + File.separatorChar + Commons.site_theme(); + String filePath = themePath + File.separatorChar + templateParam.getFileName(); + if (Files.exists(Paths.get(filePath))) { + byte[] rf_wiki_byte = content.getBytes("UTF-8"); + Files.write(Paths.get(filePath), rf_wiki_byte); + } else { + Files.createFile(Paths.get(filePath)); + byte[] rf_wiki_byte = content.getBytes("UTF-8"); + Files.write(Paths.get(filePath), rf_wiki_byte); + } + return RestResponse.ok(); + } + +} diff --git a/src/main/java/com/tale/controller/admin/ArticleController.java b/src/main/java/com/tale/controller/admin/ArticleController.java deleted file mode 100644 index 319d981b..00000000 --- a/src/main/java/com/tale/controller/admin/ArticleController.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.tale.controller.admin; - -import com.blade.ioc.annotation.Inject; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.StringKit; -import com.blade.mvc.annotation.*; -import com.blade.mvc.http.HttpMethod; -import com.blade.mvc.http.Request; -import com.blade.mvc.view.RestResponse; -import com.tale.controller.BaseController; -import com.tale.dto.LogActions; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.ext.Commons; -import com.tale.model.Contents; -import com.tale.model.Metas; -import com.tale.model.Users; -import com.tale.service.ContentsService; -import com.tale.service.LogService; -import com.tale.service.MetasService; -import com.tale.service.SiteService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * 文章管理控制器 - * Created by biezhi on 2017/2/21. - */ -@Controller("admin/article") -public class ArticleController extends BaseController { - - private static final Logger LOGGER = LoggerFactory.getLogger(ArticleController.class); - - @Inject - private ContentsService contentsService; - - @Inject - private MetasService metasService; - - @Inject - private LogService logService; - - @Inject - private SiteService siteService; - - /** - * 文章管理首页 - * @param page - * @param limit - * @param request - * @return - */ - @Route(value = "", method = HttpMethod.GET) - public String index(@QueryParam(value = "page", defaultValue = "1") int page, - @QueryParam(value = "limit", defaultValue = "15") int limit, Request request) { - - Paginator contentsPaginator = contentsService.getArticles(new Take(Contents.class).eq("type", Types.ARTICLE).page(page, limit, "created desc")); - request.attribute("articles", contentsPaginator); - return "admin/article_list"; - } - - /** - * 文章发布页面 - * @param request - * @return - */ - @Route(value = "publish", method = HttpMethod.GET) - public String newArticle(Request request) { - List categories = metasService.getMetas(Types.CATEGORY); - request.attribute("categories", categories); - request.attribute(Types.ATTACH_URL, Commons.site_option(Types.ATTACH_URL, Commons.site_url())); - return "admin/article_edit"; - } - - /** - * 文章编辑页面 - * @param cid - * @param request - * @return - */ - @Route(value = "/:cid", method = HttpMethod.GET) - public String editArticle(@PathParam String cid, Request request) { - Contents contents = contentsService.getContents(cid); - request.attribute("contents", contents); - List categories = metasService.getMetas(Types.CATEGORY); - request.attribute("categories", categories); - request.attribute("active", "article"); - request.attribute(Types.ATTACH_URL, Commons.site_option(Types.ATTACH_URL, Commons.site_url())); - return "admin/article_edit"; - } - - /** - * 发布文章操作 - * - * @param title - * @param content - * @param tags - * @param categories - * @param status - * @param slug - * @param allow_comment - * @param allow_ping - * @param allow_feed - * @return - */ - @Route(value = "publish", method = HttpMethod.POST) - @JSON - public RestResponse publishArticle(@QueryParam String title, @QueryParam String content, - @QueryParam String tags, @QueryParam String categories, - @QueryParam String status, @QueryParam String slug, - @QueryParam String fmt_type,@QueryParam String thumb_img, - @QueryParam Boolean allow_comment, @QueryParam Boolean allow_ping, @QueryParam Boolean allow_feed) { - - Users users = this.user(); - - Contents contents = new Contents(); - contents.setTitle(title); - contents.setContent(content); - contents.setStatus(status); - contents.setSlug(slug); - contents.setType(Types.ARTICLE); - contents.setThumb_img(thumb_img); - contents.setFmt_type(fmt_type); - if (null != allow_comment) { - contents.setAllow_comment(allow_comment); - } - if (null != allow_ping) { - contents.setAllow_ping(allow_ping); - } - if (null != allow_feed) { - contents.setAllow_feed(allow_feed); - } - contents.setAuthor_id(users.getUid()); - contents.setTags(tags); - if (StringKit.isBlank(categories)) { - categories = "默认分类"; - } - contents.setCategories(categories); - - try { - Integer cid = contentsService.publish(contents); - siteService.cleanCache(Types.C_STATISTICS); - return RestResponse.ok(cid); - } catch (Exception e) { - String msg = "文章发布失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - } - - /** - * 修改文章操作 - * - * @param cid - * @param title - * @param content - * @param tags - * @param categories - * @param status - * @param slug - * @param allow_comment - * @param allow_ping - * @param allow_feed - * @return - */ - @Route(value = "modify", method = HttpMethod.POST) - @JSON - public RestResponse modifyArticle(@QueryParam Integer cid, @QueryParam String title, - @QueryParam String content,@QueryParam String fmt_type, - @QueryParam String tags, @QueryParam String categories, - @QueryParam String status, @QueryParam String slug, - @QueryParam String thumb_img, - @QueryParam Boolean allow_comment, @QueryParam Boolean allow_ping, @QueryParam Boolean allow_feed) { - - Users users = this.user(); - Contents contents = new Contents(); - contents.setCid(cid); - contents.setTitle(title); - contents.setContent(content); - contents.setStatus(status); - contents.setFmt_type(fmt_type); - contents.setSlug(slug); - contents.setThumb_img(thumb_img); - if (null != allow_comment) { - contents.setAllow_comment(allow_comment); - } - if (null != allow_ping) { - contents.setAllow_ping(allow_ping); - } - if (null != allow_feed) { - contents.setAllow_feed(allow_feed); - } - contents.setAuthor_id(users.getUid()); - contents.setTags(tags); - contents.setCategories(categories); - try { - contentsService.updateArticle(contents); - return RestResponse.ok(cid); - } catch (Exception e) { - String msg = "文章编辑失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - } - - /** - * 删除文章操作 - * - * @param cid - * @param request - * @return - */ - @Route(value = "delete") - @JSON - public RestResponse delete(@QueryParam int cid, Request request) { - try { - contentsService.delete(cid); - siteService.cleanCache(Types.C_STATISTICS); - logService.save(LogActions.DEL_ARTICLE, cid+"", request.address(), this.getUid()); - } catch (Exception e) { - String msg = "文章删除失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } -} diff --git a/src/main/java/com/tale/controller/admin/AttachController.java b/src/main/java/com/tale/controller/admin/AttachController.java deleted file mode 100644 index 8fde6803..00000000 --- a/src/main/java/com/tale/controller/admin/AttachController.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.tale.controller.admin; - -import com.blade.ioc.annotation.Inject; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.FileKit; -import com.blade.kit.Tools; -import com.blade.mvc.annotation.Controller; -import com.blade.mvc.annotation.JSON; -import com.blade.mvc.annotation.QueryParam; -import com.blade.mvc.annotation.Route; -import com.blade.mvc.http.HttpMethod; -import com.blade.mvc.http.Request; -import com.blade.mvc.multipart.FileItem; -import com.blade.mvc.view.RestResponse; -import com.tale.controller.BaseController; -import com.tale.dto.LogActions; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.ext.Commons; -import com.tale.init.TaleConst; -import com.tale.model.Attach; -import com.tale.model.Users; -import com.tale.service.AttachService; -import com.tale.service.LogService; -import com.tale.service.SiteService; -import com.tale.utils.TaleUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * 附件管理 - * - * Created by biezhi on 2017/2/21. - */ -@Controller("admin/attach") -public class AttachController extends BaseController { - - private static final Logger LOGGER = LoggerFactory.getLogger(AttachController.class); - - public static final String CLASSPATH = AttachController.class.getClassLoader().getResource("").getPath(); - - @Inject - private AttachService attachService; - - @Inject - private LogService logService; - - @Inject - private SiteService siteService; - - /** - * 附件页面 - * - * @param request - * @param page - * @param limit - * @return - */ - @Route(value = "", method = HttpMethod.GET) - public String index(Request request, @QueryParam(value = "page", defaultValue = "1") int page, - @QueryParam(value = "limit", defaultValue = "12") int limit) { - Paginator attachPaginator = attachService.getAttachs(new Take(Attach.class).page(page, limit, "id desc")); - request.attribute("attachs", attachPaginator); - request.attribute(Types.ATTACH_URL, Commons.site_option(Types.ATTACH_URL, Commons.site_url())); - request.attribute("max_file_size", TaleConst.MAX_FILE_SIZE / 1024); - return "admin/attach"; - } - - /** - * 上传文件接口 - * - * 返回格式 - * @param request - * @return - */ - @Route(value = "upload", method = HttpMethod.POST) - @JSON - public RestResponse upload(Request request) { - - LOGGER.info("UPLOAD DIR = {}", TaleUtils.upDir); - - Users users = this.user(); - Integer uid = users.getUid(); - Map fileItemMap = request.fileItems(); - Collection fileItems = fileItemMap.values(); - List errorFiles = new ArrayList<>(); - List urls = new ArrayList<>(); - try { - fileItems.forEach((FileItem f) -> { - String fname = f.fileName(); - - if(f.file().length() / 1024 <= TaleConst.MAX_FILE_SIZE){ - String fkey = TaleUtils.getFileKey(fname); - String ftype = TaleUtils.isImage(f.file()) ? Types.IMAGE : Types.FILE; - String filePath = TaleUtils.upDir + fkey; - - File file = new File(filePath); - try { - Tools.copyFileUsingFileChannels(f.file(), file); - f.file().delete(); - } catch (IOException e) { - e.printStackTrace(); - } - Attach attach = attachService.save(fname, fkey, ftype, uid); - urls.add(attach); - siteService.cleanCache(Types.C_STATISTICS); - } else { - errorFiles.add(new Attach(fname)); - } - }); - if(errorFiles.size() > 0){ - RestResponse restResponse = new RestResponse(); - restResponse.setSuccess(false); - restResponse.setPayload(errorFiles); - return restResponse; - } - return RestResponse.ok(urls); - } catch (Exception e) { - String msg = "文件上传失败"; - if(e instanceof TipException){ - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - } - - @Route(value = "delete") - @JSON - public RestResponse delete(@QueryParam Integer id, Request request) { - try { - Attach attach = attachService.byId(id); - if (null == attach) return RestResponse.fail("不存在该附件"); - attachService.delete(id); - siteService.cleanCache(Types.C_STATISTICS); - String upDir = CLASSPATH.substring(0, CLASSPATH.length() - 1); - FileKit.delete(upDir + attach.getFkey()); - logService.save(LogActions.DEL_ARTICLE, attach.getFkey(), request.address(), this.getUid()); - } catch (Exception e) { - String msg = "附件删除失败"; - if (e instanceof TipException) msg = e.getMessage(); - else LOGGER.error(msg, e); - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - -} diff --git a/src/main/java/com/tale/controller/admin/AuthController.java b/src/main/java/com/tale/controller/admin/AuthController.java index 697bf425..cbf9fb8a 100644 --- a/src/main/java/com/tale/controller/admin/AuthController.java +++ b/src/main/java/com/tale/controller/admin/AuthController.java @@ -1,93 +1,88 @@ package com.tale.controller.admin; -import com.blade.ioc.annotation.Inject; +import com.blade.exception.ValidatorException; import com.blade.kit.DateKit; +import com.blade.kit.EncryptKit; import com.blade.kit.StringKit; -import com.blade.kit.json.JSONKit; -import com.blade.mvc.annotation.Controller; -import com.blade.mvc.annotation.JSON; -import com.blade.mvc.annotation.QueryParam; -import com.blade.mvc.annotation.Route; -import com.blade.mvc.http.HttpMethod; -import com.blade.mvc.http.Request; -import com.blade.mvc.http.Response; -import com.blade.mvc.http.wrapper.Session; -import com.blade.mvc.view.RestResponse; +import com.blade.mvc.RouteContext; +import com.blade.mvc.annotation.Path; +import com.blade.mvc.annotation.PostRoute; +import com.blade.mvc.ui.RestResponse; +import com.tale.annotation.SysLog; +import com.tale.bootstrap.TaleConst; import com.tale.controller.BaseController; -import com.tale.dto.LogActions; -import com.tale.exception.TipException; -import com.tale.init.TaleConst; -import com.tale.model.Users; -import com.tale.service.LogService; -import com.tale.service.UsersService; +import com.tale.model.entity.Users; +import com.tale.model.params.LoginParam; import com.tale.utils.TaleUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.tale.validators.CommonValidator; +import lombok.extern.slf4j.Slf4j; + +import static com.tale.bootstrap.TaleConst.LOGIN_ERROR_COUNT; +import static io.github.biezhi.anima.Anima.select; /** * 登录,退出 + *

* Created by biezhi on 2017/2/21. */ -@Controller("admin") +@Slf4j +@Path(value = "admin", restful = true) public class AuthController extends BaseController { - private static final Logger LOGGER = LoggerFactory.getLogger(AuthController.class); - - @Inject - private UsersService usersService; + @SysLog("登录后台") + @PostRoute("login") + public RestResponse doLogin(LoginParam loginParam, RouteContext context) { - @Inject - private LogService logService; + CommonValidator.valid(loginParam); - @Route(value = "login", method = HttpMethod.GET) - public String login(Response response) { - if(null != this.user()){ - response.go("/admin/index"); - return null; - } - return "admin/login"; - } + Integer errorCount = cache.get(LOGIN_ERROR_COUNT); + try { + errorCount = null == errorCount ? 0 : errorCount; + if (errorCount > 3) { + return RestResponse.fail("您输入密码已经错误超过3次,请10分钟后尝试"); + } - @Route(value = "login", method = HttpMethod.POST) - @JSON - public RestResponse doLogin(@QueryParam String username, - @QueryParam String password, - @QueryParam String remeber_me, - Request request, - Session session, Response response) { + long count = new Users().where("username", loginParam.getUsername()).count(); + if (count < 1) { + errorCount += 1; + return RestResponse.fail("不存在该用户"); + } + String pwd = EncryptKit.md5(loginParam.getUsername(), loginParam.getPassword()); - Integer error_count = cache.get("login_error_count"); - try { - error_count = null == error_count ? 0 : error_count; + Users user = select().from(Users.class) + .where(Users::getUsername, loginParam.getUsername()) + .and(Users::getPassword, pwd).one(); - if(null != error_count && error_count > 3){ - return RestResponse.fail("您输入密码已经错误超过3次,请10分钟后尝试"); + if (null == user) { + errorCount += 1; + return RestResponse.fail("用户名或密码错误"); } + context.session().attribute(TaleConst.LOGIN_SESSION_KEY, user); - Users user = usersService.login(username, password); - session.attribute(TaleConst.LOGIN_SESSION_KEY, user); - if (StringKit.isNotBlank(remeber_me)) { - TaleUtils.setCookie(response, user.getUid()); + if (StringKit.isNotBlank(loginParam.getRememberMe())) { + TaleUtils.setCookie(context, user.getUid()); } + Users temp = new Users(); - temp.setUid(user.getUid()); - temp.setLogged(DateKit.getCurrentUnixTime()); - usersService.update(temp); - LOGGER.info("登录成功:{}", JSONKit.toJSONString(request.querys())); - cache.set("login_error_count", 0); - logService.save(LogActions.LOGIN, JSONKit.toJSONString(request.querys()), request.address(), user.getUid()); + temp.setLogged(DateKit.nowUnix()); + temp.updateById(user.getUid()); + log.info("登录成功:{}", loginParam.getUsername()); + + cache.set(LOGIN_ERROR_COUNT, 0); + + return RestResponse.ok(); + } catch (Exception e) { - error_count+=1; - cache.set("login_error_count", error_count, 10 * 60); + errorCount += 1; + cache.set(LOGIN_ERROR_COUNT, errorCount, 10 * 60); String msg = "登录失败"; - if (e instanceof TipException) { + if (e instanceof ValidatorException) { msg = e.getMessage(); } else { - LOGGER.error(msg, e); + log.error(msg, e); } return RestResponse.fail(msg); } - return RestResponse.ok(); } } diff --git a/src/main/java/com/tale/controller/admin/CategoryController.java b/src/main/java/com/tale/controller/admin/CategoryController.java deleted file mode 100644 index 46e08d00..00000000 --- a/src/main/java/com/tale/controller/admin/CategoryController.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.tale.controller.admin; - -import com.blade.ioc.annotation.Inject; -import com.blade.mvc.annotation.Controller; -import com.blade.mvc.annotation.JSON; -import com.blade.mvc.annotation.QueryParam; -import com.blade.mvc.annotation.Route; -import com.blade.mvc.http.HttpMethod; -import com.blade.mvc.http.Request; -import com.blade.mvc.view.RestResponse; -import com.tale.controller.BaseController; -import com.tale.dto.MetaDto; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.init.TaleConst; -import com.tale.service.MetasService; -import com.tale.service.SiteService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * Created by biezhi on 2017/2/21. - */ -@Controller("admin/category") -public class CategoryController extends BaseController { - - private static final Logger LOGGER = LoggerFactory.getLogger(CategoryController.class); - - @Inject - private MetasService metasService; - - @Inject - private SiteService siteService; - - @Route(value = "", method = HttpMethod.GET) - public String index(Request request) { - List categories = siteService.getMetas(Types.RECENT_META, Types.CATEGORY, TaleConst.MAX_POSTS); - List tags = siteService.getMetas(Types.RECENT_META, Types.TAG, TaleConst.MAX_POSTS); - request.attribute("categories", categories); - request.attribute("tags", tags); - return "admin/category"; - } - - @Route(value = "save", method = HttpMethod.POST) - @JSON - public RestResponse saveCategory(@QueryParam String cname, @QueryParam Integer mid) { - try { - metasService.saveMeta(Types.CATEGORY, cname, mid); - siteService.cleanCache(Types.C_STATISTICS); - } catch (Exception e) { - String msg = "分类保存失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - - @Route(value = "delete") - @JSON - public RestResponse delete(@QueryParam int mid) { - try { - metasService.delete(mid); - siteService.cleanCache(Types.C_STATISTICS); - } catch (Exception e) { - String msg = "删除失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - -} diff --git a/src/main/java/com/tale/controller/admin/CommentController.java b/src/main/java/com/tale/controller/admin/CommentController.java deleted file mode 100644 index a3c26479..00000000 --- a/src/main/java/com/tale/controller/admin/CommentController.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.tale.controller.admin; - -import com.blade.ioc.annotation.Inject; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.StringKit; -import com.blade.mvc.annotation.Controller; -import com.blade.mvc.annotation.JSON; -import com.blade.mvc.annotation.QueryParam; -import com.blade.mvc.annotation.Route; -import com.blade.mvc.http.HttpMethod; -import com.blade.mvc.http.Request; -import com.blade.mvc.view.RestResponse; -import com.tale.controller.BaseController; -import com.tale.dto.Comment; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.model.Comments; -import com.tale.model.Users; -import com.tale.service.CommentsService; -import com.tale.service.SiteService; -import com.tale.utils.TaleUtils; -import com.vdurmont.emoji.EmojiParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Created by biezhi on 2017/2/26. - */ -@Controller("admin/comments") -public class CommentController extends BaseController { - - private static final Logger LOGGER = LoggerFactory.getLogger(CommentController.class); - - @Inject - private CommentsService commentsService; - - @Inject - private SiteService siteService; - - @Route(value = "", method = HttpMethod.GET) - public String index(@QueryParam(value = "page", defaultValue = "1") int page, - @QueryParam(value = "limit", defaultValue = "15") int limit, Request request) { - Users users = this.user(); - Paginator commentsPaginator = commentsService.getComments(new Take(Comments.class).notEq("author_id", users.getUid()).page(page, limit, "coid desc")); - request.attribute("comments", commentsPaginator); - return "admin/comment_list"; - } - - /** - * 删除一条评论 - * @param coid - * @return - */ - @Route(value = "delete", method = HttpMethod.POST) - @JSON - public RestResponse delete(@QueryParam Integer coid) { - try { - Comments comments = commentsService.byId(coid); - if(null == comments){ - return RestResponse.fail("不存在该评论"); - } - commentsService.delete(coid, comments.getCid()); - siteService.cleanCache(Types.C_STATISTICS); - } catch (Exception e) { - String msg = "评论删除失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - - @Route(value = "status", method = HttpMethod.POST) - @JSON - public RestResponse delete(@QueryParam Integer coid, @QueryParam String status) { - try { - Comments comments = new Comments(); - comments.setCoid(coid); - comments.setStatus(status); - commentsService.update(comments); - siteService.cleanCache(Types.C_STATISTICS); - } catch (Exception e) { - String msg = "操作失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - - @Route(value = "", method = HttpMethod.POST) - @JSON - public RestResponse reply(@QueryParam Integer coid, @QueryParam String content, Request request) { - if(null == coid || StringKit.isBlank(content)){ - return RestResponse.fail("请输入完整后评论"); - } - - if(content.length() > 2000){ - return RestResponse.fail("请输入2000个字符以内的回复"); - } - Comments c = commentsService.byId(coid); - if(null == c){ - return RestResponse.fail("不存在该评论"); - } - Users users = this.user(); - content = TaleUtils.cleanXSS(content); - content = EmojiParser.parseToAliases(content); - - Comments comments = new Comments(); - comments.setAuthor(users.getUsername()); - comments.setAuthor_id(users.getUid()); - comments.setCid(c.getCid()); - comments.setIp(request.address()); - comments.setUrl(users.getHome_url()); - comments.setContent(content); - if(StringKit.isNotBlank(users.getEmail())){ - comments.setMail(users.getEmail()); - } else { - comments.setMail("support@tale.me"); - } - comments.setParent(coid); - try { - commentsService.saveComment(comments); - siteService.cleanCache(Types.C_STATISTICS); - return RestResponse.ok(); - } catch (Exception e) { - String msg = "回复失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - } - -} diff --git a/src/main/java/com/tale/controller/admin/IndexController.java b/src/main/java/com/tale/controller/admin/IndexController.java index 53b4b8ba..9a4761a8 100644 --- a/src/main/java/com/tale/controller/admin/IndexController.java +++ b/src/main/java/com/tale/controller/admin/IndexController.java @@ -1,313 +1,136 @@ package com.tale.controller.admin; -import com.blade.Blade; import com.blade.ioc.annotation.Inject; -import com.blade.kit.StringKit; -import com.blade.kit.Tools; -import com.blade.kit.base.Config; -import com.blade.kit.json.JSONKit; -import com.blade.mvc.annotation.Controller; +import com.blade.kit.DateKit; +import com.blade.mvc.annotation.GetRoute; import com.blade.mvc.annotation.JSON; -import com.blade.mvc.annotation.QueryParam; -import com.blade.mvc.annotation.Route; -import com.blade.mvc.http.HttpMethod; +import com.blade.mvc.annotation.Path; +import com.blade.mvc.annotation.PostRoute; import com.blade.mvc.http.Request; -import com.blade.mvc.view.RestResponse; +import com.blade.mvc.multipart.FileItem; +import com.blade.mvc.ui.RestResponse; +import com.tale.annotation.SysLog; +import com.tale.bootstrap.TaleConst; import com.tale.controller.BaseController; -import com.tale.dto.BackResponse; -import com.tale.dto.LogActions; -import com.tale.dto.Statistics; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.ext.Commons; -import com.tale.init.TaleConst; -import com.tale.model.Comments; -import com.tale.model.Contents; -import com.tale.model.Logs; -import com.tale.model.Users; -import com.tale.service.LogService; -import com.tale.service.OptionsService; +import com.tale.model.dto.Statistics; +import com.tale.model.dto.Types; +import com.tale.model.entity.Attach; +import com.tale.model.entity.Comments; +import com.tale.model.entity.Contents; +import com.tale.model.entity.Users; import com.tale.service.SiteService; -import com.tale.service.UsersService; -import jetbrick.util.ShellUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.tale.utils.ImageUtils; +import com.tale.utils.TaleUtils; +import lombok.extern.slf4j.Slf4j; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; + +import static com.tale.bootstrap.TaleConst.CLASSPATH; /** * 后台控制器 * Created by biezhi on 2017/2/21. */ -@Controller("admin") +@Slf4j +@Path("admin") public class IndexController extends BaseController { - private static final Logger LOGGER = LoggerFactory.getLogger(IndexController.class); - - @Inject - private OptionsService optionsService; @Inject private SiteService siteService; - @Inject - private UsersService usersService; - - @Inject - private LogService logService; - /** * 仪表盘 */ - @Route(value = {"/", "index"}, method = HttpMethod.GET) + @GetRoute(value = {"/", "index"}) public String index(Request request) { - List comments = siteService.recentComments(5); - List contents = siteService.getContens(Types.RECENT_ARTICLE, 5); - Statistics statistics = siteService.getStatistics(); - // 取最新的20条日志 - List logs = logService.getLogs(1, 20); + List comments = siteService.recentComments(5); + List contents = siteService.getContens(Types.RECENT_ARTICLE, 5); + Statistics statistics = siteService.getStatistics(); request.attribute("comments", comments); request.attribute("articles", contents); request.attribute("statistics", statistics); - request.attribute("logs", logs); return "admin/index"; } - /** - * 系统设置 - */ - @Route(value = "setting", method = HttpMethod.GET) - public String setting(Request request) { - Map options = optionsService.getOptions(); - request.attribute("options", options); - // 读取主题 - String themesDir = AttachController.CLASSPATH + "templates/themes"; - File[] themesFile = new File(themesDir).listFiles(); - List themems = new ArrayList<>(themesFile.length); - for(File f : themesFile){ - if(f.isDirectory()){ - themems.add(f.getName()); - } - } - request.attribute("themes", themems); - return "admin/setting"; - } - - /** - * 保存系统设置 - */ - @Route(value = "setting", method = HttpMethod.POST) - @JSON - public RestResponse saveSetting(@QueryParam String site_theme, Request request) { - try { - Map querys = request.querys(); - optionsService.saveOptions(querys); - - Config config = new Config(); - config.addAll(optionsService.getOptions()); - TaleConst.OPTIONS = config; - - if (StringKit.isNotBlank(site_theme)) { - BaseController.THEME = "themes/" + site_theme; - } - logService.save(LogActions.SYS_SETTING, JSONKit.toJSONString(querys), request.address(), this.getUid()); - return RestResponse.ok(); - } catch (Exception e) { - String msg = "保存设置失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - } - /** * 个人设置页面 */ - @Route(value = "profile", method = HttpMethod.GET) + @GetRoute("profile") public String profile() { return "admin/profile"; } /** - * 保存个人信息 + * 上传文件接口 */ - @Route(value = "profile", method = HttpMethod.POST) + @SysLog("上传附件") + @PostRoute("api/attach/upload") @JSON - public RestResponse saveProfile(@QueryParam String screen_name, @QueryParam String email, Request request) { - Users users = this.user(); - if (StringKit.isNotBlank(screen_name) && StringKit.isNotBlank(email)) { - Users temp = new Users(); - temp.setUid(users.getUid()); - temp.setScreen_name(screen_name); - temp.setEmail(email); - usersService.update(temp); - logService.save(LogActions.UP_INFO, JSONKit.toJSONString(temp), request.address(), this.getUid()); - } - return RestResponse.ok(); - } + public RestResponse upload(Request request) { - /** - * 修改密码 - */ - @Route(value = "password", method = HttpMethod.POST) - @JSON - public RestResponse upPwd(@QueryParam String old_password, @QueryParam String password, Request request) { - Users users = this.user(); - if (StringKit.isBlank(old_password) || StringKit.isBlank(password)) { - return RestResponse.fail("请确认信息输入完整"); - } + log.info("UPLOAD DIR = {}", TaleUtils.UP_DIR); - if (!users.getPassword().equals(Tools.md5(users.getUsername() + old_password))) { - return RestResponse.fail("旧密码错误"); - } - if (password.length() < 6 || password.length() > 14) { - return RestResponse.fail("请输入6-14位密码"); - } + Users users = this.user(); + Integer uid = users.getUid(); + List errorFiles = new ArrayList<>(); + List urls = new ArrayList<>(); - try { - Users temp = new Users(); - temp.setUid(users.getUid()); - String pwd = Tools.md5(users.getUsername() + password); - temp.setPassword(pwd); - usersService.update(temp); - logService.save(LogActions.UP_PWD, null, request.address(), this.getUid()); - return RestResponse.ok(); - } catch (Exception e){ - String msg = "密码修改失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); + Map fileItems = request.fileItems(); + if (null == fileItems || fileItems.size() == 0) { + return RestResponse.fail("请选择文件上传"); } - } - /** - * 系统备份 - * @return - */ - @Route(value = "backup", method = HttpMethod.POST) - @JSON - public RestResponse backup(@QueryParam String bk_type, @QueryParam String bk_path, - Request request) { - if (StringKit.isBlank(bk_type)) { - return RestResponse.fail("请确认信息输入完整"); - } + fileItems.forEach((fileName, fileItem) -> { + String fname = fileItem.getFileName(); + if ((fileItem.getLength() / 1024) <= TaleConst.MAX_FILE_SIZE) { + String fkey = TaleUtils.getFileKey(fname); - try { - BackResponse backResponse = siteService.backup(bk_type, bk_path, "yyyyMMddHHmm"); - logService.save(LogActions.SYS_BACKUP, null, request.address(), this.getUid()); - return RestResponse.ok(backResponse); - } catch (Exception e){ - String msg = "备份失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - } + String ftype = fileItem.getContentType().contains("image") ? Types.IMAGE : Types.FILE; + String filePath = TaleUtils.UP_DIR + fkey; - /** - * 后台高级选项页面 - * - * @return - */ - @Route(value = "advanced", method = HttpMethod.GET) - public String advanced(Request request){ - Map options = optionsService.getOptions(); - request.attribute("options", options); - return "admin/advanced"; - } - /** - * 保存高级选项设置 - * @return - */ - @Route(value = "advanced", method = HttpMethod.POST) - @JSON - public RestResponse doAdvanced(@QueryParam String cache_key, @QueryParam String block_ips, - @QueryParam String plugin_name, @QueryParam String rewrite_url, - @QueryParam String allow_install){ - // 清除缓存 - if(StringKit.isNotBlank(cache_key)){ - if(cache_key.equals("*")){ - cache.clean(); - } else { - cache.del(cache_key); - } - } - // 要过过滤的黑名单列表 - if(StringKit.isNotBlank(block_ips)){ - optionsService.saveOption(Types.BLOCK_IPS, block_ips); - TaleConst.BLOCK_IPS.addAll(Arrays.asList(StringKit.split(block_ips, ","))); - } else { - optionsService.saveOption(Types.BLOCK_IPS, ""); - TaleConst.BLOCK_IPS.clear(); - } - // 处理卸载插件 - if(StringKit.isNotBlank(plugin_name)){ - String key = "plugin_"; - // 卸载所有插件 - if(!"*".equals(plugin_name)){ - key = "plugin_" + plugin_name; - } else { - optionsService.saveOption(Types.ATTACH_URL, Commons.site_url()); - } - optionsService.deleteOption(key); - } - // 是否允许重新安装 - if(StringKit.isNotBlank(allow_install)){ - optionsService.saveOption("allow_install", allow_install); - TaleConst.OPTIONS.asMap().put("allow_install", allow_install); - } + try { + Files.write(Paths.get(filePath), fileItem.getData()); + if (TaleUtils.isImage(new File(filePath))) { + String newFileName = TaleUtils.getFileName(fkey); + String thumbnailFilePath = TaleUtils.UP_DIR + fkey.replace(newFileName, "thumbnail_" + newFileName); + ImageUtils.cutCenterImage(CLASSPATH + fkey, thumbnailFilePath, 270, 380); - String db_rewrite = TaleConst.OPTIONS.get("rewrite_url", ""); - if(db_rewrite.length() > 0){ - Blade.$().delRoute("/:pagename" + rewrite_url); - Blade.$().routeMatcher().update(); - } + } + } catch (IOException e) { + log.error("", e); + } - if(StringKit.isBlank(rewrite_url)){ - Blade.$().route("/:pagename" + rewrite_url, com.tale.controller.IndexController.class, "page"); - Blade.$().routeMatcher().update(); - } + Attach attach = new Attach(); + attach.setFname(fname); + attach.setAuthorId(uid); + attach.setFkey(fkey); + attach.setFtype(ftype); + attach.setCreated(DateKit.nowUnix()); + attach.save(); - optionsService.saveOption("rewrite_url", rewrite_url); - return RestResponse.ok(); - } + urls.add(attach); + siteService.cleanCache(Types.SYS_STATISTICS); + } else { + Attach attach = new Attach(); + attach.setFname(fname); + errorFiles.add(attach); + } + }); - /** - * 重启系统 - * @param sleep - * @return - */ - @Route(value = "reload", method = HttpMethod.GET) - public void reload(@QueryParam(value = "sleep", defaultValue = "0") int sleep, Request request){ - if(sleep < 0 || sleep > 999){ - sleep = 10; - } - try { - // sh tale.sh reload 10 - String webHome = new File(AttachController.CLASSPATH).getParent(); - String cmd = "sh " + webHome + "/bin tale.sh reload " + sleep; - LOGGER.info("execute shell: {}", cmd); - ShellUtils.shell(cmd); - logService.save(LogActions.RELOAD_SYS, "", request.address(), this.getUid()); - TimeUnit.SECONDS.sleep(sleep); - } catch (Exception e){ - LOGGER.error("重启系统失败", e); + if (errorFiles.size() > 0) { + return RestResponse.fail().payload(errorFiles); } + return RestResponse.ok(urls); } + + } diff --git a/src/main/java/com/tale/controller/admin/LinksController.java b/src/main/java/com/tale/controller/admin/LinksController.java deleted file mode 100644 index 50fcfb48..00000000 --- a/src/main/java/com/tale/controller/admin/LinksController.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.tale.controller.admin; - -import com.blade.ioc.annotation.Inject; -import com.blade.mvc.annotation.Controller; -import com.blade.mvc.annotation.JSON; -import com.blade.mvc.annotation.QueryParam; -import com.blade.mvc.annotation.Route; -import com.blade.mvc.http.HttpMethod; -import com.blade.mvc.http.Request; -import com.blade.mvc.view.RestResponse; -import com.tale.controller.BaseController; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.model.Metas; -import com.tale.service.MetasService; -import com.tale.service.SiteService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * Created by biezhi on 2017/2/21. - */ -@Controller("admin/links") -public class LinksController extends BaseController { - - private static final Logger LOGGER = LoggerFactory.getLogger(LinksController.class); - - @Inject - private MetasService metasService; - - @Inject - private SiteService siteService; - - @Route(value = "", method = HttpMethod.GET) - public String index(Request request) { - List metass = metasService.getMetas(Types.LINK); - request.attribute("links", metass); - return "admin/links"; - } - - @Route(value = "save", method = HttpMethod.POST) - @JSON - public RestResponse saveLink(@QueryParam String title, @QueryParam String url, - @QueryParam String logo, @QueryParam Integer mid, - @QueryParam(value = "sort", defaultValue = "0") int sort) { - try { - Metas metas = new Metas(); - metas.setName(title); - metas.setSlug(url); - metas.setDescription(logo); - metas.setSort(sort); - metas.setType(Types.LINK); - if (null != mid) { - metas.setMid(mid); - metasService.update(metas); - } else { - metasService.saveMeta(metas); - } - siteService.cleanCache(Types.C_STATISTICS); - } catch (Exception e) { - String msg = "友链保存失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - - @Route(value = "delete") - @JSON - public RestResponse delete(@QueryParam int mid) { - try { - metasService.delete(mid); - siteService.cleanCache(Types.C_STATISTICS); - } catch (Exception e) { - String msg = "友链删除失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - -} diff --git a/src/main/java/com/tale/controller/admin/PageController.java b/src/main/java/com/tale/controller/admin/PageController.java deleted file mode 100644 index b480aa31..00000000 --- a/src/main/java/com/tale/controller/admin/PageController.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.tale.controller.admin; - -import com.blade.ioc.annotation.Inject; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.mvc.annotation.*; -import com.blade.mvc.http.HttpMethod; -import com.blade.mvc.http.Request; -import com.blade.mvc.view.RestResponse; -import com.tale.controller.BaseController; -import com.tale.dto.LogActions; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.ext.Commons; -import com.tale.init.TaleConst; -import com.tale.model.Contents; -import com.tale.model.Users; -import com.tale.service.ContentsService; -import com.tale.service.LogService; -import com.tale.service.MetasService; -import com.tale.service.SiteService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Created by biezhi on 2017/2/21. - */ -@Controller("admin/page") -public class PageController extends BaseController { - - private static final Logger LOGGER = LoggerFactory.getLogger(PageController.class); - - @Inject - private ContentsService contentsService; - - @Inject - private MetasService metasService; - - @Inject - private LogService logService; - - @Inject - private SiteService siteService; - - @Route(value = "", method = HttpMethod.GET) - public String index(Request request) { - Paginator contentsPaginator = contentsService.getArticles(new Take(Contents.class).eq("type", Types.PAGE).page(1, TaleConst.MAX_POSTS, "created desc")); - request.attribute("articles", contentsPaginator); - return "admin/page_list"; - } - - @Route(value = "new", method = HttpMethod.GET) - public String newPage(Request request) { - request.attribute(Types.ATTACH_URL, Commons.site_option(Types.ATTACH_URL, Commons.site_url())); - return "admin/page_edit"; - } - - @Route(value = "/:cid", method = HttpMethod.GET) - public String editPage(@PathParam String cid, Request request) { - Contents contents = contentsService.getContents(cid); - request.attribute("contents", contents); - request.attribute(Types.ATTACH_URL, Commons.site_option(Types.ATTACH_URL, Commons.site_url())); - return "admin/page_edit"; - } - - @Route(value = "publish", method = HttpMethod.POST) - @JSON - public RestResponse publishPage(@QueryParam String title, @QueryParam String content, - @QueryParam String status, @QueryParam String slug, - @QueryParam String fmt_type, - @QueryParam Boolean allow_comment) { - - Users users = this.user(); - Contents contents = new Contents(); - contents.setTitle(title); - contents.setContent(content); - contents.setStatus(status); - contents.setSlug(slug); - contents.setFmt_type(fmt_type); - contents.setType(Types.PAGE); - contents.setAllow_comment(allow_comment); - contents.setAllow_ping(true); - contents.setAuthor_id(users.getUid()); - - try { - contentsService.publish(contents); - siteService.cleanCache(Types.C_STATISTICS); - } catch (Exception e) { - String msg = "页面发布失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - - @Route(value = "modify", method = HttpMethod.POST) - @JSON - public RestResponse modifyArticle(@QueryParam Integer cid, @QueryParam String title, - @QueryParam String content,@QueryParam String fmt_type, - @QueryParam String status, @QueryParam String slug, - @QueryParam Boolean allow_comment) { - - Users users = this.user(); - Contents contents = new Contents(); - contents.setCid(cid); - contents.setTitle(title); - contents.setContent(content); - contents.setStatus(status); - contents.setFmt_type(fmt_type); - contents.setSlug(slug); - contents.setType(Types.PAGE); - contents.setAllow_comment(allow_comment); - contents.setAllow_ping(true); - contents.setAuthor_id(users.getUid()); - try { - contentsService.updateArticle(contents); - } catch (Exception e) { - String msg = "页面编辑失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } - - @Route(value = "delete") - @JSON - public RestResponse delete(@QueryParam int cid, Request request) { - try { - contentsService.delete(cid); - siteService.cleanCache(Types.C_STATISTICS); - logService.save(LogActions.DEL_PAGE, cid+"", request.address(), this.getUid()); - } catch (Exception e) { - String msg = "页面删除失败"; - if (e instanceof TipException) { - msg = e.getMessage(); - } else { - LOGGER.error(msg, e); - } - return RestResponse.fail(msg); - } - return RestResponse.ok(); - } -} diff --git a/src/main/java/com/tale/controller/admin/PagesController.java b/src/main/java/com/tale/controller/admin/PagesController.java new file mode 100644 index 00000000..45e9fe0c --- /dev/null +++ b/src/main/java/com/tale/controller/admin/PagesController.java @@ -0,0 +1,144 @@ +package com.tale.controller.admin; + +import com.blade.ioc.annotation.Inject; +import com.blade.kit.JsonKit; +import com.blade.kit.StringKit; +import com.blade.mvc.Const; +import com.blade.mvc.annotation.GetRoute; +import com.blade.mvc.annotation.Param; +import com.blade.mvc.annotation.Path; +import com.blade.mvc.annotation.PathParam; +import com.blade.mvc.http.Request; +import com.blade.mvc.http.Response; +import com.tale.controller.BaseController; +import com.tale.extension.Commons; +import com.tale.service.ContentsService; +import com.tale.service.MetasService; +import com.tale.service.OptionsService; +import com.tale.service.SiteService; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author biezhi + * @date 2018/6/5 + */ +@Slf4j +@Path("admin") +public class PagesController extends BaseController { + + @Inject + private ContentsService contentsService; + + @Inject + private MetasService metasService; + + @Inject + private OptionsService optionsService; + + @Inject + private SiteService siteService; + + @GetRoute("/:page") + public String commonPage(@PathParam String page) { + return "admin/" + page + ".html"; + } + + @GetRoute("/:module/:page") + public String commonPage(@PathParam String module, @PathParam String page) { + return "admin/" + module + "/" + page + ".html"; + } + + @GetRoute("/article/edit/:cid") + public String editArticle(@PathParam String cid) { + return "admin/article/edit.html"; + } + + @GetRoute("/page/edit/:cid") + public String editPage(@PathParam String cid) { + return "admin/page/edit.html"; + } + + @GetRoute("login") + public String login(Response response) { + if (null != this.user()) { + response.redirect("/admin/index"); + return null; + } + return "admin/login"; + } + + @GetRoute("template") + public String index(Request request) { + String themePath = Const.CLASSPATH + File.separatorChar + "templates" + File.separatorChar + "themes" + File.separatorChar + Commons.site_theme(); + try { + List files = Files.list(Paths.get(themePath)) + .map(path -> path.getFileName().toString()) + .filter(path -> path.endsWith(".html")) + .collect(Collectors.toList()); + + List partial = Files.list(Paths.get(themePath + File.separatorChar + "partial")) + .map(path -> path.getFileName().toString()) + .filter(path -> path.endsWith(".html")) + .map(fileName -> "partial/" + fileName) + .collect(Collectors.toList()); + + List statics = Files.list(Paths.get(themePath + File.separatorChar + "static")) + .map(path -> path.getFileName().toString()) + .filter(path -> path.endsWith(".js") || path.endsWith(".css")) + .map(fileName -> "static/" + fileName) + .collect(Collectors.toList()); + + files.addAll(partial); + files.addAll(statics); + + request.attribute("tpls", files); + } catch (IOException e) { + log.error("找不到模板路径"); + } + return "admin/tpl_list"; + } + + @GetRoute("template/content") + public void getContent(@Param String fileName, Response response) { + try { + String themePath = Const.CLASSPATH + File.separatorChar + "templates" + File.separatorChar + "themes" + File.separatorChar + Commons.site_theme(); + String filePath = themePath + File.separatorChar + fileName; + String content = Files.readAllLines(Paths.get(filePath)).stream().collect(Collectors.joining("\n")); + response.text(content); + } catch (IOException e) { + log.error("获取模板文件失败", e); + } + } + + /** + * 主题设置页面 + */ + @GetRoute("theme/setting") + public String setting(Request request) { + String currentTheme = Commons.site_theme(); + String key = "theme_" + currentTheme + "_options"; + + String option = optionsService.getOption(key); + Map map = new HashMap<>(); + try { + if (StringKit.isNotBlank(option)) { + map = (Map) JsonKit.toAson(option); + } + request.attribute("options", map); + } catch (Exception e) { + log.error("解析主题设置出现异常", e); + } + request.attribute("theme_options", map); + return this.render("setting"); + } + +} diff --git a/src/main/java/com/tale/controller/admin/SystemController.java b/src/main/java/com/tale/controller/admin/SystemController.java new file mode 100644 index 00000000..3b063033 --- /dev/null +++ b/src/main/java/com/tale/controller/admin/SystemController.java @@ -0,0 +1,68 @@ +package com.tale.controller.admin; + +import com.blade.ioc.annotation.Inject; +import com.blade.kit.EncryptKit; +import com.blade.kit.StringKit; +import com.blade.mvc.annotation.Param; +import com.blade.mvc.annotation.Path; +import com.blade.mvc.annotation.PostRoute; +import com.blade.mvc.ui.RestResponse; +import com.tale.annotation.SysLog; +import com.tale.bootstrap.TaleConst; +import com.tale.controller.BaseController; +import com.tale.model.entity.Users; +import com.tale.service.OptionsService; +import com.tale.service.SiteService; +import lombok.extern.slf4j.Slf4j; + +/** + * 后台控制器 + * Created by biezhi on 2017/2/21. + */ +@Slf4j +@Path(value = "admin", restful = true) +public class SystemController extends BaseController { + + @Inject + private OptionsService optionsService; + + @Inject + private SiteService siteService; + + @SysLog("保存个人信息") + @PostRoute("profile") + public RestResponse saveProfile(@Param String screenName, @Param String email) { + Users users = this.user(); + if (StringKit.isNotBlank(screenName) && StringKit.isNotBlank(email)) { + Users temp = new Users(); + temp.setScreenName(screenName); + temp.setEmail(email); + temp.updateById(users.getUid()); + } + return RestResponse.ok(); + } + + @SysLog("修改登录密码") + @PostRoute("password") + public RestResponse upPwd(@Param String old_password, @Param String password) { + Users users = this.user(); + if (StringKit.isBlank(old_password) || StringKit.isBlank(password)) { + return RestResponse.fail("请确认信息输入完整"); + } + + if (!users.getPassword().equals(EncryptKit.md5(users.getUsername() + old_password))) { + return RestResponse.fail("旧密码错误"); + } + if (password.length() < 6 || password.length() > 14) { + return RestResponse.fail("请输入6-14位密码"); + } + + Users temp = new Users(); + String pwd = EncryptKit.md5(users.getUsername() + password); + temp.setPassword(pwd); + temp.updateById(users.getUid()); + optionsService.deleteOption(TaleConst.OPTION_SAFE_REMEMBER_ME); + return RestResponse.ok(); + } + +} diff --git a/src/main/java/com/tale/dto/Archive.java b/src/main/java/com/tale/dto/Archive.java deleted file mode 100644 index 59f9bc30..00000000 --- a/src/main/java/com/tale/dto/Archive.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.tale.dto; - -import com.tale.model.Contents; - -import java.io.Serializable; -import java.util.Date; -import java.util.List; - -/** - * Created by biezhi on 2017/2/23. - */ -public class Archive implements Serializable { - - private String date_str; - private Date date; - private String count; - private List articles; - - public String getDate_str() { - return date_str; - } - - public void setDate_str(String date_str) { - this.date_str = date_str; - } - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - - public String getCount() { - return count; - } - - public void setCount(String count) { - this.count = count; - } - - public List getArticles() { - return articles; - } - - public void setArticles(List articles) { - this.articles = articles; - } - -} diff --git a/src/main/java/com/tale/dto/BackResponse.java b/src/main/java/com/tale/dto/BackResponse.java deleted file mode 100644 index 435a5b8a..00000000 --- a/src/main/java/com/tale/dto/BackResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.tale.dto; - -import java.io.Serializable; - -/** - * Created by biezhi on 2017/2/25. - */ -public class BackResponse implements Serializable { - - private String attach_path; - private String theme_path; - private String sql_path; - - public String getAttach_path() { - return attach_path; - } - - public void setAttach_path(String attach_path) { - this.attach_path = attach_path; - } - - public String getTheme_path() { - return theme_path; - } - - public void setTheme_path(String theme_path) { - this.theme_path = theme_path; - } - - public String getSql_path() { - return sql_path; - } - - public void setSql_path(String sql_path) { - this.sql_path = sql_path; - } -} diff --git a/src/main/java/com/tale/dto/LogActions.java b/src/main/java/com/tale/dto/LogActions.java deleted file mode 100644 index ae2aaae5..00000000 --- a/src/main/java/com/tale/dto/LogActions.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.tale.dto; - -/** - * 日志操作常量 - * - * Created by biezhi on 2017/2/26. - */ -public interface LogActions { - - String LOGIN = "登录后台"; - - String UP_PWD = "修改密码"; - - String UP_INFO = "修改个人信息"; - - String DEL_ARTICLE = "删除文章"; - - String DEL_PAGE = "删除页面"; - - String SYS_BACKUP = "系统备份"; - - String SYS_SETTING = "保存系统设置"; - - String INIT_SITE = "初始化站点"; - - String RELOAD_SYS = "重启系统"; - -} diff --git a/src/main/java/com/tale/dto/MetaDto.java b/src/main/java/com/tale/dto/MetaDto.java deleted file mode 100644 index 34339097..00000000 --- a/src/main/java/com/tale/dto/MetaDto.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.tale.dto; - -import com.tale.model.Metas; - -/** - * Created by biezhi on 2017/2/22. - */ -public class MetaDto extends Metas { - - private int count; - - public int getCount() { - return count; - } - - public void setCount(int count) { - this.count = count; - } -} diff --git a/src/main/java/com/tale/dto/PluginMenu.java b/src/main/java/com/tale/dto/PluginMenu.java deleted file mode 100644 index 28095325..00000000 --- a/src/main/java/com/tale/dto/PluginMenu.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.tale.dto; - -/** - * Created by biezhi on 2017/3/1. - */ -public class PluginMenu { - - private String name; - private String slug; - private String icon; - - public PluginMenu(String name, String slug, String icon) { - this.name = name; - this.slug = slug; - this.icon = icon; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getSlug() { - return slug; - } - - public void setSlug(String slug) { - this.slug = slug; - } - - public String getIcon() { - return icon; - } - - public void setIcon(String icon) { - this.icon = icon; - } -} diff --git a/src/main/java/com/tale/dto/Statistics.java b/src/main/java/com/tale/dto/Statistics.java deleted file mode 100644 index ef309155..00000000 --- a/src/main/java/com/tale/dto/Statistics.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.tale.dto; - -import java.io.Serializable; - -/** - * 后台统计对象 - *

- * Created by biezhi on 2017/2/24. - */ -public class Statistics implements Serializable { - - // 文章数 - private int articles; - // 页面数 - private int pages; - // 评论数 - private int comments; - // 分类数 - private int categories; - // 标签数 - private int tags; - // 链接数 - private int links; - // 附件数 - private int attachs; - - public int getArticles() { - return articles; - } - - public void setArticles(int articles) { - this.articles = articles; - } - - public int getComments() { - return comments; - } - - public void setComments(int comments) { - this.comments = comments; - } - - public int getLinks() { - return links; - } - - public void setLinks(int links) { - this.links = links; - } - - public int getAttachs() { - return attachs; - } - - public void setAttachs(int attachs) { - this.attachs = attachs; - } - - public int getCategories() { - return categories; - } - - public void setCategories(int categories) { - this.categories = categories; - } - - public int getTags() { - return tags; - } - - public void setTags(int tags) { - this.tags = tags; - } - - public int getPages() { - return pages; - } - - public void setPages(int pages) { - this.pages = pages; - } -} diff --git a/src/main/java/com/tale/exception/TipException.java b/src/main/java/com/tale/exception/TipException.java deleted file mode 100644 index d086021b..00000000 --- a/src/main/java/com/tale/exception/TipException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.tale.exception; - -public class TipException extends RuntimeException { - - public TipException() { - } - - public TipException(String message) { - super(message); - } - - public TipException(String message, Throwable cause) { - super(message, cause); - } - - public TipException(Throwable cause) { - super(cause); - } - -} diff --git a/src/main/java/com/tale/ext/AdminCommons.java b/src/main/java/com/tale/ext/AdminCommons.java deleted file mode 100644 index 38894687..00000000 --- a/src/main/java/com/tale/ext/AdminCommons.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.tale.ext; - -import com.blade.kit.StringKit; -import com.blade.kit.Tools; -import com.tale.model.Metas; - -/** - * 后台公共函数 - *

- * Created by biezhi on 2017/2/21. - */ -public final class AdminCommons { - - /** - * 判断category和cat的交集 - * - * @param cats - * @return - */ - public static boolean exist_cat(Metas category, String cats) { - String[] arr = StringKit.split(cats, ","); - if (null != arr && arr.length > 0) { - for (String c : arr) { - if (c.trim().equals(category.getName())) { - return true; - } - } - } - return false; - } - - private static final String[] COLORS = {"default", "primary", "success", "info", "warning", "danger", "inverse", "purple", "pink"}; - - public static String rand_color() { - int r = Tools.rand(0, COLORS.length - 1); - return COLORS[r]; - } - -} diff --git a/src/main/java/com/tale/extension/AdminCommons.java b/src/main/java/com/tale/extension/AdminCommons.java new file mode 100644 index 00000000..b4e35edd --- /dev/null +++ b/src/main/java/com/tale/extension/AdminCommons.java @@ -0,0 +1,29 @@ +package com.tale.extension; + +import com.tale.bootstrap.TaleConst; +import com.tale.model.dto.Types; + +/** + * 后台公共函数 + *

+ * Created by biezhi on 2017/2/21. + */ +public final class AdminCommons { + + public static String attachURL(){ + return Commons.site_option(Types.ATTACH_URL, Commons.site_url()); + } + + public static int maxFileSize(){ + return TaleConst.MAX_FILE_SIZE / 1024; + } + + public static String cdnURL(){ + return Commons.site_option(Types.CDN_URL, "/static/admin"); + } + + public static String siteTheme() { + return Commons.site_theme(); + } + +} diff --git a/src/main/java/com/tale/ext/Commons.java b/src/main/java/com/tale/extension/Commons.java similarity index 70% rename from src/main/java/com/tale/ext/Commons.java rename to src/main/java/com/tale/extension/Commons.java index 21e02f6a..132a5f3b 100644 --- a/src/main/java/com/tale/ext/Commons.java +++ b/src/main/java/com/tale/extension/Commons.java @@ -1,17 +1,14 @@ -package com.tale.ext; +package com.tale.extension; -import com.blade.jdbc.model.Paginator; import com.blade.kit.*; +import com.tale.bootstrap.TaleConst; import com.tale.controller.BaseController; -import com.tale.init.TaleConst; import com.tale.service.SiteService; import com.tale.utils.TaleUtils; import com.vdurmont.emoji.EmojiParser; +import io.github.biezhi.anima.page.Page; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -22,16 +19,9 @@ */ public final class Commons { - private static SiteService siteService; - - private static final List EMPTY = new ArrayList(0); - - private static final Random rand = new Random(); - private static final String TEMPLATES = "/templates/"; public static void setSiteService(SiteService ss) { - siteService = ss; Theme.setSiteService(ss); } @@ -41,8 +31,18 @@ public static void setSiteService(SiteService ss) { * @param paginator * @return */ - public static boolean is_empty(Paginator paginator) { - return null == paginator || CollectionKit.isEmpty(paginator.getList()); + public static boolean is_empty(Page paginator) { + return null == paginator || BladeKit.isEmpty(paginator.getRows()); + } + + /** + * 判断字符串不为空 + * + * @param str + * @return + */ + public static boolean not_empty(String str) { + return StringKit.isNotBlank(str); } /** @@ -54,6 +54,15 @@ public static String site_url() { return site_url(""); } + /** + * 返回当前主题名称 + * + * @return + */ + public static String site_theme() { + return site_option("site_theme", "default"); + } + /** * 返回网站链接下的全址 * @@ -73,6 +82,24 @@ public static String site_title() { return site_option("site_title"); } + /** + * 网站子标题 + * + * @return + */ + public static String site_subtitle() { + return site_option("site_subtitle"); + } + + /** + * 是否允许使用云公共静态资源 + * + * @return + */ + public static String allow_cloud_CDN() { + return site_option("allow_cloud_CDN"); + } + /** * 网站配置项 * @@ -99,9 +126,10 @@ public static String site_option(String key, String defalutValue) { /** * 返回站点设置的描述信息 + * * @return */ - public static String site_description(){ + public static String site_description() { return site_option("site_description"); } @@ -146,11 +174,11 @@ public static String theme_url(String sub) { * @return */ public static String gravatar(String email) { - String avatarUrl = "https://secure.gravatar.com/avatar"; + String avatarUrl = "https://cn.gravatar.com/avatar"; if (StringKit.isBlank(email)) { return avatarUrl; } - String hash = Tools.md5(email.trim().toLowerCase()); + String hash = EncryptKit.md5(email.trim().toLowerCase()); return avatarUrl + "/" + hash; } @@ -166,12 +194,13 @@ public static String fmtdate(Integer unixTime) { /** * 格式化日期 + * * @param date * @param fmt * @return */ public static String fmtdate(Date date, String fmt) { - return DateKit.dateFormat(date, fmt); + return DateKit.toString(date, fmt); } /** @@ -183,33 +212,35 @@ public static String fmtdate(Date date, String fmt) { */ public static String fmtdate(Integer unixTime, String patten) { if (null != unixTime && StringKit.isNotBlank(patten)) { - return DateKit.formatDateByUnixTime(unixTime, patten); + return DateKit.toString(unixTime, patten); } return ""; } /** * 获取随机数 + * * @param max * @param str * @return */ - public static String random(int max, String str){ + public static String random(int max, String str) { return UUID.random(1, max) + str; } /** * An :grinning:awesome :smiley:string 😄with a few :wink:emojis! - * + *

* 这种格式的字符转换为emoji表情 * * @param value * @return */ - public static String emoji(String value){ + public static String emoji(String value) { return EmojiParser.parseToUnicode(value); } + private static final Pattern SRC_PATTERN = Pattern.compile("src\\s*=\\s*\'?\"?(.*?)(\'|\"|>|\\s+)"); /** * 获取文章第一张图片 * @@ -218,14 +249,14 @@ public static String emoji(String value){ public static String show_thumb(String content) { content = TaleUtils.mdToHtml(content); if (content.contains("|\\s+)").matcher(img); + Matcher m = SRC_PATTERN.matcher(img); if (m.find()) { return m.group(1); } diff --git a/src/main/java/com/tale/ext/JetTag.java b/src/main/java/com/tale/extension/JetTag.java similarity index 94% rename from src/main/java/com/tale/ext/JetTag.java rename to src/main/java/com/tale/extension/JetTag.java index 563d2f24..c8a0aea5 100644 --- a/src/main/java/com/tale/ext/JetTag.java +++ b/src/main/java/com/tale/extension/JetTag.java @@ -1,4 +1,4 @@ -package com.tale.ext; +package com.tale.extension; import com.blade.kit.StringKit; import jetbrick.template.runtime.JetTagContext; diff --git a/src/main/java/com/tale/ext/Theme.java b/src/main/java/com/tale/extension/Theme.java similarity index 54% rename from src/main/java/com/tale/ext/Theme.java rename to src/main/java/com/tale/extension/Theme.java index 69503ef8..9aac7623 100644 --- a/src/main/java/com/tale/ext/Theme.java +++ b/src/main/java/com/tale/extension/Theme.java @@ -1,23 +1,31 @@ -package com.tale.ext; +package com.tale.extension; -import com.blade.jdbc.model.Paginator; +import com.blade.kit.JsonKit; import com.blade.kit.StringKit; -import com.tale.controller.BaseController; -import com.tale.dto.Comment; -import com.tale.dto.MetaDto; -import com.tale.dto.Types; -import com.tale.init.TaleConst; -import com.tale.model.Comments; -import com.tale.model.Contents; +import com.blade.kit.json.Ason; +import com.blade.mvc.WebContext; +import com.blade.mvc.http.Request; +import com.tale.bootstrap.TaleConst; +import com.tale.model.dto.Comment; +import com.tale.model.dto.Types; +import com.tale.model.entity.Comments; +import com.tale.model.entity.Contents; +import com.tale.model.entity.Metas; import com.tale.service.SiteService; import com.tale.utils.TaleUtils; +import io.github.biezhi.anima.enums.OrderBy; +import io.github.biezhi.anima.page.Page; import jetbrick.template.runtime.InterpretContext; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import static io.github.biezhi.anima.Anima.select; + /** * 主题函数 *

@@ -27,20 +35,19 @@ public final class Theme { private static SiteService siteService; - public static final List EMPTY = new ArrayList(0); - public static void setSiteService(SiteService ss) { siteService = ss; } /** * 获取header keywords + * * @return */ - public static String meta_keywords(){ - InterpretContext ctx = InterpretContext.current(); - Object value = ctx.getValueStack().getValue("keywords"); - if(null != value){ + public static String meta_keywords() { + InterpretContext ctx = InterpretContext.current(); + Object value = ctx.getValueStack().getValue("keywords"); + if (null != value) { return value.toString(); } return Commons.site_option("site_keywords"); @@ -48,12 +55,13 @@ public static String meta_keywords(){ /** * 获取header description + * * @return */ - public static String meta_description(){ - InterpretContext ctx = InterpretContext.current(); - Object value = ctx.getValueStack().getValue("description"); - if(null != value){ + public static String meta_description() { + InterpretContext ctx = InterpretContext.current(); + Object value = ctx.getValueStack().getValue("description"); + if (null != value) { return value.toString(); } return Commons.site_option("site_description"); @@ -61,17 +69,18 @@ public static String meta_description(){ /** * header title + * * @return */ - public static String head_title(){ - InterpretContext ctx = InterpretContext.current(); - Object value = ctx.getValueStack().getValue("title"); + public static String head_title() { + InterpretContext ctx = InterpretContext.current(); + Object value = ctx.getValueStack().getValue("title"); - String p = "首页"; - if(null != value){ - p = value.toString(); + String p = ""; + if (null != value) { + p = value.toString() + " - "; } - return p + " - " + Commons.site_option("site_title", "Tale 博客"); + return p + Commons.site_option("site_title", "Tale 博客"); } /** @@ -156,6 +165,34 @@ public static String show_categories() throws UnsupportedEncodingException { return ""; } + /** + * 当前文章的分类列表 + * + * @return + * @since b1.3.0 + */ + public static List category_list() { + Contents contents = current_article(); + if (null != contents && StringKit.isNotBlank(contents.getCategories())) { + return Arrays.asList(contents.getCategories().split(",")); + } + return Collections.emptyList(); + } + + /** + * 当前文章的标签列表 + * + * @return + * @since b1.3.0 + */ + public static List tag_list() { + Contents contents = current_article(); + if (null != contents && StringKit.isNotBlank(contents.getTags())) { + return Arrays.asList(contents.getTags().split(",")); + } + return Collections.emptyList(); + } + /** * 显示分类 * @@ -164,7 +201,7 @@ public static String show_categories() throws UnsupportedEncodingException { */ public static String show_categories(String categories) throws UnsupportedEncodingException { if (StringKit.isNotBlank(categories)) { - String[] arr = categories.split(","); + String[] arr = categories.split(","); StringBuffer sbuf = new StringBuffer(); for (String c : arr) { sbuf.append("" + c + ""); @@ -183,7 +220,7 @@ public static String show_categories(String categories) throws UnsupportedEncodi public static String show_tags(String split) throws UnsupportedEncodingException { Contents contents = current_article(); if (StringKit.isNotBlank(contents.getTags())) { - String[] arr = contents.getTags().split(","); + String[] arr = contents.getTags().split(","); StringBuffer sbuf = new StringBuffer(); for (String c : arr) { sbuf.append(split).append("" + c + ""); @@ -195,9 +232,10 @@ public static String show_tags(String split) throws UnsupportedEncodingException /** * 显示文章浏览量 + * * @return */ - public static String views(){ + public static String views() { Contents contents = current_article(); return null != contents ? contents.getHits().toString() : "0"; } @@ -223,15 +261,17 @@ public static String show_content() { /** * 获取文章摘要 + * * @param len * @return */ - public static String excerpt(int len){ + public static String excerpt(int len) { return intro(len); } /** * 获取文章摘要 + * * @param len * @return */ @@ -243,6 +283,25 @@ public static String intro(int len) { return ""; } + /** + * 截取文章摘要(返回HTML) + * + * @param value 文章内容 + * @return 转换 markdown 为 html + */ + public static String intro(String value) { + if (StringKit.isBlank(value)) { + return null; + } + int pos = value.indexOf(""); + if (pos != -1) { + String html = value.substring(0, pos); + return TaleUtils.mdToHtml(html); + } else { + return TaleUtils.mdToHtml(value); + } + } + /** * 截取文章摘要 * @@ -287,15 +346,17 @@ public static String show_thumb(Contents contents) { if (null == contents) { return ""; } - if(StringKit.isNotBlank(contents.getThumb_img())){ - return contents.getThumb_img(); + if (StringKit.isNotBlank(contents.getThumbImg())) { + String newFileName = TaleUtils.getFileName(contents.getThumbImg()); + String thumbnailImgUrl = (contents.getThumbImg()).replace(newFileName, "thumbnail_" + newFileName); + return thumbnailImgUrl; } String content = article(contents.getContent()); - String img = Commons.show_thumb(content); + String img = Commons.show_thumb(content); if (StringKit.isNotBlank(img)) { return img; } - int cid = contents.getCid(); + int cid = contents.getCid(); int size = cid % 20; size = size == 0 ? 1 : size; return "/templates/themes/default/static/img/rand/" + size + ".jpg"; @@ -303,11 +364,12 @@ public static String show_thumb(Contents contents) { /** * 获取当前文章的下一篇 + * * @return */ - public static Contents article_next(){ + public static Contents article_next() { Contents cur = current_article(); - return null != cur ? siteService.getNhContent(Types.NEXT, cur.getCid()) : null; + return null != cur ? siteService.getNhContent(Types.NEXT, cur.getCreated()) : null; } /** @@ -315,9 +377,63 @@ public static Contents article_next(){ * * @return */ - public static Contents article_prev(){ + public static Contents article_prev() { Contents cur = current_article(); - return null != cur ? siteService.getNhContent(Types.PREV, cur.getCid()) : null; + return null != cur ? siteService.getNhContent(Types.PREV, cur.getCreated()) : null; + } + + /** + * 当前文章的下一篇文章链接 + * + * @return + */ + public static String theNext() { + Contents contents = article_next(); + if (null != contents) { + return theNext(title(contents)); + } + return ""; + } + + /** + * 当前文章的下一篇文章链接 + * + * @param title 文章标题 + * @return + */ + public static String theNext(String title) { + Contents contents = article_next(); + if (null != contents) { + return "" + title + ""; + } + return ""; + } + + /** + * 当前文章的下一篇文章链接 + * + * @return + */ + public static String thePrev() { + Contents contents = article_prev(); + if (null != contents) { + return thePrev(title(contents)); + } + return ""; + } + + /** + * 当前文章的下一篇文章链接 + * + * @param title 文章标题 + * @return + */ + public static String thePrev(String title) { + Contents contents = article_prev(); + if (null != contents) { + return "" + title + ""; + } + return ""; } /** @@ -328,19 +444,20 @@ public static Contents article_prev(){ */ public static List recent_articles(int limit) { if (null == siteService) { - return EMPTY; + return new ArrayList<>(0); } return siteService.getContens(Types.RECENT_ARTICLE, limit); } /** * 随机获取文章 + * * @param limit * @return */ public static List rand_articles(int limit) { if (null == siteService) { - return EMPTY; + return new ArrayList<>(0); } return siteService.getContens(Types.RANDOM_ARTICLE, limit); } @@ -353,7 +470,7 @@ public static List rand_articles(int limit) { */ public static List recent_comments(int limit) { if (null == siteService) { - return EMPTY; + return new ArrayList<>(0); } return siteService.recentComments(limit); } @@ -363,21 +480,22 @@ public static List recent_comments(int limit) { * * @return */ - public static List categories(int limit) { + public static List categories(int limit) { if (null == siteService) { - return EMPTY; + return new ArrayList<>(0); } return siteService.getMetas(Types.RECENT_META, Types.CATEGORY, limit); } /** * 随机获取limit个分类 + * * @param limit * @return */ - public static List rand_categories(int limit) { + public static List rand_categories(int limit) { if (null == siteService) { - return EMPTY; + return new ArrayList<>(0); } return siteService.getMetas(Types.RANDOM_META, Types.CATEGORY, limit); } @@ -387,7 +505,7 @@ public static List rand_categories(int limit) { * * @return */ - public static List categories() { + public static List categories() { return categories(TaleConst.MAX_POSTS); } @@ -396,21 +514,22 @@ public static List categories() { * * @return */ - public static List tags(int limit) { + public static List tags(int limit) { if (null == siteService) { - return EMPTY; + return new ArrayList<>(0); } return siteService.getMetas(Types.RECENT_META, Types.TAG, limit); } /** * 随机获取limit个标签 + * * @param limit * @return */ - public static List rand_tags(int limit) { + public static List rand_tags(int limit) { if (null == siteService) { - return EMPTY; + return new ArrayList<>(0); } return siteService.getMetas(Types.RANDOM_META, Types.TAG, limit); } @@ -420,7 +539,7 @@ public static List rand_tags(int limit) { * * @return */ - public static List tags() { + public static List tags() { return tags(TaleConst.MAX_POSTS); } @@ -477,6 +596,7 @@ public static String title() { /** * 返回文章标题 + * * @param contents * @return */ @@ -484,23 +604,15 @@ public static String title(Contents contents) { return null != contents ? contents.getTitle() : Commons.site_title(); } - /** - * 返回所有友链 - * @return - */ - public static List links(){ - List links = siteService.getMetas(Types.RECENT_META, Types.LINK, TaleConst.MAX_POSTS); - return links; - } - /** * 返回社交账号链接 - * @param socialtype + * + * @param type * @return */ - public static String social_link(String socialtype) { - String id = Commons.site_option("social_" + socialtype); - switch (socialtype){ + public static String social_link(String type) { + String id = Commons.site_option("social_" + type); + switch (type) { case "github": return "https://github.com/" + id; case "weibo": @@ -509,27 +621,70 @@ public static String social_link(String socialtype) { return "https://twitter.com/" + id; case "zhihu": return "https://www.zhihu.com/people/" + id; + default: + return null; } - return ""; } /** * 获取当前文章/页面的评论 + * * @param limit * @return */ - public static Paginator comments(int limit){ + public static Page comments(int limit) { Contents contents = current_article(); - if(null == contents){ - return new Paginator<>(0,limit); + if (null == contents) { + return new Page<>(); } - InterpretContext ctx = InterpretContext.current(); - Object value = ctx.getValueStack().getValue("cp"); - int page = 1; + InterpretContext ctx = InterpretContext.current(); + Object value = ctx.getValueStack().getValue("cp"); + int page = 1; if (null != value) { page = (int) value; } - return siteService.getComments(contents.getCid(), page, limit); + Page comments = siteService.getComments(contents.getCid(), page, limit); + return comments; + } + + /** + * 获取当前文章/页面的评论数量 + * + * @return 当前页面的评论数量 + */ + public static long commentsCount() { + Contents contents = current_article(); + if (null == contents) { + return 0; + } + return siteService.getCommentCount(contents.getCid()); + } + + /** + * 分页 + * + * @param limit + * @return + */ + public static Page articles(int limit) { + Request request = WebContext.request(); + Integer page = request.attribute("page_num"); + page = null == page ? request.queryInt("page", 1) : page; + page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; + + Page articles = select().from(Contents.class) + .where(Contents::getType, Types.ARTICLE) + .and("status", Types.PUBLISH) + .order(Contents::getCreated, OrderBy.DESC) + .page(page, limit); + + request.attribute("articles", articles); + if (page > 1) { + WebContext.request().attribute("title", "第" + page + "页"); + } + request.attribute("is_home", true); + request.attribute("page_prefix", "/page"); + return articles; } /** @@ -538,8 +693,8 @@ public static Paginator comments(int limit){ * @return */ private static Contents current_article() { - InterpretContext ctx = InterpretContext.current(); - Object value = ctx.getValueStack().getValue("article"); + InterpretContext ctx = InterpretContext.current(); + Object value = ctx.getValueStack().getValue("article"); if (null != value) { return (Contents) value; } @@ -553,12 +708,83 @@ private static Contents current_article() { * @param value 评论组装文本 * @return */ - public static String comments_num(String noComment, String value){ + public static String comments_num(String noComment, String value) { Contents contents = current_article(); - if(null == contents){ + if (null == contents) { return noComment; } - return contents.getComments_num() > 0 ? String.format(value, contents.getComments_num()) : noComment; + return contents.getCommentsNum() > 0 ? String.format(value, contents.getCommentsNum()) : noComment; + } + + /** + * 返回主题设置选项 + * + * @param key + * @return + */ + public static String theme_option(String key, String defaultValue) { + String option = theme_option(key); + if (StringKit.isBlank(option)) { + return defaultValue; + } + return option; + } + + /** + * 返回主题设置选项 + * + * @param key + * @return + */ + public static String theme_option(String key) { + String theme = Commons.site_theme(); + return TaleConst.OPTIONS.get("theme_" + theme + "_options") + .filter(StringKit::isNotBlank) + .map((String json) -> { + Ason ason = JsonKit.toAson(json); + if (!ason.containsKey(key)) { + return ""; + } + return ason.getString(key); + }) + .orElse(""); } + /** + * 返回是否是某个页面 + * + * @param pageName + * @return + */ + public static boolean is_slug(String pageName) { + Contents contents = current_article(); + if (null != contents && Types.PAGE.equals(contents.getType()) && contents.getSlug().equals(pageName)) { + return true; + } + if (TaleConst.SLUG_HOME.equals(pageName)) { + Boolean isHome = WebContext.request().attribute("is_home"); + if (null != isHome && isHome) { + return true; + } + } + if (TaleConst.SLUG_ARCHIVES.equals(pageName)) { + Boolean isArchives = WebContext.request().attribute("is_archive"); + if (null != isArchives && isArchives) { + return true; + } + } + if (TaleConst.SLUG_CATEGRORIES.equals(pageName)) { + Boolean isCategory = WebContext.request().attribute("is_category"); + if (null != isCategory && isCategory) { + return true; + } + } + if (TaleConst.SLUG_TAGS.equals(pageName)) { + Boolean isTag = WebContext.request().attribute("is_tag"); + if (null != isTag && isTag) { + return true; + } + } + return false; + } } diff --git a/src/main/java/com/tale/hooks/BaseWebHook.java b/src/main/java/com/tale/hooks/BaseWebHook.java new file mode 100644 index 00000000..6259fd88 --- /dev/null +++ b/src/main/java/com/tale/hooks/BaseWebHook.java @@ -0,0 +1,91 @@ +package com.tale.hooks; + +import com.blade.ioc.annotation.Bean; +import com.blade.kit.DateKit; +import com.blade.mvc.RouteContext; +import com.blade.mvc.hook.WebHook; +import com.blade.mvc.http.Request; +import com.blade.mvc.http.Response; +import com.tale.annotation.SysLog; +import com.tale.bootstrap.TaleConst; +import com.tale.model.entity.Logs; +import com.tale.model.entity.Users; +import com.tale.utils.TaleUtils; +import lombok.extern.slf4j.Slf4j; + +import static io.github.biezhi.anima.Anima.select; + +@Bean +@Slf4j +public class BaseWebHook implements WebHook { + + @Override + public boolean before(RouteContext context) { + String uri = context.uri(); + String ip = context.address(); + + // 禁止该ip访问 + if (TaleConst.BLOCK_IPS.contains(ip)) { + context.text("You have been banned, brother"); + return false; + } + + log.info("IP: {}, UserAgent: {}", ip, context.userAgent()); + + if (uri.startsWith(TaleConst.STATIC_URI)) { + return true; + } + + if (!TaleConst.INSTALLED && !uri.startsWith(TaleConst.INSTALL_URI)) { + context.redirect(TaleConst.INSTALL_URI); + return false; + } + + if (TaleConst.INSTALLED) { + return isRedirect(context); + } + return true; + } + + @Override + public boolean after(RouteContext context) { + if(null != TaleUtils.getLoginUser()){ + SysLog sysLog = context.routeAction().getAnnotation(SysLog.class); + if (null != sysLog) { + Logs logs = new Logs(); + logs.setAction(sysLog.value()); + logs.setAuthorId(TaleUtils.getLoginUser().getUid()); + logs.setIp(context.request().address()); + if(!context.request().uri().contains("upload")){ + logs.setData(context.request().bodyToString()); + } + logs.setCreated(DateKit.nowUnix()); + logs.save(); + } + } + return true; + } + + private boolean isRedirect(RouteContext context) { + Users user = TaleUtils.getLoginUser(); + String uri = context.uri(); + if (uri.startsWith(TaleConst.ADMIN_URI) && !uri.startsWith(TaleConst.LOGIN_URI)) { + context.attribute(TaleConst.PLUGINS_MENU_NAME, TaleConst.PLUGIN_MENUS); + if(null != user){ + return true; + } + + Integer uid = TaleUtils.getCookieUid(context); + if (null != uid) { + user = select().from(Users.class).byId(uid); + context.session().attribute(TaleConst.LOGIN_SESSION_KEY, user); + } + if (null == user) { + context.redirect(TaleConst.LOGIN_URI); + return false; + } + } + return true; + } + +} diff --git a/src/main/java/com/tale/init/SqliteJdbc.java b/src/main/java/com/tale/init/SqliteJdbc.java deleted file mode 100644 index f4c4d6df..00000000 --- a/src/main/java/com/tale/init/SqliteJdbc.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.tale.init; - -import com.blade.Blade; -import com.blade.kit.IOKit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.Statement; - -/** - * Sqlite 数据库操作 - *

- * Created by biezhi on 2017/3/4. - */ -public final class SqliteJdbc { - - private static final Logger LOGGER = LoggerFactory.getLogger(SqliteJdbc.class); - - private SqliteJdbc() { - } - - public static final String DB_NAME = "tale.db"; - public static String DB_PATH = SqliteJdbc.class.getClassLoader().getResource("").getPath() + DB_NAME; - public static String DB_SRC = "jdbc:sqlite://" + DB_PATH; - - static { - try { - Class.forName("org.sqlite.JDBC"); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * 测试连接并导入数据库 - */ - public static void importSql() { - try { - if(Blade.$().isDev()){ - DB_PATH = System.getProperty("user.dir") + "/" + DB_NAME; - DB_SRC = "jdbc:sqlite://" + DB_PATH; - } - Connection con = DriverManager.getConnection(DB_SRC); - Statement statement = con.createStatement(); - ResultSet rs = statement.executeQuery("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='t_options'"); - int count = rs.getInt(1); - if (count == 0) { - String cp = SqliteJdbc.class.getClassLoader().getResource("").getPath(); - InputStreamReader isr = new InputStreamReader(new FileInputStream(cp + "schema.sql"), "UTF-8"); - String sql = IOKit.toString(isr); - statement.executeUpdate(sql); - LOGGER.info("initialize import database."); - } - rs.close(); - statement.close(); - con.close(); - LOGGER.info("database path is: {}", DB_PATH); - } catch (Exception e) { - LOGGER.error("initialize database fail", e); - } - } - -} diff --git a/src/main/java/com/tale/init/TaleConst.java b/src/main/java/com/tale/init/TaleConst.java deleted file mode 100644 index 507ddfe2..00000000 --- a/src/main/java/com/tale/init/TaleConst.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.tale.init; - -import com.blade.kit.CollectionKit; -import com.blade.kit.base.Config; -import com.tale.dto.PluginMenu; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Tale 常量存储 - * - * @author biezhi - */ -public class TaleConst { - - public static final String USER_IN_COOKIE = "S_L_ID"; - public static String AES_SALT = "0123456789abcdef"; - public static String LOGIN_SESSION_KEY = "login_user"; - public static Config OPTIONS = new Config(); - public static Boolean INSTALL = false; - public static Config BCONF = null; - - /** - * 最大页码 - */ - public static final int MAX_PAGE = 100; - - /** - * 最大获取文章条数 - */ - public static final int MAX_POSTS = 9999; - - /** - * 文章最多可以输入的文字数 - */ - public static final int MAX_TEXT_COUNT = 50000; - - /** - * 文章标题最多可以输入的文字个数 - */ - public static final int MAX_TITLE_COUNT = 200; - - /** - * 点击次数超过多少更新到数据库 - */ - public static final int HIT_EXCEED = 10; - - /** - * 插件菜单 - */ - public static final List plugin_menus = CollectionKit.newArrayList(8); - - /** - * 上传文件最大20M - */ - public static Integer MAX_FILE_SIZE = 204800; - - /** - * 要过滤的ip列表 - */ - public static final Set BLOCK_IPS = new HashSet<>(16); - -} \ No newline at end of file diff --git a/src/main/java/com/tale/init/TaleLoader.java b/src/main/java/com/tale/init/TaleLoader.java deleted file mode 100644 index 8eea486c..00000000 --- a/src/main/java/com/tale/init/TaleLoader.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.tale.init; - -import com.blade.config.BConfig; -import com.blade.kit.FileKit; -import com.tale.controller.admin.AttachController; - -import java.io.File; -import java.lang.reflect.Method; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLClassLoader; - -import static com.blade.Blade.$; - -/** - * Created by biezhi on 2017/3/1. - */ -public final class TaleLoader { - - private TaleLoader() { - } - - public static void init() { - BConfig bConfig = $().bConfig(); - loadPlugins(bConfig); - loadThemes(bConfig); - } - - public static void loadThemes(BConfig bConfig) { - - String themeDir = AttachController.CLASSPATH + "templates/themes"; - try { - themeDir = new URI(themeDir).getPath(); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - File[] dir = new File(themeDir).listFiles(); - for (File f : dir) { - if (f.isDirectory() && FileKit.isDirectory(f.getPath() + "/static")) { - bConfig.addStatic(new String[]{"/templates/themes/" + f.getName() + "/static"}); - } - } - } - - public static void loadPlugins(BConfig bConfig) { - File pluginDir = new File(AttachController.CLASSPATH + "plugins"); - if (pluginDir.exists() && pluginDir.isDirectory()) { - File[] plugins = pluginDir.listFiles(); - for (File plugin : plugins) { - loadPlugin(bConfig, plugin); - } - } - } - - /** - * 加载某个插件jar包 - * - * @param pluginFile 插件文件 - */ - public static void loadPlugin(BConfig bConfig, File pluginFile) { - try { - if (pluginFile.isFile() && pluginFile.getName().endsWith(".jar")) { - URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); - Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class}); - add.setAccessible(true); - add.invoke(classLoader, pluginFile.toURI().toURL()); - - String pluginName = pluginFile.getName().substring(6); - bConfig.addStatic(new String[]{"/templates/plugins/" + pluginName + "/static"}); - } - } catch (Exception e) { - throw new RuntimeException("插件 [" + pluginFile.getName() + "] 加载失败"); - } - } - -} diff --git a/src/main/java/com/tale/init/WebContext.java b/src/main/java/com/tale/init/WebContext.java deleted file mode 100644 index 91fc44c7..00000000 --- a/src/main/java/com/tale/init/WebContext.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.tale.init; - -import com.blade.config.BConfig; -import com.blade.context.WebContextListener; -import com.blade.ioc.BeanProcessor; -import com.blade.ioc.Ioc; -import com.blade.ioc.annotation.Inject; -import com.blade.jdbc.ActiveRecord; -import com.blade.jdbc.ar.SampleActiveRecord; -import com.blade.kit.FileKit; -import com.blade.kit.StringKit; -import com.blade.mvc.view.ViewSettings; -import com.blade.mvc.view.template.JetbrickTemplateEngine; -import com.tale.controller.BaseController; -import com.tale.controller.admin.AttachController; -import com.tale.dto.Types; -import com.tale.ext.AdminCommons; -import com.tale.ext.Commons; -import com.tale.ext.JetTag; -import com.tale.ext.Theme; -import com.tale.model.ExtSql2o; -import com.tale.service.OptionsService; -import com.tale.service.SiteService; -import com.tale.utils.RewriteUtils; -import jetbrick.template.JetGlobalContext; -import jetbrick.template.resolver.GlobalResolver; - -import javax.servlet.ServletContext; -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Tale初始化进程 - * - * @author biezhi - */ -public class WebContext implements BeanProcessor, WebContextListener { - - @Inject - private OptionsService optionsService; - - @Override - public void init(BConfig bConfig, ServletContext sec) { - JetbrickTemplateEngine templateEngine = new JetbrickTemplateEngine(); - - List macros = new ArrayList<>(8); - macros.add("/comm/macros.html"); - // 扫描主题下面的所有自定义宏 - String themeDir = AttachController.CLASSPATH + "templates/themes"; - try { - themeDir = new URI(themeDir).getPath(); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - File[] dir = new File(themeDir).listFiles(); - for (File f : dir) { - if (f.isDirectory() && FileKit.exist(f.getPath() + "/macros.html")) { - String macroName = "/themes/" + f.getName() + "/macros.html"; - macros.add(macroName); - } - } - StringBuffer macroBuf = new StringBuffer(); - macros.forEach(s -> macroBuf.append(',').append(s)); - templateEngine.addConfig("jetx.import.macros", macroBuf.substring(1)); - - GlobalResolver resolver = templateEngine.getGlobalResolver(); - resolver.registerFunctions(Commons.class); - resolver.registerFunctions(Theme.class); - resolver.registerFunctions(AdminCommons.class); - resolver.registerTags(JetTag.class); - - JetGlobalContext context = templateEngine.getGlobalContext(); - context.set("version", bConfig.config().get("app.version", "v1.0")); - - ViewSettings.$().templateEngine(templateEngine); - - TaleConst.MAX_FILE_SIZE = bConfig.config().getInt("app.max-file-size", 20480); - - TaleConst.AES_SALT = bConfig.config().get("app.salt", "012c456789abcdef"); - TaleConst.OPTIONS.addAll(optionsService.getOptions()); - String ips = TaleConst.OPTIONS.get(Types.BLOCK_IPS, ""); - if (StringKit.isNotBlank(ips)) { - TaleConst.BLOCK_IPS.addAll(Arrays.asList(StringKit.split(ips, ","))); - } - if (FileKit.exist(AttachController.CLASSPATH + "install.lock")) { - TaleConst.INSTALL = Boolean.TRUE; - } - - String db_rewrite = TaleConst.OPTIONS.get("rewrite_url", ""); - if(db_rewrite.length() > 0){ - RewriteUtils.rewrite(db_rewrite); - } - - BaseController.THEME = "themes/" + Commons.site_option("site_theme"); - - TaleConst.BCONF = bConfig.config(); - } - - @Override - public void register(Ioc ioc) { - SqliteJdbc.importSql(); - ExtSql2o sql2o = new ExtSql2o(SqliteJdbc.DB_SRC); - ActiveRecord activeRecord = new SampleActiveRecord(sql2o); - ioc.addBean(activeRecord); - Commons.setSiteService(ioc.getBean(SiteService.class)); - } - -} \ No newline at end of file diff --git a/src/main/java/com/tale/interceptor/BaseInterceptor.java b/src/main/java/com/tale/interceptor/BaseInterceptor.java deleted file mode 100644 index 5252431a..00000000 --- a/src/main/java/com/tale/interceptor/BaseInterceptor.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.tale.interceptor; - -import com.blade.ioc.annotation.Inject; -import com.blade.kit.IPKit; -import com.blade.kit.StringKit; -import com.blade.kit.UUID; -import com.blade.mvc.annotation.Intercept; -import com.blade.mvc.http.Request; -import com.blade.mvc.http.Response; -import com.blade.mvc.interceptor.Interceptor; -import com.tale.dto.Types; -import com.tale.init.TaleConst; -import com.tale.model.Users; -import com.tale.service.UsersService; -import com.tale.utils.MapCache; -import com.tale.utils.TaleUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Intercept -public class BaseInterceptor implements Interceptor { - - private static final Logger LOGGE = LoggerFactory.getLogger(BaseInterceptor.class); - - @Inject - private UsersService usersService; - - private MapCache cache = MapCache.single(); - - @Override - public boolean before(Request request, Response response) { - - String uri = request.uri(); - String ip = IPKit.getIpAddrByRequest(request.raw()); - - // 禁止该ip访问 - if(TaleConst.BLOCK_IPS.contains(ip)){ - response.text("You have been banned, brother"); - return false; - } - - LOGGE.info("UserAgent: {}", request.userAgent()); - LOGGE.info("用户访问地址: {}, 来路地址: {}", uri, ip); - - if (!TaleConst.INSTALL && !uri.startsWith("/install")) { - response.go("/install"); - return false; - } - - if (TaleConst.INSTALL) { - Users user = TaleUtils.getLoginUser(); - if (null == user) { - Integer uid = TaleUtils.getCookieUid(request); - if (null != uid) { - user = usersService.byId(Integer.valueOf(uid)); - request.session().attribute(TaleConst.LOGIN_SESSION_KEY, user); - } - } - - if(uri.startsWith("/admin") && !uri.startsWith("/admin/login")){ - if(null == user){ - response.go("/admin/login"); - return false; - } - request.attribute("plugin_menus", TaleConst.plugin_menus); - } - } - String method = request.method(); - if(method.equals("GET")){ - String csrf_token = UUID.UU64(); - // 默认存储30分钟 - cache.hset(Types.CSRF_TOKEN, csrf_token, uri, TaleConst.BCONF.getInt("app.csrf-token-timeout", 30) * 60); - request.attribute("_csrf_token", csrf_token); - } - return true; - } - - - @Override - public boolean after(Request request, Response response) { - String _csrf_token = request.query("_csrf_token"); - if(StringKit.isNotBlank(_csrf_token)){ - // 移除本次token - cache.hdel(Types.CSRF_TOKEN, _csrf_token); - } - return true; - } - -} diff --git a/src/main/java/com/tale/model/Attach.java b/src/main/java/com/tale/model/Attach.java deleted file mode 100644 index fb87e623..00000000 --- a/src/main/java/com/tale/model/Attach.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.tale.model; - -import com.blade.jdbc.annotation.Table; - -import java.io.Serializable; - -/** - * Created by biezhi on 2017/2/23. - */ -@Table(name = "t_attach") -public class Attach implements Serializable { - - private Integer id; - private String fname; - private String ftype; - private String fkey; - private Integer author_id; - private Integer created; - - public Attach(){ - - } - - public Attach(String fname) { - this.fname = fname; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getFname() { - return fname; - } - - public void setFname(String fname) { - this.fname = fname; - } - - public String getFtype() { - return ftype; - } - - public void setFtype(String ftype) { - this.ftype = ftype; - } - - public String getFkey() { - return fkey; - } - - public void setFkey(String fkey) { - this.fkey = fkey; - } - - public Integer getAuthor_id() { - return author_id; - } - - public void setAuthor_id(Integer author_id) { - this.author_id = author_id; - } - - public Integer getCreated() { - return created; - } - - public void setCreated(Integer created) { - this.created = created; - } -} diff --git a/src/main/java/com/tale/model/Comments.java b/src/main/java/com/tale/model/Comments.java deleted file mode 100644 index 6681662d..00000000 --- a/src/main/java/com/tale/model/Comments.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.tale.model; - -import com.blade.jdbc.annotation.Table; - -import java.io.Serializable; - -// -@Table(name = "t_comments", pk = "coid") -public class Comments implements Serializable { - - private static final long serialVersionUID = 1L; - - // comment表主键 - private Integer coid; - - // post表主键,关联字段 - private Integer cid; - - // 评论生成时的GMT unix时间戳 - private Integer created; - - // 评论作者 - private String author; - - // 评论所属用户id - private Integer author_id; - - // 评论所属内容作者id - private Integer owner_id; - - // 评论者邮件 - private String mail; - - // 评论者网址 - private String url; - - // 评论者ip地址 - private String ip; - - // 评论者客户端 - private String agent; - - // 评论内容 - private String content; - - // 评论类型 - private String type; - - // 评论状态 - private String status; - - // 父级评论 - private Integer parent; - - public Comments() { - } - - public Integer getCoid() { - return coid; - } - - public void setCoid(Integer coid) { - this.coid = coid; - } - - public Integer getCid() { - return cid; - } - - public void setCid(Integer cid) { - this.cid = cid; - } - - public Integer getCreated() { - return created; - } - - public void setCreated(Integer created) { - this.created = created; - } - - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public String getMail() { - return mail; - } - - public void setMail(String mail) { - this.mail = mail; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public String getAgent() { - return agent; - } - - public void setAgent(String agent) { - this.agent = agent; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public Integer getParent() { - return parent; - } - - public void setParent(Integer parent) { - this.parent = parent; - } - - public Integer getAuthor_id() { - return author_id; - } - - public void setAuthor_id(Integer author_id) { - this.author_id = author_id; - } - - public Integer getOwner_id() { - return owner_id; - } - - public void setOwner_id(Integer owner_id) { - this.owner_id = owner_id; - } -} \ No newline at end of file diff --git a/src/main/java/com/tale/model/Contents.java b/src/main/java/com/tale/model/Contents.java deleted file mode 100644 index 73e6d674..00000000 --- a/src/main/java/com/tale/model/Contents.java +++ /dev/null @@ -1,213 +0,0 @@ -package com.tale.model; - -import com.blade.jdbc.annotation.Table; - -import java.io.Serializable; - -// -@Table(name = "t_contents", pk = "cid") -public class Contents implements Serializable { - - private static final long serialVersionUID = 1L; - - // post表主键 - private Integer cid; - - // 内容标题 - private String title; - - // 内容缩略名 - private String slug; - - // 内容生成时的GMT unix时间戳 - private Integer created; - - // 内容更改时的GMT unix时间戳 - private Integer modified; - - // 内容文字 - private String content; - - // 内容所属用户id - private Integer author_id; - - // 点击次数 - private Integer hits; - - // 内容类别 - private String type; - - // 内容类型,markdown或者html - private String fmt_type; - - // 文章缩略图 - private String thumb_img; - - // 标签列表 - private String tags; - - // 分类列表 - private String categories; - - // 内容状态 - private String status; - - // 内容所属评论数 - private Integer comments_num; - - // 是否允许评论 - private Boolean allow_comment; - - // 是否允许ping - private Boolean allow_ping; - - // 允许出现在聚合中 - private Boolean allow_feed; - - public Contents() { - } - - public Integer getCid() { - return cid; - } - - public void setCid(Integer cid) { - this.cid = cid; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getSlug() { - return slug; - } - - public void setSlug(String slug) { - this.slug = slug; - } - - public Integer getCreated() { - return created; - } - - public void setCreated(Integer created) { - this.created = created; - } - - public Integer getModified() { - return modified; - } - - public void setModified(Integer modified) { - this.modified = modified; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public Integer getAuthor_id() { - return author_id; - } - - public void setAuthor_id(Integer author_id) { - this.author_id = author_id; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getTags() { - return tags; - } - - public void setTags(String tags) { - this.tags = tags; - } - - public String getCategories() { - return categories; - } - - public void setCategories(String categories) { - this.categories = categories; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public Integer getComments_num() { - return comments_num; - } - - public void setComments_num(Integer comments_num) { - this.comments_num = comments_num; - } - - public Boolean getAllow_comment() { - return allow_comment; - } - - public void setAllow_comment(Boolean allow_comment) { - this.allow_comment = allow_comment; - } - - public Boolean getAllow_ping() { - return allow_ping; - } - - public void setAllow_ping(Boolean allow_ping) { - this.allow_ping = allow_ping; - } - - public Boolean getAllow_feed() { - return allow_feed; - } - - public void setAllow_feed(Boolean allow_feed) { - this.allow_feed = allow_feed; - } - - public Integer getHits() { - return hits; - } - - public void setHits(Integer hits) { - this.hits = hits; - } - - public String getFmt_type() { - return fmt_type; - } - - public void setFmt_type(String fmt_type) { - this.fmt_type = fmt_type; - } - - public String getThumb_img() { - return thumb_img; - } - - public void setThumb_img(String thumb_img) { - this.thumb_img = thumb_img; - } -} \ No newline at end of file diff --git a/src/main/java/com/tale/model/ExtSql2o.java b/src/main/java/com/tale/model/ExtSql2o.java deleted file mode 100644 index 0cc7ab58..00000000 --- a/src/main/java/com/tale/model/ExtSql2o.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.tale.model; - -import org.sql2o.Connection; -import org.sql2o.Sql2o; - -/** - * Created by biezhi on 2017/3/4. - */ -public class ExtSql2o extends Sql2o { - - private int defaultTransactionIsolationLevel; - - public ExtSql2o(String url) { - this(url, null, null); - } - - public ExtSql2o(String url, String user, String pass) { - super(url, user, pass); - this.defaultTransactionIsolationLevel = java.sql.Connection.TRANSACTION_SERIALIZABLE; - } - - public Connection beginTransaction() { - return this.beginTransaction(defaultTransactionIsolationLevel); - } - -} diff --git a/src/main/java/com/tale/model/Logs.java b/src/main/java/com/tale/model/Logs.java deleted file mode 100644 index ba37659c..00000000 --- a/src/main/java/com/tale/model/Logs.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.tale.model; - -import com.blade.jdbc.annotation.Table; - -import java.io.Serializable; - -// 日志记录对象 -@Table(name = "t_logs") -public class Logs implements Serializable { - - private static final long serialVersionUID = 1L; - - // 项目主键 - private Integer id; - - // 产生的动作 - private String action; - - // 产生的数据 - private String data; - - // 发生人id - private Integer author_id; - - // 日志产生的ip - private String ip; - - // 日志创建时间 - private Integer created; - - public Logs() { - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getAction() { - return action; - } - - public void setAction(String action) { - this.action = action; - } - - public String getData() { - return data; - } - - public void setData(String data) { - this.data = data; - } - - public Integer getAuthor_id() { - return author_id; - } - - public void setAuthor_id(Integer author_id) { - this.author_id = author_id; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public Integer getCreated() { - return created; - } - - public void setCreated(Integer created) { - this.created = created; - } -} \ No newline at end of file diff --git a/src/main/java/com/tale/model/Metas.java b/src/main/java/com/tale/model/Metas.java deleted file mode 100644 index 6a30c897..00000000 --- a/src/main/java/com/tale/model/Metas.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.tale.model; - -import com.blade.jdbc.annotation.Table; - -import java.io.Serializable; - -// -@Table(name = "t_metas", pk = "mid") -public class Metas implements Serializable { - - private static final long serialVersionUID = 1L; - - // 项目主键 - private Integer mid; - - // 名称 - private String name; - - // 项目缩略名 - private String slug; - - // 项目类型 - private String type; - - // 选项描述 - private String description; - - // 项目排序 - private Integer sort; - - private Integer parent; - - public Metas() { - } - - public Integer getMid() { - return mid; - } - - public void setMid(Integer mid) { - this.mid = mid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getSlug() { - return slug; - } - - public void setSlug(String slug) { - this.slug = slug; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Integer getSort() { - return sort; - } - - public void setSort(Integer sort) { - this.sort = sort; - } - - public Integer getParent() { - return parent; - } - - public void setParent(Integer parent) { - this.parent = parent; - } - - -} \ No newline at end of file diff --git a/src/main/java/com/tale/model/Options.java b/src/main/java/com/tale/model/Options.java deleted file mode 100644 index 916c3601..00000000 --- a/src/main/java/com/tale/model/Options.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.tale.model; - -import com.blade.jdbc.annotation.Table; - -import java.io.Serializable; - -// -@Table(name = "t_options", pk = "name") -public class Options implements Serializable { - - private static final long serialVersionUID = 1L; - - // 配置名称 - private String name; - - // 配置值 - private String value; - - // 配置描述 - private String description; - - public Options() { - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } -} \ No newline at end of file diff --git a/src/main/java/com/tale/model/Relationships.java b/src/main/java/com/tale/model/Relationships.java deleted file mode 100644 index f49beef1..00000000 --- a/src/main/java/com/tale/model/Relationships.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.tale.model; - -import com.blade.jdbc.annotation.Table; - -import java.io.Serializable; - -// -@Table(name = "t_relationships", pk = "mid") -public class Relationships implements Serializable { - - private static final long serialVersionUID = 1L; - - // 内容主键 - private Integer cid; - - // 项目主键 - private Integer mid; - - public Relationships() { - } - - public Integer getCid() { - return cid; - } - - public void setCid(Integer cid) { - this.cid = cid; - } - - public Integer getMid() { - return mid; - } - - public void setMid(Integer mid) { - this.mid = mid; - } - - -} \ No newline at end of file diff --git a/src/main/java/com/tale/model/Users.java b/src/main/java/com/tale/model/Users.java deleted file mode 100644 index 4556f35a..00000000 --- a/src/main/java/com/tale/model/Users.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.tale.model; - -import com.blade.jdbc.annotation.Table; - -import java.io.Serializable; - -// -@Table(name = "t_users", pk = "uid") -public class Users implements Serializable { - - private static final long serialVersionUID = 1L; - - // user表主键 - private Integer uid; - - // 用户名称 - private String username; - - // 用户密码 - private String password; - - // 用户的邮箱 - private String email; - - // 用户的主页 - private String home_url; - - // 用户显示的名称 - private String screen_name; - - // 用户注册时的GMT unix时间戳 - private Integer created; - - // 最后活动时间 - private Integer activated; - - // 上次登录最后活跃时间 - private Integer logged; - - // 用户组 - private String group_name; - - public Users() { - } - - public Integer getUid() { - return uid; - } - - public void setUid(Integer uid) { - this.uid = uid; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getHome_url() { - return home_url; - } - - public void setHome_url(String home_url) { - this.home_url = home_url; - } - - public String getScreen_name() { - return screen_name; - } - - public void setScreen_name(String screen_name) { - this.screen_name = screen_name; - } - - public Integer getCreated() { - return created; - } - - public void setCreated(Integer created) { - this.created = created; - } - - public Integer getActivated() { - return activated; - } - - public void setActivated(Integer activated) { - this.activated = activated; - } - - public Integer getLogged() { - return logged; - } - - public void setLogged(Integer logged) { - this.logged = logged; - } - - public String getGroup_name() { - return group_name; - } - - public void setGroup_name(String group_name) { - this.group_name = group_name; - } -} \ No newline at end of file diff --git a/src/main/java/com/tale/model/dto/Archive.java b/src/main/java/com/tale/model/dto/Archive.java new file mode 100644 index 00000000..4124b7fc --- /dev/null +++ b/src/main/java/com/tale/model/dto/Archive.java @@ -0,0 +1,23 @@ +package com.tale.model.dto; + +import com.tale.model.entity.Contents; +import io.github.biezhi.anima.Model; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * 文章归档 + *

+ * Created by biezhi on 2017/2/23. + */ +@Data +public class Archive extends Model { + + private String dateStr; + private Date date; + private String count; + private List articles; + +} diff --git a/src/main/java/com/tale/model/dto/BackResponse.java b/src/main/java/com/tale/model/dto/BackResponse.java new file mode 100644 index 00000000..e6d63bd5 --- /dev/null +++ b/src/main/java/com/tale/model/dto/BackResponse.java @@ -0,0 +1,18 @@ +package com.tale.model.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * Created by biezhi on 2017/2/25. + */ +@Data +public class BackResponse implements Serializable { + + private static final long serialVersionUID = 5992812699862539321L; + private String attach_path; + private String theme_path; + private String sql_path; + +} diff --git a/src/main/java/com/tale/dto/Comment.java b/src/main/java/com/tale/model/dto/Comment.java similarity index 55% rename from src/main/java/com/tale/dto/Comment.java rename to src/main/java/com/tale/model/dto/Comment.java index 96424424..80f75d65 100644 --- a/src/main/java/com/tale/dto/Comment.java +++ b/src/main/java/com/tale/model/dto/Comment.java @@ -1,12 +1,18 @@ -package com.tale.dto; +package com.tale.model.dto; -import com.tale.model.Comments; +import com.tale.model.entity.Comments; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import java.util.List; /** * Created by biezhi on 2017/2/24. */ +@Data +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) public class Comment extends Comments { private int levels; @@ -16,29 +22,14 @@ public Comment(Comments comments) { setAuthor(comments.getAuthor()); setMail(comments.getMail()); setCoid(comments.getCoid()); - setAuthor_id(comments.getAuthor_id()); + setAuthorId(comments.getAuthorId()); setUrl(comments.getUrl()); setCreated(comments.getCreated()); setAgent(comments.getAgent()); setIp(comments.getIp()); setContent(comments.getContent()); - setOwner_id(comments.getOwner_id()); + setOwnerId(comments.getOwnerId()); setCid(comments.getCid()); } - public int getLevels() { - return levels; - } - - public void setLevels(int levels) { - this.levels = levels; - } - - public List getChildren() { - return children; - } - - public void setChildren(List children) { - this.children = children; - } } diff --git a/src/main/java/com/tale/dto/ErrorCode.java b/src/main/java/com/tale/model/dto/ErrorCode.java similarity index 85% rename from src/main/java/com/tale/dto/ErrorCode.java rename to src/main/java/com/tale/model/dto/ErrorCode.java index bcbf7e34..56f8e574 100644 --- a/src/main/java/com/tale/dto/ErrorCode.java +++ b/src/main/java/com/tale/model/dto/ErrorCode.java @@ -1,4 +1,4 @@ -package com.tale.dto; +package com.tale.model.dto; /** * 错误提示 @@ -11,4 +11,4 @@ public interface ErrorCode { */ String BAD_REQUEST = "BAD REQUEST"; -} +} \ No newline at end of file diff --git a/src/main/java/com/tale/model/dto/PluginMenu.java b/src/main/java/com/tale/model/dto/PluginMenu.java new file mode 100644 index 00000000..ebb4972f --- /dev/null +++ b/src/main/java/com/tale/model/dto/PluginMenu.java @@ -0,0 +1,19 @@ +package com.tale.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Created by biezhi on 2017/3/1. + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PluginMenu { + + private String name; + private String slug; + private String icon; + +} diff --git a/src/main/java/com/tale/model/dto/RememberMe.java b/src/main/java/com/tale/model/dto/RememberMe.java new file mode 100644 index 00000000..157bdefb --- /dev/null +++ b/src/main/java/com/tale/model/dto/RememberMe.java @@ -0,0 +1,21 @@ +package com.tale.model.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 记住我数据结构 + * + * @author biezhi + * @date 2018-10-25 + */ +@Data +public class RememberMe { + + private Integer uid; + private Integer expires; + private String token; + private List recentIp; + +} diff --git a/src/main/java/com/tale/model/dto/Statistics.java b/src/main/java/com/tale/model/dto/Statistics.java new file mode 100644 index 00000000..03bea27c --- /dev/null +++ b/src/main/java/com/tale/model/dto/Statistics.java @@ -0,0 +1,29 @@ +package com.tale.model.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 后台统计对象 + *

+ * Created by biezhi on 2017/2/24. + */ +@Data +public class Statistics implements Serializable { + + private static final long serialVersionUID = 2329863829741481287L; + // 文章数 + private long articles; + // 页面数 + private long pages; + // 评论数 + private long comments; + // 分类数 + private long categories; + // 标签数 + private long tags; + // 附件数 + private long attachs; + +} diff --git a/src/main/java/com/tale/model/dto/ThemeDto.java b/src/main/java/com/tale/model/dto/ThemeDto.java new file mode 100644 index 00000000..90f1b68c --- /dev/null +++ b/src/main/java/com/tale/model/dto/ThemeDto.java @@ -0,0 +1,29 @@ +package com.tale.model.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * Created by biezhi on 2017/3/15. + */ +@Data +public class ThemeDto implements Serializable { + + private static final long serialVersionUID = 7122701941161403708L; + + /** + * 主题名称 + */ + private String name; + + /** + * 是否有设置项 + */ + private boolean hasSetting; + + public ThemeDto(String name) { + this.name = name; + } + +} diff --git a/src/main/java/com/tale/dto/Types.java b/src/main/java/com/tale/model/dto/Types.java similarity index 82% rename from src/main/java/com/tale/dto/Types.java rename to src/main/java/com/tale/model/dto/Types.java index bbb03a05..cc525449 100644 --- a/src/main/java/com/tale/dto/Types.java +++ b/src/main/java/com/tale/model/dto/Types.java @@ -1,6 +1,8 @@ -package com.tale.dto; +package com.tale.model.dto; /** + * Types + * * Created by biezhi on 2017/2/21. */ public interface Types { @@ -11,10 +13,8 @@ public interface Types { String PUBLISH = "publish"; String PAGE = "page"; String DRAFT = "draft"; - String LINK = "link"; String IMAGE = "image"; String FILE = "file"; - String CSRF_TOKEN = "csrf_token"; String COMMENTS_FREQUENCY = "comments:frequency"; String RECENT_ARTICLE = "recent_article"; @@ -23,9 +23,7 @@ public interface Types { String RECENT_META = "recent_meta"; String RANDOM_META = "random_meta"; - String C_STATISTICS = "sys:statistics"; - - String C_ARTICLE_HITS = "article:hits"; + String SYS_STATISTICS = "sys:statistics"; String NEXT = "next"; String PREV = "prev"; @@ -34,6 +32,7 @@ public interface Types { * 附件存放的URL,默认为网站地址,如集成第三方则为第三方CDN域名 */ String ATTACH_URL = "attach_url"; + String CDN_URL = "cdn_url"; /** * 网站要过滤,禁止访问的ip列表 diff --git a/src/main/java/com/tale/model/entity/Attach.java b/src/main/java/com/tale/model/entity/Attach.java new file mode 100644 index 00000000..b48997db --- /dev/null +++ b/src/main/java/com/tale/model/entity/Attach.java @@ -0,0 +1,23 @@ +package com.tale.model.entity; + +import io.github.biezhi.anima.Model; +import io.github.biezhi.anima.annotation.Table; +import lombok.Data; + +/** + * 附件 + *

+ * Created by biezhi on 2017/2/23. + */ +@Data +@Table(name = "t_attach") +public class Attach extends Model { + + private Integer id; + private String fname; + private String ftype; + private String fkey; + private Integer authorId; + private Integer created; + +} diff --git a/src/main/java/com/tale/model/entity/Comments.java b/src/main/java/com/tale/model/entity/Comments.java new file mode 100644 index 00000000..9d7c6e33 --- /dev/null +++ b/src/main/java/com/tale/model/entity/Comments.java @@ -0,0 +1,58 @@ +package com.tale.model.entity; + +import io.github.biezhi.anima.Model; +import io.github.biezhi.anima.annotation.Table; +import lombok.Data; + +/** + * 评论 + * + * @author biezhi + */ +@Data +@Table(name = "t_comments", pk = "coid") +public class Comments extends Model { + + // comment表主键 + private Integer coid; + + // post表主键,关联字段 + private Integer cid; + + // 评论生成时的GMT unix时间戳 + private Integer created; + + // 评论作者 + private String author; + + // 评论所属用户id + private Integer authorId; + + // 评论所属内容作者id + private Integer ownerId; + + // 评论者邮件 + private String mail; + + // 评论者网址 + private String url; + + // 评论者ip地址 + private String ip; + + // 评论者客户端 + private String agent; + + // 评论内容 + private String content; + + // 评论类型 + private String type; + + // 评论状态 + private String status; + + // 父级评论 + private Integer parent; + +} \ No newline at end of file diff --git a/src/main/java/com/tale/model/entity/Contents.java b/src/main/java/com/tale/model/entity/Contents.java new file mode 100644 index 00000000..7e9a7fd0 --- /dev/null +++ b/src/main/java/com/tale/model/entity/Contents.java @@ -0,0 +1,109 @@ +package com.tale.model.entity; + +import io.github.biezhi.anima.Model; +import io.github.biezhi.anima.annotation.Ignore; +import io.github.biezhi.anima.annotation.Table; +import lombok.Data; + +/** + * 文章 + * + * @author biezhi + */ +@Data +@Table(name = "t_contents", pk = "cid") +public class Contents extends Model { + + /** + * 文章表主键 + */ + private Integer cid; + + /** + * 文章标题 + */ + private String title; + + /** + * 文章缩略名 + */ + private String slug; + + /** + * 文章创建时间戳 + */ + private Integer created; + + /** + * 文章修改时间戳 + */ + private Integer modified; + + /** + * 文章内容 + */ + private String content; + + /** + * 文章创建用户 + */ + private Integer authorId; + + /** + * 文章点击次数 + */ + private Integer hits; + + /** + * 文章类型: PAGE、POST + */ + private String type; + + /** + * 内容类型,markdown或者html + */ + private String fmtType; + + /** + * 文章缩略图 + */ + private String thumbImg; + + /** + * 标签列表 + */ + private String tags; + + /** + * 分类列表 + */ + private String categories; + + /** + * 内容状态 + */ + private String status; + + /** + * 内容所属评论数 + */ + private Integer commentsNum; + + /** + * 是否允许评论 + */ + private Boolean allowComment; + + /** + * 是否允许ping + */ + private Boolean allowPing; + + /** + * 允许出现在Feed中 + */ + private Boolean allowFeed; + + @Ignore + private String url; +} \ No newline at end of file diff --git a/src/main/java/com/tale/model/entity/Logs.java b/src/main/java/com/tale/model/entity/Logs.java new file mode 100644 index 00000000..4cd3c23f --- /dev/null +++ b/src/main/java/com/tale/model/entity/Logs.java @@ -0,0 +1,59 @@ +package com.tale.model.entity; + +import com.blade.kit.DateKit; +import io.github.biezhi.anima.Model; +import io.github.biezhi.anima.annotation.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 日志记录 + * + * @author biezhi + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "t_logs") +public class Logs extends Model { + + /** + * 日志主键 + */ + private Integer id; + + /** + * 产生的动作 + */ + private String action; + + /** + * 产生的数据 + */ + private String data; + + /** + * 发生人id + */ + private Integer authorId; + + /** + * 日志产生的ip + */ + private String ip; + + /** + * 日志创建时间 + */ + private Integer created; + + public Logs(String action, String data, String ip, Integer uid) { + this.action = action; + this.data = data; + this.ip = ip; + this.authorId = uid; + this.created = DateKit.nowUnix(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/tale/model/entity/Metas.java b/src/main/java/com/tale/model/entity/Metas.java new file mode 100644 index 00000000..27619202 --- /dev/null +++ b/src/main/java/com/tale/model/entity/Metas.java @@ -0,0 +1,58 @@ +package com.tale.model.entity; + +import io.github.biezhi.anima.Model; +import io.github.biezhi.anima.annotation.Ignore; +import io.github.biezhi.anima.annotation.Table; +import lombok.Data; + +/** + * 元数据 + * + * @author biezhi + */ +@Data +@Table(name = "t_metas", pk = "mid") +public class Metas extends Model { + + /** + * 项目主键 + */ + private Integer mid; + + /** + * 项目名称 + */ + private String name; + + /** + * 项目缩略名 + */ + private String slug; + + /** + * 项目类型 + */ + private String type; + + /** + * 项目描述 + */ + private String description; + + /** + * 项目排序 + */ + private Integer sort; + + /** + * 父级 + */ + private Integer parent; + + /** + * 项目下文章数 + */ + @Ignore + private Integer count; + +} \ No newline at end of file diff --git a/src/main/java/com/tale/model/entity/Options.java b/src/main/java/com/tale/model/entity/Options.java new file mode 100644 index 00000000..716307b8 --- /dev/null +++ b/src/main/java/com/tale/model/entity/Options.java @@ -0,0 +1,33 @@ +package com.tale.model.entity; + +import io.github.biezhi.anima.Model; +import io.github.biezhi.anima.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 配置选项 + * + * @author biezhi + */ +@Data +@Table(name = "t_options", pk = "name") +@EqualsAndHashCode(callSuper = true) +public class Options extends Model { + + /** + * 配置键 + */ + private String name; + + /** + * 配置值 + */ + private String value; + + /** + * 配置描述 + */ + private String description; + +} \ No newline at end of file diff --git a/src/main/java/com/tale/model/entity/Relationships.java b/src/main/java/com/tale/model/entity/Relationships.java new file mode 100644 index 00000000..1acb4bb1 --- /dev/null +++ b/src/main/java/com/tale/model/entity/Relationships.java @@ -0,0 +1,26 @@ +package com.tale.model.entity; + +import io.github.biezhi.anima.Model; +import io.github.biezhi.anima.annotation.Table; +import lombok.Data; + +/** + * 数据关系 + * + * @author biezhi + */ +@Data +@Table(name = "t_relationships", pk = "mid") +public class Relationships extends Model { + + /** + * 文章主键 + */ + private Integer cid; + + /** + * 项目主键 + */ + private Integer mid; + +} \ No newline at end of file diff --git a/src/main/java/com/tale/model/entity/Users.java b/src/main/java/com/tale/model/entity/Users.java new file mode 100644 index 00000000..8a515940 --- /dev/null +++ b/src/main/java/com/tale/model/entity/Users.java @@ -0,0 +1,66 @@ +package com.tale.model.entity; + +import io.github.biezhi.anima.Model; +import io.github.biezhi.anima.annotation.Table; +import lombok.Data; + +/** + * 用户 + * + * @author biezhi + */ +@Data +@Table(name = "t_users", pk = "uid") +public class Users extends Model { + + /** + * user表主键 + */ + private Integer uid; + + /** + * 用户名称 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 用户的邮箱 + */ + private String email; + + /** + * 用户的主页 + */ + private String homeUrl; + + /** + * 用户显示的名称 + */ + private String screenName; + + /** + * 用户注册时的GMT unix时间戳 + */ + private Integer created; + + /** + * 最后活动时间 + */ + private Integer activated; + + /** + * 上次登录最后活跃时间 + */ + private Integer logged; + + /** + * 用户组 + */ + private String groupName; + +} \ No newline at end of file diff --git a/src/main/java/com/tale/model/params/AdvanceParam.java b/src/main/java/com/tale/model/params/AdvanceParam.java new file mode 100644 index 00000000..cd83589d --- /dev/null +++ b/src/main/java/com/tale/model/params/AdvanceParam.java @@ -0,0 +1,20 @@ +package com.tale.model.params; + +import lombok.Data; + +/** + * @author biezhi + * @date 2018/6/10 + */ +@Data +public class AdvanceParam { + + private String cacheKey; + private String blockIps; + private String pluginName; + private String cdnURL; + private String allowInstall; + private String allowCommentAudit; + private String allowCloudCDN; + +} diff --git a/src/main/java/com/tale/model/params/ArticleParam.java b/src/main/java/com/tale/model/params/ArticleParam.java new file mode 100644 index 00000000..d86ecb94 --- /dev/null +++ b/src/main/java/com/tale/model/params/ArticleParam.java @@ -0,0 +1,20 @@ +package com.tale.model.params; + +import lombok.Data; +import lombok.ToString; + +/** + * @author biezhi + * @date 2018/6/9 + */ +@Data +@ToString(callSuper = true) +public class ArticleParam extends PageParam { + + private String title; + private String categories; + private String status; + private String type; + private String orderBy; + +} diff --git a/src/main/java/com/tale/model/params/CommentParam.java b/src/main/java/com/tale/model/params/CommentParam.java new file mode 100644 index 00000000..982aede0 --- /dev/null +++ b/src/main/java/com/tale/model/params/CommentParam.java @@ -0,0 +1,16 @@ +package com.tale.model.params; + +import lombok.Data; +import lombok.ToString; + +/** + * @author biezhi + * @date 2018/6/9 + */ +@Data +@ToString(callSuper = true) +public class CommentParam extends PageParam { + + private Integer excludeUID; + +} diff --git a/src/main/java/com/tale/model/params/InstallParam.java b/src/main/java/com/tale/model/params/InstallParam.java new file mode 100644 index 00000000..799d1e9a --- /dev/null +++ b/src/main/java/com/tale/model/params/InstallParam.java @@ -0,0 +1,18 @@ +package com.tale.model.params; + +import lombok.Data; + +/** + * @author biezhi + * @date 2018/6/5 + */ +@Data +public class InstallParam { + + private String siteTitle; + private String siteUrl; + private String adminUser; + private String adminEmail; + private String adminPwd; + +} diff --git a/src/main/java/com/tale/model/params/LoginParam.java b/src/main/java/com/tale/model/params/LoginParam.java new file mode 100644 index 00000000..66ab693c --- /dev/null +++ b/src/main/java/com/tale/model/params/LoginParam.java @@ -0,0 +1,18 @@ +package com.tale.model.params; + +import lombok.Data; + +/** + * 登录参数 + * + * @author biezhi + * @date 2017/9/17 + */ +@Data +public class LoginParam { + + private String username; + private String password; + private String rememberMe; + +} diff --git a/src/main/java/com/tale/model/params/MetaParam.java b/src/main/java/com/tale/model/params/MetaParam.java new file mode 100644 index 00000000..fc1c3528 --- /dev/null +++ b/src/main/java/com/tale/model/params/MetaParam.java @@ -0,0 +1,15 @@ +package com.tale.model.params; + +import lombok.Data; + +/** + * @author biezhi + * @date 2018/6/9 + */ +@Data +public class MetaParam { + + private Integer mid; + private String cname; + +} diff --git a/src/main/java/com/tale/model/params/PageParam.java b/src/main/java/com/tale/model/params/PageParam.java new file mode 100644 index 00000000..885e0a73 --- /dev/null +++ b/src/main/java/com/tale/model/params/PageParam.java @@ -0,0 +1,17 @@ +package com.tale.model.params; + +import lombok.Data; + +/** + * 分页基础参数 + * + * @author biezhi + * @date 2018/6/5 + */ +@Data +public class PageParam { + + private Integer page = 1; + private Integer limit = 12; + +} diff --git a/src/main/java/com/tale/model/params/TemplateParam.java b/src/main/java/com/tale/model/params/TemplateParam.java new file mode 100644 index 00000000..f1b9c448 --- /dev/null +++ b/src/main/java/com/tale/model/params/TemplateParam.java @@ -0,0 +1,17 @@ +package com.tale.model.params; + +import lombok.Data; + +/** + * 保存模板参数 + * + * @author biezhi + * @date 2018/10/12 + */ +@Data +public class TemplateParam { + + private String fileName; + private String content; + +} diff --git a/src/main/java/com/tale/model/params/ThemeParam.java b/src/main/java/com/tale/model/params/ThemeParam.java new file mode 100644 index 00000000..989a4bab --- /dev/null +++ b/src/main/java/com/tale/model/params/ThemeParam.java @@ -0,0 +1,14 @@ +package com.tale.model.params; + +import lombok.Data; + +/** + * @author biezhi + * @date 2018/6/10 + */ +@Data +public class ThemeParam { + + private String siteTheme; + +} diff --git a/src/main/java/com/tale/service/AttachService.java b/src/main/java/com/tale/service/AttachService.java deleted file mode 100644 index c73599a3..00000000 --- a/src/main/java/com/tale/service/AttachService.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.tale.service; - -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.tale.model.Attach; - -/** - * Created by biezhi on 2017/2/23. - */ -public interface AttachService { - - /** - * 保存附件 - * - * @param fname - * @param fkey - * @param ftype - * @param author - */ - Attach save(String fname, String fkey, String ftype, Integer author); - - /** - * 删除附件 - * @param id - */ - void delete(Integer id); - - /** - * 分页查询附件 - * @param take - * @return - */ - Paginator getAttachs(Take take); - - /** - * 根据附件id查询附件 - * @param id - * @return - */ - Attach byId(Integer id); -} diff --git a/src/main/java/com/tale/service/CommentsService.java b/src/main/java/com/tale/service/CommentsService.java index 6e899196..efe969dc 100644 --- a/src/main/java/com/tale/service/CommentsService.java +++ b/src/main/java/com/tale/service/CommentsService.java @@ -1,53 +1,145 @@ package com.tale.service; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.tale.dto.Comment; -import com.tale.model.Comments; +import com.blade.exception.ValidatorException; +import com.blade.ioc.annotation.Bean; +import com.blade.kit.BladeKit; +import com.blade.kit.DateKit; +import com.tale.bootstrap.TaleConst; +import com.tale.extension.Commons; +import com.tale.model.dto.Comment; +import com.tale.model.entity.Comments; +import com.tale.model.entity.Contents; +import com.tale.model.params.CommentParam; +import com.tale.utils.TaleUtils; +import com.vdurmont.emoji.EmojiParser; +import io.github.biezhi.anima.Anima; +import io.github.biezhi.anima.enums.OrderBy; +import io.github.biezhi.anima.page.Page; -public interface CommentsService { +import java.util.ArrayList; +import java.util.List; + +import static com.tale.bootstrap.TaleConst.*; +import static io.github.biezhi.anima.Anima.select; +import static io.github.biezhi.anima.Anima.update; + +/** + * 评论Service + * + * @author biezhi + * @since 1.3.1 + */ +@Bean +public class CommentsService { /** * 保存评论 * * @param comments */ - void saveComment(Comments comments); + public void saveComment(Comments comments) { + comments.setAuthor(TaleUtils.cleanXSS(comments.getAuthor())); + comments.setContent(TaleUtils.cleanXSS(comments.getContent())); + + comments.setAuthor(EmojiParser.parseToAliases(comments.getAuthor())); + comments.setContent(EmojiParser.parseToAliases(comments.getContent())); + + Contents contents = select().from(Contents.class).byId(comments.getCid()); + if (null == contents) { + throw new ValidatorException("不存在的文章"); + } + try { + comments.setOwnerId(contents.getAuthorId()); + comments.setAuthorId(null == comments.getAuthorId() ? 0 : comments.getAuthorId()); + comments.setCreated(DateKit.nowUnix()); + comments.setParent(null == comments.getCoid() ? 0 : comments.getCoid()); + comments.setCoid(null); + comments.save(); + + new Contents().set(Contents::getCommentsNum, contents.getCommentsNum() + 1).updateById(contents.getCid()); + } catch (Exception e) { + throw e; + } + } /** * 删除评论,暂时没用 + * * @param coid * @param cid * @throws Exception */ - void delete(Integer coid, Integer cid); + public void delete(Integer coid, Integer cid) { + Anima.delete().from(Comments.class).deleteById(coid); + + Contents contents = select().from(Contents.class).byId(cid); + if (null != contents && contents.getCommentsNum() > 0) { + update().from(Contents.class).set(Contents::getCommentsNum, contents.getCommentsNum() - 1).updateById(cid); + } + } /** * 获取文章下的评论 + * * @param cid * @param page * @param limit * @return */ - Paginator getComments(Integer cid, int page, int limit); + public Page getComments(Integer cid, int page, int limit) { + if (null == cid) { + return null; + } + + Page commentsPage = select().from(Comments.class) + .where(Comments::getCid, cid).and(Comments::getParent, 0) + .and(Comments::getStatus, COMMENT_APPROVED) + .order(Comments::getCoid, OrderBy.DESC).page(page, limit); + + return commentsPage.map(this::apply); + } /** - * 分页管理评论 - * @param take - * @return + * 获取文章下的评论统计 + * + * @param cid 文章ID */ - Paginator getComments(Take take); + public long getCommentCount(Integer cid) { + if (null == cid) { + return 0; + } + return select().from(Comments.class).where(Comments::getCid, cid).count(); + } /** - * 根据主键查询评论 + * 获取该评论下的追加评论 + * * @param coid * @return */ - Comments byId(Integer coid); + private void getChildren(List list, Integer coid) { + List cms = select().from(Comments.class).where(Comments::getParent, coid).order(Comments::getCoid, OrderBy.ASC).all(); + if (null != cms) { + list.addAll(cms); + cms.forEach(c -> getChildren(list, c.getCoid())); + } + } + + private Comment apply(Comments parent) { + Comment comment = new Comment(parent); + List children = new ArrayList<>(); + getChildren(children, comment.getCoid()); + comment.setChildren(children); + if (BladeKit.isNotEmpty(children)) { + comment.setLevels(1); + } + return comment; + } + + public Page findComments(CommentParam commentParam) { + return select().from(Comments.class) + .order(Comments::getCoid, OrderBy.DESC) + .page(commentParam.getPage(), commentParam.getLimit()); + } - /** - * 更新评论状态 - * @param comments - */ - void update(Comments comments); } diff --git a/src/main/java/com/tale/service/ContentsService.java b/src/main/java/com/tale/service/ContentsService.java index 2c29c6ce..3e1d63de 100644 --- a/src/main/java/com/tale/service/ContentsService.java +++ b/src/main/java/com/tale/service/ContentsService.java @@ -1,60 +1,170 @@ package com.tale.service; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.tale.dto.Archive; -import com.tale.model.Contents; +import com.blade.exception.ValidatorException; +import com.blade.ioc.annotation.Bean; +import com.blade.ioc.annotation.Inject; +import com.blade.kit.DateKit; +import com.blade.kit.StringKit; +import com.tale.model.dto.Types; +import com.tale.model.entity.Comments; +import com.tale.model.entity.Contents; +import com.tale.model.entity.Relationships; +import com.tale.model.params.ArticleParam; +import com.vdurmont.emoji.EmojiParser; +import io.github.biezhi.anima.Anima; +import io.github.biezhi.anima.core.AnimaQuery; +import io.github.biezhi.anima.page.Page; -import java.util.List; +import static com.tale.bootstrap.TaleConst.SQL_QUERY_ARTICLES; +import static io.github.biezhi.anima.Anima.deleteById; +import static io.github.biezhi.anima.Anima.select; -public interface ContentsService { +/** + * 文章Service + * + * @author biezhi + * @since 1.3.1 + */ +@Bean +public class ContentsService { + + @Inject + private MetasService metasService; /** * 根据id或slug获取文章 * - * @param id - * @return + * @param id 唯一标识 */ - Contents getContents(String id); - - /** - * 根据Take条件查询分页信息 - * @param take - * @return - */ - Paginator getArticles(Take take); + public Contents getContents(String id) { + Contents contents = null; + if (StringKit.isNotBlank(id)) { + if (StringKit.isNumber(id)) { + contents = select().from(Contents.class).byId(id); + } else { + contents = select().from(Contents.class).where(Contents::getSlug, id).one(); + } + if (null != contents) { + return this.mapContent(contents); + } + } + return contents; + } /** * 发布文章 - * @param contents + * + * @param contents 文章对象 */ - Integer publish(Contents contents); + public Integer publish(Contents contents) { + if (null == contents.getAuthorId()) { + throw new ValidatorException("请登录后发布文章"); + } + + contents.setContent(EmojiParser.parseToAliases(contents.getContent())); + + int time = DateKit.nowUnix(); + contents.setCreated(time); + contents.setModified(time); + contents.setHits(0); + + String tags = contents.getTags(); + String categories = contents.getCategories(); + + Integer cid = contents.save().asInt(); + + metasService.saveMetas(cid, tags, Types.TAG); + metasService.saveMetas(cid, categories, Types.CATEGORY); + + return cid; + } /** * 编辑文章 - * @param contents + * + * @param contents 文章对象 */ - void updateArticle(Contents contents); + public void updateArticle(Contents contents) { + contents.setCreated(contents.getCreated()); + contents.setModified(DateKit.nowUnix()); + contents.setContent(EmojiParser.parseToAliases(contents.getContent())); + contents.setTags(contents.getTags() != null ? contents.getTags() : ""); + contents.setCategories(contents.getCategories() != null ? contents.getCategories() : ""); - /** - * 自定义update - * @param contents - */ - void update(Contents contents); + Integer cid = contents.getCid(); + + contents.updateById(cid); + + String tags = contents.getTags(); + String categories = contents.getCategories(); + + if (null != contents.getType() && !contents.getType().equals(Types.PAGE)) { + Anima.delete().from(Relationships.class).where(Relationships::getCid, cid).execute(); + } + + metasService.saveMetas(cid, tags, Types.TAG); + metasService.saveMetas(cid, categories, Types.CATEGORY); + } /** * 根据文章id删除 - * @param cid + * + * @param cid 文章id */ - void delete(int cid); + public void delete(int cid) { + Contents contents = this.getContents(cid + ""); + if (null != contents) { + deleteById(Contents.class, cid); + Anima.delete().from(Relationships.class).where(Relationships::getCid, cid).execute(); + Anima.delete().from(Comments.class).where(Comments::getCid, cid).execute(); + } + } /** * 查询分类/标签下的文章归档 - * @param mid - * @param page - * @param limit + * + * @param mid 分类、标签id + * @param page 页码 + * @param limit 每页条数 * @return */ - Paginator getArticles(Integer mid, int page, int limit); + public Page getArticles(Integer mid, int page, int limit) { + return select().bySQL(Contents.class, SQL_QUERY_ARTICLES, mid).page(page, limit); + } + + public Page findArticles(ArticleParam articleParam) { + AnimaQuery query = select().from(Contents.class).exclude(Contents::getContent); + + if (StringKit.isNotEmpty(articleParam.getStatus())) { + query.and(Contents::getStatus, articleParam.getStatus()); + } + + if (StringKit.isNotEmpty(articleParam.getTitle())) { + query.and(Contents::getTitle).like("%" + articleParam.getTitle() + "%"); + } + + if (StringKit.isNotEmpty(articleParam.getCategories())) { + query.and(Contents::getCategories).like("%" + articleParam.getCategories() + "%"); + } + + query.and(Contents::getType, articleParam.getType()); + query.order(articleParam.getOrderBy()); + Page articles = query.page(articleParam.getPage(), articleParam.getLimit()); + return articles.map(this::mapContent); + } + private Contents mapContent(Contents contents) { + if (StringKit.isNotEmpty(contents.getSlug())) { + String url = "/" + contents.getSlug(); + contents.setUrl(url.replaceAll("[/]+", "/")); + } else { + contents.setUrl("/article/" + contents.getCid()); + } + String content = contents.getContent(); + if (StringKit.isNotEmpty(content)) { + content = content.replaceAll("\\\\\"", "\\\""); + contents.setContent(content); + } + return contents; + } } diff --git a/src/main/java/com/tale/service/LogService.java b/src/main/java/com/tale/service/LogService.java deleted file mode 100644 index 54838da1..00000000 --- a/src/main/java/com/tale/service/LogService.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.tale.service; - -import com.tale.model.Logs; - -import java.util.List; - -/** - * Created by biezhi on 2017/2/26. - */ -public interface LogService { - - /** - * 记录日志 - * @param action - * @param data - * @param ip - * @param author_id - */ - void save(String action, String data, String ip, Integer author_id); - - /** - * 读取日志 - * @param page - * @param limit - * @return - */ - List getLogs(int page, int limit); -} diff --git a/src/main/java/com/tale/service/MetasService.java b/src/main/java/com/tale/service/MetasService.java index dbbcf051..46cb6870 100644 --- a/src/main/java/com/tale/service/MetasService.java +++ b/src/main/java/com/tale/service/MetasService.java @@ -1,60 +1,206 @@ package com.tale.service; -import com.tale.dto.MetaDto; -import com.tale.model.Metas; +import com.blade.exception.ValidatorException; +import com.blade.ioc.annotation.Bean; +import com.blade.kit.StringKit; +import com.tale.model.dto.Types; +import com.tale.model.entity.Contents; +import com.tale.model.entity.Metas; +import com.tale.model.entity.Relationships; +import io.github.biezhi.anima.Anima; +import io.github.biezhi.anima.enums.OrderBy; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; -public interface MetasService { +import static com.tale.bootstrap.TaleConst.SQL_QUERY_METAS; +import static io.github.biezhi.anima.Anima.select; + +/** + * 分类、标签Service + * + * @author biezhi + * @since 1.3.1 + */ +@Bean +public class MetasService { /** - * 根据类型和名字查询项 + * 根据类型查询项目列表 * - * @param type - * @param name - * @return + * @param type 类型,tag or category */ - MetaDto getMeta(String type, String name); + public List getMetas(String type) { + if (StringKit.isNotBlank(type)) { + return select().from(Metas.class).where(Metas::getType, type) + .order(Metas::getSort, OrderBy.DESC) + .order(Metas::getMid, OrderBy.DESC) + .all(); + } + return null; + } /** - * 根据类型查询项目列表 - * @param types - * @return + * 查询项目映射 + * + * @param type 类型,tag or category */ - List getMetas(String types); + public Map> getMetaMapping(String type) { + if (StringKit.isNotBlank(type)) { + List metas = getMetas(type); + if (null != metas) { + return metas.stream().collect(Collectors.toMap(Metas::getName, this::getMetaContents)); + } + } + return new HashMap<>(); + } + + private List getMetaContents(Metas m) { + Integer mid = m.getMid(); + + List relationships = select().from(Relationships.class).where(Relationships::getMid, mid).all(); + if (null == relationships || relationships.size() == 0) { + return new ArrayList<>(); + } + List cidList = relationships.stream().map(Relationships::getCid).collect(Collectors.toList()); + return select().from(Contents.class).in(Contents::getCid, cidList).order(Contents::getCreated, OrderBy.DESC).all(); + } + + /** + * 根据类型和名字查询项 + * + * @param type 类型,tag or category + * @param name 类型名 + */ + public Metas getMeta(String type, String name) { + if (StringKit.isNotBlank(type) && StringKit.isNotBlank(name)) { + return select().bySQL(Metas.class, SQL_QUERY_METAS, type, name).one(); + } + return null; + } /** * 保存多个项目 - * @param cid - * @param names - * @param type + * + * @param cid 文章id + * @param names 类型名称列表 + * @param type 类型,tag or category */ - void saveMetas(Integer cid, String names, String type); + public void saveMetas(Integer cid, String names, String type) { + if (null == cid) { + throw new ValidatorException("项目关联id不能为空"); + } + if (StringKit.isNotBlank(names) && StringKit.isNotBlank(type)) { + String[] nameArr = names.split(","); + for (String name : nameArr) { + this.saveOrUpdate(cid, name, type); + } + } + } + + private void saveOrUpdate(Integer cid, String name, String type) { + Metas metas = select().from(Metas.class).where(Metas::getName, name).and(Metas::getType, type).one(); + int mid; + if (null != metas) { + mid = metas.getMid(); + } else { + metas = new Metas(); + metas.setSlug(name); + metas.setName(name); + metas.setType(type); + mid = metas.save().asInt(); + } + if (mid != 0) { + long count = new Relationships().where("cid", cid).and("mid", mid).count(); + if (count == 0) { + Relationships relationships = new Relationships(); + relationships.setCid(cid); + relationships.setMid(mid); + relationships.save(); + } + } + } /** * 删除项目 - * @param mid + * + * @param mid 项目id */ - void delete(int mid); + public void delete(int mid) { + Metas metas = select().from(Metas.class).byId(mid); + if (null == metas) { + return; + } + + String type = metas.getType(); + String name = metas.getName(); + Anima.deleteById(Metas.class, mid); + + List list = select().from(Relationships.class).where(Relationships::getMid, mid).all(); + if (null != list) { + list.stream() + .map(r -> select().from(Contents.class).byId(r.getCid())) + .filter(Objects::nonNull) + .forEach(contents -> exec(type, name, contents)); + } + Anima.delete().from(Relationships.class).where(Relationships::getMid, mid).execute(); + } + + private void exec(String type, String name, Contents contents) { + Integer cid = contents.getCid(); + boolean isUpdate = false; + Contents temp = new Contents(); + if (type.equals(Types.CATEGORY)) { + temp.setCategories(reMeta(name, contents.getCategories())); + isUpdate = true; + } + if (type.equals(Types.TAG)) { + temp.setTags(reMeta(name, contents.getTags())); + isUpdate = true; + } + if (isUpdate) { + temp.updateById(cid); + } + } /** * 保存项目 + * * @param type * @param name * @param mid */ - void saveMeta(String type, String name, Integer mid); + public void saveMeta(String type, String name, Integer mid) { + if (StringKit.isEmpty(type) || StringKit.isEmpty(name)) { + return; + } + Metas metas = select().from(Metas.class).where(Metas::getType, type).and(Metas::getName, name).one(); + if (null != metas) { + throw new ValidatorException("已经存在该项"); + } else { + metas = new Metas(); + metas.setName(name); + if (null != mid) { + metas.updateById(mid); + } else { + metas.setType(type); + metas.save(); + } + } + } - /** - * 保存项目 - * @param metas - */ - void saveMeta(Metas metas); - - /** - * 更新项目 - * @param metas - */ - void update(Metas metas); + private String reMeta(String name, String metas) { + String[] ms = metas.split(","); + StringBuilder sba = new StringBuilder(); + for (String m : ms) { + if (!name.equals(m)) { + sba.append(",").append(m); + } + } + if (sba.length() > 0) { + return sba.substring(1); + } + return ""; + } } diff --git a/src/main/java/com/tale/service/OptionsService.java b/src/main/java/com/tale/service/OptionsService.java index 2f54517f..8b1408cb 100644 --- a/src/main/java/com/tale/service/OptionsService.java +++ b/src/main/java/com/tale/service/OptionsService.java @@ -1,32 +1,81 @@ package com.tale.service; +import com.blade.ioc.annotation.Bean; +import com.blade.kit.StringKit; +import com.tale.model.entity.Options; +import io.github.biezhi.anima.core.AnimaQuery; + +import java.util.HashMap; +import java.util.List; import java.util.Map; -public interface OptionsService { +import static io.github.biezhi.anima.Anima.delete; +import static io.github.biezhi.anima.Anima.select; - /** - * 保存一组配置 - * - * @param options - */ - void saveOptions(Map options); +/** + * 配置Service + * + * @author biezhi + * @since 1.3.1 + */ +@Bean +public class OptionsService { /** * 保存配置 - * @param key - * @param value + * + * @param key 配置key + * @param value 配置值 */ - void saveOption(String key, String value); + public void saveOption(String key, String value) { + if (StringKit.isNotBlank(key) && StringKit.isNotBlank(value)) { + Options options = new Options(); + options.setName(key); + + long count = select().from(Options.class).where(Options::getName, key).count(); + + if (count == 0) { + options = new Options(); + options.setName(key); + options.setValue(value); + options.save(); + } else { + options = new Options(); + options.setValue(value); + options.updateById(key); + } + } + } /** * 获取系统配置 - * @return */ - Map getOptions(); + public Map getOptions() { + Map options = new HashMap<>(); + AnimaQuery animaQuery = select().from(Options.class); + List optionsList = animaQuery.all(); + if (null != optionsList) { + optionsList.forEach(option -> options.put(option.getName(), option.getValue())); + } + return options; + } + + public String getOption(String key) { + Options options = select().from(Options.class).byId(key); + if (null != options) { + return options.getValue(); + } + return null; + } /** * 根据key删除配置项 - * @param key + * + * @param key 配置key */ - void deleteOption(String key); + public void deleteOption(String key) { + if (StringKit.isNotBlank(key)) { + delete().from(Options.class).where(Options::getName).like(key + "%").execute(); + } + } } diff --git a/src/main/java/com/tale/service/SiteService.java b/src/main/java/com/tale/service/SiteService.java index 35736b25..f98f1a0f 100644 --- a/src/main/java/com/tale/service/SiteService.java +++ b/src/main/java/com/tale/service/SiteService.java @@ -1,102 +1,342 @@ package com.tale.service; -import com.blade.jdbc.model.Paginator; -import com.tale.dto.*; -import com.tale.model.Comments; -import com.tale.model.Contents; -import com.tale.model.Users; +import com.blade.exception.ValidatorException; +import com.blade.ioc.annotation.Bean; +import com.blade.ioc.annotation.Inject; +import com.blade.kit.BladeKit; +import com.blade.kit.DateKit; +import com.blade.kit.EncryptKit; +import com.blade.kit.StringKit; +import com.tale.bootstrap.SqliteJdbc; +import com.tale.bootstrap.TaleConst; +import com.tale.model.dto.*; +import com.tale.model.entity.*; +import com.tale.utils.MapCache; +import com.tale.utils.TaleUtils; +import io.github.biezhi.anima.enums.OrderBy; +import io.github.biezhi.anima.page.Page; -import java.awt.*; -import java.util.List; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + +import static com.tale.bootstrap.TaleConst.CLASSPATH; +import static com.tale.bootstrap.TaleConst.COMMENT_APPROVED; +import static io.github.biezhi.anima.Anima.select; /** - * 站点服务 + * 站点Service * - * Created by biezhi on 2017/2/23. + * @author biezhi + * @since 1.3.1 */ -public interface SiteService { +@Bean +public class SiteService { + + @Inject + private CommentsService commentsService; + + public MapCache mapCache = new MapCache(); /** * 初始化站点 * - * @param users + * @param users 用户 */ - void initSite(Users users); + public void initSite(Users users) { + String pwd = EncryptKit.md5(users.getUsername() + users.getPassword()); + users.setPassword(pwd); + users.setScreenName(users.getUsername()); + users.setCreated(DateKit.nowUnix()); + Integer uid = users.save().asInt(); + + try { + String cp = SiteService.class.getClassLoader().getResource("").getPath(); + File lock = new File(cp + "install.lock"); + lock.createNewFile(); + TaleConst.INSTALLED = Boolean.TRUE; + new Logs("初始化站点", null, "", uid).save(); + } catch (Exception e) { + throw new ValidatorException("初始化站点失败"); + } + } /** * 最新收到的评论 * - * @param limit - * @return + * @param limit 评论数 */ - List recentComments(int limit); + public List recentComments(int limit) { + if (limit < 0 || limit > 10) { + limit = 10; + } + + Page commentsPage = select().from(Comments.class) + .where(Comments::getStatus, COMMENT_APPROVED) + .order(Comments::getCreated, OrderBy.DESC) + .page(1, limit); + return commentsPage.getRows(); + } /** * 根据类型获取文章列表 * - * @param type 最新,随机 + * @param type 最新,随机 * @param limit 获取条数 - * @return */ - List getContens(String type, int limit); + public List getContens(String type, int limit) { + + if (limit < 0 || limit > 20) { + limit = 10; + } + + // 最新文章 + if (Types.RECENT_ARTICLE.equals(type)) { + Page contentsPage = select().from(Contents.class) + .where(Contents::getStatus, Types.PUBLISH) + .and(Contents::getType, Types.ARTICLE) + .order(Contents::getCreated, OrderBy.DESC) + .page(1, limit); + + return contentsPage.getRows(); + } + + // 随机文章 + if (Types.RANDOM_ARTICLE.equals(type)) { + List cids = select().bySQL(Integer.class, + "select cid from t_contents where type = ? and status = ? order by random() * cid limit ?", + Types.ARTICLE, Types.PUBLISH, limit).all(); + if (BladeKit.isNotEmpty(cids)) { + return select().from(Contents.class).in(Contents::getCid, cids).all(); + } + } + return new ArrayList<>(); + } /** * 获取后台统计数据 - * - * @return */ - Statistics getStatistics(); + public Statistics getStatistics() { + + Statistics statistics = mapCache.get(Types.SYS_STATISTICS); + if (null != statistics) { + return statistics; + } + + statistics = new Statistics(); + + long articles = select().from(Contents.class).where(Contents::getType, Types.ARTICLE) + .and(Contents::getStatus, Types.PUBLISH).count(); + long pages = select().from(Contents.class).where(Contents::getType, Types.PAGE) + .and(Contents::getStatus, Types.PUBLISH).count(); + long comments = select().from(Comments.class).count(); + long attachs = select().from(Attach.class).count(); + long tags = select().from(Metas.class).where(Metas::getType, Types.TAG).count(); + long categories = select().from(Metas.class).where(Metas::getType, Types.CATEGORY).count(); + + statistics.setArticles(articles); + statistics.setPages(pages); + statistics.setComments(comments); + statistics.setAttachs(attachs); + statistics.setTags(tags); + statistics.setCategories(categories); + + mapCache.set(Types.SYS_STATISTICS, statistics); + return statistics; + } /** * 查询文章归档 - * - * @return */ - List getArchives(); + public List getArchives() { + String sql = + "select strftime('%Y年%m月', datetime(created, 'unixepoch') ) as date_str, count(*) as count from t_contents " + + + "where type = 'post' and status = 'publish' group by date_str order by date_str desc"; + + List archives = select().bySQL(Archive.class, sql).all(); + if (null != archives) { + return archives.stream() + .map(this::parseArchive) + .collect(Collectors.toList()); + } + return new ArrayList<>(0); + } + + private Archive parseArchive(Archive archive) { + String dateStr = archive.getDateStr(); + Date sd = DateKit.toDate(dateStr + "01", "yyyy年MM月dd"); + archive.setDate(sd); + int start = DateKit.toUnix(sd); + Calendar calender = Calendar.getInstance(); + calender.setTime(sd); + calender.add(Calendar.MONTH, 1); + Date endSd = calender.getTime(); + int end = DateKit.toUnix(endSd) - 1; + + List contents = select().from(Contents.class) + .where(Contents::getType, Types.ARTICLE) + .and(Contents::getStatus, Types.PUBLISH) + .and(Contents::getCreated).gt(start) + .and(Contents::getCreated).lt(end) + .order(Contents::getCreated, OrderBy.DESC) + .all(); + + archive.setArticles(contents); + return archive; + } /** * 查询一条评论 - * @param coid - * @return + * + * @param coid 评论主键 */ - Comments getComment(Integer coid); + public Comments getComment(Integer coid) { + if (null != coid) { + return select().from(Comments.class).byId(coid); + } + return null; + } /** * 系统备份 - * @param bk_type - * @param bk_path - * @param fmt - * @return */ - BackResponse backup(String bk_type, String bk_path, String fmt) throws Exception; + public BackResponse backup(String bkType, String bkPath, String fmt) throws Exception { + BackResponse backResponse = new BackResponse(); + if ("attach".equals(bkType)) { + if (StringKit.isBlank(bkPath)) { + throw new ValidatorException("请输入备份文件存储路径"); + } + if (!Files.isDirectory(Paths.get(bkPath))) { + throw new ValidatorException("请输入一个存在的目录"); + } + String bkAttachDir = CLASSPATH + "upload"; + String bkThemesDir = CLASSPATH + "templates/themes"; + + String fname = DateKit.toString(new Date(), fmt) + "_" + StringKit.rand(5) + ".zip"; + + String attachPath = bkPath + "/" + "attachs_" + fname; + String themesPath = bkPath + "/" + "themes_" + fname; + + backResponse.setAttach_path(attachPath); + backResponse.setTheme_path(themesPath); + } + // 备份数据库 + if ("db".equals(bkType)) { + String filePath = "upload/" + DateKit.toString(new Date(), "yyyyMMddHHmmss") + "_" + + StringKit.rand(8) + ".db"; + String cp = CLASSPATH + filePath; + Files.createDirectory(Paths.get(cp)); + Files.copy(Paths.get(SqliteJdbc.DB_PATH), Paths.get(cp)); + backResponse.setSql_path("/" + filePath); + // 10秒后删除备份文件 + new Timer().schedule(new TimerTask() { + @Override + public void run() { + new File(cp).delete(); + } + }, 10 * 1000); + } + return backResponse; + } /** * 获取分类/标签列表 - * @return */ - List getMetas(String seachType, String type, int limit); + public List getMetas(String searchType, String type, int limit) { - /** - * 清楚缓存 - * @param key - */ - void cleanCache(String key); + if (StringKit.isBlank(searchType) || StringKit.isBlank(type)) { + return new ArrayList<>(0); + } + + if (limit < 1 || limit > TaleConst.MAX_POSTS) { + limit = 10; + } + + // 获取最新的项目 + if (Types.RECENT_META.equals(searchType)) { + String sql = + "select a.*, count(b.cid) as count from t_metas a left join `t_relationships` b on a.mid = b.mid " + + + "where a.type = ? group by a.mid order by count desc, a.mid desc limit ?"; + + return select().bySQL(Metas.class, sql, type, limit).all(); + } + + // 随机获取项目 + if (Types.RANDOM_META.equals(searchType)) { + List mids = select().bySQL(Integer.class, + "select mid from t_metas where type = ? order by random() * mid limit ?", + type, limit).all(); + if (BladeKit.isNotEmpty(mids)) { + String in = TaleUtils.listToInSql(mids); + String sql = + "select a.*, count(b.cid) as count from t_metas a left join `t_relationships` b on a.mid = b.mid " + + + "where a.mid in " + in + "group by a.mid order by count desc, a.mid desc"; + + return select().bySQL(Metas.class, sql).all(); + } + } + return new ArrayList<>(0); + } /** * 获取相邻的文章 * - * @param type 上一篇:prev 下一篇:next - * @param cid 当前文章id - * @return + * @param type 上一篇:prev | 下一篇:next + * @param created 当前文章创建时间 */ - Contents getNhContent(String type, Integer cid); + public Contents getNhContent(String type, Integer created) { + Contents contents = null; + if (Types.NEXT.equals(type)) { + contents = select().bySQL(Contents.class, + "SELECT * FROM t_contents WHERE type = ? AND status = ? AND created > ? ORDER BY created ASC LIMIT 1", + Types.ARTICLE, Types.PUBLISH, created).one(); + } + if (Types.PREV.equals(type)) { + contents = select().bySQL(Contents.class, + "SELECT * FROM t_contents WHERE type = ? AND status = ? AND created < ? ORDER BY created DESC LIMIT 1", + Types.ARTICLE, Types.PUBLISH, created).one(); + } + return contents; + } /** * 获取文章的评论 - * @param cid - * @param page - * @param limit - * @return + * + * @param cid 文章id + * @param page 页码 + * @param limit 每页条数 */ - Paginator getComments(Integer cid, int page, int limit); + public Page getComments(Integer cid, int page, int limit) { + return commentsService.getComments(cid, page, limit); + } + + /** + * 获取文章的评论总数 + * + * @param cid 文章id + */ + public long getCommentCount(Integer cid) { + return commentsService.getCommentCount(cid); + } + + /** + * 清楚缓存 + * + * @param key 缓存key + */ + public void cleanCache(String key) { + if (StringKit.isNotBlank(key)) { + if ("*".equals(key)) { + mapCache.clean(); + } else { + mapCache.del(key); + } + } + } + } diff --git a/src/main/java/com/tale/service/UsersService.java b/src/main/java/com/tale/service/UsersService.java deleted file mode 100644 index c02962c1..00000000 --- a/src/main/java/com/tale/service/UsersService.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.tale.service; - -import com.tale.model.Users; - -public interface UsersService { - - /** - * 根据用户id查询 - * - * @param uid - * @return - */ - Users byId(Integer uid); - - /** - * 更新用户信息 - * @param users - */ - void update(Users users); - - /** - * 用户登录 - * @param username - * @param password - * @return - */ - Users login(String username, String password); -} diff --git a/src/main/java/com/tale/service/impl/AttachServiceImpl.java b/src/main/java/com/tale/service/impl/AttachServiceImpl.java deleted file mode 100644 index 0947a710..00000000 --- a/src/main/java/com/tale/service/impl/AttachServiceImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.tale.service.impl; - -import com.blade.ioc.annotation.Inject; -import com.blade.ioc.annotation.Service; -import com.blade.jdbc.ActiveRecord; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.DateKit; -import com.tale.model.Attach; -import com.tale.service.AttachService; - -/** - * Created by biezhi on 2017/2/23. - */ -@Service -public class AttachServiceImpl implements AttachService { - - @Inject - private ActiveRecord activeRecord; - - @Override - public Attach save(String fname, String fkey, String ftype, Integer author) { - Attach attach = new Attach(); - attach.setFname(fname); - attach.setAuthor_id(author); - attach.setFkey(fkey); - attach.setFtype(ftype); - attach.setCreated(DateKit.getCurrentUnixTime()); - activeRecord.insert(attach); - return attach; - } - - @Override - public Attach byId(Integer id) { - if(null != id){ - return activeRecord.byId(Attach.class, id); - } - return null; - } - - @Override - public void delete(Integer id) { - if (null != id) { - activeRecord.delete(Attach.class, id); - } - } - - @Override - public Paginator getAttachs(Take take) { - if (null != take) { - return activeRecord.page(take); - } - return null; - } -} diff --git a/src/main/java/com/tale/service/impl/CommentsServiceImpl.java b/src/main/java/com/tale/service/impl/CommentsServiceImpl.java deleted file mode 100644 index 77528e50..00000000 --- a/src/main/java/com/tale/service/impl/CommentsServiceImpl.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.tale.service.impl; - -import com.blade.ioc.annotation.Inject; -import com.blade.ioc.annotation.Service; -import com.blade.jdbc.ActiveRecord; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.CollectionKit; -import com.blade.kit.DateKit; -import com.blade.kit.StringKit; -import com.tale.dto.Comment; -import com.tale.exception.TipException; -import com.tale.model.Comments; -import com.tale.model.Contents; -import com.tale.service.CommentsService; -import com.tale.service.ContentsService; -import com.tale.utils.TaleUtils; - -import java.util.ArrayList; -import java.util.List; - -@Service -public class CommentsServiceImpl implements CommentsService { - - @Inject - private ActiveRecord activeRecord; - - @Inject - private ContentsService contentsService; - - @Override - public void saveComment(Comments comments) { - if (null == comments) { - throw new TipException("评论对象为空"); - } - if (StringKit.isBlank(comments.getAuthor())) { - throw new TipException("姓名不能为空"); - } - if (StringKit.isBlank(comments.getMail())) { - throw new TipException("邮箱不能为空"); - } - if (!TaleUtils.isEmail(comments.getMail())) { - throw new TipException("请输入正确的邮箱格式"); - } - if (StringKit.isBlank(comments.getContent())) { - throw new TipException("评论内容不能为空"); - } - if(comments.getContent().length() < 5 || comments.getContent().length() > 2000){ - throw new TipException("评论字数在5-2000个字符"); - } - if (null == comments.getCid()) { - throw new TipException("评论文章不能为空"); - } - Contents contents = activeRecord.byId(Contents.class, comments.getCid()); - if (null == contents) { - throw new TipException("不存在的文章"); - } - try { - comments.setOwner_id(contents.getAuthor_id()); - comments.setCreated(DateKit.getCurrentUnixTime()); - activeRecord.insert(comments); - - Contents temp = new Contents(); - temp.setCid(contents.getCid()); - temp.setComments_num(contents.getComments_num() + 1); - activeRecord.update(temp); - } catch (Exception e) { - throw e; - } - } - - @Override - public void delete(Integer coid, Integer cid){ - if (null == coid) { - throw new TipException("主键为空"); - } - try { - activeRecord.delete(Comments.class, coid); - Contents contents = contentsService.getContents(cid+""); - if(null != contents && contents.getComments_num() > 0){ - Contents temp = new Contents(); - temp.setCid(cid); - temp.setComments_num(contents.getComments_num() - 1); - contentsService.update(temp); - } - } catch (Exception e) { - throw e; - } - } - - @Override - public Paginator getComments(Integer cid, int page, int limit) { - if (null != cid) { - Take take = new Take(Comments.class); - take.eq("cid", cid).eq("parent", 0); - take.page(page, limit, "coid desc"); - Paginator cp = activeRecord.page(take); - Paginator commentPaginator = new Paginator<>(cp.getTotal(), page, limit); - if (null != cp.getList()) { - List parents = cp.getList(); - List comments = new ArrayList<>(parents.size()); - parents.forEach(parent -> { - Comment comment = new Comment(parent); - List children = new ArrayList<>(); - getChildren(children, comment.getCoid()); - comment.setChildren(children); - if (CollectionKit.isNotEmpty(children)) { - comment.setLevels(1); - } - comments.add(comment); - }); - commentPaginator.setList(comments); - } - return commentPaginator; - } - return null; - } - - /** - * 获取该评论下的追加评论 - * - * @param coid - * @return - */ - private void getChildren(List list, Integer coid) { - List cms = activeRecord.list(new Take(Comments.class).eq("parent", coid).orderby("coid asc")); - if (null != cms) { - list.addAll(cms); - cms.forEach(c -> getChildren(list, c.getCoid())); - } - } - - @Override - public Paginator getComments(Take take) { - if(null != take){ - return activeRecord.page(take); - } - return null; - } - - @Override - public Comments byId(Integer coid) { - if(null != coid){ - return activeRecord.byId(Comments.class, coid); - } - return null; - } - - @Override - public void update(Comments comments) { - if(null != comments && null != comments.getCoid()){ - activeRecord.update(comments); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/tale/service/impl/ContentsServiceImpl.java b/src/main/java/com/tale/service/impl/ContentsServiceImpl.java deleted file mode 100644 index c3fc939b..00000000 --- a/src/main/java/com/tale/service/impl/ContentsServiceImpl.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.tale.service.impl; - -import com.blade.ioc.annotation.Inject; -import com.blade.ioc.annotation.Service; -import com.blade.jdbc.ActiveRecord; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.PageRow; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.DateKit; -import com.blade.kit.StringKit; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.init.TaleConst; -import com.tale.model.Contents; -import com.tale.service.ContentsService; -import com.tale.service.MetasService; -import com.tale.utils.TaleUtils; -import com.vdurmont.emoji.EmojiParser; - -import java.util.List; - -@Service -public class ContentsServiceImpl implements ContentsService { - - @Inject - private ActiveRecord activeRecord; - - @Inject - private MetasService metasService; - - @Override - public Contents getContents(String id) { - if (StringKit.isNotBlank(id)) { - if (StringKit.isNumber(id)) { - return activeRecord.byId(Contents.class, id); - } else { - return activeRecord.one(new Take(Contents.class).eq("slug", id)); - } - } - return null; - } - - public Paginator getContentsPage(Take take) { - if (null != take) { - return activeRecord.page(take); - } - return null; - } - - public Paginator getArticles(Take take) { - return this.getContentsPage(take); - } - - @Override - public Integer publish(Contents contents) { - if (null == contents) - throw new TipException("文章对象为空"); - if (StringKit.isBlank(contents.getTitle())) - throw new TipException("文章标题不能为空"); - if (contents.getTitle().length() > TaleConst.MAX_TITLE_COUNT) { - throw new TipException("文章标题最多可以输入"+ TaleConst.MAX_TITLE_COUNT +"个字符"); - } - - if (StringKit.isBlank(contents.getContent())) - throw new TipException("文章内容不能为空"); - // 最多可以输入5w个字 - if (contents.getContent().length() > TaleConst.MAX_TEXT_COUNT) - throw new TipException("文章内容最多可以输入"+ TaleConst.MAX_TEXT_COUNT +"个字符"); - if (null == contents.getAuthor_id()) - throw new TipException("请登录后发布文章"); - - if (StringKit.isNotBlank(contents.getSlug())) { - if (contents.getSlug().length() < 5) { - throw new TipException("路径太短了"); - } - if (!TaleUtils.isPath(contents.getSlug())) throw new TipException("您输入的路径不合法"); - - int count = activeRecord.count(new Take(Contents.class).eq("type", contents.getType()).eq("slug", contents.getSlug())); - if (count > 0) throw new TipException("该路径已经存在,请重新输入"); - } - - contents.setContent(EmojiParser.parseToAliases(contents.getContent())); - - int time = DateKit.getCurrentUnixTime(); - contents.setCreated(time); - contents.setModified(time); - - String tags = contents.getTags(); - String categories = contents.getCategories(); - - Integer cid = activeRecord.insert(contents); - - metasService.saveMetas(cid, tags, Types.TAG); - metasService.saveMetas(cid, categories, Types.CATEGORY); - - return cid; - } - - @Override - public void updateArticle(Contents contents) { - if (null == contents || null == contents.getCid()) { - throw new TipException("文章对象不能为空"); - } - if (StringKit.isBlank(contents.getTitle())) { - throw new TipException("文章标题不能为空"); - } - if (contents.getTitle().length() > TaleConst.MAX_TITLE_COUNT) { - throw new TipException("文章标题最多可以输入"+ TaleConst.MAX_TITLE_COUNT +"个字符"); - } - if (StringKit.isBlank(contents.getContent())) { - throw new TipException("文章内容不能为空"); - } - if (contents.getContent().length() > TaleConst.MAX_TEXT_COUNT) - throw new TipException("文章内容最多可以输入"+ TaleConst.MAX_TEXT_COUNT +"个字符"); - if (null == contents.getAuthor_id()) { - throw new TipException("请登录后发布文章"); - } - int time = DateKit.getCurrentUnixTime(); - contents.setModified(time); - - Integer cid = contents.getCid(); - - contents.setContent(EmojiParser.parseToAliases(contents.getContent())); - - activeRecord.update(contents); - - String sql = "delete from t_relationships where cid = ?"; - activeRecord.execute(sql, cid); - - metasService.saveMetas(cid, contents.getTags(), Types.TAG); - metasService.saveMetas(cid, contents.getCategories(), Types.CATEGORY); - } - - @Override - public void update(Contents contents) { - if (null != contents && null != contents.getCid()) { - activeRecord.update(contents); - } - } - - @Override - public void delete(int cid) { - Contents contents = this.getContents(cid + ""); - if (null != contents) { - activeRecord.delete(Contents.class, cid); - activeRecord.execute("delete from t_relationships where cid = ?", cid); - } - } - - @Override - public Paginator getArticles(Integer mid, int page, int limit) { - String countSql = "select count(0) from t_contents a left join t_relationships b on a.cid = b.cid " + - "where b.mid = ? and a.status = 'publish' and a.type = 'post'"; - int total = activeRecord.one(Integer.class, countSql, mid); - - PageRow pageRow = new PageRow(page, limit); - Paginator paginator = new Paginator<>(total, pageRow.getPage(), pageRow.getLimit()); - - String sql = "select a.* from t_contents a left join t_relationships b on a.cid = b.cid " + - "where b.mid = ? and a.status = 'publish' and a.type = 'post' order by a.created desc limit " + pageRow.getOffSet() + "," + limit; - - List list = activeRecord.list(Contents.class, sql, mid); - if (null != list) { - paginator.setList(list); - } - return paginator; - } - -} diff --git a/src/main/java/com/tale/service/impl/LogServiceImpl.java b/src/main/java/com/tale/service/impl/LogServiceImpl.java deleted file mode 100644 index e70d6423..00000000 --- a/src/main/java/com/tale/service/impl/LogServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.tale.service.impl; - -import com.blade.ioc.annotation.Inject; -import com.blade.ioc.annotation.Service; -import com.blade.jdbc.ActiveRecord; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.DateKit; -import com.tale.init.TaleConst; -import com.tale.model.Logs; -import com.tale.service.LogService; - -import java.util.List; - -/** - * Created by biezhi on 2017/2/26. - */ -@Service -public class LogServiceImpl implements LogService { - - @Inject - private ActiveRecord activeRecord; - - @Override - public void save(String action, String data, String ip, Integer author_id) { - Logs logs = new Logs(); - logs.setAction(action); - logs.setData(data); - logs.setIp(ip); - logs.setAuthor_id(author_id); - logs.setCreated(DateKit.getCurrentUnixTime()); - activeRecord.insert(logs); - } - - @Override - public List getLogs(int page, int limit) { - if (page <= 0) { - page = 1; - } - - if (limit < 1 || limit > TaleConst.MAX_POSTS) { - limit = 10; - } - Paginator logsPaginator = activeRecord.page(new Take(Logs.class).page(page, limit, "id desc")); - return logsPaginator.getList(); - } -} diff --git a/src/main/java/com/tale/service/impl/MetasServiceImpl.java b/src/main/java/com/tale/service/impl/MetasServiceImpl.java deleted file mode 100644 index e9d31296..00000000 --- a/src/main/java/com/tale/service/impl/MetasServiceImpl.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.tale.service.impl; - -import com.blade.ioc.annotation.Inject; -import com.blade.ioc.annotation.Service; -import com.blade.jdbc.ActiveRecord; -import com.blade.jdbc.core.Take; -import com.blade.kit.StringKit; -import com.tale.dto.MetaDto; -import com.tale.dto.Types; -import com.tale.exception.TipException; -import com.tale.model.Contents; -import com.tale.model.Metas; -import com.tale.model.Relationships; -import com.tale.service.MetasService; - -import java.util.List; - -@Service -public class MetasServiceImpl implements MetasService { - - @Inject - private ActiveRecord activeRecord; - - @Override - public List getMetas(String types) { - if (StringKit.isNotBlank(types)) { - return activeRecord.list(new Take(Metas.class).eq("type", types).orderby("sort desc, mid desc")); - } - return null; - } - - @Override - public MetaDto getMeta(String type, String name) { - if (StringKit.isNotBlank(type) && StringKit.isNotBlank(name)) { - String sql = "select a.*, count(b.cid) as count from t_metas a left join `t_relationships` b on a.mid = b.mid " + - "where a.type = ? and a.name = ? group by a.mid"; - return activeRecord.one(MetaDto.class, sql, type, name); - } - return null; - } - - @Override - public void saveMetas(Integer cid, String names, String type) { - if (null == cid) { - throw new TipException("项目关联id不能为空"); - } - if (StringKit.isNotBlank(names) && StringKit.isNotBlank(type)) { - String[] nameArr = StringKit.split(names, ","); - for (String name : nameArr) { - this.saveOrUpdate(cid, name, type); - } - } - } - - private void saveOrUpdate(Integer cid, String name, String type) { - Metas metas = activeRecord.one(new Take(Metas.class).eq("name", name).eq("type", type)); - int mid = 0; - if (null != metas) { -// Metas temp = new Metas(); -// temp.setMid(metas.getMid()); -// activeRecord.update(temp); - mid = metas.getMid(); - } else { - metas = new Metas(); - metas.setSlug(name); - metas.setName(name); - metas.setType(type); - mid = activeRecord.insert(metas); - } - if (mid != 0) { - int count = activeRecord.count(new Take(Relationships.class).eq("cid", cid).eq("mid", mid)); - if (count == 0) { - Relationships relationships = new Relationships(); - relationships.setCid(cid); - relationships.setMid(mid); - activeRecord.insert(relationships); - } - } - } - - @Override - public void delete(int mid) { - Metas metas = activeRecord.byId(Metas.class, mid); - if (null != metas) { - - String type = metas.getType(); - String name = metas.getName(); - - activeRecord.delete(Metas.class, mid); - - List rlist = activeRecord.list(new Take(Relationships.class).eq("mid", mid)); - if (null != rlist) { - for (Relationships r : rlist) { - Contents contents = activeRecord.byId(Contents.class, r.getCid()); - if (null != contents) { - boolean isUpdate = false; - Contents temp = new Contents(); - temp.setCid(r.getCid()); - if (type.equals(Types.CATEGORY)) { - temp.setCategories(reMeta(name, contents.getCategories())); - isUpdate = true; - } - if (type.equals(Types.TAG)) { - temp.setTags(reMeta(name, contents.getTags())); - isUpdate = true; - } - if(isUpdate){ - activeRecord.update(temp); - } - } - } - } - activeRecord.delete(new Take(Relationships.class).eq("mid", mid)); - } - } - - @Override - public void saveMeta(String type, String name, Integer mid) { - if (StringKit.isNotBlank(type) && StringKit.isNotBlank(name)) { - Metas metas = activeRecord.one(new Take(Metas.class).eq("type", type).eq("name", name)); - if (null != metas) { - throw new TipException("已经存在该项"); - } else { - if (null != mid) { - metas = new Metas(); - metas.setMid(mid); - metas.setName(name); - activeRecord.update(metas); - } else { - metas = new Metas(); - metas.setType(type); - metas.setName(name); - activeRecord.insert(metas); - } - } - } - } - - @Override - public void saveMeta(Metas metas) { - if (null != metas) { - activeRecord.insert(metas); - } - } - - @Override - public void update(Metas metas) { - if (null != metas && null != metas.getMid()) { - activeRecord.update(metas); - } - } - - private String reMeta(String name, String metas) { - String[] ms = StringKit.split(metas, ","); - StringBuffer sbuf = new StringBuffer(); - for (String m : ms) { - if (!name.equals(m)) { - sbuf.append(",").append(m); - } - } - if (sbuf.length() > 0) { - return sbuf.substring(1); - } - return ""; - } -} diff --git a/src/main/java/com/tale/service/impl/OptionsServiceImpl.java b/src/main/java/com/tale/service/impl/OptionsServiceImpl.java deleted file mode 100644 index 5683293a..00000000 --- a/src/main/java/com/tale/service/impl/OptionsServiceImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.tale.service.impl; - -import com.blade.ioc.annotation.Inject; -import com.blade.ioc.annotation.Service; -import com.blade.jdbc.ActiveRecord; -import com.blade.jdbc.core.Take; -import com.blade.kit.StringKit; -import com.tale.model.Options; -import com.tale.service.OptionsService; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Service -public class OptionsServiceImpl implements OptionsService { - - @Inject - private ActiveRecord activeRecord; - - @Override - public void saveOptions(Map options) { - if (null != options && !options.isEmpty()) { - options.forEach((k, v) -> saveOption(k, v)); - } - } - - @Override - public void saveOption(String key, String value) { - if (StringKit.isNotBlank(key) && StringKit.isNotBlank(value)) { - Options options = new Options(); - options.setName(key); - int count = activeRecord.count(options); - if (count == 0) { - options.setValue(value); - activeRecord.insert(options); - } else { - options.setValue(value); - activeRecord.update(options); - } - } - } - - @Override - public Map getOptions() { - Map options = new HashMap<>(); - List optionsList = activeRecord.list(new Options()); - for (Options option : optionsList) { - options.put(option.getName(), option.getValue()); - } - return options; - } - - @Override - public void deleteOption(String key) { - if(StringKit.isNotBlank(key)){ - activeRecord.delete(new Take(Options.class).like("name", key + "%")); - } - } -} diff --git a/src/main/java/com/tale/service/impl/SiteServiceImpl.java b/src/main/java/com/tale/service/impl/SiteServiceImpl.java deleted file mode 100644 index 0e8d67a8..00000000 --- a/src/main/java/com/tale/service/impl/SiteServiceImpl.java +++ /dev/null @@ -1,272 +0,0 @@ -package com.tale.service.impl; - -import com.blade.ioc.annotation.Inject; -import com.blade.ioc.annotation.Service; -import com.blade.jdbc.ar.SampleActiveRecord; -import com.blade.jdbc.core.Take; -import com.blade.jdbc.model.Paginator; -import com.blade.kit.*; -import com.tale.controller.admin.AttachController; -import com.tale.dto.*; -import com.tale.exception.TipException; -import com.tale.ext.Theme; -import com.tale.init.SqliteJdbc; -import com.tale.init.TaleConst; -import com.tale.model.*; -import com.tale.service.*; -import com.tale.utils.MapCache; -import com.tale.utils.TaleUtils; -import com.tale.utils.ZipUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.util.*; - -/** - * Created by biezhi on 2017/2/23. - */ -@Service -public class SiteServiceImpl implements SiteService { - - private static final Logger LOGGER = LoggerFactory.getLogger(SiteService.class); - - @Inject - private SampleActiveRecord activeRecord; - - @Inject - private OptionsService optionsService; - - @Inject - private LogService logService; - - @Inject - private MetasService metasService; - - @Inject - private CommentsService commentsService; - - public MapCache mapCache = new MapCache(); - - @Override - public void initSite(Users users) { - String pwd = Tools.md5(users.getUsername() + users.getPassword()); - users.setPassword(pwd); - users.setScreen_name(users.getUsername()); - users.setCreated(DateKit.getCurrentUnixTime()); - Integer uid = activeRecord.insert(users); - - try { - String cp = SiteServiceImpl.class.getClassLoader().getResource("").getPath(); - File lock = new File(cp + "install.lock"); - lock.createNewFile(); - TaleConst.INSTALL = Boolean.TRUE; - logService.save(LogActions.INIT_SITE, null, "", uid.intValue()); - } catch (Exception e) { - throw new TipException("初始化站点失败"); - } - } - - @Override - public List recentComments(int limit) { - if (limit < 0 || limit > 10) { - limit = 10; - } - Paginator cp = activeRecord.page(new Take(Comments.class).page(1, limit, "created desc")); - return cp.getList(); - } - - @Override - public List getContens(String type, int limit) { - - if (limit < 0 || limit > 20) { - limit = 10; - } - - Paginator cp = new Paginator<>(1, 1); - - // 最新文章 - if (Types.RECENT_ARTICLE.equals(type)) { - cp = activeRecord.page(new Take(Contents.class) - .eq("status", Types.PUBLISH).eq("type", Types.ARTICLE).page(1, limit, "created")); - } - - // 随机文章 - if (Types.RANDOM_ARTICLE.equals(type)) { - List cids = activeRecord.list(Integer.class, "select cid from t_contents where type = ? and status = ? order by rand() * cid limit ?", Types.ARTICLE, Types.PUBLISH, limit); - if (CollectionKit.isNotEmpty(cids)) { - Integer[] inCids = cids.toArray(new Integer[cids.size()]); - return activeRecord.list(new Take(Contents.class).in("cid", inCids)); - } - } - return cp.getList(); - } - - @Override - public Statistics getStatistics() { - - Statistics statistics = mapCache.get(Types.C_STATISTICS); - if (null != statistics) { - return statistics; - } - - statistics = new Statistics(); - int articles = activeRecord.count(new Take(Contents.class).eq("type", Types.ARTICLE).eq("status", Types.PUBLISH)); - int pages = activeRecord.count(new Take(Contents.class).eq("type", Types.PAGE).eq("status", Types.PUBLISH)); - int comments = activeRecord.count(new Take(Comments.class)); - int attachs = activeRecord.count(new Take(Attach.class)); - int links = activeRecord.count(new Take(Metas.class).eq("type", Types.LINK)); - int tags = activeRecord.count(new Take(Metas.class).eq("type", Types.TAG)); - int categories = activeRecord.count(new Take(Metas.class).eq("type", Types.CATEGORY)); - - statistics.setArticles(articles); - statistics.setPages(pages); - statistics.setComments(comments); - statistics.setAttachs(attachs); - statistics.setLinks(links); - statistics.setTags(tags); - statistics.setCategories(categories); - - mapCache.set(Types.C_STATISTICS, statistics); - return statistics; - } - - @Override - public List getArchives() { - - String sql = "select strftime('%Y年%m月', datetime(created, 'unixepoch') ) as date_str, count(*) as count from t_contents\n" + - "where type = 'post' and status = 'publish' group by date_str order by date_str desc"; - - List archives = activeRecord.list(Archive.class, sql); - if (null != archives) { - archives.forEach(archive -> { - String date_str = archive.getDate_str(); - Date sd = DateKit.dateFormat(date_str, "yyyy年MM月"); - archive.setDate(sd); - - int start = DateKit.getUnixTimeByDate(sd); - - Calendar calender = Calendar.getInstance(); - calender.setTime(sd); - calender.add(Calendar.MONTH, 1); - Date endSd = calender.getTime(); - - int end = DateKit.getUnixTimeByDate(endSd) - 1; - List contentss = activeRecord.list(new Take(Contents.class) - .eq("type", Types.ARTICLE) - .eq("status", Types.PUBLISH) - .gt("created", start).lt("created", end).orderby("created desc")); - archive.setArticles(contentss); - }); - } - return archives; - } - - @Override - public Comments getComment(Integer coid) { - if (null != coid) { - return activeRecord.byId(Comments.class, coid); - } - return null; - } - - @Override - public BackResponse backup(String bk_type, String bk_path, String fmt) throws Exception { - BackResponse backResponse = new BackResponse(); - if ("attach".equals(bk_type)) { - if (StringKit.isBlank(bk_path)) { - throw new TipException("请输入备份文件存储路径"); - } - if (!FileKit.isDirectory(bk_path)) { - throw new TipException("请输入一个存在的目录"); - } - String bkAttachDir = AttachController.CLASSPATH + "upload"; - String bkThemesDir = AttachController.CLASSPATH + "templates/themes"; - - String fname = DateKit.dateFormat(new Date(), fmt) + "_" + StringKit.getRandomNumber(5) + ".zip"; - - String attachPath = bk_path + "/" + "attachs_" + fname; - String themesPath = bk_path + "/" + "themes_" + fname; - - ZipUtils.zipFolder(bkAttachDir, attachPath); - ZipUtils.zipFolder(bkThemesDir, themesPath); - - backResponse.setAttach_path(attachPath); - backResponse.setTheme_path(themesPath); - } - // 备份数据库 - if("db".equals(bk_type)){ - String filePath = "upload/" + DateKit.getToday("yyyyMMddHHmmss") + "_" + StringKit.getRandomNumber(8) + ".db"; - String cp = AttachController.CLASSPATH + filePath; - FileKit.createParentDir(cp); - FileKit.copy(SqliteJdbc.DB_PATH, cp); - backResponse.setSql_path("/" + filePath); - // 10秒后删除备份文件 - new Timer().schedule(new TimerTask() { - @Override - public void run() { - new File(cp).delete(); - } - }, 10 * 1000); - } - return backResponse; - } - - @Override - public List getMetas(String searchType, String type, int limit) { - - if (StringKit.isBlank(searchType) || StringKit.isBlank(type)) { - return Theme.EMPTY; - } - - if (limit < 1 || limit > TaleConst.MAX_POSTS) { - limit = 10; - } - - // 获取最新的项目 - if (Types.RECENT_META.equals(searchType)) { - String sql = "select a.*, count(b.cid) as count from t_metas a left join `t_relationships` b on a.mid = b.mid " + - "where a.type = ? group by a.mid order by count desc, a.mid desc limit ?"; - return activeRecord.list(MetaDto.class, sql, type, limit); - } - - // 随机获取项目 - if (Types.RANDOM_META.equals(searchType)) { - List mids = activeRecord.list(Integer.class, "select mid from t_metas where type = ? order by rand() * mid limit ?", type, limit); - if (CollectionKit.isNotEmpty(mids)) { - String in = TaleUtils.listToInSql(mids); - String sql = "select a.*, count(b.cid) as count from t_metas a left join `t_relationships` b on a.mid = b.mid " + - "where a.mid in "+ in + "group by a.mid order by count desc, a.mid desc"; - return activeRecord.list(MetaDto.class, sql); - } - } - return Theme.EMPTY; - } - - @Override - public Contents getNhContent(String type, Integer cid) { - if(Types.NEXT.equals(type)){ - return activeRecord.one(new Take(Contents.class).eq("type", Types.ARTICLE).eq("status", Types.PUBLISH).gt("cid", cid)); - } - if(Types.PREV.equals(type)){ - return activeRecord.one(new Take(Contents.class).eq("type", Types.ARTICLE).eq("status", Types.PUBLISH).lt("cid", cid)); - } - return null; - } - - @Override - public Paginator getComments(Integer cid, int page, int limit) { - return commentsService.getComments(cid, page, limit); - } - - @Override - public void cleanCache(String key) { - if (StringKit.isNotBlank(key)) { - if ("*".equals(key)) { - mapCache.clean(); - } else { - mapCache.del(key); - } - } - } -} diff --git a/src/main/java/com/tale/service/impl/UsersServiceImpl.java b/src/main/java/com/tale/service/impl/UsersServiceImpl.java deleted file mode 100644 index 4e745ed7..00000000 --- a/src/main/java/com/tale/service/impl/UsersServiceImpl.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.tale.service.impl; - -import com.blade.ioc.annotation.Inject; -import com.blade.ioc.annotation.Service; -import com.blade.jdbc.ActiveRecord; -import com.blade.jdbc.core.Take; -import com.blade.kit.StringKit; -import com.blade.kit.Tools; -import com.tale.exception.TipException; -import com.tale.model.Users; -import com.tale.service.UsersService; - -@Service -public class UsersServiceImpl implements UsersService { - - @Inject - private ActiveRecord activeRecord; - - @Override - public Users byId(Integer uid) { - if (null == uid) { - return null; - } - return activeRecord.byId(Users.class, uid); - } - - @Override - public void update(Users users) { - if (null == users || null == users.getUid()) { - throw new TipException("对象为空"); - } - activeRecord.update(users); - } - - @Override - public Users login(String username, String password) { - - if (StringKit.isBlank(username) || StringKit.isBlank(password)) { - throw new TipException("用户名和密码不能为空"); - } - - int count = activeRecord.count(new Take(Users.class).eq("username", username)); - if (count < 1) { - throw new TipException("不存在该用户"); - } - String pwd = Tools.md5(username, password); - Users users = activeRecord.one(new Take(Users.class).eq("username", username).eq("password", pwd)); - if (null == users) { - throw new TipException("用户名或密码错误"); - } - return users; - } -} diff --git a/src/main/java/com/tale/task/PageViewTask.java b/src/main/java/com/tale/task/PageViewTask.java new file mode 100644 index 00000000..72036403 --- /dev/null +++ b/src/main/java/com/tale/task/PageViewTask.java @@ -0,0 +1,32 @@ +package com.tale.task; + +import com.blade.ioc.annotation.Bean; +import com.blade.task.annotation.Schedule; +import io.github.biezhi.anima.Anima; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 定时刷新 PV 到数据库 + * + * @author biezhi + * @date 2018/8/13 + */ +@Bean +public class PageViewTask { + +// public static Map counts = new ConcurrentHashMap<>(); +// +// @Schedule(cron = "*/5 * * * * ?") +// public void syncToDB() { +// Set cids = counts.keySet(); +// for (Long cid : cids) { +// long count = counts.get(cid).getAndSet(0); +// Anima.execute("update contents set hits = hits + " + count + " where cid = " + cid); +// } +// } + +} diff --git a/src/main/java/com/tale/utils/ImageUtils.java b/src/main/java/com/tale/utils/ImageUtils.java new file mode 100644 index 00000000..4f99b3df --- /dev/null +++ b/src/main/java/com/tale/utils/ImageUtils.java @@ -0,0 +1,47 @@ +package com.tale.utils; + +import lombok.experimental.UtilityClass; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; + +@UtilityClass +public class ImageUtils { + + /** + * 根据尺寸图片居中裁剪 + * + * @param src + * @param dist + * @param w + * @param h + * @throws IOException + */ + public static void cutCenterImage(String src, String dist, int w, int h) throws IOException { + String imgExt = src.substring(src.lastIndexOf(".") + 1); + Iterator iterator = ImageIO.getImageReadersByFormatName(imgExt); + ImageReader reader = (ImageReader) iterator.next(); + InputStream in = new FileInputStream(src); + ImageInputStream iis = ImageIO.createImageInputStream(in); + reader.setInput(iis, true); + ImageReadParam param = reader.getDefaultReadParam(); + int imageIndex = 0; + int x = (reader.getWidth(imageIndex) - w) / 2 <= 0 ? 0 : (reader.getWidth(imageIndex) - w) / 2; + int y = (reader.getHeight(imageIndex) - h) / 2 <= 0 ? 0 : (reader.getHeight(imageIndex) - h) / 2; + Rectangle rect = new Rectangle(x, y, w, h); + param.setSourceRegion(rect); + BufferedImage bi = reader.read(0, param); + ImageIO.write(bi, imgExt, new File(dist)); + + } + +} diff --git a/src/main/java/com/tale/utils/MapCache.java b/src/main/java/com/tale/utils/MapCache.java index 9f268a58..024db089 100644 --- a/src/main/java/com/tale/utils/MapCache.java +++ b/src/main/java/com/tale/utils/MapCache.java @@ -48,6 +48,8 @@ public T get(String key) { if (cacheObject.getExpired() <= 0 || cacheObject.getExpired() > cur) { Object result = cacheObject.getValue(); return (T) result; + }else{ + del(key); } } return null; diff --git a/src/main/java/com/tale/utils/RewriteUtils.java b/src/main/java/com/tale/utils/RewriteUtils.java deleted file mode 100644 index 7c2d77be..00000000 --- a/src/main/java/com/tale/utils/RewriteUtils.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.tale.utils; - -public class RewriteUtils { - - public static void rewrite(String suffix){ - try { - } catch (Exception e){ - e.printStackTrace(); - } - } - -} diff --git a/src/main/java/com/tale/utils/TaleUtils.java b/src/main/java/com/tale/utils/TaleUtils.java index 0d812817..01f5b5a2 100644 --- a/src/main/java/com/tale/utils/TaleUtils.java +++ b/src/main/java/com/tale/utils/TaleUtils.java @@ -1,37 +1,45 @@ package com.tale.utils; -import com.blade.context.WebContextHolder; +import com.blade.kit.UUID; import com.blade.kit.*; -import com.blade.mvc.http.Request; -import com.blade.mvc.http.Response; -import com.blade.mvc.http.wrapper.Session; +import com.blade.mvc.RouteContext; +import com.blade.mvc.http.Cookie; +import com.blade.mvc.http.Session; import com.sun.syndication.feed.rss.Channel; import com.sun.syndication.feed.rss.Content; import com.sun.syndication.feed.rss.Item; import com.sun.syndication.io.FeedException; import com.sun.syndication.io.WireFeedOutput; -import com.tale.controller.admin.AttachController; -import com.tale.ext.Commons; -import com.tale.ext.Theme; -import com.tale.init.TaleConst; -import com.tale.model.Contents; -import com.tale.model.Users; +import com.tale.bootstrap.TaleConst; +import com.tale.extension.Commons; +import com.tale.extension.Theme; +import com.tale.model.dto.RememberMe; +import com.tale.model.entity.Contents; +import com.tale.model.entity.Options; +import com.tale.model.entity.Users; import org.commonmark.Extension; import org.commonmark.ext.gfm.tables.TablesExtension; +import org.commonmark.node.Link; import org.commonmark.node.Node; import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.AttributeProvider; import org.commonmark.renderer.html.HtmlRenderer; import javax.imageio.ImageIO; import java.awt.*; import java.io.File; -import java.io.FileInputStream; -import java.io.OutputStream; -import java.text.Normalizer; -import java.util.*; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static com.tale.bootstrap.TaleConst.*; +import static io.github.biezhi.anima.Anima.select; +import static io.github.biezhi.anima.Anima.update; /** * Tale工具类 @@ -41,11 +49,9 @@ public class TaleUtils { /** - * 一个月 + * 一周 */ - private static final int one_month = 30 * 24 * 60 * 60; - - private static Random r = new Random(); + private static final int ONE_MONTH = 7 * 24 * 60 * 60; /** * 匹配邮箱正则 @@ -53,31 +59,72 @@ public class TaleUtils { private static final Pattern VALID_EMAIL_ADDRESS_REGEX = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); - private static final Pattern SLUG_REGEX = Pattern.compile("^[A-Za-z0-9_-]{5,100}$", Pattern.CASE_INSENSITIVE); + private static final Pattern SLUG_REGEX = Pattern.compile("^[A-Za-z0-9_-]{3,50}$", Pattern.CASE_INSENSITIVE); /** - * 设置记住密码cookie - * - * @param response - * @param uid + * 设置记住密码 cookie */ - public static void setCookie(Response response, Integer uid) { - try { - String val = Tools.enAes(uid.toString(), TaleConst.AES_SALT); - boolean isSSL = Commons.site_url().startsWith("https"); - response.cookie("/", TaleConst.USER_IN_COOKIE, val, one_month, isSSL); - } catch (Exception e) { - e.printStackTrace(); + public static void setCookie(RouteContext context, Integer uid) { + boolean isSSL = Commons.site_url().startsWith("https"); + + String token = EncryptKit.md5(UUID.UU64()); + RememberMe rememberMe = new RememberMe(); + rememberMe.setUid(uid); + rememberMe.setExpires(DateKit.nowUnix() + ONE_MONTH); + rememberMe.setRecentIp(Collections.singletonList(context.address())); + rememberMe.setToken(token); + + long count = select().from(Options.class).where(Options::getName, OPTION_SAFE_REMEMBER_ME).count(); + if (count == 0) { + Options options = new Options(); + options.setName(OPTION_SAFE_REMEMBER_ME); + options.setValue(JsonKit.toString(rememberMe)); + options.setDescription("记住我 Token"); + options.save(); + } else { + update().from(Options.class).set(Options::getValue, JsonKit.toString(rememberMe)) + .where(Options::getName, OPTION_SAFE_REMEMBER_ME) + .execute(); + } + + Cookie cookie = new Cookie(); + cookie.name(REMEMBER_IN_COOKIE); + cookie.value(token); + cookie.httpOnly(true); + cookie.secure(isSSL); + cookie.maxAge(ONE_MONTH); + cookie.path("/"); + + context.response().cookie(cookie); + } + + public static Integer getCookieUid(RouteContext context) { + String rememberToken = context.cookie(REMEMBER_IN_COOKIE); + if (null == rememberToken || rememberToken.isEmpty() || REMEMBER_TOKEN.isEmpty()) { + return null; + } + if (!REMEMBER_TOKEN.equals(rememberToken)) { + return null; + } + Options options = select().from(Options.class).where(Options::getName, OPTION_SAFE_REMEMBER_ME).one(); + if (null == options) { + return null; + } + RememberMe rememberMe = JsonKit.formJson(options.getValue(), RememberMe.class); + if (rememberMe.getExpires() < DateKit.nowUnix()) { + return null; + } + if (!rememberMe.getRecentIp().contains(context.address())) { + return null; } + return rememberMe.getUid(); } /** * 返回当前登录用户 - * - * @return */ public static Users getLoginUser() { - Session session = WebContextHolder.session(); + Session session = com.blade.mvc.WebContext.request().session(); if (null == session) { return null; } @@ -87,88 +134,57 @@ public static Users getLoginUser() { /** * 退出登录状态 - * - * @param session - * @param response */ - public static void logout(Session session, Response response) { - session.removeAttribute(TaleConst.LOGIN_SESSION_KEY); - response.removeCookie(TaleConst.USER_IN_COOKIE); - response.redirect(Commons.site_url()); - } - - /** - * 获取cookie中的用户id - * - * @param request - * @return - */ - public static Integer getCookieUid(Request request) { - if (null != request) { - String value = request.cookie(TaleConst.USER_IN_COOKIE); - if (StringKit.isNotBlank(value)) { - try { - String uid = Tools.deAes(value, TaleConst.AES_SALT); - return StringKit.isNotBlank(uid) && StringKit.isNumber(uid) ? Integer.valueOf(uid) : null; - } catch (Exception e) { - } - } - } - return null; - } - - /** - * 重新拼接字符串 - * - * @param arr - * @return - */ - public static String rejoin(String[] arr) { - if (null == arr) { - return ""; - } - if (arr.length == 1) { - return "'" + arr[0] + "'"; - } - String a = StringKit.join(arr, "','"); - a = a.substring(2) + "'"; - return a; + public static void logout(RouteContext context) { + TaleConst.REMEMBER_TOKEN = ""; + context.session().remove(TaleConst.LOGIN_SESSION_KEY); + context.response().removeCookie(TaleConst.REMEMBER_IN_COOKIE); + context.redirect(Commons.site_url()); } /** * markdown转换为html - * - * @param markdown - * @return */ public static String mdToHtml(String markdown) { if (StringKit.isBlank(markdown)) { return ""; } - List extensions = Arrays.asList(TablesExtension.create()); - Parser parser = Parser.builder().extensions(extensions).build(); - Node document = parser.parse(markdown); - HtmlRenderer renderer = HtmlRenderer.builder().extensions(extensions).build(); + List extensions = Collections.singletonList(TablesExtension.create()); + Parser parser = Parser.builder().extensions(extensions).build(); + Node document = parser.parse(markdown); + HtmlRenderer renderer = HtmlRenderer.builder() + .attributeProviderFactory(context -> new LinkAttributeProvider()) + .extensions(extensions).build(); + String content = renderer.render(document); content = Commons.emoji(content); // 支持网易云音乐输出 - if (TaleConst.BCONF.getBoolean("app.support_163_music", true) && content.contains("[mp3:")) { - content = content.replaceAll("\\[mp3:(\\d+)\\]", ""); + if (TaleConst.BCONF.getBoolean(ENV_SUPPORT_163_MUSIC, true) && content.contains(MP3_PREFIX)) { + content = content.replaceAll(MUSIC_REG_PATTERN, MUSIC_IFRAME); } // 支持gist代码输出 - if (TaleConst.BCONF.getBoolean("app.support_gist", true) && content.contains("https://gist.github.com/")) { - content = content.replaceAll("<script src=\"https://gist.github.com/(\\w+)/(\\w+)\\.js\"></script>", ""); + if (TaleConst.BCONF.getBoolean(ENV_SUPPORT_GIST, true) && content.contains(GIST_PREFIX_URL)) { + content = content.replaceAll(GIST_REG_PATTERN, GIST_REPLATE_PATTERN); } return content; } + static class LinkAttributeProvider implements AttributeProvider { + @Override + public void setAttributes(Node node, String tagName, Map attributes) { + if (node instanceof Link) { + attributes.put("target", "_blank"); + } + if (node instanceof org.commonmark.node.Image) { + attributes.put("title", attributes.get("alt")); + } + } + } + /** * 提取html中的文字 - * - * @param html - * @return */ public static String htmlToText(String html) { if (StringKit.isNotBlank(html)) { @@ -179,9 +195,6 @@ public static String htmlToText(String html) { /** * 判断文件是否是图片类型 - * - * @param imageFile - * @return */ public static boolean isImage(File imageFile) { if (!imageFile.exists()) { @@ -200,9 +213,6 @@ public static boolean isImage(File imageFile) { /** * 判断是否是邮箱 - * - * @param emailStr - * @return */ public static boolean isEmail(String emailStr) { Matcher matcher = VALID_EMAIL_ADDRESS_REGEX.matcher(emailStr); @@ -211,9 +221,6 @@ public static boolean isEmail(String emailStr) { /** * 判断是否是合法路径 - * - * @param slug - * @return */ public static boolean isPath(String slug) { if (StringKit.isNotBlank(slug)) { @@ -228,26 +235,37 @@ public static boolean isPath(String slug) { /** * 获取RSS输出 - * - * @param articles - * @return - * @throws FeedException */ public static String getRssXml(java.util.List articles) throws FeedException { Channel channel = new Channel("rss_2.0"); - channel.setTitle(TaleConst.OPTIONS.get("site_title")); + channel.setTitle(TaleConst.OPTIONS.get("site_title", "")); channel.setLink(Commons.site_url()); - channel.setDescription(TaleConst.OPTIONS.get("site_description")); + channel.setDescription(TaleConst.OPTIONS.get("site_description", "")); channel.setLanguage("zh-CN"); java.util.List items = new ArrayList<>(); articles.forEach(post -> { Item item = new Item(); item.setTitle(post.getTitle()); Content content = new Content(); - content.setValue(Theme.article(post.getContent())); + String value = Theme.article(post.getContent()); + + char[] xmlChar = value.toCharArray(); + for (int i = 0; i < xmlChar.length; ++i) { + if (xmlChar[i] > 0xFFFD) { + //直接替换掉0xb + xmlChar[i] = ' '; + } else if (xmlChar[i] < 0x20 && xmlChar[i] != 't' & xmlChar[i] != 'n' & xmlChar[i] != 'r') { + //直接替换掉0xb + xmlChar[i] = ' '; + } + } + + value = new String(xmlChar); + + content.setValue(value); item.setContent(content); item.setLink(Theme.permalink(post.getCid(), post.getSlug())); - item.setPubDate(DateKit.getDateByUnixTime(post.getCreated())); + item.setPubDate(DateKit.toDate(post.getCreated())); items.add(item); }); channel.setItems(items); @@ -255,11 +273,45 @@ public static String getRssXml(java.util.List articles) throws FeedExc return out.outputString(channel); } + private static final String SITEMAP_HEAD = "\n" + + ""; + + static class Url { + String loc; + String lastmod; + + public Url(String loc) { + this.loc = loc; + } + } + + public static String getSitemapXml(List articles) { + List urls = articles.stream() + .map(TaleUtils::parse) + .collect(Collectors.toList()); + urls.add(new Url(Commons.site_url() + "/archives")); + + String urlBody = urls.stream() + .map(url -> { + String s = "" + url.loc + ""; + if (null != url.lastmod) { + s += "" + url.lastmod + ""; + } + return s + ""; + }) + .collect(Collectors.joining("\n")); + + return SITEMAP_HEAD + urlBody + ""; + } + + private static Url parse(Contents contents) { + Url url = new Url(Commons.site_url() + "/article/" + contents.getCid()); + url.lastmod = DateKit.toString(contents.getModified(), "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + return url; + } + /** * 替换HTML脚本 - * - * @param value - * @return */ public static String cleanXSS(String value) { //You'll need to remove the spaces from the html entities below @@ -272,86 +324,11 @@ public static String cleanXSS(String value) { return value; } - /** - * 过滤XSS注入 - * - * @param value - * @return - */ - public static String filterXSS(String value) { - String cleanValue = null; - if (value != null) { - cleanValue = Normalizer.normalize(value, Normalizer.Form.NFD); - // Avoid null characters - cleanValue = cleanValue.replaceAll("\0", ""); - - // Avoid anything between script tags - Pattern scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE); - cleanValue = scriptPattern.matcher(cleanValue).replaceAll(""); - - // Avoid anything in a src='...' type of expression - scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); - cleanValue = scriptPattern.matcher(cleanValue).replaceAll(""); - - scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); - cleanValue = scriptPattern.matcher(cleanValue).replaceAll(""); - - // Remove any lonesome tag - scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE); - cleanValue = scriptPattern.matcher(cleanValue).replaceAll(""); - - // Remove any lonesome - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/article/_footer.html b/src/main/resources/templates/admin/article/_footer.html new file mode 100644 index 00000000..a7195ac9 --- /dev/null +++ b/src/main/resources/templates/admin/article/_footer.html @@ -0,0 +1,13 @@ +#include('../footer.html') + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/article/_header.html b/src/main/resources/templates/admin/article/_header.html new file mode 100644 index 00000000..1ec35f49 --- /dev/null +++ b/src/main/resources/templates/admin/article/_header.html @@ -0,0 +1,73 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/article/edit.html b/src/main/resources/templates/admin/article/edit.html new file mode 100644 index 00000000..ef7cf6a1 --- /dev/null +++ b/src/main/resources/templates/admin/article/edit.html @@ -0,0 +1,121 @@ +#include('../header.html',{active: 'articles', title:'编辑文章'}) +#include('./_header.html') +

+ +
+
+
+

编辑文章

+
+
+ + +
+ + + + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+

可以为你的文章添加一张缩略图 ;)

+
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ +
+
+ 返回列表 + + +
+
+
+
+
+
+
+#include('./_footer.html') + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/article/new.html b/src/main/resources/templates/admin/article/new.html new file mode 100644 index 00000000..5fd76f23 --- /dev/null +++ b/src/main/resources/templates/admin/article/new.html @@ -0,0 +1,122 @@ +#include('../header.html',{active: 'publish', title:'发布文章'}) +#include('./_header.html') +
+ +
+
+
+

发布文章

+
+
+ +
+ + + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+

可以为你的文章添加一张缩略图 ;)

+
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ +
+
+ 返回列表 + + +
+
+
+
+
+
+
+#include('./_footer.html') + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/article_edit.html b/src/main/resources/templates/admin/article_edit.html deleted file mode 100644 index 34b924b4..00000000 --- a/src/main/resources/templates/admin/article_edit.html +++ /dev/null @@ -1,214 +0,0 @@ -#include('./header.html',{active: 'publish', title:'保存文章'}) - - - - - - - - - - - - -
-
-

- #if(null != contents) - 编辑文章 - #else - 发布文章 - #end -

-
-
- - - -
- - - - - - - - - -
- -
- -
- -
- -
- -
- -
- -
-
- - - -
- -
-
-
${contents.content ?! ''}
-
- -
- -
-
-
-
- -
- -
-
-
-
- -
- -
-
-
-
- -
- -
-
-
-
- -
-
-
-

可以为你的文章添加一张缩略图 ;)

-
-
- -
- -
- -
- 返回列表 - - -
-
-
-
-#include('./footer.html') - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/article_list.html b/src/main/resources/templates/admin/article_list.html deleted file mode 100644 index 652df9b2..00000000 --- a/src/main/resources/templates/admin/article_list.html +++ /dev/null @@ -1,79 +0,0 @@ -#include('./header.html',{active:'article', title:'文章管理'}) -
-
-

文章管理

-
-
- - - - - - - - - - - - - #for(post : articles.list) - - - - - - - - - #end - -
文章标题发布时间浏览量所属分类发布状态操作
- ${post.title} - ${fmtdate(post.created, 'yyyy-MM-dd HH:mm:ss')}${post.hits}${post.categories} - #if(post.status == 'publish') - 已发布 - #elseif(post.status == 'draft') - 草稿 - #end - - 编辑 - 删除 - 预览 - -
- #call pageAdminNav(articles) -
-
- -#include('./footer.html') - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/articles.html b/src/main/resources/templates/admin/articles.html new file mode 100644 index 00000000..eba517ed --- /dev/null +++ b/src/main/resources/templates/admin/articles.html @@ -0,0 +1,190 @@ +#include('./header.html',{active:'articles', title:'文章管理'}) +
+ +
+
+
+

文章管理

+
+
+
+
+
+
+
+ +
+ + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
文章标题发布时间浏览量所属分类发布状态操作
+ {{ item.title }} + {{ item.created | formatUnix }}{{ item.hits }}{{ item.categories }} + 已发布 + 草稿 + + + 编辑 + + + 删除 + + + 预览 + +
+
+ + +
+
+
+
+
+
+#include('./footer.html') + + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/attach.html b/src/main/resources/templates/admin/attach.html deleted file mode 100644 index 263665f4..00000000 --- a/src/main/resources/templates/admin/attach.html +++ /dev/null @@ -1,166 +0,0 @@ -#include('./header.html',{active:'attach', title:'文件管理'}) - - -
-
-

文件管理

-
-
-
- -
-
-
- -
-
-

将文件拖至此处或点击上传.${attach_url}

-

- 一次最多可以上传10个文件 -

-
-
-
-
-
-
- #if(is_empty(attachs)) -
-
-
- 目前还没有一个附件呢,你可以上传试试! -
-
-
- #else - - #for(attach : attachs.list) -
- - - -
- ${substr(attach.fname, 12)} -
-
- - -
-
- #end - #call pageAdminNav(attachs) - #end -
-
- -#include('./footer.html') - - - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/attaches.html b/src/main/resources/templates/admin/attaches.html new file mode 100644 index 00000000..f010ab76 --- /dev/null +++ b/src/main/resources/templates/admin/attaches.html @@ -0,0 +1,237 @@ +#include('./header.html',{active:'attaches', title:'文件管理'}) + + +
+ +
+
+

附件管理

+
+
+
+
+ +
+
+
+ +
+
+

将文件拖至此处或点击上传.${attachURL()}

+

+ 一次最多可以上传10个文件 +

+
+
+
+
+
+
+
+
+ 目前还没有一个附件呢,你可以上传试试! +
+
+
+ +
+ + + + +
+ {{item.fname | truncate(15)}} +
+
+ + +
+
+ +
+ + +
+
+
+
+
+#include('./footer.html') + + + + + diff --git a/src/main/resources/templates/admin/categories.html b/src/main/resources/templates/admin/categories.html new file mode 100644 index 00000000..a4414d5d --- /dev/null +++ b/src/main/resources/templates/admin/categories.html @@ -0,0 +1,180 @@ +#include('./header.html',{has_sub:'other', active:'categories', title:'分类管理'}) +
+ +
+
+
+

分类/标签管理

+
+
+ +
+
+
+
+

分类列表

+
+
+
+ +   + + +
+
+
+
+ +
+
+
+

标签列表

+
+
+
+ +   + +
+
+
+
+
+ +
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+#include('./footer.html') + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/category.html b/src/main/resources/templates/admin/category.html deleted file mode 100644 index f6f0a83c..00000000 --- a/src/main/resources/templates/admin/category.html +++ /dev/null @@ -1,124 +0,0 @@ -#include('./header.html',{has_sub:'other', active:'category', title:'分类管理'}) -
-
-

分类/标签管理

-
-
-
-
-

分类列表

-
-
- #for(c : categories) -
- #if(c.name == '默认分类') - - #else - - - #end -
- #end -
-
-
- -
-
-
-

标签列表

-
-
- #for(c : tags) -
- - -
- #end -
-
-
- -
-
-
-
- -
- -
- -
-
-
-
-
-#include('./footer.html') - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/comment_list.html b/src/main/resources/templates/admin/comment_list.html deleted file mode 100644 index c6f0d01f..00000000 --- a/src/main/resources/templates/admin/comment_list.html +++ /dev/null @@ -1,120 +0,0 @@ -#include('./header.html',{has_sub:'other', active:'comments', title:'评论管理'}) -
-
-

评论管理

-
-
- - - - - - - - - - - - - - #for(comment : comments.list) - - - - - - - - - - #end - -
评论内容评论人评论时间评论人邮箱评论人网址评论状态操作
- ${article(comment.content)} - ${comment.author}${fmtdate(comment.created, 'yyyy-MM-dd HH:mm:ss')}${comment.mail}${comment.url} - #if(comment.status == 'approved') - 审核通过 - #elseif(comment.status == 'not_audit') - 未审核 - #end - - 回复 - #if(comment.status == 'not_audit') - 通过 - #end - 删除 -
- #call pageAdminNav(comments) -
-
- -#include('./footer.html') - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/comments.html b/src/main/resources/templates/admin/comments.html new file mode 100644 index 00000000..dfbff820 --- /dev/null +++ b/src/main/resources/templates/admin/comments.html @@ -0,0 +1,197 @@ +#include('./header.html',{active:'comments', title:'评论管理'}) +
+ +
+
+
+

评论管理

+
+
+
+
+
+ 目前还没有评论哦! +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
评论内容评论人评论时间评论人邮箱评论人网址评论状态操作
+ {{ item.content }} + {{ item.author }}{{ item.created | formatUnix }}{{ item.mail }}{{ item.url }} +
+ 后台回复 + 审核通过 +
+
+ 后台回复 + 未审核 +
+
+ + + +
+
+ + +
+
+
+
+#include('./footer.html') + + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/footer.html b/src/main/resources/templates/admin/footer.html index 5a28465e..28f42ba6 100644 --- a/src/main/resources/templates/admin/footer.html +++ b/src/main/resources/templates/admin/footer.html @@ -3,9 +3,19 @@ - - - - - - + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/admin/header.html b/src/main/resources/templates/admin/header.html index 987fd5a1..087865c8 100644 --- a/src/main/resources/templates/admin/header.html +++ b/src/main/resources/templates/admin/header.html @@ -4,20 +4,35 @@ ${title ?! '博客后台'} - Tale - + + - - - - - + #if(allow_cloud_CDN() == "true") + + + + + + #else + + + + + + + + + #end
@@ -29,7 +44,7 @@
-
+
Tale Blog
@@ -49,9 +64,8 @@ user-img @@ -68,40 +82,41 @@ 仪表盘
  • - 发布文章 + 发布文章
  • -
  • - 文章管理 +
  • + 文章管理
  • -
  • - 文件管理 +
  • + 页面管理
  • -
  • - 友链管理 + +
  • + 文件管理
  • 其他管理
  • -
  • - 系统设置 +
  • + 主题设置
  • -
  • - 高级选项 +
  • + 系统设置
  • #for(item : plugin_menus) diff --git a/src/main/resources/templates/admin/index.html b/src/main/resources/templates/admin/index.html index 5c8bbb74..a284c884 100644 --- a/src/main/resources/templates/admin/index.html +++ b/src/main/resources/templates/admin/index.html @@ -1,110 +1,218 @@ #include('./header.html',{active:'home', title:'管理中心'}) -
    -
    -

    Tale仪表盘

    -
    - -
    -
    -
    - -
    - 发表了${statistics.articles}篇文章 -
    -
    -
    -
    -
    - -
    - 收到了${statistics.comments}条留言 -
    +
    + +
    +
    +
    +

    Tale仪表盘

    -
    +
    -
    -
    - -
    - 上传了${statistics.attachs}个附件 -
    -
    -
    +
    +
    +
    + + + +
    + 发表了${statistics.articles}篇文章 +
    +
    +
    +
    +
    + + + +
    + 收到了${statistics.comments}条留言 +
    +
    +
    -
    -
    - -
    - 友链了${statistics.links}个好友 +
    +
    + + + +
    + 上传了${statistics.attachs}个附件 +
    +
    +
    -
    -
    -
    -
    -
    -
    -
    -

    最新文章

    -
    -
    -
      - #for(article : articles) -
    • +
      +
      +
      +
      +

      最新文章

      +
      +
      +
        + #for(item : articles) +
      • ${article.comments_num} - ${article.title} -
      • - #end -
      -
      -
      -
      -
      -
      -
      -

      最新留言

      -
      -
      - #if(null == comments || comments.size() == 0) -
      - 还没有收到留言. + title="${item.commentsNum}条评论">${item.commentsNum} + ${item.title} +
    • + #end +
    +
    +
    +
    +
    +
    +
    +

    最新留言

    +
    +
    +
    + 还没有收到留言. +
    + +
    +
    +
    + + + +
    +

    + {{item.author}} +

    +

    + + ${article("{{item.content}}")} + +

    +

    {{item.created | formatUnix('MM月DD日 HH:mm')}}

    +

    + + +

    +
    +
    +
    +
    +
    +
    +
    +
    +

    系统日志

    +
    +
    +
      +
    • + {{item.created | formatUnix}}   {{item.action}} +
    • +
    + +
    +
    - #else -
      - #for(comment : comments) -
    • - #if(null != comment.url && comment.url != '') - ${comment.author} - #else - ${comment.author} - #end - 于${fmtdate(comment.created,'MM月dd日HH:mm')} :${article(comment.content)} -
    • - #end -
    - #end -
    -
    -
    -
    -
    -
    -

    系统日志

    -
    -
    -
      - #for(log : logs) -
    • - ${fmtdate(log.created, 'yyyy-MM-dd HH:mm:ss')} => ${log.action} -
    • - #end -
    +
    #include('./footer.html') + \ No newline at end of file diff --git a/src/main/resources/templates/admin/links.html b/src/main/resources/templates/admin/links.html deleted file mode 100644 index 985f4a1a..00000000 --- a/src/main/resources/templates/admin/links.html +++ /dev/null @@ -1,142 +0,0 @@ -#include('./header.html',{active:'links', title:'友链管理'}) -
    -
    -

    友链管理

    -
    -
    - - - - - - - - - - - - #for(link : links) - - - - - - - - #end - -
    链接名称链接地址链接LOGO链接排序操作
    ${link.name}${link.slug}${link.description}${link.sort} - 编辑 - 删除 -
    -
    -
    -
    -
    -

    保存友链

    -
    - -
    - - - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - - -
    -
    -
    -
    - -
    -
    - -#include('./footer.html') - - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/login.html b/src/main/resources/templates/admin/login.html index 75feb45b..e393dd17 100644 --- a/src/main/resources/templates/admin/login.html +++ b/src/main/resources/templates/admin/login.html @@ -3,15 +3,18 @@ + Tale - 用户登录 - - - - - -
    -
    -

    - #if(null != contents) - 编辑页面 - #else - 发布新页面 - #end -

    -
    -
    - - - -
    - - - - - - -
    - -
    - -
    - -
    - - - -
    - -
    -
    -
    ${contents.content ?! ''}
    -
    - -
    - -
    -
    -
    -
    - -
    - -
    - - -
    -
    -
    - -
    -#include('./footer.html') - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/page_list.html b/src/main/resources/templates/admin/page_list.html deleted file mode 100644 index 3a6a083b..00000000 --- a/src/main/resources/templates/admin/page_list.html +++ /dev/null @@ -1,74 +0,0 @@ -#include('./header.html',{has_sub:'other', active:'page', title:'页面管理'}) -
    -
    -

    文章管理

    -
    -
    - - - - - - - - - - - - - #for(post : articles.list) - - - - - - - - #end - -
    页面名称页面路径发布时间发布状态操作
    ${post.title}${post.slug}${fmtdate(post.created, 'yyyy-MM-dd HH:mm:ss')} - #if(post.status == 'publish') - 已发布 - #elseif(post.status == 'draft') - 草稿 - #end - - 编辑 - 删除 - 预览 -
    -
    -
    - -#include('./footer.html') - - - - \ No newline at end of file diff --git a/src/main/resources/templates/admin/pages.html b/src/main/resources/templates/admin/pages.html new file mode 100644 index 00000000..0c13a0ca --- /dev/null +++ b/src/main/resources/templates/admin/pages.html @@ -0,0 +1,139 @@ +#include('./header.html',{active:'pages', title:'页面管理'}) +
    + +
    +
    +
    +

    页面管理

    +
    +
    +
    + +
    +
    +
    + + + + + + + + + + + + + + + + + + + +
    页面名称页面路径发布时间发布状态操作
    {{ item.title }}{{ item.slug }}{{ item.created | formatUnix }} + 已发布 + 草稿 + + + 编辑 + + + 删除 + + + 预览 + +
    +
    + + +
    +
    +
    +
    +
    + +#include('./footer.html') + + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/profile.html b/src/main/resources/templates/admin/profile.html index f79a17ee..0b82828b 100644 --- a/src/main/resources/templates/admin/profile.html +++ b/src/main/resources/templates/admin/profile.html @@ -21,8 +21,8 @@

    个人信息

    - +
    @@ -81,10 +81,9 @@

    修改密码

    #include('./footer.html') - - - + + + - \ No newline at end of file + diff --git a/src/main/resources/templates/admin/themes.html b/src/main/resources/templates/admin/themes.html new file mode 100644 index 00000000..0a1122db --- /dev/null +++ b/src/main/resources/templates/admin/themes.html @@ -0,0 +1,88 @@ +#include('./header.html',{active:'themes', title:'主题管理'}) +
    + +
    +
    +
    +

    主题管理

    +
    +
    +
    + +
    + 主题:{{item.name}} (当前主题) +
    +
    + + 主题设置 + + +
    +
    +
    +
    +
    +
    + +#include('./footer.html') + + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/tpl_list.html b/src/main/resources/templates/admin/tpl_list.html new file mode 100644 index 00000000..95d65167 --- /dev/null +++ b/src/main/resources/templates/admin/tpl_list.html @@ -0,0 +1,100 @@ +#include('./header.html',{has_sub:'other', active:'template', title:'编辑模板'}) +
    + +
    +
    +
    +

    编辑模板

    +
    +
    +
    + + + + +
    + +
    +
    +
    +
    +
    +
    + +#include('./footer.html') + + + \ No newline at end of file diff --git a/src/main/resources/templates/comm/error_404.html b/src/main/resources/templates/comm/error_404.html index 6a678a1b..fdc3d7d2 100644 --- a/src/main/resources/templates/comm/error_404.html +++ b/src/main/resources/templates/comm/error_404.html @@ -6,8 +6,8 @@ - - + + diff --git a/src/main/resources/templates/comm/error_500.html b/src/main/resources/templates/comm/error_500.html index 7fa58f5e..63b152ce 100644 --- a/src/main/resources/templates/comm/error_500.html +++ b/src/main/resources/templates/comm/error_500.html @@ -6,8 +6,8 @@ - - + + diff --git a/src/main/resources/templates/comm/macros.html b/src/main/resources/templates/comm/macros.html index 260c8465..c0e4b07c 100644 --- a/src/main/resources/templates/comm/macros.html +++ b/src/main/resources/templates/comm/macros.html @@ -12,30 +12,4 @@ #end -#end - -#macro pageAdminNav(pageInfo) -
    - #end \ No newline at end of file diff --git a/src/main/resources/templates/comm/tale_comment.html b/src/main/resources/templates/comm/tale_comment.html index dd3d6535..f0019259 100644 --- a/src/main/resources/templates/comm/tale_comment.html +++ b/src/main/resources/templates/comm/tale_comment.html @@ -1,54 +1,56 @@ - \ No newline at end of file diff --git a/src/main/resources/templates/comm/ts_base_layout.html b/src/main/resources/templates/comm/ts_base_layout.html new file mode 100644 index 00000000..2dd3741a --- /dev/null +++ b/src/main/resources/templates/comm/ts_base_layout.html @@ -0,0 +1,47 @@ +#include('/admin/header.html',{active:'themes', title:'主题设置'}) +
    +${bodyContent} +
    +#include('/admin/footer.html') + + + \ No newline at end of file diff --git a/src/main/resources/templates/install.html b/src/main/resources/templates/install.html index bd36089a..f51fc82c 100644 --- a/src/main/resources/templates/install.html +++ b/src/main/resources/templates/install.html @@ -3,12 +3,14 @@ + Tale - 博客安装 - - - - + + + + + ",rE:!0,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[r],starts:{e:"",rE:!0,sL:"javascript"}},t,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"",c:[{cN:"title",b:/[^ \/><\n\t]+/,r:0},r]}]}}),hljs.registerLanguage("markdown",function(){return{aliases:["md","mkdown","mkd"],c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:"^\\[.+\\]:",rB:!0,c:[{cN:"link_reference",b:"\\[",e:"\\]:",eB:!0,eE:!0,starts:{cN:"link_url",e:"$"}}]}]}}),hljs.registerLanguage("nginx",function(e){var t={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},r={eW:!0,l:"[a-z/_]+",k:{built_in:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,t],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{cN:"url",b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[t]},{cN:"regexp",c:[e.BE,t],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},t]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"title",b:e.UIR,starts:r}],r:0}],i:"[^\\s\\}]"}}),hljs.registerLanguage("objectivec",function(e){var t={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"NSString NSData NSDictionary CGRect CGPoint UIButton UILabel UITextView UIWebView MKMapView NSView NSViewController NSWindow NSWindowController NSSet NSUUID NSIndexSet UISegmentedControl NSObject UITableViewDelegate UITableViewDataSource NSThread UIActivityIndicator UITabbar UIToolBar UIBarButtonItem UIImageView NSAutoreleasePool UITableView BOOL NSInteger CGFloat NSException NSLog NSMutableString NSMutableArray NSMutableDictionary NSURL NSIndexPath CGSize UITableViewCell UIView UIViewController UINavigationBar UINavigationController UITabBarController UIPopoverController UIPopoverControllerDelegate UIImage NSNumber UISearchBar NSFetchedResultsController NSFetchedResultsChangeType UIScrollView UIScrollViewDelegate UIEdgeInsets UIColor UIFont UIApplication NSNotFound NSNotificationCenter NSNotification UILocalNotification NSBundle NSFileManager NSTimeInterval NSDate NSCalendar NSUserDefaults UIWindow NSRange NSArray NSError NSURLRequest NSURLConnection NSURLSession NSURLSessionDataTask NSURLSessionDownloadTask NSURLSessionUploadTask NSURLResponseUIInterfaceOrientation MPMoviePlayerController dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},r=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["m","mm","objc","obj-c"],k:t,l:r,i:""}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:r,c:[e.UTM]},{cN:"variable",b:"\\."+e.UIR,r:0}]}}),hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},n={b:"->{",e:"}"},a={cN:"variable",v:[{b:/\$\d/},{b:/[\$\%\@](\^\w\b|#\w+(\:\:\w+)*|{\w+}|\w+(\:\:\w*)*)/},{b:/[\$\%\@][^\s\w{]/,r:0}]},i={cN:"comment",b:"^(__END__|__DATA__)",e:"\\n$",r:5},s=[e.BE,r,a],c=[a,e.HCM,i,{cN:"comment",b:"^\\=\\w",e:"\\=cut",eW:!0},n,{cN:"string",c:s,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,i,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",r:5},{cN:"operator",b:"-\\w\\b",r:0}];return r.c=c,n.c=c,{aliases:["pl"],k:t,c:c}}),hljs.registerLanguage("php",function(e){var t={cN:"variable",b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},r={cN:"preprocessor",b:/<\?(php)?|\?>/},n={cN:"string",c:[e.BE,r],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},a={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.CLCM,e.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"},r]},{cN:"comment",b:"__halt_compiler.+?;",eW:!0,k:"__halt_compiler",l:e.UIR},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},r,t,{b:/->+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",t,e.CBCM,n,a]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},n,a]}}),hljs.registerLanguage("python",function(e){var t={cN:"prompt",b:/^(>>>|\.\.\.) /},r={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[t],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[t],r:10},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},e.ASM,e.QSM]},n={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},a={cN:"params",b:/\(/,e:/\)/,c:["self",t,n,r]};return{aliases:["py","gyp"],k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},i:/(<\/|->|\?)/,c:[t,n,r,e.HCM,{v:[{cN:"function",bK:"def",r:10},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n]/,c:[e.UTM,a]},{cN:"decorator",b:/@/,e:/$/},{b:/\b(print|exec)\(/}]}}),hljs.registerLanguage("ruby",function(e){var t="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r="and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",n={cN:"yardoctag",b:"@[A-Za-z]+"},a={cN:"value",b:"#<",e:">"},i={cN:"comment",v:[{b:"#",e:"$",c:[n]},{b:"^\\=begin",e:"^\\=end",c:[n],r:10},{b:"^__END__",e:"\\n$"}]},s={cN:"subst",b:"#\\{",e:"}",k:r},c={cN:"string",c:[e.BE,s],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/}]},o={cN:"params",b:"\\(",e:"\\)",k:r},l=[c,a,i,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]},i]},{cN:"function",bK:"def",e:" |$|;",r:0,c:[e.inherit(e.TM,{b:t}),o,i]},{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":",c:[c,{b:t}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:[a,i,{cN:"regexp",c:[e.BE,s],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}],r:0}];s.c=l,o.c=l;var u="[>?]>",d="[\\w#]+\\(\\w+\\):\\d+:\\d+>",b="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",p=[{b:/^\s*=>/,cN:"status",starts:{e:"$",c:l}},{cN:"prompt",b:"^("+u+"|"+d+"|"+b+")",starts:{e:"$",c:l}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,c:[i].concat(p).concat(l)}}),hljs.registerLanguage("sql",function(e){var t={cN:"comment",b:"--",e:"$"};return{cI:!0,i:/[<>]/,c:[{cN:"operator",bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate savepoint release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup",e:/;/,eW:!0,k:{keyword:"abs absolute acos action add adddate addtime aes_decrypt aes_encrypt after aggregate all allocate alter analyze and any are as asc ascii asin assertion at atan atan2 atn2 authorization authors avg backup before begin benchmark between bin binlog bit_and bit_count bit_length bit_or bit_xor both by cache call cascade cascaded case cast catalog ceil ceiling chain change changed char_length character_length charindex charset check checksum checksum_agg choose close coalesce coercibility collate collation collationproperty column columns columns_updated commit compress concat concat_ws concurrent connect connection connection_id consistent constraint constraints continue contributors conv convert convert_tz corresponding cos cot count count_big crc32 create cross cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime data database databases datalength date_add date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts datetimeoffsetfromparts day dayname dayofmonth dayofweek dayofyear deallocate declare decode default deferrable deferred degrees delayed delete des_decrypt des_encrypt des_key_file desc describe descriptor diagnostics difference disconnect distinct distinctrow div do domain double drop dumpfile each else elt enclosed encode encrypt end end-exec engine engines eomonth errors escape escaped event eventdata events except exception exec execute exists exp explain export_set extended external extract fast fetch field fields find_in_set first first_value floor flush for force foreign format found found_rows from from_base64 from_days from_unixtime full function get get_format get_lock getdate getutcdate global go goto grant grants greatest group group_concat grouping grouping_id gtid_subset gtid_subtract handler having help hex high_priority hosts hour ident_current ident_incr ident_seed identified identity if ifnull ignore iif ilike immediate in index indicator inet6_aton inet6_ntoa inet_aton inet_ntoa infile initially inner innodb input insert install instr intersect into is is_free_lock is_ipv4 is_ipv4_compat is_ipv4_mapped is_not is_not_null is_used_lock isdate isnull isolation join key kill language last last_day last_insert_id last_value lcase lead leading least leaves left len lenght level like limit lines ln load load_file local localtime localtimestamp locate lock log log10 log2 logfile logs low_priority lower lpad ltrim make_set makedate maketime master master_pos_wait match matched max md5 medium merge microsecond mid min minute mod mode module month monthname mutex name_const names national natural nchar next no no_write_to_binlog not now nullif nvarchar oct octet_length of old_password on only open optimize option optionally or ord order outer outfile output pad parse partial partition password patindex percent_rank percentile_cont percentile_disc period_add period_diff pi plugin position pow power pragma precision prepare preserve primary prior privileges procedure procedure_analyze processlist profile profiles public publishingservername purge quarter query quick quote quotename radians rand read references regexp relative relaylog release release_lock rename repair repeat replace replicate reset restore restrict return returns reverse revoke right rlike rollback rollup round row row_count rows rpad rtrim savepoint schema scroll sec_to_time second section select serializable server session session_user set sha sha1 sha2 share show sign sin size slave sleep smalldatetimefromparts snapshot some soname soundex sounds_like space sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sql_variant_property sqlstate sqrt square start starting status std stddev stddev_pop stddev_samp stdev stdevp stop str str_to_date straight_join strcmp string stuff subdate substr substring subtime subtring_index sum switchoffset sysdate sysdatetime sysdatetimeoffset system_user sysutcdatetime table tables tablespace tan temporary terminated tertiary_weights then time time_format time_to_sec timediff timefromparts timestamp timestampadd timestampdiff timezone_hour timezone_minute to to_base64 to_days to_seconds todatetimeoffset trailing transaction translation trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse ucase uncompress uncompressed_length unhex unicode uninstall union unique unix_timestamp unknown unlock update upgrade upped upper usage use user user_resources using utc_date utc_time utc_timestamp uuid uuid_short validate_password_strength value values var var_pop var_samp variables variance varp version view warnings week weekday weekofyear weight_string when whenever where with work write xml xor year yearweek zon",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int integer interval number numeric real serial smallint varchar varying int8 serial8 text"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]} +}); \ No newline at end of file diff --git a/src/main/resources/templates/themes/default/static/js/instantclick.min.js b/src/main/resources/templates/themes/default/static/js/instantclick.min.js new file mode 100644 index 00000000..f7edcd6c --- /dev/null +++ b/src/main/resources/templates/themes/default/static/js/instantclick.min.js @@ -0,0 +1 @@ +var InstantClick=function(d,r){var B=navigator.userAgent,E="createTouch" in d,C,e,l,c={},G,L=false,h=false,u=false,A=false,I={},a=false,o=false,g=[],x,F,v,y={fetch:[],receive:[],wait:[],change:[]};function t(R){var Q=R.indexOf("#");if(Q<0){return R}return R.substr(0,Q)}function f(Q){while(Q.nodeName!="A"){Q=Q.parentNode}return Q}function M(Q){do{if(!Q.hasAttribute){break}if(Q.hasAttribute("data-instant")){return false}if(Q.hasAttribute("data-no-instant")){return true}}while(Q=Q.parentNode);return false}function P(Q){do{if(!Q.hasAttribute){break}if(Q.hasAttribute("data-no-instant")){return false}if(Q.hasAttribute("data-instant")){return true}}while(Q=Q.parentNode);return false}function b(S,Q){for(var R=0;R-1&&d.getElementById(U.substr(S+1)),V=0;if(R){while(R.offsetParent){V+=R.offsetTop;R=R.offsetParent}}scrollTo(0,V);C=t(U)}else{scrollTo(0,T)}m();H.done();b("change",false)}function p(){a=false;o=false}function q(Q){n(f(Q.target).href)}function K(R){var Q=f(R.target);Q.addEventListener("mouseout",N);if(!v){n(Q.href)}else{e=Q.href;l=setTimeout(n,v)}}function O(R){var Q=f(R.target);if(F){Q.removeEventListener("mousedown",q)}else{Q.removeEventListener("mouseover",K)}n(Q.href)}function D(Q){if(Q.which>1||Q.metaKey||Q.ctrlKey){return}Q.preventDefault();w(f(Q.target).href)}function N(){if(l){clearTimeout(l);l=false;return}if(!a||o){return}G.abort();p()}function s(){if(G.readyState<4){return}if(G.status==0){return}I.ready=+new Date-I.start;b("receive");if(G.getResponseHeader("Content-Type").match(/\/(x|ht|xht)ml/)){var X=d.implementation.createHTMLDocument("");X.documentElement.innerHTML=G.responseText;h=X.title;A=X.body;var T=t(L);c[T]={body:A,title:h,scrollY:T in c?c[T].scrollY:0};var Q=X.head.children,W=0,U,V;for(var S=Q.length-1;S>=0;S--){U=Q[S];if(U.hasAttribute("data-instant-track")){V=U.getAttribute("href")||U.getAttribute("src")||U.innerHTML;for(var R=g.length-1;R>=0;R--){if(g[R]==V){W++}}}}if(W!=g.length){u=true}}else{u=true}if(o){o=false;w(L)}}function m(Z){var R=d.getElementsByTagName("a"),Y,U=r.protocol+"//"+r.host;for(var V=R.length-1;V>=0;V--){Y=R[V];if(Y.target||Y.hasAttribute("download")||Y.href.indexOf(U+"/")!=0||(Y.href.indexOf("#")>-1&&t(Y.href)==C)||(x?!P(Y):M(Y))){continue}Y.addEventListener("touchstart",O);if(F){Y.addEventListener("mousedown",q)}else{Y.addEventListener("mouseover",K)}Y.addEventListener("click",D)}if(!Z){var S=d.body.getElementsByTagName("script"),X,Q,W,T;for(V=0,j=S.length;V=98){Q=98}else{Y=setTimeout(T,500)}X()}function X(){U.style[aa]="translate("+Q+"%)";if(!d.getElementById(Z.id)){d.body.appendChild(Z)}}function W(){if(d.getElementById(Z.id)){clearTimeout(Y);Q=100;X();Z.style.opacity="0";return}R(Q==100?0:Q);setTimeout(W,0)}function S(){Z.style.left=pageXOffset+"px";Z.style.width=innerWidth+"px";Z.style.top=pageYOffset+"px";var ad="orientation" in window&&Math.abs(orientation)==90,ac=innerWidth/screen[ad?"height":"width"]*2;Z.style[aa]="scaleY("+ac+")"}return{init:ab,start:R,done:W}}();var i="pushState" in history&&(!B.match("Android")||B.match("Chrome/"))&&r.protocol!="file:";function J(){if(C){return}if(!i){b("change",true);return}for(var S=arguments.length-1;S>=0;S--){var Q=arguments[S];if(Q===true){x=true}else{if(Q=="mousedown"){F=true}else{if(typeof Q=="number"){v=Q}}}}C=t(r.href);c[C]={body:d.body,title:d.title,scrollY:pageYOffset};var R=d.head.children,T,U;for(var S=R.length-1;S>=0;S--){T=R[S];if(T.hasAttribute("data-instant-track")){U=T.getAttribute("href")||T.getAttribute("src")||T.innerHTML;g.push(U)}}G=new XMLHttpRequest();G.addEventListener("readystatechange",s);m(true);H.init();b("change",true);addEventListener("popstate",function(){var V=t(r.href);if(V==C){return}if(!(V in c)){r.href=r.href;return}c[C].scrollY=pageYOffset;C=V;k(c[V].title,c[V].body,false,c[V].scrollY)})}function z(Q,R){y[Q].push(R)}return{supported:i,init:J,on:z}}(document,location); \ No newline at end of file diff --git a/src/test/java/Ma.java b/src/test/java/Ma.java deleted file mode 100644 index 4764b6dc..00000000 --- a/src/test/java/Ma.java +++ /dev/null @@ -1,12 +0,0 @@ -import com.blade.kit.UUID; - -/** - * Created by biezhi on 2017/2/22. - */ -public class Ma { - - - public static void main(String[] args) { - System.out.println(UUID.UU64()); - } -} diff --git a/src/test/java/com/tale/test/BaseTest.java b/src/test/java/com/tale/test/BaseTest.java new file mode 100644 index 00000000..50376c22 --- /dev/null +++ b/src/test/java/com/tale/test/BaseTest.java @@ -0,0 +1,15 @@ +package com.tale.test; + +import com.blade.test.BladeApplication; +import com.tale.Application; +import org.junit.runner.RunWith; + +/** + * @author biezhi + * @date 2018/6/3 + */ +@RunWith(BladeTestRunner.class) +@BladeApplication(Application.class) +public abstract class BaseTest { + +} diff --git a/src/test/java/com/tale/test/BladeTestRunner.java b/src/test/java/com/tale/test/BladeTestRunner.java new file mode 100644 index 00000000..f0ae26a7 --- /dev/null +++ b/src/test/java/com/tale/test/BladeTestRunner.java @@ -0,0 +1,91 @@ +package com.tale.test; + +import com.blade.Blade; +import com.blade.ioc.annotation.Inject; +import com.blade.security.web.csrf.CsrfMiddleware; +import com.blade.test.BladeApplication; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; + +import java.lang.reflect.Field; + +@SuppressWarnings("deprecation") +public class BladeTestRunner extends BlockJUnit4ClassRunner { + + private Class clazz; + private Class mainCls; + private Blade blade; + + public BladeTestRunner(Class clazz) throws InitializationError { + super(clazz); + this.clazz = clazz; + BladeApplication bladeApplication = clazz.getAnnotation(BladeApplication.class); + if(null == bladeApplication){ + throw new RuntimeException("Please use @BladeApplication configuration main class type :)"); + } + mainCls = bladeApplication.value(); + } + + @Override + protected Statement withBeforeClasses(final Statement statement) { + final Statement junitStatement = super.withBeforeClasses(statement); + return new Statement() { + @Override + public void evaluate() throws Throwable { + blade = Blade.me(); + blade.use(new CsrfMiddleware()).start(mainCls).await(); + junitStatement.evaluate(); + } + }; + } + + @Override + protected Statement withBefores(final FrameworkMethod method, Object target, final Statement statement) { + Field[] declaredFields = clazz.getDeclaredFields(); + for (Field declaredField : declaredFields) { + Inject inject = declaredField.getAnnotation(Inject.class); + if (null != inject) { + Object bean = blade.getBean(declaredField.getType()); + try { + declaredField.setAccessible(true); + declaredField.set(target, bean); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + final Statement junitStatement = super.withBefores(method, target, statement); + return new Statement() { + @Override + public void evaluate() throws Throwable { + junitStatement.evaluate(); + } + }; + } + + @Override + protected Statement withAfters(final FrameworkMethod method, Object target, final Statement statement) { + final Statement junitStatement = super.withAfters(method, target, statement); + return new Statement() { + @Override + public void evaluate() throws Throwable { + junitStatement.evaluate(); + } + }; + } + + @Override + protected Statement withAfterClasses(final Statement statement) { + final Statement junitStatement = super.withAfterClasses(statement); + return new Statement() { + @Override + public void evaluate() throws Throwable { + junitStatement.evaluate(); + blade.stop(); + } + }; + } +} \ No newline at end of file diff --git a/src/test/java/com/tale/test/service/CommentsServiceTest.java b/src/test/java/com/tale/test/service/CommentsServiceTest.java new file mode 100644 index 00000000..ea7f193c --- /dev/null +++ b/src/test/java/com/tale/test/service/CommentsServiceTest.java @@ -0,0 +1,37 @@ +package com.tale.test.service; + +import com.blade.ioc.annotation.Inject; +import com.tale.service.CommentsService; +import com.tale.test.BaseTest; +import org.junit.Test; + +/** + * @author biezhi + * @date 2018/6/3 + */ +public class CommentsServiceTest extends BaseTest { + + @Inject + private CommentsService commentsService; + + @Test + public void testSaveComment() { + // TODO + } + + @Test + public void testDelete() { + // TODO + } + + @Test + public void testGetComments() { + // TODO + } + + @Test + public void testById() { + // TODO + } + +} diff --git a/src/test/java/com/tale/test/service/ContentsServiceTest.java b/src/test/java/com/tale/test/service/ContentsServiceTest.java new file mode 100644 index 00000000..8c7a02a7 --- /dev/null +++ b/src/test/java/com/tale/test/service/ContentsServiceTest.java @@ -0,0 +1,42 @@ +package com.tale.test.service; + +import com.blade.ioc.annotation.Inject; +import com.tale.service.ContentsService; +import com.tale.test.BaseTest; +import org.junit.Test; + +/** + * @author biezhi + * @date 2018/6/3 + */ +public class ContentsServiceTest extends BaseTest { + + @Inject + private ContentsService contentsService; + + @Test + public void testGetContents() { + // TODO + } + + @Test + public void testPublish() { + // TODO + } + + @Test + public void testUpdateArticle() { + // TODO + } + + @Test + public void testDelete() { + // TODO + } + + @Test + public void testGetArticles() { + // TODO + } + +} diff --git a/src/test/java/com/tale/test/service/MetasServiceTest.java b/src/test/java/com/tale/test/service/MetasServiceTest.java new file mode 100644 index 00000000..68755163 --- /dev/null +++ b/src/test/java/com/tale/test/service/MetasServiceTest.java @@ -0,0 +1,47 @@ +package com.tale.test.service; + +import com.blade.ioc.annotation.Inject; +import com.tale.service.MetasService; +import com.tale.test.BaseTest; +import org.junit.Test; + +/** + * @author biezhi + * @date 2018/6/3 + */ +public class MetasServiceTest extends BaseTest { + + @Inject + private MetasService metasService; + + @Test + public void testGetMetas(){ + // TODO + } + + @Test + public void testGetMetaMapping(){ + // TODO + } + + @Test + public void testGetMeta(){ + // TODO + } + + @Test + public void testSaveMetas(){ + // TODO + } + + @Test + public void testSaveMeta(){ + // TODO + } + + @Test + public void testDelete(){ + // TODO + } + +} diff --git a/src/test/java/com/tale/test/service/OptionsServiceTest.java b/src/test/java/com/tale/test/service/OptionsServiceTest.java new file mode 100644 index 00000000..e0212f21 --- /dev/null +++ b/src/test/java/com/tale/test/service/OptionsServiceTest.java @@ -0,0 +1,34 @@ +package com.tale.test.service; + +import com.blade.ioc.annotation.Inject; +import com.tale.service.OptionsService; +import com.tale.test.BaseTest; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author biezhi + * @date 2018/6/3 + */ +public class OptionsServiceTest extends BaseTest { + + @Inject + private OptionsService optionsService; + + @Test + public void testSaveOption() { + optionsService.saveOption("hello2", "world2"); + + String value = optionsService.getOption("hello2"); + Assert.assertEquals("world2", value); + } + + @Test + public void testGetOption() { + optionsService.saveOption("hello3", "world3"); + optionsService.deleteOption("hello3"); + String value = optionsService.getOption("hello3"); + Assert.assertNull(value); + } + +} diff --git a/src/test/resources/app.properties b/src/test/resources/app.properties new file mode 100644 index 00000000..1b79a805 --- /dev/null +++ b/src/test/resources/app.properties @@ -0,0 +1,12 @@ +app.name=Tale +#app.max-file-size=20480 +app.devMode=true +mvc.view.404=/comm/error_404.html +mvc.view.500=/comm/error_500.html +mvc.statics=/upload +app.support_163_music=true +app.support_gist=true + +com.blade.logger.org.sql2o=debug +com.blade.logger.com.blade.server.netty.StaticFileHandler=warn +com.blade.logger.dir=./logs \ No newline at end of file diff --git a/src/test/resources/schema.sql b/src/test/resources/schema.sql new file mode 100644 index 00000000..dbb1716a --- /dev/null +++ b/src/test/resources/schema.sql @@ -0,0 +1,103 @@ +-- +-- 由SQLiteStudio v3.1.1 产生的文件 周六 3月 4 01:05:21 2017 +-- +-- 文本编码:UTF-8 +-- +PRAGMA foreign_keys = off; +BEGIN TRANSACTION; + +-- 表:t_attach +DROP TABLE IF EXISTS t_attach; +CREATE TABLE t_attach (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, fname VARCHAR (100) NOT NULL, ftype VARCHAR (50), fkey VARCHAR (100) NOT NULL, author_id INTEGER (10) NOT NULL, created INTEGER (10) NOT NULL); + +-- 表:t_comments +DROP TABLE IF EXISTS t_comments; +CREATE TABLE t_comments (coid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, cid INTEGER DEFAULT (0) NOT NULL, created INTEGER (10) NOT NULL, author VARCHAR (200) NOT NULL, author_id INTEGER (10) DEFAULT (0), owner_id INTEGER (10) DEFAULT (0), mail VARCHAR (200) NOT NULL, url VARCHAR (200), ip VARCHAR (64), agent VARCHAR (200), content TEXT NOT NULL, type VARCHAR (16), status VARCHAR (16), parent INTEGER (10) DEFAULT (0)); + +-- 表:t_contents +DROP TABLE IF EXISTS t_contents; + +CREATE TABLE t_contents ( cid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, title VARCHAR (255) NOT NULL, slug VARCHAR (255) CONSTRAINT idx_u_slug UNIQUE, thumb_img VARCHAR(255), created INTEGER (10) NOT NULL, modified INTEGER (10), content TEXT, author_id INTEGER (10) NOT NULL, type VARCHAR (16) NOT NULL, status VARCHAR (16) NOT NULL, fmt_type VARCHAR (16) DEFAULT ('markdown'), tags VARCHAR (200), categories VARCHAR (200), hits INTEGER (10) DEFAULT (0), comments_num INTEGER (1) DEFAULT (0), allow_comment INTEGER (1) DEFAULT (1), allow_ping INTEGER (1), allow_feed INTEGER (1) ); +INSERT INTO t_contents (cid, title, slug, created, modified, content, author_id, type, status, tags, categories, hits, comments_num, allow_comment, allow_ping, allow_feed) VALUES (1, '关于', 'about', 1487853610, 1487872488, '### Hello World + +这是我的关于页面 + +### 当然还有其他 + +具体你来写点什么吧', 1, 'page', 'publish', NULL, NULL, 0, 0, 1, 1, 1); +INSERT INTO t_contents (cid, title, slug, created, modified, content, author_id, type, status, tags, categories, hits, comments_num, allow_comment, allow_ping, allow_feed) VALUES (2, '第一篇文章', NULL, 1487861184, 1487872798, '## Hello World. + +> 第一篇文章总得写点儿什么?... + +---------- + + + + +```java +public static void main(String[] args){ + System.out.println(\"Hello Tale.\"); +} +```', 1, 'post', 'publish', '', '默认分类', 10, 0, 1, 1, 1); + +INSERT INTO t_contents (allow_feed,allow_ping,allow_comment,comments_num,hits, +categories,tags,fmt_type,status,type,author_id,content,modified,created,thumb_img,slug,title,cid) VALUES ( +NULL,1,1,0,0,NULL,NULL,'markdown','publish','page',1,'## 友情链接 + +- :lock: [王爵的技术博客]() +- :lock: [cyang.tech]() +- :lock: [Bakumon''s Blog]() + +## 链接须知 + +> 请确定贵站可以稳定运营 +> 原创博客优先,技术类博客优先,设计、视觉类博客优先 +> 经常过来访问和评论,眼熟的 + +备注:默认申请友情链接均为内页(当前页面) + +## 基本信息 + + 网站名称:Tale博客 + 网站地址:https://tale.biezhi.me + +请在当页通过评论来申请友链,其他地方不予回复 + +暂时先这样,同时欢迎互换友链,这个页面留言即可。 ^_^ + +还有,我会不定时对无法访问的网址进行清理,请保证自己的链接长期有效。',1505643888,1505643727,NULL,'links','友情链接',3); + +-- 表:t_logs +DROP TABLE IF EXISTS t_logs; +CREATE TABLE t_logs (id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, "action" VARCHAR (100) NOT NULL, data VARCHAR (2000), author_id INTEGER (10) NOT NULL, ip VARCHAR (20), created INTEGER (10) NOT NULL); + +-- 表:t_metas +DROP TABLE IF EXISTS t_metas; +CREATE TABLE t_metas (mid INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, name VARCHAR (200) NOT NULL, slug VARCHAR (200), type VARCHAR (32) NOT NULL, description VARCHAR (255), sort INTEGER (4) DEFAULT (0), parent INTEGER (10) DEFAULT (0)); +INSERT INTO t_metas (mid, name, slug, type, description, sort, parent) VALUES (1, '默认分类', NULL, 'category', NULL, 0, 0); + +-- 表:t_options +DROP TABLE IF EXISTS t_options; +CREATE TABLE t_options (name VARCHAR (100) PRIMARY KEY UNIQUE NOT NULL, value TEXT, description VARCHAR (255)); +INSERT INTO t_options (name, value, description) VALUES ('site_title', 'Tale博客系统', ''); +INSERT INTO t_options (name, value, description) VALUES ('social_weibo', '', NULL); +INSERT INTO t_options (name, value, description) VALUES ('social_zhihu', '', NULL); +INSERT INTO t_options (name, value, description) VALUES ('social_github', '', NULL); +INSERT INTO t_options (name, value, description) VALUES ('social_twitter', '', NULL); +INSERT INTO t_options (name, value, description) VALUES ('allow_install', '0', '是否允许重新安装博客'); +INSERT INTO t_options (name, value, description) VALUES ('site_theme', 'default', NULL); +INSERT INTO t_options (name, value, description) VALUES ('site_keywords', '博客系统,Blade框架,Tale', NULL); +INSERT INTO t_options (name, value, description) VALUES ('site_description', '博客系统,Blade框架,Tale', NULL); + +-- 表:t_relationships +DROP TABLE IF EXISTS t_relationships; +CREATE TABLE t_relationships (cid INTEGER (10) NOT NULL, mid INTEGER (10) NOT NULL); + +INSERT INTO t_relationships(cid, mid) VALUES(2, 1); + +-- 表:t_users +DROP TABLE IF EXISTS t_users; +CREATE TABLE t_users (uid INTEGER PRIMARY KEY UNIQUE NOT NULL, username VARCHAR (64) UNIQUE NOT NULL, password VARCHAR (64) NOT NULL, email VARCHAR (100), home_url VARCHAR (255), screen_name VARCHAR (100), created INTEGER (10) NOT NULL, activated INTEGER (10), logged INTEGER (10), group_name VARCHAR (16)); + +COMMIT TRANSACTION; +PRAGMA foreign_keys = on; diff --git a/update.md b/update.md deleted file mode 100644 index 50e6ebb8..00000000 --- a/update.md +++ /dev/null @@ -1,10 +0,0 @@ -# 更新日志 - - -## 版本 - -- Tale-1.2.x:支持内嵌Sqlite,命令行一键启动,关闭,重启 -- [Tale-Mysql版本](https://github.com/otale/tale/tree/tale-mysql) - -## 更新日志 - diff --git a/video.md b/video.md deleted file mode 100644 index d964e120..00000000 --- a/video.md +++ /dev/null @@ -1,37 +0,0 @@ -# Tale 视频教程 - -1. 开发环境快速开始 - - [IDEA](https://v.qq.com/x/page/b0379xmo0ad.html) - - [Eclipse](https://v.qq.com/x/page/x0379l5ttpq.html) -2. [部署到服务器](https://v.qq.com/x/page/b0379544hqe.html) -3. 常见问题解决 - -## 视频中的脚本 - -```bash -create user 'tale_test'@'127.0.0.1' identified by 'gZ3F}u%br]9uVb_hOuL:'; - -create database `tale_test` default character set utf8 collate utf8_general_ci; - -grant select,insert,update,delete on tale_test.* to tale_test@'%' Identified by "gZ3F}u%br]9uVb_hOuL:"; - -grant all privileges on tale_test.* to tale_test@'%' identified by "gZ3F}u%br]9uVb_hOuL:"; - -flush privileges; -``` - -```nginx -server { - listen 80; - server_name tale.writty.me; - - location / { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass http://127.0.0.1:9025; - client_max_body_size 10m; - } -} - -``` \ No newline at end of file