Commit b93deb7e authored by Liang Ding's avatar Liang Ding

💥 #12530

parent 2ce472df
......@@ -17,7 +17,6 @@
*/
package org.b3log.solo.processor;
import org.apache.commons.lang.StringUtils;
import org.b3log.latke.Keys;
import org.b3log.latke.Latkes;
import org.b3log.latke.ioc.inject.Inject;
......@@ -36,24 +35,19 @@ import org.b3log.latke.util.Locales;
import org.b3log.solo.SoloServletListener;
import org.b3log.solo.model.Article;
import org.b3log.solo.model.Option;
import org.b3log.solo.model.Tag;
import org.b3log.solo.model.feed.atom.Category;
import org.b3log.solo.model.feed.atom.Entry;
import org.b3log.solo.model.feed.atom.Feed;
import org.b3log.solo.model.feed.rss.Channel;
import org.b3log.solo.model.feed.rss.Item;
import org.b3log.solo.repository.ArticleRepository;
import org.b3log.solo.repository.TagArticleRepository;
import org.b3log.solo.repository.TagRepository;
import org.b3log.solo.service.ArticleQueryService;
import org.b3log.solo.service.PreferenceQueryService;
import org.b3log.solo.service.UserQueryService;
import org.b3log.solo.util.Emotions;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
......@@ -65,7 +59,7 @@ import java.util.List;
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @author <a href="https://github.com/feroozkhanchintu">feroozkhanchintu</a>
* @author <a href="https://github.com/nanolikeyou">nanolikeyou</a>
* @version 1.1.1.2, Sep 20, 2018
* @version 2.0.0.0, Sep 26, 2018
* @since 0.3.1
*/
@RequestProcessor
......@@ -94,31 +88,13 @@ public class FeedProcessor {
@Inject
private PreferenceQueryService preferenceQueryService;
/**
* User query service.
*/
@Inject
private UserQueryService userQueryService;
/**
* Tag repository.
*/
@Inject
private TagRepository tagRepository;
/**
* Tag-Article repository.
*/
@Inject
private TagArticleRepository tagArticleRepository;
/**
* Blog articles Atom output.
*
* @param context the specified context
* @throws Exception exception
*/
@RequestProcessing(value = {"/blog-articles-feed.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
@RequestProcessing(value = "/atom.xml", method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
public void blogArticlesAtom(final HTTPRequestContext context) throws Exception {
final AtomRenderer renderer = new AtomRenderer();
context.setRenderer(renderer);
......@@ -133,7 +109,7 @@ public class FeedProcessor {
feed.setSubtitle(blogSubtitle);
feed.setUpdated(new Date());
feed.setAuthor(blogTitle);
feed.setLink(Latkes.getServePath() + "/blog-articles-feed.do");
feed.setLink(Latkes.getServePath() + "/atom.xml");
feed.setId(Latkes.getServePath() + "/");
final List<Filter> filters = new ArrayList<>();
......@@ -185,126 +161,13 @@ public class FeedProcessor {
return ret;
}
/**
* Tag articles Atom output.
*
* @param context the specified context
* @throws Exception exception
*/
@RequestProcessing(value = {"/tag-articles-feed.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
public void tagArticlesAtom(final HTTPRequestContext context) throws Exception {
final AtomRenderer renderer = new AtomRenderer();
context.setRenderer(renderer);
final HttpServletRequest request = context.getRequest();
final HttpServletResponse response = context.getResponse();
final String tagId = request.getParameter(Keys.OBJECT_ID);
if (StringUtils.isBlank(tagId)) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final Feed feed = new Feed();
try {
final JSONObject tag = tagRepository.get(tagId);
if (null == tag) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final String tagTitle = tag.getString(Tag.TAG_TITLE);
final JSONObject preference = preferenceQueryService.getPreference();
if (null == preference) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final String blogTitle = preference.getString(Option.ID_C_BLOG_TITLE);
final String blogSubtitle = preference.getString(Option.ID_C_BLOG_SUBTITLE) + ", " + tagTitle;
final int outputCnt = preference.getInt(Option.ID_C_FEED_OUTPUT_CNT);
feed.setTitle(blogTitle);
feed.setSubtitle(blogSubtitle);
feed.setUpdated(new Date());
feed.setAuthor(blogTitle);
feed.setLink(Latkes.getServePath() + "/tag-articles-feed.do");
feed.setId(Latkes.getServePath() + "/");
final JSONObject tagArticleResult = tagArticleRepository.getByTagId(tagId, 1, outputCnt);
final JSONArray tagArticleRelations = tagArticleResult.getJSONArray(Keys.RESULTS);
if (0 == tagArticleRelations.length()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final List<JSONObject> articles = new ArrayList<>();
for (int i = 0; i < tagArticleRelations.length(); i++) {
final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(i);
final String articleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID);
final JSONObject article = articleRepository.get(articleId);
if (article.getBoolean(Article.ARTICLE_IS_PUBLISHED) // Skips the unpublished article
&& StringUtils.isBlank(article.optString(Article.ARTICLE_VIEW_PWD))) { // Skips article with password
articles.add(article);
}
}
final boolean isFullContent = "fullContent".equals(preference.getString(Option.ID_C_FEED_OUTPUT_MODE));
for (int i = 0; i < articles.size(); i++) {
final Entry entry = getEntryForArticle(articles, isFullContent, i);
feed.addEntry(entry);
}
renderer.setContent(feed.toString());
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "Get tag article feed error", e);
context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
}
private Entry getEntryForArticle(final List<JSONObject> articles, final boolean isFullContent, int i)
throws JSONException, ServiceException {
final JSONObject article = articles.get(i);
final Entry ret = new Entry();
final String title = article.getString(Article.ARTICLE_TITLE);
ret.setTitle(title);
final String summary = isFullContent ? article.getString(Article.ARTICLE_CONTENT)
: article.optString(Article.ARTICLE_ABSTRACT);
ret.setSummary(summary);
final long updated = article.getLong(Article.ARTICLE_UPDATED);
ret.setUpdated(new Date(updated));
final String link = Latkes.getServePath() + article.getString(Article.ARTICLE_PERMALINK);
ret.setLink(link);
ret.setId(link);
final String authorName = articleQueryService.getAuthor(article).getString(User.USER_NAME);
ret.setAuthor(authorName);
final String tagsString = article.getString(Article.ARTICLE_TAGS_REF);
final String[] tagStrings = tagsString.split(",");
for (final String tagString : tagStrings) {
final Category catetory = new Category();
ret.addCatetory(catetory);
catetory.setTerm(tagString);
}
return ret;
}
/**
* Blog articles RSS output.
*
* @param context the specified context
* @throws Exception exception
*/
@RequestProcessing(value = {"/blog-articles-rss.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
@RequestProcessing(value = "/rss.xml", method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
public void blogArticlesRSS(final HTTPRequestContext context) throws Exception {
final HttpServletResponse response = context.getResponse();
final RssRenderer renderer = new RssRenderer();
......@@ -326,7 +189,7 @@ public class FeedProcessor {
channel.setTitle(blogTitle);
channel.setLastBuildDate(new Date());
channel.setLink(Latkes.getServePath());
channel.setAtomLink(Latkes.getServePath() + "/blog-articles-rss.do");
channel.setAtomLink(Latkes.getServePath() + "/rss.xml");
channel.setGenerator("Solo, ver " + SoloServletListener.VERSION);
final String localeString = preference.getString(Option.ID_C_LOCALE_STRING);
final String country = Locales.getCountry(localeString).toLowerCase();
......@@ -390,123 +253,4 @@ public class FeedProcessor {
return ret;
}
/**
* Tag articles RSS output.
*
* @param context the specified context
* @throws Exception exception
*/
@RequestProcessing(value = {"/tag-articles-rss.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
public void tagArticlesRSS(final HTTPRequestContext context) throws Exception {
final HttpServletResponse response = context.getResponse();
final HttpServletRequest request = context.getRequest();
final RssRenderer renderer = new RssRenderer();
context.setRenderer(renderer);
final String tagId = request.getParameter(Keys.OBJECT_ID);
if (StringUtils.isBlank(tagId)) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final Channel channel = new Channel();
try {
final JSONObject tag = tagRepository.get(tagId);
if (null == tag) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final String tagTitle = tag.getString(Tag.TAG_TITLE);
final JSONObject preference = preferenceQueryService.getPreference();
if (null == preference) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final String blogTitle = preference.getString(Option.ID_C_BLOG_TITLE);
final String blogSubtitle = preference.getString(Option.ID_C_BLOG_SUBTITLE) + ", " + tagTitle;
final int outputCnt = preference.getInt(Option.ID_C_FEED_OUTPUT_CNT);
channel.setTitle(blogTitle);
channel.setLastBuildDate(new Date());
channel.setLink(Latkes.getServePath());
channel.setAtomLink(Latkes.getServePath() + "/tag-articles-rss.do");
channel.setGenerator("Solo, ver " + SoloServletListener.VERSION);
final String localeString = preference.getString(Option.ID_C_LOCALE_STRING);
final String country = Locales.getCountry(localeString).toLowerCase();
final String language = Locales.getLanguage(localeString).toLowerCase();
channel.setLanguage(language + '-' + country);
channel.setDescription(blogSubtitle);
final JSONObject tagArticleResult = tagArticleRepository.getByTagId(tagId, 1, outputCnt);
final JSONArray tagArticleRelations = tagArticleResult.getJSONArray(Keys.RESULTS);
if (0 == tagArticleRelations.length()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
final List<JSONObject> articles = new ArrayList<>();
for (int i = 0; i < tagArticleRelations.length(); i++) {
final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(i);
final String articleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID);
final JSONObject article = articleRepository.get(articleId);
if (article.getBoolean(Article.ARTICLE_IS_PUBLISHED) // Skips the unpublished article
&& StringUtils.isBlank(article.optString(Article.ARTICLE_VIEW_PWD))) { // Skips article with password
articles.add(article);
}
}
final boolean isFullContent = "fullContent".equals(preference.getString(Option.ID_C_FEED_OUTPUT_MODE));
for (int i = 0; i < articles.size(); i++) {
final Item item = getItemForArticles(articles, isFullContent, i);
channel.addItem(item);
}
renderer.setContent(channel.toString());
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "Get tag article rss error", e);
context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
}
private Item getItemForArticles(final List<JSONObject> articles, final boolean isFullContent, int i)
throws JSONException, ServiceException {
final JSONObject article = articles.get(i);
final Item ret = new Item();
final String title = article.getString(Article.ARTICLE_TITLE);
ret.setTitle(title);
final String description = isFullContent
? article.getString(Article.ARTICLE_CONTENT)
: article.optString(Article.ARTICLE_ABSTRACT);
ret.setDescription(description);
final long pubDate = article.getLong(Article.ARTICLE_UPDATED);
ret.setPubDate(new Date(pubDate));
final String link = Latkes.getServePath() + article.getString(Article.ARTICLE_PERMALINK);
ret.setLink(link);
ret.setGUID(link);
final String authorName = articleQueryService.getAuthor(article).getString(User.USER_NAME);
ret.setAuthor(authorName);
final String tagsString = article.getString(Article.ARTICLE_TAGS_REF);
final String[] tagStrings = tagsString.split(",");
for (final String tagString : tagStrings) {
final org.b3log.solo.model.feed.rss.Category catetory = new org.b3log.solo.model.feed.rss.Category();
ret.addCatetory(catetory);
catetory.setTerm(tagString);
}
return ret;
}
}
......@@ -35,7 +35,7 @@ import java.util.regex.Pattern;
* Permalink query service.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.4, Sep 7, 2018
* @version 1.0.0.5, Sep 26, 2018
* @since 0.6.1
*/
@Service
......@@ -62,8 +62,7 @@ public class PermalinkQueryService {
* Reserved permalinks.
*/
public static final String[] RESERVED_LINKS = new String[]{
"/", "/article", "/tags.html", "/tags", "/page", "/blog-articles-feed.do", "/tag-articles-feed.do", "/blog-articles-rss.do",
"/tag-articles-rss.do", "/get-random-articles.do", "/captcha.do", "/kill-browser",
"/", "/article", "/tags.html", "/tags", "/page", "/atom.xml", "/rss.xml", "/get-random-articles.do", "/captcha.do", "/kill-browser",
"/add-article-comment.do", "/add-article-from-symphony-comment.do", "/add-page-comment.do", "/get-article-content", "/sitemap.xml",
"/login", "/logout", "/forgot", "/get-article-content", "/admin-index.do", "/admin-article.do", "/admin-article-list.do",
"/admin-link-list.do", "/admin-preference.do", "/admin-file-list.do", "/admin-page-list.do", "/admin-others.do",
......
......@@ -117,7 +117,7 @@ var soloKanbanniang = {
});
$('#soloKanbanniangRSS').click(function() {
window.location = latkeConfig.servePath + '/blog-articles-rss.do';
window.location = latkeConfig.servePath + '/rss.xml';
});
$('#soloKanbanniangGithub').click(function() {
......
User-agent: *
Allow: /blog-articles-feed.do
Allow: /tag-articles-feed.do*
Disallow: /*.do
Disallow: /login
Disallow: /logout
......
Subproject commit c2c29ee93631faf3b69292de03345cc8740e5b8c
Subproject commit f197f14243b60c389a334fa39dabd563ea6e039a
......@@ -18,9 +18,7 @@
package org.b3log.solo.processor;
import org.apache.commons.lang.StringUtils;
import org.b3log.latke.Keys;
import org.b3log.latke.model.User;
import org.b3log.latke.repository.Query;
import org.b3log.solo.AbstractTestCase;
import org.b3log.solo.service.InitService;
import org.b3log.solo.service.UserQueryService;
......@@ -41,7 +39,7 @@ import static org.mockito.Mockito.when;
* {@link FeedProcessor} test case.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.2, Oct 21, 2017
* @version 1.0.0.3, Sep 26, 2018
* @since 1.7.0
*/
@Test(suiteName = "processor")
......@@ -76,7 +74,7 @@ public class FeedProcessorTestCase extends AbstractTestCase {
public void blogArticlesAtom() throws Exception {
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getServletContext()).thenReturn(mock(ServletContext.class));
when(request.getRequestURI()).thenReturn("/blog-articles-feed.do");
when(request.getRequestURI()).thenReturn("/atom.xml");
when(request.getMethod()).thenReturn("GET");
final MockDispatcherServlet dispatcherServlet = new MockDispatcherServlet();
......@@ -94,36 +92,6 @@ public class FeedProcessorTestCase extends AbstractTestCase {
Assert.assertTrue(StringUtils.startsWith(content, "<?xml version=\"1.0\""));
}
/**
* tagArticlesAtom.
*
* @throws Exception exception
*/
@Test(dependsOnMethods = "init")
public void tagArticlesAtom() throws Exception {
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getServletContext()).thenReturn(mock(ServletContext.class));
when(request.getRequestURI()).thenReturn("/tag-articles-feed.do");
when(request.getMethod()).thenReturn("GET");
final JSONObject tag = getTagRepository().get(new Query()).optJSONArray(Keys.RESULTS).optJSONObject(0);
when(request.getParameter(Keys.OBJECT_ID)).thenReturn(tag.optString(Keys.OBJECT_ID));
final MockDispatcherServlet dispatcherServlet = new MockDispatcherServlet();
dispatcherServlet.init();
final StringWriter stringWriter = new StringWriter();
final PrintWriter printWriter = new PrintWriter(stringWriter);
final HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getWriter()).thenReturn(printWriter);
dispatcherServlet.service(request, response);
final String content = stringWriter.toString();
Assert.assertTrue(StringUtils.startsWith(content, "<?xml version=\"1.0\""));
}
/**
* blogArticlesRSS.
*
......@@ -133,7 +101,7 @@ public class FeedProcessorTestCase extends AbstractTestCase {
public void blogArticlesRSS() throws Exception {
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getServletContext()).thenReturn(mock(ServletContext.class));
when(request.getRequestURI()).thenReturn("/blog-articles-rss.do");
when(request.getRequestURI()).thenReturn("/rss.xml");
when(request.getMethod()).thenReturn("GET");
final MockDispatcherServlet dispatcherServlet = new MockDispatcherServlet();
dispatcherServlet.init();
......@@ -149,33 +117,4 @@ public class FeedProcessorTestCase extends AbstractTestCase {
final String content = stringWriter.toString();
Assert.assertTrue(StringUtils.startsWith(content, "<?xml version=\"1.0\""));
}
/**
* tagArticlesRSS.
*
* @throws Exception exception
*/
@Test(dependsOnMethods = "init")
public void tagArticlesRSS() throws Exception {
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getServletContext()).thenReturn(mock(ServletContext.class));
when(request.getRequestURI()).thenReturn("/tag-articles-rss.do");
when(request.getMethod()).thenReturn("GET");
final JSONObject tag = getTagRepository().get(new Query()).optJSONArray(Keys.RESULTS).optJSONObject(0);
when(request.getParameter(Keys.OBJECT_ID)).thenReturn(tag.optString(Keys.OBJECT_ID));
final MockDispatcherServlet dispatcherServlet = new MockDispatcherServlet();
dispatcherServlet.init();
final StringWriter stringWriter = new StringWriter();
final PrintWriter printWriter = new PrintWriter(stringWriter);
final HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getWriter()).thenReturn(printWriter);
dispatcherServlet.service(request, response);
final String content = stringWriter.toString();
Assert.assertTrue(StringUtils.startsWith(content, "<?xml version=\"1.0\""));
}
}
====
Solo - A small and beautiful blogging system written in Java.
Copyright (c) 2010-2018, b3log.org & hacpai.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
====
User-agent: *
Allow: /blog-articles-feed.do
Allow: /tag-articles-feed.do*
Disallow: /*.do
Disallow: /login
Disallow: /logout
......
......@@ -29,7 +29,7 @@
<meta name="copyright" content="B3log" />
<meta http-equiv="Window-target" content="_top" />
<link type="text/css" rel="stylesheet" href="${staticServePath}/skins/${skinDirName}/css/base${miniPostfix}.css?${staticResourceVersion}" charset="utf-8" />
<link href="${servePath}/blog-articles-rss.do" title="RSS" type="application/rss+xml" rel="alternate" />
<link href="${servePath}/rss.xml" title="RSS" type="application/rss+xml" rel="alternate" />
<link rel="icon" type="image/png" href="${servePath}/favicon.png" />
<link rel="manifest" href="${servePath}/manifest.json">
<link rel="search" type="application/opensearchdescription+xml" title="${title}" href="/opensearch.xml">
......
......@@ -48,7 +48,7 @@
</svg> ${linkLabel}
</a>
<a rel="alternate" href="${servePath}/blog-articles-rss.do" rel="section">
<a rel="alternate" href="${servePath}/rss.xml" rel="section">
<svg>
<use xlink:href="#icon-feed"></use>
</svg>
......
......@@ -57,7 +57,7 @@
<#list pageNavigations as page>
<li><a href="${page.pagePermalink}" target="${page.pageOpenTarget}"><#if page.pageIcon != ''><img class="page-icon" src="${page.pageIcon}"></#if>${page.pageTitle}</a></li>
</#list>
<li><a rel="alternate" href="${servePath}/blog-articles-rss.do"><img src="${staticServePath}/skins/${skinDirName}/images/icon-pool/RSS.png" alt="" />RSS Feed</a></li>
<li><a rel="alternate" href="${servePath}/rss.xml"><img src="${staticServePath}/skins/${skinDirName}/images/icon-pool/RSS.png" alt="" />RSS Feed</a></li>
<li><a href="${servePath}/search?keyword=">Search</a></li>
</ul>
<ul id="head-tags">
......
......@@ -63,7 +63,7 @@ overflow: hidden;
}
</style>
<link href="${servePath}/blog-articles-rss.do" title="RSS" type="application/rss+xml" rel="alternate" />
<link href="${servePath}/rss.xml" title="RSS" type="application/rss+xml" rel="alternate" />
<link rel="icon" type="image/png" href="${servePath}/favicon.png" />
<script type='text/javascript' src='${staticServePath}/skins/${skinDirName}/js/l10n${miniPostfix}.js?${staticResourceVersion}'></script>
<script type="text/javascript" src="${staticServePath}/js/lib/jquery/jquery.min.js" charset="utf-8"></script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment