diff options
| author | Syndamia <kamen.d.mladenov@protonmail.com> | 2021-05-15 22:20:53 +0300 |
|---|---|---|
| committer | Syndamia <kamen.d.mladenov@protonmail.com> | 2021-05-15 22:20:53 +0300 |
| commit | f35a7aecd313547a6f6478a056fb2d5593f1c07b (patch) | |
| tree | 4473c30a5b51591d79f627b7518f8c9e15787cbf /ExamTemplate/Web/ExamTemplate.Web | |
| parent | 2ac139d0854c0f6d1b4cebc1487dd41128f00c60 (diff) | |
| download | it-kariera-exam-template-f35a7aecd313547a6f6478a056fb2d5593f1c07b.tar it-kariera-exam-template-f35a7aecd313547a6f6478a056fb2d5593f1c07b.tar.gz it-kariera-exam-template-f35a7aecd313547a6f6478a056fb2d5593f1c07b.zip | |
Big daddy refactor
Diffstat (limited to 'ExamTemplate/Web/ExamTemplate.Web')
23 files changed, 856 insertions, 0 deletions
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Configurations/ControllerUserMappings.cs b/ExamTemplate/Web/ExamTemplate.Web/Configurations/ControllerUserMappings.cs new file mode 100644 index 0000000..05c57e2 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Configurations/ControllerUserMappings.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using ExamTemplate.Services.Models.User; +using ExamTemplate.Web.Models.User; + +namespace ExamTemplate.Services.Configurations +{ + public class ControllerUserMappings : Profile + { + public ControllerUserMappings() + { + CreateMap<RegisterUserWebModel, RegisterUserServiceModel>(); + CreateMap<LoginUserWebModel, LoginUserServiceModel>(); + CreateMap<UserServiceModel, UserWebModel>(); + CreateMap<UserServiceModel, EditUserWebModel>(); + CreateMap<EditUserWebModel, UserServiceModel>(); + } + } +} diff --git a/ExamTemplate/Web/ExamTemplate.Web/Controllers/AccountController.cs b/ExamTemplate/Web/ExamTemplate.Web/Controllers/AccountController.cs new file mode 100644 index 0000000..2c2eb32 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Controllers/AccountController.cs @@ -0,0 +1,146 @@ +using ExamTemplate.Services.Interfaces; +using Microsoft.AspNetCore.Mvc; +using ExamTemplate.Web.Models.User; +using AutoMapper; +using ExamTemplate.Services.Models.User; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; + +namespace ExamTemplate.Web.Controllers +{ + [Authorize] + public class AccountController : Controller + { + private readonly IMapper _autoMapper; + private readonly IUserService _userService; + + public AccountController(IMapper autoMapper, IUserService userService) + { + this._autoMapper = autoMapper; + this._userService = userService; + } + + [HttpGet] + [AllowAnonymous] + public IActionResult Register() + { + return View(); + } + + [HttpPost] + [AllowAnonymous] + public async Task<IActionResult> Register(RegisterUserWebModel registerUserWebModel) + { + if (!ModelState.IsValid) + return View(registerUserWebModel); + + RegisterUserServiceModel registerUserServiceModel = this._autoMapper.Map<RegisterUserServiceModel>(registerUserWebModel); + + bool result = await this._userService.RegisterUserAsync(registerUserServiceModel); + + if (result) + return await this.Login(new LoginUserWebModel { + Username = registerUserServiceModel.Username, + Password = registerUserServiceModel.Password + }); + else + return View(registerUserWebModel); + } + + [HttpGet] + [AllowAnonymous] + public IActionResult Login() + { + return View(); + } + + [HttpPost] + [AllowAnonymous] + public async Task<IActionResult> Login(LoginUserWebModel loginUserWebModel) + { + if (!ModelState.IsValid) + return View(loginUserWebModel); + + LoginUserServiceModel loginUserServiceModel = this._autoMapper.Map<LoginUserServiceModel>(loginUserWebModel); + + bool result = await this._userService.LoginUserAsync(loginUserServiceModel); + + if (result) + return RedirectToAction("Index", "Home"); + else + return View(loginUserWebModel); + } + + [HttpPost] + public async Task<IActionResult> Logout() + { + await this._userService.LogoutAsync(); + + return RedirectToAction("Login"); + } + + [HttpGet] + [AllowAnonymous] + public async Task<IActionResult> Profile(string username) + { + UserServiceModel userServiceModel = await this._userService.GetUserByUsernameAsync(username); + + if (userServiceModel == default(UserServiceModel)) + return RedirectToAction("ErrorNotFound", "Home"); + + UserWebModel userWebModel = this._autoMapper.Map<UserWebModel>(userServiceModel); + + return View(userWebModel); + } + + [HttpGet] + public async Task<IActionResult> Edit() + { + UserServiceModel userServiceModel = await this._userService.GetUserByClaimsAsync(this.HttpContext.User); + + if (userServiceModel == default(UserServiceModel)) + return RedirectToAction("ErrorNotFound", "Home"); + + EditUserWebModel editUserWebModel = this._autoMapper.Map<EditUserWebModel>(userServiceModel); + + return View(editUserWebModel); + } + + [HttpPost] + public async Task<IActionResult> Edit(EditUserWebModel editUserWebModel) + { + if (!ModelState.IsValid) + return View(editUserWebModel); + + if (!this._userService.IsSignedIn(HttpContext.User)) + return RedirectToAction("Login"); + + UserServiceModel loggedInUser = await this._userService.GetUserByClaimsAsync(HttpContext.User); + + UserServiceModel userServiceModel = this._autoMapper.Map<UserServiceModel>(editUserWebModel); + bool result = await this._userService.EditUserAsync(HttpContext.User, userServiceModel); + + if (result) + { + if (loggedInUser.Username != editUserWebModel.Username) + await this._userService.LogoutAsync(); + + return RedirectToAction("Profile", new { username = editUserWebModel.Username }); + } + else + return RedirectToAction("Profile", new { username = loggedInUser.Username }); + } + + [HttpPost] + public async Task<IActionResult> Delete() + { + await this._userService.LogoutAsync(); + bool result = await this._userService.DeleteUserAsync(HttpContext.User); + + if (result) + return RedirectToAction("Login"); + else + return RedirectToAction("Index", "Home"); + } + } +} diff --git a/ExamTemplate/Web/ExamTemplate.Web/Controllers/HomeController.cs b/ExamTemplate/Web/ExamTemplate.Web/Controllers/HomeController.cs new file mode 100644 index 0000000..d9cfc45 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Controllers/HomeController.cs @@ -0,0 +1,33 @@ +using System.Diagnostics;
+using ExamTemplate.Web.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+
+namespace ExamTemplate.Web.Controllers
+{
+ public class HomeController : Controller
+ {
+ private readonly ILogger<HomeController> _logger;
+
+ public HomeController(ILogger<HomeController> logger)
+ {
+ _logger = logger;
+ }
+
+ public IActionResult Index()
+ {
+ return View();
+ }
+
+ [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
+ public IActionResult Error()
+ {
+ return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
+ }
+
+ public IActionResult ErrorNotFound()
+ {
+ return View();
+ }
+ }
+}
diff --git a/ExamTemplate/Web/ExamTemplate.Web/ExamTemplate.Web.csproj b/ExamTemplate/Web/ExamTemplate.Web/ExamTemplate.Web.csproj new file mode 100644 index 0000000..d2c2de4 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/ExamTemplate.Web.csproj @@ -0,0 +1,20 @@ +<Project Sdk="Microsoft.NET.Sdk">
+
+ <ItemGroup> + <ProjectReference Include="..\ExamTemplate.Web.Models\ExamTemplate.Web.Models.csproj" /> + <ProjectReference Include="..\..\Services\ExamTemplate.Services\ExamTemplate.Services.csproj" /> + <ProjectReference Include="..\..\Services\ExamTemplate.Services.Models\ExamTemplate.Services.Models.csproj" /> + <ProjectReference Include="..\..\Common\ExamTemplate.Common\ExamTemplate.Common.csproj" /> + </ItemGroup>
+
+ <ItemGroup> + <PackageReference Include="Automapper" Version="10.1.1" /> + <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" /> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.5.1" /> + </ItemGroup>
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ </PropertyGroup>
+
+</Project>
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Program.cs b/ExamTemplate/Web/ExamTemplate.Web/Program.cs new file mode 100644 index 0000000..be33374 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+
+namespace ExamTemplate.Web
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup<Startup>();
+ });
+ }
+}
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Properties/launchSettings.json b/ExamTemplate/Web/ExamTemplate.Web/Properties/launchSettings.json new file mode 100644 index 0000000..080115b --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:36205",
+ "sslPort": 44322
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Web": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Startup.cs b/ExamTemplate/Web/ExamTemplate.Web/Startup.cs new file mode 100644 index 0000000..c18bca6 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Startup.cs @@ -0,0 +1,134 @@ +using System;
+using System.Linq;
+using ExamTemplate.Common;
+using ExamTemplate.Data;
+using ExamTemplate.Data.Models;
+using ExamTemplate.Services.Services;
+using ExamTemplate.Services.Interfaces;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace ExamTemplate.Web
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddControllersWithViews();
+ services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
+
+ /*
+ * Dependency Injection configuration
+ */
+
+ services.AddTransient<ICloudinaryService, CloudinaryService>(options =>
+ new CloudinaryService(
+ cloudName: this.Configuration.GetSection("Cloud").GetSection("cloudName").Value,
+ apiKey: this.Configuration.GetSection("Cloud").GetSection("apiKey").Value,
+ apiSecret: this.Configuration.GetSection("Cloud").GetSection("apiSecret").Value));
+ services.AddTransient<IUserService, UserService>();
+
+ /*
+ * Database configuration
+ */
+
+ services.AddDbContext<TemplateContext>(options =>
+ options.UseNpgsql(this.Configuration.GetConnectionString("LocalDBConnection")));
+
+ // Needed for SignInManager and UserManager
+ services.AddIdentity<User, IdentityRole<Guid>>(options =>
+ {
+ options.SignIn.RequireConfirmedAccount = false;
+
+ // Password settings
+ options.Password.RequireDigit = false;
+ options.Password.RequireLowercase = false;
+ options.Password.RequireNonAlphanumeric = false;
+ options.Password.RequireUppercase = false;
+ options.Password.RequiredLength = 3;
+ options.Password.RequiredUniqueChars = 0;
+ }).AddRoles<IdentityRole<Guid>>()
+ .AddEntityFrameworkStores<TemplateContext>();
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ app.UseExceptionHandler("/Home/Error");
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
+
+ app.UseStaticFiles();
+
+ app.UseRouting();
+
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllerRoute(
+ name: "default",
+ pattern: "{controller=Home}/{action=Index}/{id?}");
+ endpoints.MapFallbackToController("ErrorNotFound", "Home");
+ });
+
+ /*
+ * Make sure that the database is migrated
+ * and that the User and Admin role exist in database
+ */
+
+ using var serviceScope = app.ApplicationServices.CreateScope();
+ using var dbContext = serviceScope.ServiceProvider.GetRequiredService<TemplateContext>();
+
+ dbContext.Database.Migrate();
+
+ var roleManager = (RoleManager<IdentityRole<Guid>>)serviceScope.ServiceProvider.GetService(typeof(RoleManager<IdentityRole<Guid>>));
+ foreach (string name in RoleConst.GetAllNames())
+ {
+ if (!dbContext.Roles.Any(x => x.Name == name))
+ {
+ IdentityRole<Guid> role = new IdentityRole<Guid>() { Name = name };
+ roleManager.CreateAsync(role).Wait();
+ }
+ }
+
+ /* If you want to create some custom database values at startup
+ * uncomment the following code
+ * and replace OBJCONST_ with your static class with constants (e.g. RoleConst)
+ * replace OBJS_ with the name of the DbSet of your database model (e.g. Roles)
+ * replace OBJ_ with the name of your database model (e.g. Role)
+
+ foreach (string name in OBJCONST_.GetAllNames())
+ {
+ if (!dbContext.OBJS_.Any(x => x.Name == name))
+ {
+ var entity = new OBJ_() { Id = Guid.NewGuid(), Name = name };
+ dbContext.OBJS_.Add(entity);
+ dbContext.SaveChanges();
+ }
+ }
+ */
+ }
+ }
+}
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Edit.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Edit.cshtml new file mode 100644 index 0000000..a088742 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Edit.cshtml @@ -0,0 +1,20 @@ +@model EditUserWebModel +@{ + ViewData["Title"] = "Edit Profile"; +} + +<form asp-controller="Account" asp-action="Edit" method="post"> + <label asp-for="Username">Username:</label> + <input type="text" asp-for="Username" placeholder="@Model.Username"> + <span asp-validation-for="Username" class="form-error"></span> + + <label asp-for="FirstName">First Name:</label> + <input type="text" asp-for="FirstName" placeholder="@Model.FirstName"> + <span asp-validation-for="FirstName" class="form-error"></span> + + <label asp-for="LastName">Last Name:</label> + <input type="text" asp-for="LastName" placeholder="@Model.LastName"> + <span asp-validation-for="LastName" class="form-error"></span> + + <input type="submit" value="Update Profile"> +</form> diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Login.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Login.cshtml new file mode 100644 index 0000000..daa3f3e --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Login.cshtml @@ -0,0 +1,23 @@ +@model LoginUserWebModel +@{ + ViewData["Title"] = "Login"; +} + +<form asp-controller="Account" asp-action="Login" method="post"> + <input type="text" asp-for="Username" placeholder="Username"> + <span asp-validation-for="Username" class="form-error"></span> + + <input type="password" asp-for="Password" placeholder="Password"> + <span asp-validation-for="Password" class="form-error"></span> + + <input type="submit"> + + @if (Model != null) + { + <p class="form-error"> + Invalid credentials or account doesn't exist! + </p> + } +</form> + + diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Profile.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Profile.cshtml new file mode 100644 index 0000000..33fc882 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Profile.cshtml @@ -0,0 +1,33 @@ +@using Microsoft.AspNetCore.Identity + +@inject SignInManager<User> SignInManager +@inject UserManager<User> UserManager + +@model UserWebModel +@{ + ViewData["Title"] = Model.Username + "'s Profile"; +} + +<p> + <h2> + @Model.FirstName @Model.LastName + </h2> + <div> + @Model.Username + </div> + @if (SignInManager.IsSignedIn(User)) + { + @if(UserManager.GetUserName(User) == Model.Username) + { + <br> + + <form asp-controller="Account" asp-action="Edit" method="get"> + <input type="submit" value="Edit Profile"> + </form> + + <form asp-controller="Account" asp-action="Delete" method="post"> + <input type="submit" value="Delete Profile"> + </form> + } + } +</p> diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Register.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Register.cshtml new file mode 100644 index 0000000..e436d72 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Account/Register.cshtml @@ -0,0 +1,27 @@ +@model RegisterUserWebModel +@{ + ViewData["Title"] = "Register"; +} + +<form asp-controller="Account" asp-action="Register" method="post"> + <input type="text" asp-for="FirstName" placeholder="FirstName"> + <span asp-validation-for="FirstName" class="form-error"></span> + + <input type="text" asp-for="LastName" placeholder="LastName"> + <span asp-validation-for="LastName" class="form-error"></span> + + <input type="text" asp-for="Username" placeholder="Username"> + <span asp-validation-for="Username" class="form-error"></span> + + <input type="password" asp-for="Password" placeholder="Password"> + <span asp-validation-for="Password" class="form-error"></span> + + <input type="submit"> + + @if (Model != null) + { + <p class="form-error"> + Couldn't register account! + </p> + } +</form> diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Home/Index.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Home/Index.cshtml new file mode 100644 index 0000000..56ea950 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ +@{
+ ViewData["Title"] = "Home";
+}
+
+<div class="text-center">
+ <h1 class="display-4">Welcome</h1>
+ <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
+</div>
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/Error.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/Error.cshtml new file mode 100644 index 0000000..5a7ce95 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/Error.cshtml @@ -0,0 +1,25 @@ +@model ErrorViewModel
+@{
+ ViewData["Title"] = "Error";
+}
+
+<h1 class="text-danger">Error.</h1>
+<h2 class="text-danger">An error occurred while processing your request.</h2>
+
+@if (Model.ShowRequestId)
+{
+ <p>
+ <strong>Request ID:</strong> <code>@Model.RequestId</code>
+ </p>
+}
+
+<h3>Development Mode</h3>
+<p>
+ Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
+</p>
+<p>
+ <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
+ It can result in displaying sensitive information from exceptions to end users.
+ For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
+ and restarting the app.
+</p>
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/ErrorNotFound.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/ErrorNotFound.cshtml new file mode 100644 index 0000000..39fc5ca --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/ErrorNotFound.cshtml @@ -0,0 +1,10 @@ +@{ + ViewData["Title"] = "404: Not found!"; +} + +<h2> + 404: Not found! +</h2> +<p> + The page you're looking for couldn't be found! +</p> diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_FooterContent.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_FooterContent.cshtml new file mode 100644 index 0000000..60a21aa --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_FooterContent.cshtml @@ -0,0 +1,8 @@ +<div class="border-top box-shadow"> + <div class="footer-content middle-content-container"> + <section> + IT-kariera ExamTemplate - 2021 + </section> + <div class="flex-spacer"></div> + </div> +</div> diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_Layout.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000..dd7bf82 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_Layout.cshtml @@ -0,0 +1,25 @@ +<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>@ViewData["Title"]</title>
+ <link rel="stylesheet" href="~/css/site.css" />
+ <link rel="stylesheet" href="~/css/styles.css" />
+</head>
+<body>
+ <header>
+ <partial name="_Navbar.cshtml" />
+ </header>
+
+ <main class="main">
+ <div class="middle-content-container">
+ @RenderBody()
+ </div>
+ </main>
+
+ <footer class="border-top box-shadow">
+ <partial name="_FooterContent.cshtml" />
+ </footer>
+</body>
+</html>
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_Navbar.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_Navbar.cshtml new file mode 100644 index 0000000..0ec5c4d --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/Shared/_Navbar.cshtml @@ -0,0 +1,31 @@ +@using Microsoft.AspNetCore.Identity + +@inject SignInManager<User> SignInManager +@inject UserManager<User> UserManager + +<nav class="navbar border-bottom box-shadow"> + <div class="middle-content-container navbar-contents"> + <section> + <b>ExamTemplate</b> + <a asp-controller="Home" asp-action="Index">Home</a> + </section> + <div class="flex-spacer"></div> + <section> + @if (SignInManager.IsSignedIn(User)) + { + <a asp-controller="Account" asp-action="Profile" asp-route-username="@UserManager.GetUserName(User)"> + @UserManager.GetUserName(User) + </a> + + <form asp-controller="Account" asp-action="Logout" method="post"> + <input type="submit" value="Logout"> + </form> + } + else + { + <a asp-controller="Account" asp-action="Login">Login</a> + <a asp-controller="Account" asp-action="Register">Register</a> + } + </section> + </div> +</nav> diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/_ViewImports.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/_ViewImports.cshtml new file mode 100644 index 0000000..18502e4 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/_ViewImports.cshtml @@ -0,0 +1,5 @@ +@using ExamTemplate.Web
+@using ExamTemplate.Web.Models
+@using ExamTemplate.Web.Models.User
+@using ExamTemplate.Data.Models
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
diff --git a/ExamTemplate/Web/ExamTemplate.Web/Views/_ViewStart.cshtml b/ExamTemplate/Web/ExamTemplate.Web/Views/_ViewStart.cshtml new file mode 100644 index 0000000..3a04d05 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{
+ Layout = "_Layout";
+}
diff --git a/ExamTemplate/Web/ExamTemplate.Web/appsettings.json b/ExamTemplate/Web/ExamTemplate.Web/appsettings.json new file mode 100644 index 0000000..f1b58be --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/appsettings.json @@ -0,0 +1,18 @@ +{
+ "ConnectionStrings": {
+ "LocalDBConnection": "Server=localhost;Port=5432;Database=TemplateContext;User Id=;Password=;"
+ },
+ "Cloud": {
+ "cloudName": "ExamTemplate",
+ "apiKey": "",
+ "apiSecret": ""
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/ExamTemplate/Web/ExamTemplate.Web/wwwroot/css/site.css b/ExamTemplate/Web/ExamTemplate.Web/wwwroot/css/site.css new file mode 100644 index 0000000..5923427 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/wwwroot/css/site.css @@ -0,0 +1,79 @@ +/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
+for details on configuring this project to bundle and minify static web assets. */
+
+a.navbar-brand {
+ white-space: normal;
+ text-align: center;
+ word-break: break-all;
+}
+
+/* Provide sufficient contrast against white background */
+a {
+ color: #0366d6;
+}
+
+.btn-primary {
+ color: #fff;
+ background-color: #1b6ec2;
+ border-color: #1861ac;
+}
+
+.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
+ color: #fff;
+ background-color: #1b6ec2;
+ border-color: #1861ac;
+}
+
+/* Sticky footer styles
+-------------------------------------------------- */
+html {
+ font-size: 14px;
+}
+@media (min-width: 768px) {
+ html {
+ font-size: 16px;
+ }
+}
+
+.border-top {
+ border-top: 1px solid #e5e5e5;
+}
+.border-bottom {
+ border-bottom: 1px solid #e5e5e5;
+}
+
+.box-shadow {
+ box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
+}
+
+button.accept-policy {
+ font-size: 1rem;
+ line-height: inherit;
+}
+
+/* Sticky footer styles
+-------------------------------------------------- */
+html {
+ position: relative;
+ min-height: 100%;
+ line-height: 1.15;
+}
+
+body {
+ min-height: 100vh;
+ margin: 0;
+ font-size: 1.15em;
+ background-color: white;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+}
+
+/* The following is kinda dirty, that's why it's separated */
+
+body {
+ display: flex;
+ flex-direction: column;
+}
+
+body > main {
+ flex: 1;
+}
diff --git a/ExamTemplate/Web/ExamTemplate.Web/wwwroot/css/styles.css b/ExamTemplate/Web/ExamTemplate.Web/wwwroot/css/styles.css new file mode 100644 index 0000000..e7fc7b3 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/wwwroot/css/styles.css @@ -0,0 +1,143 @@ + +/* Change the maximum width of content (stuff inside pages and navbar), + * depending on width of browser window. + * Configuration copied from default Bootstrap + */ + +@media (min-width: 576px) { + :root { + --max-content-width: 540px; + } +} + +@media (min-width: 768px) { + :root { + --max-content-width: 720px; + } +} + +@media (min-width: 992px) { + :root { + --max-content-width: 960px; + } +} + +@media (min-width: 1200px) { + :root { + --max-content-width: 1140px; + } +} + +/* Main */ + +.main { + width: 100%; + height: 100%; +} + + /* Stuff that you need in the middle portion + * of the screen (like the stuff inside the + * navbar and footer) should be inside + * an element with this tag + */ +.middle-content-container { + max-width: var(--max-content-width); + margin-left: auto; + margin-right: auto; +} + +/* Navbar and footer */ + +.navbar, .footer-content { + width: 100%; + min-height: 45px; + + padding-top: 8px; + padding-bottom: 8px; + + display: flex; + align-items: center; + justify-content: center; +} + +.navbar section > :not(*:first-child) { + padding-left: 5px; +} + +.navbar section > :not(*:last-child) { + padding-right: 5px; +} + +.navbar a { + text-decoration: none; + color: #343a40; +} + +.navbar a:hover { + color: black; +} + +.navbar-contents { + width: 100%; + display: flex; + box-sizing: border-box; +} + +.navbar-contents > * { + display: flex; + align-items: center; + justify-content: center; +} + +/* Forms */ + +form { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: fit-content; +} + +.main > div > form { + margin-top: 10px; +} + +.main > div > form:first-child { + width: 100%; + max-width: 300px; + margin-left: auto; + margin-right: auto; +} + +form > * { + width: 100%; + box-sizing: border-box; +} + +input { + margin: 5px; + padding: 9px; + border: 1px solid darkgrey; + border-radius: 4px; +} + +input[type="submit"] { + color: white; + background-color: black; +} + +input[type="submit"]:hover { + cursor: pointer; +} + +.form-error { + font-size: 0.8em; + color: red; +} + +/* Other general stuff */ + +.flex-spacer { + flex: 1; +} diff --git a/ExamTemplate/Web/ExamTemplate.Web/wwwroot/favicon.ico b/ExamTemplate/Web/ExamTemplate.Web/wwwroot/favicon.ico Binary files differnew file mode 100644 index 0000000..a3a7999 --- /dev/null +++ b/ExamTemplate/Web/ExamTemplate.Web/wwwroot/favicon.ico |
