/**
   * Gets the latest comments with the specified fetch size.
   *
   * <p>The returned comments content is plain text.
   *
   * @param fetchSize the specified fetch size
   * @return the latest comments, returns an empty list if not found
   * @throws ServiceException service exception
   */
  public List<JSONObject> getLatestComments(final int fetchSize) throws ServiceException {
    final Query query =
        new Query()
            .addSort(Comment.COMMENT_CREATE_TIME, SortDirection.DESCENDING)
            .setCurrentPageNum(1)
            .setPageSize(fetchSize)
            .setPageCount(1);
    try {
      final JSONObject result = commentRepository.get(query);
      final List<JSONObject> ret =
          CollectionUtils.<JSONObject>jsonArrayToList(result.optJSONArray(Keys.RESULTS));

      for (final JSONObject comment : ret) {
        comment.put(Comment.COMMENT_CREATE_TIME, comment.optLong(Comment.COMMENT_CREATE_TIME));
        final String articleId = comment.optString(Comment.COMMENT_ON_ARTICLE_ID);
        final JSONObject article = articleRepository.get(articleId);
        comment.put(
            Comment.COMMENT_T_ARTICLE_TITLE,
            Emotions.clear(article.optString(Article.ARTICLE_TITLE)));
        comment.put(
            Comment.COMMENT_T_ARTICLE_PERMALINK, article.optString(Article.ARTICLE_PERMALINK));

        final String commenterId = comment.optString(Comment.COMMENT_AUTHOR_ID);
        final JSONObject commenter = userRepository.get(commenterId);

        if (UserExt.USER_STATUS_C_INVALID == commenter.optInt(UserExt.USER_STATUS)
            || Comment.COMMENT_STATUS_C_INVALID == comment.optInt(Comment.COMMENT_STATUS)) {
          comment.put(Comment.COMMENT_CONTENT, langPropsService.get("commentContentBlockLabel"));
        }

        if (Article.ARTICLE_TYPE_C_DISCUSSION == article.optInt(Article.ARTICLE_TYPE)) {
          comment.put(Comment.COMMENT_CONTENT, "....");
        }

        String content = comment.optString(Comment.COMMENT_CONTENT);
        content = Emotions.clear(content);
        content = Jsoup.clean(content, Whitelist.none());
        if (StringUtils.isBlank(content)) {
          comment.put(Comment.COMMENT_CONTENT, "....");
        } else {
          comment.put(Comment.COMMENT_CONTENT, content);
        }

        final String commenterEmail = comment.optString(Comment.COMMENT_AUTHOR_EMAIL);
        final String avatarURL = avatarQueryService.getAvatarURL(commenterEmail);
        commenter.put(UserExt.USER_AVATAR_URL, avatarURL);

        comment.put(Comment.COMMENT_T_COMMENTER, commenter);
      }

      return ret;
    } catch (final RepositoryException e) {
      LOGGER.log(Level.ERROR, "Gets user comments failed", e);
      throw new ServiceException(e);
    }
  }
  /**
   * Markdowns.
   *
   * <p>Renders the response with a json object, for example,
   *
   * <pre>
   * {
   *     "html": ""
   * }
   * </pre>
   *
   * @param request the specified http servlet request
   * @param response the specified http servlet response
   * @param context the specified http request context
   * @throws Exception exception
   */
  @RequestProcessing(value = "/markdown", method = HTTPRequestMethod.POST)
  @Before(adviceClass = StopwatchStartAdvice.class)
  @After(adviceClass = StopwatchEndAdvice.class)
  public void markdown2HTML(
      final HttpServletRequest request,
      final HttpServletResponse response,
      final HTTPRequestContext context)
      throws Exception {
    final JSONRenderer renderer = new JSONRenderer();
    context.setRenderer(renderer);
    final JSONObject result = new JSONObject();
    renderer.setJSONObject(result);

    result.put(Keys.STATUS_CODE, true);

    String markdownText = request.getParameter("markdownText");
    if (Strings.isEmptyOrNull(markdownText)) {
      result.put("html", "");

      return;
    }

    final JSONObject currentUser = userQueryService.getCurrentUser(request);
    if (null == currentUser) {
      response.sendError(HttpServletResponse.SC_FORBIDDEN);

      return;
    }

    final Set<String> userNames = userQueryService.getUserNames(markdownText);
    for (final String userName : userNames) {
      markdownText =
          markdownText.replace(
              '@' + userName,
              "@<a href='"
                  + Latkes.getServePath()
                  + "/member/"
                  + userName
                  + "'>"
                  + userName
                  + "</a>");
    }
    markdownText = shortLinkQueryService.linkArticle(markdownText);
    markdownText = shortLinkQueryService.linkTag(markdownText);
    markdownText = Emotions.convert(markdownText);
    markdownText = Markdowns.toHTML(markdownText);
    markdownText = Markdowns.clean(markdownText, "");

    result.put("html", markdownText);
  }
  /**
   * Processes the specified comment content.
   *
   * <ul>
   *   <li>Generates &#64;username home URL
   *   <li>Markdowns
   *   <li>Blocks comment if need
   *   <li>Generates emotion images
   *   <li>Generates article link with article id
   * </ul>
   *
   * @param comment the specified comment, for example,
   *     <pre>
   * {
   *     "commentContent": "",
   *     ....,
   *     "commenter": {}
   * }
   * </pre>
   */
  private void processCommentContent(final JSONObject comment) {
    final JSONObject commenter = comment.optJSONObject(Comment.COMMENT_T_COMMENTER);

    if (Comment.COMMENT_STATUS_C_INVALID == comment.optInt(Comment.COMMENT_STATUS)
        || UserExt.USER_STATUS_C_INVALID == commenter.optInt(UserExt.USER_STATUS)) {
      comment.put(Comment.COMMENT_CONTENT, langPropsService.get("commentContentBlockLabel"));

      return;
    }

    genCommentContentUserName(comment);

    String commentContent = comment.optString(Comment.COMMENT_CONTENT);

    commentContent = shortLinkQueryService.linkArticle(commentContent);
    commentContent = shortLinkQueryService.linkTag(commentContent);
    commentContent = Emotions.convert(commentContent);
    commentContent = Markdowns.toHTML(commentContent);
    commentContent = Markdowns.clean(commentContent, "");

    comment.put(Comment.COMMENT_CONTENT, commentContent);
  }
  /**
   * Gets article preview content.
   *
   * <p>Renders the response with a json object, for example,
   *
   * <pre>
   * {
   *     "html": ""
   * }
   * </pre>
   *
   * @param request the specified http servlet request
   * @param response the specified http servlet response
   * @param context the specified http request context
   * @param articleId the specified article id
   * @throws Exception exception
   */
  @RequestProcessing(value = "/article/{articleId}/preview", method = HTTPRequestMethod.GET)
  @Before(adviceClass = StopwatchStartAdvice.class)
  @After(adviceClass = StopwatchEndAdvice.class)
  public void getArticlePreviewContent(
      final HttpServletRequest request,
      final HttpServletResponse response,
      final HTTPRequestContext context,
      final String articleId)
      throws Exception {
    final JSONRenderer renderer = new JSONRenderer();
    context.setRenderer(renderer);
    final JSONObject result = Results.trueResult();
    renderer.setJSONObject(result);

    result.put("html", "");

    final JSONObject article = articleQueryService.getArticle(articleId);
    if (null == article) {
      result.put(Keys.STATUS_CODE, false);

      return;
    }

    final int length = Integer.valueOf("150");
    String content = article.optString(Article.ARTICLE_CONTENT);
    final String authorId = article.optString(Article.ARTICLE_AUTHOR_ID);
    final JSONObject author = userQueryService.getUser(authorId);

    if (null != author && UserExt.USER_STATUS_C_INVALID == author.optInt(UserExt.USER_STATUS)
        || Article.ARTICLE_STATUS_C_INVALID == article.optInt(Article.ARTICLE_STATUS)) {
      result.put("html", langPropsService.get("articleContentBlockLabel"));

      return;
    }

    final Set<String> userNames = userQueryService.getUserNames(content);
    final JSONObject currentUser = userQueryService.getCurrentUser(request);
    final String currentUserName = null == currentUser ? "" : currentUser.optString(User.USER_NAME);
    final String authorName = author.optString(User.USER_NAME);
    if (Article.ARTICLE_TYPE_C_DISCUSSION == article.optInt(Article.ARTICLE_TYPE)
        && !authorName.equals(currentUserName)) {
      boolean invited = false;
      for (final String userName : userNames) {
        if (userName.equals(currentUserName)) {
          invited = true;

          break;
        }
      }

      if (!invited) {
        String blockContent = langPropsService.get("articleDiscussionLabel");
        blockContent =
            blockContent.replace(
                "{user}",
                "<a href='"
                    + Latkes.getServePath()
                    + "/member/"
                    + authorName
                    + "'>"
                    + authorName
                    + "</a>");

        result.put("html", blockContent);

        return;
      }
    }

    content = Emotions.convert(content);
    content = Markdowns.toHTML(content);

    content = Jsoup.clean(content, Whitelist.none());
    if (content.length() >= length) {
      content = StringUtils.substring(content, 0, length) + " ....";
    }

    result.put("html", content);
  }
  /**
   * Gets comments by the specified request json object.
   *
   * @param requestJSONObject the specified request json object, for example,
   *     <pre>
   * {
   *     "paginationCurrentPageNum": 1,
   *     "paginationPageSize": 20,
   *     "paginationWindowSize": 10,
   * }, see {@link Pagination} for more details
   * </pre>
   *
   * @param commentFields the specified article fields to return
   * @return for example,
   *     <pre>
   * {
   *     "pagination": {
   *         "paginationPageCount": 100,
   *         "paginationPageNums": [1, 2, 3, 4, 5]
   *     },
   *     "comments": [{
   *         "oId": "",
   *         "commentContent": "",
   *         "commentCreateTime": "",
   *         ....
   *      }, ....]
   * }
   * </pre>
   *
   * @throws ServiceException service exception
   * @see Pagination
   */
  public JSONObject getComments(
      final JSONObject requestJSONObject, final Map<String, Class<?>> commentFields)
      throws ServiceException {
    final JSONObject ret = new JSONObject();

    final int currentPageNum = requestJSONObject.optInt(Pagination.PAGINATION_CURRENT_PAGE_NUM);
    final int pageSize = requestJSONObject.optInt(Pagination.PAGINATION_PAGE_SIZE);
    final int windowSize = requestJSONObject.optInt(Pagination.PAGINATION_WINDOW_SIZE);
    final Query query =
        new Query()
            .setCurrentPageNum(currentPageNum)
            .setPageSize(pageSize)
            .addSort(Comment.COMMENT_CREATE_TIME, SortDirection.DESCENDING);
    for (final Map.Entry<String, Class<?>> commentField : commentFields.entrySet()) {
      query.addProjection(commentField.getKey(), commentField.getValue());
    }

    JSONObject result = null;

    try {
      result = commentRepository.get(query);
    } catch (final RepositoryException e) {
      LOGGER.log(Level.ERROR, "Gets comments failed", e);

      throw new ServiceException(e);
    }

    final int pageCount =
        result.optJSONObject(Pagination.PAGINATION).optInt(Pagination.PAGINATION_PAGE_COUNT);

    final JSONObject pagination = new JSONObject();
    ret.put(Pagination.PAGINATION, pagination);
    final List<Integer> pageNums =
        Paginator.paginate(currentPageNum, pageSize, pageCount, windowSize);
    pagination.put(Pagination.PAGINATION_PAGE_COUNT, pageCount);
    pagination.put(Pagination.PAGINATION_PAGE_NUMS, pageNums);

    final JSONArray data = result.optJSONArray(Keys.RESULTS);
    final List<JSONObject> comments = CollectionUtils.<JSONObject>jsonArrayToList(data);

    try {
      for (final JSONObject comment : comments) {
        organizeComment(comment);

        final String articleId = comment.optString(Comment.COMMENT_ON_ARTICLE_ID);
        final JSONObject article = articleRepository.get(articleId);

        comment.put(
            Comment.COMMENT_T_ARTICLE_TITLE,
            Article.ARTICLE_STATUS_C_INVALID == article.optInt(Article.ARTICLE_STATUS)
                ? langPropsService.get("articleTitleBlockLabel")
                : Emotions.convert(article.optString(Article.ARTICLE_TITLE)));
        comment.put(
            Comment.COMMENT_T_ARTICLE_PERMALINK, article.optString(Article.ARTICLE_PERMALINK));
      }
    } catch (final RepositoryException e) {
      LOGGER.log(Level.ERROR, "Organizes comments failed", e);

      throw new ServiceException(e);
    }

    ret.put(Comment.COMMENTS, comments);

    return ret;
  }
  /**
   * Gets the user comments with the specified user id, page number and page size.
   *
   * @param userId the specified user id
   * @param currentPageNum the specified page number
   * @param pageSize the specified page size
   * @param viewer the specified viewer, may be {@code null}
   * @return user comments, return an empty list if not found
   * @throws ServiceException service exception
   */
  public List<JSONObject> getUserComments(
      final String userId, final int currentPageNum, final int pageSize, final JSONObject viewer)
      throws ServiceException {
    final Query query =
        new Query()
            .addSort(Comment.COMMENT_CREATE_TIME, SortDirection.DESCENDING)
            .setCurrentPageNum(currentPageNum)
            .setPageSize(pageSize)
            .setFilter(new PropertyFilter(Comment.COMMENT_AUTHOR_ID, FilterOperator.EQUAL, userId));
    try {
      final JSONObject result = commentRepository.get(query);
      final List<JSONObject> ret =
          CollectionUtils.<JSONObject>jsonArrayToList(result.optJSONArray(Keys.RESULTS));

      for (final JSONObject comment : ret) {
        comment.put(
            Comment.COMMENT_CREATE_TIME, new Date(comment.optLong(Comment.COMMENT_CREATE_TIME)));

        final String articleId = comment.optString(Comment.COMMENT_ON_ARTICLE_ID);
        final JSONObject article = articleRepository.get(articleId);

        comment.put(
            Comment.COMMENT_T_ARTICLE_TITLE,
            Article.ARTICLE_STATUS_C_INVALID == article.optInt(Article.ARTICLE_STATUS)
                ? langPropsService.get("articleTitleBlockLabel")
                : Emotions.convert(article.optString(Article.ARTICLE_TITLE)));
        comment.put(Comment.COMMENT_T_ARTICLE_TYPE, article.optInt(Article.ARTICLE_TYPE));
        comment.put(
            Comment.COMMENT_T_ARTICLE_PERMALINK, article.optString(Article.ARTICLE_PERMALINK));

        final JSONObject commenter = userRepository.get(userId);
        comment.put(Comment.COMMENT_T_COMMENTER, commenter);

        final String articleAuthorId = article.optString(Article.ARTICLE_AUTHOR_ID);
        final JSONObject articleAuthor = userRepository.get(articleAuthorId);
        final String articleAuthorName = articleAuthor.optString(User.USER_NAME);
        final String articleAuthorURL = "/member/" + articleAuthor.optString(User.USER_NAME);
        comment.put(Comment.COMMENT_T_ARTICLE_AUTHOR_NAME, articleAuthorName);
        comment.put(Comment.COMMENT_T_ARTICLE_AUTHOR_URL, articleAuthorURL);
        final String articleAuthorEmail = articleAuthor.optString(User.USER_EMAIL);
        final String articleAuthorThumbnailURL =
            avatarQueryService.getAvatarURL(articleAuthorEmail);
        comment.put(Comment.COMMENT_T_ARTICLE_AUTHOR_THUMBNAIL_URL, articleAuthorThumbnailURL);

        if (Article.ARTICLE_TYPE_C_DISCUSSION == article.optInt(Article.ARTICLE_TYPE)) {
          final String msgContent =
              langPropsService
                  .get("articleDiscussionLabel")
                  .replace(
                      "{user}",
                      "<a href='"
                          + Latkes.getServePath()
                          + "/member/"
                          + articleAuthorName
                          + "'>"
                          + articleAuthorName
                          + "</a>");

          if (null == viewer) {
            comment.put(Comment.COMMENT_CONTENT, msgContent);
          } else {
            final String commenterName = commenter.optString(User.USER_NAME);
            final String viewerUserName = viewer.optString(User.USER_NAME);
            final String viewerRole = viewer.optString(User.USER_ROLE);

            if (!commenterName.equals(viewerUserName) && !Role.ADMIN_ROLE.equals(viewerRole)) {
              final String articleContent = article.optString(Article.ARTICLE_CONTENT);
              final Set<String> userNames = userQueryService.getUserNames(articleContent);

              boolean invited = false;
              for (final String userName : userNames) {
                if (userName.equals(viewerUserName)) {
                  invited = true;

                  break;
                }
              }

              if (!invited) {
                comment.put(Comment.COMMENT_CONTENT, msgContent);
              }
            }
          }
        }

        processCommentContent(comment);
      }

      return ret;
    } catch (final RepositoryException e) {
      LOGGER.log(Level.ERROR, "Gets user comments failed", e);
      throw new ServiceException(e);
    }
  }
Beispiel #7
0
  @Override
  public void action(final Event<JSONObject> event) throws EventException {
    final JSONObject data = event.getData();
    LOGGER.log(
        Level.DEBUG,
        "Processing an event[type={0}, data={1}] in listener[className={2}]",
        new Object[] {event.getType(), data, CommentNotifier.class.getName()});

    try {
      final JSONObject originalArticle = data.getJSONObject(Article.ARTICLE);
      final JSONObject originalComment = data.getJSONObject(Comment.COMMENT);
      final String commentContent = originalComment.optString(Comment.COMMENT_CONTENT);
      final JSONObject commenter =
          userQueryService.getUser(originalComment.optString(Comment.COMMENT_AUTHOR_ID));
      final String commenterName = commenter.optString(User.USER_NAME);

      // 0. Data channel (WebSocket)
      final JSONObject chData = new JSONObject();
      chData.put(Article.ARTICLE_T_ID, originalArticle.optString(Keys.OBJECT_ID));
      chData.put(Comment.COMMENT_T_ID, originalComment.optString(Keys.OBJECT_ID));
      chData.put(Comment.COMMENT_T_AUTHOR_NAME, commenterName);

      final String userEmail = commenter.optString(User.USER_EMAIL);
      chData.put(
          Comment.COMMENT_T_AUTHOR_THUMBNAIL_URL, avatarQueryService.getAvatarURL(userEmail));
      chData.put(Common.THUMBNAIL_UPDATE_TIME, commenter.optLong(UserExt.USER_UPDATE_TIME));

      chData.put(
          Comment.COMMENT_CREATE_TIME,
          DateFormatUtils.format(
              new Date(originalComment.optLong(Comment.COMMENT_CREATE_TIME)), "yyyy-MM-dd HH:mm"));
      String cc = shortLinkQueryService.linkArticle(commentContent);
      cc = shortLinkQueryService.linkTag(cc);
      cc = Emotions.convert(cc);
      cc = Markdowns.toHTML(cc);
      cc = Markdowns.clean(cc, "");
      try {
        final Set<String> userNames = userQueryService.getUserNames(commentContent);
        for (final String userName : userNames) {
          cc =
              cc.replace(
                  '@' + userName,
                  "@<a href='"
                      + Latkes.getServePath()
                      + "/member/"
                      + userName
                      + "'>"
                      + userName
                      + "</a>");
        }
      } catch (final ServiceException e) {
        LOGGER.log(Level.ERROR, "Generates @username home URL for comment content failed", e);
      }
      chData.put(Comment.COMMENT_CONTENT, cc);

      ArticleChannel.notifyComment(chData);

      // + Article Heat
      final JSONObject articleHeat = new JSONObject();
      articleHeat.put(Article.ARTICLE_T_ID, originalArticle.optString(Keys.OBJECT_ID));
      articleHeat.put(Common.OPERATION, "+");
      ArticleListChannel.notifyHeat(articleHeat);

      final boolean isDiscussion =
          originalArticle.optInt(Article.ARTICLE_TYPE) == Article.ARTICLE_TYPE_C_DISCUSSION;

      // Timeline
      if (!isDiscussion) {
        final String articleTitle =
            StringUtils.substring(
                Jsoup.parse(originalArticle.optString(Article.ARTICLE_TITLE)).text(), 0, 28);
        final String articlePermalink =
            Latkes.getServePath() + originalArticle.optString(Article.ARTICLE_PERMALINK);

        final JSONObject timeline = new JSONObject();
        timeline.put(Common.TYPE, Comment.COMMENT);
        String content = langPropsService.get("timelineCommentLabel");
        content =
            content
                .replace(
                    "{user}",
                    "<a target='_blank' rel='nofollow' href='"
                        + Latkes.getServePath()
                        + "/member/"
                        + commenterName
                        + "'>"
                        + commenterName
                        + "</a>")
                .replace(
                    "{article}",
                    "<a target='_blank' rel='nofollow' href='"
                        + articlePermalink
                        + "'>"
                        + articleTitle
                        + "</a>")
                .replace("{comment}", StringUtils.substring(Jsoup.parse(cc).text(), 0, 28));
        timeline.put(Common.CONTENT, content);

        timelineMgmtService.addTimeline(timeline);
      }

      // 1. 'Commented' Notification
      final String articleAuthorId = originalArticle.optString(Article.ARTICLE_AUTHOR_ID);
      final Set<String> atUserNames = userQueryService.getUserNames(commentContent);
      final boolean commenterIsArticleAuthor =
          articleAuthorId.equals(originalComment.optString(Comment.COMMENT_AUTHOR_ID));
      if (commenterIsArticleAuthor && atUserNames.isEmpty()) {
        return;
      }

      atUserNames.remove(commenterName); // Do not notify commenter itself

      if (!commenterIsArticleAuthor) {
        final JSONObject requestJSONObject = new JSONObject();
        requestJSONObject.put(Notification.NOTIFICATION_USER_ID, articleAuthorId);
        requestJSONObject.put(
            Notification.NOTIFICATION_DATA_ID, originalComment.optString(Keys.OBJECT_ID));

        notificationMgmtService.addCommentedNotification(requestJSONObject);
      }

      final String articleContent = originalArticle.optString(Article.ARTICLE_CONTENT);
      final Set<String> articleContentAtUserNames = userQueryService.getUserNames(articleContent);

      // 2. 'At' Notification
      for (final String userName : atUserNames) {
        if (isDiscussion && !articleContentAtUserNames.contains(userName)) {
          continue;
        }

        final JSONObject user = userQueryService.getUserByName(userName);

        if (null == user) {
          LOGGER.log(Level.WARN, "Not found user by name [{0}]", userName);

          continue;
        }

        if (user.optString(Keys.OBJECT_ID).equals(articleAuthorId)) {
          continue; // Has added in step 1
        }

        final JSONObject requestJSONObject = new JSONObject();
        requestJSONObject.put(Notification.NOTIFICATION_USER_ID, user.optString(Keys.OBJECT_ID));
        requestJSONObject.put(
            Notification.NOTIFICATION_DATA_ID, originalComment.optString(Keys.OBJECT_ID));

        notificationMgmtService.addAtNotification(requestJSONObject);
      }
    } catch (final Exception e) {
      LOGGER.log(Level.ERROR, "Sends the comment notification failed", e);
    }
  }