Добавлена аутентификация пользователя по email/паролю

Добавлены представления аутентификации и выхода из приложения.
Добавлено представление отображения детальной информации по фильму.
Добавлено представление профиля пользователя.
Вынесены линки на регистрацию/вход/ добавление фильма в Layout
parent b32ee1e6
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
<e p="Cinema.csproj" t="IncludeRecursive" /> <e p="Cinema.csproj" t="IncludeRecursive" />
<e p="Cinema.sln" t="IncludeFlat" /> <e p="Cinema.sln" t="IncludeFlat" />
<e p="Controllers" t="Include"> <e p="Controllers" t="Include">
<e p="AccountController.cs" t="Include" />
<e p="FilmsController.cs" t="Include" /> <e p="FilmsController.cs" t="Include" />
</e> </e>
<e p="Data" t="Include"> <e p="Data" t="Include">
...@@ -16,12 +17,15 @@ ...@@ -16,12 +17,15 @@
<e p="Migrations" t="Include"> <e p="Migrations" t="Include">
<e p="20201119082753_Initial.Designer.cs" t="Include" /> <e p="20201119082753_Initial.Designer.cs" t="Include" />
<e p="20201119082753_Initial.cs" t="Include" /> <e p="20201119082753_Initial.cs" t="Include" />
<e p="20201121110726_AddProducerFieldToFilm.Designer.cs" t="Include" />
<e p="20201121110726_AddProducerFieldToFilm.cs" t="Include" />
<e p="CinemaContextModelSnapshot.cs" t="Include" /> <e p="CinemaContextModelSnapshot.cs" t="Include" />
</e> </e>
<e p="Models" t="Include"> <e p="Models" t="Include">
<e p="ErrorViewModel.cs" t="Include" />
<e p="Film.cs" t="Include" /> <e p="Film.cs" t="Include" />
<e p="FilmViewModel.cs" t="Include" /> <e p="FilmViewModel.cs" t="Include" />
<e p="LoginViewModel.cs" t="Include" />
<e p="RegisterViewModel.cs" t="Include" />
<e p="User.cs" t="Include" /> <e p="User.cs" t="Include" />
</e> </e>
<e p="Program.cs" t="Include" /> <e p="Program.cs" t="Include" />
...@@ -33,7 +37,13 @@ ...@@ -33,7 +37,13 @@
</e> </e>
<e p="Startup.cs" t="Include" /> <e p="Startup.cs" t="Include" />
<e p="Views" t="Include"> <e p="Views" t="Include">
<e p="Account" t="Include">
<e p="Index.cshtml" t="Include" />
<e p="Login.cshtml" t="Include" />
<e p="Register.cshtml" t="Include" />
</e>
<e p="Films" t="Include"> <e p="Films" t="Include">
<e p="About.cshtml" t="Include" />
<e p="Add.cshtml" t="Include" /> <e p="Add.cshtml" t="Include" />
<e p="Index.cshtml" t="Include" /> <e p="Index.cshtml" t="Include" />
</e> </e>
...@@ -64,7 +74,7 @@ ...@@ -64,7 +74,7 @@
<e p="favicon.ico" t="Include" /> <e p="favicon.ico" t="Include" />
<e p="images" t="Include"> <e p="images" t="Include">
<e p="Posters" t="Include"> <e p="Posters" t="Include">
<e p="bell-1096280_960_720.png" t="Include" /> <e p="Три мушкетера_Три_мушкетёра_(2013).jpg" t="Include" />
</e> </e>
</e> </e>
<e p="js" t="Include"> <e p="js" t="Include">
......
...@@ -14,16 +14,27 @@ ...@@ -14,16 +14,27 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="411e953d-ac1b-431b-8c52-f61006a5f2f2" name="Default Changelist" comment="&#10;Добавлен файл БД(cinema.db)&#10;Удалены лишние файлы не относящиеся к проекту (HomeController) и его представления&#10;Стартовая страница изменена на Films/Index&#10;Добавлено представление для главной страницы Films/Index&#10;В контроллере Films добавлен Экшен Index"> <list default="true" id="411e953d-ac1b-431b-8c52-f61006a5f2f2" name="Default Changelist" comment="&#10;Добавлен файл БД(cinema.db)&#10;Удалены лишние файлы не относящиеся к проекту (HomeController) и его представления&#10;Стартовая страница изменена на Films/Index&#10;Добавлено представление для главной страницы Films/Index&#10;В контроллере Films добавлен Экшен Index">
<change afterPath="$PROJECT_DIR$/Controllers/FilmsController.cs" afterDir="false" /> <change afterPath="$PROJECT_DIR$/Controllers/AccountController.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Models/FilmViewModel.cs" afterDir="false" /> <change afterPath="$PROJECT_DIR$/Models/LoginViewModel.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Services/UploadFileService.cs" afterDir="false" /> <change afterPath="$PROJECT_DIR$/Models/RegisterViewModel.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Views/Films/Add.cshtml" afterDir="false" /> <change afterPath="$PROJECT_DIR$/Views/Account/Index.cshtml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Views/Account/Login.cshtml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Views/Account/Register.cshtml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Views/Films/About.cshtml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.Cinema/.idea/contentModel.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Cinema/.idea/contentModel.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/.idea.Cinema/.idea/contentModel.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Cinema/.idea/contentModel.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.Cinema/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Cinema/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/.idea.Cinema/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Cinema/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Controllers/Films.cs" beforeDir="false" /> <change beforePath="$PROJECT_DIR$/Cinema.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/Cinema.csproj" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Controllers/FilmsController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Controllers/FilmsController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Data/CinemaContext.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Data/CinemaContext.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Data/CinemaContext.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Data/CinemaContext.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Models/ErrorViewModel.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Models/Film.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Models/Film.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Models/Film.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Models/Film.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Models/FilmViewModel.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Models/FilmViewModel.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Startup.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Startup.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Startup.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Startup.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Views/Films/Add.cshtml" beforeDir="false" afterPath="$PROJECT_DIR$/Views/Films/Add.cshtml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Views/Films/Index.cshtml" beforeDir="false" afterPath="$PROJECT_DIR$/Views/Films/Index.cshtml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Views/Shared/_Layout.cshtml" beforeDir="false" afterPath="$PROJECT_DIR$/Views/Shared/_Layout.cshtml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cinema.db" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/wwwroot/css/site.css" beforeDir="false" afterPath="$PROJECT_DIR$/wwwroot/css/site.css" afterDir="false" />
</list> </list>
<list id="b32251c1-d844-4e65-bf1c-9f6cfe4d536a" name="Добавлены модели: User для дополнительного описания сущности пользователя, в виде Имени и Фамилии. Film для описания сущности фильма. Определены и добавлены следующие nuget-пакеты : Identity, efCore Design, Proxies, Sqlite Настроена конфигурация используемой БД. Добавлена начальная миграция" comment="" /> <list id="b32251c1-d844-4e65-bf1c-9f6cfe4d536a" name="Добавлены модели: User для дополнительного описания сущности пользователя, в виде Имени и Фамилии. Film для описания сущности фильма. Определены и добавлены следующие nuget-пакеты : Identity, efCore Design, Proxies, Sqlite Настроена конфигурация используемой БД. Добавлена начальная миграция" comment="" />
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
...@@ -47,16 +58,25 @@ ...@@ -47,16 +58,25 @@
<option name="CHANGED_PATHS"> <option name="CHANGED_PATHS">
<list> <list>
<option value="$PROJECT_DIR$/Models/User.cs" /> <option value="$PROJECT_DIR$/Models/User.cs" />
<option value="$PROJECT_DIR$/Views/Shared/_Layout.cshtml" />
<option value="$PROJECT_DIR$/Views/Films/Index.cshtml" />
<option value="$PROJECT_DIR$/Services/UploadFileService.cs" /> <option value="$PROJECT_DIR$/Services/UploadFileService.cs" />
<option value="$PROJECT_DIR$/Controllers/Films.cs" />
<option value="$PROJECT_DIR$/Models/LoginViewModel.cs" />
<option value="$PROJECT_DIR$/Models/RegisterViewModel.cs" />
<option value="$PROJECT_DIR$/Views/Account/Login.cshtml" />
<option value="$PROJECT_DIR$/Views/Account/Register.cshtml" />
<option value="$PROJECT_DIR$/Controllers/AccountController.cs" />
<option value="$PROJECT_DIR$/Migrations/20201121095402_UpdateFilmFields.cs" />
<option value="$PROJECT_DIR$/Data/CinemaContext.cs" /> <option value="$PROJECT_DIR$/Data/CinemaContext.cs" />
<option value="$PROJECT_DIR$/Startup.cs" /> <option value="$PROJECT_DIR$/Startup.cs" />
<option value="$PROJECT_DIR$/Controllers/Films.cs" /> <option value="$PROJECT_DIR$/Views/Shared/_Layout.cshtml" />
<option value="$PROJECT_DIR$/Views/Account/Index.cshtml" />
<option value="$PROJECT_DIR$/wwwroot/css/site.css" />
<option value="$PROJECT_DIR$/Views/Films/Index.cshtml" />
<option value="$PROJECT_DIR$/Models/Film.cs" />
<option value="$PROJECT_DIR$/Controllers/FilmsController.cs" />
<option value="$PROJECT_DIR$/Models/FilmViewModel.cs" /> <option value="$PROJECT_DIR$/Models/FilmViewModel.cs" />
<option value="$PROJECT_DIR$/Views/Films/Add.cshtml" /> <option value="$PROJECT_DIR$/Views/Films/Add.cshtml" />
<option value="$PROJECT_DIR$/Controllers/FilmsController.cs" /> <option value="$PROJECT_DIR$/Views/Films/About.cshtml" />
<option value="$PROJECT_DIR$/Models/Film.cs" />
</list> </list>
</option> </option>
</component> </component>
...@@ -114,6 +134,7 @@ ...@@ -114,6 +134,7 @@
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1605771522746</updated> <updated>1605771522746</updated>
<workItem from="1605771536927" duration="10080000" /> <workItem from="1605771536927" duration="10080000" />
<workItem from="1605950916335" duration="5649000" />
</task> </task>
<task id="LOCAL-00001" summary="Начало проекта"> <task id="LOCAL-00001" summary="Начало проекта">
<created>1605772561160</created> <created>1605772561160</created>
...@@ -149,7 +170,7 @@ ...@@ -149,7 +170,7 @@
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" /> <option name="version" value="3" />
</component> </component>
<component name="UnityProjectConfiguration" hasMinimizedUI="null" /> <component name="UnityProjectConfiguration" hasMinimizedUI="false" />
<component name="UnityUnitTestConfiguration" currentTestLauncher="NUnit" /> <component name="UnityUnitTestConfiguration" currentTestLauncher="NUnit" />
<component name="Vcs.Log.Tabs.Properties"> <component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES"> <option name="TAB_STATES">
...@@ -161,6 +182,7 @@ ...@@ -161,6 +182,7 @@
</entry> </entry>
</map> </map>
</option> </option>
<option name="oldMeFiltersMigrated" value="true" />
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" /> <option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
...@@ -175,26 +197,31 @@ ...@@ -175,26 +197,31 @@
<screen x="0" y="0" width="1920" height="1044" /> <screen x="0" y="0" width="1920" height="1044" />
</state> </state>
<state x="529" y="406" width="852" height="229" key="Git.Rebase.Log.Action.NewCommitMessage.Dialog/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605775025446" /> <state x="529" y="406" width="852" height="229" key="Git.Rebase.Log.Action.NewCommitMessage.Dialog/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605775025446" />
<state width="1878" height="277" key="GridCell.Tab.0.bottom" timestamp="1605785567204"> <state width="1878" height="277" key="GridCell.Tab.0.bottom" timestamp="1605956791150">
<screen x="0" y="0" width="1920" height="1044" /> <screen x="0" y="0" width="1920" height="1044" />
</state> </state>
<state width="1878" height="277" key="GridCell.Tab.0.bottom/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605785567204" /> <state width="1878" height="277" key="GridCell.Tab.0.bottom/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605785567204" />
<state width="1878" height="277" key="GridCell.Tab.0.center" timestamp="1605785567204"> <state width="1878" height="277" key="GridCell.Tab.0.bottom/0.0.1920.1044@0.0.1920.1044" timestamp="1605956791150" />
<state width="1878" height="277" key="GridCell.Tab.0.center" timestamp="1605956791150">
<screen x="0" y="0" width="1920" height="1044" /> <screen x="0" y="0" width="1920" height="1044" />
</state> </state>
<state width="1878" height="277" key="GridCell.Tab.0.center/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605785567204" /> <state width="1878" height="277" key="GridCell.Tab.0.center/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605785567204" />
<state width="1878" height="277" key="GridCell.Tab.0.left" timestamp="1605785567204"> <state width="1878" height="277" key="GridCell.Tab.0.center/0.0.1920.1044@0.0.1920.1044" timestamp="1605956791150" />
<state width="1878" height="277" key="GridCell.Tab.0.left" timestamp="1605956791149">
<screen x="0" y="0" width="1920" height="1044" /> <screen x="0" y="0" width="1920" height="1044" />
</state> </state>
<state width="1878" height="277" key="GridCell.Tab.0.left/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605785567204" /> <state width="1878" height="277" key="GridCell.Tab.0.left/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605785567204" />
<state width="1878" height="277" key="GridCell.Tab.0.right" timestamp="1605785567204"> <state width="1878" height="277" key="GridCell.Tab.0.left/0.0.1920.1044@0.0.1920.1044" timestamp="1605956791149" />
<state width="1878" height="277" key="GridCell.Tab.0.right" timestamp="1605956791150">
<screen x="0" y="0" width="1920" height="1044" /> <screen x="0" y="0" width="1920" height="1044" />
</state> </state>
<state width="1878" height="277" key="GridCell.Tab.0.right/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605785567204" /> <state width="1878" height="277" key="GridCell.Tab.0.right/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605785567204" />
<state x="644" y="228" width="622" height="585" key="RiderGenerateDialog" timestamp="1605774080803"> <state width="1878" height="277" key="GridCell.Tab.0.right/0.0.1920.1044@0.0.1920.1044" timestamp="1605956791150" />
<state x="644" y="228" width="622" height="585" key="RiderGenerateDialog" timestamp="1605951142994">
<screen x="0" y="0" width="1920" height="1044" /> <screen x="0" y="0" width="1920" height="1044" />
</state> </state>
<state x="644" y="228" width="622" height="585" key="RiderGenerateDialog/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605774080803" /> <state x="644" y="228" width="622" height="585" key="RiderGenerateDialog/0.0.1920.1044/1920.0.1920.1080@0.0.1920.1044" timestamp="1605774080803" />
<state x="644" y="228" key="RiderGenerateDialog/0.0.1920.1044@0.0.1920.1044" timestamp="1605951142994" />
<state x="809" y="438" width="292" height="164" key="VCS.ChangelistChooser" timestamp="1605775030802"> <state x="809" y="438" width="292" height="164" key="VCS.ChangelistChooser" timestamp="1605775030802">
<screen x="0" y="0" width="1920" height="1044" /> <screen x="0" y="0" width="1920" height="1044" />
</state> </state>
......
...@@ -14,4 +14,8 @@ ...@@ -14,4 +14,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.10" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\images\Posters" />
</ItemGroup>
</Project> </Project>
using System;
using System.Threading.Tasks;
using Cinema.Data;
using Cinema.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace Cinema.Controllers
{
public class AccountController : Controller
{
private readonly UserManager<User> _userManager;
private readonly SignInManager<User> _signInManager;
public AccountController(CinemaContext db, UserManager<User> userManager, SignInManager<User> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public IActionResult Register()
{
return View();
}
[Authorize]
public IActionResult Index(string id = null){
User user = id == null? _userManager.GetUserAsync(User).Result : _userManager.FindByIdAsync(id).Result;
return View(user);
}
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (!ModelState.IsValid) return View(model);
User user = new User
{
Email = model.Email,
UserName = model.Email,
FirstName = model.FirstName,
SecondName = model.SecondName
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, false);
return RedirectToAction("Index", "Films");
}
foreach (var error in result.Errors)
ModelState.AddModelError(String.Empty, error.Description);
return View(model);
}
public IActionResult Login(string returnUrl = null)
{
return View(new LoginViewModel {ReturnUrl = returnUrl});
}
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
User user = await _userManager.FindByEmailAsync(model.Email);
if (user != null)
{
var result = await _signInManager.PasswordSignInAsync(
user,
model.Password,
model.RememberMe,
false);
if (result.Succeeded)
{
if (!string.IsNullOrEmpty(model.ReturnUrl)&&
Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
return RedirectToAction("Index", "Films");
}
}
ModelState.AddModelError("", "Неправильный логин или пароль");
}
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
return RedirectToAction("Login");
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
...@@ -15,19 +16,19 @@ namespace Cinema.Controllers ...@@ -15,19 +16,19 @@ namespace Cinema.Controllers
{ {
private CinemaContext _db; private CinemaContext _db;
private UploadFileService _uploader; private UploadFileService _uploader;
private UserManager<User> _userManager;
private readonly IHostEnvironment _environment; private readonly IHostEnvironment _environment;
public Films( public Films(
CinemaContext db, CinemaContext db,
UploadFileService uploader, UploadFileService uploader,
IHostEnvironment environment IHostEnvironment environment, UserManager<User> userManager)
)
{ {
_db = db; _db = db;
_uploader = uploader; _uploader = uploader;
_environment = environment; _environment = environment;
_userManager = userManager;
} }
public IActionResult Index() public IActionResult Index()
...@@ -36,6 +37,14 @@ namespace Cinema.Controllers ...@@ -36,6 +37,14 @@ namespace Cinema.Controllers
return View(films); return View(films);
} }
public IActionResult About(int id)
{
var film = _db.Films.FirstOrDefault(f => f.Id == id);
if (film is null)
return RedirectToAction("Index");
return View(film);
}
[HttpGet] [HttpGet]
public IActionResult Add() public IActionResult Add()
{ {
...@@ -49,13 +58,17 @@ namespace Cinema.Controllers ...@@ -49,13 +58,17 @@ namespace Cinema.Controllers
if (!Directory.Exists(path)) if (!Directory.Exists(path))
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
string posterPath = $"images/Posters/{film.Name}{film.File.FileName}"; string posterPath = $"images/Posters/{film.Name}_{film.File.FileName}";
_uploader.Upload(path, film.File.FileName, film.File); _uploader.Upload(path, (film.Name + '_' + film.File.FileName), film.File);
Film post = new Film() Film post = new Film()
{ {
Description = film.Description, Description = film.Description,
Name = film.Name,
Poster = posterPath, Poster = posterPath,
UserId = "fixture" UserId = _userManager.GetUserId(User),
PublishYear = film.PublishYear,
CreatedAt = DateTime.Now,
Producer = film.Producer
}; };
var result = _db.Films.AddAsync(post); var result = _db.Films.AddAsync(post);
if (!result.IsCompleted) return View(film); if (!result.IsCompleted) return View(film);
......
using Cinema.Models; using Cinema.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
......
// <auto-generated />
using System;
using Cinema.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Cinema.Migrations
{
[DbContext(typeof(CinemaContext))]
[Migration("20201121110726_AddProducerFieldToFilm")]
partial class AddProducerFieldToFilm
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.10");
modelBuilder.Entity("Cinema.Models.Film", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Poster")
.HasColumnType("TEXT");
b.Property<string>("Producer")
.HasColumnType("TEXT");
b.Property<int>("PublishYear")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Films");
});
modelBuilder.Entity("Cinema.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasColumnType("TEXT")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("FirstName")
.HasColumnType("TEXT");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasColumnType("TEXT")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasColumnType("TEXT")
.HasMaxLength(256);
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecondName")
.HasColumnType("TEXT");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasColumnType("TEXT")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasColumnType("TEXT")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Cinema.Models.Film", b =>
{
b.HasOne("Cinema.Models.User", "User")
.WithMany("Films")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Cinema.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Cinema.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Cinema.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Cinema.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
using Microsoft.EntityFrameworkCore.Migrations;
namespace Cinema.Migrations
{
public partial class AddProducerFieldToFilm : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Producer",
table: "Films",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Producer",
table: "Films");
}
}
}
...@@ -34,6 +34,9 @@ namespace Cinema.Migrations ...@@ -34,6 +34,9 @@ namespace Cinema.Migrations
b.Property<string>("Poster") b.Property<string>("Poster")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("Producer")
.HasColumnType("TEXT");
b.Property<int>("PublishYear") b.Property<int>("PublishYear")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
......
using System;
namespace Cinema.Models
{
public class ErrorViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
\ No newline at end of file
...@@ -7,6 +7,9 @@ namespace Cinema.Models ...@@ -7,6 +7,9 @@ namespace Cinema.Models
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public string Producer { get; set; }
public string Poster { get; set; } public string Poster { get; set; }
public int PublishYear { get; set; } public int PublishYear { get; set; }
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
......
...@@ -7,21 +7,27 @@ namespace Cinema.Models ...@@ -7,21 +7,27 @@ namespace Cinema.Models
{ {
[Required(ErrorMessage = "Поле название обязательно для заполнения")] [Required(ErrorMessage = "Поле название обязательно для заполнения")]
[MinLength(10, ErrorMessage = "Минимальная длина описания 10 символов")] [MinLength(3, ErrorMessage = "Минимальная длина названия 3 символа")]
[Display(Name = "Название фильма")] [Display(Name = "Название фильма")]
public string Name { get; set; } public string Name { get; set; }
[Required(ErrorMessage = "Поле описание обязательно для заполнения")] [Required(ErrorMessage = "Поле описание обязательно для заполнения")]
[MinLength(10, ErrorMessage = "Минимальная длина описания 10 символов")] [MinLength(10, ErrorMessage = "Минимальная длина описания 10 символов")]
[Display(Name = "Описание")] [Display(Name = "Описание")]
[DataType(DataType.Text)] [DataType(DataType.Text)]
public string Description { get; set; } public string Description { get; set; }
[Required(ErrorMessage = "Поле режиссер обязательно для заполнения")]
[Display(Name = "Режиссер")]
[DataType(DataType.Text)]
public string Producer { get; set; }
[Required(ErrorMessage = "Загрузка фото обязательна")] [Required(ErrorMessage = "Загрузка фото обязательна")]
[DataType(DataType.Upload)] [DataType(DataType.Upload)]
[Display(Name = "Постер фильма")] [Display(Name = "Постер фильма")]
public IFormFile File { get; set; } public IFormFile File { get; set; }
[Display(Name = "Название фильма")] [Display(Name = "Год создания")]
public int PublishYear { get; set; } public int PublishYear { get; set; }
} }
} }
\ No newline at end of file
using System.ComponentModel.DataAnnotations;
namespace Cinema.Models
{
public class LoginViewModel
{
[Required(ErrorMessage = "Это поле обязательно")]
[Display(Name = "Электронная почта")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "Это поле обязательно")]
[Display(Name = "Пароль")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Запомнить меня")]
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
}
}
\ No newline at end of file
using System.ComponentModel.DataAnnotations;
namespace Cinema.Models
{
public class RegisterViewModel
{
[Required(ErrorMessage = "Это поле обязательно")]
[Display(Name = "Электронная почта")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "Это поле обязательно")]
[Display(Name = "Имя")]
[DataType(DataType.Text)]
public string FirstName { get; set; }
[Display(Name = "Фамилия")]
[DataType(DataType.Text)]
public string SecondName { get; set; }
[Required(ErrorMessage = "Это поле обязательно")]
[Display(Name = "Пароль")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required(ErrorMessage = "Это поле обязательно")]
[Display(Name = "Введите пароль повторно")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Пароли не совпадают")]
public string ConfirmPassword { get; set; }
}
}
\ No newline at end of file
...@@ -3,10 +3,12 @@ using System.Collections.Generic; ...@@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Cinema.Data; using Cinema.Data;
using Cinema.Models;
using Cinema.Services; using Cinema.Services;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
...@@ -28,6 +30,17 @@ namespace Cinema ...@@ -28,6 +30,17 @@ namespace Cinema
services.AddDbContext<CinemaContext>(); services.AddDbContext<CinemaContext>();
services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/Login"); services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/Login");
services.AddTransient<UploadFileService>(); services.AddTransient<UploadFileService>();
services.AddIdentity<User, IdentityRole>(
options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 5;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequiredUniqueChars = 1;
options.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<CinemaContext>();;
} }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
...@@ -46,7 +59,9 @@ namespace Cinema ...@@ -46,7 +59,9 @@ namespace Cinema
app.UseRouting(); app.UseRouting();
app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.UseCookiePolicy();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
......
@model User
@{
ViewBag.Title = "Профиль пользователя";
Layout = "_Layout";
}
<h1>Кабинет пользователя</h1>
<div class="row">
<div class="col-md-6">
<p>Имя пользователя - @Model.FirstName</p>
<p>Фамилия пользователя - @Model.SecondName</p>
<p>Фильмов загружено - @Model.Films.Count</p>
</div>
</div>
<h2>Список загруженных фильмов</h2>
<div class="d-flex align-items-stretch flex-wrap justify-content-between">
@foreach (var film in @Model.Films)
{
<div class="film-card">
<a asp-action="About" asp-controller="Films" asp-route-id="@film.Id">
<div class="film-poster">
<img src="~/@film.Poster" alt="@film.Name">
</div>
<p class="film-name">
Название - @film.Name
</p>
</a>
<p>Год выпуска - @film.PublishYear</p>
</div>
}
</div>
\ No newline at end of file
@model LoginViewModel
@{
ViewBag.Title = "Вход";
Layout = "_Layout";
}
<h2>Введите данные для входа</h2>
<div class="row justify-content-center">
<div class="col-md-6">
<form method="post" asp-controller="Account" asp-action="Login"
asp-route-returnUrl="@Model.ReturnUrl">
<div asp-validation-summary="ModelOnly"></div>
<div class="form-group">
<label asp-for="Email"></label><br/>
<input class="form-control" asp-for="Email"/>
<span asp-validation-for="Email"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label><br/>
<input class="form-control" asp-for="Password"/>
<span asp-validation-for="Password"></span>
</div>
<div class="form-group">
<label asp-for="RememberMe"></label>
<input class="form-check-inline" asp-for="RememberMe"/>
</div>
<div class="form-group">
<button class="form-control" type="submit" id="submit">Войти</button>
</div>
</form>
</div>
</div>
\ No newline at end of file
@model RegisterViewModel
@{
ViewBag.Title = "Регистрация";
Layout = "_Layout";
}
<h2>Регистрация нового пользователя</h2>
<div class="row justify-content-center">
<div class="col-md-6">
<form method="post" asp-controller="Account" asp-action="Register" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly"></div>
<div class="form-group">
<label asp-for="Email"></label><br/>
<input class="form-control" asp-for="Email"/>
<span asp-validation-for="Email"></span>
</div>
<div class="form-group">
<label asp-for="FirstName"></label><br/>
<input class="form-control" asp-for="FirstName"/>
<span asp-validation-for="FirstName"></span>
</div>
<div class="form-group">
<label asp-for="SecondName"></label><br/>
<input class="form-control" asp-for="SecondName"/>
<span asp-validation-for="SecondName"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label><br />
<input class="form-control" asp-for="Password" />
<span asp-validation-for="Password"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label><br />
<input class="form-control" asp-for="ConfirmPassword" />
<span asp-validation-for="ConfirmPassword"></span>
</div>
<div>
<input class="form-control" type="submit" value="Регистрация" />
</div>
</form>
</div>
</div>
@model Film
@{
ViewBag.Title = @Model.Name;
Layout = "_Layout";
}
<h2>@Model.Name</h2>
<div class="row">
<div class="col-md-6">
<div class="film-poster">
<img src="~/@Model.Poster" alt="@Model.Name">
</div>
</div>
<div class="col-md-6">
<p>Опубликован - @Model.CreatedAt.ToShortDateString()</p>
<p>Обновлен - @Model.UpdatedAt.ToShortDateString()</p>
<p>Кем добавлен -
<a asp-action="Index" asp-controller="Account" asp-route-id="@Model.UserId">
@Model.User.Email
</a>
</p>
<hr>
<p>Год выпуска - @Model.PublishYear</p>
<p>Режиссер - @Model.Producer</p>
<h3>Описание фильма</h3>
<p>@Model.Description</p>
</div>
</div>
\ No newline at end of file
...@@ -6,18 +6,18 @@ ...@@ -6,18 +6,18 @@
} }
<div class="row"> <div class="row">
<div class="col align-self-center"> <div class="col-6 align-self-center">
<div class="form"> <div class="form">
<form asp-action="Add" asp-controller="Films" method="post" enctype="multipart/form-data"> <form asp-action="Add" asp-controller="Films" method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly"></div> <div asp-validation-summary="ModelOnly"></div>
<div class="form-group"> <div class="form-group">
<label asp-for="Name">Название фильма</label><br/> <label asp-for="Name">Название фильма</label><br/>
<input class="form-control" asp-for="Name"/> <input class="form-control" asp-for="Name"/>
<span asp-validation-for="Description"></span> <span asp-validation-for="Name"></span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label asp-for="Description">Описание фильма</label><br/> <label asp-for="Description">Описание фильма</label><br/>
<input class="form-control" asp-for="Description"/> <textarea class="form-control" asp-for="Description" name="Description" style="resize: none;" id="" rows="10"></textarea>
<span asp-validation-for="Description"></span> <span asp-validation-for="Description"></span>
</div> </div>
<div class="form-group"> <div class="form-group">
...@@ -25,10 +25,15 @@ ...@@ -25,10 +25,15 @@
<input class="form-control-file" asp-for="File"/> <input class="form-control-file" asp-for="File"/>
<span asp-validation-for="File"></span> <span asp-validation-for="File"></span>
</div> </div>
<div class="form-group">
<label asp-for="Producer"></label><br />
<input class="form-control-file" asp-for="Producer"/>
<span asp-validation-for="Producer"></span>
</div>
<div class="form-group"> <div class="form-group">
<label asp-for="PublishYear"></label><br /> <label asp-for="PublishYear"></label><br />
<input class="form-control-file" asp-for="PublishYear"/> <input class="form-control-file" asp-for="PublishYear"/>
<span asp-validation-for="File"></span> <span asp-validation-for="PublishYear"></span>
</div> </div>
<div> <div>
<button class="form-control" type="submit" id="submit" >Добавить</button> <button class="form-control" type="submit" id="submit" >Добавить</button>
......
...@@ -5,13 +5,31 @@ ...@@ -5,13 +5,31 @@
Layout = "_Layout"; Layout = "_Layout";
} }
<h2>Все фильмы</h2>
@if (@Model.Count == 0) @if (@Model.Count == 0)
{ {
<h3>Список фильмов Пуст</h3> <h3>Список фильмов Пуст</h3>
} }
else else
{ {
<p>Здесь будет список фильмов</p> <h2>Список загруженных фильмов</h2>
<div class="d-flex align-items-stretch flex-wrap justify-content-between">
@foreach (var film in @Model)
{
<div class="film-card">
<a asp-action="About" asp-route-id="@film.Id">
<div class="film-poster">
<img src="@film.Poster" alt="@film.Name">
</div>
<p class="film-name">
Название - @film.Name
</p>
</a>
<p>Год выпуска - @film.PublishYear</p>
<a asp-action="Index" asp-controller="Account" asp-route-id="@film.UserId">
Добавлен @film.User.Email
</a>
</div>
}
</div>
} }
\ No newline at end of file
...@@ -12,16 +12,32 @@ ...@@ -12,16 +12,32 @@
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3"> <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container"> <div class="container">
<a class="navbar-brand" asp-area="" asp-controller="Films" asp-action="Index">Cinema</a> <a class="navbar-brand" asp-area="" asp-controller="Films" asp-action="Index">Cinema</a>
@if (User.Identity.IsAuthenticated)
{
<a asp-action="Add" asp-controller="Films" class="nav-item">Новый фильм</a>
}
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent" <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation"> aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse"> <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1"> <div class="login_group">
<li class="nav-item"> @if (User.Identity.IsAuthenticated)
<a class="nav-link text-dark" asp-area="" asp-controller="Films" asp-action="Index">Home</a> {
</li>
</ul> <form asp-action="Logout" asp-controller="Account" method="post" asp-antiforgery="true">
<a asp-action="Index" asp-controller="Account">
<i class="fa fa-user fa-2x" style="margin-right: 10px;" aria-hidden="true"></i>
</a>
<input type="submit" value="Выход" class="brn btn-dark">
</form>
}
else
{
<a asp-action="Login" asp-controller="Account">Вход</a>
<a asp-action="Register" asp-controller="Account">Регистрация</a>
}
</div>
</div> </div>
</div> </div>
</nav> </nav>
......
No preview for this file type
...@@ -69,3 +69,14 @@ body { ...@@ -69,3 +69,14 @@ body {
white-space: nowrap; white-space: nowrap;
line-height: 60px; /* Vertically center the text there */ line-height: 60px; /* Vertically center the text there */
} }
img{
width: 100%;
height: auto;
}
.film-card{
padding: 10px;
border: 1px solid silver;
border-radius: 5px;
width: 32%;
margin-bottom: 15px;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment