/** * Returns the topic breadcrumbs. * * @param topic {@link org.jtalks.jcommune.model.entity.Topic} the breadcrumbed topic. * @return the breadcrumb list for the current <code>Topic</code> location. */ public List<Breadcrumb> getForumBreadcrumb(Topic topic) { List<Breadcrumb> breadcrumbList = getForumBreadcrumb(topic.getBranch()); breadcrumbList.add(prepareTopicBreadcrumb(topic.getBranch())); return breadcrumbList; }
/** * {@inheritDoc} */ @Override public <T extends SubscriptionAwareEntity> String getUnsubscribeLinkForSubscribersOf(Class<T> clazz) { if (Branch.class.isAssignableFrom(clazz)) { return getBranch().getUnsubscribeLinkForSubscribersOf(clazz); } else { //In case of post unsubscribe from topic too return String.format("/topics/%s/unsubscribe", getId()); } }
private List<Branch> getBranchesWithViewPermission(Long topicId, List<Branch> branches) { Topic topic = topicDao.get(topicId); List<Branch> result = new ArrayList<>(); for (Branch branch : branches) { if (permissionService.hasBranchPermission(branch.getId(), BranchPermission.VIEW_TOPICS)) { result.add(branch); } } result.remove(topic.getBranch()); return result; }
/** * Delete topic * * @param topicId topic id, this is the topic which contains the first post which should be deleted * @return redirect to branch page * @throws NotFoundException when topic not found */ @RequestMapping(value = "/topics/{topicId}", method = RequestMethod.DELETE) public ModelAndView deleteTopic(@PathVariable(TOPIC_ID) Long topicId) throws NotFoundException { Topic topic = topicFetchService.get(topicId); topicModificationService.deleteTopic(topic); return new ModelAndView("redirect:/branches/" + topic.getBranch().getId()); }
/** * Saves topic after edit topic form submission. * * @param topicDto Dto populated in form * @param result validation result * @param topicId the current topicId * @return {@code ModelAndView} with redirect to saved topic or the same page with validation errors, if any * @throws NotFoundException when topic or branch not found */ @RequestMapping(value = "/topics/{topicId}/edit", method = RequestMethod.POST) public ModelAndView editTopic(@Valid @ModelAttribute TopicDto topicDto, BindingResult result, @PathVariable(TOPIC_ID) Long topicId) throws NotFoundException { Topic topic = topicFetchService.get(topicId); if (result.hasErrors()) { return new ModelAndView(TOPIC_VIEW) .addObject(BRANCH_ID, topic.getBranch().getId()) .addObject(TOPIC_DTO, topicDto) .addObject(POLL, topic.getPoll()) .addObject(SUBMIT_URL, "/topics/" + topicId + "/edit") .addObject(BREADCRUMB_LIST, breadcrumbBuilder.getForumBreadcrumb(topic)); } topicDto.fillTopic(topic); topicModificationService.updateTopic(topic, topicDto.getPoll()); return new ModelAndView(REDIRECT_URL + topicId); }
/** * Shows edit topic page with form, populated with fields from topic. * * @param topicId the id of selected Topic * @return {@code ModelAndView} * @throws NotFoundException when topic or branch not found */ @RequestMapping(value = "/topics/{topicId}/edit", method = RequestMethod.GET) public ModelAndView editTopicPage(@PathVariable(TOPIC_ID) Long topicId) throws NotFoundException { Topic topic = topicFetchService.get(topicId); if (topic.isCodeReview()) { throw new AccessDeniedException("Edit page for code review"); } TopicDto topicDto = new TopicDto(topic); return new ModelAndView(TOPIC_VIEW) .addObject(BRANCH_ID, topic.getBranch().getId()) .addObject(TOPIC_DTO, topicDto) .addObject(POLL, topic.getPoll()) .addObject(SUBMIT_URL, "/topics/" + topicId + "/edit") .addObject(BREADCRUMB_LIST, breadcrumbBuilder.getForumBreadcrumb(topic)); }
/** * Notify about new topic creation in the subscribed branch. * * @param topic newly created topic */ public void sendNotificationAboutTopicCreated(Topic topic) { Collection<JCUser> branchSubscribers = subscriptionService.getAllowedSubscribers(topic.getBranch()); this.filterSubscribers(branchSubscribers, topic); for (JCUser subscriber : branchSubscribers) { mailService.sendTopicCreationMail(subscriber, topic); } } }
/** * Send notification to subscribers about removing topic or code review. * * @param topic Current topic */ public void sendNotificationAboutRemovingTopic(Topic topic) { subscribedEntityChanged(topic.getBranch()); Collection<JCUser> subscribers = subscriptionService.getAllowedSubscribers(topic); Collection<JCUser> branchSubscribers = subscriptionService.getAllowedSubscribers(topic.getBranch()); subscribers.removeAll(branchSubscribers); String curUser = userService.getCurrentUser().getUsername(); this.filterSubscribers(subscribers, topic); for (JCUser subscriber : subscribers) { mailService.sendRemovingTopicMail(subscriber, topic, curUser); } }
/** * Send email about new topic in the subscribed branch. * * @param subscriber recipient * @param topic newly created topic */ void sendTopicCreationMail(JCUser subscriber, Topic topic) { try { String urlSuffix = getTopicUrlSuffix(topic); String url = this.getDeploymentRootUrl() + urlSuffix; Locale locale = subscriber.getLanguage().getLocale(); Map<String, Object> model = new HashMap<>(); model.put(LINK, url); model.put(LINK_UNSUBSCRIBE, this.getDeploymentRootUrl() + topic.getBranch().getUnsubscribeLinkForSubscribersOf(Branch.class)); model.put(LINK_LABEL, getDeploymentRootUrlWithoutPort() + urlSuffix); sendEmailOnForumUpdates(subscriber, model, locale, topic.getBranch(), "subscriptionNotification.subject", "branchSubscriptionNotification.vm"); } catch (MailingFailedException e) { LOGGER.error("Failed to sent mail about creation topic for user: " + subscriber.getUsername()); } }
/** * Checks if the current topic is closed for posting. * Some users, however, can add posts even to the closed branches. These * users are granted with BranchPermission.CLOSE_TOPICS permission. * * @param topic topic to be checked for if posting is allowed */ private void assertPostingIsAllowed(Topic topic) { Authentication auth = securityContextFacade.getContext().getAuthentication(); if (topic.isClosed() && !permissionEvaluator.hasPermission( auth, topic.getBranch().getId(), "BRANCH", "BranchPermission.CLOSE_TOPICS")) { // holy shit... throw new AccessDeniedException("Posting is forbidden for closed topics"); } }
final long branchId = topic.getBranch().getId(); Post newbie = retryTemplate.execute(new RetryCallback<Post, NotFoundException>() { @Override
Topic topic = topicFetchService.get(topicId); topicFetchService.checkViewTopicPermission(topic.getBranch().getId()); Page<Post> postsPage = postService.getPosts(topic, page);
/** * Notifies topic starter by email that his or her topic * was moved to another sections and also notifies all branch * subscribers * * @param topic topic moved */ public void sendNotificationAboutTopicMoved(Topic topic) { String curUser = userService.getCurrentUser().getUsername(); //send notification to topic subscribers Collection<JCUser> topicSubscribers = subscriptionService.getAllowedSubscribers(topic); this.filterSubscribers(topicSubscribers, topic); for (JCUser subscriber : topicSubscribers) { mailService.sendTopicMovedMail(subscriber, topic, curUser, Topic.class); } //send notification to branch subscribers Collection<JCUser> branchSubscribers = subscriptionService.getAllowedSubscribers(topic.getBranch()); branchSubscribers.removeAll(topicSubscribers); this.filterSubscribers(branchSubscribers, topic.getBranch()); for (JCUser subscriber : branchSubscribers) { if (!topicSubscribers.contains(subscriber)) { mailService.sendTopicMovedMail(subscriber, topic, curUser, Branch.class); } } }
/** * {@inheritDoc} */ @Override public List<Section> getAllAvailableSections(long currentTopicId) { List<Section> result = new ArrayList<>(); Topic topic = topicDao.get(currentTopicId); List<Section> sections = this.getDao().getAll(); JCUser user = userService.getCurrentUser(); for (Section section : sections) { List<Branch> branches = new ArrayList<>(section.getBranches()); for (Branch branch : branches) { if (branch.equals(topic.getBranch())) { branches.remove(branch); break; } } if (getDao().getCountAvailableBranches(user, branches) > 0) { result.add(section); } } return result; }
/** * Checks if current user can create comments in specified topic * * @param topic topic to check permission * @throws AccessDeniedException if user can't create comments in specified topic */ private void assertCommentAllowed(Topic topic) { if (topic.isCodeReview()) { permissionService.checkPermission(topic.getBranch().getId(), AclClassName.BRANCH, BranchPermission.LEAVE_COMMENTS_IN_CODE_REVIEW); } else if (topic.isPlugable()) { assertCommentsAllowedForPlugableTopic(topic); } else { throw new AccessDeniedException("Adding comments not allowed for core topic types"); } if (topic.isClosed()) { permissionService.checkPermission(topic.getBranch().getId(), AclClassName.BRANCH, BranchPermission.CLOSE_TOPICS); } }
/** * {@inheritDoc} */ @PreAuthorize("hasPermission(#topic.branch.id, 'BRANCH', 'BranchPermission.MOVE_TOPICS')") @Override public void moveTopic(Topic topic, Long branchId) throws NotFoundException { Branch sourceBranch = topic.getBranch(); Branch targetBranch = branchDao.get(branchId); if (targetBranch == null) { throw new NotFoundException("Target branch not exist"); } targetBranch.addTopic(topic); branchDao.saveOrUpdate(targetBranch); List<Post> topicPosts = topic.getPosts(); if (topicPosts.contains(sourceBranch.getLastPost())) { branchLastPostService.refreshLastPostInBranch(sourceBranch); } branchLastPostService.refreshLastPostInBranch(targetBranch); notificationService.sendNotificationAboutTopicMoved(topic); logger.info("Moved topic \"{}\". Topic id: {}", topic.getTitle(), topic.getId()); }
/** * Checks if current user can create comments in specified plugable topic * * @param topic plugable topic to check permission * @throws AccessDeniedException if user not granted to create comments in specified topic type or if type of * current topic is unknown */ private void assertCommentsAllowedForPlugableTopic(Topic topic) { List<Plugin> topicPlugins = pluginLoader.getPlugins(new TypeFilter(TopicPlugin.class), new StateFilter(Plugin.State.ENABLED)); boolean pluginFound = false; for (Plugin plugin : topicPlugins) { TopicPlugin topicPlugin = (TopicPlugin)plugin; if (topicPlugin.getTopicType().equals(topic.getType())) { pluginFound = true; permissionService.checkPermission(topic.getBranch().getId(), AclClassName.BRANCH, topicPlugin.getCommentPermission()); break; } } if (!pluginFound) { throw new AccessDeniedException("Creation of comments not allowed for unknown topic type"); } } }
/** * Checks for plugable topic if current user is granted to create topics with type. * * @param topic topic to be checked * @throws AccessDeniedException if user not granted to create current topic type * or if type of current topic is unknown */ private void assertCreationAllowedForPlugableTopic(Topic topic) { if (topic.isPlugable()) { Authentication auth = securityContextFacade.getContext().getAuthentication(); List<Plugin> topicPlugins = pluginLoader.getPlugins(new TypeFilter(TopicPlugin.class), new StateFilter(Plugin.State.ENABLED)); boolean pluginFound = false; for (Plugin plugin : topicPlugins) { TopicPlugin topicPlugin = (TopicPlugin)plugin; if (topicPlugin.getTopicType().equals(topic.getType())) { pluginFound = true; if (!permissionEvaluator.hasPermission(auth, topic.getBranch().getId(), "BRANCH", topicPlugin.getCreateTopicPermission())) { throw new AccessDeniedException("Creating of topic with type " + topic.getType() + " is forbidden"); } } } if (!pluginFound) { throw new AccessDeniedException("Creating of unknown topic type is forbidden"); } } }
/** * Performs actual topic deletion. Deletes all topic related data and * recalculates user's post count. * * @param topic topic to delete * @return branch without deleted topic */ private Branch deleteTopicSilent(Topic topic) { List<Post> topicPosts = topic.getPosts(); for (Post post : topicPosts) { JCUser user = post.getUserCreated(); user.setPostCount(user.getPostCount() - 1); } Branch branch = topic.getBranch(); Post lastPostInBranch = branch.getLastPost(); boolean branchLastPostFromDeletedTopic = topicPosts.contains(lastPostInBranch); if (branchLastPostFromDeletedTopic) { branch.clearLastPost(); } branch.deleteTopic(topic); branchDao.saveOrUpdate(branch); if (branchLastPostFromDeletedTopic) { branchLastPostService.refreshLastPostInBranch(branch); } securityService.deleteFromAcl(Topic.class, topic.getId()); return branch; }
/** * {@inheritDoc} */ @Override @PreAuthorize("hasPermission(#branchId, 'BRANCH', 'BranchPermission.CREATE_POSTS')") public Post replyToTopic(long topicId, String answerBody, long branchId) throws NotFoundException { Topic topic = topicFetchService.getTopicSilently(topicId); this.assertPostingIsAllowed(topic); JCUser currentUser = userService.getCurrentUser(); currentUser.setPostCount(currentUser.getPostCount() + 1); Post answer = new Post(currentUser, answerBody); topic.addPost(answer); topic.removeDraftOfUser(currentUser); if (currentUser.isAutosubscribe()){ subscriptionService.subscribe(topic); } postDao.saveOrUpdate(answer); Branch branch = topic.getBranch(); branch.setLastPost(answer); branchDao.saveOrUpdate(branch); dao.saveOrUpdate(topic); securityService.createAclBuilder().grant(GeneralPermission.WRITE).to(currentUser).on(answer).flush(); notificationService.subscribedEntityChanged(topic); userService.notifyAndMarkNewlyMentionedUsers(answer); logger.debug("New post in topic. Topic id={}, Post id={}, Post author={}", new Object[]{topicId, answer.getId(), currentUser.getUsername()}); return answer; }