using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.EntityFrameworkCore; using GoMovies2020.Shared.Models; using Microsoft.AspNetCore.Http; using System.IO; using Microsoft.Extensions.Configuration; using GoMovies2020.Shared.ViewModels; using GoMovies2020.Shared.Queryables; namespace GoMovies2020.Web.Controllers { public class FilmsController : Controller { private readonly GoMovies2020DbContext _context; private readonly IConfiguration _config; private int filmsQueryCount; public FilmsController(GoMovies2020DbContext context, IConfiguration configuration) { _context = context; _config = configuration; } // GET: Films public async Task Index() { var films = _context.Film.Include(f => f.AgeRating).AsQueryable(); // Create a new FilmFilterViewModel object for the user to specify the filtering and paging criteria FilmsFilterViewModel filmsFilter = new FilmsFilterViewModel(); // Count the film records filmsFilter.RecordCount = await films.CountAsync(); // Apply the paging (fetch range) criteria to the films films = FilmFilterQueries.ApplyQueryFetchRange(films, filmsFilter); // Send the Paging and Filtering view model, FilmFilterViewModel, (filmsFilter variable) to the view ViewData["FilmsFilter"] = filmsFilter; return View(await films.ToListAsync()); } // POST: Films [HttpPost] public async Task Index(FilmsFilterViewModel filmsFilter) { var films = _context.Film.Include(f => f.AgeRating).AsQueryable(); // Apply the filter criteria to the films films = FilmFilterQueries.ApplyQueryFilter(films, filmsFilter); // Count the film records filmsFilter.RecordCount = await films.CountAsync(); // Update the current page if necessary if (filmsFilter.PageNumber > filmsFilter.PageCount) filmsFilter.PageNumber = 1; // Apply the paging (fetch range) criteria to the films films = FilmFilterQueries.ApplyQueryFetchRange(films, filmsFilter); // Send the Paging and Filtering view model, FilmFilterViewModel, (filmsFilter variable) to the view ViewData["FilmsFilter"] = filmsFilter; return View(await films.ToListAsync()); } // GET: Films/Details/5 public async Task Details(int? id) { if (id == null) { return NotFound(); } var film = await _context.Film .Include(f => f.AgeRating) .FirstOrDefaultAsync(m => m.FilmId == id); if (film == null) { return NotFound(); } return View(film); } // GET: Films/Create public IActionResult Create() { ViewData["AgeRatingId"] = new SelectList(_context.AgeRating, "AgeRatingId", "Classification"); return View(); } // POST: Films/Create // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task Create([Bind("FilmId,Title,Genre,AgeRatingId,Summary,Duration,Cast,Director,Trailer,PosterSmall,PosterLarge,CinemaReleaseDate,BuyReleaseDate,RentalReleaseDate,ReviewRating")] Film film) { if (ModelState.IsValid) { _context.Add(film); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } ViewData["AgeRatingId"] = new SelectList(_context.AgeRating, "AgeRatingId", "Classification", film.AgeRatingId); return View(film); } // GET: Films/Edit/5 public async Task Edit(int? id) { if (id == null) { return NotFound(); } var film = await _context.Film.FindAsync(id); if (film == null) { return NotFound(); } ViewData["AgeRatingId"] = new SelectList(_context.AgeRating, "AgeRatingId", "Classification", film.AgeRatingId); return View(film); } // POST: Films/Edit/5 // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task Edit(int id, [Bind("FilmId,Title,Genre,AgeRatingId,Summary,Duration,Cast,Director,Trailer,PosterSmall,PosterLarge,CinemaReleaseDate,BuyReleaseDate,RentalReleaseDate,ReviewRating,PosterSmallFile,PosterLargeFile")] Film film) { if (id != film.FilmId) { return NotFound(); } if (ModelState.IsValid) { try { // Upload specified poster files using the UploadPoster function. // If the upload is successful a new random file name will be created for security reasons // Set the film's PosterSmall and PosterLarge properties (which are saved in the database) to the new random file name generated film.PosterSmall = await UploadPosterImage(film.PosterSmallFile, film.PosterSmall); film.PosterLarge = await UploadPosterImage(film.PosterLargeFile, film.PosterLarge); _context.Update(film); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!FilmExists(film.FilmId)) { return NotFound(); } else { throw; } } return RedirectToAction(nameof(Index)); } ViewData["AgeRatingId"] = new SelectList(_context.AgeRating, "AgeRatingId", "Classification", film.AgeRatingId); return View(film); } // GET: Films/Delete/5 public async Task Delete(int? id) { if (id == null) { return NotFound(); } var film = await _context.Film .Include(f => f.AgeRating) .FirstOrDefaultAsync(m => m.FilmId == id); if (film == null) { return NotFound(); } return View(film); } // POST: Films/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task DeleteConfirmed(int id) { var film = await _context.Film.FindAsync(id); _context.Film.Remove(film); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } private bool FilmExists(int id) { return _context.Film.Any(e => e.FilmId == id); } // Add the following method to the FilmsController private async Task UploadPosterImage(IFormFile posterFile, string currentPosterFile) { // Check if poster file to upload has been specified if (posterFile != null && !string.IsNullOrEmpty(posterFile.FileName)) { try { //// Ideally some server-side validation should also be carried out //// E.g. check extension again, file size not more than max etc. //// See: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation // Generate a new random file name - never use a user file name in case it is malicious // See more: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.2#security-considerations string randomFileName = Path.GetRandomFileName(); // Set the file extension to the same as the original file, otherwise it may not be displayed in the browser randomFileName = Path.ChangeExtension(randomFileName, Path.GetExtension(posterFile.FileName)); // Combine the random file name with the path of the folder dedicated for storing poster images // The path is stored in the appsettings.json file string savedFileName = Path.Combine(_config["StoredFilesPath:StoredFilmPostersPath"], randomFileName); // Copy the file with the new file name to the folder dedicated for storing poster images using (var stream = System.IO.File.Create(savedFileName)) { await posterFile.CopyToAsync(stream); } // return the random file name so this can be stored in the database return randomFileName; } catch (Exception ex) when (ex is FileNotFoundException || ex is UnauthorizedAccessException) { // an error has occurred, rethrow the error to the calling function throw; } } // return the current poster file name, i.e. original nane before update return currentPosterFile; } } }