/** * Copyright (C) 2011 JTalks.org Team * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jtalks.jcommune.web.controller; import org.jtalks.common.model.entity.Group; import org.jtalks.common.model.permissions.JtalksPermission; import org.jtalks.common.validation.ValidationError; import org.jtalks.common.validation.ValidationException; import org.jtalks.jcommune.model.dto.*; import org.jtalks.jcommune.model.entity.Branch; import org.jtalks.jcommune.model.entity.ComponentInformation; import org.jtalks.jcommune.plugin.api.exceptions.NotFoundException; import org.jtalks.jcommune.plugin.api.web.dto.json.JsonResponse; import org.jtalks.jcommune.plugin.api.web.dto.json.JsonResponseStatus; import org.jtalks.jcommune.service.BranchService; import org.jtalks.jcommune.service.ComponentService; import org.jtalks.jcommune.service.GroupService; import org.jtalks.jcommune.service.SpamProtectionService; import org.jtalks.jcommune.service.security.PermissionManager; import org.jtalks.jcommune.web.dto.BranchDto; import org.jtalks.jcommune.web.dto.BranchPermissionDto; import org.jtalks.jcommune.web.dto.GroupDto; import org.jtalks.jcommune.web.dto.PermissionGroupsDto; import org.jtalks.jcommune.web.listeners.SessionSetupListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.data.domain.Page; import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Set; /** * @author Andrei Alikov * Controller for processing forum administration related requests * such as setting up Forum title, description, logo and fav icon * Some methods in controller are directly used from test classes, * and some of them are not @SuppressWarnings("unused") is used to * disable IDE warnings about methods without direct access. */ @SuppressWarnings("unused") @Controller public class AdministrationController { /** * Session's marker attribute name for Administration mode */ static final String ADMIN_ATTRIBUTE_NAME = "adminMode"; private static final String ACCESS_DENIED_MESSAGE = "access.denied"; private static final int GROUP_USER_PAGE_SIZE = 20; private final ComponentService componentService; private final GroupService groupService; private final MessageSource messageSource; private final BranchService branchService; private final PermissionManager permissionManager; private final SpamProtectionService spamProtectionService; /** * Creates instance of the service * * @param componentService service to work with the forum component * @param messageSource to resolve locale-dependent messages * @param permissionManager * @param spamProtectionService service to check is email in blacklist */ @Autowired public AdministrationController(ComponentService componentService, MessageSource messageSource, BranchService branchService, PermissionManager permissionManager, GroupService groupService, SpamProtectionService spamProtectionService) { this.messageSource = messageSource; this.componentService = componentService; this.branchService = branchService; this.permissionManager = permissionManager; this.groupService = groupService; this.spamProtectionService = spamProtectionService; } /** * Change mode to Administrator mode in which user can edit * forum parameters - external links, banners, logo, title, etc. * * @param request Client request * @return redirect back to previous page */ @RequestMapping(value = "/admin/enter", method = RequestMethod.GET) public String enterAdministrationMode(HttpServletRequest request) { if (componentService.getComponentOfForum() != null) { checkForAdminPermissions(); } request.getSession().setAttribute(ADMIN_ATTRIBUTE_NAME, true); return getRedirectToPrevPage(request); } /** * Return back from Administrator mode to Normal mode * * @param request Client request * @return redirect back to previous page */ @RequestMapping(value = "/admin/exit", method = RequestMethod.GET) public String exitAdministrationMode(HttpServletRequest request) { request.getSession().removeAttribute(ADMIN_ATTRIBUTE_NAME); return getRedirectToPrevPage(request); } /** * Handler for request of updating Administration information * * @param componentInformation new forum information * @param result form validation result */ @RequestMapping(value = "/admin/edit", method = RequestMethod.POST) @ResponseBody public JsonResponse setForumInformation(@Valid @RequestBody ComponentInformation componentInformation, BindingResult result, Locale locale) { if (result.hasErrors()) { return new JsonResponse(JsonResponseStatus.FAIL, result.getAllErrors()); } componentInformation.setId(componentService.getComponentOfForum().getId()); try { componentService.setComponentInformation(componentInformation); // SessionSetupListener read session timeout property once on application startup and // keeps it in memory, so when property is updated we need to read it again. SessionSetupListener.resetSessionTimeoutProperty(); } catch (AccessDeniedException e) { String errorMessage = messageSource.getMessage(ACCESS_DENIED_MESSAGE, null, locale); return new JsonResponse(JsonResponseStatus.FAIL, errorMessage); } return new JsonResponse(JsonResponseStatus.SUCCESS, null); } /** * Handler for request of updating Administration information * * @param result form validation result */ @RequestMapping(value = "/branch/edit", method = RequestMethod.POST) @ResponseBody public JsonResponse setBranchInformation(@Valid @RequestBody BranchDto branchDto, BindingResult result, Locale locale) throws NotFoundException { if (result.hasErrors()) { return new JsonResponse(JsonResponseStatus.FAIL, result.getAllErrors()); } long forumId = componentService.getComponentOfForum().getId(); try { branchService.changeBranchInfo(forumId, branchDto.getId(), branchDto.getName(), branchDto.getDescription()); } catch (AccessDeniedException e) { String errorMessage = messageSource.getMessage(ACCESS_DENIED_MESSAGE, null, locale); return new JsonResponse(JsonResponseStatus.FAIL, errorMessage); } return new JsonResponse(JsonResponseStatus.SUCCESS, null); } @RequestMapping(value = "/branch/new", method = RequestMethod.POST) @ResponseBody public JsonResponse createNewBranch(@Valid @RequestBody BranchDto branchDto, BindingResult result, Locale locale) throws NotFoundException { if (result.hasErrors()) { return new JsonResponse(JsonResponseStatus.FAIL, result.getAllErrors()); } long forumId = componentService.getComponentOfForum().getId(); try { branchService.createNewBranch(forumId, branchDto.getSectionId(), branchDto.getName(), branchDto.getDescription()); } catch (AccessDeniedException e) { String errorMessage = messageSource.getMessage(ACCESS_DENIED_MESSAGE, null, locale); return new JsonResponse(JsonResponseStatus.FAIL, errorMessage); } return new JsonResponse(JsonResponseStatus.SUCCESS, null); } /** * Displays to user a list of branch permissions. * @param branchId id of the branch * */ @RequestMapping(value = "/branch/permissions/{branchId}", method = RequestMethod.GET) public ModelAndView showBranchPermissions(@PathVariable("branchId") long branchId) throws NotFoundException { long forumId = componentService.getComponentOfForum().getId(); GroupsPermissions permissions = branchService.getPermissionsFor(forumId, branchId); Branch branch = branchService.getBranchOfComponent(forumId, branchId); return new ModelAndView("branchPermissions") .addObject("branch", branch) .addObject("permissions", permissions); } /** * Process permission information request * @param permissionInfo information about permission for which data was requested * @return DTO with two lists for already selected groups and still available groups */ @RequestMapping(value = "/branch/permissions/json", method = RequestMethod.POST) @ResponseBody public JsonResponse getGroupsForBranchPermission(@RequestBody BranchPermissionDto permissionInfo) { long forumId = componentService.getComponentOfForum().getId(); PermissionGroupsDto permission = new PermissionGroupsDto(); JtalksPermission branchPermission = permissionManager.findBranchPermissionByMask(permissionInfo.getPermissionMask()); List<Group> selectedGroups; try { selectedGroups = branchService.getPermissionGroupsFor(forumId, permissionInfo.getBranchId(), permissionInfo.isAllowed(), branchPermission); } catch (NotFoundException e) { return new JsonResponse(JsonResponseStatus.FAIL, null); } List<GroupDto> alreadySelected = GroupDto.convertToGroupDtoList(selectedGroups, GroupDto.BY_NAME_COMPARATOR); List<Group> availableGroups = permissionManager.getAllGroupsWithoutExcluded(selectedGroups, branchPermission); List<GroupDto> available = GroupDto.convertToGroupDtoList(availableGroups, GroupDto.BY_NAME_COMPARATOR); permission.setSelectedGroups(alreadySelected); permission.setAvailableGroups(available); return new JsonResponse(JsonResponseStatus.SUCCESS, permission); } /** * Process change branch permission request * @param permissionInfo information about permission which will be changed * @return "success" or "fail" response status in JSON format */ @RequestMapping(value = "/branch/permissions/edit", method = RequestMethod.POST) @ResponseBody public JsonResponse editBranchPermissions(@RequestBody BranchPermissionDto permissionInfo) { long forumId = componentService.getComponentOfForum().getId(); JtalksPermission branchPermission = permissionManager.findBranchPermissionByMask(permissionInfo.getPermissionMask()); List<Group> newlyAddedGroups = permissionManager.getGroupsByIds(permissionInfo.getNewlyAddedGroupIds()); List<Group> removedGroups = permissionManager.getGroupsByIds(permissionInfo.getRemovedGroupIds()); PermissionChanges changes = new PermissionChanges(branchPermission, newlyAddedGroups, removedGroups); try { branchService.changeBranchPermissions(forumId, permissionInfo.getBranchId(), permissionInfo.isAllowed(),changes); } catch (NotFoundException e) { return new JsonResponse(JsonResponseStatus.FAIL); } return new JsonResponse(JsonResponseStatus.SUCCESS); } /** * Display to user list of groups with count of users. */ @RequestMapping(value = "/group/list", method = RequestMethod.GET) public ModelAndView showGroupsWithUsers() { checkForAdminPermissions(); List<GroupAdministrationDto> groupAdministrationDtos = groupService.getGroupNamesWithCountOfUsers(); return new ModelAndView("groupAdministration").addObject("groups",groupAdministrationDtos); } /** * Register {@link org.jtalks.common.model.entity.Group} from populated in form {@link GroupAdministrationDto}. * * @param groupDto {@link GroupAdministrationDto} populated in form * @return JsonResponse with JsonResponseStatus. SUCCESS if registration successful or FAIL if failed */ @RequestMapping(value = "/group", method = RequestMethod.POST) @ResponseBody public JsonResponse createNewGroup(@Valid @RequestBody GroupAdministrationDto groupDto, BindingResult result, Locale locale) throws org.jtalks.common.service.exceptions.NotFoundException { return saveOrUpdateGroup(groupDto, result, locale); } @RequestMapping(value = "/group/{groupId}", method = RequestMethod.PUT) @ResponseBody public JsonResponse editGroup(@Valid @RequestBody GroupAdministrationDto groupDto, BindingResult result, @PathVariable("groupId") Long groupId, Locale locale) throws org.jtalks.common.service.exceptions.NotFoundException { return saveOrUpdateGroup(groupDto, result, locale); } @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) public ModelAndView getGroupUsers(@PathVariable("groupId") long groupId, @RequestParam(value = "page", defaultValue = "1", required = false) int page) throws org.jtalks.common.service.exceptions.NotFoundException { checkForAdminPermissions(); GroupDto groupDto = new GroupDto(groupService.get(groupId)); PageRequest pageRequest = new PageRequest(page, GROUP_USER_PAGE_SIZE); Page<UserDto> pagedGroupUsers = groupService.getPagedGroupUsers(groupId, pageRequest); return new ModelAndView("groupUserList") .addObject("group", groupDto) .addObject("groupUsersPage", pagedGroupUsers); } @RequestMapping(value = "/group/{groupId}", method = RequestMethod.DELETE) @ResponseBody public JsonResponse deleteGroup(@PathVariable("groupId") Long groupId) throws org.jtalks.common.service.exceptions.NotFoundException { checkForAdminPermissions(); Group group = groupService.get(groupId); groupService.deleteGroup(group); return new JsonResponse(JsonResponseStatus.SUCCESS); } @RequestMapping(value = "/spamprotection", method = RequestMethod.GET) public ModelAndView getSpamProtectionPage(){ checkForAdminPermissions(); List<SpamRuleDto> ruleDtos = SpamRuleDto.fromEntities(spamProtectionService.getAllRules()); return new ModelAndView("spamProtection").addObject("rules", ruleDtos); } /** * Returns redirect string to previous page * * @param request Client HTTP request */ private String getRedirectToPrevPage(HttpServletRequest request) { return "redirect:" + request.getHeader("Referer"); } /** * Check if currently logged user has permissions for administrative * functions for forum */ private void checkForAdminPermissions() { long forumId = componentService.getComponentOfForum().getId(); componentService.checkPermissionsForComponent(forumId); } private JsonResponse saveOrUpdateGroup(@Valid @RequestBody GroupAdministrationDto groupDto, BindingResult result, Locale locale) throws org.jtalks.common.service.exceptions.NotFoundException { checkForAdminPermissions(); if (result.hasFieldErrors() || result.hasGlobalErrors()) { return new JsonResponse(JsonResponseStatus.FAIL, result.getAllErrors()); } try { groupService.saveOrUpdate(groupDto); } catch (ValidationException e) { return new JsonResponse(JsonResponseStatus.FAIL, convertErrors(e.getErrors(), result.getObjectName(), locale)); } return new JsonResponse(JsonResponseStatus.SUCCESS); } private List<ObjectError> convertErrors(Set<ValidationError> errors, String objectName, Locale locale) { List<ObjectError> objectErrors = new ArrayList<>(); for (ValidationError error : errors) { objectErrors.add(new FieldError(objectName, error.getFieldName(), messageSource.getMessage(error.getErrorMessageCode(), null, locale))); } return objectErrors; } }