/* * Symphony - A modern community (forum/SNS/blog) platform written in Java. * Copyright (C) 2012-2017, b3log.org & hacpai.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.b3log.symphony.processor; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.b3log.latke.Keys; import org.b3log.latke.Latkes; import org.b3log.latke.ioc.inject.Inject; import org.b3log.latke.logging.Level; import org.b3log.latke.logging.Logger; import org.b3log.latke.model.Pagination; import org.b3log.latke.service.LangPropsService; import org.b3log.latke.servlet.HTTPRequestContext; import org.b3log.latke.servlet.HTTPRequestMethod; import org.b3log.latke.servlet.annotation.After; import org.b3log.latke.servlet.annotation.Before; import org.b3log.latke.servlet.annotation.RequestProcessing; import org.b3log.latke.servlet.annotation.RequestProcessor; import org.b3log.latke.servlet.renderer.freemarker.AbstractFreeMarkerRenderer; import org.b3log.latke.util.Locales; import org.b3log.latke.util.Stopwatchs; import org.b3log.latke.util.Strings; import org.b3log.symphony.model.Article; import org.b3log.symphony.model.Common; import org.b3log.symphony.model.UserExt; import org.b3log.symphony.processor.advice.AnonymousViewCheck; import org.b3log.symphony.processor.advice.PermissionGrant; import org.b3log.symphony.processor.advice.stopwatch.StopwatchEndAdvice; import org.b3log.symphony.processor.advice.stopwatch.StopwatchStartAdvice; import org.b3log.symphony.service.*; import org.b3log.symphony.util.Emotions; import org.b3log.symphony.util.Markdowns; import org.b3log.symphony.util.Symphonys; import org.json.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.util.*; /** * Index processor. * <p> * <ul> * <li>Shows index (/), GET</li> * <li>Shows recent articles (/recent), GET</li> * <li>Shows hot articles (/hot), GET</li> * <li>Shows perfect articles (/perfect), GET</li> * <li>Shows about (/about), GET</li> * <li>Shows b3log (/b3log), GET</li> * <li>Shows SymHub (/symhub), GET</li> * <li>Shows kill browser (/kill-browser), GET</li> * </ul> * </p> * * @author <a href="http://88250.b3log.org">Liang Ding</a> * @author <a href="http://vanessa.b3log.org">Liyuan Li</a> * @version 1.12.3.24, Dec 26, 2016 * @since 0.2.0 */ @RequestProcessor public class IndexProcessor { /** * Logger. */ private static final Logger LOGGER = Logger.getLogger(IndexProcessor.class.getName()); /** * Article query service. */ @Inject private ArticleQueryService articleQueryService; /** * User query service. */ @Inject private UserQueryService userQueryService; /** * User management service. */ @Inject private UserMgmtService userMgmtService; /** * Data model service. */ @Inject private DataModelService dataModelService; /** * Language service. */ @Inject private LangPropsService langPropsService; /** * Timeline management service. */ @Inject private TimelineMgmtService timelineMgmtService; /** * Shows md guide. * * @param response the specified response * @throws Exception exception */ @RequestProcessing(value = "/guide/markdown", method = HTTPRequestMethod.GET) @Before(adviceClass = {StopwatchStartAdvice.class}) @After(adviceClass = {PermissionGrant.class, StopwatchEndAdvice.class}) public void showMDGuide(final HTTPRequestContext context, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request); context.setRenderer(renderer); renderer.setTemplateName("other/md-guide.ftl"); final Map<String, Object> dataModel = renderer.getDataModel(); InputStream inputStream = null; try { inputStream = IndexProcessor.class.getResourceAsStream("/md_guide.md"); final String md = IOUtils.toString(inputStream, "UTF-8"); String html = Emotions.convert(md); html = Markdowns.toHTML(html); dataModel.put("md", md); dataModel.put("html", html); } catch (final Exception e) { LOGGER.log(Level.ERROR, "Loads markdown guide failed", e); } finally { IOUtils.closeQuietly(inputStream); } dataModelService.fillHeaderAndFooter(request, response, dataModel); } /** * Shows index. * * @param context the specified context * @param request the specified request * @param response the specified response * @throws Exception exception */ @RequestProcessing(value = {"", "/"}, method = HTTPRequestMethod.GET) @Before(adviceClass = {StopwatchStartAdvice.class}) @After(adviceClass = {PermissionGrant.class, StopwatchEndAdvice.class}) public void showIndex(final HTTPRequestContext context, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request); context.setRenderer(renderer); renderer.setTemplateName("index.ftl"); final Map<String, Object> dataModel = renderer.getDataModel(); final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE); final List<JSONObject> recentArticles = articleQueryService.getIndexRecentArticles(avatarViewMode); dataModel.put(Common.RECENT_ARTICLES, recentArticles); JSONObject currentUser = userQueryService.getCurrentUser(request); if (null == currentUser) { userMgmtService.tryLogInWithCookie(request, context.getResponse()); } currentUser = userQueryService.getCurrentUser(request); if (null != currentUser) { if (!UserExt.finshedGuide(currentUser)) { response.sendRedirect(Latkes.getServePath() + "/guide"); return; } final String userId = currentUser.optString(Keys.OBJECT_ID); final int pageSize = Symphonys.getInt("indexArticlesCnt"); final List<JSONObject> followingTagArticles = articleQueryService.getFollowingTagArticles( avatarViewMode, userId, 1, pageSize); dataModel.put(Common.FOLLOWING_TAG_ARTICLES, followingTagArticles); final List<JSONObject> followingUserArticles = articleQueryService.getFollowingUserArticles( avatarViewMode, userId, 1, pageSize); dataModel.put(Common.FOLLOWING_USER_ARTICLES, followingUserArticles); } else { dataModel.put(Common.FOLLOWING_TAG_ARTICLES, Collections.emptyList()); dataModel.put(Common.FOLLOWING_USER_ARTICLES, Collections.emptyList()); } final List<JSONObject> perfectArticles = articleQueryService.getIndexPerfectArticles(avatarViewMode); dataModel.put(Common.PERFECT_ARTICLES, perfectArticles); final List<JSONObject> timelines = timelineMgmtService.getTimelines(); dataModel.put(Common.TIMELINES, timelines); dataModel.put(Common.SELECTED, Common.INDEX); dataModelService.fillHeaderAndFooter(request, response, dataModel); dataModelService.fillIndexTags(dataModel); } /** * Shows recent articles. * * @param context the specified context * @param request the specified request * @param response the specified response * @throws Exception exception */ @RequestProcessing(value = {"/recent", "/recent/hot", "/recent/good", "/recent/reply"}, method = HTTPRequestMethod.GET) @Before(adviceClass = {StopwatchStartAdvice.class, AnonymousViewCheck.class}) @After(adviceClass = {PermissionGrant.class, StopwatchEndAdvice.class}) public void showRecent(final HTTPRequestContext context, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request); context.setRenderer(renderer); renderer.setTemplateName("recent.ftl"); final Map<String, Object> dataModel = renderer.getDataModel(); String pageNumStr = request.getParameter("p"); if (Strings.isEmptyOrNull(pageNumStr) || !Strings.isNumeric(pageNumStr)) { pageNumStr = "1"; } final int pageNum = Integer.valueOf(pageNumStr); int pageSize = Symphonys.getInt("indexArticlesCnt"); final JSONObject user = userQueryService.getCurrentUser(request); if (null != user) { pageSize = user.optInt(UserExt.USER_LIST_PAGE_SIZE); if (!UserExt.finshedGuide(user)) { response.sendRedirect(Latkes.getServePath() + "/guide"); return; } } final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE); String sortModeStr = StringUtils.substringAfter(request.getRequestURI(), "/recent"); int sortMode; switch (sortModeStr) { case "": sortMode = 0; break; case "/hot": sortMode = 1; break; case "/good": sortMode = 2; break; case "/reply": sortMode = 3; break; default: sortMode = 0; } final JSONObject result = articleQueryService.getRecentArticles(avatarViewMode, sortMode, pageNum, pageSize); final List<JSONObject> allArticles = (List<JSONObject>) result.get(Article.ARTICLES); dataModel.put(Common.SELECTED, Common.RECENT); final List<JSONObject> stickArticles = new ArrayList<>(); final Iterator<JSONObject> iterator = allArticles.iterator(); while (iterator.hasNext()) { final JSONObject article = iterator.next(); final boolean stick = article.optInt(Article.ARTICLE_T_STICK_REMAINS) > 0; article.put(Article.ARTICLE_T_IS_STICK, stick); if (stick) { stickArticles.add(article); iterator.remove(); } } dataModel.put(Common.STICK_ARTICLES, stickArticles); dataModel.put(Common.LATEST_ARTICLES, allArticles); final JSONObject pagination = result.getJSONObject(Pagination.PAGINATION); final int pageCount = pagination.optInt(Pagination.PAGINATION_PAGE_COUNT); final List<Integer> pageNums = (List<Integer>) pagination.get(Pagination.PAGINATION_PAGE_NUMS); if (!pageNums.isEmpty()) { dataModel.put(Pagination.PAGINATION_FIRST_PAGE_NUM, pageNums.get(0)); dataModel.put(Pagination.PAGINATION_LAST_PAGE_NUM, pageNums.get(pageNums.size() - 1)); } dataModel.put(Pagination.PAGINATION_CURRENT_PAGE_NUM, pageNum); dataModel.put(Pagination.PAGINATION_PAGE_COUNT, pageCount); dataModel.put(Pagination.PAGINATION_PAGE_NUMS, pageNums); dataModelService.fillHeaderAndFooter(request, response, dataModel); dataModelService.fillRandomArticles(avatarViewMode, dataModel); dataModelService.fillSideHotArticles(avatarViewMode, dataModel); dataModelService.fillSideTags(dataModel); dataModelService.fillLatestCmts(dataModel); dataModel.put(Common.CURRENT, StringUtils.substringAfter(request.getRequestURI(), "/recent")); } /** * Shows hot articles. * * @param context the specified context * @param request the specified request * @param response the specified response * @throws Exception exception */ @RequestProcessing(value = "/hot", method = HTTPRequestMethod.GET) @Before(adviceClass = {StopwatchStartAdvice.class, AnonymousViewCheck.class}) @After(adviceClass = {PermissionGrant.class, StopwatchEndAdvice.class}) public void showHotArticles(final HTTPRequestContext context, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request); context.setRenderer(renderer); renderer.setTemplateName("hot.ftl"); final Map<String, Object> dataModel = renderer.getDataModel(); int pageSize = Symphonys.getInt("indexArticlesCnt"); final JSONObject user = userQueryService.getCurrentUser(request); if (null != user) { pageSize = user.optInt(UserExt.USER_LIST_PAGE_SIZE); } final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE); final List<JSONObject> indexArticles = articleQueryService.getHotArticles(avatarViewMode, pageSize); dataModel.put(Common.INDEX_ARTICLES, indexArticles); dataModel.put(Common.SELECTED, Common.HOT); Stopwatchs.start("Fills"); try { dataModelService.fillHeaderAndFooter(request, response, dataModel); if (!(Boolean) dataModel.get(Common.IS_MOBILE)) { dataModelService.fillRandomArticles(avatarViewMode, dataModel); } dataModelService.fillSideHotArticles(avatarViewMode, dataModel); dataModelService.fillSideTags(dataModel); dataModelService.fillLatestCmts(dataModel); } finally { Stopwatchs.end(); } } /** * Shows SymHub page. * * @param context the specified context * @param request the specified request * @param response the specified response * @throws Exception exception */ @RequestProcessing(value = "/symhub", method = HTTPRequestMethod.GET) @Before(adviceClass = {StopwatchStartAdvice.class, AnonymousViewCheck.class}) @After(adviceClass = {PermissionGrant.class, StopwatchEndAdvice.class}) public void showSymHub(final HTTPRequestContext context, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request); context.setRenderer(renderer); renderer.setTemplateName("other/symhub.ftl"); final Map<String, Object> dataModel = renderer.getDataModel(); final List<JSONObject> syms = Symphonys.getSyms(); dataModel.put("syms", (Object) syms); Stopwatchs.start("Fills"); try { final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE); dataModelService.fillHeaderAndFooter(request, response, dataModel); if (!(Boolean) dataModel.get(Common.IS_MOBILE)) { dataModelService.fillRandomArticles(avatarViewMode, dataModel); } dataModelService.fillSideHotArticles(avatarViewMode, dataModel); dataModelService.fillSideTags(dataModel); dataModelService.fillLatestCmts(dataModel); } finally { Stopwatchs.end(); } } /** * Shows perfect articles. * * @param context the specified context * @param request the specified request * @param response the specified response * @throws Exception exception */ @RequestProcessing(value = "/perfect", method = HTTPRequestMethod.GET) @Before(adviceClass = {StopwatchStartAdvice.class, AnonymousViewCheck.class}) @After(adviceClass = {PermissionGrant.class, StopwatchEndAdvice.class}) public void showPerfectArticles(final HTTPRequestContext context, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request); context.setRenderer(renderer); renderer.setTemplateName("perfect.ftl"); final Map<String, Object> dataModel = renderer.getDataModel(); String pageNumStr = request.getParameter("p"); if (Strings.isEmptyOrNull(pageNumStr) || !Strings.isNumeric(pageNumStr)) { pageNumStr = "1"; } final int pageNum = Integer.valueOf(pageNumStr); int pageSize = Symphonys.getInt("indexArticlesCnt"); final JSONObject user = userQueryService.getCurrentUser(request); if (null != user) { pageSize = user.optInt(UserExt.USER_LIST_PAGE_SIZE); if (!UserExt.finshedGuide(user)) { response.sendRedirect(Latkes.getServePath() + "/guide"); return; } } final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE); final JSONObject result = articleQueryService.getPerfectArticles(avatarViewMode, pageNum, pageSize); final List<JSONObject> perfectArticles = (List<JSONObject>) result.get(Article.ARTICLES); dataModel.put(Common.PERFECT_ARTICLES, perfectArticles); dataModel.put(Common.SELECTED, Common.PERFECT); final JSONObject pagination = result.getJSONObject(Pagination.PAGINATION); final int pageCount = pagination.optInt(Pagination.PAGINATION_PAGE_COUNT); final List<Integer> pageNums = (List<Integer>) pagination.get(Pagination.PAGINATION_PAGE_NUMS); if (!pageNums.isEmpty()) { dataModel.put(Pagination.PAGINATION_FIRST_PAGE_NUM, pageNums.get(0)); dataModel.put(Pagination.PAGINATION_LAST_PAGE_NUM, pageNums.get(pageNums.size() - 1)); } dataModel.put(Pagination.PAGINATION_CURRENT_PAGE_NUM, pageNum); dataModel.put(Pagination.PAGINATION_PAGE_COUNT, pageCount); dataModel.put(Pagination.PAGINATION_PAGE_NUMS, pageNums); dataModelService.fillHeaderAndFooter(request, response, dataModel); dataModelService.fillRandomArticles(avatarViewMode, dataModel); dataModelService.fillSideHotArticles(avatarViewMode, dataModel); dataModelService.fillSideTags(dataModel); dataModelService.fillLatestCmts(dataModel); } /** * Shows about. * * @param response the specified response * @throws Exception exception */ @RequestProcessing(value = "/about", method = HTTPRequestMethod.GET) @Before(adviceClass = StopwatchStartAdvice.class) @After(adviceClass = StopwatchEndAdvice.class) public void showAbout(final HttpServletResponse response) throws Exception { response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); response.setHeader("Location", "https://hacpai.com/article/1440573175609"); response.flushBuffer(); } /** * Shows b3log. * * @param context the specified context * @param request the specified request * @param response the specified response * @throws Exception exception */ @RequestProcessing(value = "/b3log", method = HTTPRequestMethod.GET) @Before(adviceClass = StopwatchStartAdvice.class) @After(adviceClass = {PermissionGrant.class, StopwatchEndAdvice.class}) public void showB3log(final HTTPRequestContext context, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request); context.setRenderer(renderer); renderer.setTemplateName("other/b3log.ftl"); final Map<String, Object> dataModel = renderer.getDataModel(); dataModelService.fillHeaderAndFooter(request, response, dataModel); final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE); dataModelService.fillRandomArticles(avatarViewMode, dataModel); dataModelService.fillSideHotArticles(avatarViewMode, dataModel); dataModelService.fillSideTags(dataModel); dataModelService.fillLatestCmts(dataModel); } /** * Shows kill browser page with the specified context. * * @param context the specified context * @param request the specified HTTP servlet request * @param response the specified HTTP servlet response */ @RequestProcessing(value = "/kill-browser", method = HTTPRequestMethod.GET) @Before(adviceClass = StopwatchStartAdvice.class) @After(adviceClass = StopwatchEndAdvice.class) public void showKillBrowser(final HTTPRequestContext context, final HttpServletRequest request, final HttpServletResponse response) { final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request); renderer.setTemplateName("other/kill-browser.ftl"); context.setRenderer(renderer); final Map<String, Object> dataModel = renderer.getDataModel(); final Map<String, String> langs = langPropsService.getAll(Locales.getLocale()); dataModel.putAll(langs); Keys.fillRuntime(dataModel); dataModelService.fillMinified(dataModel); } }