/** * 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 @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); } }
@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); } }