/core/src/main/java/org/b3log/solo/util/Comments.java

http://github.com/b3log/b3log-solo · Java · 346 lines · 183 code · 38 blank · 125 comment · 28 complexity · 224e09858433082644404f5d5227b43d MD5 · raw file

  1. /*
  2. * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.b3log.solo.util;
  17. import java.io.IOException;
  18. import java.net.URL;
  19. import java.util.logging.Level;
  20. import java.util.logging.Logger;
  21. import org.b3log.latke.Keys;
  22. import org.b3log.latke.mail.MailService;
  23. import org.b3log.latke.mail.MailService.Message;
  24. import org.b3log.latke.mail.MailServiceFactory;
  25. import org.b3log.latke.service.LangPropsService;
  26. import org.b3log.latke.util.Strings;
  27. import org.b3log.solo.SoloServletListener;
  28. import org.b3log.solo.model.*;
  29. import org.b3log.solo.repository.ArticleRepository;
  30. import org.b3log.solo.repository.PageRepository;
  31. import org.b3log.solo.repository.impl.ArticleRepositoryImpl;
  32. import org.b3log.solo.repository.impl.PageRepositoryImpl;
  33. import org.b3log.solo.service.PreferenceQueryService;
  34. import org.json.JSONException;
  35. import org.json.JSONObject;
  36. /**
  37. * Comment utilities.
  38. *
  39. * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
  40. * @version 1.0.0.9, Mar 28, 2012
  41. * @since 0.3.1
  42. */
  43. public final class Comments {
  44. /**
  45. * Logger.
  46. */
  47. private static final Logger LOGGER = Logger.getLogger(Comments.class.getName());
  48. /**
  49. * Language service.
  50. */
  51. private static LangPropsService langPropsService = LangPropsService.getInstance();
  52. /**
  53. * Preference query service.
  54. */
  55. private static PreferenceQueryService preferenceQueryService = PreferenceQueryService.getInstance();
  56. /**
  57. * Article repository.
  58. */
  59. private static ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance();
  60. /**
  61. * Page repository.
  62. */
  63. private static PageRepository pageRepository = PageRepositoryImpl.getInstance();
  64. /**
  65. * Mail service.
  66. */
  67. private static final MailService MAIL_SVC = MailServiceFactory.getMailService();
  68. /**
  69. * Minimum length of comment name.
  70. */
  71. private static final int MIN_COMMENT_NAME_LENGTH = 2;
  72. /**
  73. * Maximum length of comment name.
  74. */
  75. private static final int MAX_COMMENT_NAME_LENGTH = 20;
  76. /**
  77. * Minimum length of comment content.
  78. */
  79. private static final int MIN_COMMENT_CONTENT_LENGTH = 2;
  80. /**
  81. * Maximum length of comment content.
  82. */
  83. private static final int MAX_COMMENT_CONTENT_LENGTH = 500;
  84. /**
  85. * Comment mail HTML body.
  86. */
  87. public static final String COMMENT_MAIL_HTML_BODY =
  88. "<p>{articleOrPage} [<a href=\"" + "{articleOrPageURL}\">"
  89. + "{title}</a>]" + " received a new comment:</p>"
  90. + "{commenter}: <span><a href=\"http://{commentSharpURL}\">"
  91. + "{commentContent}</a></span>";
  92. /**
  93. * Gets comment sharp URL with the specified page and comment id.
  94. *
  95. * @param page the specified page
  96. * @param commentId the specified comment id
  97. * @return comment sharp URL
  98. * @throws JSONException json exception
  99. */
  100. public static String getCommentSharpURLForPage(final JSONObject page, final String commentId) throws JSONException {
  101. return page.getString(Page.PAGE_PERMALINK) + "#" + commentId;
  102. }
  103. /**
  104. * Gets comment sharp URL with the specified article and comment id.
  105. *
  106. * @param article the specified article
  107. * @param commentId the specified comment id
  108. * @return comment sharp URL
  109. * @throws JSONException json exception
  110. */
  111. public static String getCommentSharpURLForArticle(final JSONObject article, final String commentId) throws JSONException {
  112. final String articleLink = article.getString(Article.ARTICLE_PERMALINK);
  113. return articleLink + "#" + commentId;
  114. }
  115. /**
  116. * Checks the specified comment adding request.
  117. *
  118. * @param requestJSONObject the specified comment adding request, for example,
  119. * <pre>
  120. * {
  121. * "type": "", // "article"/"page"
  122. * "oId": "",
  123. * "commentName": "",
  124. * "commentEmail": "",
  125. * "commentURL": "",
  126. * "commentContent": "",
  127. * }
  128. * </pre>
  129. * @return check result, for example,
  130. * <pre>
  131. * {
  132. * "sc": boolean,
  133. * "msg": "" // Exists if "sc" equals to false
  134. * }
  135. * </pre>
  136. */
  137. public static JSONObject checkAddCommentRequest(final JSONObject requestJSONObject) {
  138. final JSONObject ret = new JSONObject();
  139. try {
  140. ret.put(Keys.STATUS_CODE, false);
  141. final JSONObject preference = preferenceQueryService.getPreference();
  142. if (null == preference || !preference.optBoolean(Preference.COMMENTABLE)) {
  143. ret.put(Keys.MSG, langPropsService.get("notAllowCommentLabel"));
  144. return ret;
  145. }
  146. final String id = requestJSONObject.optString(Keys.OBJECT_ID);
  147. final String type = requestJSONObject.optString(Common.TYPE);
  148. if (Article.ARTICLE.equals(type)) {
  149. final JSONObject article = articleRepository.get(id);
  150. if (null == article || !article.optBoolean(Article.ARTICLE_COMMENTABLE)) {
  151. ret.put(Keys.MSG, langPropsService.get("notAllowCommentLabel"));
  152. return ret;
  153. }
  154. } else {
  155. final JSONObject page = pageRepository.get(id);
  156. if (null == page || !page.optBoolean(Page.PAGE_COMMENTABLE)) {
  157. ret.put(Keys.MSG, langPropsService.get("notAllowCommentLabel"));
  158. return ret;
  159. }
  160. }
  161. final String commentName = requestJSONObject.getString(Comment.COMMENT_NAME);
  162. if (MAX_COMMENT_NAME_LENGTH < commentName.length() || MIN_COMMENT_NAME_LENGTH > commentName.length()) {
  163. LOGGER.log(Level.WARNING, "Comment name is too long[{0}]", commentName);
  164. ret.put(Keys.MSG, langPropsService.get("nameTooLongLabel"));
  165. return ret;
  166. }
  167. final String commentEmail = requestJSONObject.getString(Comment.COMMENT_EMAIL).trim().toLowerCase();
  168. if (!Strings.isEmail(commentEmail)) {
  169. LOGGER.log(Level.WARNING, "Comment email is invalid[{0}]", commentEmail);
  170. ret.put(Keys.MSG, langPropsService.get("mailInvalidLabel"));
  171. return ret;
  172. }
  173. final String commentURL = requestJSONObject.optString(Comment.COMMENT_URL);
  174. try {
  175. new URL(commentURL);
  176. if (commentURL.contains("<") || commentURL.contains(">")) {
  177. throw new IllegalArgumentException();
  178. }
  179. } catch (final Exception e) {
  180. LOGGER.log(Level.WARNING, "Comment URL is invalid[{0}]", commentURL);
  181. ret.put(Keys.MSG, langPropsService.get("urlInvalidLabel"));
  182. return ret;
  183. }
  184. final String commentContent = requestJSONObject.optString(Comment.COMMENT_CONTENT).
  185. replaceAll("\\n", SoloServletListener.ENTER_ESC);
  186. if (MAX_COMMENT_CONTENT_LENGTH < commentContent.length() || MIN_COMMENT_CONTENT_LENGTH > commentContent.length()) {
  187. LOGGER.log(Level.WARNING, "Comment conent length is invalid[{0}]", commentContent.length());
  188. ret.put(Keys.MSG, langPropsService.get("commentContentCannotEmptyLabel"));
  189. return ret;
  190. }
  191. ret.put(Keys.STATUS_CODE, true);
  192. return ret;
  193. } catch (final Exception e) {
  194. LOGGER.log(Level.WARNING, "Checks add comment request[" + requestJSONObject.toString() + "] failed", e);
  195. ret.put(Keys.STATUS_CODE, false);
  196. ret.put(Keys.MSG, langPropsService.get("addFailLabel"));
  197. return ret;
  198. }
  199. }
  200. /**
  201. * Sends a notification mail to administrator for notifying the specified
  202. * article or page received the specified comment and original comment.
  203. *
  204. * @param articleOrPage the specified article or page
  205. * @param comment the specified comment
  206. * @param originalComment original comment, if not exists, set it as
  207. * {@code null}
  208. * @param preference the specified preference
  209. * @throws IOException io exception
  210. * @throws JSONException json exception
  211. */
  212. public static void sendNotificationMail(final JSONObject articleOrPage,
  213. final JSONObject comment,
  214. final JSONObject originalComment,
  215. final JSONObject preference)
  216. throws IOException, JSONException {
  217. final String commentEmail = comment.getString(Comment.COMMENT_EMAIL);
  218. final String commentId = comment.getString(Keys.OBJECT_ID);
  219. final String commentContent = comment.getString(Comment.COMMENT_CONTENT).
  220. replaceAll(SoloServletListener.ENTER_ESC, "<br/>");
  221. final String adminEmail = preference.getString(Preference.ADMIN_EMAIL);
  222. if (adminEmail.equalsIgnoreCase(commentEmail)) {
  223. LOGGER.log(Level.FINER, "Do not send comment notification mail to admin itself[{0}]", adminEmail);
  224. return;
  225. }
  226. if (null != originalComment && comment.has(Comment.COMMENT_ORIGINAL_COMMENT_ID)) {
  227. final String originalEmail = originalComment.getString(Comment.COMMENT_EMAIL);
  228. if (originalEmail.equalsIgnoreCase(adminEmail)) {
  229. LOGGER.log(Level.FINER, "Do not send comment notification mail to admin while the specified comment[{0}] is an reply",
  230. commentId);
  231. return;
  232. }
  233. }
  234. final String blogTitle = preference.getString(Preference.BLOG_TITLE);
  235. final String blogHost = preference.getString(Preference.BLOG_HOST);
  236. boolean isArticle = true;
  237. String title = articleOrPage.optString(Article.ARTICLE_TITLE);
  238. if (Strings.isEmptyOrNull(title)) {
  239. title = articleOrPage.getString(Page.PAGE_TITLE);
  240. isArticle = false;
  241. }
  242. final String commentSharpURL = comment.getString(Comment.COMMENT_SHARP_URL);
  243. final Message message = new Message();
  244. message.setFrom(adminEmail);
  245. message.addRecipient(adminEmail);
  246. String mailSubject = null;
  247. String articleOrPageURL = null;
  248. String mailBody = null;
  249. if (isArticle) {
  250. mailSubject = blogTitle + ": New comment on article [" + title + "]";
  251. articleOrPageURL = "http://" + blogHost + articleOrPage.getString(Article.ARTICLE_PERMALINK);
  252. mailBody = COMMENT_MAIL_HTML_BODY.replace("{articleOrPage}", "Article");
  253. } else {
  254. mailSubject = blogTitle + ": New comment on page [" + title + "]";
  255. articleOrPageURL = "http://" + blogHost + articleOrPage.getString(Page.PAGE_PERMALINK);
  256. mailBody = COMMENT_MAIL_HTML_BODY.replace("{articleOrPage}", "Page");
  257. }
  258. message.setSubject(mailSubject);
  259. final String commentName = comment.getString(Comment.COMMENT_NAME);
  260. final String commentURL = comment.getString(Comment.COMMENT_URL);
  261. String commenter = null;
  262. if (!"http://".equals(commentURL)) {
  263. commenter = "<a target=\"_blank\" " + "href=\"" + commentURL + "\">" + commentName + "</a>";
  264. } else {
  265. commenter = commentName;
  266. }
  267. mailBody = mailBody.replace("{articleOrPageURL}", articleOrPageURL).
  268. replace("{title}", title).
  269. replace("{commentContent}", commentContent).
  270. replace("{commentSharpURL}", blogHost + commentSharpURL).
  271. replace("{commenter}", commenter);
  272. message.setHtmlBody(mailBody);
  273. LOGGER.log(Level.FINER,
  274. "Sending a mail[mailSubject={0}, mailBody=[{1}] to admin[email={2}]",
  275. new Object[]{mailSubject, mailBody, adminEmail});
  276. MAIL_SVC.send(message);
  277. }
  278. /**
  279. * Gets the {@link Comments} singleton.
  280. *
  281. * @return the singleton
  282. */
  283. public static Comments getInstance() {
  284. return SingletonHolder.SINGLETON;
  285. }
  286. /**
  287. * Private default constructor.
  288. */
  289. private Comments() {
  290. }
  291. /**
  292. * Singleton holder.
  293. *
  294. * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
  295. * @version 1.0.0.0, Jan 12, 2011
  296. */
  297. private static final class SingletonHolder {
  298. /**
  299. * Singleton.
  300. */
  301. private static final Comments SINGLETON = new Comments();
  302. /**
  303. * Private default constructor.
  304. */
  305. private SingletonHolder() {
  306. }
  307. }
  308. }