public async Task <IActionResult> Index(string eventId, int puzzleId) { Event currentEvent = await EventHelper.GetEventFromEventId(context, eventId); PuzzleUser user = await PuzzleUser.GetPuzzleUserForCurrentUser(context, User, userManager); Team team = await UserEventHelper.GetTeamForPlayer(context, currentEvent, user); Puzzle thisPuzzle = await context.Puzzles.FirstOrDefaultAsync(m => m.ID == puzzleId); // Find the material file with the latest-alphabetically ShortName that contains the substring "customPage". var materialFile = await(from f in context.ContentFiles where f.Puzzle == thisPuzzle && f.FileType == ContentFileType.PuzzleMaterial && f.ShortName.Contains("customPage") orderby f.ShortName descending select f).FirstOrDefaultAsync(); if (materialFile == null) { return(Content("ERROR: There's no custom page uploaded for this puzzle")); } var materialUrl = materialFile.Url; // Download that material file. string fileContents; using (var wc = new System.Net.WebClient()) { fileContents = await wc.DownloadStringTaskAsync(materialUrl); } // Return the file contents to the user. return(Content(fileContents, "text/html")); }
public async Task IsPuzzleAuthorCheck(AuthorizationHandlerContext authContext, IAuthorizationRequirement requirement) { EventRole role = GetEventRoleFromRoute(); if (role != EventRole.author) { return; } PuzzleUser puzzleUser = await PuzzleUser.GetPuzzleUserForCurrentUser(dbContext, authContext.User, userManager); Puzzle puzzle = await GetPuzzleFromRoute(); Event thisEvent = await GetEventFromRoute(); if (thisEvent != null && await UserEventHelper.IsAuthorOfPuzzle(dbContext, puzzle, puzzleUser)) { authContext.Succeed(requirement); } if (puzzle != null) { dbContext.Entry(puzzle).State = EntityState.Detached; } }
public async Task IsPlayerOnTeamCheck(AuthorizationHandlerContext authContext, IAuthorizationRequirement requirement) { EventRole role = GetEventRoleFromRoute(); if (role != EventRole.play) { return; } PuzzleUser puzzleUser = await PuzzleUser.GetPuzzleUserForCurrentUser(dbContext, authContext.User, userManager); Team team = await GetTeamFromRoute(); Event thisEvent = await GetEventFromRoute(); if (thisEvent != null) { Team userTeam = await UserEventHelper.GetTeamForPlayer(dbContext, thisEvent, puzzleUser); if (userTeam != null && userTeam.ID == team.ID) { authContext.Succeed(requirement); } } }
public async Task <IActionResult> OnGetAsync(int?puzzleId, int?refresh) { if (refresh != null) { Refresh = refresh; } if (puzzleId != null) { Puzzle = await _context.Puzzles.Where(m => m.ID == puzzleId && m.EventID == Event.ID).FirstOrDefaultAsync(); if (Puzzle == null) { return(NotFound()); } if (EventRole == EventRole.author && !await UserEventHelper.IsAuthorOfPuzzle(_context, Puzzle, LoggedInUser)) { return(Forbid()); } } await PopulateUI(puzzleId); return(Page()); }
public async Task <IActionResult> OnGetAsync(int puzzleId) { Puzzle = await _context.Puzzles.Where(p => p.ID == puzzleId).FirstOrDefaultAsync(); if (Puzzle == null) { return(NotFound("Puzzle does not exist.")); } Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); int solvedPuzzleCount = 0; switch (Puzzle.PieceMetaUsage) { case PieceMetaUsage.EntireEvent: solvedPuzzleCount = await _context.PuzzleStatePerTeam.Where(ps => ps.Team == team && ps.SolvedTime != null && ps.Puzzle.SolveValue >= 10).CountAsync(); break; case PieceMetaUsage.GroupOnly: solvedPuzzleCount = await _context.PuzzleStatePerTeam.Where(ps => ps.Team == team && ps.SolvedTime != null && ps.Puzzle.SolveValue >= 10 && ps.Puzzle.Group == Puzzle.Group).CountAsync(); break; default: return(NotFound("Puzzle does not support the simple meta view.")); } EarnedPieces = await _context.Pieces.Where(p => p.PuzzleID == puzzleId && p.ProgressLevel <= solvedPuzzleCount).OrderBy(p => p.ProgressLevel).ToListAsync(); return(Page()); }
public async Task <IActionResult> Post(string eventId, int puzzleId, List <int> query_puzzle_ids, int?min_solve_count, string annotations, string last_sync_time) { // Find what team this user is on, relative to the event. Event currentEvent = await EventHelper.GetEventFromEventId(context, eventId); if (currentEvent == null) { return(Unauthorized()); } PuzzleUser user = await PuzzleUser.GetPuzzleUserForCurrentUser(context, User, userManager); if (user == null) { return(Unauthorized()); } Team team = await UserEventHelper.GetTeamForPlayer(context, currentEvent, user); if (team == null) { return(Unauthorized()); } var helper = new SyncHelper(context); var response = await helper.GetSyncResponse(currentEvent.ID, team.ID, puzzleId, query_puzzle_ids, min_solve_count, annotations, last_sync_time, false); return(Json(response)); }
public async Task <IActionResult> OnGetAsync(int puzzleId) { Puzzle = await _context.Puzzles.Where(p => p.ID == puzzleId).FirstOrDefaultAsync(); if (Puzzle == null) { return(NotFound("Puzzle does not exist.")); } Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); int totalWeight = 0; if (string.IsNullOrEmpty(Puzzle.PieceMetaTagFilter)) { // NOTE: By default, puzzles are filtered by looking at score. // If you want a more sophisticated filtering mechanism, set a PieceMetaTagFilter (which is a Regex) // and set tags on all puzzles you wish to consider. IQueryable <PuzzleStatePerTeam> pieceQuery = _context.PuzzleStatePerTeam.Where(ps => ps.Team == team && ps.SolvedTime != null && ps.Puzzle.SolveValue >= 10 && ps.Puzzle.SolveValue < 50); if (Puzzle.PieceMetaUsage == PieceMetaUsage.GroupOnly) { pieceQuery = pieceQuery.Where(ps => ps.Puzzle.Group == Puzzle.Group); } else if (Puzzle.PieceMetaUsage != PieceMetaUsage.EntireEvent) { return(NotFound("Puzzle does not support the simple meta view.")); } totalWeight = await pieceQuery.SumAsync(ps => ps.Puzzle.PieceWeight ?? 1); } else { var puzData = await(from pspt in _context.PuzzleStatePerTeam join puz in _context.Puzzles on pspt.PuzzleID equals puz.ID where pspt.Team == team && pspt.SolvedTime != null && (Puzzle.PieceMetaUsage != PieceMetaUsage.GroupOnly || puz.Group == Puzzle.Group) select new { PieceTag = puz.PieceTag, PieceWeight = puz.PieceWeight }).ToListAsync(); Regex filterRegex = new Regex(Puzzle.PieceMetaTagFilter); foreach (var puz in puzData) { if (!string.IsNullOrEmpty(puz.PieceTag) && filterRegex.Match(puz.PieceTag)?.Success == true) { totalWeight += puz.PieceWeight ?? 1; } } } EarnedPieces = await _context.Pieces.Where(p => p.PuzzleID == puzzleId && p.ProgressLevel <= totalWeight).OrderBy(p => p.ProgressLevel).ThenBy(p => p.Contents).ToListAsync(); return(Page()); }
public async Task <IActionResult> OnGetAsync() { if (EventRole == EventRole.admin) { Puzzles = await _context.Puzzles.Where(p => p.Event == Event).ToListAsync(); } else { Puzzles = await UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser).ToListAsync(); } return(Page()); }
public async Task <int> GetTeamId() { if (EventRole == ModelBases.EventRole.play) { Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); return(team != null ? team.ID : -1); } else { return(-1); } }
private async Task SetupContext(int puzzleId) { Team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); Puzzle = await _context.Puzzles.Where( (p) => p.ID == puzzleId).FirstOrDefaultAsync(); PuzzleState = await(PuzzleStateHelper .GetFullReadOnlyQuery( _context, Event, Puzzle, Team)) .FirstAsync(); SubmissionViews = await(from submission in _context.Submissions join user in _context.PuzzleUsers on submission.Submitter equals user join r in _context.Responses on submission.Response equals r into responses from response in responses.DefaultIfEmpty() where submission.Team == Team && submission.Puzzle == Puzzle orderby submission.TimeSubmitted select new SubmissionView() { Submission = submission, Response = response, SubmitterName = user.Name, FreeformReponse = submission.FreeformResponse, IsFreeform = Puzzle.IsFreeform }).ToListAsync(); Submissions = new List <Submission>(SubmissionViews.Count); foreach (SubmissionView submissionView in SubmissionViews) { Submissions.Add(submissionView.Submission); } PuzzlesCausingGlobalLockout = await PuzzleStateHelper.PuzzlesCausingGlobalLockout(_context, Event, Team).ToListAsync(); if (PuzzleState.SolvedTime != null) { if (!Puzzle.IsFreeform && Submissions?.Count > 0) { AnswerToken = Submissions.Last().SubmissionText; } else { AnswerToken = "(marked as solved by admin or author)"; } } }
public async Task <IActionResult> OnGetAsync(SortOrder?sort) { Sort = sort; if (EventRole == EventRole.admin) { HintViews = await _context.Hints.Where(h => h.Puzzle.Event == Event) .Select(h => new HintView { PuzzleId = h.Puzzle.ID, PuzzleName = h.Puzzle.Name, Description = h.Description, Content = h.Content, Cost = h.Cost }) .ToListAsync(); } else { HintViews = await(from p in UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser) join h in _context.Hints on p equals h.Puzzle select new HintView { PuzzleId = p.ID, PuzzleName = p.Name, Description = h.Description, Content = h.Content, Cost = h.Cost }) .ToListAsync(); } switch (sort ?? DefaultSort) { case SortOrder.PuzzleAscending: HintViews.Sort((a, b) => a.PuzzleName.CompareTo(b.PuzzleName)); break; case SortOrder.PuzzleDescending: HintViews.Sort((a, b) => - a.PuzzleName.CompareTo(b.PuzzleName)); break; case SortOrder.DescriptionAscending: HintViews.Sort((a, b) => a.Description.CompareTo(b.Description)); break; case SortOrder.DescriptionDescending: HintViews.Sort((a, b) => - a.Description.CompareTo(b.Description)); break; case SortOrder.CostAscending: HintViews.Sort((a, b) => Math.Abs(a.Cost).CompareTo(Math.Abs(b.Cost))); break; case SortOrder.CostDescending: HintViews.Sort((a, b) => - Math.Abs(a.Cost).CompareTo(Math.Abs(b.Cost))); break; } return(Page()); }
public async Task <IActionResult> OnGetAsync(string eventId, int puzzleId) { Event currentEvent = await EventHelper.GetEventFromEventId(_context, eventId); if (currentEvent == null) { return(Unauthorized()); } EventId = currentEvent.ID; PuzzleUser user = LoggedInUser; if (user == null) { return(Unauthorized()); } Team team = await UserEventHelper.GetTeamForPlayer(_context, currentEvent, user); if (team == null) { return(Unauthorized()); } Puzzle kitchenSyncPuzzle = await _context.Puzzles.FirstOrDefaultAsync(m => m.Name == "Kitchen Sync" && m.Event.ID == EventId); KitchenSyncId = kitchenSyncPuzzle.ID; Puzzle heatSyncPuzzle = await _context.Puzzles.FirstOrDefaultAsync(m => m.Name == "Heat Sync" && m.Event.ID == EventId); HeatSyncId = heatSyncPuzzle.ID; Puzzle lipSyncPuzzle = await _context.Puzzles.FirstOrDefaultAsync(m => m.Name == "Lip Sync" && m.Event.ID == EventId); LipSyncId = lipSyncPuzzle.ID; Puzzle syncTestMetapuzzle = await _context.Puzzles.FirstOrDefaultAsync(m => m.Name == "Sync Test" && m.Event.ID == EventId); MetapuzzleId = syncTestMetapuzzle.ID; var helper = new SyncHelper(_context); List <int> query_puzzle_ids = new List <int> { KitchenSyncId, HeatSyncId, LipSyncId }; var response = await helper.GetSyncResponse(currentEvent.ID, team.ID, puzzleId, query_puzzle_ids, 0, null, null); var responseSerialized = JsonConvert.SerializeObject(response); InitialSyncString = HttpUtility.JavaScriptStringEncode(responseSerialized); return(Page()); }
public async Task <IActionResult> OnGet(int puzzleId) { PrereqPuzzleId = puzzleId; Puzzle puzzle = await(from puzz in _context.Puzzles where puzz.ID == puzzleId select puzz).FirstOrDefaultAsync(); if (puzzle == null) { return(NotFound()); } // Restrict this page to whistle stop non-puzzles if (puzzle.MinutesOfEventLockout == 0 || puzzle.IsPuzzle) { return(NotFound()); } Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); var puzzleStateQuery = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, null, team); PuzzleStatePerTeam state = await(from pspt in puzzleStateQuery where pspt.PuzzleID == puzzle.ID select pspt).FirstOrDefaultAsync(); if (state == null) { return(NotFound()); } // Only move forward if the puzzle is open and unsolved if (state.UnlockedTime == null || state.SolvedTime != null) { return(NotFound()); } CurrentPuzzle = puzzle; // Treat all locked puzzles where this is a prerequisite as puzzles that can be unlocked PuzzleOptions = await(from pspt in puzzleStateQuery join prereq in _context.Prerequisites on pspt.PuzzleID equals prereq.PuzzleID where prereq.PrerequisiteID == puzzle.ID && pspt.SolvedTime == null && pspt.UnlockedTime == null select prereq.Puzzle).ToListAsync(); return(Page()); }
public async Task <IActionResult> OnGetAsync(int?puzzleId) { IQueryable <Submission> submissionsQ = null; if (puzzleId == null) { if (EventRole == EventRole.admin) { submissionsQ = _context.Submissions.Where((s) => s.Puzzle.Event == Event); } else { submissionsQ = UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser) .SelectMany((p) => p.Submissions); } } else { Puzzle = await _context.Puzzles.Where(m => m.ID == puzzleId).FirstOrDefaultAsync(); if (EventRole == EventRole.author && !await UserEventHelper.IsAuthorOfPuzzle(_context, Puzzle, LoggedInUser)) { return(Forbid()); } submissionsQ = _context.Submissions.Where((s) => s.Puzzle != null && s.Puzzle.ID == puzzleId); } IQueryable <SubmissionCountsView> incorrectCounts = submissionsQ .Where(submission => submission.Response == null) .GroupBy(submission => Tuple.Create <string, int> ( submission.SubmissionText, submission.PuzzleID )) .Select((submissions) => new SubmissionCountsView() { PuzzleID = submissions.First().PuzzleID, PuzzleName = submissions.First().Puzzle.Name, SubmissionText = submissions.Key.Item1, NumberOfTimesSubmitted = submissions.Count() }) .OrderByDescending(incorrectCountView => incorrectCountView.NumberOfTimesSubmitted); SubmissionCounts = incorrectCounts.ToAsyncEnumerable().ToEnumerable(); return(Page()); }
public async Task <IActionResult> OnGetAsync() { IQueryable <Puzzle> query; if (EventRole == EventRole.admin) { query = _context.Puzzles.Where(p => p.Event == Event); } else { query = UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser); } Puzzles = await query.OrderBy((p) => p.Group).ThenBy((p) => p.OrderInGroup).ThenBy((p) => p.Name).ToListAsync(); return(Page()); }
public async Task <IActionResult> OnGetAsync(int?puzzleId) { IQueryable <Submission> submissionsQ = null; if (puzzleId == null) { if (EventRole == EventRole.admin) { submissionsQ = _context.Submissions.Where((s) => s.Puzzle.Event == Event); } else { submissionsQ = UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser) .SelectMany((p) => p.Submissions); } } else { Puzzle = await _context.Puzzles.Where(m => m.ID == puzzleId).FirstOrDefaultAsync(); if (EventRole == EventRole.author && !await UserEventHelper.IsAuthorOfPuzzle(_context, Puzzle, LoggedInUser)) { return(Forbid()); } submissionsQ = _context.Submissions.Where((s) => s.Puzzle != null && s.Puzzle.ID == puzzleId); } SubmissionCounts = await(from submission in submissionsQ where submission.Response == null && !submission.Puzzle.IsFreeform group submission by new { submission.PuzzleID, submission.SubmissionText, submission.Puzzle.Name } into submissionCounts orderby submissionCounts.Count() descending select new SubmissionCountsView { PuzzleID = submissionCounts.Key.PuzzleID, PuzzleName = submissionCounts.Key.Name, SubmissionText = submissionCounts.Key.SubmissionText, NumberOfTimesSubmitted = submissionCounts.Count() } ).ToListAsync(); return(Page()); }
public async Task <IActionResult> OnGet(int puzzleId) { Puzzle puzzle = await(from puzz in _context.Puzzles where puzz.ID == puzzleId select puzz).FirstOrDefaultAsync(); if (puzzle == null) { return(NotFound()); } // Restrict to the Backstage metapuzzle if (puzzle.Group != "Backstage") { return(NotFound()); } if (puzzle.SolveValue != 50) { return(NotFound()); } Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); var puzzleQuery = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, null, team, null); var solvedBackstagePuzzles = await(from PuzzleStatePerTeam pspt in puzzleQuery where pspt.SolvedTime != null && pspt.UnlockedTime != null && pspt.Puzzle.IsPuzzle && pspt.Puzzle.Group == "Backstage" select new BackstageSolveStatus() { PuzzleName = pspt.Puzzle.Name, SolveTime = (int)(pspt.SolvedTime.Value - pspt.UnlockedTime.Value).TotalSeconds }).ToListAsync(); string json = JsonConvert.SerializeObject(solvedBackstagePuzzles); string escapedJson = Uri.EscapeDataString(json); byte[] macKey = Encoding.ASCII.GetBytes(puzzle.Description); HMACSHA1 hasher = new HMACSHA1(macKey); byte[] jsonBytes = Encoding.ASCII.GetBytes(escapedJson); byte[] mac = hasher.ComputeHash(jsonBytes); string macString = Convert.ToBase64String(mac).Replace('/', '_').Replace('+', '.'); return(Redirect($"https://ph20backstage.azurewebsites.net/openingnight/{macString}/{escapedJson}")); }
public async Task <IActionResult> OnGetAsync(string eventId, int puzzleId) { Event currentEvent = await EventHelper.GetEventFromEventId(_context, eventId); if (currentEvent == null) { return(Unauthorized()); } EventId = currentEvent.ID; PuzzleUser user = LoggedInUser; if (user == null) { return(Unauthorized()); } Team team = await UserEventHelper.GetTeamForPlayer(_context, currentEvent, user); if (team == null) { return(Unauthorized()); } Puzzle thisPuzzle = await _context.Puzzles.FirstOrDefaultAsync(m => m.ID == puzzleId && m.Event.ID == EventId); if (thisPuzzle == null || !thisPuzzle.Name.Equals("The Play That Goes Wrong")) { return(Unauthorized()); } MetapuzzleId = puzzleId; var helper = new SyncHelper(_context); List <int> query_puzzle_ids = Enumerable.Range(MetapuzzleId - 10, 11).ToList(); var response = await helper.GetSyncResponse(currentEvent.ID, team.ID, puzzleId, query_puzzle_ids, 0, null, null); var responseSerialized = JsonConvert.SerializeObject(response); InitialSyncString = HttpUtility.JavaScriptStringEncode(responseSerialized); return(Page()); }
public static async Task IsPuzzleAuthorCheck(AuthorizationHandlerContext authContext, PuzzleServerContext dbContext, UserManager <IdentityUser> userManager, IAuthorizationRequirement requirement) { PuzzleUser puzzleUser = await PuzzleUser.GetPuzzleUserForCurrentUser(dbContext, authContext.User, userManager); Puzzle puzzle = await AuthorizationHelper.GetPuzzleFromContext(authContext); Event thisEvent = await AuthorizationHelper.GetEventFromContext(authContext); EventRole role = AuthorizationHelper.GetEventRoleFromContext(authContext); if (thisEvent != null && role == EventRole.author && await UserEventHelper.IsAuthorOfPuzzle(dbContext, puzzle, puzzleUser)) { authContext.Succeed(requirement); } if (puzzle != null) { dbContext.Entry(puzzle).State = EntityState.Detached; } }
public static async Task IsPlayerOnTeamCheck(AuthorizationHandlerContext authContext, PuzzleServerContext dbContext, UserManager <IdentityUser> userManager, IAuthorizationRequirement requirement) { PuzzleUser puzzleUser = await PuzzleUser.GetPuzzleUserForCurrentUser(dbContext, authContext.User, userManager); Team team = await AuthorizationHelper.GetTeamFromContext(authContext); Event thisEvent = await AuthorizationHelper.GetEventFromContext(authContext); EventRole role = AuthorizationHelper.GetEventRoleFromContext(authContext); if (thisEvent != null && role == EventRole.play) { Team userTeam = await UserEventHelper.GetTeamForPlayer(dbContext, thisEvent, puzzleUser); if (userTeam != null && userTeam.ID == team.ID) { authContext.Succeed(requirement); } } }
private async Task PopulateUIAsync() { IQueryable <PuzzleUser> currentAuthorsQ = _context.PuzzleAuthors.Where(m => m.Puzzle == Puzzle).Select(m => m.Author); IQueryable <PuzzleUser> potentialAuthorsQ = _context.EventAuthors.Where(m => m.Event == Event).Select(m => m.Author).Except(currentAuthorsQ); CurrentAuthors = await currentAuthorsQ.OrderBy(p => p.Name).ToListAsync(); PotentialAuthors = await potentialAuthorsQ.OrderBy(p => p.Name).ToListAsync(); IQueryable <Puzzle> allVisiblePuzzles; IQueryable <Puzzle> allVisiblePuzzlesAndGlobalPrerequisites; if (EventRole == EventRole.admin) { allVisiblePuzzles = allVisiblePuzzlesAndGlobalPrerequisites = _context.Puzzles.Where(m => m.Event == Event && m != Puzzle); } else { IQueryable <Puzzle> authorPuzzles = UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser); Puzzle[] thisPuzzle = new Puzzle[] { Puzzle }; allVisiblePuzzles = authorPuzzles.Except(thisPuzzle); allVisiblePuzzlesAndGlobalPrerequisites = authorPuzzles.Union(_context.Puzzles.Where(m => m.Event == Event && m.IsGloballyVisiblePrerequisite)).Except(thisPuzzle); } IQueryable <Puzzle> currentPrerequisitesQ = _context.Prerequisites.Where(m => m.Puzzle == Puzzle).Select(m => m.Prerequisite); IQueryable <Puzzle> potentialPrerequitesQ = allVisiblePuzzlesAndGlobalPrerequisites.Except(currentPrerequisitesQ); CurrentPrerequisites = await currentPrerequisitesQ.OrderBy(p => p.Name).ToListAsync(); PotentialPrerequisites = await potentialPrerequitesQ.OrderBy(p => p.Name).ToListAsync(); IQueryable <Puzzle> currentPrerequisitesOfQ = _context.Prerequisites.Where(m => m.Prerequisite == Puzzle).Select(m => m.Puzzle); IQueryable <Puzzle> potentialPrerequitesOfQ = allVisiblePuzzles.Except(currentPrerequisitesOfQ); CurrentPrerequisitesOf = await currentPrerequisitesOfQ.OrderBy(p => p.Name).ToListAsync(); PotentialPrerequisitesOf = await potentialPrerequitesOfQ.OrderBy(p => p.Name).ToListAsync(); }
private async Task SetupContext(int puzzleId) { Team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); Puzzle = await _context.Puzzles.Where( (p) => p.ID == puzzleId).FirstOrDefaultAsync(); PuzzleState = await(PuzzleStateHelper .GetFullReadOnlyQuery( _context, Event, Puzzle, Team)) .FirstOrDefaultAsync(); Submissions = await _context.Submissions.Where( (s) => s.Team == Team && s.Puzzle == Puzzle) .OrderBy(submission => submission.TimeSubmitted) .ToListAsync(); PuzzlesCausingGlobalLockout = await PuzzleStateHelper.PuzzlesCausingGlobalLockout(_context, Event, Team).ToListAsync(); }
public async Task PlayerCanSeePuzzleCheck(AuthorizationHandlerContext authContext, IAuthorizationRequirement requirement) { PuzzleUser puzzleUser = await PuzzleUser.GetPuzzleUserForCurrentUser(dbContext, authContext.User, userManager); Puzzle puzzle = await GetPuzzleFromRoute(); Event thisEvent = await GetEventFromRoute(); if (thisEvent != null && puzzle != null) { Team team = await UserEventHelper.GetTeamForPlayer(dbContext, thisEvent, puzzleUser); if (team != null) { IQueryable <PuzzleStatePerTeam> statesQ = PuzzleStateHelper.GetFullReadOnlyQuery(dbContext, thisEvent, puzzle, team); if (statesQ.FirstOrDefault().UnlockedTime != null || thisEvent.AreAnswersAvailableNow) { authContext.Succeed(requirement); } } } }
public async Task <IActionResult> OnGetAsync(int puzzleId) { Puzzle = await _context.Puzzles.Where(p => p.ID == puzzleId).FirstOrDefaultAsync(); if (Puzzle == null) { return(NotFound("Puzzle does not exist.")); } Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); int solvedPuzzleCount = 0; switch (Puzzle.PieceMetaUsage) { // TODO: Metas are filtered out by looking at score. // Ideally each puzzle would have a flag saying whether it counts or not, // but changing the database is more risk than we need right now. case PieceMetaUsage.EntireEvent: solvedPuzzleCount = await _context.PuzzleStatePerTeam.Where(ps => ps.Team == team && ps.SolvedTime != null && ps.Puzzle.SolveValue >= 10 && ps.Puzzle.SolveValue < 50).CountAsync(); break; case PieceMetaUsage.GroupOnly: solvedPuzzleCount = await _context.PuzzleStatePerTeam.Where(ps => ps.Team == team && ps.SolvedTime != null && ps.Puzzle.SolveValue >= 10 && ps.Puzzle.SolveValue < 50 && ps.Puzzle.Group == Puzzle.Group).CountAsync(); break; default: return(NotFound("Puzzle does not support the simple meta view.")); } EarnedPieces = await _context.Pieces.Where(p => p.PuzzleID == puzzleId && p.ProgressLevel <= solvedPuzzleCount).OrderBy(p => p.ProgressLevel).ThenBy(p => p.Contents).ToListAsync(); return(Page()); }
/// <summary> /// Get a writable query of puzzle state. In the course of constructing the query, it will instantiate any state records that are missing on the server. /// </summary> /// <param name="context">The puzzle DB context</param> /// <param name="eventObj">The event we are querying from</param> /// <param name="puzzle">The puzzle; if null, get all puzzles in the event.</param> /// <param name="team">The team; if null, get all the teams in the event.</param> /// <returns>A query of PuzzleStatePerTeam objects that can be sorted and instantiated, but you can't edit the results.</returns> private static async Task <IQueryable <PuzzleStatePerTeam> > GetFullReadWriteQueryAsync(PuzzleServerContext context, Event eventObj, Puzzle puzzle, Team team, PuzzleUser author) { if (context == null) { throw new ArgumentNullException("Context required."); } if (eventObj == null) { throw new ArgumentNullException("Event required."); } if (puzzle != null && team != null) { PuzzleStatePerTeam state = await context.PuzzleStatePerTeam.Where(s => s.Puzzle == puzzle && s.Team == team).FirstOrDefaultAsync(); if (state == null) { context.PuzzleStatePerTeam.Add(new DataModel.PuzzleStatePerTeam() { Puzzle = puzzle, Team = team }); } } else if (puzzle != null) { IQueryable <int> teamIdsQ = context.Teams.Where(p => p.Event == eventObj).Select(p => p.ID); IQueryable <int> puzzleStateTeamIdsQ = context.PuzzleStatePerTeam.Where(s => s.Puzzle == puzzle).Select(s => s.TeamID); List <int> teamIdsWithoutState = await teamIdsQ.Except(puzzleStateTeamIdsQ).ToListAsync(); if (teamIdsWithoutState.Count > 0) { for (int i = 0; i < teamIdsWithoutState.Count; i++) { context.PuzzleStatePerTeam.Add(new DataModel.PuzzleStatePerTeam() { Puzzle = puzzle, TeamID = teamIdsWithoutState[i] }); } } } else if (team != null) { IQueryable <int> puzzleIdsQ = author == null?context.Puzzles.Where(p => p.Event == eventObj).Select(p => p.ID) : UserEventHelper.GetPuzzlesForAuthorAndEvent(context, eventObj, author).Select(p => p.ID); IQueryable <int> puzzleStatePuzzleIdsQ = context.PuzzleStatePerTeam.Where(s => s.Team == team).Select(s => s.PuzzleID); List <int> puzzleIdsWithoutState = await puzzleIdsQ.Except(puzzleStatePuzzleIdsQ).ToListAsync(); if (puzzleIdsWithoutState.Count > 0) { for (int i = 0; i < puzzleIdsWithoutState.Count; i++) { context.PuzzleStatePerTeam.Add(new DataModel.PuzzleStatePerTeam() { Team = team, PuzzleID = puzzleIdsWithoutState[i] }); } } } else if (puzzle == null && team == null) { throw new NotImplementedException("Full event query is NYI and may never be needed"); } await context.SaveChangesAsync(); // query below will not return these unless we save // now this query is no longer sparse because we just filled it all out! return(GetSparseQuery(context, eventObj, puzzle, team, author)); }
/// <summary> /// Get a read-only query of puzzle state. You won't be able to write to these values, but the query will be resilient to state records that are missing on the server. /// </summary> /// <param name="context">The puzzle DB context</param> /// <param name="eventObj">The event we are querying from</param> /// <param name="puzzle">The puzzle; if null, get all puzzles in the event.</param> /// <param name="team">The team; if null, get all the teams in the event.</param> /// <returns>A query of PuzzleStatePerTeam objects that can be sorted and instantiated, but you can't edit the results.</returns> public static IQueryable <PuzzleStatePerTeam> GetFullReadOnlyQuery(PuzzleServerContext context, Event eventObj, Puzzle puzzle, Team team, PuzzleUser author = null) { if (context == null) { throw new ArgumentNullException("Context required."); } if (eventObj == null) { throw new ArgumentNullException("Event required."); } if (puzzle != null && team != null) { return(context.PuzzleStatePerTeam .Where(state => state.Puzzle == puzzle && state.Team == team) .DefaultIfEmpty(new DataModel.PuzzleStatePerTeam() { Puzzle = puzzle, PuzzleID = puzzle.ID, Team = team, TeamID = team.ID })); } #pragma warning disable IDE0031 // despite the compiler message, "teamstate?.UnlockedTime", etc does not compile here if (puzzle != null) { IQueryable <Team> teams = context.Teams.Where(t => t.Event == eventObj); IQueryable <PuzzleStatePerTeam> states = context.PuzzleStatePerTeam.Where(state => state.Puzzle == puzzle); return(from t in teams join state in states on t.ID equals state.TeamID into tmp from teamstate in tmp.DefaultIfEmpty() select new PuzzleStatePerTeam { Puzzle = puzzle, PuzzleID = puzzle.ID, Team = t, TeamID = t.ID, UnlockedTime = teamstate == null ? null : teamstate.UnlockedTime, SolvedTime = teamstate == null ? null : teamstate.SolvedTime, Printed = teamstate == null ? false : teamstate.Printed, Notes = teamstate == null ? null : teamstate.Notes, IsEmailOnlyMode = teamstate == null ? false : teamstate.IsEmailOnlyMode }); } if (team != null) { IQueryable <Puzzle> puzzles = author == null?context.Puzzles.Where(p => p.Event == eventObj) : UserEventHelper.GetPuzzlesForAuthorAndEvent(context, eventObj, author); IQueryable <PuzzleStatePerTeam> states = context.PuzzleStatePerTeam.Where(state => state.Team == team); return(from p in puzzles join state in states on p.ID equals state.PuzzleID into tmp from teamstate in tmp.DefaultIfEmpty() select new PuzzleStatePerTeam { Puzzle = p, PuzzleID = p.ID, Team = team, TeamID = team.ID, UnlockedTime = teamstate == null ? null : teamstate.UnlockedTime, SolvedTime = teamstate == null ? null : teamstate.SolvedTime, Printed = teamstate == null ? false : teamstate.Printed, Notes = teamstate == null ? null : teamstate.Notes, IsEmailOnlyMode = teamstate == null ? false : teamstate.IsEmailOnlyMode }); } #pragma warning restore IDE0031 throw new NotImplementedException("Full event query is NYI and may never be needed; use the sparse one"); }
public async Task <IActionResult> OnGetAsync(int?puzzleId, int?teamId, SortOrder?sort) { Sort = sort; if (puzzleId == null) { if (EventRole == EventRole.admin) { IQueryable <Submission> submissionsQ; if (teamId == null) { submissionsQ = _context.Submissions.Where((s) => s.Puzzle.Event == Event); } else { submissionsQ = _context.Submissions.Where((s) => s.Team.ID == teamId); } Submissions = await submissionsQ .Select((s) => new SubmissionView { SubmitterName = s.Submitter.Name, PuzzleID = s.Puzzle.ID, PuzzleName = s.Puzzle.Name, TeamID = s.Team.ID, TeamName = s.Team.Name, SubmissionText = s.SubmissionText, ResponseText = s.Response == null ? null : s.Response.ResponseText, TimeSubmitted = s.TimeSubmitted }) .ToListAsync(); } else { var submissions = new List <SubmissionView>(); if (teamId == null) { List <IEnumerable <SubmissionView> > submissionsList = await UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser) .Select((p) => p.Submissions.Select((s) => new SubmissionView { SubmitterName = s.Submitter.Name, PuzzleID = s.Puzzle.ID, PuzzleName = s.Puzzle.Name, TeamID = s.Team.ID, TeamName = s.Team.Name, SubmissionText = s.SubmissionText, ResponseText = s.Response == null ? null : s.Response.ResponseText, TimeSubmitted = s.TimeSubmitted })) .ToListAsync(); foreach (var list in submissionsList) { submissions.AddRange(list); } } else { List <IEnumerable <SubmissionView> > submissionsList = await UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser) .Select((p) => p.Submissions.Where((s) => s.Team.ID == teamId).Select((s) => new SubmissionView { SubmitterName = s.Submitter.Name, PuzzleID = s.Puzzle.ID, PuzzleName = s.Puzzle.Name, TeamID = s.Team.ID, TeamName = s.Team.Name, SubmissionText = s.SubmissionText, ResponseText = s.Response == null ? null : s.Response.ResponseText, TimeSubmitted = s.TimeSubmitted })) .ToListAsync(); foreach (var list in submissionsList) { submissions.AddRange(list); } } Submissions = submissions; } } else { Puzzle = await _context.Puzzles.Where(m => m.ID == puzzleId).FirstOrDefaultAsync(); if (EventRole == EventRole.author && !await UserEventHelper.IsAuthorOfPuzzle(_context, Puzzle, LoggedInUser)) { return(Forbid()); } IQueryable <Submission> submissionsQ; if (teamId == null) { submissionsQ = _context.Submissions.Where((s) => s.Puzzle != null && s.Puzzle.ID == puzzleId); } else { submissionsQ = _context.Submissions.Where((s) => s.Puzzle != null && s.Puzzle.ID == puzzleId && s.Team.ID == teamId); } Submissions = await submissionsQ .Select((s) => new SubmissionView { SubmitterName = s.Submitter.Name, PuzzleID = s.Puzzle.ID, PuzzleName = s.Puzzle.Name, TeamID = s.Team.ID, TeamName = s.Team.Name, SubmissionText = s.SubmissionText, ResponseText = s.Response == null ? null : s.Response.ResponseText, TimeSubmitted = s.TimeSubmitted }) .ToListAsync(); } if (teamId != null) { Team = await _context.Teams.Where(m => m.ID == teamId).FirstOrDefaultAsync(); } switch (sort ?? DefaultSort) { case SortOrder.PlayerAscending: Submissions.Sort((a, b) => a.SubmitterName.CompareTo(b.SubmitterName)); break; case SortOrder.PlayerDescending: Submissions.Sort((a, b) => - a.SubmitterName.CompareTo(b.SubmitterName)); break; case SortOrder.TeamAscending: Submissions.Sort((a, b) => a.TeamName.CompareTo(b.TeamName)); break; case SortOrder.TeamDescending: Submissions.Sort((a, b) => - a.TeamName.CompareTo(b.TeamName)); break; case SortOrder.PuzzleAscending: Submissions.Sort((a, b) => a.PuzzleName.CompareTo(b.PuzzleName)); break; case SortOrder.PuzzleDescending: Submissions.Sort((a, b) => - a.PuzzleName.CompareTo(b.PuzzleName)); break; case SortOrder.ResponseAscending: Submissions.Sort((a, b) => a.ResponseText.CompareTo(b.ResponseText)); break; case SortOrder.ResponseDescending: Submissions.Sort((a, b) => - a.ResponseText.CompareTo(b.ResponseText)); break; case SortOrder.SubmissionAscending: Submissions.Sort((a, b) => a.SubmissionText.CompareTo(b.SubmissionText)); break; case SortOrder.SubmissionDescending: Submissions.Sort((a, b) => - a.SubmissionText.CompareTo(b.SubmissionText)); break; case SortOrder.TimeAscending: Submissions.Sort((a, b) => a.TimeSubmitted.CompareTo(b.TimeSubmitted)); break; case SortOrder.TimeDescending: Submissions.Sort((a, b) => - a.TimeSubmitted.CompareTo(b.TimeSubmitted)); break; } return(Page()); }
public async Task <IActionResult> OnPostUnlock(int puzzleId, int unlockId) { Puzzle puzzle = await(from puzz in _context.Puzzles where puzz.ID == puzzleId select puzz).FirstOrDefaultAsync(); if (puzzle == null) { return(NotFound()); } // Restrict this page to whistle stop non-puzzles if (puzzle.MinutesOfEventLockout == 0 || puzzle.IsPuzzle) { return(NotFound()); } Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); using (var transaction = _context.Database.BeginTransaction()) { var puzzleStateQuery = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, null, team); PuzzleStatePerTeam prereqState = await(from pspt in puzzleStateQuery where pspt.PuzzleID == puzzle.ID select pspt).FirstOrDefaultAsync(); if (prereqState == null) { return(NotFound()); } // Only move forward if the prereq is open and unsolved if (prereqState.UnlockedTime == null || prereqState.SolvedTime != null) { return(NotFound("Your team has already chosen and can't choose again")); } PuzzleStatePerTeam unlockState = await(from pspt in puzzleStateQuery where pspt.PuzzleID == unlockId select pspt).FirstOrDefaultAsync(); if (unlockState == null) { return(NotFound()); } // The chosen puzzle must be locked (and unsolved) if (unlockState.UnlockedTime != null || unlockState.SolvedTime != null) { return(NotFound("You've already chosen this puzzle")); } // Ensure the puzzle is actually one of the unlock options Prerequisites prereq = await(from pre in _context.Prerequisites where pre.PrerequisiteID == puzzleId && pre.PuzzleID == unlockId select pre).FirstOrDefaultAsync(); if (prereq == null) { return(NotFound()); } await PuzzleStateHelper.SetSolveStateAsync(_context, Event, puzzle, team, DateTime.UtcNow); await PuzzleStateHelper.SetUnlockStateAsync(_context, Event, unlockState.Puzzle, team, DateTime.UtcNow); transaction.Commit(); } Puzzle puzzleToUnlock = await(from p in _context.Puzzles where p.ID == unlockId select p).FirstOrDefaultAsync(); string puzzleUrl; if (puzzleToUnlock.CustomURL != null) { puzzleUrl = PuzzleHelper.GetFormattedUrl(puzzleToUnlock, Event.ID); } else { puzzleUrl = puzzleToUnlock.PuzzleFile.UrlString; } return(Redirect(puzzleUrl)); }
public async Task <IActionResult> Index(string eventId, int puzzleId) { Event currentEvent = await EventHelper.GetEventFromEventId(context, eventId); if (currentEvent == null) { return(Content("ERROR: That event doesn't exist")); } PuzzleUser user = await PuzzleUser.GetPuzzleUserForCurrentUser(context, User, userManager); if (user == null) { return(Content("ERROR: You aren't logged in")); } Team team = await UserEventHelper.GetTeamForPlayer(context, currentEvent, user); if (team == null) { return(Content("ERROR: You're not on a team")); } Puzzle thisPuzzle = await context.Puzzles.FirstOrDefaultAsync(m => m.ID == puzzleId); if (thisPuzzle == null) { return(Content("ERROR: That's not a valid puzzle ID")); } if (!currentEvent.AreAnswersAvailableNow) { var puzzleState = await(from state in context.PuzzleStatePerTeam where state.Puzzle == thisPuzzle && state.Team == team select state).FirstOrDefaultAsync(); if (puzzleState == null || puzzleState.UnlockedTime == null) { return(Content("ERROR: You haven't unlocked this puzzle yet")); } } // Find the material file with the latest-alphabetically ShortName that contains the substring "client". var materialFile = await(from f in context.ContentFiles where f.Puzzle == thisPuzzle && f.FileType == ContentFileType.PuzzleMaterial && f.ShortName.Contains("client") orderby f.ShortName descending select f).FirstOrDefaultAsync(); if (materialFile == null) { return(Content("ERROR: There's no sync client registered for this puzzle")); } var materialUrl = materialFile.Url; // Start doing a sync asynchronously while we download the file contents. var helper = new SyncHelper(context); Task <Dictionary <string, object> > responseTask = helper.GetSyncResponse(currentEvent.ID, team.ID, puzzleId, null, 0, null, null, true); // Download that material file. string fileContents; using (var wc = new System.Net.WebClient()) { fileContents = await wc.DownloadStringTaskAsync(materialUrl); } // Wait for the asynchronous sync we started earlier to complete, then serialize // its results and use them to replace the substring "@SYNC" where it appears // in the downloaded file contents. Dictionary <string, object> response = await responseTask; var responseSerialized = JsonConvert.SerializeObject(response); var initialSyncString = HttpUtility.JavaScriptStringEncode(responseSerialized); fileContents = fileContents.Replace("@SYNC", initialSyncString); // Return the file contents to the user. return(Content(fileContents, "text/html")); }
public async Task OnGetAsync(SortOrder?sort, PuzzleStateFilter?stateFilter) { this.Sort = sort; this.StateFilter = stateFilter; this.CurrentTeam = (await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser)); Dictionary <int, string> teamNameLookup = new Dictionary <int, string>(); // build an ID-to-name mapping to improve perf var names = await _context.Teams.Where(t => t.Event == Event) .Select(t => new { t.ID, t.Name }) .ToListAsync(); names.ForEach(t => teamNameLookup[t.ID] = t.Name); // get the page data: puzzle, solve count, top three fastest var puzzlesData = await PuzzleStateHelper.GetSparseQuery(_context, this.Event, null, null) .Where(s => s.SolvedTime != null && s.Puzzle.IsPuzzle) .GroupBy(state => state.Puzzle) .Select(g => new { Puzzle = g.Key, SolveCount = g.Count(), Fastest = g.OrderBy(s => s.SolvedTime - s.UnlockedTime).Take(3).Select(s => new { s.Team.ID, Time = s.SolvedTime - s.UnlockedTime }), IsSolvedByUserTeam = g.Where(s => s.Team == this.CurrentTeam).Any() }) .OrderByDescending(p => p.SolveCount).ThenBy(p => p.Puzzle.Name) .ToListAsync(); var unlockedData = this.CurrentTeam == null ? null : (new HashSet <int>( await PuzzleStateHelper.GetSparseQuery(_context, this.Event, null, null) .Where(state => state.Team == this.CurrentTeam && state.UnlockedTime != null) .Select(s => s.PuzzleID) .ToListAsync())); var puzzles = new List <PuzzleStats>(puzzlesData.Count); for (int i = 0; i < puzzlesData.Count; i++) { var data = puzzlesData[i]; // For players, we will hide puzzles they have not unlocked yet. if (EventRole == EventRole.play && (unlockedData == null || !unlockedData.Contains(data.Puzzle.ID))) { continue; } var stats = new PuzzleStats() { Puzzle = data.Puzzle, SolveCount = data.SolveCount, SortOrder = i, Fastest = data.Fastest.Select(f => new FastRecord() { ID = f.ID, Name = teamNameLookup[f.ID], Time = f.Time }).ToArray(), IsSolved = data.IsSolvedByUserTeam }; puzzles.Add(stats); } if (this.StateFilter == PuzzleStateFilter.Unsolved) { puzzles = puzzles.Where(stats => !stats.IsSolved).ToList(); } switch (sort ?? DefaultSort) { case SortOrder.RankAscending: puzzles.Sort((rhs, lhs) => (rhs.SortOrder - lhs.SortOrder)); break; case SortOrder.RankDescending: puzzles.Sort((rhs, lhs) => (lhs.SortOrder - rhs.SortOrder)); break; case SortOrder.CountAscending: puzzles.Sort((rhs, lhs) => (rhs.SolveCount - lhs.SolveCount)); break; case SortOrder.CountDescending: puzzles.Sort((rhs, lhs) => (lhs.SolveCount - rhs.SolveCount)); break; case SortOrder.PuzzleAscending: puzzles.Sort((rhs, lhs) => (String.Compare(rhs.Puzzle.Name, lhs.Puzzle.Name, StringComparison.OrdinalIgnoreCase))); break; case SortOrder.PuzzleDescending: puzzles.Sort((rhs, lhs) => (String.Compare(lhs.Puzzle.Name, rhs.Puzzle.Name, StringComparison.OrdinalIgnoreCase))); break; default: throw new ArgumentException($"unknown sort: {sort}"); } this.Puzzles = puzzles; }