org.verwandlung.voj.web.controller.AdministrationController.java Source code

Java tutorial

Introduction

Here is the source code for org.verwandlung.voj.web.controller.AdministrationController.java

Source

/* Verwandlung Online Judge - A cross-platform judge online system
 * Copyright (C) 2018 Haozhe Xie <cshzxie@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *                              _ooOoo_  
 *                             o8888888o  
 *                             88" . "88  
 *                             (| -_- |)  
 *                             O\  =  /O  
 *                          ____/`---'\____  
 *                        .'  \\|     |//  `.  
 *                       /  \\|||  :  |||//  \  
 *                      /  _||||| -:- |||||-  \  
 *                      |   | \\\  -  /// |   |  
 *                      | \_|  ''\---/''  |   |  
 *                      \  .-\__  `-`  ___/-. /  
 *                    ___`. .'  /--.--\  `. . __  
 *                 ."" '<  `.___\_<|>_/___.'  >'"".  
 *                | | :  `- \`.;`\ _ /`;.`/ - ` : | |  
 *                \  \ `-.   \_ __\ /__ _/   .-` /  /  
 *           ======`-.____`-.___\_____/___.-`____.-'======  
 *                              `=---=' 
 *
 *                          HERE BE BUDDHA
 *
 */
package org.verwandlung.voj.web.controller;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSON;
import org.verwandlung.voj.web.exception.ResourceNotFoundException;
import org.verwandlung.voj.web.messenger.ApplicationEventListener;
import org.verwandlung.voj.web.model.Checkpoint;
import org.verwandlung.voj.web.model.Language;
import org.verwandlung.voj.web.model.Option;
import org.verwandlung.voj.web.model.Problem;
import org.verwandlung.voj.web.model.ProblemCategory;
import org.verwandlung.voj.web.model.ProblemTag;
import org.verwandlung.voj.web.model.Submission;
import org.verwandlung.voj.web.model.User;
import org.verwandlung.voj.web.model.UserGroup;
import org.verwandlung.voj.web.service.LanguageService;
import org.verwandlung.voj.web.service.OptionService;
import org.verwandlung.voj.web.service.ProblemService;
import org.verwandlung.voj.web.service.SubmissionService;
import org.verwandlung.voj.web.service.UserService;
import org.verwandlung.voj.web.util.CsrfProtector;
import org.verwandlung.voj.web.util.DateUtils;
import org.verwandlung.voj.web.util.HttpRequestParser;
import org.verwandlung.voj.web.util.SessionListener;

/**
 * ??.
 * 
 * @author Haozhe Xie
 */
@Controller
@RequestMapping(value = "/administration")
public class AdministrationController {
    /**
     * ?.
     * @param request - HttpRequest
     * @param response - HttpResponse
     * @return ????ModelAndView
     */
    @RequestMapping(value = "", method = RequestMethod.GET)
    public ModelAndView indexView(HttpServletRequest request, HttpServletResponse response) {
        ModelAndView view = new ModelAndView("administration/index");
        view.addObject("totalUsers", getTotalUsers());
        view.addObject("newUsersToday", getNumberOfUserRegisteredToday());
        view.addObject("onlineUsers", getOnlineUsers());
        view.addObject("totalProblems", getTotalProblems());
        view.addObject("numberOfCheckpoints", getNumberOfCheckpoints());
        view.addObject("privateProblems", getPrivateProblems());
        view.addObject("submissionsToday", getSubmissionsToday());
        view.addObject("memoryUsage", getCurrentMemoryUsage());
        view.addObject("onlineJudgers", getOnlineJudgers());
        return view;
    }

    /**
     * ?.
     * @return 
     */
    private long getTotalUsers() {
        UserGroup userGroup = userService.getUserGroupUsingSlug("users");
        return userService.getNumberOfUsers(userGroup);
    }

    /**
     * ??.
     * @return ?
     */
    public long getNumberOfUserRegisteredToday() {
        return userService.getNumberOfUserRegisteredToday();
    }

    /**
     * ??.
     * @return ?
     */
    private long getOnlineUsers() {
        return SessionListener.getTotalSessions();
    }

    /**
     * ??.
     * @return ?
     */
    private long getTotalProblems() {
        return problemService.getNumberOfProblems();
    }

    /**
     * ???.
     * @return ??
     */
    private long getPrivateProblems() {
        long numberOfTotalProblems = getTotalProblems();
        long numberOfPublicProblems = problemService.getNumberOfProblemsUsingFilters(null, "", true);
        return numberOfTotalProblems - numberOfPublicProblems;
    }

    /**
     * ??(?).
     * @return ?
     */
    private long getNumberOfCheckpoints() {
        return problemService.getNumberOfCheckpoints();
    }

    /**
     * ????.
     * @return ???
     */
    private long getSubmissionsToday() {
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int date = calendar.get(Calendar.DAY_OF_MONTH);

        calendar.set(year, month, date, 0, 0, 0);
        Date startTime = calendar.getTime();

        calendar.set(year, month, date + 1, 0, 0, 0);
        Date endTime = calendar.getTime();
        return submissionService.getNumberOfSubmissionsUsingDate(startTime, endTime);
    }

    /**
     * ?Web??.
     * @return Web??(MB)
     */
    private long getCurrentMemoryUsage() {
        long totalMemory = Runtime.getRuntime().totalMemory();
        long freeMemory = Runtime.getRuntime().freeMemory();

        return (totalMemory - freeMemory) / 1048576;
    }

    /**
     * ??.
     * ???Consumer?.
     * @return ?
     */
    private long getOnlineJudgers() {
        return eventListener.getOnlineJudgers();
    }

    /**
     * ???.
     * @param period - 
     * @param request - HttpServletRequest
     * @return ??? Map 
     */
    @RequestMapping(value = "/getNumberOfSubmissions.action", method = RequestMethod.GET)
    public @ResponseBody Map<String, Object> getNumberOfSubmissionsAction(
            @RequestParam(value = "period") int period, HttpServletRequest request) {
        Map<String, Object> submissions = new HashMap<>(2, 1);
        Date today = new Date();
        Date previousDate = DateUtils.getPreviousDate(period);
        Map<String, Long> totalSubmissions = submissionService.getNumberOfSubmissionsUsingDate(previousDate, today,
                0, false);
        Map<String, Long> acceptedSubmissions = submissionService.getNumberOfSubmissionsUsingDate(previousDate,
                today, 0, true);

        submissions.put("totalSubmissions", totalSubmissions);
        submissions.put("acceptedSubmissions", acceptedSubmissions);
        return submissions;
    }

    /**
     * ?.
     * @param request - HttpRequest
     * @param response - HttpResponse
     * @return ???ModelAndView
     */
    @RequestMapping(value = "/all-users", method = RequestMethod.GET)
    public ModelAndView allUsersView(
            @RequestParam(value = "userGroup", required = false, defaultValue = "") String userGroupSlug,
            @RequestParam(value = "username", required = false, defaultValue = "") String username,
            @RequestParam(value = "page", required = false, defaultValue = "1") long pageNumber,
            HttpServletRequest request, HttpServletResponse response) {
        final int NUMBER_OF_USERS_PER_PAGE = 100;
        List<UserGroup> userGroups = userService.getUserGroups();
        UserGroup userGroup = userService.getUserGroupUsingSlug(userGroupSlug);
        long totalUsers = userService.getNumberOfUsersUsingUserGroupAndUsername(userGroup, username);
        long offset = (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_USERS_PER_PAGE;
        List<User> users = userService.getUserUsingUserGroupAndUsername(userGroup, username, offset,
                NUMBER_OF_USERS_PER_PAGE);

        ModelAndView view = new ModelAndView("administration/all-users");
        view.addObject("userGroups", userGroups);
        view.addObject("selectedUserGroup", userGroupSlug);
        view.addObject("username", username);
        view.addObject("currentPage", pageNumber);
        view.addObject("totalPages", (long) Math.ceil(totalUsers * 1.0 / NUMBER_OF_USERS_PER_PAGE));
        view.addObject("users", users);
        return view;
    }

    /**
     * .
     * @param users - ID?, ?(, )
     * @param request - HttpServletRequest
     * @return ??
     */
    @RequestMapping(value = "/deleteUsers.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Boolean> deleteUsersAction(@RequestParam(value = "users") String users,
            HttpServletRequest request) {
        Map<String, Boolean> result = new HashMap<>(2, 1);
        List<Long> userList = JSON.parseArray(users, Long.class);

        for (Long userId : userList) {
            userService.deleteUser(userId);
        }
        result.put("isSuccessful", true);
        return result;
    }

    /**
     * ??.
     * @param userId - 
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ??ModelAndView
     */
    @RequestMapping(value = "/edit-user/{userId}", method = RequestMethod.GET)
    public ModelAndView editUserView(@PathVariable(value = "userId") long userId, HttpServletRequest request,
            HttpServletResponse response) {
        User user = userService.getUserUsingUid(userId);
        Map<String, Object> userMeta = userService.getUserMetaUsingUid(user);
        if (user == null) {
            throw new ResourceNotFoundException();
        }

        List<UserGroup> userGroups = userService.getUserGroups();
        List<Language> languages = languageService.getAllLanguages();
        ModelAndView view = new ModelAndView("administration/edit-user");
        view.addObject("user", user);
        view.addAllObjects(userMeta);
        view.addObject("userGroups", userGroups);
        view.addObject("languages", languages);
        return view;
    }

    /**
     * ?.
     * @param uid - .
     * @param password - ?(?MD5)
     * @param email - ??
     * @param userGroupSlug - ??
     * @param preferLanguageSlug - ????
     * @param location - 
     * @param website - 
     * @param socialLinks - ?
     * @param aboutMe - 
     * @param request - HttpServletRequest
     * @return ?Map<String, Boolean>
     */
    @RequestMapping(value = "/editUser.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Boolean> editUserAction(@RequestParam(value = "uid") long uid,
            @RequestParam(value = "password") String password, @RequestParam(value = "email") String email,
            @RequestParam(value = "userGroup") String userGroupSlug,
            @RequestParam(value = "preferLanguage") String preferLanguageSlug,
            @RequestParam(value = "location") String location, @RequestParam(value = "website") String website,
            @RequestParam(value = "socialLinks") String socialLinks,
            @RequestParam(value = "aboutMe") String aboutMe, HttpServletRequest request) {
        User user = userService.getUserUsingUid(uid);
        Map<String, Boolean> result = new HashMap<>(12, 1);
        result.put("isSuccessful", false);
        result.put("isUserExists", false);

        if (user != null) {
            Map<String, Boolean> updateProfileResult = userService.updateProfile(user, password, userGroupSlug,
                    preferLanguageSlug);
            Map<String, Boolean> updateUserMetaResult = userService.updateProfile(user, email, location, website,
                    socialLinks, aboutMe);
            boolean isUpdateProfileSuccessful = updateProfileResult.get("isSuccessful");
            boolean isUpdateUserMetaSuccessful = updateUserMetaResult.get("isSuccessful");

            result.putAll(updateProfileResult);
            result.putAll(updateUserMetaResult);
            result.put("isUserExists", true);
            result.put("isSuccessful", isUpdateProfileSuccessful && isUpdateUserMetaSuccessful);
        }
        return result;
    }

    /**
     * ?.
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ???ModelAndView
     */
    @RequestMapping(value = "/new-user", method = RequestMethod.GET)
    public ModelAndView newUserView(HttpServletRequest request, HttpServletResponse response) {
        List<UserGroup> userGroups = userService.getUserGroups();
        List<Language> languages = languageService.getAllLanguages();
        ModelAndView view = new ModelAndView("administration/new-user");
        view.addObject("userGroups", userGroups);
        view.addObject("languages", languages);
        return view;
    }

    /**
     * .
     * @param username - ??
     * @param password - ?
     * @param email - ??
     * @param userGroupSlug - ??
     * @param preferLanguageSlug - ????
     * @param request - HttpServletRequest
     * @return ?Map<String, Boolean>
     */
    @RequestMapping(value = "/newUser.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Boolean> newUserAction(@RequestParam(value = "username") String username,
            @RequestParam(value = "password") String password, @RequestParam(value = "email") String email,
            @RequestParam(value = "userGroup") String userGroupSlug,
            @RequestParam(value = "preferLanguage") String preferLanguageSlug, HttpServletRequest request) {
        Map<String, Boolean> result = userService.createUser(username, password, email, userGroupSlug,
                preferLanguageSlug);

        if (result.get("isSuccessful")) {
            String ipAddress = HttpRequestParser.getRemoteAddr(request);
            LOGGER.info(String.format("User: [Username=%s] was created by administrator at %s.",
                    new Object[] { username, ipAddress }));
        }
        return result;
    }

    /**
     * ?.
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ?????ModelAndView
     */
    @RequestMapping(value = "/all-problems", method = RequestMethod.GET)
    public ModelAndView allProblemsView(
            @RequestParam(value = "keyword", required = false, defaultValue = "") String keyword,
            @RequestParam(value = "problemCategory", required = false, defaultValue = "") String problemCategorySlug,
            @RequestParam(value = "problemTag", required = false, defaultValue = "") String problemTagSlug,
            @RequestParam(value = "page", required = false, defaultValue = "1") long pageNumber,
            HttpServletRequest request, HttpServletResponse response) {
        final int NUMBER_OF_PROBLEMS_PER_PAGE = 100;
        List<ProblemCategory> problemCategories = problemService.getProblemCategories();
        long totalProblems = problemService.getNumberOfProblemsUsingFilters(keyword, problemCategorySlug, false);

        long offset = (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_PROBLEMS_PER_PAGE;
        long problemIdLowerBound = problemService.getFirstIndexOfProblems() + offset;
        long problemIdUpperBound = problemIdLowerBound + NUMBER_OF_PROBLEMS_PER_PAGE - 1;

        List<Problem> problems = problemService.getProblemsUsingFilters(problemIdLowerBound, keyword,
                problemCategorySlug, problemTagSlug, false, NUMBER_OF_PROBLEMS_PER_PAGE);
        Map<Long, List<ProblemCategory>> problemCategoryRelationships = problemService
                .getProblemCategoriesOfProblems(problemIdLowerBound, problemIdUpperBound);
        Map<Long, List<ProblemTag>> problemTagRelationships = problemService
                .getProblemTagsOfProblems(problemIdLowerBound, problemIdUpperBound);

        ModelAndView view = new ModelAndView("administration/all-problems");
        view.addObject("problemCategories", problemCategories);
        view.addObject("selectedProblemCategory", problemCategorySlug);
        view.addObject("keyword", keyword);
        view.addObject("currentPage", pageNumber);
        view.addObject("totalPages", (long) Math.ceil(totalProblems * 1.0 / NUMBER_OF_PROBLEMS_PER_PAGE));
        view.addObject("problems", problems);
        view.addObject("problemCategoryRelationships", problemCategoryRelationships);
        view.addObject("problemTagRelationships", problemTagRelationships);
        return view;
    }

    /**
     * .
     * @param problems - ID?, ?(, )
     * @param request - HttpServletRequest
     * @return 
     */
    @RequestMapping(value = "/deleteProblems.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Boolean> deleteProblemsAction(
            @RequestParam(value = "problems") String problems, HttpServletRequest request) {
        Map<String, Boolean> result = new HashMap<>(2, 1);
        List<Long> problemList = JSON.parseArray(problems, Long.class);

        for (Long problemId : problemList) {
            problemService.deleteProblem(problemId);

            String ipAddress = HttpRequestParser.getRemoteAddr(request);
            LOGGER.info(String.format("Problem: [ProblemId=%s] was deleted by administrator at %s.",
                    new Object[] { problemId, ipAddress }));
        }
        result.put("isSuccessful", true);
        return result;
    }

    /**
     * ?.
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ???ModelAndView
     */
    @RequestMapping(value = "/new-problem", method = RequestMethod.GET)
    public ModelAndView newProblemView(HttpServletRequest request, HttpServletResponse response) {
        Map<ProblemCategory, List<ProblemCategory>> problemCategories = problemService
                .getProblemCategoriesWithHierarchy();

        ModelAndView view = new ModelAndView("administration/new-problem");
        view.addObject("problemCategories", problemCategories);
        return view;
    }

    /**
     * ?.
     * @param problemName - ??
     * @param timeLimit - ?
     * @param memoryLimit - ??
     * @param description - ??
     * @param hint - ??
     * @param inputFormat - ?
     * @param outputFormat - ?
     * @param inputSample - 
     * @param outputSample - 
     * @param testCases - (JSON ?)
     * @param problemCategories - (JSON ?)
     * @param problemTags - ((JSON ?)
     * @param isPublic - ?
     * @param isExactlyMatch - ??
     * @param request - HttpServletRequest
     * @return ? Map<String, Boolean>
     */
    @RequestMapping(value = "/createProblem.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Object> createProblemAction(
            @RequestParam(value = "problemName") String problemName,
            @RequestParam(value = "timeLimit") String timeLimit,
            @RequestParam(value = "memoryLimit") String memoryLimit,
            @RequestParam(value = "description") String description, @RequestParam(value = "hint") String hint,
            @RequestParam(value = "inputFormat") String inputFormat,
            @RequestParam(value = "outputFormat") String outputFormat,
            @RequestParam(value = "inputSample") String inputSample,
            @RequestParam(value = "outputSample") String outputSample,
            @RequestParam(value = "testCases") String testCases,
            @RequestParam(value = "problemCategories") String problemCategories,
            @RequestParam(value = "problemTags") String problemTags,
            @RequestParam(value = "isPublic") boolean isPublic,
            @RequestParam(value = "isExactlyMatch") boolean isExactlyMatch, HttpServletRequest request) {
        if (timeLimit.isEmpty() || !StringUtils.isNumeric(timeLimit)) {
            timeLimit = "-1";
        }
        if (memoryLimit.isEmpty() || !StringUtils.isNumeric(memoryLimit)) {
            memoryLimit = "-1";
        }
        Map<String, Object> result = problemService.createProblem(problemName, Integer.parseInt(timeLimit),
                Integer.parseInt(memoryLimit), description, hint, inputFormat, outputFormat, inputSample,
                outputSample, testCases, problemCategories, problemTags, isPublic, isExactlyMatch);

        if ((boolean) result.get("isSuccessful")) {
            long problemId = (Long) result.get("problemId");
            String ipAddress = HttpRequestParser.getRemoteAddr(request);

            LOGGER.info(String.format("Problem: [ProblemId=%s] was created by administrator at %s.",
                    new Object[] { problemId, ipAddress }));
        }
        return result;
    }

    /**
     * ?.
     * @param problemId - 
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ?????ModelAndView
     */
    @RequestMapping(value = "/edit-problem/{problemId}", method = RequestMethod.GET)
    public ModelAndView editProblemsView(@PathVariable(value = "problemId") long problemId,
            HttpServletRequest request, HttpServletResponse response) {
        Problem problem = problemService.getProblem(problemId);

        if (problem == null) {
            throw new ResourceNotFoundException();
        }
        List<Checkpoint> checkpoints = problemService.getCheckpointsUsingProblemId(problemId);
        List<ProblemCategory> selectedProblemCategories = problemService
                .getProblemCategoriesUsingProblemId(problemId);
        Map<ProblemCategory, List<ProblemCategory>> problemCategories = problemService
                .getProblemCategoriesWithHierarchy();
        List<ProblemTag> problemTags = problemService.getProblemTagsUsingProblemId(problemId);

        ModelAndView view = new ModelAndView("administration/edit-problem");
        view.addObject("problem", problem);
        view.addObject("checkpoints", checkpoints);
        view.addObject("problemCategories", problemCategories);
        view.addObject("selectedProblemCategories", selectedProblemCategories);
        view.addObject("problemTags", problemTags);
        return view;
    }

    /**
     * ?.
     * @param problemName - ??
     * @param timeLimit - ?
     * @param memoryLimit - ??
     * @param description - ??
     * @param hint - ??
     * @param inputFormat - ?
     * @param outputFormat - ?
     * @param inputSample - 
     * @param outputSample - 
     * @param testCases - (JSON ?)
     * @param problemCategories - (JSON ?)
     * @param problemTags - ((JSON ?)
     * @param isPublic - ?
     * @param isExactlyMatch - ??
     * @param request - HttpServletRequest
     * @return ? Map<String, Boolean>
     */
    @RequestMapping(value = "/editProblem.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Boolean> editProblemAction(@RequestParam(value = "problemId") long problemId,
            @RequestParam(value = "problemName") String problemName,
            @RequestParam(value = "timeLimit") String timeLimit,
            @RequestParam(value = "memoryLimit") String memoryLimit,
            @RequestParam(value = "description") String description, @RequestParam(value = "hint") String hint,
            @RequestParam(value = "inputFormat") String inputFormat,
            @RequestParam(value = "outputFormat") String outputFormat,
            @RequestParam(value = "inputSample") String inputSample,
            @RequestParam(value = "outputSample") String outputSample,
            @RequestParam(value = "testCases") String testCases,
            @RequestParam(value = "problemCategories") String problemCategories,
            @RequestParam(value = "problemTags") String problemTags,
            @RequestParam(value = "isPublic") boolean isPublic,
            @RequestParam(value = "isExactlyMatch") boolean isExactlyMatch, HttpServletRequest request) {
        if (timeLimit.isEmpty() || !StringUtils.isNumeric(timeLimit)) {
            timeLimit = "-1";
        }
        if (memoryLimit.isEmpty() || !StringUtils.isNumeric(memoryLimit)) {
            memoryLimit = "-1";
        }
        Map<String, Boolean> result = problemService.editProblem(problemId, problemName,
                Integer.parseInt(timeLimit), Integer.parseInt(memoryLimit), description, hint, inputFormat,
                outputFormat, inputSample, outputSample, testCases, problemCategories, problemTags, isPublic,
                isExactlyMatch);

        if (result.get("isSuccessful")) {
            String ipAddress = HttpRequestParser.getRemoteAddr(request);

            LOGGER.info(String.format("Problem: [ProblemId=%s] was edited by administrator at %s.",
                    new Object[] { problemId, ipAddress }));
        }
        return result;
    }

    /**
     * ?.
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ???ModelAndView.
     */
    @RequestMapping(value = "/problem-categories", method = RequestMethod.GET)
    public ModelAndView problemCategoriesView(HttpServletRequest request, HttpServletResponse response) {
        List<ProblemCategory> problemCategories = problemService.getProblemCategories();

        ModelAndView view = new ModelAndView("administration/problem-categories");
        view.addObject("problemCategories", problemCategories);
        return view;
    }

    /**
     * .
     * @param problemCategorySlug - ??
     * @param problemCategoryName - ??
     * @param parentProblemCategorySlug - ??
     * @param request - HttpServletRequest
     * @return ?Map<String, Object>
     */
    @RequestMapping(value = "/createProblemCategory.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Object> createProblemCategoryAction(
            @RequestParam(value = "problemCategorySlug") String problemCategorySlug,
            @RequestParam(value = "problemCategoryName") String problemCategoryName,
            @RequestParam(value = "parentProblemCategory") String parentProblemCategorySlug,
            HttpServletRequest request) {
        Map<String, Object> result = problemService.createProblemCategory(problemCategorySlug, problemCategoryName,
                parentProblemCategorySlug);

        if ((boolean) result.get("isSuccessful")) {
            long problemCategoryId = (Long) result.get("problemCategoryId");
            String ipAddress = HttpRequestParser.getRemoteAddr(request);

            LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was created by administrator at %s.",
                    new Object[] { problemCategoryId, ipAddress }));
        }
        return result;
    }

    /**
     * .
     * @param problemCategoryId - 
     * @param problemCategorySlug - ??
     * @param problemCategoryName - ??
     * @param parentProblemCategorySlug - ??
     * @param request - HttpServletRequest
     * @return ?Map<String, Boolean>
     */
    @RequestMapping(value = "/editProblemCategory.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Boolean> editProblemCategoryAction(
            @RequestParam(value = "problemCategoryId") String problemCategoryId,
            @RequestParam(value = "problemCategorySlug") String problemCategorySlug,
            @RequestParam(value = "problemCategoryName") String problemCategoryName,
            @RequestParam(value = "parentProblemCategory") String parentProblemCategorySlug,
            HttpServletRequest request) {
        Map<String, Boolean> result = problemService.editProblemCategory(Integer.parseInt(problemCategoryId),
                problemCategorySlug, problemCategoryName, parentProblemCategorySlug);

        if (result.get("isSuccessful")) {
            String ipAddress = HttpRequestParser.getRemoteAddr(request);

            LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was edited by administrator at %s.",
                    new Object[] { problemCategoryId, ipAddress }));
        }
        return result;
    }

    /**
     * .
     * @param problemCategories - ?
     * @param request - HttpServletRequest
     * @return ?Map<String, Boolean>
     */
    @RequestMapping(value = "/deleteProblemCategories.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Object> deleteProblemCategoryAction(
            @RequestParam(value = "problemCategories") String problemCategories, HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>(3, 1);
        List<Integer> problemCategoryList = JSON.parseArray(problemCategories, Integer.class);
        List<Integer> deletedProblemCategories = new ArrayList<>();

        for (int problemCategoryId : problemCategoryList) {
            if (problemService.deleteProblemCategory(problemCategoryId)) {
                deletedProblemCategories.add(problemCategoryId);
            }
            String ipAddress = HttpRequestParser.getRemoteAddr(request);
            LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was deleted by administrator at %s.",
                    new Object[] { problemCategoryId, ipAddress }));
        }
        result.put("isSuccessful", true);
        result.put("deletedProblemCategories", deletedProblemCategories);
        return result;
    }

    /**
     * ???.
     * @param problemId - ??
     * @param username - ????
     * @param pageNumber - ???
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ?????ModelAndView
     */
    @RequestMapping(value = "/all-submissions", method = RequestMethod.GET)
    public ModelAndView allSubmissionsView(
            @RequestParam(value = "problemId", required = false, defaultValue = "0") long problemId,
            @RequestParam(value = "username", required = false, defaultValue = "") String username,
            @RequestParam(value = "page", required = false, defaultValue = "1") long pageNumber,
            HttpServletRequest request, HttpServletResponse response) {
        final int NUMBER_OF_SUBMISSIONS_PER_PAGE = 100;

        long totalSubmissions = submissionService.getNumberOfSubmissionsUsingProblemIdAndUsername(problemId,
                username);
        long latestSubmissionId = submissionService.getLatestSubmissionId();
        long offset = latestSubmissionId - (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_SUBMISSIONS_PER_PAGE;
        List<Submission> submissions = submissionService.getSubmissions(problemId, username, offset,
                NUMBER_OF_SUBMISSIONS_PER_PAGE);

        ModelAndView view = new ModelAndView("administration/all-submissions");
        view.addObject("problemId", problemId);
        view.addObject("username", username);
        view.addObject("currentPage", pageNumber);
        view.addObject("totalPages", (long) Math.ceil(totalSubmissions * 1.0 / NUMBER_OF_SUBMISSIONS_PER_PAGE));
        view.addObject("submissions", submissions);
        return view;
    }

    /**
     * ??.
     * @param submissions - ??ID?, ?(, )
     * @param request - HttpServletRequest
     * @return ??
     */
    @RequestMapping(value = "/deleteSubmissions.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Object> deleteSubmissionsAction(
            @RequestParam(value = "submissions") String submissions, HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>(3, 1);
        List<Long> submissionList = JSON.parseArray(submissions, Long.class);
        List<Long> deletedSubmissions = new ArrayList<>();

        for (Long submissionId : submissionList) {
            if (submissionService.deleteSubmission(submissionId)) {
                deletedSubmissions.add(submissionId);
            }
            String ipAddress = HttpRequestParser.getRemoteAddr(request);
            LOGGER.info(String.format("Submission: [SubmissionId=%s] deleted by administrator at %s.",
                    new Object[] { submissionId, ipAddress }));
        }
        result.put("isSuccessful", true);
        result.put("deletedSubmissions", deletedSubmissions);
        return result;
    }

    /**
     * ???.
     * @param submissions - ??ID?, ?(, )
     * @param request - HttpServletRequest
     * @return ?
     */
    @RequestMapping(value = "/restartSubmissions.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Boolean> restartSubmissionsAction(
            @RequestParam(value = "submissions") String submissions, HttpServletRequest request) {
        Map<String, Boolean> result = new HashMap<>(2, 1);
        List<Long> submissionList = JSON.parseArray(submissions, Long.class);

        for (Long submissionId : submissionList) {
            submissionService.createSubmissionTask(submissionId);
        }
        result.put("isSuccessful", true);
        return result;
    }

    /**
     * ??.
     * @param submissionId - ??
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ????ModelAndView
     */
    @RequestMapping(value = "/edit-submission/{submissionId}", method = RequestMethod.GET)
    public ModelAndView editSubmissionView(@PathVariable(value = "submissionId") long submissionId,
            HttpServletRequest request, HttpServletResponse response) {
        Submission submission = submissionService.getSubmission(submissionId);
        if (submission == null) {
            throw new ResourceNotFoundException();
        }
        ModelAndView view = new ModelAndView("administration/edit-submission");
        view.addObject("submission", submission);
        view.addObject("csrfToken", CsrfProtector.getCsrfToken(request.getSession()));
        return view;
    }

    /**
     * ?.
     * @param request - HttpRequest
     * @param response - HttpResponse
     * @return ???ModelAndView
     */
    @RequestMapping(value = "/general-settings", method = RequestMethod.GET)
    public ModelAndView generalSettingsView(HttpServletRequest request, HttpServletResponse response) {
        ModelAndView view = new ModelAndView("administration/general-settings");
        view.addObject("options", getOptions());
        return view;
    }

    /**
     * ?, ?.
     * @return ?
     */
    private Map<String, String> getOptions() {
        Map<String, String> optionMap = new HashMap<>();
        List<Option> options = optionService.getOptions();

        for (Option option : options) {
            optionMap.put(option.getOptionName(), option.getOptionValue());
        }
        return optionMap;
    }

    /**
     * .
     * @param websiteName - ??
     * @param websiteDescription - ??
     * @param copyright - ??
     * @param allowUserRegister - ??
     * @param icpNumber - ?
     * @param policeIcpNumber - ?
     * @param googleAnalyticsCode - Google Analytics?
     * @param offensiveWords - ??
     * @param request - HttpServletRequest
     * @return 
     */
    @RequestMapping(value = "/updateGeneralSettings.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Boolean> updateGeneralSettingsAction(
            @RequestParam(value = "websiteName") String websiteName,
            @RequestParam(value = "websiteDescription") String websiteDescription,
            @RequestParam(value = "copyright") String copyright,
            @RequestParam(value = "allowUserRegister") boolean allowUserRegister,
            @RequestParam(value = "icpNumber") String icpNumber,
            @RequestParam(value = "policeIcpNumber") String policeIcpNumber,
            @RequestParam(value = "googleAnalyticsCode") String googleAnalyticsCode,
            @RequestParam(value = "offensiveWords") String offensiveWords, HttpServletRequest request) {
        Map<String, Boolean> result = optionService.updateOptions(websiteName, websiteDescription, copyright,
                allowUserRegister, icpNumber, policeIcpNumber, googleAnalyticsCode, offensiveWords);
        return result;
    }

    /**
     * ?.
     * @param request - HttpServletRequest
     * @param response - HttpServletResponse
     * @return ??ModelAndView
     */
    @RequestMapping(value = "/language-settings", method = RequestMethod.GET)
    public ModelAndView languageSettingsView(HttpServletRequest request, HttpServletResponse response) {
        ModelAndView view = new ModelAndView("administration/language-settings");
        view.addObject("languages", languageService.getAllLanguages());
        return view;
    }

    /**
     * .
     * @param languages - ?
     * @param request - HttpServletRequest
     * @return 
     */
    @RequestMapping(value = "/updateLanguageSettings.action", method = RequestMethod.POST)
    public @ResponseBody Map<String, Object> updateLanguageSettingsAction(
            @RequestParam(value = "languages") String languages, HttpServletRequest request) {
        List<Language> languagesList = JSON.parseArray(languages, Language.class);
        Map<String, Object> result = languageService.updateLanguageSettings(languagesList);
        return result;
    }

    /**
     * UserService.
     */
    @Autowired
    private UserService userService;

    /**
     * ProblemService.
     * ??.
     */
    @Autowired
    private ProblemService problemService;

    /**
     * SubmissionService.
     * ????.
     */
    @Autowired
    private SubmissionService submissionService;

    /**
     * OptionService.
     * ?.
     */
    @Autowired
    private OptionService optionService;

    /**
     * LanguageService.
     * ?.
     */
    @Autowired
    private LanguageService languageService;

    /**
     * ApplicationEventListener.
     * ??.
     */
    @Autowired
    private ApplicationEventListener eventListener;

    /**
     * .
     */
    private static final Logger LOGGER = LogManager.getLogger(AdministrationController.class);
}