diff --git a/nb-configuration.xml b/nb-configuration.xml index 55bcdc233..2c0d94580 100644 --- a/nb-configuration.xml +++ b/nb-configuration.xml @@ -17,6 +17,7 @@ Without this configuration present, some functionality in the IDE may be limited markdowns permalink servlet + Unfollows whitelist diff --git a/src/main/java/org/b3log/symphony/event/ArticleNotifier.java b/src/main/java/org/b3log/symphony/event/ArticleNotifier.java index f14098240..d45331e22 100644 --- a/src/main/java/org/b3log/symphony/event/ArticleNotifier.java +++ b/src/main/java/org/b3log/symphony/event/ArticleNotifier.java @@ -15,6 +15,8 @@ */ package org.b3log.symphony.event; +import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; @@ -22,15 +24,15 @@ import org.b3log.latke.event.AbstractEventListener; import org.b3log.latke.event.Event; import org.b3log.latke.event.EventException; -import org.b3log.latke.ioc.LatkeBeanManager; -import org.b3log.latke.ioc.Lifecycle; import org.b3log.latke.logging.Level; import org.b3log.latke.logging.Logger; import org.b3log.latke.model.User; import org.b3log.latke.urlfetch.URLFetchService; import org.b3log.latke.urlfetch.URLFetchServiceFactory; import org.b3log.symphony.model.Article; +import org.b3log.symphony.model.Follow; import org.b3log.symphony.model.Notification; +import org.b3log.symphony.service.FollowQueryService; import org.b3log.symphony.service.NotificationMgmtService; import org.b3log.symphony.service.UserQueryService; import org.json.JSONObject; @@ -39,7 +41,7 @@ * Sends an article notification to the user who be @username in the article content. * * @author Liang Ding - * @version 1.0.0.3, Sep 6, 2013 + * @version 1.0.0.4, Nov 11, 2013 * @since 0.2.0 */ @Named @@ -61,15 +63,23 @@ public class ArticleNotifier extends AbstractEventListener { @Inject private NotificationMgmtService notificationMgmtService; + /** + * Follow query service. + */ + @Inject + private FollowQueryService followQueryService; + + /** + * User query service. + */ + @Inject + private UserQueryService userQueryService; + @Override public void action(final Event 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, ArticleNotifier.class.getName()}); - - - final LatkeBeanManager beanManager = Lifecycle.getBeanManager(); - final UserQueryService userQueryService = beanManager.getReference(UserQueryService.class); + new Object[]{event.getType(), data, ArticleNotifier.class.getName()}); try { final JSONObject originalArticle = data.getJSONObject(Article.ARTICLE); @@ -86,6 +96,8 @@ public void action(final Event event) throws EventException { return; } + final Set atedUserIds = new HashSet(); + // 'At' Notification for (final String userName : atUserNames) { final JSONObject user = userQueryService.getUserByName(userName); @@ -97,12 +109,30 @@ public void action(final Event event) throws EventException { } final JSONObject requestJSONObject = new JSONObject(); - requestJSONObject.put(Notification.NOTIFICATION_USER_ID, user.optString(Keys.OBJECT_ID)); + final String atedUserId = user.optString(Keys.OBJECT_ID); + requestJSONObject.put(Notification.NOTIFICATION_USER_ID, atedUserId); requestJSONObject.put(Notification.NOTIFICATION_DATA_ID, originalArticle.optString(Keys.OBJECT_ID)); notificationMgmtService.addAtNotification(requestJSONObject); + + atedUserIds.add(atedUserId); } + + // 'Article' Notification + final List followerUsers = followQueryService.getFollowerUsers(articleAuthorId, 1, Integer.MAX_VALUE); + for (final JSONObject followerUser : followerUsers) { + final JSONObject requestJSONObject = new JSONObject(); + final String followerUserId = followerUser.optString(Follow.FOLLOWER_ID); + + if (atedUserIds.contains(followerUserId)) { + continue; + } + + requestJSONObject.put(Notification.NOTIFICATION_USER_ID, followerUserId); + requestJSONObject.put(Notification.NOTIFICATION_DATA_ID, originalArticle.optString(Keys.OBJECT_ID)); + notificationMgmtService.addFollowingUserNotification(requestJSONObject); + } // final Set qqSet = new HashSet(); // for (final String userName : atUserNames) { @@ -116,7 +146,6 @@ public void action(final Event event) throws EventException { // if (qqSet.isEmpty()) { // return; // } - // /* // * { // * "key": "", @@ -153,7 +182,7 @@ public void action(final Event event) throws EventException { /** * Gets the event type {@linkplain EventTypes#ADD_ARTICLE}. - * + * * @return event type */ @Override diff --git a/src/main/java/org/b3log/symphony/model/Common.java b/src/main/java/org/b3log/symphony/model/Common.java index 56dfaaa5b..bd0fde8c6 100644 --- a/src/main/java/org/b3log/symphony/model/Common.java +++ b/src/main/java/org/b3log/symphony/model/Common.java @@ -19,7 +19,7 @@ * This class defines all common model relevant keys. * * @author Liang Ding - * @version 1.0.1.8, Oct 11, 2013 + * @version 1.0.1.9, Nov 11, 2013 * @since 0.2.0 */ public final class Common { @@ -103,6 +103,11 @@ public final class Common { * Key of latest comment articles. */ public static final String LATEST_CMT_ARTICLES = "latestCmtArticles"; + + /** + * Key of user id. + */ + public static final String USER_ID = "userId"; /** * Key of user home articles. @@ -134,6 +139,16 @@ public final class Common { */ public static final String UNREAD_AT_NOTIFICATION_CNT = "unreadAtNotificationCnt"; + /** + * Key of 'followingUser' notifications. + */ + public static final String FOLLOWING_USER_NOTIFICATIONS = "followingUserNotifications"; + + /** + * Key of unread 'followingUser' notifications count. + */ + public static final String UNREAD_FOLLOWING_USER_NOTIFICATION_CNT = "unreadFollowingUserNotificationCnt"; + /** * Key of author name. */ diff --git a/src/main/java/org/b3log/symphony/model/Notification.java b/src/main/java/org/b3log/symphony/model/Notification.java index 9cef2abf0..4da5d9688 100644 --- a/src/main/java/org/b3log/symphony/model/Notification.java +++ b/src/main/java/org/b3log/symphony/model/Notification.java @@ -19,7 +19,7 @@ * This class defines all notification model relevant keys. * * @author Liang Ding - * @version 1.0.0.1, Sep 2, 2013 + * @version 1.0.0.2, Nov 11, 2013 * @since 0.2.5 */ public final class Notification { @@ -74,7 +74,12 @@ public final class Notification { * Data type - commented. */ public static final int DATA_TYPE_C_COMMENTED = 3; - + + /** + * Data type - followingUser. + */ + public static final int DATA_TYPE_C_FOLLOWING_USER = 4; + //// Transient //// /** * Key of unread notification count. diff --git a/src/main/java/org/b3log/symphony/processor/FollowProcessor.java b/src/main/java/org/b3log/symphony/processor/FollowProcessor.java new file mode 100644 index 000000000..604d88345 --- /dev/null +++ b/src/main/java/org/b3log/symphony/processor/FollowProcessor.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2009, 2010, 2011, 2012, 2013, B3log Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.b3log.symphony.processor; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.b3log.latke.Keys; +import org.b3log.latke.logging.Logger; +import org.b3log.latke.model.User; +import org.b3log.latke.servlet.HTTPRequestContext; +import org.b3log.latke.servlet.HTTPRequestMethod; +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.servlet.renderer.JSONRenderer; +import org.b3log.latke.util.Requests; +import org.b3log.symphony.model.Follow; +import org.b3log.symphony.processor.advice.LoginCheck; +import org.b3log.symphony.service.FollowMgmtService; +import org.b3log.symphony.util.QueryResults; +import org.json.JSONObject; + +/** + * Follow processor. + * + *
    + *
  • Follows a user (/follow/user), POST
  • + *
  • Unfollows a user (/follow/user), DELETE
  • + *
  • Follows a tag (/follow/tag), POST
  • + *
  • Unfollows a tag (/follow/tag), DELETE
  • + *
+ * + * @author Liang Ding + * @version 1.0.0.0, Nov 11, 2013 + * @since 0.2.5 + */ +@RequestProcessor +public class FollowProcessor { + + /** + * Logger. + */ + private static final Logger LOGGER = Logger.getLogger(FollowProcessor.class.getName()); + + /** + * Follow management service. + */ + @Inject + private FollowMgmtService followMgmtService; + + /** + * Follows a user. + * + *

+ * The request json object: + *

+     * {
+     *   "followingId": ""
+     * }
+     * 
+ *

+ * + * @param context the specified context + * @param request the specified request + * @param response the specified response + * @throws Exception exception + */ + @RequestProcessing(value = "/follow/user", method = HTTPRequestMethod.POST) + @Before(adviceClass = LoginCheck.class) + public void followUser(final HTTPRequestContext context, final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + final JSONRenderer renderer = new JSONRenderer(); + context.setRenderer(renderer); + + final JSONObject ret = QueryResults.falseResult(); + renderer.setJSONObject(ret); + + final JSONObject requestJSONObject = Requests.parseRequestJSONObject(request, context.getResponse()); + final String followingUserId = requestJSONObject.optString(Follow.FOLLOWING_ID); + + final JSONObject currentUser = (JSONObject) request.getAttribute(User.USER); + final String followerUserId = currentUser.optString(Keys.OBJECT_ID); + + followMgmtService.followUser(followerUserId, followingUserId); + } + + /** + * Unfollows a user. + * + *

+ * The request json object: + *

+     * {
+     *   "followingId": ""
+     * }
+     * 
+ *

+ * + * @param context the specified context + * @param request the specified request + * @param response the specified response + * @throws Exception exception + */ + @RequestProcessing(value = "/follow/user", method = HTTPRequestMethod.DELETE) + @Before(adviceClass = LoginCheck.class) + public void unfollowUser(final HTTPRequestContext context, final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + final JSONRenderer renderer = new JSONRenderer(); + context.setRenderer(renderer); + + final JSONObject ret = QueryResults.falseResult(); + renderer.setJSONObject(ret); + + final JSONObject requestJSONObject = Requests.parseRequestJSONObject(request, context.getResponse()); + final String followingUserId = requestJSONObject.optString(Follow.FOLLOWING_ID); + + final JSONObject currentUser = (JSONObject) request.getAttribute(User.USER); + final String followerUserId = currentUser.optString(Keys.OBJECT_ID); + + followMgmtService.removeFollow(followerUserId, followingUserId); + } + + /** + * Follows a tag. + * + *

+ * The request json object: + *

+     * {
+     *   "followingId": ""
+     * }
+     * 
+ *

+ * + * @param context the specified context + * @param request the specified request + * @param response the specified response + * @throws Exception exception + */ + @RequestProcessing(value = "/follow/tag", method = HTTPRequestMethod.POST) + @Before(adviceClass = LoginCheck.class) + public void followTag(final HTTPRequestContext context, final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + final JSONRenderer renderer = new JSONRenderer(); + context.setRenderer(renderer); + + final JSONObject ret = QueryResults.falseResult(); + renderer.setJSONObject(ret); + + final JSONObject requestJSONObject = Requests.parseRequestJSONObject(request, context.getResponse()); + final String followingTagId = requestJSONObject.optString(Follow.FOLLOWING_ID); + + final JSONObject currentUser = (JSONObject) request.getAttribute(User.USER); + final String followerUserId = currentUser.optString(Keys.OBJECT_ID); + + followMgmtService.followTag(followerUserId, followingTagId); + } + + /** + * Unfollows a tag. + * + *

+ * The request json object: + *

+     * {
+     *   "followingId": ""
+     * }
+     * 
+ *

+ * + * @param context the specified context + * @param request the specified request + * @param response the specified response + * @throws Exception exception + */ + @RequestProcessing(value = "/follow/tag", method = HTTPRequestMethod.DELETE) + @Before(adviceClass = LoginCheck.class) + public void unfollowTag(final HTTPRequestContext context, final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + final JSONRenderer renderer = new JSONRenderer(); + context.setRenderer(renderer); + + final JSONObject ret = QueryResults.falseResult(); + renderer.setJSONObject(ret); + + final JSONObject requestJSONObject = Requests.parseRequestJSONObject(request, context.getResponse()); + final String followingTagId = requestJSONObject.optString(Follow.FOLLOWING_ID); + + final JSONObject currentUser = (JSONObject) request.getAttribute(User.USER); + final String followerUserId = currentUser.optString(Keys.OBJECT_ID); + + followMgmtService.removeFollow(followerUserId, followingTagId); + } +} diff --git a/src/main/java/org/b3log/symphony/processor/NotificationProcessor.java b/src/main/java/org/b3log/symphony/processor/NotificationProcessor.java index 7c28f3449..97ff1890a 100644 --- a/src/main/java/org/b3log/symphony/processor/NotificationProcessor.java +++ b/src/main/java/org/b3log/symphony/processor/NotificationProcessor.java @@ -43,13 +43,15 @@ /** * Notification processor. - * - *
    - *
  • Displays comments of an article (/notifications/commented), GET
  • - *
+ * + *
    + *
  • Displays comments of my articles (/notifications/commented), GET
  • + *
  • Displays at me (/notifications/at), GET
  • + *
  • Displays following user's articles (/notifications/following-user), GET
  • + *
* * @author Liang Ding - * @version 1.0.0.0, Sep 1, 2013 + * @version 1.0.0.1, Nov 11, 2013 * @since 0.2.5 */ @RequestProcessor @@ -100,7 +102,7 @@ public class NotificationProcessor { */ @RequestProcessing(value = "/notifications/commented", method = HTTPRequestMethod.GET) public void showCommentedNotifications(final HTTPRequestContext context, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) throws Exception { final JSONObject currentUser = userQueryService.getCurrentUser(request); if (null == currentUser) { response.sendError(HttpServletResponse.SC_FORBIDDEN); @@ -128,19 +130,18 @@ public void showCommentedNotifications(final HTTPRequestContext context, final H final JSONObject result = notificationQueryService.getCommentedNotifications(userId, pageNum, pageSize); @SuppressWarnings("unchecked") final List commentedNotifications = (List) result.get(Keys.RESULTS); - dataModel.put(Common.COMMENTED_NOTIFICATIONS, commentedNotifications); - - final int unreadCommentedNotificationCnt = - notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_COMMENTED); + + final int unreadCommentedNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_COMMENTED); dataModel.put(Common.UNREAD_COMMENTED_NOTIFICATION_CNT, unreadCommentedNotificationCnt); - final int unreadAtNotificationCnt = - notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_AT); + final int unreadAtNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_AT); dataModel.put(Common.UNREAD_AT_NOTIFICATION_CNT, unreadAtNotificationCnt); - final int unreadCommentNotificationCnt = - notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_COMMENT); - final int unreadArticleNotificationCnt = - notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_ARTICLE); + + final int unreadFollowingUserNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_FOLLOWING_USER); + dataModel.put(Common.UNREAD_FOLLOWING_USER_NOTIFICATION_CNT, unreadFollowingUserNotificationCnt); notificationMgmtService.makeRead(commentedNotifications); @@ -171,7 +172,7 @@ public void showCommentedNotifications(final HTTPRequestContext context, final H */ @RequestProcessing(value = "/notifications/at", method = HTTPRequestMethod.GET) public void showAtNotifications(final HTTPRequestContext context, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) throws Exception { final JSONObject currentUser = userQueryService.getCurrentUser(request); if (null == currentUser) { response.sendError(HttpServletResponse.SC_FORBIDDEN); @@ -202,16 +203,16 @@ public void showAtNotifications(final HTTPRequestContext context, final HttpServ dataModel.put(Common.AT_NOTIFICATIONS, atNotifications); - final int unreadCommentedNotificationCnt = - notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_COMMENTED); + final int unreadCommentedNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_COMMENTED); dataModel.put(Common.UNREAD_COMMENTED_NOTIFICATION_CNT, unreadCommentedNotificationCnt); - final int unreadAtNotificationCnt = - notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_AT); + final int unreadAtNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_AT); dataModel.put(Common.UNREAD_AT_NOTIFICATION_CNT, unreadAtNotificationCnt); - final int unreadCommentNotificationCnt = - notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_COMMENT); - final int unreadArticleNotificationCnt = - notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_ARTICLE); + + final int unreadFollowingUserNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_FOLLOWING_USER); + dataModel.put(Common.UNREAD_FOLLOWING_USER_NOTIFICATION_CNT, unreadFollowingUserNotificationCnt); notificationMgmtService.makeRead(atNotifications); @@ -228,6 +229,78 @@ public void showAtNotifications(final HTTPRequestContext context, final HttpServ dataModel.put(Pagination.PAGINATION_PAGE_COUNT, pageCount); dataModel.put(Pagination.PAGINATION_PAGE_NUMS, pageNums); + filler.fillHeader(request, response, dataModel); + filler.fillFooter(dataModel); + } + + /** + * Shows [followingUser] notifications. + * + * @param context the specified context + * @param request the specified request + * @param response the specified response + * @throws Exception exception + */ + @RequestProcessing(value = "/notifications/following-user", method = HTTPRequestMethod.GET) + public void showFollowingUserNotifications(final HTTPRequestContext context, final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + final JSONObject currentUser = userQueryService.getCurrentUser(request); + if (null == currentUser) { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + + return; + } + + final AbstractFreeMarkerRenderer renderer = new FreeMarkerRenderer(); + context.setRenderer(renderer); + renderer.setTemplateName("/home/notifications/following-user.ftl"); + final Map dataModel = renderer.getDataModel(); + + final String userId = currentUser.optString(Keys.OBJECT_ID); + + String pageNumStr = request.getParameter("p"); + if (Strings.isEmptyOrNull(pageNumStr) || !Strings.isNumeric(pageNumStr)) { + pageNumStr = "1"; + } + + final int pageNum = Integer.valueOf(pageNumStr); + + final int pageSize = Symphonys.getInt("followingUserNotificationsCnt"); + final int windowSize = Symphonys.getInt("followingUserNotificationsWindowSize"); + + // TODO: notificationQueryService.getFollowingUserNotifications + final JSONObject result = notificationQueryService.getAtNotifications(userId, pageNum, pageSize); + @SuppressWarnings("unchecked") + final List followingUserNotifications = (List) result.get(Keys.RESULTS); + + dataModel.put(Common.FOLLOWING_USER_NOTIFICATIONS, followingUserNotifications); + + final int unreadCommentedNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_COMMENTED); + dataModel.put(Common.UNREAD_COMMENTED_NOTIFICATION_CNT, unreadCommentedNotificationCnt); + final int unreadAtNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_AT); + dataModel.put(Common.UNREAD_AT_NOTIFICATION_CNT, unreadAtNotificationCnt); + + final int unreadFollowingUserNotificationCnt + = notificationQueryService.getUnreadNotificationCountByType(userId, Notification.DATA_TYPE_C_FOLLOWING_USER); + dataModel.put(Common.UNREAD_FOLLOWING_USER_NOTIFICATION_CNT, unreadFollowingUserNotificationCnt); + + notificationMgmtService.makeRead(followingUserNotifications); + + final int recordCnt = result.getInt(Pagination.PAGINATION_RECORD_COUNT); + final int pageCount = (int) Math.ceil((double) recordCnt / (double) pageSize); + + final List pageNums = Paginator.paginate(pageNum, pageSize, pageCount, windowSize); + if (!pageNums.isEmpty()) { + dataModel.put(Pagination.PAGINATION_FIRST_PAGE_NUM, pageNums.get(0)); + dataModel.put(Pagination.PAGINATION_LAST_PAGE_NUM, pageNums.get(pageNums.size() - 1)); + } + + dataModel.put(Pagination.PAGINATION_CURRENT_PAGE_NUM, pageNum); + dataModel.put(Pagination.PAGINATION_PAGE_COUNT, pageCount); + dataModel.put(Pagination.PAGINATION_PAGE_NUMS, pageNums); + filler.fillHeader(request, response, dataModel); filler.fillFooter(dataModel); } diff --git a/src/main/java/org/b3log/symphony/processor/advice/LoginCheck.java b/src/main/java/org/b3log/symphony/processor/advice/LoginCheck.java new file mode 100644 index 000000000..72a85c502 --- /dev/null +++ b/src/main/java/org/b3log/symphony/processor/advice/LoginCheck.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 2010, 2011, 2012, 2013, B3log Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.b3log.symphony.processor.advice; + +import java.util.Map; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.b3log.latke.Keys; +import org.b3log.latke.logging.Logger; +import org.b3log.latke.logging.Level; +import org.b3log.latke.model.User; +import org.b3log.latke.servlet.HTTPRequestContext; +import org.b3log.latke.servlet.advice.BeforeRequestProcessAdvice; +import org.b3log.latke.servlet.advice.RequestProcessAdviceException; +import org.b3log.symphony.service.UserQueryService; +import org.json.JSONObject; + +/** + * Login check, Gets user from request attribute named "user" if logged in. + * + * @author Liang Ding + * @version 1.0.0.0, Nov 11, 2013 + * @since 0.2.5 + */ +@Named +@Singleton +public class LoginCheck extends BeforeRequestProcessAdvice { + + /** + * Logger. + */ + private static final Logger LOGGER = Logger.getLogger(LoginCheck.class.getName()); + + /** + * User query service. + */ + @Inject + private UserQueryService userQueryService; + + @Override + public void doAdvice(final HTTPRequestContext context, final Map args) throws RequestProcessAdviceException { + final HttpServletRequest request = context.getRequest(); + + final JSONObject exception = new JSONObject(); + exception.put(Keys.MSG, HttpServletResponse.SC_FORBIDDEN); + + try { + final JSONObject currentUser = userQueryService.getCurrentUser(request); + if (null == currentUser) { + throw new RequestProcessAdviceException(exception); + } + + request.setAttribute(User.USER, currentUser); + } catch (final Exception e) { + LOGGER.log(Level.ERROR, "Login check failed", e); + + throw new RequestProcessAdviceException(exception); + } + } +} diff --git a/src/main/java/org/b3log/symphony/service/FollowQueryService.java b/src/main/java/org/b3log/symphony/service/FollowQueryService.java index bc4194160..a077d78a3 100644 --- a/src/main/java/org/b3log/symphony/service/FollowQueryService.java +++ b/src/main/java/org/b3log/symphony/service/FollowQueryService.java @@ -41,7 +41,7 @@ * Follow query service. * * @author Liang Ding - * @version 1.0.0.0, Aug 28, 2013 + * @version 1.0.0.1, Nov 11, 2013 * @since 0.2.5 */ @Service @@ -60,7 +60,7 @@ public class FollowQueryService { /** * Gets following tags of the specified follower. - * + * * @param followerId the specified follower id * @param currentPageNum the specified page number * @param pageSize the specified page size @@ -80,7 +80,7 @@ public List getFollowingTags(final String followerId, final int curr /** * Gets following users of the specified follower. - * + * * @param followerId the specified follower id * @param currentPageNum the specified page number * @param pageSize the specified page size @@ -99,14 +99,34 @@ public List getFollowingUsers(final String followerId, final int cur } /** - * Gets the followings of a follower specified by the given follower id and follow type. - * + * Gets follower users of the specified following user. + * + * @param followingUserId the specified following user id + * @param currentPageNum the specified page number + * @param pageSize the specified page size + * @return follower users, returns an empty list if not found + * @throws ServiceException service exception + */ + public List getFollowerUsers(final String followingUserId, final int currentPageNum, final int pageSize) + throws ServiceException { + try { + return getFollowers(followingUserId, pageSize, currentPageNum, Follow.FOLLOWING_TYPE_C_USER); + } catch (final RepositoryException e) { + LOGGER.log(Level.ERROR, "Gets follower users of following user[id=" + followingUserId + "] failed", e); + + return Collections.emptyList(); + } + } + + /** + * Gets the followings of a follower specified by the given follower id and following type. + * * @param followerId the given follower id * @param followingType the specified following type * @param currentPageNum the specified current page number * @param pageSize the specified page size * @return followings, returns an empty list if not found - * @throws RepositoryException + * @throws RepositoryException repository exception */ private List getFollowings(final String followerId, final int followingType, final int currentPageNum, final int pageSize) throws RepositoryException { @@ -122,4 +142,29 @@ private List getFollowings(final String followerId, final int follow return CollectionUtils.jsonArrayToList(result.optJSONArray(Keys.RESULTS)); } + + /** + * Gets the followers of a following specified by the given following id and follow type. + * + * @param followingId the given following id + * @param followingType the specified following type + * @param currentPageNum the specified current page number + * @param pageSize the specified page size + * @return followers, returns an empty list if not found + * @throws RepositoryException repository exception + */ + private List getFollowers(final String followingId, final int followingType, final int currentPageNum, final int pageSize) + throws RepositoryException { + final List filters = new ArrayList(); + filters.add(new PropertyFilter(Follow.FOLLOWING_ID, FilterOperator.EQUAL, followingId)); + filters.add(new PropertyFilter(Follow.FOLLOWING_TYPE, FilterOperator.EQUAL, followingType)); + + final Query query = new Query().addSort(Keys.OBJECT_ID, SortDirection.DESCENDING). + setFilter(new CompositeFilter(CompositeFilterOperator.AND, filters)) + .setPageSize(pageSize).setCurrentPageNum(currentPageNum); + + final JSONObject result = followRepository.get(query); + + return CollectionUtils.jsonArrayToList(result.optJSONArray(Keys.RESULTS)); + } } diff --git a/src/main/java/org/b3log/symphony/service/NotificationMgmtService.java b/src/main/java/org/b3log/symphony/service/NotificationMgmtService.java index 50e865732..e0d0f2b79 100644 --- a/src/main/java/org/b3log/symphony/service/NotificationMgmtService.java +++ b/src/main/java/org/b3log/symphony/service/NotificationMgmtService.java @@ -168,6 +168,32 @@ public void addArticleNotification(final JSONObject requestJSONObject) throws Se throw new ServiceException(msg); } } + + /** + * Adds a 'followingUser' type notification with the specified request json object. + * + * @param requestJSONObject the specified request json object, for example, + *
+     * {
+     *     "userId"; "",
+     *     "dataId": ""
+     * }
+     * 
+ * @throws ServiceException + */ + @Transactional + public void addFollowingUserNotification(final JSONObject requestJSONObject) throws ServiceException { + try { + requestJSONObject.put(Notification.NOTIFICATION_DATA_TYPE, Notification.DATA_TYPE_C_FOLLOWING_USER); + + addNotification(requestJSONObject); + } catch (final RepositoryException e) { + final String msg = "Adds notification [type=followingUser] failed"; + LOGGER.log(Level.ERROR, msg, e); + + throw new ServiceException(msg); + } + } /** * Adds a 'commented' type notification with the specified request json object. diff --git a/src/main/resources/repository.json b/src/main/resources/repository.json index 48c7f593a..66e57b48c 100644 --- a/src/main/resources/repository.json +++ b/src/main/resources/repository.json @@ -1,6 +1,6 @@ { "description": "Description of repository structures, for generation (DDL: http://en.wikipedia.org/wiki/Data_Definition_Language) of the relational database table and persistence validation.", - "version": "1.0.1.9, Aug 28, 2013", + "version": "1.0.2.0, Nov 11, 2013", "authors": ["Liang Ding"], "since": "0.2.0", "repositories": [ @@ -25,7 +25,7 @@ { "name": "dataType", "type": "int", - "description": "0: article, 1: comment, 2: at, 3: commented" + "description": "0: article, 1: comment, 2: at, 3: commented, 4: followingUseer" }, { "name": "hasRead", diff --git a/src/main/resources/symphony.properties b/src/main/resources/symphony.properties index dd75e53db..a565933df 100644 --- a/src/main/resources/symphony.properties +++ b/src/main/resources/symphony.properties @@ -16,7 +16,7 @@ # # Description: B3log Symphony configurations. -# Version: 1.0.0.12, Oct 11, 2013 +# Version: 1.0.1.0, Nov 11, 2013 # Author: Liang Ding # @@ -47,6 +47,8 @@ atNotificationsCnt=10 atNotificationsWindowSize=10 commentedNotificationsCnt=10 commentedNotificationsWindowSize=10 +followingUserNotificationsCnt=10 +followingUserNotificationsWindowSize=10 ### Article Comment ### articleCommentsPageSize=30 articleCommentsWindowSize=5