-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add file management page for puzzles (#71)
* Add file management page for puzzles
- Loading branch information
Showing
5 changed files
with
301 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
@page "/{eventId}/Puzzles/FileManagement/{id}/{handler?}" | ||
@model ServerCore.Pages.Puzzles.FileManagementModel | ||
|
||
@{ | ||
ViewData["Title"] = "FileManagement"; | ||
} | ||
|
||
<h2>FileManagement</h2> | ||
|
||
<div> | ||
<h4>Puzzle</h4> | ||
<hr /> | ||
<dl class="dl-horizontal"> | ||
<dt> | ||
@Html.DisplayNameFor(model => model.Puzzle.Name) | ||
</dt> | ||
<dd> | ||
@Html.DisplayFor(model => model.Puzzle.Name) | ||
</dd> | ||
|
||
</dl> | ||
<div class="form-group"> | ||
<form method="post" enctype="multipart/form-data"> | ||
<div asp-validation-summary="ModelOnly" class="text-danger"></div> | ||
<input type="hidden" asp-for="Puzzle.ID" /> | ||
<label class="control-label">Puzzle File</label> | ||
<div> | ||
@if (Model.Puzzle.PuzzleFile != null) | ||
{ | ||
@Html.ActionLink(Model.Puzzle.PuzzleFile.ShortName, "Index", "Files", new { eventId = Model.Event.ID, filename = Model.Puzzle.PuzzleFile.ShortName }) | ||
<button type="submit" asp-route-id="@Model.Puzzle.ID" asp-route-fileId="@Model.Puzzle.PuzzleFile.ID" asp-page-handler="Delete">Delete</button> | ||
} | ||
</div> | ||
<input asp-for="PuzzleFile" class="form-control" style="height:auto" /> | ||
<span asp-validation-for="PuzzleFile" class="text-danger"></span> | ||
<hr /> | ||
|
||
<label class="control-label">Answer File</label> | ||
<div> | ||
@if (Model.Puzzle.AnswerFile != null) | ||
{ | ||
@Html.ActionLink(Model.Puzzle.AnswerFile.ShortName, "Index", "Files", new { eventId = Model.Event.ID, filename = Model.Puzzle.AnswerFile.ShortName }) | ||
<button type="submit" asp-route-id="@Model.Puzzle.ID" asp-route-fileId="@Model.Puzzle.AnswerFile.ID" asp-page-handler="Delete">Delete</button> | ||
} | ||
</div> | ||
<input asp-for="AnswerFile" class="form-control" style="height:auto" /> | ||
<span asp-validation-for="AnswerFile" class="text-danger"></span> | ||
<hr /> | ||
|
||
<label class="control-label">Materials</label> | ||
@foreach (var puzzleFile in Model.Puzzle.Materials) | ||
{ | ||
<p> | ||
@Html.ActionLink(puzzleFile.ShortName, "Index", "Files", new { eventId = Model.Event.ID, filename = puzzleFile.ShortName }) | ||
<button type="submit" asp-route-id="@Model.Puzzle.ID" asp-route-fileId="@puzzleFile.ID" asp-page-handler="Delete">Delete</button> | ||
</p> | ||
} | ||
<input asp-for="PuzzleMaterialFiles" class="form-control" style="height:auto" /> | ||
<span asp-validation-for="PuzzleMaterialFiles" class="text-danger"></span> | ||
<hr /> | ||
|
||
<label class="control-label">Solve Token Files</label> | ||
@foreach (var solveFile in Model.Puzzle.SolveTokenFiles) | ||
{ | ||
<p> | ||
@Html.ActionLink(solveFile.ShortName, "Index", "Files", new { eventId = Model.Event.ID, filename = solveFile.ShortName }) | ||
<button type="submit" asp-route-id="@Model.Puzzle.ID" asp-route-fileId="@solveFile.ID" asp-page-handler="Delete">Delete</button> | ||
</p> | ||
} | ||
<input asp-for="SolveTokenFiles" class="form-control" style="height:auto" /> | ||
<span asp-validation-for="SolveTokenFiles" class="text-danger"></span> | ||
|
||
<div class="form-group"> | ||
<input type="submit" value="Upload" class="btn btn-default" /> | ||
</div> | ||
</form> | ||
</div> | ||
</div> | ||
<div> | ||
| | ||
<a asp-page="./Index">Back to List</a> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
using Microsoft.EntityFrameworkCore; | ||
using ServerCore.DataModel; | ||
using ServerCore.ModelBases; | ||
using ServerCore.Models; | ||
|
||
namespace ServerCore.Pages.Puzzles | ||
{ | ||
/// <summary> | ||
/// Page for managing the files associated with a puzzle | ||
/// </summary> | ||
public class FileManagementModel : EventSpecificPageModel | ||
{ | ||
private readonly ServerCore.Models.PuzzleServerContext _context; | ||
|
||
public FileManagementModel(ServerCore.Models.PuzzleServerContext context) | ||
{ | ||
_context = context; | ||
} | ||
|
||
//[BindProperty] | ||
public Puzzle Puzzle { get; set; } | ||
|
||
[BindProperty] | ||
public IFormFile PuzzleFile { get; set; } | ||
|
||
[BindProperty] | ||
public IFormFile AnswerFile { get; set; } | ||
|
||
[BindProperty] | ||
public List<IFormFile> PuzzleMaterialFiles { get; set; } | ||
|
||
[BindProperty] | ||
public List<IFormFile> SolveTokenFiles { get; set; } | ||
|
||
/// <summary> | ||
/// Handler for listing files | ||
/// </summary> | ||
public async Task<IActionResult> OnGetAsync(int id) | ||
{ | ||
Puzzle = await _context.Puzzles.FirstOrDefaultAsync(m => m.ID == id); | ||
|
||
if (Puzzle == null) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
return Page(); | ||
} | ||
|
||
/// <summary> | ||
/// Handler for uploading files | ||
/// </summary> | ||
public async Task<IActionResult> OnPostAsync(int id) | ||
{ | ||
if (!ModelState.IsValid) | ||
{ | ||
return Page(); | ||
} | ||
|
||
Puzzle = await _context.Puzzles.FirstOrDefaultAsync(m => m.ID == id); | ||
|
||
if (Puzzle == null) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
if (PuzzleFile != null) | ||
{ | ||
// Remove previous puzzle files | ||
var oldFiles = from oldFile in _context.ContentFiles | ||
where oldFile.Puzzle == Puzzle && | ||
oldFile.FileType == ContentFileType.Puzzle | ||
select oldFile; | ||
foreach (ContentFile oldFile in oldFiles) | ||
{ | ||
await FileManager.DeleteBlobAsync(oldFile.Url); | ||
_context.ContentFiles.Remove(oldFile); | ||
} | ||
await UploadFileAsync(PuzzleFile, ContentFileType.Puzzle); | ||
} | ||
|
||
if (AnswerFile != null) | ||
{ | ||
// Remove previous answer files | ||
var oldFiles = from oldFile in _context.ContentFiles | ||
where oldFile.Puzzle == Puzzle && | ||
oldFile.FileType == ContentFileType.Answer | ||
select oldFile; | ||
foreach (ContentFile oldFile in oldFiles) | ||
{ | ||
await FileManager.DeleteBlobAsync(oldFile.Url); | ||
_context.ContentFiles.Remove(oldFile); | ||
} | ||
await UploadFileAsync(AnswerFile, ContentFileType.Answer); | ||
} | ||
|
||
if (PuzzleMaterialFiles != null) | ||
{ | ||
foreach (IFormFile materialFile in PuzzleMaterialFiles) | ||
{ | ||
await UploadFileAsync(materialFile, ContentFileType.PuzzleMaterial); | ||
} | ||
} | ||
|
||
if (SolveTokenFiles != null) | ||
{ | ||
foreach (IFormFile solveToken in SolveTokenFiles) | ||
{ | ||
await UploadFileAsync(solveToken, ContentFileType.SolveToken); | ||
} | ||
} | ||
|
||
try | ||
{ | ||
await _context.SaveChangesAsync(); | ||
} | ||
catch (DbUpdateConcurrencyException) | ||
{ | ||
if (!PuzzleExists(Puzzle.ID)) | ||
{ | ||
return NotFound(); | ||
} | ||
else | ||
{ | ||
throw; | ||
} | ||
} | ||
|
||
return RedirectToPage("FileManagement"); | ||
} | ||
|
||
/// <summary> | ||
/// Handler for deleting an uploaded file | ||
/// </summary> | ||
public async Task<IActionResult> OnPostDeleteAsync(int id, int fileId) | ||
{ | ||
Puzzle = await _context.Puzzles.FirstOrDefaultAsync(m => m.ID == id); | ||
|
||
if (Puzzle == null) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
ContentFile fileToDelete = (from file in Puzzle.Contents | ||
where file.ID == fileId | ||
select file).SingleOrDefault(); | ||
|
||
if (fileToDelete == null) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
await FileManager.DeleteBlobAsync(fileToDelete.Url); | ||
Puzzle.Contents.Remove(fileToDelete); | ||
|
||
try | ||
{ | ||
await _context.SaveChangesAsync(); | ||
} | ||
catch (DbUpdateConcurrencyException) | ||
{ | ||
if (!PuzzleExists(Puzzle.ID)) | ||
{ | ||
return NotFound(); | ||
} | ||
else | ||
{ | ||
throw; | ||
} | ||
} | ||
|
||
return RedirectToPage("FileManagement"); | ||
} | ||
|
||
/// <summary> | ||
/// Returns true if a puzzle exists with a given id | ||
/// </summary> | ||
private bool PuzzleExists(int id) | ||
{ | ||
return _context.Puzzles.Any(e => e.ID == id); | ||
} | ||
|
||
/// <summary> | ||
/// Helper for taking an uploaded form file, uploading it, and tracking it in the database | ||
/// </summary> | ||
private async Task UploadFileAsync(IFormFile uploadedFile, ContentFileType fileType) | ||
{ | ||
ContentFile file = new ContentFile(); | ||
string fileName = WebUtility.UrlEncode(Path.GetFileName(uploadedFile.FileName)); | ||
|
||
file.ShortName = fileName; | ||
file.Puzzle = Puzzle; | ||
file.Event = Event; | ||
file.FileType = fileType; | ||
|
||
file.Url = await FileManager.UploadBlobAsync(fileName, Event.ID, uploadedFile.OpenReadStream()); | ||
|
||
_context.ContentFiles.Add(file); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters