Support set password to post.

pull/296/head
ruibaby 2019-09-06 16:57:26 +08:00
parent 6564710453
commit 83789d52df
8 changed files with 244 additions and 12 deletions

View File

@ -150,9 +150,9 @@ public class PostController {
cacheStore.putAny("preview-post-token-" + postId, token, 10, TimeUnit.MINUTES);
// build preview post url
String url = String.format("%s/archives/%s?preview=true&token=%s", optionService.getBlogBaseUrl(), post.getUrl(), token);
String redirect = String.format("%s/archives/%s?preview=true&token=%s", optionService.getBlogBaseUrl(), post.getUrl(), token);
// redirect to preview url
response.sendRedirect(url);
response.sendRedirect(redirect);
}
}

View File

@ -123,9 +123,9 @@ public class SheetController {
cacheStore.putAny("preview-sheet-token-" + sheetId, token, 10, TimeUnit.MINUTES);
// build preview sheet url
String url = String.format("%s/s/%s?preview=true&token=%s", optionService.getBlogBaseUrl(), sheet.getUrl(), token);
String redirect = String.format("%s/s/%s?preview=true&token=%s", optionService.getBlogBaseUrl(), sheet.getUrl(), token);
// redirect to preview url
response.sendRedirect(url);
response.sendRedirect(redirect);
}
}

View File

@ -1,6 +1,8 @@
package run.halo.app.controller.content;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.crypto.digest.BCrypt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@ -9,11 +11,9 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.web.SortDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
import run.halo.app.cache.StringCacheStore;
import run.halo.app.cache.lock.CacheLock;
import run.halo.app.exception.ForbiddenException;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post;
@ -111,10 +111,19 @@ public class ContentArchiveController {
@GetMapping("{url}")
public String post(@PathVariable("url") String url,
@RequestParam(value = "preview", required = false, defaultValue = "false") boolean preview,
@RequestParam(value = "intimate", required = false, defaultValue = "false") boolean intimate,
@RequestParam(value = "token", required = false) String token,
Model model) {
Post post = postService.getBy(preview ? PostStatus.DRAFT : PostStatus.PUBLISHED, url);
Post post;
if (preview) {
post = postService.getBy(PostStatus.DRAFT, url);
} else if (intimate) {
post = postService.getBy(PostStatus.INTIMATE, url);
} else {
post = postService.getBy(PostStatus.PUBLISHED, url);
}
// if this is a preview url.
if (preview) {
// render markdown to html when preview post
post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent()));
@ -127,6 +136,15 @@ public class ContentArchiveController {
}
}
// if this is a intimate url.
if (intimate) {
// verify token
String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该文章的访问权限"));
if (!cachedToken.equals(token)) {
throw new ForbiddenException("您没有该文章的访问权限");
}
}
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost));
@ -145,4 +163,37 @@ public class ContentArchiveController {
return themeService.render("post");
}
@GetMapping(value = "{url}/password")
public String password(@PathVariable("url") String url,
Model model) {
Post post = postService.getBy(PostStatus.INTIMATE, url);
if (null == post) {
throw new ForbiddenException("没有查询到该文章信息");
}
model.addAttribute("url", url);
return "common/template/post_password";
}
@PostMapping(value = "{url}/password")
@CacheLock
public String password(@PathVariable("url") String url,
@RequestParam(value = "password") String password) {
Post post = postService.getBy(PostStatus.INTIMATE, url);
if (null == post) {
throw new ForbiddenException("没有查询到该文章信息");
}
if (BCrypt.checkpw(password, post.getPassword())) {
String token = IdUtil.simpleUUID();
cacheStore.putAny(token, token, 10, TimeUnit.SECONDS);
String redirect = String.format("%s/archives/%s?intimate=true&token=%s", optionService.getBlogBaseUrl(), post.getUrl(), token);
return "redirect:" + redirect;
} else {
String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl());
return "redirect:" + redirect;
}
}
}

View File

@ -23,6 +23,8 @@ public class BasePostSimpleDTO extends BasePostMinimalDTO {
private Boolean disallowComment;
private String password;
private String template;
private Integer topPriority = 0;

View File

@ -13,9 +13,9 @@ public enum JournalType implements ValueEnum<Integer> {
PUBLIC(1),
/**
* Private type.
* Intimate type.
*/
PRIVATE(0);
INTIMATE(0);
private final int value;

View File

@ -20,7 +20,12 @@ public enum PostStatus implements ValueEnum<Integer> {
/**
* Recycle status.
*/
RECYCLE(2);
RECYCLE(2),
/**
* Intimate status
*/
INTIMATE(3);
private final int value;

View File

@ -217,6 +217,11 @@ public abstract class BasePostServiceImpl<POST extends BasePost> extends Abstrac
post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent()));
}
// if password is not empty,change status to intimate
if (StringUtils.isNotEmpty(post.getPassword()) && post.getStatus() != PostStatus.DRAFT) {
post.setStatus(PostStatus.INTIMATE);
}
// Create or update post
if (ServiceUtils.isEmptyId(post.getId())) {
// The sheet will be created

View File

@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
<meta name="robots" content="noindex,nofllow"/>
<title>私密文章访问 - ${options.blog_title!}</title>
<style>
body {
background-color: #080821;
}
.container {
position: absolute;
top: 50%;
left: 50%;
margin: -140px 0 0 -140px;
width: 280px;
}
.password-input {
position: relative;
}
.password-input input {
box-sizing: border-box;
width: 100%;
color: white;
font-size: inherit;
font-family: inherit;
background-color: hsl(236, 32%, 26%);
padding: 0.5em 1em;
border: 1px solid transparent;
transition: background-color 0.3s ease-in-out;
}
.password-input input:focus {
outline: none;
}
.password-input input::placeholder {
color: hsla(0, 0%, 100%, 0.6);
}
.password-input span {
position: absolute;
background-color: #fc2f70;
transition: transform 0.1s ease;
}
.password-input .bottom,
.password-input .top {
height: 1px;
left: 0;
right: 0;
transform: scaleX(0);
}
.password-input .left,
.password-input .right {
width: 1px;
top: 0;
bottom: 0;
transform: scaleY(0);
}
.password-input .bottom {
bottom: 0;
transform-origin: bottom right;
}
.password-input input:focus ~ .bottom {
transform-origin: bottom left;
transform: scaleX(1);
}
.password-input .right {
right: 0;
transform-origin: top right;
transition-delay: 0.05s;
}
.password-input input:focus ~ .right {
transform-origin: bottom right;
transform: scaleY(1);
}
.password-input .top {
top: 0;
transform-origin: top left;
transition-delay: 0.15s;
}
.password-input input:focus ~ .top {
transform-origin: top right;
transform: scaleX(1);
}
.password-input .left {
left: 0;
transform-origin: bottom left;
transition-delay: 0.25s;
}
.password-input input:focus ~ .left {
transform-origin: top left;
transform: scaleY(1);
}
.submit-input {
margin-top: 20px;
}
.submit-input button {
width: 100%;
z-index: 1;
position: relative;
font-size: inherit;
font-family: inherit;
color: white;
padding: 0.5em 1em;
outline: none;
border: 1px solid transparent;
background-color: hsl(236, 32%, 26%);
}
.submit-input button:hover {
cursor: pointer;
}
.submit-input button::before {
content: '';
z-index: -1;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
border: 4px solid hsl(236, 32%, 26%);
transform-origin: center;
transform: scale(1);
}
.submit-input button:hover::before {
transition: all 0.75s ease-in-out;
transform-origin: center;
transform: scale(1.75);
opacity: 0;
}
</style>
</head>
<body>
<div class="container">
<form method="post" action="${context!}/archives/${url}/password">
<div class="password-input">
<input type="password" name="password" placeholder="请输入文章访问密码">
<span class="bottom"></span>
<span class="right"></span>
<span class="top"></span>
<span class="left"></span>
</div>
<div class="submit-input">
<button type="submit">验证</button>
</div>
</form>
</div>
</body>
</html>