pull/597/head
Ryan Wang 2020-02-25 16:08:59 +08:00 committed by GitHub
parent 7166829d87
commit f4a4d5f250
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 31 deletions

View File

@ -13,19 +13,22 @@ import org.springframework.lang.NonNull;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.util.Assert;
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.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.enums.PostStatus;
import run.halo.app.model.vo.PostDetailVO;
import run.halo.app.service.CategoryService;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostCategoryService;
import run.halo.app.service.PostService;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
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 {
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 PostService postService;
private final CategoryService categoryService;
private final PostCategoryService postCategoryService;
private final OptionService optionService;
private final FreeMarkerConfigurer freeMarker;
public ContentFeedController(PostService postService,
CategoryService categoryService,
PostCategoryService postCategoryService,
OptionService optionService,
FreeMarkerConfigurer freeMarker) {
this.postService = postService;
this.categoryService = categoryService;
this.postCategoryService = postCategoryService;
this.optionService = optionService;
this.freeMarker = freeMarker;
}
@ -56,9 +71,9 @@ public class ContentFeedController {
* Get post rss
*
* @param model model
* @return String
* @throws IOException IOException
* @throws TemplateException TemplateException
* @return rss xml content
* @throws IOException throw IOException
* @throws TemplateException throw TemplateException
*/
@GetMapping(value = {"feed", "feed.xml", "rss", "rss.xml"}, produces = XML_MEDIA_TYPE)
@ResponseBody
@ -68,11 +83,31 @@ public class ContentFeedController {
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
*
* @param model model
* @return String
* @return atom xml content
* @throws IOException IOException
* @throws TemplateException TemplateException
*/
@ -84,11 +119,31 @@ public class ContentFeedController {
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.
*
* @param model model
* @return String
* @return sitemap xml content.
* @throws IOException IOException
* @throws TemplateException TemplateException
*/
@ -105,7 +160,7 @@ public class ContentFeedController {
* Get sitemap.html.
*
* @param model model
* @return String
* @return template path: common/web/sitemap_html
*/
@GetMapping(value = "sitemap.html")
public String sitemapHtml(Model model,
@ -115,10 +170,10 @@ public class ContentFeedController {
}
/**
* Get robots.
* Get robots.txt
*
* @param model model
* @return String
* @return robots.txt content
* @throws IOException IOException
* @throws TemplateException TemplateException
*/
@ -141,22 +196,32 @@ public class ContentFeedController {
}
/**
* Build posts for feed
* Build posts.
*
* @param pageable pageable
* @return List<Post>
* @return list of post detail vo
*/
private List<PostDetailVO> buildPosts(@NonNull Pageable pageable) {
Assert.notNull(pageable, "Pageable must not be null");
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
Page<PostDetailVO> posts = postService.convertToDetailVo(postPage);
posts.getContent().forEach(postListVO -> {
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);
}
});
return posts.getContent();
}
/**
* 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();
}
}

View File

@ -1,13 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text">${blog_title!}</title>
<#if user.description??>
<subtitle type="text">${user.description!}</subtitle>
<#if category??>
<title type="text">分类:${category.name!} - ${blog_title!}</title>
<#else>
<title type="text">${blog_title!}</title>
</#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>
</#if>
</#if>
<updated>${.now?iso_local}</updated>
<id>${blog_url!}</id>
<link rel="alternate" type="text/html" href="${blog_url!}" />
<link rel="self" type="application/atom+xml" href="${atom_url!}" />
<#if category??>
<id>${category.fullPath!}</id>
<#else>
<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="self" type="application/atom+xml" href="${atom_url!}" />
</#if>
<rights>Copyright © ${.now?string('yyyy')}, ${blog_title!}</rights>
<generator uri="https://halo.run/" version="${version!}">Halo</generator>
<#if posts?? && posts?size gt 0>

View File

@ -1,10 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>${blog_title!}</title>
<link>${blog_url!}</link>
<#if user.description??>
<description>${user.description!}</description>
<#if category??>
<title>分类:${category.name!} - ${blog_title!}</title>
<#else>
<title>${blog_title!}</title>
</#if>
<#if category??>
<link>${category.fullPath!}</link>
<#else>
<link>${blog_url!}</link>
</#if>
<#if category??>
<#if category.description?? && category.description!=''>
<description>${category.description!}</description>
</#if>
<#else>
<#if user.description?? && user.description!=''>
<description>${user.description!}</description>
</#if>
</#if>
<language>zh-CN</language>
<generator>Halo ${version!}</generator>