/**
   * Updates an article remotely.
   *
   * <p>This interface will be called by Rhythm, so here is no article data validation, just only
   * validate the B3 key.
   *
   * <p>The request json object, for example,
   *
   * <pre>
   * {
   *     "article": {
   *         "articleAuthorEmail": "*****@*****.**",
   *         "articleContent": "&lt;p&gt;test&lt;\/p&gt;",
   *         "articleCreateDate": 1350635469922,
   *         "articlePermalink": "/articles/2012/10/19/1350635469866.html",
   *         "articleTags": "test",
   *         "articleTitle": "test",
   *         "clientArticleId": "1350635469866",
   *         "oId": "1350635469866"
   *     },
   *     "clientAdminEmail": "*****@*****.**",
   *     "clientHost": "http://localhost:11099",
   *     "clientName": "B3log Solo",
   *     "clientTitle": "简约设计の艺术",
   *     "clientRuntimeEnv": "LOCAL",
   *     "clientVersion": "0.5.0",
   *     "symphonyKey": "....",
   *     "userB3Key": "Your key"
   * }
   * </pre>
   *
   * @param context the specified context
   * @param request the specified request
   * @param response the specified response
   * @throws Exception exception
   */
  @RequestProcessing(value = "/rhythm/article", method = HTTPRequestMethod.PUT)
  @Before(adviceClass = StopwatchStartAdvice.class)
  @After(adviceClass = StopwatchEndAdvice.class)
  public void updateArticleFromRhythm(
      final HTTPRequestContext context,
      final HttpServletRequest request,
      final HttpServletResponse response)
      throws Exception {
    final JSONRenderer renderer = new JSONRenderer();
    context.setRenderer(renderer);

    final JSONObject ret = Results.falseResult();
    renderer.setJSONObject(ret);

    final JSONObject requestJSONObject = Requests.parseRequestJSONObject(request, response);

    final String userB3Key = requestJSONObject.getString(UserExt.USER_B3_KEY);
    final String symphonyKey = requestJSONObject.getString(Common.SYMPHONY_KEY);
    final String clientAdminEmail = requestJSONObject.getString(Client.CLIENT_ADMIN_EMAIL);
    final String clientName = requestJSONObject.getString(Client.CLIENT_NAME);
    final String clientTitle = requestJSONObject.getString(Client.CLIENT_T_TITLE);
    final String clientVersion = requestJSONObject.getString(Client.CLIENT_VERSION);
    final String clientHost = requestJSONObject.getString(Client.CLIENT_HOST);
    final String clientRuntimeEnv = requestJSONObject.getString(Client.CLIENT_RUNTIME_ENV);

    final String maybeIP = StringUtils.substringBetween(clientHost, "://", ":");
    if (Networks.isIPv4(maybeIP)) {
      LOGGER.log(
          Level.WARN,
          "Sync update article error, caused by the client host [{0}] is invalid",
          clientHost);

      return;
    }

    final JSONObject user = userQueryService.getUserByEmail(clientAdminEmail);
    if (null == user) {
      LOGGER.log(
          Level.WARN,
          "The user[email={0}, host={1}] not found in community",
          clientAdminEmail,
          clientHost);

      return;
    }

    final String userName = user.optString(User.USER_NAME);

    if (!Symphonys.get("keyOfSymphony").equals(symphonyKey)
        || !user.optString(UserExt.USER_B3_KEY).equals(userB3Key)) {
      LOGGER.log(
          Level.WARN,
          "B3 key not match, ignored update article [name={0}, host={1}]",
          userName,
          clientHost);

      return;
    }

    if (UserExt.USER_STATUS_C_VALID != user.optInt(UserExt.USER_STATUS)) {
      LOGGER.log(
          Level.WARN, "The user[name={0}, host={1}] has been forbidden", userName, clientHost);

      return;
    }

    final JSONObject originalArticle = requestJSONObject.getJSONObject(Article.ARTICLE);

    final String articleTitle = originalArticle.optString(Article.ARTICLE_TITLE);
    String articleTags =
        articleMgmtService.formatArticleTags(originalArticle.optString(Article.ARTICLE_TAGS));
    String articleContent = originalArticle.optString(Article.ARTICLE_CONTENT);

    final String permalink = originalArticle.optString(Article.ARTICLE_PERMALINK);
    articleContent +=
        "<p class='fn-clear'><span class='fn-right'><span class='ft-small'>该文章同步自</span> "
            + "<i style='margin-right:5px;'><a target='_blank' href='"
            + clientHost
            + permalink
            + "'>"
            + clientTitle
            + "</a></i></span></p>";

    final String authorId = user.optString(Keys.OBJECT_ID);
    final String clientArticleId = originalArticle.optString(Keys.OBJECT_ID);
    final JSONObject oldArticle =
        articleQueryService.getArticleByClientArticleId(authorId, clientArticleId);
    if (null == oldArticle) {
      LOGGER.log(
          Level.WARN,
          "Not found article [clientHost={0}, clientArticleId={1}] to update",
          clientHost,
          clientArticleId);

      return;
    }

    final JSONObject article = new JSONObject();
    article.put(Keys.OBJECT_ID, oldArticle.optString(Keys.OBJECT_ID));
    article.put(Article.ARTICLE_TITLE, articleTitle);
    article.put(Article.ARTICLE_CONTENT, articleContent);
    article.put(Article.ARTICLE_EDITOR_TYPE, 0);
    article.put(Article.ARTICLE_SYNC_TO_CLIENT, false);
    article.put(Article.ARTICLE_CLIENT_ARTICLE_ID, clientArticleId);
    article.put(Article.ARTICLE_AUTHOR_ID, authorId);
    article.put(Article.ARTICLE_AUTHOR_EMAIL, clientAdminEmail.toLowerCase().trim());
    article.put(Article.ARTICLE_T_IS_BROADCAST, false);

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

    try {
      if (Strings.isEmptyOrNull(articleTags)) {
        throw new ServiceException(langPropsService.get("articleTagReservedLabel"));
      }

      article.put(Article.ARTICLE_TAGS, articleTags);

      articleMgmtService.updateArticle(article);

      ret.put(Keys.STATUS_CODE, true);
    } catch (final ServiceException e) {
      final String msg = langPropsService.get("updateFailLabel") + " - " + e.getMessage();
      LOGGER.log(Level.ERROR, msg, e);

      ret.put(Keys.MSG, msg);
    }

    // Updates client record
    JSONObject client = clientQueryService.getClientByAdminEmail(clientAdminEmail);
    if (null == client) {
      client = new JSONObject();
      client.put(Client.CLIENT_ADMIN_EMAIL, clientAdminEmail);
      client.put(Client.CLIENT_HOST, clientHost);
      client.put(Client.CLIENT_NAME, clientName);
      client.put(Client.CLIENT_RUNTIME_ENV, clientRuntimeEnv);
      client.put(Client.CLIENT_VERSION, clientVersion);
      client.put(Client.CLIENT_LATEST_ADD_COMMENT_TIME, 0L);
      client.put(Client.CLIENT_LATEST_ADD_ARTICLE_TIME, System.currentTimeMillis());

      clientMgmtService.addClient(client);
    } else {
      client.put(Client.CLIENT_ADMIN_EMAIL, clientAdminEmail);
      client.put(Client.CLIENT_HOST, clientHost);
      client.put(Client.CLIENT_NAME, clientName);
      client.put(Client.CLIENT_RUNTIME_ENV, clientRuntimeEnv);
      client.put(Client.CLIENT_VERSION, clientVersion);
      client.put(Client.CLIENT_LATEST_ADD_ARTICLE_TIME, System.currentTimeMillis());

      clientMgmtService.updateClient(client);
    }
  }
  /**
   * Try to write response from cache.
   *
   * @param request the specified request
   * @param response the specified response
   * @param chain filter chain
   * @throws IOException io exception
   * @throws ServletException servlet exception
   */
  @Override
  public void doFilter(
      final ServletRequest request, final ServletResponse response, final FilterChain chain)
      throws IOException, ServletException {
    final long startTimeMillis = System.currentTimeMillis();
    request.setAttribute(Keys.HttpRequest.START_TIME_MILLIS, startTimeMillis);

    final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    final String requestURI = httpServletRequest.getRequestURI();
    LOGGER.log(Level.FINER, "Request URI[{0}]", requestURI);

    if (StaticResources.isStatic(httpServletRequest)) {
      final String path = httpServletRequest.getServletPath() + httpServletRequest.getPathInfo();
      LOGGER.log(Level.FINEST, "Requests a static resource, forwards to servlet[path={0}]", path);
      request.getRequestDispatcher(path).forward(request, response);

      return;
    }

    if (!Latkes.isPageCacheEnabled()) {
      LOGGER.log(Level.FINEST, "Page cache is disabled");
      chain.doFilter(request, response);

      return;
    }

    final String skinDirName = (String) httpServletRequest.getAttribute(Keys.TEMAPLTE_DIR_NAME);
    if ("mobile".equals(skinDirName)) {
      // Mobile request, bypasses page caching
      chain.doFilter(request, response);

      return;
    }

    String pageCacheKey;
    final String queryString = httpServletRequest.getQueryString();
    pageCacheKey = (String) request.getAttribute(Keys.PAGE_CACHE_KEY);
    if (Strings.isEmptyOrNull(pageCacheKey)) {
      pageCacheKey = PageCaches.getPageCacheKey(requestURI, queryString);
      request.setAttribute(Keys.PAGE_CACHE_KEY, pageCacheKey);
    }

    final JSONObject cachedPageContentObject =
        PageCaches.get(pageCacheKey, httpServletRequest, (HttpServletResponse) response);

    if (null == cachedPageContentObject) {
      LOGGER.log(Level.FINER, "Page cache miss for request URI[{0}]", requestURI);
      chain.doFilter(request, response);

      return;
    }

    final String cachedType = cachedPageContentObject.optString(PageCaches.CACHED_TYPE);

    try {
      // If cached an article that has view password, dispatches the password form
      if (langPropsService.get(PageTypes.ARTICLE.getLangeLabel()).equals(cachedType)
          && cachedPageContentObject.has(PageCaches.CACHED_PWD)) {
        JSONObject article = new JSONObject();

        final String articleId = cachedPageContentObject.optString(PageCaches.CACHED_OID);

        article.put(Keys.OBJECT_ID, articleId);
        article.put(
            Article.ARTICLE_VIEW_PWD, cachedPageContentObject.optString(PageCaches.CACHED_PWD));

        if (articles.needViewPwd(httpServletRequest, article)) {
          article = articleRepository.get(articleId); // Loads the article entity

          final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
          try {
            httpServletResponse.sendRedirect(
                Latkes.getServePath()
                    + "/console/article-pwd"
                    + articles.buildArticleViewPwdFormParameters(article));
            return;
          } catch (final Exception e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
          }
        }
      }
    } catch (final Exception e) {
      LOGGER.log(Level.SEVERE, e.getMessage(), e);
      chain.doFilter(request, response);
    }

    try {
      LOGGER.log(
          Level.FINEST, "Writes resposne for page[pageCacheKey={0}] from cache", pageCacheKey);
      response.setContentType("text/html");
      response.setCharacterEncoding("UTF-8");
      final PrintWriter writer = response.getWriter();
      String cachedPageContent = cachedPageContentObject.getString(PageCaches.CACHED_CONTENT);
      final String topBarHTML =
          TopBars.getTopBarHTML((HttpServletRequest) request, (HttpServletResponse) response);
      cachedPageContent = cachedPageContent.replace(Common.TOP_BAR_REPLACEMENT_FLAG, topBarHTML);

      final String cachedTitle = cachedPageContentObject.getString(PageCaches.CACHED_TITLE);
      LOGGER.log(
          Level.FINEST,
          "Cached value[key={0}, type={1}, title={2}]",
          new Object[] {pageCacheKey, cachedType, cachedTitle});

      statistics.incBlogViewCount((HttpServletRequest) request, (HttpServletResponse) response);

      final long endimeMillis = System.currentTimeMillis();
      final String dateString = DateFormatUtils.format(endimeMillis, "yyyy/MM/dd HH:mm:ss");
      final String msg =
          String.format(
              "<!-- Cached by B3log Solo(%1$d ms), %2$s -->",
              endimeMillis - startTimeMillis, dateString);
      LOGGER.finer(msg);
      cachedPageContent += Strings.LINE_SEPARATOR + msg;
      writer.write(cachedPageContent);
      writer.flush();
      writer.close();
    } catch (final JSONException e) {
      LOGGER.log(Level.SEVERE, e.getMessage(), e);
      chain.doFilter(request, response);
    } catch (final RepositoryException e) {
      LOGGER.log(Level.SEVERE, e.getMessage(), e);
      chain.doFilter(request, response);
    } catch (final ServiceException e) {
      LOGGER.log(Level.SEVERE, e.getMessage(), e);
      chain.doFilter(request, response);
    }
  }
  /**
   * Updates an article locally.
   *
   * <p>The request json object (an article):
   *
   * <pre>
   * {
   *   "articleTitle": "",
   *   "articleTags": "", // Tags spliting by ','
   *   "articleContent": "",
   *   "articleCommentable": boolean,
   *   "articleType": int,
   *   "articleRewardContent": "",
   *   "articleRewardPoint": int
   * }
   * </pre>
   *
   * @param context the specified context
   * @param request the specified request
   * @param response the specified response
   * @param id the specified article id
   * @throws Exception exception
   */
  @RequestProcessing(value = "/article/{id}", method = HTTPRequestMethod.PUT)
  @Before(
      adviceClass = {
        StopwatchStartAdvice.class,
        LoginCheck.class,
        CSRFCheck.class,
        ArticleUpdateValidation.class
      })
  @After(adviceClass = StopwatchEndAdvice.class)
  public void updateArticle(
      final HTTPRequestContext context,
      final HttpServletRequest request,
      final HttpServletResponse response,
      final String id)
      throws Exception {
    if (Strings.isEmptyOrNull(id)) {
      response.sendError(HttpServletResponse.SC_NOT_FOUND);

      return;
    }

    final JSONObject oldArticle = articleQueryService.getArticleById(id);
    if (null == oldArticle) {
      response.sendError(HttpServletResponse.SC_NOT_FOUND);

      return;
    }

    final JSONRenderer renderer = new JSONRenderer();
    context.setRenderer(renderer);

    final JSONObject ret = Results.falseResult();
    renderer.setJSONObject(ret);

    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 = requestJSONObject.optBoolean(Article.ARTICLE_COMMENTABLE);
    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 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);

    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));

    final String authorEmail = currentUser.optString(User.USER_EMAIL);
    article.put(Article.ARTICLE_AUTHOR_EMAIL, authorEmail);

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

    try {
      if (Strings.isEmptyOrNull(articleTags)) {
        throw new ServiceException(langPropsService.get("articleTagReservedLabel"));
      }

      article.put(Article.ARTICLE_TAGS, articleTags);

      articleMgmtService.updateArticle(article);
      ret.put(Keys.STATUS_CODE, true);
    } catch (final ServiceException e) {
      final String msg = e.getMessage();
      LOGGER.log(
          Level.ERROR, "Adds article[title=" + articleTitle + "] failed: {0}", e.getMessage());

      ret.put(Keys.MSG, msg);
    }
  }
Exemple #4
0
  /**
   * Shows articles related with a tag with the specified context.
   *
   * @param context the specified context
   * @throws IOException io exception
   */
  @RequestProcessing(value = "/tags/**", method = HTTPRequestMethod.GET)
  public void showTagArticles(final HTTPRequestContext context) throws IOException {
    final AbstractFreeMarkerRenderer renderer = new FreeMarkerRenderer();

    context.setRenderer(renderer);

    renderer.setTemplateName("tag-articles.ftl");
    final Map<String, Object> dataModel = renderer.getDataModel();

    final HttpServletRequest request = context.getRequest();
    final HttpServletResponse response = context.getResponse();

    try {
      String requestURI = request.getRequestURI();

      if (!requestURI.endsWith("/")) {
        requestURI += "/";
      }

      String tagTitle = getTagTitle(requestURI);
      final int currentPageNum = getCurrentPageNum(requestURI, tagTitle);

      if (-1 == currentPageNum) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
      }

      LOGGER.log(
          Level.DEBUG,
          "Tag[title={0}, currentPageNum={1}]",
          new Object[] {tagTitle, currentPageNum});

      tagTitle = URLDecoder.decode(tagTitle, "UTF-8");
      final JSONObject result = tagQueryService.getTagByTitle(tagTitle);

      if (null == result) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
      }

      final JSONObject tag = result.getJSONObject(Tag.TAG);
      final String tagId = tag.getString(Keys.OBJECT_ID);

      final JSONObject preference = preferenceQueryService.getPreference();

      Skins.fillLangs(
          preference.optString(Option.ID_C_LOCALE_STRING),
          (String) request.getAttribute(Keys.TEMAPLTE_DIR_NAME),
          dataModel);

      final int pageSize = preference.getInt(Option.ID_C_ARTICLE_LIST_DISPLAY_COUNT);
      final int windowSize = preference.getInt(Option.ID_C_ARTICLE_LIST_PAGINATION_WINDOW_SIZE);

      final List<JSONObject> articles =
          articleQueryService.getArticlesByTag(tagId, currentPageNum, pageSize);

      if (articles.isEmpty()) {
        try {
          response.sendError(HttpServletResponse.SC_NOT_FOUND);
          return;
        } catch (final IOException ex) {
          LOGGER.error(ex.getMessage());
        }
      }

      final boolean hasMultipleUsers = userQueryService.hasMultipleUsers();

      if (hasMultipleUsers) {
        filler.setArticlesExProperties(request, articles, preference);
      } else {
        // All articles composed by the same author
        final JSONObject author = articleQueryService.getAuthor(articles.get(0));

        filler.setArticlesExProperties(request, articles, author, preference);
      }

      final int tagArticleCount = tag.getInt(Tag.TAG_PUBLISHED_REFERENCE_COUNT);
      final int pageCount = (int) Math.ceil((double) tagArticleCount / (double) pageSize);

      LOGGER.log(
          Level.TRACE,
          "Paginate tag-articles[currentPageNum={0}, pageSize={1}, pageCount={2}, windowSize={3}]",
          new Object[] {currentPageNum, pageSize, pageCount, windowSize});
      final List<Integer> pageNums =
          Paginator.paginate(currentPageNum, pageSize, pageCount, windowSize);

      LOGGER.log(Level.TRACE, "tag-articles[pageNums={0}]", pageNums);

      Collections.sort(articles, Comparators.ARTICLE_CREATE_DATE_COMPARATOR);

      fillPagination(dataModel, pageCount, currentPageNum, articles, pageNums);
      dataModel.put(Common.PATH, "/tags/" + URLEncoder.encode(tagTitle, "UTF-8"));
      dataModel.put(Keys.OBJECT_ID, tagId);
      dataModel.put(Tag.TAG, tag);

      filler.fillSide(request, dataModel, preference);
      filler.fillBlogHeader(request, response, dataModel, preference);
      filler.fillBlogFooter(request, dataModel, preference);

      statisticMgmtService.incBlogViewCount(request, response);
    } catch (final ServiceException e) {
      LOGGER.log(Level.ERROR, e.getMessage(), e);

      try {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
      } catch (final IOException ex) {
        LOGGER.error(ex.getMessage());
      }
    } catch (final JSONException e) {
      LOGGER.log(Level.ERROR, e.getMessage(), e);

      try {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
      } catch (final IOException ex) {
        LOGGER.error(ex.getMessage());
      }
    }
  }