org.b3log.symphony.api.v2.ArticleAPI2.java Source code

Java tutorial

Introduction

Here is the source code for org.b3log.symphony.api.v2.ArticleAPI2.java

Source

/*
 * 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.api.v2;

import org.apache.commons.lang.StringUtils;
import org.b3log.latke.Keys;
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.model.User;
import org.b3log.latke.service.ServiceException;
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.util.Paginator;
import org.b3log.latke.util.Requests;
import org.b3log.latke.util.Strings;
import org.b3log.symphony.model.*;
import org.b3log.symphony.processor.advice.AnonymousViewCheck;
import org.b3log.symphony.processor.advice.LoginCheck;
import org.b3log.symphony.processor.advice.PermissionCheck;
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.processor.advice.validate.ArticleAddValidation;
import org.b3log.symphony.processor.advice.validate.ArticleUpdateValidation;
import org.b3log.symphony.service.*;
import org.b3log.symphony.util.StatusCodes;
import org.json.JSONObject;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * Article API v2.
 * <p>
 * <ul>
 * <li>Gets latest articles (/api/v2/articles/latest), GET</li>
 * <li>Gets domain articles (/api/v2/articles/domain/{domainURI}), GET</li>
 * <li>Gets tag articles (/api/v2/articles/tag/{tagURI}), GET</li>
 * <li>Gets an article (/api/v2/article/{articleId}), GET</li>
 * <li>Adds an article (/api/v2/article), POST</li>
 * <li>Updates an article (/api/v2/article/{articleId}), PUT</li>
 * <li>Adds a comment (/api/v2/article/comment), POST</li>
 * </ul>
 * </p>
 *
 * @author <a href="http://88250.b3log.org">Liang Ding</a>
 * @version 1.3.0.1, Mar 10, 2016
 * @since 2.0.0
 */
@RequestProcessor
public class ArticleAPI2 {

    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(ArticleAPI2.class);
    /**
     * Article query service.
     */
    @Inject
    private ArticleQueryService articleQueryService;
    /**
     * Domain query service.
     */
    @Inject
    private DomainQueryService domainQueryService;
    /**
     * Tag query service.
     */
    @Inject
    private TagQueryService tagQueryService;
    /**
     * Article mangement service.
     */
    @Inject
    private ArticleMgmtService articleMgmtService;
    /**
     * Comment query service.
     */
    @Inject
    private CommentQueryService commentQueryService;

    /**
     * Updates an article.
     *
     * @param context  the specified context
     * @param request  the specified request
     * @param response the specified response
     * @param id       the specified article id
     */
    @RequestProcessing(value = "/api/v2/article/{id}", method = HTTPRequestMethod.PUT)
    @Before(adviceClass = { StopwatchStartAdvice.class, LoginCheck.class, ArticleUpdateValidation.class,
            PermissionCheck.class })
    @After(adviceClass = StopwatchEndAdvice.class)
    public void updateArticle(final HTTPRequestContext context, final HttpServletRequest request,
            final HttpServletResponse response, final String id) {
        try {
            if (Strings.isEmptyOrNull(id)) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND);

                return;
            }

            context.renderJSON();

            final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE);

            final JSONObject oldArticle = articleQueryService.getArticleById(avatarViewMode, id);
            if (null == oldArticle) {
                context.renderJSONValue(Keys.STATUS_CODE, StatusCodes.NOT_FOUND);
                context.renderMsg("Article not found");

                return;
            }

            final JSONObject requestJSONObject = (JSONObject) request.getAttribute(Keys.REQUEST);

            final String articleTitle = requestJSONObject.optString(Article.ARTICLE_TITLE);
            String articleTags = requestJSONObject.optString(Article.ARTICLE_TAGS);
            final String articleContent = requestJSONObject.optString(Article.ARTICLE_CONTENT);
            final boolean articleCommentable = true;
            final int articleType = requestJSONObject.optInt(Article.ARTICLE_TYPE, Article.ARTICLE_TYPE_C_NORMAL);
            final String articleRewardContent = requestJSONObject.optString(Article.ARTICLE_REWARD_CONTENT);
            final int articleRewardPoint = requestJSONObject.optInt(Article.ARTICLE_REWARD_POINT);
            final String ip = Requests.getRemoteAddr(request);
            final String ua = request.getHeader(Common.USER_AGENT);

            final JSONObject article = new JSONObject();
            article.put(Keys.OBJECT_ID, id);
            article.put(Article.ARTICLE_TITLE, articleTitle);
            article.put(Article.ARTICLE_CONTENT, articleContent);
            article.put(Article.ARTICLE_EDITOR_TYPE, 0);
            article.put(Article.ARTICLE_COMMENTABLE, articleCommentable);
            article.put(Article.ARTICLE_TYPE, articleType);
            article.put(Article.ARTICLE_REWARD_CONTENT, articleRewardContent);
            article.put(Article.ARTICLE_REWARD_POINT, articleRewardPoint);
            article.put(Article.ARTICLE_IP, "");
            if (StringUtils.isNotBlank(ip)) {
                article.put(Article.ARTICLE_IP, ip);
            }
            article.put(Article.ARTICLE_UA, "");
            if (StringUtils.isNotBlank(ua)) {
                article.put(Article.ARTICLE_UA, ua);
            }

            final JSONObject currentUser = (JSONObject) request.getAttribute(User.USER);
            if (null == currentUser || !currentUser.optString(Keys.OBJECT_ID)
                    .equals(oldArticle.optString(Article.ARTICLE_AUTHOR_ID))) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN);

                return;
            }

            article.put(Article.ARTICLE_AUTHOR_ID, currentUser.optString(Keys.OBJECT_ID));

            if (!Role.ROLE_ID_C_ADMIN.equals(currentUser.optString(User.USER_ROLE))) {
                articleTags = articleMgmtService.filterReservedTags(articleTags);
            }

            if (Article.ARTICLE_TYPE_C_DISCUSSION == articleType && StringUtils.isBlank(articleTags)) {
                articleTags = "?";
            }

            if (Article.ARTICLE_TYPE_C_THOUGHT == articleType && StringUtils.isBlank(articleTags)) {
                articleTags = "?";
            }

            article.put(Article.ARTICLE_TAGS, articleTags);

            articleMgmtService.updateArticle(article);

            context.renderJSONValue(Keys.STATUS_CODE, StatusCodes.SUCC);
        } catch (final Exception e) {
            final String msg = e.getMessage();
            LOGGER.log(Level.ERROR, "Updates article[id=" + id + "] failed: {0}", e.getMessage());

            context.renderMsg(msg);
            context.renderJSONValue(Keys.STATUS_CODE, StatusCodes.ERR);
        }
    }

    /**
     * Adds an article.
     *
     * @param context the specified context
     * @param request the specified request
     */
    @RequestProcessing(value = "/api/v2/article", method = HTTPRequestMethod.POST)
    @Before(adviceClass = { StopwatchStartAdvice.class, LoginCheck.class, ArticleAddValidation.class,
            PermissionCheck.class })
    @After(adviceClass = StopwatchEndAdvice.class)
    public void addArticle(final HTTPRequestContext context, final HttpServletRequest request) {
        context.renderJSON();

        final JSONObject requestJSONObject = (JSONObject) request.getAttribute(Keys.REQUEST);

        final String articleTitle = requestJSONObject.optString(Article.ARTICLE_TITLE);
        String articleTags = requestJSONObject.optString(Article.ARTICLE_TAGS);
        final String articleContent = requestJSONObject.optString(Article.ARTICLE_CONTENT);
        final boolean articleCommentable = true;
        final int articleType = Article.ARTICLE_TYPE_C_NORMAL;
        final String articleRewardContent = requestJSONObject.optString(Article.ARTICLE_REWARD_CONTENT);
        final int articleRewardPoint = requestJSONObject.optInt(Article.ARTICLE_REWARD_POINT);
        final String ip = Requests.getRemoteAddr(request);
        final String ua = request.getHeader(Common.USER_AGENT);
        final boolean isAnonymous = requestJSONObject.optBoolean(Article.ARTICLE_ANONYMOUS, false);
        final int articleAnonymous = isAnonymous ? Article.ARTICLE_ANONYMOUS_C_ANONYMOUS
                : Article.ARTICLE_ANONYMOUS_C_PUBLIC;
        final boolean syncWithSymphonyClient = requestJSONObject.optBoolean(Article.ARTICLE_SYNC_TO_CLIENT, false);

        final JSONObject article = new JSONObject();
        article.put(Article.ARTICLE_TITLE, articleTitle);
        article.put(Article.ARTICLE_CONTENT, articleContent);
        article.put(Article.ARTICLE_EDITOR_TYPE, 0);
        article.put(Article.ARTICLE_COMMENTABLE, articleCommentable);
        article.put(Article.ARTICLE_TYPE, articleType);
        article.put(Article.ARTICLE_REWARD_CONTENT, articleRewardContent);
        article.put(Article.ARTICLE_REWARD_POINT, articleRewardPoint);
        article.put(Article.ARTICLE_IP, "");
        if (StringUtils.isNotBlank(ip)) {
            article.put(Article.ARTICLE_IP, ip);
        }
        article.put(Article.ARTICLE_UA, "");
        if (StringUtils.isNotBlank(ua)) {
            article.put(Article.ARTICLE_UA, ua);
        }
        article.put(Article.ARTICLE_ANONYMOUS, articleAnonymous);
        article.put(Article.ARTICLE_SYNC_TO_CLIENT, syncWithSymphonyClient);

        try {
            final JSONObject currentUser = (JSONObject) request.getAttribute(User.USER);

            article.put(Article.ARTICLE_AUTHOR_ID, currentUser.optString(Keys.OBJECT_ID));

            if (!Role.ROLE_ID_C_ADMIN.equals(currentUser.optString(User.USER_ROLE))) {
                articleTags = articleMgmtService.filterReservedTags(articleTags);
            }

            if (Article.ARTICLE_TYPE_C_DISCUSSION == articleType && StringUtils.isBlank(articleTags)) {
                articleTags = "?";
            }

            if (Article.ARTICLE_TYPE_C_THOUGHT == articleType && StringUtils.isBlank(articleTags)) {
                articleTags = "?";
            }

            article.put(Article.ARTICLE_TAGS, articleTags);
            article.put(Article.ARTICLE_T_IS_BROADCAST, false);

            articleMgmtService.addArticle(article);

            context.renderJSONValue(Keys.STATUS_CODE, StatusCodes.SUCC);
        } catch (final ServiceException e) {
            final String msg = e.getMessage();
            LOGGER.log(Level.ERROR, "Adds article[title=" + articleTitle + "] failed: {0}", e.getMessage());

            context.renderMsg(msg);
            context.renderJSONValue(Keys.STATUS_CODE, StatusCodes.ERR);
        }
    }

    /**
     * Gets an article.
     *
     * @param context   the specified context
     * @param request   the specified request
     * @param articleId the specified article id
     */
    @RequestProcessing(value = { "/api/v2/article/{articleId}" }, method = HTTPRequestMethod.GET)
    @Before(adviceClass = { StopwatchStartAdvice.class, AnonymousViewCheck.class })
    @After(adviceClass = { PermissionGrant.class, StopwatchEndAdvice.class })
    public void getArticle(final HTTPRequestContext context, final HttpServletRequest request,
            final String articleId) {
        int page = 1;
        final String p = request.getParameter("p");
        if (Strings.isNumeric(p)) {
            page = Integer.parseInt(p);
        }

        final JSONObject ret = new JSONObject();
        context.renderJSONPretty(ret);

        ret.put(Keys.STATUS_CODE, StatusCodes.ERR);
        ret.put(Keys.MSG, "");

        JSONObject data = null;
        try {
            final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE);
            final JSONObject article = articleQueryService.getArticleById(avatarViewMode, articleId);
            if (null == article) {
                ret.put(Keys.MSG, "Article not found");
                ret.put(Keys.STATUS_CODE, StatusCodes.NOT_FOUND);

                return;
            }

            data = new JSONObject();
            data.put(Article.ARTICLE, article);

            final List<JSONObject> articleComments = commentQueryService.getArticleComments(avatarViewMode,
                    articleId, page, V2s.PAGE_SIZE, UserExt.USER_COMMENT_VIEW_MODE_C_TRADITIONAL);
            article.put(Article.ARTICLE_T_COMMENTS, (Object) articleComments);

            V2s.cleanArticle(article);

            final int commentCnt = article.getInt(Article.ARTICLE_COMMENT_CNT);
            final int pageCount = (int) Math.ceil((double) commentCnt / (double) V2s.PAGE_SIZE);

            final JSONObject pagination = new JSONObject();
            final List<Integer> pageNums = Paginator.paginate(page, V2s.PAGE_SIZE, pageCount, V2s.WINDOW_SIZE);
            pagination.put(Pagination.PAGINATION_PAGE_COUNT, pageCount);
            pagination.put(Pagination.PAGINATION_PAGE_NUMS, pageNums);

            data.put(Pagination.PAGINATION, pagination);

            ret.put(Keys.STATUS_CODE, StatusCodes.SUCC);
        } catch (final Exception e) {
            final String msg = "Gets article failed";

            LOGGER.log(Level.ERROR, msg, e);
            ret.put(Keys.MSG, msg);
        }

        ret.put(Common.DATA, data);
    }

    /**
     * Gets tag articles.
     *
     * @param context the specified context
     * @param request the specified request
     * @param tagURI  the specified tag URI
     */
    @RequestProcessing(value = { "/api/v2/articles/tag/{tagURI}", "/api/v2/articles/tag/{tagURI}/hot",
            "/api/v2/articles/tag/{tagURI}/good", "/api/v2/articles/tag/{tagURI}/reply",
            "/api/v2/articles/tag/{tagURI}/perfect" }, method = HTTPRequestMethod.GET)
    @Before(adviceClass = { StopwatchStartAdvice.class, AnonymousViewCheck.class })
    @After(adviceClass = { PermissionGrant.class, StopwatchEndAdvice.class })
    public void getTagArticles(final HTTPRequestContext context, final HttpServletRequest request,
            final String tagURI) {
        int page = 1;
        final String p = request.getParameter("p");
        if (Strings.isNumeric(p)) {
            page = Integer.parseInt(p);
        }

        final JSONObject ret = new JSONObject();
        context.renderJSONPretty(ret);

        ret.put(Keys.STATUS_CODE, StatusCodes.ERR);
        ret.put(Keys.MSG, "");

        JSONObject data = null;
        try {
            final JSONObject tag = tagQueryService.getTagByURI(tagURI);
            if (null == tag) {
                ret.put(Keys.MSG, "Tag not found");
                ret.put(Keys.STATUS_CODE, StatusCodes.NOT_FOUND);

                return;
            }

            data = new JSONObject();

            data.put(Tag.TAG, tag);
            V2s.cleanTag(tag);

            String sortModeStr = StringUtils.substringAfter(request.getRequestURI(), "/tag/" + tagURI);
            int sortMode;
            switch (sortModeStr) {
            case "":
                sortMode = 0;

                break;
            case "/hot":
                sortMode = 1;

                break;
            case "/good":
                sortMode = 2;

                break;
            case "/reply":
                sortMode = 3;

                break;
            case "/perfect":
                sortMode = 4;

                break;
            default:
                sortMode = 0;
            }

            final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE);
            final List<JSONObject> articles = articleQueryService.getArticlesByTag(avatarViewMode, sortMode, tag,
                    page, V2s.PAGE_SIZE);
            data.put(Article.ARTICLES, articles);
            V2s.cleanArticles(articles);

            final int tagRefCnt = tag.getInt(Tag.TAG_REFERENCE_CNT);
            final int pageCount = (int) Math.ceil(tagRefCnt / (double) V2s.PAGE_SIZE);

            final JSONObject pagination = new JSONObject();
            final List<Integer> pageNums = Paginator.paginate(page, V2s.PAGE_SIZE, pageCount, V2s.WINDOW_SIZE);
            pagination.put(Pagination.PAGINATION_PAGE_COUNT, pageCount);
            pagination.put(Pagination.PAGINATION_PAGE_NUMS, pageNums);

            data.put(Pagination.PAGINATION, pagination);

            ret.put(Keys.STATUS_CODE, StatusCodes.SUCC);
        } catch (final Exception e) {
            final String msg = "Gets tag [uri=" + tagURI + "] articles failed";

            LOGGER.log(Level.ERROR, msg, e);
            ret.put(Keys.MSG, msg);
        }

        ret.put(Common.DATA, data);
    }

    /**
     * Gets domain articles.
     *
     * @param context   the specified context
     * @param request   the specified request
     * @param domainURI the specified domain URI
     */
    @RequestProcessing(value = { "/api/v2/articles/domain/{domainURI}" }, method = HTTPRequestMethod.GET)
    @Before(adviceClass = { StopwatchStartAdvice.class, AnonymousViewCheck.class })
    @After(adviceClass = { PermissionGrant.class, StopwatchEndAdvice.class })
    public void getDomainArticles(final HTTPRequestContext context, final HttpServletRequest request,
            final String domainURI) {
        int page = 1;
        final String p = request.getParameter("p");
        if (Strings.isNumeric(p)) {
            page = Integer.parseInt(p);
        }

        final JSONObject ret = new JSONObject();
        context.renderJSONPretty(ret);

        ret.put(Keys.STATUS_CODE, StatusCodes.ERR);
        ret.put(Keys.MSG, "");

        JSONObject data = null;
        try {
            final JSONObject domain = domainQueryService.getByURI(domainURI);
            if (null == domain) {
                ret.put(Keys.MSG, "Domain not found");
                ret.put(Keys.STATUS_CODE, StatusCodes.NOT_FOUND);

                return;
            }

            data = new JSONObject();

            final List<JSONObject> tags = domainQueryService.getTags(domain.optString(Keys.OBJECT_ID));
            domain.put(Domain.DOMAIN_T_TAGS, (Object) tags);

            data.put(Domain.DOMAIN, domain);
            V2s.cleanDomain(domain);

            final String domainId = domain.optString(Keys.OBJECT_ID);
            final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE);

            final JSONObject result = articleQueryService.getDomainArticles(avatarViewMode, domainId, page,
                    V2s.PAGE_SIZE);
            final List<JSONObject> articles = (List<JSONObject>) result.opt(Article.ARTICLES);
            data.put(Article.ARTICLES, articles);
            V2s.cleanArticles(articles);

            data.put(Pagination.PAGINATION, result.optJSONObject(Pagination.PAGINATION));

            ret.put(Keys.STATUS_CODE, StatusCodes.SUCC);
        } catch (final Exception e) {
            final String msg = "Gets domain [uri=" + domainURI + "] articles failed";

            LOGGER.log(Level.ERROR, msg, e);
            ret.put(Keys.MSG, msg);
        }

        ret.put(Common.DATA, data);
    }

    /**
     * Gets latest articles.
     *
     * @param context the specified context
     * @param request the specified request
     */
    @RequestProcessing(value = { "/api/v2/articles/latest", "/api/v2/articles/latest/hot",
            "/api/v2/articles/latest/good", "/api/v2/articles/latest/reply" }, method = HTTPRequestMethod.GET)
    @Before(adviceClass = { StopwatchStartAdvice.class, AnonymousViewCheck.class })
    @After(adviceClass = { PermissionGrant.class, StopwatchEndAdvice.class })
    public void getLatestArticles(final HTTPRequestContext context, final HttpServletRequest request) {
        int page = 1;
        final String p = request.getParameter("p");
        if (Strings.isNumeric(p)) {
            page = Integer.parseInt(p);
        }

        final JSONObject ret = new JSONObject();
        context.renderJSONPretty(ret);

        ret.put(Keys.STATUS_CODE, StatusCodes.ERR);
        ret.put(Keys.MSG, "");

        JSONObject data = null;
        try {
            final int avatarViewMode = (int) request.getAttribute(UserExt.USER_AVATAR_VIEW_MODE);

            String sortModeStr = StringUtils.substringAfter(request.getRequestURI(), "/latest");
            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;
            }

            data = articleQueryService.getRecentArticles(avatarViewMode, sortMode, page, V2s.PAGE_SIZE);
            final List<JSONObject> articles = (List<JSONObject>) data.opt(Article.ARTICLES);
            V2s.cleanArticles(articles);

            ret.put(Keys.STATUS_CODE, StatusCodes.SUCC);
        } catch (final Exception e) {
            final String msg = "Gets latest articles failed";

            LOGGER.log(Level.ERROR, msg, e);
            ret.put(Keys.MSG, msg);
        }

        ret.put(Common.DATA, data);
    }
}