package org.iatoki.judgels.uriel.controllers; import com.google.common.collect.ImmutableList; import com.google.gson.Gson; import org.iatoki.judgels.play.IdentityUtils; import org.iatoki.judgels.play.InternalLink; import org.iatoki.judgels.play.LazyHtml; import org.iatoki.judgels.play.Page; import org.iatoki.judgels.play.controllers.AbstractJudgelsController; import org.iatoki.judgels.play.views.html.layouts.heading3Layout; import org.iatoki.judgels.play.views.html.layouts.heading3WithActionLayout; import org.iatoki.judgels.sandalphon.Sandalphon; import org.iatoki.judgels.sandalphon.services.SubmissionService; import org.iatoki.judgels.uriel.Contest; import org.iatoki.judgels.uriel.ContestConfiguration; import org.iatoki.judgels.uriel.ContestNotFoundException; import org.iatoki.judgels.uriel.ContestPermissions; import org.iatoki.judgels.uriel.ContestProblem; import org.iatoki.judgels.uriel.forms.ContestProblemCreateForm; import org.iatoki.judgels.uriel.ContestProblemNotFoundException; import org.iatoki.judgels.uriel.ContestProblemStatus; import org.iatoki.judgels.uriel.forms.ContestProblemUpdateForm; import org.iatoki.judgels.uriel.services.ContestProblemService; import org.iatoki.judgels.uriel.services.ContestService; import org.iatoki.judgels.uriel.ICPCContestStyleConfig; import org.iatoki.judgels.uriel.IOIContestStyleConfig; import org.iatoki.judgels.uriel.services.ContestSupervisorService; import org.iatoki.judgels.uriel.services.impls.JidCacheServiceImpl; import org.iatoki.judgels.uriel.UrielProperties; import org.iatoki.judgels.uriel.controllers.securities.Authenticated; import org.iatoki.judgels.uriel.controllers.securities.HasRole; import org.iatoki.judgels.uriel.controllers.securities.LoggedIn; import org.iatoki.judgels.uriel.views.html.contest.problem.createProblemView; import org.iatoki.judgels.uriel.views.html.contest.problem.listUsedProblemsView; import org.iatoki.judgels.uriel.views.html.contest.problem.listProblemsView; import org.iatoki.judgels.uriel.views.html.contest.problem.updateProblemView; import org.iatoki.judgels.uriel.views.html.contest.problem.viewProblemView; import org.iatoki.judgels.uriel.views.html.contest.problem.viewProblemCriticalView; import org.iatoki.judgels.uriel.views.html.layouts.accessTypeByStatusLayout; import play.data.DynamicForm; import play.data.Form; import play.db.jpa.Transactional; import play.filters.csrf.AddCSRFToken; import play.filters.csrf.RequireCSRFCheck; import play.i18n.Messages; import play.mvc.Http; import play.mvc.Result; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import java.io.IOException; import java.net.URI; import java.util.Arrays; @Authenticated(value = {LoggedIn.class, HasRole.class}) @Singleton @Named public class ContestProblemController extends AbstractJudgelsController { private static final long PAGE_SIZE = 50; private final Sandalphon sandalphon; private final ContestService contestService; private final ContestProblemService contestProblemService; private final ContestSupervisorService contestSupervisorService; private final SubmissionService submissionService; @Inject public ContestProblemController(Sandalphon sandalphon, ContestService contestService, ContestProblemService contestProblemService, ContestSupervisorService contestSupervisorService, SubmissionService submissionService) { this.sandalphon = sandalphon; this.contestService = contestService; this.contestProblemService = contestProblemService; this.contestSupervisorService = contestSupervisorService; this.submissionService = submissionService; } @Transactional(readOnly = true) public Result viewUsedProblems(long contestId) throws ContestNotFoundException { return listUsedProblems(contestId, 0); } @Transactional(readOnly = true) public Result listUsedProblems(long contestId, long pageIndex) throws ContestNotFoundException { Contest contest = contestService.findContestById(contestId); if (ContestControllerUtils.getInstance().isAllowedToEnterContest(contest)) { Page<ContestProblem> contestProblems = contestProblemService.pageUsedContestProblemsByContestJid(contest.getJid(), pageIndex, PAGE_SIZE); ImmutableList.Builder<ContestProblem> replacementBuilder = ImmutableList.builder(); for (ContestProblem contestProblem : contestProblems.getData()) { contestProblem.setTotalSubmissions(submissionService.countSubmissionsByContestJidByUser(contest.getJid(), contestProblem.getProblemJid(), IdentityUtils.getUserJid())); replacementBuilder.add(contestProblem); } contestProblems = new Page<>(replacementBuilder.build(), contestProblems.getTotalRowsCount(), contestProblems.getPageIndex(), contestProblems.getPageSize()); LazyHtml content = new LazyHtml(listUsedProblemsView.render(contest.getId(), contestProblems, pageIndex)); content.appendLayout(c -> heading3Layout.render(Messages.get("problem.problems"), c)); if (isAllowedToSuperviseProblems(contest)) { appendSubtabsLayout(content, contest); } ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("problem.list"), routes.ContestProblemController.viewUsedProblems(contest.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Problems"); ControllerUtils.getInstance().addActivityLog("Open list of valid problems in contest " + contest.getName() + " <a href=\"" + "http://" + Http.Context.current().request().host() + Http.Context.current().request().uri() + "\">link</a>."); return ControllerUtils.getInstance().lazyOk(content); } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional(readOnly = true) public Result viewProblem(long contestId, long contestProblemId) throws ContestNotFoundException, ContestProblemNotFoundException { Contest contest = contestService.findContestById(contestId); ContestProblem contestProblem = contestProblemService.findContestProblemByContestProblemId(contestProblemId); if (ContestControllerUtils.getInstance().isAllowedToEnterContest(contest) && isAllowedToViewProblem(contest, contestProblem)) { long submissionsLeft = -1; if (contestProblem.getSubmissionsLimit() != 0) { submissionsLeft = contestProblem.getSubmissionsLimit() - submissionService.countSubmissionsByContestJidByUser(contest.getJid(), contestProblem.getProblemJid(), IdentityUtils.getUserJid()); } String requestUrl = sandalphon.getProblemStatementRenderUri().toString(); String requestBody = ""; ContestConfiguration config = contestService.findContestConfigurationByContestJid(contest.getJid()); String styleConfig = config.getStyleConfig(); if (contest.isICPC()) { requestBody = sandalphon.getProblemStatementRenderRequestBody(contestProblem.getProblemJid(), contestProblem.getProblemSecret(), System.currentTimeMillis(), ContestControllerUtils.getInstance().getCurrentStatementLanguage(), routes.ContestSubmissionController.postSubmitProblem(contestId, contestProblem.getProblemJid()).absoluteURL(request(), request().secure()), routes.ContestProblemController.switchLanguage(contestId, contestProblemId).absoluteURL(request(), request().secure()), null, new Gson().fromJson(styleConfig, ICPCContestStyleConfig.class).getLanguageRestriction()); } else if (contest.isIOI()) { requestBody = sandalphon.getProblemStatementRenderRequestBody(contestProblem.getProblemJid(), contestProblem.getProblemSecret(), System.currentTimeMillis(), ContestControllerUtils.getInstance().getCurrentStatementLanguage(), routes.ContestSubmissionController.postSubmitProblem(contestId, contestProblem.getProblemJid()).absoluteURL(request(), request().secure()), routes.ContestProblemController.switchLanguage(contestId, contestProblemId).absoluteURL(request(), request().secure()), null, new Gson().fromJson(styleConfig, IOIContestStyleConfig.class).getLanguageRestriction()); } LazyHtml content; if (UrielProperties.getInstance().isContestCritial(contest.getJid())) { content = new LazyHtml(viewProblemCriticalView.render(requestUrl, requestBody, submissionsLeft, contestProblem.getStatus() == ContestProblemStatus.CLOSED, contest, contestProblem)); } else { content = new LazyHtml(viewProblemView.render(requestUrl, requestBody, submissionsLeft, contestProblem.getStatus() == ContestProblemStatus.CLOSED)); } ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("status.contestant"), routes.ContestProblemController.viewUsedProblems(contest.getId())), new InternalLink(contestProblem.getAlias(), routes.ContestProblemController.viewProblem(contest.getId(), contestProblem.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Problem - View"); ControllerUtils.getInstance().addActivityLog("View problem " + contestProblem.getAlias() + " in contest " + contest.getName() + " <a href=\"" + "http://" + Http.Context.current().request().host() + Http.Context.current().request().uri() + "\">link</a>."); return ControllerUtils.getInstance().lazyOk(content); } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional(readOnly = true) public Result renderImage(long contestId, long contestProblemId, String imageFilename) throws ContestNotFoundException, ContestProblemNotFoundException { Contest contest = contestService.findContestById(contestId); ContestProblem contestProblem = contestProblemService.findContestProblemByContestProblemId(contestProblemId); if (contest.getJid().equals(contestProblem.getContestJid())) { URI imageUri = sandalphon.getProblemMediaRenderUri(contestProblem.getProblemJid(), imageFilename); return redirect(imageUri.toString()); } else { return notFound(); } } public Result switchLanguage(long contestId, long contestProblemId) { String languageCode = DynamicForm.form().bindFromRequest().get("langCode"); ContestControllerUtils.getInstance().setCurrentStatementLanguage(languageCode); return redirect(routes.ContestProblemController.viewProblem(contestId, contestProblemId)); } @Transactional(readOnly = true) public Result viewProblems(long contestId) throws ContestNotFoundException { return listProblems(contestId, 0, "alias", "asc", ""); } @Transactional(readOnly = true) public Result listProblems(long contestId, long page, String sortBy, String orderBy, String filterString) throws ContestNotFoundException { Contest contest = contestService.findContestById(contestId); if (isAllowedToSuperviseProblems(contest)) { Page<ContestProblem> contestProblemPage = contestProblemService.pageContestProblemsByContestJid(contest.getJid(), page, PAGE_SIZE, sortBy, orderBy, filterString, null); LazyHtml content = new LazyHtml(listProblemsView.render(contest.getId(), contestProblemPage, page, sortBy, orderBy, filterString)); content.appendLayout(c -> heading3WithActionLayout.render(Messages.get("problem.list"), new InternalLink(Messages.get("commons.create"), routes.ContestProblemController.createProblem(contestId)), c)); appendSubtabsLayout(content, contest); ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("problem.list"), routes.ContestProblemController.viewProblems(contest.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Problems"); ControllerUtils.getInstance().addActivityLog("Open all problems in contest " + contest.getName() + " <a href=\"" + "http://" + Http.Context.current().request().host() + Http.Context.current().request().uri() + "\">link</a>."); return ControllerUtils.getInstance().lazyOk(content); } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional(readOnly = true) @AddCSRFToken public Result createProblem(long contestId) throws ContestNotFoundException { Contest contest = contestService.findContestById(contestId); if (isAllowedToSuperviseProblems(contest)) { Form<ContestProblemCreateForm> form = Form.form(ContestProblemCreateForm.class); form = form.fill(new ContestProblemCreateForm(0)); ControllerUtils.getInstance().addActivityLog("Try to add problem in contest " + contest.getName() + " <a href=\"" + "http://" + Http.Context.current().request().host() + Http.Context.current().request().uri() + "\">link</a>."); return showCreateProblem(form, contest); } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional @RequireCSRFCheck public Result postCreateProblem(long contestId) throws ContestNotFoundException { Contest contest = contestService.findContestById(contestId); if (isAllowedToSuperviseProblems(contest)) { Form<ContestProblemCreateForm> form = Form.form(ContestProblemCreateForm.class).bindFromRequest(); if (form.hasErrors() || form.hasGlobalErrors()) { return showCreateProblem(form, contest); } else { ContestProblemCreateForm contestProblemCreateForm = form.get(); String problemName = null; try { problemName = sandalphon.verifyProblemJid(contestProblemCreateForm.problemJid); } catch (IOException e) { form.reject("error.system.sandalphon.connection"); return showCreateProblem(form, contest); } if ((problemName != null) && (!contestProblemService.isContestProblemInContestByProblemJidOrAlias(contest.getJid(), contestProblemCreateForm.problemJid, contestProblemCreateForm.alias))) { contestProblemService.createContestProblem(contest.getId(), contestProblemCreateForm.problemJid, contestProblemCreateForm.problemSecret, contestProblemCreateForm.alias, contestProblemCreateForm.submissionsLimit, ContestProblemStatus.valueOf(contestProblemCreateForm.status)); JidCacheServiceImpl.getInstance().putDisplayName(contestProblemCreateForm.problemJid, problemName, IdentityUtils.getUserJid(), IdentityUtils.getIpAddress()); ControllerUtils.getInstance().addActivityLog("Add problem " + contestProblemCreateForm.alias + " in contest " + contest.getName() + "."); return redirect(routes.ContestProblemController.viewProblems(contest.getId())); } else { form.reject("error.problem.create.problemJidOrAlias.invalid"); return showCreateProblem(form, contest); } } } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional(readOnly = true) @AddCSRFToken public Result updateProblem(long contestId, long contestProblemId) throws ContestNotFoundException, ContestProblemNotFoundException { Contest contest = contestService.findContestById(contestId); ContestProblem contestProblem = contestProblemService.findContestProblemByContestProblemId(contestProblemId); if (isAllowedToSuperviseProblems(contest) && contestProblem.getContestJid().equals(contest.getJid())) { ContestProblemUpdateForm contestProblemUpdateForm = new ContestProblemUpdateForm(contestProblem); Form<ContestProblemUpdateForm> form = Form.form(ContestProblemUpdateForm.class).fill(contestProblemUpdateForm); ControllerUtils.getInstance().addActivityLog("Try to update problem " + contestProblem.getAlias() + " in contest " + contest.getName() + " <a href=\"" + "http://" + Http.Context.current().request().host() + Http.Context.current().request().uri() + "\">link</a>."); return showUpdateProblem(form, contest, contestProblem); } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional @RequireCSRFCheck public Result postUpdateProblem(long contestId, long contestProblemId) throws ContestNotFoundException, ContestProblemNotFoundException { Contest contest = contestService.findContestById(contestId); ContestProblem contestProblem = contestProblemService.findContestProblemByContestProblemId(contestProblemId); if (isAllowedToSuperviseProblems(contest) && contestProblem.getContestJid().equals(contest.getJid())) { Form<ContestProblemUpdateForm> form = Form.form(ContestProblemUpdateForm.class).bindFromRequest(); if (form.hasErrors() || form.hasGlobalErrors()) { return showUpdateProblem(form, contest, contestProblem); } else { ContestProblemUpdateForm contestProblemUpdateForm = form.get(); contestProblemService.updateContestProblem(contestProblem.getId(), contestProblemUpdateForm.alias, contestProblemUpdateForm.submissionsLimit, ContestProblemStatus.valueOf(contestProblemUpdateForm.status)); ControllerUtils.getInstance().addActivityLog("Update problem " + contestProblem.getAlias() + " in contest " + contest.getName() + "."); return redirect(routes.ContestProblemController.viewProblems(contest.getId())); } } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } private Result showCreateProblem(Form<ContestProblemCreateForm> form, Contest contest){ LazyHtml content = new LazyHtml(createProblemView.render(contest.getId(), form)); content.appendLayout(c -> heading3Layout.render(Messages.get("problem.create"), c)); appendSubtabsLayout(content, contest); ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("status.supervisor"), routes.ContestProblemController.viewProblems(contest.getId())), new InternalLink(Messages.get("problem.create"), routes.ContestProblemController.createProblem(contest.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Problem - Create"); return ControllerUtils.getInstance().lazyOk(content); } private Result showUpdateProblem(Form<ContestProblemUpdateForm> form, Contest contest, ContestProblem contestProblem){ LazyHtml content = new LazyHtml(updateProblemView.render(contest.getId(), contestProblem, form)); content.appendLayout(c -> heading3Layout.render(Messages.get("problem.update") + " " + contestProblem.getAlias(), c)); appendSubtabsLayout(content, contest); ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("status.supervisor"), routes.ContestProblemController.viewProblems(contest.getId())), new InternalLink(Messages.get("problem.update"), routes.ContestProblemController.updateProblem(contest.getId(), contestProblem.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Problem - Update"); return ControllerUtils.getInstance().lazyOk(content); } private void appendSubtabsLayout(LazyHtml content, Contest contest) { content.appendLayout(c -> accessTypeByStatusLayout.render(routes.ContestProblemController.viewUsedProblems(contest.getId()), routes.ContestProblemController.viewProblems(contest.getId()), c)); } private void appendBreadcrumbsLayout(LazyHtml content, Contest contest, InternalLink... lastLinks) { ControllerUtils.getInstance().appendBreadcrumbsLayout(content, ContestControllerUtils.getInstance().getContestBreadcrumbsBuilder(contest) .add(new InternalLink(Messages.get("problem.problems"), routes.ContestController.jumpToProblems(contest.getId()))) .addAll(Arrays.asList(lastLinks)) .build() ); } private boolean isAllowedToSuperviseProblems(Contest contest) { return ControllerUtils.getInstance().isAdmin() || ContestControllerUtils.getInstance().isManager(contest) || (ContestControllerUtils.getInstance().isSupervisor(contest) && contestSupervisorService.findContestSupervisorByContestJidAndUserJid(contest.getJid(), IdentityUtils.getUserJid()).getContestPermission().isAllowed(ContestPermissions.PROBLEM)); } private boolean isAllowedToViewProblem(Contest contest, ContestProblem contestProblem) { return contestProblem.getContestJid().equals(contest.getJid()) && contestProblem.getStatus() != ContestProblemStatus.UNUSED; } }