mirror of https://github.com/halo-dev/halo
parent
7166829d87
commit
f4a4d5f250
|
@ -13,19 +13,22 @@ import org.springframework.lang.NonNull;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
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.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||||
|
import run.halo.app.model.dto.CategoryDTO;
|
||||||
|
import run.halo.app.model.entity.Category;
|
||||||
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.PostDetailVO;
|
import run.halo.app.model.vo.PostDetailVO;
|
||||||
|
import run.halo.app.service.CategoryService;
|
||||||
import run.halo.app.service.OptionService;
|
import run.halo.app.service.OptionService;
|
||||||
|
import run.halo.app.service.PostCategoryService;
|
||||||
import run.halo.app.service.PostService;
|
import run.halo.app.service.PostService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||||
|
@ -39,15 +42,27 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||||
public class ContentFeedController {
|
public class ContentFeedController {
|
||||||
|
|
||||||
private final static String UTF_8_SUFFIX = ";charset=UTF-8";
|
private final static String UTF_8_SUFFIX = ";charset=UTF-8";
|
||||||
|
|
||||||
private final static String XML_MEDIA_TYPE = MediaType.APPLICATION_XML_VALUE + UTF_8_SUFFIX;
|
private final static String XML_MEDIA_TYPE = MediaType.APPLICATION_XML_VALUE + UTF_8_SUFFIX;
|
||||||
|
|
||||||
private final PostService postService;
|
private final PostService postService;
|
||||||
|
|
||||||
|
private final CategoryService categoryService;
|
||||||
|
|
||||||
|
private final PostCategoryService postCategoryService;
|
||||||
|
|
||||||
private final OptionService optionService;
|
private final OptionService optionService;
|
||||||
|
|
||||||
private final FreeMarkerConfigurer freeMarker;
|
private final FreeMarkerConfigurer freeMarker;
|
||||||
|
|
||||||
public ContentFeedController(PostService postService,
|
public ContentFeedController(PostService postService,
|
||||||
|
CategoryService categoryService,
|
||||||
|
PostCategoryService postCategoryService,
|
||||||
OptionService optionService,
|
OptionService optionService,
|
||||||
FreeMarkerConfigurer freeMarker) {
|
FreeMarkerConfigurer freeMarker) {
|
||||||
this.postService = postService;
|
this.postService = postService;
|
||||||
|
this.categoryService = categoryService;
|
||||||
|
this.postCategoryService = postCategoryService;
|
||||||
this.optionService = optionService;
|
this.optionService = optionService;
|
||||||
this.freeMarker = freeMarker;
|
this.freeMarker = freeMarker;
|
||||||
}
|
}
|
||||||
|
@ -56,9 +71,9 @@ public class ContentFeedController {
|
||||||
* Get post rss
|
* Get post rss
|
||||||
*
|
*
|
||||||
* @param model model
|
* @param model model
|
||||||
* @return String
|
* @return rss xml content
|
||||||
* @throws IOException IOException
|
* @throws IOException throw IOException
|
||||||
* @throws TemplateException TemplateException
|
* @throws TemplateException throw TemplateException
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = {"feed", "feed.xml", "rss", "rss.xml"}, produces = XML_MEDIA_TYPE)
|
@GetMapping(value = {"feed", "feed.xml", "rss", "rss.xml"}, produces = XML_MEDIA_TYPE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -68,11 +83,31 @@ public class ContentFeedController {
|
||||||
return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get category post rss.
|
||||||
|
*
|
||||||
|
* @param model model
|
||||||
|
* @param slugName slugName
|
||||||
|
* @return rss xml content
|
||||||
|
* @throws IOException throw IOException
|
||||||
|
* @throws TemplateException throw TemplateException
|
||||||
|
*/
|
||||||
|
@GetMapping(value = {"feed/categories/{slugName}", "feed/categories/{slugName}.xml"}, produces = XML_MEDIA_TYPE)
|
||||||
|
@ResponseBody
|
||||||
|
public String feed(Model model, @PathVariable(name = "slugName") String slugName) throws IOException, TemplateException {
|
||||||
|
Category category = categoryService.getBySlugNameOfNonNull(slugName);
|
||||||
|
CategoryDTO categoryDTO = categoryService.convertTo(category);
|
||||||
|
model.addAttribute("category", categoryDTO);
|
||||||
|
model.addAttribute("posts", buildCategoryPosts(buildPostPageable(optionService.getRssPageSize()), categoryDTO));
|
||||||
|
Template template = freeMarker.getConfiguration().getTemplate("common/web/rss.ftl");
|
||||||
|
return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get atom.xml
|
* Get atom.xml
|
||||||
*
|
*
|
||||||
* @param model model
|
* @param model model
|
||||||
* @return String
|
* @return atom xml content
|
||||||
* @throws IOException IOException
|
* @throws IOException IOException
|
||||||
* @throws TemplateException TemplateException
|
* @throws TemplateException TemplateException
|
||||||
*/
|
*/
|
||||||
|
@ -84,11 +119,31 @@ public class ContentFeedController {
|
||||||
return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get category posts atom.xml
|
||||||
|
*
|
||||||
|
* @param model model
|
||||||
|
* @param slugName slugName
|
||||||
|
* @return atom xml content
|
||||||
|
* @throws IOException throw IOException
|
||||||
|
* @throws TemplateException throw TemplateException
|
||||||
|
*/
|
||||||
|
@GetMapping(value = {"atom/categories/{slugName}", "atom/categories/{slugName}.xml"}, produces = XML_MEDIA_TYPE)
|
||||||
|
@ResponseBody
|
||||||
|
public String atom(Model model, @PathVariable(name = "slugName") String slugName) throws IOException, TemplateException {
|
||||||
|
Category category = categoryService.getBySlugNameOfNonNull(slugName);
|
||||||
|
CategoryDTO categoryDTO = categoryService.convertTo(category);
|
||||||
|
model.addAttribute("category", categoryDTO);
|
||||||
|
model.addAttribute("posts", buildCategoryPosts(buildPostPageable(optionService.getRssPageSize()), categoryDTO));
|
||||||
|
Template template = freeMarker.getConfiguration().getTemplate("common/web/atom.ftl");
|
||||||
|
return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get sitemap.xml.
|
* Get sitemap.xml.
|
||||||
*
|
*
|
||||||
* @param model model
|
* @param model model
|
||||||
* @return String
|
* @return sitemap xml content.
|
||||||
* @throws IOException IOException
|
* @throws IOException IOException
|
||||||
* @throws TemplateException TemplateException
|
* @throws TemplateException TemplateException
|
||||||
*/
|
*/
|
||||||
|
@ -105,7 +160,7 @@ public class ContentFeedController {
|
||||||
* Get sitemap.html.
|
* Get sitemap.html.
|
||||||
*
|
*
|
||||||
* @param model model
|
* @param model model
|
||||||
* @return String
|
* @return template path: common/web/sitemap_html
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = "sitemap.html")
|
@GetMapping(value = "sitemap.html")
|
||||||
public String sitemapHtml(Model model,
|
public String sitemapHtml(Model model,
|
||||||
|
@ -115,10 +170,10 @@ public class ContentFeedController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get robots.
|
* Get robots.txt
|
||||||
*
|
*
|
||||||
* @param model model
|
* @param model model
|
||||||
* @return String
|
* @return robots.txt content
|
||||||
* @throws IOException IOException
|
* @throws IOException IOException
|
||||||
* @throws TemplateException TemplateException
|
* @throws TemplateException TemplateException
|
||||||
*/
|
*/
|
||||||
|
@ -141,22 +196,32 @@ public class ContentFeedController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build posts for feed
|
* Build posts.
|
||||||
*
|
*
|
||||||
* @param pageable pageable
|
* @param pageable pageable
|
||||||
* @return List<Post>
|
* @return list of post detail vo
|
||||||
*/
|
*/
|
||||||
private List<PostDetailVO> buildPosts(@NonNull Pageable pageable) {
|
private List<PostDetailVO> buildPosts(@NonNull Pageable pageable) {
|
||||||
|
Assert.notNull(pageable, "Pageable must not be null");
|
||||||
|
|
||||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||||
Page<PostDetailVO> posts = postService.convertToDetailVo(postPage);
|
Page<PostDetailVO> posts = postService.convertToDetailVo(postPage);
|
||||||
posts.getContent().forEach(postListVO -> {
|
return posts.getContent();
|
||||||
try {
|
|
||||||
// Encode post url
|
|
||||||
postListVO.setUrl(URLEncoder.encode(postListVO.getUrl(), StandardCharsets.UTF_8.name()));
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
log.warn("Failed to encode url: " + postListVO.getUrl(), e);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
/**
|
||||||
|
* Build category posts.
|
||||||
|
*
|
||||||
|
* @param pageable pageable must not be null.
|
||||||
|
* @param slugName slugName must not be null.
|
||||||
|
* @return list of post detail vo.
|
||||||
|
*/
|
||||||
|
private List<PostDetailVO> buildCategoryPosts(@NonNull Pageable pageable, @NonNull CategoryDTO category) {
|
||||||
|
Assert.notNull(pageable, "Pageable must not be null");
|
||||||
|
Assert.notNull(category, "Slug name must not be null");
|
||||||
|
|
||||||
|
Page<Post> postPage = postCategoryService.pagePostBy(category.getId(), PostStatus.PUBLISHED, pageable);
|
||||||
|
Page<PostDetailVO> posts = postService.convertToDetailVo(postPage);
|
||||||
return posts.getContent();
|
return posts.getContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,32 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<#if category??>
|
||||||
|
<title type="text">分类:${category.name!} - ${blog_title!}</title>
|
||||||
|
<#else>
|
||||||
<title type="text">${blog_title!}</title>
|
<title type="text">${blog_title!}</title>
|
||||||
<#if user.description??>
|
</#if>
|
||||||
|
<#if category??>
|
||||||
|
<#if category.description?? && category.description!=''>
|
||||||
|
<subtitle type="text">${category.description!}</subtitle>
|
||||||
|
</#if>
|
||||||
|
<#else>
|
||||||
|
<#if user.description?? && user.description!=''>
|
||||||
<subtitle type="text">${user.description!}</subtitle>
|
<subtitle type="text">${user.description!}</subtitle>
|
||||||
</#if>
|
</#if>
|
||||||
|
</#if>
|
||||||
<updated>${.now?iso_local}</updated>
|
<updated>${.now?iso_local}</updated>
|
||||||
|
<#if category??>
|
||||||
|
<id>${category.fullPath!}</id>
|
||||||
|
<#else>
|
||||||
<id>${blog_url!}</id>
|
<id>${blog_url!}</id>
|
||||||
|
</#if>
|
||||||
|
<#if category??>
|
||||||
|
<link rel="alternate" type="text/html" href="${category.fullPath!}" />
|
||||||
|
<link rel="self" type="application/atom+xml" href="${blog_url!}/feed/categories/${category.slugName}.xml" />
|
||||||
|
<#else>
|
||||||
<link rel="alternate" type="text/html" href="${blog_url!}" />
|
<link rel="alternate" type="text/html" href="${blog_url!}" />
|
||||||
<link rel="self" type="application/atom+xml" href="${atom_url!}" />
|
<link rel="self" type="application/atom+xml" href="${atom_url!}" />
|
||||||
|
</#if>
|
||||||
<rights>Copyright © ${.now?string('yyyy')}, ${blog_title!}</rights>
|
<rights>Copyright © ${.now?string('yyyy')}, ${blog_title!}</rights>
|
||||||
<generator uri="https://halo.run/" version="${version!}">Halo</generator>
|
<generator uri="https://halo.run/" version="${version!}">Halo</generator>
|
||||||
<#if posts?? && posts?size gt 0>
|
<#if posts?? && posts?size gt 0>
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<rss version="2.0">
|
<rss version="2.0">
|
||||||
<channel>
|
<channel>
|
||||||
|
<#if category??>
|
||||||
|
<title>分类:${category.name!} - ${blog_title!}</title>
|
||||||
|
<#else>
|
||||||
<title>${blog_title!}</title>
|
<title>${blog_title!}</title>
|
||||||
|
</#if>
|
||||||
|
<#if category??>
|
||||||
|
<link>${category.fullPath!}</link>
|
||||||
|
<#else>
|
||||||
<link>${blog_url!}</link>
|
<link>${blog_url!}</link>
|
||||||
<#if user.description??>
|
</#if>
|
||||||
|
<#if category??>
|
||||||
|
<#if category.description?? && category.description!=''>
|
||||||
|
<description>${category.description!}</description>
|
||||||
|
</#if>
|
||||||
|
<#else>
|
||||||
|
<#if user.description?? && user.description!=''>
|
||||||
<description>${user.description!}</description>
|
<description>${user.description!}</description>
|
||||||
</#if>
|
</#if>
|
||||||
|
</#if>
|
||||||
<language>zh-CN</language>
|
<language>zh-CN</language>
|
||||||
<generator>Halo ${version!}</generator>
|
<generator>Halo ${version!}</generator>
|
||||||
<#if posts?? && posts?size gt 0>
|
<#if posts?? && posts?size gt 0>
|
||||||
|
|
Loading…
Reference in New Issue