feat: support set path suffix.

pull/484/head
ruibaby 2020-01-07 20:16:11 +08:00
parent 13ab09b799
commit bb58dd5ec4
10 changed files with 209 additions and 158 deletions

View File

@ -1,13 +1,7 @@
package run.halo.app.controller.content; package run.halo.app.controller.content;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.PageUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.SortDefault;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -16,13 +10,11 @@ import run.halo.app.cache.lock.CacheLock;
import run.halo.app.controller.content.model.PostModel; import run.halo.app.controller.content.model.PostModel;
import run.halo.app.model.entity.Post; import run.halo.app.model.entity.Post;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.vo.PostListVO; import run.halo.app.service.OptionService;
import run.halo.app.service.*; import run.halo.app.service.PostService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.springframework.data.domain.Sort.Direction.DESC;
/** /**
* Blog archive page controller * Blog archive page controller
* *
@ -38,14 +30,6 @@ public class ContentArchiveController {
private final PostService postService; private final PostService postService;
private final ThemeService themeService;
private final PostCategoryService postCategoryService;
private final PostMetaService postMetaService;
private final PostTagService postTagService;
private final OptionService optionService; private final OptionService optionService;
private final StringCacheStore cacheStore; private final StringCacheStore cacheStore;
@ -53,18 +37,10 @@ public class ContentArchiveController {
private final PostModel postModel; private final PostModel postModel;
public ContentArchiveController(PostService postService, public ContentArchiveController(PostService postService,
ThemeService themeService,
PostCategoryService postCategoryService,
PostMetaService postMetaService,
PostTagService postTagService,
OptionService optionService, OptionService optionService,
StringCacheStore cacheStore, StringCacheStore cacheStore,
PostModel postModel) { PostModel postModel) {
this.postService = postService; this.postService = postService;
this.themeService = themeService;
this.postCategoryService = postCategoryService;
this.postMetaService = postMetaService;
this.postTagService = postTagService;
this.optionService = optionService; this.optionService = optionService;
this.cacheStore = cacheStore; this.cacheStore = cacheStore;
this.postModel = postModel; this.postModel = postModel;
@ -78,7 +54,7 @@ public class ContentArchiveController {
*/ */
@GetMapping @GetMapping
public String archives(Model model) { public String archives(Model model) {
return this.archives(model, 1, Sort.by(DESC, "createTime")); return this.archives(model, 1);
} }
/** /**
@ -89,36 +65,8 @@ public class ContentArchiveController {
*/ */
@GetMapping(value = "page/{page}") @GetMapping(value = "page/{page}")
public String archives(Model model, public String archives(Model model,
@PathVariable(value = "page") Integer page, @PathVariable(value = "page") Integer page) {
@SortDefault(sort = "createTime", direction = DESC) Sort sort) { return postModel.list(page, model, "is_archives", "archives");
Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort);
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
Page<PostListVO> postListVos = postService.convertToListVo(postPage);
int[] pageRainbow = PageUtil.rainbow(page, postListVos.getTotalPages(), 3);
model.addAttribute("is_archives", true);
model.addAttribute("pageRainbow", pageRainbow);
model.addAttribute("posts", postListVos);
return themeService.render("archives");
}
/**
* Render post page.
*
* @param url post slug url.
* @param token view token.
* @param model model
* @return template path: themes/{theme}/post.ftl
*/
@GetMapping("{url}")
public String post(@PathVariable("url") String url,
@RequestParam(value = "token", required = false) String token,
Model model) {
Post post = postService.getByUrl(url);
return postModel.post(post, token, model);
} }
@GetMapping(value = "{url}/password") @GetMapping(value = "{url}/password")

View File

@ -0,0 +1,71 @@
package run.halo.app.controller.content;
import lombok.extern.slf4j.Slf4j;
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 run.halo.app.controller.content.model.PostModel;
import run.halo.app.controller.content.model.SheetModel;
import run.halo.app.exception.NotFoundException;
import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.Sheet;
import run.halo.app.model.enums.PostPermalinkType;
import run.halo.app.model.properties.PermalinkProperties;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostService;
import run.halo.app.service.SheetService;
/**
* @author ryanwang
* @date 2020-01-07
*/
@Slf4j
@Controller
@RequestMapping
public class ContentContentController {
private final PostModel postModel;
private final SheetModel sheetModel;
private final OptionService optionService;
private final PostService postService;
private final SheetService sheetService;
public ContentContentController(PostModel postModel,
SheetModel sheetModel,
OptionService optionService,
PostService postService,
SheetService sheetService) {
this.postModel = postModel;
this.sheetModel = sheetModel;
this.optionService = optionService;
this.postService = postService;
this.sheetService = sheetService;
}
@GetMapping("{prefix}/{url}")
public String content(@PathVariable("prefix") String prefix,
@PathVariable("url") String url,
@RequestParam(value = "token", required = false) String token,
Model model) {
PostPermalinkType postPermalinkType = optionService.getPostPermalinkType();
String archivesPrefix = optionService.getByPropertyOrDefault(PermalinkProperties.ARCHIVES_PREFIX, String.class, PermalinkProperties.ARCHIVES_PREFIX.defaultValue());
String sheetPrefix = optionService.getByPropertyOrDefault(PermalinkProperties.SHEET_PREFIX, String.class, PermalinkProperties.SHEET_PREFIX.defaultValue());
if (postPermalinkType.equals(PostPermalinkType.DEFAULT) && archivesPrefix.equals(prefix)) {
Post post = postService.getByUrl(url);
return postModel.content(post, token, model);
} else if (sheetPrefix.equals(prefix)) {
Sheet sheet = sheetService.getByUrl(url);
return sheetModel.content(sheet, token, model);
} else {
throw new NotFoundException("Not Found");
}
}
}

View File

@ -1,11 +1,6 @@
package run.halo.app.controller.content; package run.halo.app.controller.content;
import cn.hutool.core.util.PageUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -14,17 +9,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import run.halo.app.controller.content.model.PostModel; import run.halo.app.controller.content.model.PostModel;
import run.halo.app.model.entity.Post; import run.halo.app.model.entity.Post;
import run.halo.app.model.enums.PostPermalinkType; import run.halo.app.model.enums.PostPermalinkType;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.properties.PostProperties;
import run.halo.app.model.vo.PostListVO;
import run.halo.app.service.OptionService; import run.halo.app.service.OptionService;
import run.halo.app.service.PostService; import run.halo.app.service.PostService;
import run.halo.app.service.ThemeService;
import java.util.Objects; import java.util.Objects;
import static org.springframework.data.domain.Sort.Direction.DESC;
/** /**
* Blog index page controller * Blog index page controller
* *
@ -40,17 +29,13 @@ public class ContentIndexController {
private final OptionService optionService; private final OptionService optionService;
private final ThemeService themeService;
private final PostModel postModel; private final PostModel postModel;
public ContentIndexController(PostService postService, public ContentIndexController(PostService postService,
OptionService optionService, OptionService optionService,
ThemeService themeService,
PostModel postModel) { PostModel postModel) {
this.postService = postService; this.postService = postService;
this.optionService = optionService; this.optionService = optionService;
this.themeService = themeService;
this.postModel = postModel; this.postModel = postModel;
} }
@ -69,7 +54,7 @@ public class ContentIndexController {
if (PostPermalinkType.ID.equals(permalinkType) && !Objects.isNull(p)) { if (PostPermalinkType.ID.equals(permalinkType) && !Objects.isNull(p)) {
Post post = postService.getById(p); Post post = postService.getById(p);
return postModel.post(post, token, model); return postModel.content(post, token, model);
} }
return this.index(model, 1); return this.index(model, 1);
@ -85,18 +70,6 @@ public class ContentIndexController {
@GetMapping(value = "page/{page}") @GetMapping(value = "page/{page}")
public String index(Model model, public String index(Model model,
@PathVariable(value = "page") Integer page) { @PathVariable(value = "page") Integer page) {
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT).toString(); return postModel.list(page, model, "is_index", "index");
int pageSize = optionService.getPostPageSize();
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, pageSize, Sort.by(DESC, "topPriority").and(Sort.by(DESC, indexSort)));
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
Page<PostListVO> posts = postService.convertToListVo(postPage);
int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
model.addAttribute("is_index", true);
model.addAttribute("posts", posts);
model.addAttribute("rainbow", rainbow);
return themeService.render("index");
} }
} }

View File

@ -1,6 +1,5 @@
package run.halo.app.controller.content; package run.halo.app.controller.content;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@ -10,17 +9,9 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import run.halo.app.cache.StringCacheStore;
import run.halo.app.exception.ForbiddenException;
import run.halo.app.model.dto.PhotoDTO; import run.halo.app.model.dto.PhotoDTO;
import run.halo.app.model.entity.Sheet;
import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.support.HaloConst;
import run.halo.app.model.vo.SheetDetailVO;
import run.halo.app.service.PhotoService; import run.halo.app.service.PhotoService;
import run.halo.app.service.SheetService;
import run.halo.app.service.ThemeService; import run.halo.app.service.ThemeService;
import run.halo.app.utils.MarkdownUtils;
import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.domain.Sort.Direction.DESC;
@ -35,22 +26,16 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
public class ContentSheetController { public class ContentSheetController {
private final SheetService sheetService;
private final ThemeService themeService; private final ThemeService themeService;
private final PhotoService photoService; private final PhotoService photoService;
private final StringCacheStore cacheStore; public ContentSheetController(
public ContentSheetController(SheetService sheetService,
ThemeService themeService, ThemeService themeService,
PhotoService photoService, PhotoService photoService
StringCacheStore cacheStore) { ) {
this.sheetService = sheetService;
this.themeService = themeService; this.themeService = themeService;
this.photoService = photoService; this.photoService = photoService;
this.cacheStore = cacheStore;
} }
/** /**
@ -91,50 +76,4 @@ public class ContentSheetController {
public String links() { public String links() {
return themeService.render("links"); return themeService.render("links");
} }
/**
* Render custom sheet
*
* @param url sheet url
* @param token view token
* @param model model
* @return template path: themes/{theme}/sheet.ftl
*/
@GetMapping(value = "/s/{url}")
public String sheet(@PathVariable(value = "url") String url,
@RequestParam(value = "token", required = false) String token,
Model model) {
Sheet sheet = sheetService.getByUrl(url);
if (StringUtils.isEmpty(token)) {
sheet = sheetService.getBy(PostStatus.PUBLISHED, url);
} else {
// render markdown to html when preview sheet
sheet.setFormatContent(MarkdownUtils.renderHtml(sheet.getOriginalContent()));
// verify token
String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该页面的访问权限"));
if (!cachedToken.equals(token)) {
throw new ForbiddenException("您没有该页面的访问权限");
}
}
sheetService.publishVisitEvent(sheet.getId());
SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheet);
// sheet and post all can use
model.addAttribute("sheet", sheetDetailVO);
model.addAttribute("post", sheetDetailVO);
model.addAttribute("is_sheet", true);
// TODO,Will be deprecated
model.addAttribute("comments", Page.empty());
if (themeService.templateExists(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate() + HaloConst.SUFFIX_FTL)) {
return themeService.render(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate());
}
return themeService.render("sheet");
}
} }

View File

@ -12,7 +12,6 @@ import run.halo.app.model.support.HaloConst;
import run.halo.app.service.OptionService; import run.halo.app.service.OptionService;
import run.halo.app.service.UserService; import run.halo.app.service.UserService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
@ -48,7 +47,7 @@ public class MainController {
} }
@GetMapping("${halo.admin-path:admin}") @GetMapping("${halo.admin-path:admin}")
public void admin(HttpServletRequest request, HttpServletResponse response) throws IOException { public void admin(HttpServletResponse response) throws IOException {
String adminIndexRedirectUri = StringUtils.appendIfMissing(this.haloProperties.getAdminPath(), "/") + INDEX_REDIRECT_URI; String adminIndexRedirectUri = StringUtils.appendIfMissing(this.haloProperties.getAdminPath(), "/") + INDEX_REDIRECT_URI;
response.sendRedirect(adminIndexRedirectUri); response.sendRedirect(adminIndexRedirectUri);
} }

View File

@ -1,7 +1,11 @@
package run.halo.app.controller.content.model; package run.halo.app.controller.content.model;
import cn.hutool.core.util.PageUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import run.halo.app.cache.StringCacheStore; import run.halo.app.cache.StringCacheStore;
@ -11,15 +15,21 @@ import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.PostMeta; import run.halo.app.model.entity.PostMeta;
import run.halo.app.model.entity.Tag; import run.halo.app.model.entity.Tag;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.properties.PostProperties;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
import run.halo.app.model.vo.PostListVO;
import run.halo.app.service.*; import run.halo.app.service.*;
import run.halo.app.utils.MarkdownUtils; import run.halo.app.utils.MarkdownUtils;
import java.util.List; import java.util.List;
import static org.springframework.data.domain.Sort.Direction.DESC;
/** /**
* @author ryan0up * Post Model
* @date 2020/1/7 *
* @author ryanwang
* @date 2020-01-07
*/ */
@Component @Component
public class PostModel { public class PostModel {
@ -54,7 +64,7 @@ public class PostModel {
this.cacheStore = cacheStore; this.cacheStore = cacheStore;
} }
public String post(Post post, String token, Model model) { public String content(Post post, String token, Model model) {
if (post.getStatus().equals(PostStatus.INTIMATE) && StringUtils.isEmpty(token)) { if (post.getStatus().equals(PostStatus.INTIMATE) && StringUtils.isEmpty(token)) {
String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl()); String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl());
@ -92,4 +102,21 @@ public class PostModel {
return themeService.render("post"); return themeService.render("post");
} }
public String list(Integer page, Model model, String decide, String template) {
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT).toString();
int pageSize = optionService.getPostPageSize();
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, pageSize, Sort.by(DESC, "topPriority").and(Sort.by(DESC, indexSort)));
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
Page<PostListVO> posts = postService.convertToListVo(postPage);
int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
model.addAttribute(decide, true);
model.addAttribute("posts", posts);
model.addAttribute("rainbow", rainbow);
model.addAttribute("pageRainbow", rainbow);
return themeService.render(template);
}
} }

View File

@ -0,0 +1,66 @@
package run.halo.app.controller.content.model;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import run.halo.app.cache.StringCacheStore;
import run.halo.app.exception.ForbiddenException;
import run.halo.app.model.entity.Sheet;
import run.halo.app.model.support.HaloConst;
import run.halo.app.model.vo.SheetDetailVO;
import run.halo.app.service.SheetService;
import run.halo.app.service.ThemeService;
import run.halo.app.utils.MarkdownUtils;
/**
* Sheet model.
*
* @author ryanwang
* @date 2020-01-07
*/
@Component
public class SheetModel {
private final SheetService sheetService;
private final StringCacheStore cacheStore;
private final ThemeService themeService;
public SheetModel(SheetService sheetService, StringCacheStore cacheStore, ThemeService themeService) {
this.sheetService = sheetService;
this.cacheStore = cacheStore;
this.themeService = themeService;
}
public String content(Sheet sheet, String token, Model model) {
if (!StringUtils.isEmpty(token)) {
// render markdown to html when preview sheet
sheet.setFormatContent(MarkdownUtils.renderHtml(sheet.getOriginalContent()));
// verify token
String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该页面的访问权限"));
if (!cachedToken.equals(token)) {
throw new ForbiddenException("您没有该页面的访问权限");
}
}
sheetService.publishVisitEvent(sheet.getId());
SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheet);
// sheet and post all can use
model.addAttribute("sheet", sheetDetailVO);
model.addAttribute("post", sheetDetailVO);
model.addAttribute("is_sheet", true);
// TODO,Will be deprecated
model.addAttribute("comments", Page.empty());
if (themeService.templateExists(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate() + HaloConst.SUFFIX_FTL)) {
return themeService.render(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate());
}
return themeService.render("sheet");
}
}

View File

@ -37,7 +37,13 @@ public enum PermalinkProperties implements PropertyEnum {
* Sheet prefix * Sheet prefix
* such as: /s/${url} * such as: /s/${url}
*/ */
SHEET_PREFIX("sheet_prefix", String.class, "s"); SHEET_PREFIX("sheet_prefix", String.class, "s"),
/**
* Path suffix
* such as: .html or .jsp
*/
PATH_SUFFIX("path_suffix", String.class, "");
private final String value; private final String value;

View File

@ -24,8 +24,10 @@ import run.halo.app.model.dto.TagDTO;
import run.halo.app.model.dto.post.BasePostDetailDTO; import run.halo.app.model.dto.post.BasePostDetailDTO;
import run.halo.app.model.entity.*; import run.halo.app.model.entity.*;
import run.halo.app.model.enums.LogType; import run.halo.app.model.enums.LogType;
import run.halo.app.model.enums.PostPermalinkType;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.params.PostQuery; import run.halo.app.model.params.PostQuery;
import run.halo.app.model.properties.PermalinkProperties;
import run.halo.app.model.vo.ArchiveMonthVO; import run.halo.app.model.vo.ArchiveMonthVO;
import run.halo.app.model.vo.ArchiveYearVO; import run.halo.app.model.vo.ArchiveYearVO;
import run.halo.app.model.vo.PostDetailVO; import run.halo.app.model.vo.PostDetailVO;
@ -450,6 +452,12 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
String blogUrl = optionService.getBlogBaseUrl(); String blogUrl = optionService.getBlogBaseUrl();
PostPermalinkType permalinkType = optionService.getPostPermalinkType();
String pathSuffix = optionService.getByPropertyOrDefault(PermalinkProperties.PATH_SUFFIX, String.class, "");
String archivesPrefix = optionService.getByPropertyOrDefault(PermalinkProperties.ARCHIVES_PREFIX, String.class, "");
return postPage.map(post -> { return postPage.map(post -> {
PostListVO postListVO = new PostListVO().convertFrom(post); PostListVO postListVO = new PostListVO().convertFrom(post);
@ -486,6 +494,20 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
// Set comment count // Set comment count
postListVO.setCommentCount(commentCountMap.getOrDefault(post.getId(), 0L)); postListVO.setCommentCount(commentCountMap.getOrDefault(post.getId(), 0L));
StringBuilder fullPath = new StringBuilder(blogUrl);
if (permalinkType.equals(PostPermalinkType.DEFAULT)) {
fullPath.append("/")
.append(archivesPrefix)
.append("/")
.append(postListVO.getUrl())
.append(pathSuffix);
} else if (permalinkType.equals(PostPermalinkType.ID)) {
fullPath.append("/?p=")
.append(postListVO.getId());
}
postListVO.setFullPath(fullPath.toString());
return postListVO; return postListVO;
}); });
} }

View File

@ -2,7 +2,7 @@
<div class="post animated fadeInDown"> <div class="post animated fadeInDown">
<div class="post-title"> <div class="post-title">
<h3> <h3>
<a href="${context!}/archives/${post.url}">${post.title}</a> <a href="${post.fullPath!}">${post.title}</a>
</h3> </h3>
</div> </div>
<div class="post-content"> <div class="post-content">