package org.iatoki.judgels.uriel.controllers; import com.google.common.collect.ImmutableList; 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.alertLayout; import org.iatoki.judgels.play.views.html.layouts.heading3Layout; import org.iatoki.judgels.play.views.html.layouts.heading3WithActionLayout; import org.iatoki.judgels.uriel.Contest; import org.iatoki.judgels.uriel.ContestClarification; import org.iatoki.judgels.uriel.ContestPermissions; import org.iatoki.judgels.uriel.forms.ContestClarificationChangeForm; import org.iatoki.judgels.uriel.forms.ContestClarificationCreateForm; import org.iatoki.judgels.uriel.ContestClarificationNotFoundException; import org.iatoki.judgels.uriel.ContestClarificationStatus; import org.iatoki.judgels.uriel.forms.ContestClarificationUpdateForm; import org.iatoki.judgels.uriel.ContestNotFoundException; import org.iatoki.judgels.uriel.ContestProblem; import org.iatoki.judgels.uriel.services.ContestClarificationService; import org.iatoki.judgels.uriel.services.ContestProblemService; import org.iatoki.judgels.uriel.services.ContestService; import org.iatoki.judgels.uriel.ContestTeam; import org.iatoki.judgels.uriel.ContestTeamMember; 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.services.ContestSupervisorService; import org.iatoki.judgels.uriel.services.ContestTeamService; import org.iatoki.judgels.uriel.views.html.contest.clarification.createClarificationView; import org.iatoki.judgels.uriel.views.html.contest.clarification.listClarificationsView; import org.iatoki.judgels.uriel.views.html.contest.clarification.listScreenedClarificationsView; import org.iatoki.judgels.uriel.views.html.contest.clarification.updateClarificationAnswerView; import org.iatoki.judgels.uriel.views.html.contest.clarification.updateClarificationContentView; import org.iatoki.judgels.uriel.views.html.layouts.accessTypeByStatusLayout; 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.util.Arrays; import java.util.List; import java.util.stream.Collectors; @Authenticated(value = {LoggedIn.class, HasRole.class}) @Singleton @Named public class ContestClarificationController extends AbstractJudgelsController { private static final long PAGE_SIZE = 20; private final ContestService contestService; private final ContestClarificationService contestClarificationService; private final ContestProblemService contestProblemService; private final ContestSupervisorService contestSupervisorService; private final ContestTeamService contestTeamService; @Inject public ContestClarificationController(ContestService contestService, ContestClarificationService contestClarificationService, ContestProblemService contestProblemService, ContestSupervisorService contestSupervisorService, ContestTeamService contestTeamService) { this.contestService = contestService; this.contestClarificationService = contestClarificationService; this.contestProblemService = contestProblemService; this.contestSupervisorService = contestSupervisorService; this.contestTeamService = contestTeamService; } @Transactional public Result viewScreenedClarifications(long contestId) throws ContestNotFoundException { return listScreenedClarifications(contestId, 0, "id", "desc", ""); } @Transactional public Result listScreenedClarifications(long contestId, long pageIndex, String orderBy, String orderDir, String filterString) throws ContestNotFoundException { Contest contest = contestService.findContestById(contestId); if (ContestControllerUtils.getInstance().isAllowedToEnterContest(contest)) { Page<ContestClarification> contestClarifications; boolean coach = ContestControllerUtils.getInstance().isCoach(contest); if (coach) { List<ContestTeam> contestTeams = contestTeamService.findContestTeamsByContestJidAndCoachJid(contest.getJid(), IdentityUtils.getUserJid()); ImmutableList.Builder<ContestTeamMember> contestTeamMembersBuilder = ImmutableList.builder(); for (ContestTeam team : contestTeams) { contestTeamMembersBuilder.addAll(team.getMembers()); } List<ContestTeamMember> contestTeamMembers = contestTeamMembersBuilder.build(); contestClarifications = contestClarificationService.pageContestClarificationsByContestJid(contest.getJid(), pageIndex, PAGE_SIZE, orderBy, orderDir, filterString, contestTeamMembers.stream().map(ct -> ct.getMemberJid()).collect(Collectors.toList())); } else { contestClarifications = contestClarificationService.pageContestClarificationsByContestJid(contest.getJid(), pageIndex, PAGE_SIZE, orderBy, orderDir, filterString, ImmutableList.of(IdentityUtils.getUserJid())); } contestClarificationService.readContestClarifications(IdentityUtils.getUserJid(), contestClarifications.getData().stream().filter(c -> c.isAnswered()).map(c -> c.getJid()).collect(Collectors.toList())); LazyHtml content = new LazyHtml(listScreenedClarificationsView.render(contest, contestClarifications, pageIndex, orderBy, orderDir, filterString, coach)); if (coach) { content.appendLayout(c -> heading3Layout.render(Messages.get("clarification.list"), c)); } else if (contest.isClarificationTimeValid()) { content.appendLayout(c -> heading3WithActionLayout.render(Messages.get("clarification.list"), new InternalLink(Messages.get("commons.create"), routes.ContestClarificationController.createClarification(contest.getId())), c)); } else { content.appendLayout(c -> alertLayout.render(Messages.get("clarification.time_ended"), c)); content.appendLayout(c -> heading3Layout.render(Messages.get("clarification.list"), c)); } if (isAllowedToSuperviseClarifications(contest)) { content.appendLayout(c -> accessTypeByStatusLayout.render(routes.ContestClarificationController.viewScreenedClarifications(contest.getId()), routes.ContestClarificationController.viewClarifications(contest.getId()), c)); } ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("clarification.clarifications"), routes.ContestClarificationController.viewScreenedClarifications(contest.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Clarifications"); ControllerUtils.getInstance().addActivityLog("Open list of own clarifications 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 createClarification(long contestId) throws ContestNotFoundException { Contest contest = contestService.findContestById(contestId); if (ContestControllerUtils.getInstance().isAllowedToDoContest(contest)) { if (contest.isClarificationTimeValid()) { Form<ContestClarificationCreateForm> form = Form.form(ContestClarificationCreateForm.class); ControllerUtils.getInstance().addActivityLog("Try to create clarification in contest " + contest.getName() + " <a href=\"" + "http://" + Http.Context.current().request().host() + Http.Context.current().request().uri() + "\">link</a>."); return showCreateClarification(form, contest); } else { return redirect(routes.ContestClarificationController.viewScreenedClarifications(contest.getId())); } } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional @RequireCSRFCheck public Result postCreateClarification(long contestId) throws ContestNotFoundException { Contest contest = contestService.findContestById(contestId); if (ContestControllerUtils.getInstance().isAllowedToDoContest(contest)) { if (contest.isClarificationTimeValid()) { Form<ContestClarificationCreateForm> form = Form.form(ContestClarificationCreateForm.class).bindFromRequest(); if (form.hasErrors() || form.hasGlobalErrors()) { return showCreateClarification(form, contest); } else { ContestClarificationCreateForm contestClarificationCreateForm = form.get(); contestClarificationService.createContestClarification(contest.getId(), contestClarificationCreateForm.title, contestClarificationCreateForm.question, contestClarificationCreateForm.topicJid); ControllerUtils.getInstance().addActivityLog("Create clarification " + contestClarificationCreateForm.title + " in contest " + contest.getName() + "."); return redirect(routes.ContestClarificationController.viewScreenedClarifications(contest.getId())); } } else { return redirect(routes.ContestClarificationController.viewScreenedClarifications(contest.getId())); } } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional(readOnly = true) @AddCSRFToken public Result updateClarificationContent(long contestId, long contestClarificationId) throws ContestNotFoundException, ContestClarificationNotFoundException { Contest contest = contestService.findContestById(contestId); ContestClarification contestClarification = contestClarificationService.findContestClarificationByContestClarificationId(contestClarificationId); if (ContestControllerUtils.getInstance().isCoach(contest) && !contestClarification.isAnswered() && contestClarification.getContestJid().equals(contest.getJid())) { ContestClarificationChangeForm contestClarificationChangeForm = new ContestClarificationChangeForm(contestClarification); Form<ContestClarificationChangeForm> form = Form.form(ContestClarificationChangeForm.class).fill(contestClarificationChangeForm); form = form.fill(contestClarificationChangeForm); ControllerUtils.getInstance().addActivityLog("Try to update clarification " + contestClarification.getTitle() + " content in contest " + contest.getName() + " <a href=\"" + "http://" + Http.Context.current().request().host() + Http.Context.current().request().uri() + "\">link</a>."); return showUpdateClarificationContent(form, contest, contestClarification); } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional @RequireCSRFCheck public Result postUpdateClarificationContent(long contestId, long contestClarificationId) throws ContestNotFoundException, ContestClarificationNotFoundException { Contest contest = contestService.findContestById(contestId); ContestClarification contestClarification = contestClarificationService.findContestClarificationByContestClarificationId(contestClarificationId); if (ContestControllerUtils.getInstance().isCoach(contest) && !contestClarification.isAnswered() && contestClarification.getContestJid().equals(contest.getJid())) { Form<ContestClarificationChangeForm> form = Form.form(ContestClarificationChangeForm.class).bindFromRequest(); if (form.hasErrors() || form.hasGlobalErrors()) { return showUpdateClarificationContent(form, contest, contestClarification); } else { ContestClarificationChangeForm contestClarificationChangeForm = form.get(); contestClarificationService.updateContestClarification(contestClarification.getId(), contestClarificationChangeForm.title, contestClarificationChangeForm.question); ControllerUtils.getInstance().addActivityLog("Update clarification " + contestClarification.getTitle() + " content in contest " + contest.getName() + "."); return redirect(routes.ContestClarificationController.viewScreenedClarifications(contest.getId())); } } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional(readOnly = true) public Result viewClarifications(long contestId) throws ContestNotFoundException { return listClarifications(contestId, 0, "id", "desc", ""); } @Transactional(readOnly = true) public Result listClarifications(long contestId, long pageIndex, String orderBy, String orderDir, String filterString) throws ContestNotFoundException { Contest contest = contestService.findContestById(contestId); if (isAllowedToSuperviseClarifications(contest)) { Page<ContestClarification> contestClarifications = contestClarificationService.pageContestClarificationsByContestJid(contest.getJid(), pageIndex, PAGE_SIZE, orderBy, orderDir, filterString, null); LazyHtml content = new LazyHtml(listClarificationsView.render(contest.getId(), contestClarifications, pageIndex, orderBy, orderDir, filterString)); content.appendLayout(c -> heading3Layout.render(Messages.get("clarification.list"), c)); appendSubtabsLayout(content, contest); ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("status.supervisor"), routes.ContestClarificationController.viewClarifications(contest.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - All Clarifications"); ControllerUtils.getInstance().addActivityLog("Open all clarifications 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 updateClarificationAnswer(long contestId, long contestClarificationId) throws ContestNotFoundException, ContestClarificationNotFoundException { Contest contest = contestService.findContestById(contestId); ContestClarification contestClarification = contestClarificationService.findContestClarificationByContestClarificationId(contestClarificationId); if (isAllowedToSuperviseClarifications(contest) && contestClarification.getContestJid().equals(contest.getJid())) { ContestClarificationUpdateForm contestClarificationUpsertForm = new ContestClarificationUpdateForm(contestClarification); Form<ContestClarificationUpdateForm> form = Form.form(ContestClarificationUpdateForm.class).fill(contestClarificationUpsertForm); ControllerUtils.getInstance().addActivityLog("Try to answer clarification " + contestClarification.getTitle() + " in contest " + contest.getName() + " <a href=\"" + "http://" + Http.Context.current().request().host() + Http.Context.current().request().uri() + "\">link</a>."); return showUpdateClarificationAnswer(form, contest, contestClarification); } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } @Transactional @RequireCSRFCheck public Result postUpdateClarificationAnswer(long contestId, long contestClarificationId) throws ContestNotFoundException, ContestClarificationNotFoundException { Contest contest = contestService.findContestById(contestId); ContestClarification contestClarification = contestClarificationService.findContestClarificationByContestClarificationId(contestClarificationId); if (isAllowedToSuperviseClarifications(contest) && contestClarification.getContestJid().equals(contest.getJid())) { Form<ContestClarificationUpdateForm> form = Form.form(ContestClarificationUpdateForm.class).bindFromRequest(); if (form.hasErrors() || form.hasGlobalErrors()) { return showUpdateClarificationAnswer(form, contest, contestClarification); } else { ContestClarificationUpdateForm contestClarificationUpdateForm = form.get(); contestClarificationService.updateContestClarification(contestClarification.getId(), contestClarificationUpdateForm.answer, ContestClarificationStatus.valueOf(contestClarificationUpdateForm.status)); ControllerUtils.getInstance().addActivityLog("Answer clarification " + contestClarification.getTitle() + " in contest " + contest.getName() + "."); return redirect(routes.ContestClarificationController.viewClarifications(contest.getId())); } } else { return ContestControllerUtils.getInstance().tryEnteringContest(contest); } } private Result showCreateClarification(Form<ContestClarificationCreateForm> form, Contest contest){ List<ContestProblem> contestProblemList = contestProblemService.findOpenedContestProblemByContestJid(contest.getJid()); LazyHtml content = new LazyHtml(createClarificationView.render(contest, form, contestProblemList)); content.appendLayout(c -> heading3Layout.render(Messages.get("clarification.create"), c)); if (isAllowedToSuperviseClarifications(contest)) { appendSubtabsLayout(content, contest); } ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("status.contestant"), routes.ContestClarificationController.viewScreenedClarifications(contest.getId())), new InternalLink(Messages.get("clarification.create"), routes.ContestClarificationController.createClarification(contest.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Clarification - Create"); return ControllerUtils.getInstance().lazyOk(content); } private Result showUpdateClarificationContent(Form<ContestClarificationChangeForm> form, Contest contest, ContestClarification contestClarification){ LazyHtml content = new LazyHtml(updateClarificationContentView.render(contest, contestClarification, form)); content.appendLayout(c -> heading3Layout.render(Messages.get("clarification.update"), c)); if(ContestControllerUtils.getInstance().isSupervisorOrAbove(contest)) { appendSubtabsLayout(content, contest); } ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("status.contestant"), routes.ContestClarificationController.viewScreenedClarifications(contest.getId())), new InternalLink(Messages.get("clarification.update"), routes.ContestClarificationController.updateClarificationContent(contest.getId(), contestClarification.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Clarification - Update Content"); return ControllerUtils.getInstance().lazyOk(content); } private Result showUpdateClarificationAnswer(Form<ContestClarificationUpdateForm> form, Contest contest, ContestClarification contestClarification){ LazyHtml content = new LazyHtml(updateClarificationAnswerView.render(contest.getId(), contestClarification, form)); content.appendLayout(c -> heading3Layout.render(Messages.get("clarification.update"), c)); appendSubtabsLayout(content, contest); ContestControllerUtils.getInstance().appendTabsLayout(content, contest); ControllerUtils.getInstance().appendSidebarLayout(content); appendBreadcrumbsLayout(content, contest, new InternalLink(Messages.get("status.supervisor"), routes.ContestClarificationController.viewClarifications(contest.getId())), new InternalLink(Messages.get("clarification.update"), routes.ContestClarificationController.updateClarificationAnswer(contest.getId(), contestClarification.getId())) ); ControllerUtils.getInstance().appendTemplateLayout(content, "Contest - Clarification - Update Answer"); return ControllerUtils.getInstance().lazyOk(content); } private void appendSubtabsLayout(LazyHtml content, Contest contest) { content.appendLayout(c -> accessTypeByStatusLayout.render(routes.ContestClarificationController.viewScreenedClarifications(contest.getId()), routes.ContestClarificationController.viewClarifications(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("clarification.clarifications"), routes.ContestController.jumpToClarifications(contest.getId()))) .addAll(Arrays.asList(lastLinks)) .build() ); } private boolean isAllowedToSuperviseClarifications(Contest contest) { return ControllerUtils.getInstance().isAdmin() || ContestControllerUtils.getInstance().isManager(contest) || (ContestControllerUtils.getInstance().isSupervisor(contest) && contestSupervisorService.findContestSupervisorByContestJidAndUserJid(contest.getJid(), IdentityUtils.getUserJid()).getContestPermission().isAllowed(ContestPermissions.CLARIFICATION)); } }