1
0

Updated site

This commit is contained in:
Michael Gordeev
2020-03-12 22:01:12 +03:00
parent 1e6aea6b17
commit 17bdf1ea77
97 changed files with 88723 additions and 3602 deletions
-4
View File
@@ -1,4 +0,0 @@
[*.cs]
# CA1303: Do not pass literals as localized parameters
dotnet_diagnostic.CA1303.severity = none
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers; using MyWebsite.Controllers;
using MyWebsite.Models.Databases; using MyWebsite.Models.Databases;
using System.Linq;
namespace MyWebsite.Areas.API namespace MyWebsite.Areas.API
{ {
@@ -8,10 +9,12 @@ namespace MyWebsite.Areas.API
[Route("API/[controller]")] [Route("API/[controller]")]
public class GUTScheduleController : ExtendedController public class GUTScheduleController : ExtendedController
{ {
public GUTScheduleController(DatabaseContext context) : base(context) { } GUTScheduleDatabaseContext databaseContext;
public GUTScheduleController(DatabaseContext context, GUTScheduleDatabaseContext db) : base(context) =>
databaseContext = db;
[Route("SemesterOffsetDay")] [Route("SemesterOffsetDay")]
public string SemesterOffsetDay() => public string SemesterOffsetDay() =>
Database.CustomData.Find("gut.schedule.semester.offset")?.Value ?? "undefined"; databaseContext.OffsetDates?.FirstOrDefault()?.Value ?? "undefined";
} }
} }
@@ -7,6 +7,7 @@ using MyWebsite.Models.Databases;
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
namespace MyWebsite.Areas.Admin.Controllers namespace MyWebsite.Areas.Admin.Controllers
{ {
@@ -16,21 +17,43 @@ namespace MyWebsite.Areas.Admin.Controllers
{ {
public BadgesController(DatabaseContext context) : base(context) { } public BadgesController(DatabaseContext context) : base(context) { }
[HttpGet]
public IActionResult Index() => public IActionResult Index() =>
View(Database.Badges); View(Database.Badges);
[HttpPost]
public IActionResult Index(IFormFile badgeImage)
{
if (badgeImage == null)
throw new ArgumentNullException(nameof(badgeImage));
System.Drawing.Image image = System.Drawing.Image.FromStream(badgeImage.OpenReadStream());
if (System.IO.File.Exists(Directory.GetCurrentDirectory() + "/wwwroot/images/Badges/" + badgeImage.FileName))
ModelState.AddModelError("Error", $"Badge image with such name ({badgeImage.FileName}) is already esists");
else if (image.Width != 64 || image.Height != 64 || !badgeImage.FileName.EndsWith(".PNG", true, CultureInfo.InvariantCulture))
ModelState.AddModelError("Error", "The file must be EXACTLY 64x64 pixels PNG image");
else
using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Badges/" + badgeImage.FileName))
badgeImage.CopyTo(stream);
return View(Database.Badges);
}
[HttpGet] [HttpGet]
public IActionResult Edit(string id) => public IActionResult Edit(string id) =>
View(Database.Badges.Find(id)); View(Database.Badges.Find(id));
[HttpPost] [HttpPost]
public IActionResult Edit(BadgeModel model, IFormFile file = null) public IActionResult Edit(BadgeModel model)
{ {
if (model == null) if (model == null)
throw new ArgumentNullException(nameof(model)); throw new ArgumentNullException(nameof(model));
if (file != null) if (!ModelState.IsValid)
UploadFile(file, model); {
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
Database.Badges.Update(model); Database.Badges.Update(model);
Database.SaveChanges(); Database.SaveChanges();
@@ -45,6 +68,13 @@ namespace MyWebsite.Areas.Admin.Controllers
[HttpPost] [HttpPost]
public IActionResult Delete(BadgeModel model) public IActionResult Delete(BadgeModel model)
{ {
if (Database.Projects.ToList().Any(i => i.Badges.Contains(model.Name, StringComparison.InvariantCulture)))
{
ModelState.Clear();
ModelState.AddModelError("Error", "Remove badge references from projects descriptions in order to delete the badge");
return View(Database.Badges.Find(model?.Name));
}
Database.Badges.Remove(model); Database.Badges.Remove(model);
Database.SaveChanges(); Database.SaveChanges();
@@ -53,41 +83,40 @@ namespace MyWebsite.Areas.Admin.Controllers
[HttpGet] [HttpGet]
public IActionResult Create() => public IActionResult Create() =>
View(); View(model: null);
[HttpPost] [HttpPost]
public IActionResult Create(BadgeModel model, IFormFile file = null) public IActionResult Create(BadgeModel model)
{ {
if (model == null) if (model == null)
throw new ArgumentNullException(nameof(model)); throw new ArgumentNullException(nameof(model));
if (file != null)
return UploadFile(file, model);
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
ModelState.AddModelError("Error", "Invalid data"); ModelState.AddModelError("Error", "Invalid data");
return View(model); return View(model);
} }
if (Database.Badges.Any(i => i.Name == model.Name))
{
ModelState.AddModelError("Error", $"Badge '{model.Name}' is already exists");
return View(model);
}
Database.Badges.Add(model); Database.Badges.Add(model);
Database.SaveChanges(); Database.SaveChanges();
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
private IActionResult UploadFile(IFormFile file, BadgeModel model) [HttpGet]
public IActionResult DeleteBadgeImage(string id)
{ {
System.Drawing.Image image = System.Drawing.Image.FromStream(file.OpenReadStream()); string path = Directory.GetCurrentDirectory() + "/wwwroot/images/Badges/" + id;
if (image.Width != 64 || image.Height != 64 || !file.FileName.EndsWith(".PNG", true, CultureInfo.InvariantCulture)) if (System.IO.File.Exists(path))
{ System.IO.File.Delete(path);
ViewData["UploadException"] = "error";
return View(model);
}
using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Badges/" + file.FileName))
file.CopyTo(stream);
return Redirect(Request.Path.Value); return RedirectToAction("Index");
} }
} }
} }
@@ -3,6 +3,8 @@ using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers; using MyWebsite.Controllers;
using MyWebsite.Models; using MyWebsite.Models;
using MyWebsite.Models.Databases; using MyWebsite.Models.Databases;
using System;
using System.Linq;
namespace MyWebsite.Areas.Admin.Controllers namespace MyWebsite.Areas.Admin.Controllers
{ {
@@ -15,6 +17,23 @@ namespace MyWebsite.Areas.Admin.Controllers
public IActionResult Index() => public IActionResult Index() =>
View(Database.Links); View(Database.Links);
[HttpPost]
public IActionResult Index(string[] reorderList)
{
if(reorderList?.Length != Database.Links.Count())
{
ModelState.AddModelError("Error", "Invalid or incomplete data recieved");
return View(Database.Links);
}
for (int i = 0; i < reorderList.Length; i++)
Database.Links.Find(reorderList[i]).Order = i;
Database.SaveChanges();
return View(Database.Links);
}
[HttpGet] [HttpGet]
public IActionResult Edit(string id) => public IActionResult Edit(string id) =>
View(Database.Links.Find(id)); View(Database.Links.Find(id));
@@ -22,6 +41,15 @@ namespace MyWebsite.Areas.Admin.Controllers
[HttpPost] [HttpPost]
public IActionResult Edit(LinkModel model) public IActionResult Edit(LinkModel model)
{ {
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
Database.Links.Update(model); Database.Links.Update(model);
Database.SaveChanges(); Database.SaveChanges();
@@ -43,16 +71,26 @@ namespace MyWebsite.Areas.Admin.Controllers
[HttpGet] [HttpGet]
public IActionResult Create() => public IActionResult Create() =>
View(); View(model: null);
[HttpPost] [HttpPost]
public IActionResult Create(LinkModel model) public IActionResult Create(LinkModel model)
{ {
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
ModelState.AddModelError("Error", "Invalid data"); ModelState.AddModelError("Error", "Invalid data");
return View(model); return View(model);
} }
model.Order = Database.Links.Count();
if (Database.Links.Any(i => i.Name == model.Name))
{
ModelState.AddModelError("Error", $"Link '{model.Name}' is already exists");
return View(model);
}
Database.Links.Add(model); Database.Links.Add(model);
Database.SaveChanges(); Database.SaveChanges();
@@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers; using MyWebsite.Controllers;
using MyWebsite.Helpers; using MyWebsite.Helpers;
using MyWebsite.Models;
using MyWebsite.Models.Databases; using MyWebsite.Models.Databases;
using System.Linq; using System.Linq;
@@ -20,29 +21,45 @@ namespace MyWebsite.Areas.Admin.Controllers
View(viewPath); View(viewPath);
[HttpPost] [HttpPost]
public IActionResult Index(Models.CredentialModel model) public IActionResult Index(string key, string value)
{ {
MyWebsite.Models.CredentialModel credential = Database.Users.FirstOrDefault(); if(string.IsNullOrWhiteSpace(key))
if (model == null || model.Current.Email != credential.Email || !Encryptor.VerifyHash(model.Current.Password, credential.Password))
{ {
ModelState.AddModelError("Authorization error", "Invaild e-mail or password"); ModelState.AddModelError("Validation error", "Unable to identify data to update");
return View(viewPath, model); return View(viewPath);
} }
if(string.IsNullOrWhiteSpace(model.Updated.Email) && string.IsNullOrWhiteSpace(model.Current.Password)) if(string.IsNullOrWhiteSpace(value))
{ {
ModelState.AddModelError("Validation error", "No data to update"); ModelState.AddModelError("Validation error", "No data provided");
return View(viewPath, model); return View(viewPath);
} }
Database.Users.Remove(credential); CredentialModel credential = Database.Users.FirstOrDefault();
Database.SaveChanges(); Database.Users.RemoveRange(Database.Users);
if(!string.IsNullOrWhiteSpace(model.Current.Email))
credential.Email = model.Updated.Email; switch (key)
if(!string.IsNullOrWhiteSpace(model.Current.Password)) {
credential.Password = Encryptor.ComputeHash(model.Updated.Password); case "password":
Database.Users.Add(credential); Database.Users.Add(new CredentialModel
{
Email = credential.Email,
Password = Encryptor.ComputeHash(value)
});
break;
case "email":
Database.Users.Add(new CredentialModel
{
Email = value,
Password = credential.Password
});
break;
default:
ModelState.AddModelError("Processing error", "Provided data is missing or read-only");
return View(viewPath);
}
Database.SaveChanges(); Database.SaveChanges();
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MyWebsite.Areas.Admin.Controllers
{
[Area("Admin")]
[Authorize]
public class FoxTubeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Screenshots()
{
return View();
}
}
}
@@ -4,6 +4,9 @@ using MyWebsite.Controllers;
using MyWebsite.Models; using MyWebsite.Models;
using MyWebsite.Models.Databases; using MyWebsite.Models.Databases;
using System.Linq; using System.Linq;
using System;
using MyWebsite.ViewModels;
using System.Globalization;
namespace MyWebsite.Areas.Admin.Controllers namespace MyWebsite.Areas.Admin.Controllers
{ {
@@ -11,31 +14,120 @@ namespace MyWebsite.Areas.Admin.Controllers
[Area("Admin")] [Area("Admin")]
public class GUTScheduleController : ExtendedController public class GUTScheduleController : ExtendedController
{ {
const string scheduleOffsetId = "gut.schedule.semester.offset";
const string viewPath = "Areas/Admin/Views/Shared/GUTSchedule.cshtml"; const string viewPath = "Areas/Admin/Views/Shared/GUTSchedule.cshtml";
public GUTScheduleController(DatabaseContext context) : base(context) { } private GUTScheduleDatabaseContext db;
public GUTScheduleController(DatabaseContext context, GUTScheduleDatabaseContext databaseContext) : base(context) =>
db = databaseContext;
[HttpGet] [HttpGet]
public IActionResult Index() => public IActionResult Index()
View(viewPath, Database.CustomData.Find(scheduleOffsetId) ?? new CustomData { Key = scheduleOffsetId, Value = "undefined" }); {
ViewData["Policies"] = db.PrivacyPolicies;
return View(viewPath, db.OffsetDates.FirstOrDefault());
}
[HttpPost] [HttpPost]
public IActionResult Index(CustomData model) public IActionResult Index(CustomData model)
{ {
if(!ModelState.IsValid) if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{ {
ModelState.AddModelError("Error", "Invalid data"); ModelState.AddModelError("Error", "Invalid data");
return View(model); return View(model);
} }
if(Database.CustomData.Any(i => i.Key == scheduleOffsetId)) db.OffsetDates.RemoveRange(db.OffsetDates);
Database.CustomData.Update(model); db.OffsetDates.Add(model);
else
Database.CustomData.Add(model);
Database.SaveChanges(); db.SaveChanges();
return View(viewPath, model);
return Index();
} }
[HttpGet]
public IActionResult CreatePolicy()
{
ViewData["Caption"] = "privacy policy";
return View(viewName: "Areas/Admin/Views/Resume/Create.cshtml");
}
[HttpPost]
public IActionResult CreatePolicy(ResumeModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View("Areas/Admin/Views/Resume/Create.cshtml", model);
}
model.LastUpdate = DateTime.Now;
if (db.PrivacyPolicies.Any(i => i.Language == model.Language))
{
ModelState.AddModelError("Error", $"Resume with this language ({model.Language}) is already exists");
return View("Areas/Admin/Views/Resume/Create.cshtml", model);
}
db.PrivacyPolicies.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult DeletePolicy(string id)
{
ViewData["Caption"] = "privacy policy";
return View("Areas/Admin/Views/Resume/Delete.cshtml", db.PrivacyPolicies.Find(id));
}
[HttpPost]
public IActionResult DeletePolicy(ResumeModel model)
{
db.PrivacyPolicies.Remove(model);
db.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult EditPolicy(string id)
{
ViewData["Caption"] = "privacy policy";
return View("Areas/Admin/Views/Resume/Edit.cshtml", db.PrivacyPolicies.Find(id));
}
[HttpPost]
public IActionResult EditPolicy(ResumeModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View("Areas/Admin/Views/Resume/Edit.cshtml", model);
}
model.LastUpdate = DateTime.Now;
db.PrivacyPolicies.Update(model);
db.SaveChanges();
return RedirectToAction("Index");
}
[AllowAnonymous]
[Route("/Projects/GUTSchedule/PrivacyPolicy")]
public IActionResult PrivacyPolicy() =>
View("Areas/Projects/Views/GUTSchedule/PrivacyPolicy.cshtml", new ResumeViewModel(db.PrivacyPolicies.Find(CultureInfo.CurrentCulture.TwoLetterISOLanguageName) ?? db.PrivacyPolicies.Find("en") ?? db.PrivacyPolicies.Find("ru"), Database));
} }
} }
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MyWebsite.Areas.Admin.Models;
using MyWebsite.Controllers; using MyWebsite.Controllers;
using MyWebsite.Models; using MyWebsite.Models;
using MyWebsite.Models.Databases; using MyWebsite.Models.Databases;
@@ -19,13 +19,8 @@ namespace MyWebsite.Areas.Admin.Controllers
View(Database.Gallery); View(Database.Gallery);
[HttpGet] [HttpGet]
public IActionResult Edit(Guid id) public IActionResult Edit(string id) =>
{ View(Database.Gallery.Find(id));
if (Database.Gallery.Find(id) is ImageModel model)
return View(model);
else
return NotFound();
}
[HttpPost] [HttpPost]
public IActionResult Edit(ImageModel model) public IActionResult Edit(ImageModel model)
@@ -43,13 +38,8 @@ namespace MyWebsite.Areas.Admin.Controllers
} }
[HttpGet] [HttpGet]
public IActionResult Delete(Guid id) public IActionResult Delete(string id) =>
{ View(Database.Gallery.Find(id));
if (Database.Gallery.Find(id) is ImageModel model)
return View(model);
else
return NotFound();
}
[HttpPost] [HttpPost]
public IActionResult Delete(ImageModel model) public IActionResult Delete(ImageModel model)
@@ -65,31 +55,28 @@ namespace MyWebsite.Areas.Admin.Controllers
[HttpGet] [HttpGet]
public IActionResult Upload() => public IActionResult Upload() =>
View(); View(model: null);
[HttpPost] [HttpPost]
public IActionResult Upload(ArtworkModel model) public IActionResult Upload(ImageModel model, IFormFile file)
{ {
if (model == null)
throw new ArgumentNullException(nameof(model));
if (file == null)
throw new ArgumentNullException(nameof(file));
model.FileName = file.FileName;
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
ModelState.AddModelError("Error", "Invalid data"); ModelState.AddModelError("Error", "Invalid data");
return View(model); return View(model);
} }
using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.File.FileName)) using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + file.FileName))
model.File.CopyTo(stream); file.CopyTo(stream);
ImageModel image = new ImageModel Database.Gallery.Add(model);
{
EnglishTitle = model.EnglishTitle,
RussianTitle = model.RussianTitle,
EnglishDescription = model.EnglishDescription,
RussianDescription = model.RussianDescription,
CreationDate = model.CreationDate,
FileName = model.File.FileName
};
Database.Gallery.Add(image);
Database.SaveChanges(); Database.SaveChanges();
return RedirectToAction("Index"); return RedirectToAction("Index");
@@ -1,4 +1,6 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers; using MyWebsite.Controllers;
@@ -13,12 +15,32 @@ namespace MyWebsite.Areas.Admin.Controllers
{ {
public ProjectsController(DatabaseContext context) : base(context) { } public ProjectsController(DatabaseContext context) : base(context) { }
[HttpGet]
public IActionResult Index() => public IActionResult Index() =>
View((Database.Projects as IEnumerable<ProjectModel>, Database.Badges as IEnumerable<BadgeModel>)); View((Database.Projects as IEnumerable<ProjectModel>, Database.Badges as IEnumerable<BadgeModel>));
[HttpPost]
public IActionResult Index(Guid[] reorderList)
{
if (reorderList?.Length != Database.Projects.Count())
ModelState.AddModelError("Error", "Invalid or incomplete data recieved");
else
{
for (int i = 0; i < reorderList.Length; i++)
Database.Projects.Find(reorderList[i]).Order = i;
Database.SaveChanges();
}
return View((Database.Projects as IEnumerable<ProjectModel>, Database.Badges as IEnumerable<BadgeModel>));
}
[HttpGet] [HttpGet]
public IActionResult Delete(decimal id) => public IActionResult Delete(Guid id)
View(Database.Projects.Find(id)); {
ViewData["Badges"] = Database.Badges.ToList();
return View(Database.Projects.Find(id));
}
[HttpPost] [HttpPost]
public IActionResult Delete(ProjectModel model) public IActionResult Delete(ProjectModel model)
@@ -30,17 +52,50 @@ namespace MyWebsite.Areas.Admin.Controllers
} }
[HttpGet] [HttpGet]
public IActionResult Create() => public IActionResult Edit(Guid id)
View(); {
ViewData["Badges"] = Database.Badges.ToList();
return View(Database.Projects.Find(id));
}
[HttpPost]
public IActionResult Edit(ProjectModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
ViewData["Badges"] = Database.Badges.ToList();
return View(model);
}
Database.Projects.Update(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Create()
{
ViewData["Badges"] = Database.Badges.ToList();
return View(model: null);
}
[HttpPost] [HttpPost]
public IActionResult Create(ProjectModel model) public IActionResult Create(ProjectModel model)
{ {
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
ModelState.AddModelError("Error", "Invalid data"); ModelState.AddModelError("Error", "Invalid data");
return View(model); return View(model);
} }
model.Order = Database.Projects.Count();
Database.Projects.Add(model); Database.Projects.Add(model);
Database.SaveChanges(); Database.SaveChanges();
@@ -48,16 +103,4 @@ namespace MyWebsite.Areas.Admin.Controllers
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
} }
public class ProjectEditorModel
{
public double Id { get; set; }
public string EnglishTitle { get; set; }
public string RussianTitle { get; set; }
public string EnglishDescription { get; set; }
public string RussianDescription { get; set; }
public string Link { get; set; }
public string EnglishLinkCaption { get; set; }
public string RussianLinkCaption { get; set; }
}
} }
@@ -4,6 +4,7 @@ using MyWebsite.Controllers;
using MyWebsite.Models; using MyWebsite.Models;
using MyWebsite.Models.Databases; using MyWebsite.Models.Databases;
using System; using System;
using System.Linq;
namespace MyWebsite.Areas.Admin.Controllers namespace MyWebsite.Areas.Admin.Controllers
{ {
@@ -55,7 +56,7 @@ namespace MyWebsite.Areas.Admin.Controllers
[HttpGet] [HttpGet]
public IActionResult Create() => public IActionResult Create() =>
View(); (this as Controller).View();
[HttpPost] [HttpPost]
public IActionResult Create(ResumeModel model) public IActionResult Create(ResumeModel model)
@@ -71,6 +72,12 @@ namespace MyWebsite.Areas.Admin.Controllers
model.LastUpdate = DateTime.Now; model.LastUpdate = DateTime.Now;
if(Database.Resume.Any(i => i.Language == model.Language))
{
ModelState.AddModelError("Error", $"Resume with this language ({model.Language}) is already exists");
return View(model);
}
Database.Resume.Add(model); Database.Resume.Add(model);
Database.SaveChanges(); Database.SaveChanges();
@@ -1,29 +0,0 @@
using Microsoft.AspNetCore.Http;
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace MyWebsite.Areas.Admin.Models
{
public class ArtworkModel
{
[Required]
[DisplayName("Title (en)")]
public string EnglishTitle { get; set; }
[DisplayName("Title (ru)")]
public string RussianTitle { get; set; }
[Required]
[DisplayName("Description (en)")]
public string EnglishDescription { get; set; }
[DisplayName("Description (ru)")]
public string RussianDescription { get; set; }
[Required]
[DisplayName("Created")]
public DateTime CreationDate { get; set; }
[Required]
[DisplayName("File")]
public IFormFile File { get; set; }
}
}
@@ -1,14 +0,0 @@
using MyWebsite.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyWebsite.Areas.Admin.Models
{
public class CredentialModel
{
public MyWebsite.Models.CredentialModel Current { get; set; } = new MyWebsite.Models.CredentialModel();
public MyWebsite.Models.CredentialModel Updated { get; set; } = new MyWebsite.Models.CredentialModel();
}
}
@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyWebsite.Areas.Admin.Models
{
public enum ReorderDirection { Up, Down }
public class ReorderModel
{
public string ItemId { get; set; }
public ReorderDirection Direction { get; set; }
}
}
@@ -1,31 +1,24 @@
@model MyWebsite.Models.BadgeModel @model MyWebsite.Models.BadgeModel
@using System.IO;
@{ @{
ViewData["Title"] = "Create badge"; ViewData["Title"] = "Create badge";
List<SelectListItem> files = new List<SelectListItem>();
foreach (string path in Directory.GetFiles(Directory.GetCurrentDirectory() + "/wwwroot/images/Badges"))
{
string fileName = System.IO.Path.GetFileNameWithoutExtension(path);
files.Add(new SelectListItem(fileName, fileName));
}
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Create project badge</h1> <h1>Create project badge</h1>
</header> </header>
<article> <article>
<form asp-action="Create" oninput="UpdatePreview();"> <form asp-action="Create" oninput="UpdatePreview();">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<div> <div>
<label asp-for="Name"></label> <label asp-for="Name"></label>
<input asp-for="Name" type="text" /> <input asp-for="Name" type="text" placeholder="Enter unique badge name" required />
<span asp-validation-for="Name" class="text-danger"></span> <span asp-validation-for="Name" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="EnglishDescription"></label> <label asp-for="EnglishDescription"></label>
<input asp-for="EnglishDescription" type="text" /> <input asp-for="EnglishDescription" type="text" required />
<span asp-validation-for="EnglishDescription" class="text-danger"></span> <span asp-validation-for="EnglishDescription" class="text-danger"></span>
</div> </div>
<div> <div>
@@ -36,22 +29,19 @@
<div> <div>
<label asp-for="Image"></label> <label asp-for="Image"></label>
<div class="image-selector"> <div class="image-selector">
<select asp-for="Image" asp-items="files" onchange="UpdatePreview();"></select> <select asp-for="Image" onchange="UpdatePreview();" required>
@foreach (string path in System.IO.Directory.GetFiles(System.IO.Directory.GetCurrentDirectory() + "/wwwroot/images/Badges"))
{
string file = System.IO.Path.GetFileNameWithoutExtension(path);
<option value="@file">@file</option>
}
</select>
<span>.png</span> <span>.png</span>
</div> </div>
<span asp-validation-for="Image" class="text-danger"></span> <span asp-validation-for="Image" class="text-danger"></span>
</div> </div>
<partial name="Preview.cshtml" /> <partial name="Preview.cshtml" />
<input type="submit" value="Create" class="btn" /> <input type="submit" value="Create" />
</form>
<hr />
<h2>Upload badge image</h2>
<form method="post" enctype="multipart/form-data">
<input type="file" accept="image/png" name="file" />
<label for="file" style="@(ViewData.Keys.Contains("UploadException") ? "color: red" : "")">File must be a 64x64 PNG image</label>
<input type="submit" value="Upload image" class="btn" />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -5,12 +5,13 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Delete project badge</h1> <h1>Delete project badge</h1>
<h3>Are you sure you want to delete this?</h3> <h3>Are you sure you want to delete this?</h3>
</header> </header>
<article> <article>
<div asp-validation-summary="All" class="text-danger"></div>
<p> <p>
<b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br /> <b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br />
<b>@Html.DisplayNameFor(model => model.EnglishDescription):</b> @Model.EnglishDescription<br /> <b>@Html.DisplayNameFor(model => model.EnglishDescription):</b> @Model.EnglishDescription<br />
@@ -23,15 +24,18 @@
<form asp-action="Delete"> <form asp-action="Delete">
<input hidden asp-for="Name" /> <input hidden asp-for="Name" />
<input type="submit" value="Delete" class="btn-danger" /> <input type="submit" value="Delete" required />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<style type="text/css"> {
.badge { <style type="text/css">
.badge
{
height: 50px; height: 50px;
width: 50px; width: 50px;
background-size: contain; background-size: contain;
} }
</style> </style>
}
@@ -1,31 +1,24 @@
@using System.IO; @model MyWebsite.Models.BadgeModel
@model MyWebsite.Models.BadgeModel
@{ @{
ViewData["Title"] = "Edit badge"; ViewData["Title"] = "Edit badge";
List<SelectListItem> files = new List<SelectListItem>();
foreach (string path in Directory.GetFiles(Directory.GetCurrentDirectory() + "/wwwroot/images/Badges"))
{
string fileName = System.IO.Path.GetFileNameWithoutExtension(path);
files.Add(new SelectListItem(fileName, fileName));
}
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Edit project badge</h1> <h1>Edit project badge</h1>
</header> </header>
<article> <article>
<form asp-action="Edit" oninput="UpdatePreview();"> <form asp-action="Edit" oninput="UpdatePreview();">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<div> <div>
<label asp-for="Name"></label> <label asp-for="Name"></label>
<input asp-for="Name" type="text" class="readonly" readonly /> <input asp-for="Name" type="text" readonly />
<span asp-validation-for="Name" class="text-danger"></span> <span asp-validation-for="Name" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="EnglishDescription"></label> <label asp-for="EnglishDescription"></label>
<input asp-for="EnglishDescription" type="text" /> <input asp-for="EnglishDescription" type="text" required />
<span asp-validation-for="EnglishDescription" class="text-danger"></span> <span asp-validation-for="EnglishDescription" class="text-danger"></span>
</div> </div>
<div> <div>
@@ -36,22 +29,19 @@
<div> <div>
<label asp-for="Image"></label> <label asp-for="Image"></label>
<div class="image-selector"> <div class="image-selector">
<select asp-for="Image" asp-items="files" onchange="UpdatePreview();"></select> <select asp-for="Image" onchange="UpdatePreview();" required>
@foreach (string path in System.IO.Directory.GetFiles(System.IO.Directory.GetCurrentDirectory() + "/wwwroot/images/Badges"))
{
string file = System.IO.Path.GetFileNameWithoutExtension(path);
<option value="@file">@file</option>
}
</select>
<span>.png</span> <span>.png</span>
</div> </div>
<span asp-validation-for="Image" class="text-danger"></span> <span asp-validation-for="Image" class="text-danger"></span>
</div> </div>
<partial name="Preview.cshtml" /> <partial name="Preview.cshtml" />
<input type="submit" value="Save" class="btn" /> <input type="submit" value="Update" />
</form>
<hr />
<h2>Upload badge image</h2>
<form method="post" enctype="multipart/form-data">
<input type="file" accept="image/png" name="file" />
<label for="file" style="@(ViewData.Keys.Contains("UploadException") ? "color: red" : "")">File must be a 64x64 PNG image</label>
<input type="submit" value="Upload image" class="btn"/>
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -4,31 +4,29 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p> &#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a>
<h1>Project badges list</h1> <h1>Project badges list</h1>
<p>
<a asp-action="Create" class="comment">// + Create New</a> <a asp-action="Create" class="comment">// + Create New</a>
</p>
</header> </header>
<article> <article>
<table class="table"> <table>
<thead> <thead>
<tr> <tr>
<th>Preview</th> <th>Preview</th>
<th> <th class="hide-l2">
@Html.DisplayNameFor(model => model.Name) @Html.DisplayNameFor(model => model.Name)
</th> </th>
<th> <th>
@Html.DisplayNameFor(model => model.EnglishDescription) @Html.DisplayNameFor(model => model.EnglishDescription)
</th> </th>
<th> <th class="hide-l2">
@Html.DisplayNameFor(model => model.RussianDescription) @Html.DisplayNameFor(model => model.RussianDescription)
</th> </th>
<th> <th class="hide-l1">
@Html.DisplayNameFor(model => model.Image) @Html.DisplayNameFor(model => model.Image)
</th> </th>
<th></th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -38,10 +36,10 @@
<td> <td>
<div class="badge" style="background-image: url(/images/Badges/@(item.Image).png)" title="@(item.Description)"></div> <div class="badge" style="background-image: url(/images/Badges/@(item.Image).png)" title="@(item.Description)"></div>
</td> </td>
<td>@item.Name</td> <td class="hide-l2">@item.Name</td>
<td>@item.EnglishDescription</td> <td>@item.EnglishDescription</td>
<td>@item.RussianDescription</td> <td class="hide-l2">@item.RussianDescription</td>
<td>@item.Image</td> <td class="hide-l1">@item.Image</td>
<td> <td>
<a asp-action="Edit" asp-route-id="@item.Name">Edit</a> | <a asp-action="Edit" asp-route-id="@item.Name">Edit</a> |
<a asp-action="Delete" asp-route-id="@item.Name">Delete</a> <a asp-action="Delete" asp-route-id="@item.Name">Delete</a>
@@ -52,11 +50,110 @@
</table> </table>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> <header>
<style type="text/css"> <hr />
.badge { <h1>Badge image files</h1>
</header>
<article>
<h2>Upload new badge image</h2>
<form method="post" enctype="multipart/form-data">
<div asp-validation-summary="All" class="text-danger"></div>
<label for="badgeImage">Badge image file</label>
<input name="badgeImage" type="file" required />
<span>
<b>Note:</b> Image should be exactly 64x64 pixels PNG file
</span>
<br />
<button>Upload</button>
</form>
<h2>Available badge images</h2>
<table class="files-table">
<thead>
<tr>
<th>Preview</th>
<th>File name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (string path in System.IO.Directory.GetFiles(System.IO.Directory.GetCurrentDirectory() + "/wwwroot/images/Badges"))
{
string file = System.IO.Path.GetFileName(path);
<tr>
<td>
<div class="badge" style="background-image: url(/images/Badges/@(file))" title="@(file)"></div>
</td>
<td>@file</td>
<td>
@if (Model.Any(i => i.Image + ".png" == file))
{
<span class="hide-l2" title="Delete or edit correlated badges linked with the image in order to delete it">No available actions</span>
}
else
{
<a asp-action="DeleteBadgeImage" asp-route-id="@file" onclick="return ConfirmDetetion('@file')">Delete</a>
}
</td>
</tr>
}
</tbody>
</table>
</article>
@section Imports
{
<style type="text/css">
.badge
{
height: 25px; height: 25px;
width: 25px; width: 25px;
background-size: contain; background-size: contain;
} }
</style>
.files-table
{
max-width: 500px;
}
td span
{
color: gray;
cursor: help;
}
td span:after
{
content: " (?)";
}
</style>
<script type="text/javascript">
function ConfirmDetetion(filename)
{
return confirm("Are you really want to delete \"" + filename + "\"? This action cannot be undone");
}
function Upload()
{
var form = document.createElement("form");
form.method = "post";
form.hidden = true;
document.body.appendChild(form);
var input = document.createElement("input");
input.type = "file";
input.name = "badgeImage";
input.click();
input.onchange = function ()
{
form.appendChild(input);
form.submit();
}
}
</script>
}
@@ -1,30 +1,26 @@
@model MyWebsite.Models.LinkModel @model MyWebsite.Models.LinkModel
@{ @{
ViewData["Title"] = "Create link"; ViewData["Title"] = "Create link";
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Create link</h1> <h1>Create link</h1>
</header> </header>
<article> <article>
<form asp-action="Create"> <form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="Order" type="number" hidden value="-1" />
<div> <div>
<label asp-for="Name"></label> <label asp-for="Name"></label>
<input asp-for="Name" type="text" /> <a target="_blank" class="comment" href="//socicon.com/icons">// Socicon naming cheatsheet</a>
<input asp-for="Name" type="text" required />
<span asp-validation-for="Name" class="text-danger"></span> <span asp-validation-for="Name" class="text-danger"></span>
</div> </div>
<div>
<label asp-for="Order"></label>
<input asp-for="Order" type="number" />
<span asp-validation-for="Order" class="text-danger"></span>
</div>
<div> <div>
<label asp-for="EnglishTitle"></label> <label asp-for="EnglishTitle"></label>
<input asp-for="EnglishTitle" type="text" /> <input asp-for="EnglishTitle" type="text" required />
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
</div> </div>
<div> <div>
@@ -34,22 +30,23 @@
</div> </div>
<div> <div>
<label asp-for="Username"></label> <label asp-for="Username"></label>
<input asp-for="Username" type="text" /> <input asp-for="Username" type="text" required />
<span asp-validation-for="Username" class="text-danger"></span> <span asp-validation-for="Username" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="Url"></label> <label asp-for="Url"></label>
<input asp-for="Url" type="text" /> <input asp-for="Url" type="text" required />
<span asp-validation-for="Url" class="text-danger"></span> <span asp-validation-for="Url" class="text-danger"></span>
</div> </div>
<div>
<label asp-for="CanContactMe"></label> <input type="checkbox" class="checkbox" asp-for="CanContactMe" />
<input type="checkbox" class="checkbox" asp-for="CanContactMe" /> <br /> <label asp-for="CanContactMe"></label> <br />
<label asp-for="DisplayInFooter"></label> </div>
<div>
<input type="checkbox" class="checkbox" asp-for="DisplayInFooter" /> <input type="checkbox" class="checkbox" asp-for="DisplayInFooter" />
<label asp-for="DisplayInFooter"></label>
</div>
<input type="submit" value="Create" class="btn" /> <input type="submit" value="Create" />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -1,31 +1,28 @@
@model MyWebsite.Models.LinkModel @model MyWebsite.Models.LinkModel
@{ @{
ViewData["Title"] = "Delete link"; ViewData["Title"] = "Delete link";
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Delete link</h1> <h1>Delete link</h1>
<h3>Are you sure you want to delete this?</h3> <h3>Are you sure you want to delete this?</h3>
</header> </header>
<article> <article>
<p class="form-group"> <p>
<b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br /> <b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br />
<b>@Html.DisplayNameFor(model => model.Order):</b> @Model.Order<br /> <b>@Html.DisplayNameFor(model => model.Order):</b> @Model.Order<br />
<b>@Html.DisplayNameFor(model => model.EnglishTitle):</b> @Model.EnglishTitle<br /> <b>@Html.DisplayNameFor(model => model.EnglishTitle):</b> @Model.EnglishTitle<br />
<b>@Html.DisplayNameFor(model => model.RussianTitle):</b> @Model.RussianTitle<br /> <b>@Html.DisplayNameFor(model => model.RussianTitle):</b> @Model.RussianTitle<br />
<b>@Html.DisplayNameFor(model => model.Username):</b> @Model.Username<br /> <b>@Html.DisplayNameFor(model => model.Username):</b> @Model.Username<br />
<b>@Html.DisplayNameFor(model => model.Url):</b> @Model.Url<br /> <b>@Html.DisplayNameFor(model => model.Url):</b> <a href="@Model.Url" target="_blank">@Model.Url</a><br />
<b>@Html.DisplayNameFor(model => model.CanContactMe):</b> @Html.DisplayFor(model => model.CanContactMe)<br /> <b>@Html.DisplayNameFor(model => model.CanContactMe):</b> @Html.DisplayFor(model => model.CanContactMe)<br />
<b>@Html.DisplayNameFor(model => model.DisplayInFooter):</b> @Html.DisplayFor(model => model.DisplayInFooter) <b>@Html.DisplayNameFor(model => model.DisplayInFooter):</b> @Html.DisplayFor(model => model.DisplayInFooter)
</p> </p>
<form asp-action="Delete"> <form asp-action="Delete">
<input hidden asp-for="Name" /> <input hidden asp-for="Name" />
<input type="submit" value="Delete" class="btn-danger" /> <input type="submit" value="Delete" required />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -4,25 +4,21 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Edit link</h1> <h1>Edit link</h1>
</header> </header>
<article> <article>
<form asp-action="Edit"> <form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="Order" type="number" hidden />
<div> <div>
<label asp-for="Name"></label> <label asp-for="Name"></label>
<input asp-for="Name" type="text" class="readonly" readonly /> <input asp-for="Name" type="text" readonly />
</div>
<div>
<label asp-for="Order"></label>
<input asp-for="Order" type="number" />
<span asp-validation-for="Order" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="EnglishTitle"></label> <label asp-for="EnglishTitle"></label>
<input asp-for="EnglishTitle" type="text" /> <input asp-for="EnglishTitle" type="text" required />
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
</div> </div>
<div> <div>
@@ -32,22 +28,23 @@
</div> </div>
<div> <div>
<label asp-for="Username"></label> <label asp-for="Username"></label>
<input asp-for="Username" type="text" /> <input asp-for="Username" type="text" required />
<span asp-validation-for="Username" class="text-danger"></span> <span asp-validation-for="Username" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="Url"></label> <label asp-for="Url"></label>
<input asp-for="Url" type="text" /> <input asp-for="Url" type="text" required />
<span asp-validation-for="Url" class="text-danger"></span> <span asp-validation-for="Url" class="text-danger"></span>
</div> </div>
<div>
<label asp-for="CanContactMe"></label> <input type="checkbox" class="checkbox" asp-for="CanContactMe" />
<input type="checkbox" class="checkbox" asp-for="CanContactMe" /> <br /> <label asp-for="CanContactMe"></label> <br />
<label asp-for="DisplayInFooter"></label> </div>
<div>
<input type="checkbox" class="checkbox" asp-for="DisplayInFooter" /> <input type="checkbox" class="checkbox" asp-for="DisplayInFooter" />
<label asp-for="DisplayInFooter"></label>
</div>
<input type="submit" value="Save" class="btn" /> <input type="submit" value="Save" />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -4,56 +4,58 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p> &#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a>
<h1>Links list</h1> <h1>Links list</h1>
<p>
<a asp-action="Create" class="comment">// + Create New</a> <a asp-action="Create" class="comment">// + Create New</a>
</p>
</header> </header>
<article> <article>
<div asp-validation-summary="All" class="text-danger"></div>
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Reorder</th> <th>&#xE700;</th>
<th> <th>
@Html.DisplayNameFor(model => model.Name) @Html.DisplayNameFor(model => model.Name)
</th> </th>
<th> <th class="hide-l2">
@Html.DisplayNameFor(model => model.EnglishTitle) @Html.DisplayNameFor(model => model.Title)
</th> </th>
<th> <th class="hide-l1">
@Html.DisplayNameFor(model => model.RussianTitle)
</th>
<th>
@Html.DisplayNameFor(model => model.Username) @Html.DisplayNameFor(model => model.Username)
</th> </th>
<th> <th class="hide-l2">
@Html.DisplayNameFor(model => model.Url) @Html.DisplayNameFor(model => model.Url)
</th> </th>
<th> <th class="hide-l1">
@Html.DisplayNameFor(model => model.CanContactMe) @Html.DisplayNameFor(model => model.CanContactMe)
</th> </th>
<th> <th class="hide-l1">
@Html.DisplayNameFor(model => model.DisplayInFooter) @Html.DisplayNameFor(model => model.DisplayInFooter)
</th> </th>
<th></th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var item in Model.OrderBy(i => i.Order)) @foreach (var item in Model.OrderBy(i => i.Order))
{ {
<tr draggable="true" ondragover="onDragOver(event);"> <tr id="@item.Name">
<td>&#xE700;</td> <td class="reorderingBtns">
<a onclick="Up(this)">&#xE010;</a><br />
<span>&#xE915;</span><br />
<a onclick="Down(this)">&#xE011;</a>
</td>
<td>@item.Name</td> <td>@item.Name</td>
<td>@item.EnglishTitle</td> <td class="hide-l2">
<td>@item.RussianTitle</td> @item.EnglishTitle (en)<br />
<td>@item.Username</td> @(item.RussianTitle ?? "<not_set>") (ru)
<td>@item.Url</td> </td>
<td> <td class="hide-l1">@item.Username</td>
<td class="hide-l2"><a target="_blank" href="@item.Url">@item.Url</a></td>
<td class="hide-l1">
@Html.DisplayFor(modelItem => item.CanContactMe) @Html.DisplayFor(modelItem => item.CanContactMe)
</td> </td>
<td> <td class="hide-l1">
@Html.DisplayFor(modelItem => item.DisplayInFooter) @Html.DisplayFor(modelItem => item.DisplayInFooter)
</td> </td>
<td> <td>
@@ -64,12 +66,25 @@
} }
</tbody> </tbody>
</table> </table>
<button onclick="ApplyReorder()" disabled>Apply reordering</button>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<script type="text/javascript"> {
function onDragOver(event) <style type="text/css">
.reorderingBtns
{ {
this.style.marginTop = '20px'; user-select: none;
} }
</script>
.reorderingBtns a:hover
{
cursor: pointer;
color: gray;
text-decoration: underline;
}
</style>
<script type="text/javascript" src="~/js/ReorderScript.js"></script>
}
@@ -0,0 +1,18 @@
@{
ViewData["Title"] = "FoxTube";
}
<header>
<h1>FoxTube Backend control panel</h1>
</header>
<article class="admin-menu">
<p>
<a asp-action="Gallery" class="comment">// Messages and changelogs</a><br />
<a asp-action="Gallery" class="comment">// Recieved metrics</a>
</p>
<p>
<a asp-action="Gallery" class="comment">// Privacy policies</a><br />
<a asp-action="Gallery" class="comment">// Screenshots</a>
</p>
</article>
@@ -4,13 +4,13 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Delete artwork</h1> <h1>Delete artwork</h1>
<h3>Are you sure you want to delete this?</h3> <h3>Are you sure you want to delete this?</h3>
</header> </header>
<article class="image-overview-block"> <article class="image-overview-block">
<img src="~/images/Gallery/@Model.FileName" onclick="ToggleImageSize();" id="image" /> <img src="~/images/Gallery/@Model.FileName" onclick="ToggleImageSize();" />
<div> <div>
<p> <p>
@@ -25,10 +25,37 @@
<form asp-action="Delete"> <form asp-action="Delete">
<input hidden asp-for="FileName" /> <input hidden asp-for="FileName" />
<input type="submit" value="Delete" class="btn-danger" /> <input type="submit" value="Delete" required />
</form> </form>
</div> </div>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<link type="text/css" rel="stylesheet" href="~/css/Gallery.css" /> {
<link type="text/css" rel="stylesheet" href="~/css/Gallery.css" />
<style type="text/css">
@@media only screen and (max-width: 500px)
{
.image-overview-block > div
{
margin: initial;
}
}
</style>
<script type="text/javascript">
function ToggleImageSize()
{
var image = document.querySelector("img");
if (image.style.cursor == "zoom-out")
image.style = "";
else
{
image.style.maxHeight = "none";
image.style.maxWidth = "none";
image.style.cursor = "zoom-out";
}
}
</script>
}
@@ -4,20 +4,20 @@
} }
<header> <header>
<p> &#xE760; <a asp-action="Index">Back to the list</a>
&#xE760; <a asp-action="Index">Back to the list</a> <br />
<h1>Edit artwork</h1> <h1>Edit artwork</h1>
<a class="comment" href="~/images/Gallery/@(Model.FileName)" target="_blank">// Open artwork</a> <a class="comment" href="~/images/Gallery/@(Model.FileName)" target="_blank">// Open artwork file</a>
</p>
</header> </header>
<article> <article>
<img src="~/images/Gallery/@Model.FileName" onclick="ToggleImageSize();" />
<form asp-action="Edit"> <form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="FileName" hidden/> <input asp-for="FileName" hidden />
<div> <div>
<label asp-for="EnglishTitle"></label> <label asp-for="EnglishTitle"></label>
<input asp-for="EnglishTitle" type="text" /> <input asp-for="EnglishTitle" type="text" required/>
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
</div> </div>
<div> <div>
@@ -37,12 +37,61 @@
</div> </div>
<div> <div>
<label asp-for="CreationDate"></label> <label asp-for="CreationDate"></label>
<input asp-for="CreationDate" type="date" /> <input asp-for="CreationDate" type="date" required/>
<span asp-validation-for="CreationDate" class="text-danger"></span> <span asp-validation-for="CreationDate" class="text-danger"></span>
</div> </div>
<input type="submit" value="Save" class="btn" /> <input type="submit" value="Save" />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
{
<script type="text/javascript">
function ToggleImageSize()
{
var image = document.querySelector("img");
if (image.style.cursor == "zoom-out")
image.style = "";
else
{
image.style.maxHeight = "none";
image.style.maxWidth = "none";
image.style.cursor = "zoom-out";
}
}
</script>
<style type="text/css">
article
{
display: grid;
grid-template-columns: 500px 1fr;
grid-column-gap: 20px;
}
article img
{
width: 100%;
}
@@media only screen and (max-width: 1000px)
{
article
{
display: inherit;
}
}
form
{
max-width: initial;
}
textarea
{
max-height: 250px;
}
</style>
}
@@ -4,11 +4,9 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p> &#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a>
<h1>Gallery</h1> <h1>Gallery</h1>
<p> <a asp-action="Upload" class="comment">// &#xE11C; Upload new</a>
<a asp-action="Upload" class="comment">// + Upload new</a>
</p>
</header> </header>
<article> <article>
@@ -17,7 +15,9 @@
{ {
<tr> <tr>
<td> <td>
<a asp-area="" asp-controller="Gallery" asp-action="Details" asp-route-id="@item.FileName" target="_blank">
<img title="@item.Title" src="~/images/Gallery/@item.FileName" /> <img title="@item.Title" src="~/images/Gallery/@item.FileName" />
</a>
</td> </td>
<td> <td>
<p> <p>
@@ -25,8 +25,9 @@
<span>File name: @item.FileName</span><br /> <span>File name: @item.FileName</span><br />
<span>Creation date: @item.CreationDate.ToShortDateString()</span><br /> <span>Creation date: @item.CreationDate.ToShortDateString()</span><br />
<span> <span>
@Html.ActionLink("Edit", "Edit", new { id = item.FileName }) | <a asp-action="Edit" asp-route-id="@item.FileName">Edit</a> |
@Html.ActionLink("Delete", "Delete", new { id = item.FileName }) <a asp-action="Delete" asp-route-id="@item.FileName">Delete</a> |
<a asp-area="" asp-controller="Gallery" asp-action="Details" asp-route-id="@item.FileName" target="_blank">View</a>
</span> </span>
</p> </p>
</td> </td>
@@ -35,13 +36,18 @@
</table> </table>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<style type="text/css"> {
img { <style type="text/css">
img
{
height: 200px; height: 200px;
margin-right: 20px; margin-right: 20px;
} }
table {
table
{
width: initial; width: initial;
} }
</style> </style>
}
@@ -1,19 +1,23 @@
@model ArtworkModel @model ImageModel
@{ @{
ViewData["Title"] = "Upload artwork"; ViewData["Title"] = "Upload artwork";
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Upload an artwork</h1> <h1>Upload an artwork</h1>
</header> </header>
<article> <article>
<form asp-action="Upload"> <form asp-action="Upload" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div>
<label for="file">Artwork file</label>
<input type="file" accept="image" name="File" required />
</div>
<div asp-validation-summary="All" class="text-danger"></div>
<div> <div>
<label asp-for="EnglishTitle"></label> <label asp-for="EnglishTitle"></label>
<input asp-for="EnglishTitle" type="text" /> <input asp-for="EnglishTitle" type="text" required />
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
</div> </div>
<div> <div>
@@ -33,17 +37,25 @@
</div> </div>
<div> <div>
<label asp-for="CreationDate"></label> <label asp-for="CreationDate"></label>
<input asp-for="CreationDate" type="date" /> <input asp-for="CreationDate" type="date" required />
<span asp-validation-for="CreationDate" class="text-danger"></span> <span asp-validation-for="CreationDate" class="text-danger"></span>
</div> </div>
<div>
<label asp-for="File"></label>
<input type="file" accept="image" asp-for="File" />
<span asp-validation-for="File" class="text-danger"></span>
</div>
<input type="submit" value="Upload" class="btn" /> <input type="submit" value="Upload" />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
{
<style type="text/css">
form
{
max-width: initial;
}
textarea
{
max-height: 250px;
}
</style>
}
@@ -9,26 +9,22 @@
</header> </header>
<article> <article>
<form asp-action="Create"> <form asp-action="Create" onsubmit="ConfigureBadges()">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<div> <input asp-for="Order" type="number" value="-1" hidden />
<label asp-for="Id"></label>
<input asp-for="Id" type="number"/>
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<div> <div>
<label asp-for="EnglishTitle"></label> <label asp-for="EnglishTitle"></label>
<input asp-for="EnglishTitle" type="text"/> <input asp-for="EnglishTitle" type="text" required />
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="RussianTitle"></label> <label asp-for="RussianTitle"></label>
<input asp-for="RussianTitle" type="text"/> <input asp-for="RussianTitle" type="text" />
<span asp-validation-for="RussianTitle" class="text-danger"></span> <span asp-validation-for="RussianTitle" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="EnglishDescription"></label> <label asp-for="EnglishDescription"></label>
<textarea asp-for="EnglishDescription"></textarea> <textarea asp-for="EnglishDescription" required></textarea>
<span asp-validation-for="EnglishDescription" class="text-danger"></span> <span asp-validation-for="EnglishDescription" class="text-danger"></span>
</div> </div>
<div> <div>
@@ -38,27 +34,74 @@
</div> </div>
<div> <div>
<label asp-for="Link"></label> <label asp-for="Link"></label>
<input asp-for="Link" type="url"/> <input asp-for="Link" type="text" />
<span asp-validation-for="Link" class="text-danger"></span> <span asp-validation-for="Link" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="EnglishLinkCaption"></label> <label asp-for="EnglishLinkCaption"></label>
<input asp-for="EnglishLinkCaption" type="text"/> <input asp-for="EnglishLinkCaption" type="text" />
<span asp-validation-for="EnglishLinkCaption" class="text-danger"></span> <span asp-validation-for="EnglishLinkCaption" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="RussianLinkCaption"></label> <label asp-for="RussianLinkCaption"></label>
<input asp-for="RussianLinkCaption" type="text"/> <input asp-for="RussianLinkCaption" type="text" />
<span asp-validation-for="RussianLinkCaption" class="text-danger"></span> <span asp-validation-for="RussianLinkCaption" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="Badges"></label> <label asp-for="Badges"></label>
<input asp-for="Badges" type="text" /> <input asp-for="Badges" type="text" id="badges" hidden />
<span asp-validation-for="Badges" class="text-danger"></span> <span asp-validation-for="Badges" class="text-danger"></span>
<div class="badge-list">
@foreach (BadgeModel badge in ViewData["Badges"] as List<BadgeModel>)
{
<input type="checkbox" id="@badge.Name"/>
<div class="badge" style="background-image: url('/images/Badges/@(badge?.Image).png')" title="@(badge?.Description)"></div>
<span>@badge.Description</span><br />
}
</div>
</div> </div>
<input type="submit" value="Create" class="btn" /> <input type="submit" value="Create" />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
{
<script type="text/javascript">
function ConfigureBadges()
{
var checkboxes = document.querySelectorAll(".badge-list input");
var badges = [];
for (var k = 0; k < checkboxes.length; k++)
if (checkboxes[k].checked == true)
badges[badges.length] = checkboxes[k].id;
document.querySelector("#badges").value = badges.join(",");
}
</script>
<style type="text/css">
form
{
max-width: initial;
}
textarea
{
max-height: 250px;
}
.badge-list
{
margin: 10px 0px;
}
.badge-list div
{
height: 25px;
width: 25px;
display: inline-block;
background-size: contain;
}
</style>
}
@@ -4,28 +4,60 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Delete project entry</h1> <h1>Delete project entry</h1>
<h3>Are you sure you want to delete this?</h3> <h3>Are you sure you want to delete this?</h3>
</header> </header>
<article> <article>
<p class="form-group"> <p>
<b>@Html.DisplayNameFor(model => model.Id):</b> @Model.Id<br /> <b>@Html.DisplayNameFor(model => model.Id):</b> @Model.Id<br />
</p>
<p>
<b>@Html.DisplayNameFor(model => model.EnglishTitle):</b> @Model.EnglishTitle<br /> <b>@Html.DisplayNameFor(model => model.EnglishTitle):</b> @Model.EnglishTitle<br />
<b>@Html.DisplayNameFor(model => model.RussianTitle):</b> @Model.RussianTitle<br /> <b>@Html.DisplayNameFor(model => model.RussianTitle):</b> @Model.RussianTitle<br />
<b>@Html.DisplayNameFor(model => model.EnglishDescription):</b> @Model.EnglishDescription<br /> </p>
<b>@Html.DisplayNameFor(model => model.RussianDescription):</b> @Model.RussianDescription<br /> <p>
<b>@Html.DisplayNameFor(model => model.Link):</b> @Model.Link<br />
<b>@Html.DisplayNameFor(model => model.EnglishLinkCaption):</b> @Model.EnglishLinkCaption<br /> <b>@Html.DisplayNameFor(model => model.EnglishLinkCaption):</b> @Model.EnglishLinkCaption<br />
<b>@Html.DisplayNameFor(model => model.RussianLinkCaption):</b> @Model.RussianLinkCaption<br /> <b>@Html.DisplayNameFor(model => model.RussianLinkCaption):</b> @Model.RussianLinkCaption<br />
</p>
<p>
<b>@Html.DisplayNameFor(model => model.Link):</b> <a target="_blank" href="@Model.Link">@Model.Link</a><br />
</p>
<p>
<b>@Html.DisplayNameFor(model => model.Badges):</b> @Model.Badges<br /> <b>@Html.DisplayNameFor(model => model.Badges):</b> @Model.Badges<br />
<div class="badge-placeholder">
@foreach (string b in Model.Badges.Split(','))
{
BadgeModel badge = (ViewData["Badges"] as List<BadgeModel>).FirstOrDefault(i => i.Name == b);
<div style="background-image: url('/images/Badges/@(badge?.Image).png')" title="@(badge?.Description)"></div>
}
</div>
</p> </p>
<form asp-action="Delete"> <form asp-action="Delete">
<input hidden asp-for="Id" /> <input hidden asp-for="Id" />
<input type="submit" value="Delete" class="btn-danger" /> <input type="submit" value="Delete" required />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
{
<style type="text/css">
.badge-placeholder
{
display: grid;
grid-column-gap: 10px;
grid-auto-columns: max-content;
grid-auto-flow: column;
}
.badge-placeholder div
{
height: 25px;
width: 25px;
display: inline-block;
background-size: contain;
}
</style>
}
@@ -0,0 +1,111 @@
@model MyWebsite.Models.ProjectModel
@{
ViewData["Title"] = "Edit";
}
<header>
&#xE760; <a asp-action="Index">Back to the list</a>
<h1>Edit project</h1>
</header>
<article>
<form asp-action="Edit" onsubmit="ConfigureBadges()">
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="Order" type="number" hidden />
<div>
<label asp-for="Id"></label>
<input asp-for="Id" type="text" readonly />
</div>
<div>
<label asp-for="EnglishTitle"></label>
<input asp-for="EnglishTitle" type="text" required />
<span asp-validation-for="EnglishTitle" class="text-danger"></span>
</div>
<div>
<label asp-for="RussianTitle"></label>
<input asp-for="RussianTitle" type="text" />
<span asp-validation-for="RussianTitle" class="text-danger"></span>
</div>
<div>
<label asp-for="EnglishDescription"></label>
<textarea asp-for="EnglishDescription" required></textarea>
<span asp-validation-for="EnglishDescription" class="text-danger"></span>
</div>
<div>
<label asp-for="RussianDescription"></label>
<textarea asp-for="RussianDescription"></textarea>
<span asp-validation-for="RussianDescription" class="text-danger"></span>
</div>
<div>
<label asp-for="Link"></label>
<input asp-for="Link" type="text" />
<span asp-validation-for="Link" class="text-danger"></span>
</div>
<div>
<label asp-for="EnglishLinkCaption"></label>
<input asp-for="EnglishLinkCaption" type="text" />
<span asp-validation-for="EnglishLinkCaption" class="text-danger"></span>
</div>
<div>
<label asp-for="RussianLinkCaption"></label>
<input asp-for="RussianLinkCaption" type="text" />
<span asp-validation-for="RussianLinkCaption" class="text-danger"></span>
</div>
<div>
<label asp-for="Badges"></label>
<input asp-for="Badges" type="text" id="badges" hidden />
<span asp-validation-for="Badges" class="text-danger"></span>
<div class="badge-list">
@foreach (BadgeModel badge in ViewData["Badges"] as List<BadgeModel>)
{
<input type="checkbox" id="@badge.Name" checked="@(Model.Badges.Split(',').Contains(badge.Name))"/>
<div class="badge" style="background-image: url('/images/Badges/@(badge?.Image).png')" title="@(badge?.Description)"></div>
<span>@badge.Description</span><br />
}
</div>
</div>
<input type="submit" value="Save" />
</form>
</article>
@section Imports
{
<script type="text/javascript">
function ConfigureBadges()
{
var checkboxes = document.querySelectorAll(".badge-list input");
var badges = [];
for (var k = 0; k < checkboxes.length; k++)
if (checkboxes[k].checked == true)
badges[badges.length] = checkboxes[k].id;
document.querySelector("#badges").value = badges.join(",");
}
</script>
<style type="text/css">
form
{
max-width: initial;
}
textarea
{
max-height: 250px;
}
.badge-list
{
margin: 10px 0px;
}
.badge-list div
{
height: 25px;
width: 25px;
display: inline-block;
background-size: contain;
}
</style>
}
@@ -4,48 +4,61 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p> &#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a>
<h1>Projects list</h1> <h1>Projects list</h1>
<p> <a asp-action="Create" class="comment">// + Add new project</a>
<a asp-action="Create" class="comment">// + Add New</a>
</p>
</header> </header>
<article> <article>
<div asp-validation-summary="All" class="text-danger"></div>
<table> <table>
<thead> <thead>
<tr> <tr>
<th> <th>&#xE700;</th>
<th class="hide-l1">
@Html.DisplayNameFor(model => model.projects.First().Id) @Html.DisplayNameFor(model => model.projects.First().Id)
</th> </th>
<th> <th>
@Html.DisplayNameFor(model => model.projects.First().EnglishTitle) @Html.DisplayNameFor(model => model.projects.First().Title)
</th> </th>
<th> <th class="hide-l1">
@Html.DisplayNameFor(model => model.projects.First().RussianTitle) @Html.DisplayNameFor(model => model.projects.First().LinkCaption)
</th> </th>
<th> <th class="hide-l2">
@Html.DisplayNameFor(model => model.projects.First().Link) @Html.DisplayNameFor(model => model.projects.First().Link)
</th> </th>
<th> <th class="hide-l2">
@Html.DisplayNameFor(model => model.projects.First().Badges) @Html.DisplayNameFor(model => model.projects.First().Badges)
(<a asp-controller="Badges" asp-action="Index">Edit badges</a>)
</th> </th>
<th></th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var item in Model.projects.OrderByDescending(i => i.Id)) @foreach (var item in Model.projects.OrderBy(i => i.Order))
{ {
<tr> <tr id="@item.Id">
<td>@item.Id</td> <td class="reorderingBtns">
<td>@item.EnglishTitle</td> <a onclick="Up(this)">&#xE010;</a><br />
<td>@item.RussianTitle</td> <span>&#xE915;</span><br />
<td>@item.Link</td> <a onclick="Down(this)">&#xE011;</a>
</td>
<td class="hide-l1">@item.Id</td>
<td> <td>
@item.EnglishTitle (en)<br />
@(item.RussianTitle ?? "<not_set>") (ru)
</td>
<td class="hide-l1">
@(item.EnglishLinkCaption ?? "<not_set>") (en)<br />
@(item.RussianLinkCaption ?? "<not_set>") (ru)
</td>
<td class="hide-l2"><a target="_blank" href="@item.Link">@item.Link</a></td>
<td class="hide-l2">
<div class="badge-placeholder"> <div class="badge-placeholder">
@foreach (string badge in item.Badges.Split(',')) @foreach (string b in item.Badges.Split(','))
{ {
<div style="background-image: @("../images/Badges/" + Model.badges.FirstOrDefault(i => i.Name == badge)?.Image + ".png")" title="@(Model.badges.FirstOrDefault(i => i.Name == badge)?.Description)"></div> BadgeModel badge = Model.badges.FirstOrDefault(i => i.Name == b);
<div style="background-image: url('../images/Badges/@(badge?.Image).png')" title="@(badge?.Description)"></div>
} }
</div> </div>
</td> </td>
@@ -57,10 +70,13 @@
} }
</tbody> </tbody>
</table> </table>
<button onclick="ApplyReorder()" disabled>Apply reordering</button>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<style type="text/css"> {
<style type="text/css">
.badge-placeholder .badge-placeholder
{ {
display: grid; display: grid;
@@ -76,4 +92,19 @@
display: inline-block; display: inline-block;
background-size: contain; background-size: contain;
} }
</style>
.reorderingBtns
{
user-select: none;
}
.reorderingBtns a:hover
{
cursor: pointer;
color: gray;
text-decoration: underline;
}
</style>
<script type="text/javascript" src="~/js/ReorderScript.js"></script>
}
@@ -1,39 +1,48 @@
@using System.Globalization @using System.Globalization
@model MyWebsite.Models.ResumeModel @model MyWebsite.Models.ResumeModel
@{ @{
ViewData["Title"] = "Create resume"; ViewData["Title"] = "Create " + (ViewData["Caption"] ?? "resume");
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Create resume</h1> <h1>Create @(ViewData["Caption"] ?? "resume")</h1>
</header> </header>
<article> <article>
<form asp-action="Create"> <form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<div> <div>
<label asp-for="Language"></label> <label asp-for="Language"></label>
<input type="text" asp-for="Language"/> <select asp-for="Language" required>
@foreach (var culture in CultureInfo.GetCultures(CultureTypes.NeutralCultures))
{
<option value="@culture.Name">@culture.EnglishName</option>
}
</select>
<span asp-validation-for="Language" class="text-danger"></span> <span asp-validation-for="Language" class="text-danger"></span>
</div> </div>
<div> <div>
<label asp-for="Content"></label> <label asp-for="Content"></label>
<textarea asp-for="Content" spellcheck="false"></textarea> <textarea asp-for="Content" spellcheck="false" required></textarea>
<span asp-validation-for="Content" class="text-danger"></span> <span asp-validation-for="Content" class="text-danger"></span>
</div> </div>
<br /> <br />
<input type="submit" value="Save" class="btn"/> <input type="submit" value="Save" />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<style type="text/css"> {
form { <style type="text/css">
form
{
max-width: initial; max-width: initial;
} }
textarea { textarea
{
min-height: 500px; min-height: 500px;
} }
</style> </style>
}
@@ -1,24 +1,22 @@
@model MyWebsite.Models.ResumeModel @model MyWebsite.Models.ResumeModel
@{ @{
ViewData["Title"] = "Delete resume"; ViewData["Title"] = "Delete " + (ViewData["Caption"] ?? "resume");
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Delete resume</h1> <h1>Delete @(ViewData["Caption"] ?? "resume")</h1>
<h3>Are you sure you want to delete this?</h3> <h3>Are you sure you want to delete this?</h3>
</header> </header>
<article> <article>
<p class="form-group"> <p>
<b>@Html.DisplayNameFor(model => model.Language):</b> @(new System.Globalization.CultureInfo(Model.Language).DisplayName)<br /> <b>@Html.DisplayNameFor(model => model.Language):</b> @(new System.Globalization.CultureInfo(Model.Language).DisplayName)<br />
<b>@Html.DisplayNameFor(model => model.LastUpdate):</b> @Model.LastUpdate<br /> <b>@Html.DisplayNameFor(model => model.LastUpdate):</b> @Model.LastUpdate<br />
</p> </p>
<form asp-action="Delete"> <form method="post">
<input hidden asp-for="Language" /> <input hidden asp-for="Language" />
<input type="submit" value="Delete" class="btn-danger" /> <input type="submit" value="Delete" required />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -1,50 +1,56 @@
@model MyWebsite.Models.ResumeModel @model MyWebsite.Models.ResumeModel
@{ @{
ViewData["Title"] = "Edit resume"; ViewData["Title"] = "Edit " + (ViewData["Caption"] ?? "resume");
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> &#xE760; <a asp-action="Index">Back to the list</a>
<h1>Edit resume</h1> <h1>Edit @(ViewData["Caption"] ?? "resume")</h1>
<p>
Language: @(new System.Globalization.CultureInfo(Model.Language).DisplayName)<br /> Language: @(new System.Globalization.CultureInfo(Model.Language).DisplayName)<br />
Previously updated on @Model.LastUpdate Previously updated on @Model.LastUpdate<br />
</p>
<a class="comment" onclick="CopyToClipboard()">// &#xE16D; Copy to clipboard</a>
</header> </header>
<article> <article>
<form asp-action="Edit"> <form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<input type="text" asp-for="Language" hidden /> <input type="text" asp-for="Language" hidden />
<div>
<textarea asp-for="Content" spellcheck="false"></textarea> <textarea asp-for="Content" spellcheck="false" required></textarea>
<a class="comment" onclick="CopyToClipboard()" href="#">// &#xE16D; Copy to clipboard</a>
<span style="display: none" id="copied"> - Done</span><br />
<span asp-validation-for="Content" class="text-danger"></span> <span asp-validation-for="Content" class="text-danger"></span>
</div>
<br /> <br />
<input type="submit" value="Save" class=" btn" /> <input type="submit" value="Save" />
</form> </form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<style type="text/css"> {
form { <style type="text/css">
form
{
max-width: initial; max-width: initial;
} }
textarea {
header .comment
{
cursor: pointer;
}
textarea
{
min-height: 500px; min-height: 500px;
} }
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
async function CopyToClipboard() { function CopyToClipboard()
text = document.getElementById("Content"); {
text.select(); document.querySelector("textarea").select();
document.execCommand("copy"); document.execCommand("copy");
window.getSelection().removeAllRanges(); document.getSelection().removeAllRanges();
document.getElementById("copied").style.display = "initial";
await new Promise(res => setTimeout(res, 3000)); alert("CV content copied");
document.getElementById("copied").style.display = "none";
} }
</script> </script>
}
@@ -1,15 +1,12 @@
@model IEnumerable<MyWebsite.Models.ResumeModel> @model IEnumerable<MyWebsite.Models.ResumeModel>
@{ @{
ViewData["Title"] = "Resumes"; ViewData["Title"] = "Resumes";
} }
<header> <header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p> &#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a>
<h1>Resumes list</h1> <h1>Resumes list</h1>
<p>
<a asp-action="Create" class="comment">// + Create New</a> <a asp-action="Create" class="comment">// + Create New</a>
</p>
</header> </header>
<article> <article>
@@ -22,7 +19,7 @@
<th> <th>
@Html.DisplayNameFor(model => model.LastUpdate) @Html.DisplayNameFor(model => model.LastUpdate)
</th> </th>
<th></th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -40,5 +37,3 @@
</tbody> </tbody>
</table> </table>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -1,69 +1,78 @@
@model MyWebsite.Areas.Admin.Models.CredentialModel @{
@{
ViewData["Title"] = "Edit credential"; ViewData["Title"] = "Edit credential";
} }
<header> <header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p> &#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a>
<h1>Change credential information</h1> <h1>Change credential information</h1>
</header> </header>
<article> <article>
<form asp-action="Index" onsubmit="return Validate();"> <h2>Change e-mail</h2>
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<p> <form method="post" onsubmit="return ValidateEmail()">
<label asp-for="Updated.Email">New e-mail:</label> <label for="value">New e-mail:</label>
<input asp-for="Updated.Email" id="new-email" type="email" /> <input name="value" id="newEmail" type="email" placeholder="user@example.com" required />
<span asp-validation-for="Updated.Email" class="text-danger" id="email-validation"></span><br />
<label>Confirm e-mail:</label> <label>Confirm e-mail:</label>
<input id="confirm-email" type="email" /> <input id="confirmEmail" type="email" placeholder="user@example.com" required />
</p>
<p> <span class="text-danger" id="emailValidationError"></span>
<label asp-for="Updated.Password">New password:</label>
<input asp-for="Updated.Password" id="new-password" type="password" /> <input name="key" value="email" hidden />
<span asp-validation-for="Updated.Password" class="text-danger" id="password-validation"></span><br /> <button>Update e-mail</button>
</form>
<h2>Change password</h2>
<form method="post" onsubmit="return ValidatePassword()">
<label for="value">New password:</label>
<input name="value" id="newPassword" type="password" required />
<label>Confirm password:</label> <label>Confirm password:</label>
<input id="confirm-password" type="password" /> <input id="confirmPassword" type="password" required />
</p>
<p> <span class="text-danger" id="passwordValidationError"></span>
<label asp-for="Current.Email">Current e-mail:</label>
<input asp-for="Current.Email" type="email" /> <input name="key" value="password" hidden />
<span asp-validation-for="Current.Email" class="text-danger"></span><br /> <button>Update password</button>
<label asp-for="Current.Password">Current password:</label>
<input asp-for="Current.Password" type="password" />
<span asp-validation-for="Current.Password" class="text-danger"></span>
</p>
<input type="submit" value="Update" />
</form> </form>
</article> </article>
<link href="~/css/Admin.css" type="text/css" rel="stylesheet" />
<script type="text/javascript"> @section Imports
var newEmail = document.getElementById("new-email"); {
var confirmEmail = document.getElementById("confirm-email"); <script type="text/javascript">
var newPassword = document.getElementById("new-password"); function ValidateEmail()
var confirmPassword = document.getElementById("confirm-password");
var emailValidation = document.getElementById("email-validation");
var passwordValidation = document.getElementById("password-validation");
function Validate()
{ {
var invalid = false; var newEmail = document.querySelector("#newEmail");
var confirmEmail = document.querySelector("#confirmEmail");
var emailValidation = document.querySelector("#emailValidationError");
emailValidation.innerHTML = ""; emailValidation.innerHTML = "";
if (newEmail.value == confirmEmail.value)
return true;
emailValidation.innerHTML = "E-mail addresses are invalid or doesn't match";
return false;
}
function ValidatePassword()
{
var newPassword = document.querySelector("#newPassword");
var confirmPassword = document.querySelector("#confirmPassword");
var passwordValidation = document.querySelector("#passwordValidationError");
passwordValidation.innerHTML = ""; passwordValidation.innerHTML = "";
if (newEmail.value != "" && newEmail.value != confirmEmail.value) if (newPassword.value == confirmPassword.value)
{ return true;
emailValidation.innerHTML = "Addresses don't match";
invalid = true;
}
if (newPassword.value != "" && newPassword.value != confirmPassword.value)
{
passwordValidation.innerHTML = "Passwords don't match";
invalid = true;
}
return !invalid; passwordValidation.innerHTML = "Passwords doesn't match";
return false;
} }
</script> </script>
}
@@ -1,22 +1,49 @@
@model CustomData @model CustomData
@{ @{
ViewData["Title"] = "GUTSchedule"; ViewData["Title"] = "GUTSchedule";
IEnumerable<ResumeModel> policies = ViewData["Policies"] as IEnumerable<ResumeModel>;
} }
<header> <header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p> &#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a>
<h1>GUTSchedule</h1> <h1>GUTSchedule</h1>
</header> </header>
<article> <article>
<div asp-validation-summary="All" class="text-danger"></div>
<h2>Offset dates</h2>
<form asp-action="Index"> <form asp-action="Index">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <input asp-for="Key" value="offset" hidden />
<input asp-for="Key" hidden /> <label asp-for="Value">First work day in the semester: (Current: @Model?.Value)</label>
<label asp-for="Value">First work day in the semester: (Current: @Model.Value)</label>
<input type="number" asp-for="Value" /> <input type="number" asp-for="Value" />
<span asp-validation-for="Value" class="text-danger"></span> <span asp-validation-for="Value" class="text-danger"></span>
<input type="submit" value="Update" class="btn"/> <input type="submit" value="Update" />
</form> </form>
</article>
<link href="~/css/Admin.css" type="text/css" rel="stylesheet" /> <h2>Privacy policies</h2>
<a class="comment" asp-action="PrivacyPolicy" target="_blank">// View privacy policy page</a><br />
<a class="comment" asp-action="CreatePolicy">// + Add new privacy policy</a>
<table>
<thead>
<tr>
<th>Langauge</th>
<th>Last update</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (ResumeModel item in policies)
{
<tr>
<td>@item.Language</td>
<td>@item.LastUpdate</td>
<td>
<a asp-action="EditPolicy" asp-route-id="@item.Language">Edit</a> |
<a asp-action="DeletePolicy" asp-route-id="@item.Language">Delete</a>
</td>
</tr>
}
</tbody>
</table>
</article>
@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html>
<head>
<title>@ViewData["Title"] - Admin pangel - XFox111.NET</title>
<link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="~/css/Style.css" />
<link rel="stylesheet" type="text/css" href="~/css/Fonts.css" />
<link rel="stylesheet" href="https://s3.amazonaws.com/icomoon.io/114779/Socicon/style.css?u8vidh" />
<link href="~/css/Admin.css" type="text/css" rel="stylesheet" />
<script type="text/javascript">
function ToggleMenu()
{
var menu = document.getElementById("main-menu");
if (menu.style.display == "none")
menu.style.display = "initial";
else
menu.style.display = "none";
}
</script>
@RenderSection("Imports", false)
@{
if (IsSectionDefined("OpenGraph"))
RenderSection("OpenGraph");
else
{
<meta name="author" content="Michael 'XFox' Gordeev" />
<meta name="description" content="Hi, my name is Michael. I'm C# Developer and this is my personal website. Here you can find info about me, my projects and more. Check it out!" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="XFox111.NET" />
<meta property="og:url" content="//XFox111.NET/" />
<meta property="og:locale" content="en_US" />
<meta property="og:image" content="/images/me.png" />
<meta property="og:description" content="Hi, my name is Michael. I'm C# Developer and this is my personal website. Here you can find info about me, my projects and more. Check it out!" />
<meta property="og:title" content="Michael 'XFox' Gordeev - Personal website" />
}
}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<nav>
<a asp-controller="Home" asp-action="Index" asp-area="">XFox111.NET</a>
<menu type="toolbar" id="main-menu" style="display:none">
<li><a asp-area="" asp-controller="Home" asp-action="Index">AboutMe();</a></li>
<li><a href="//xfox111.blogspot.com/" target="_blank">MyBlog();</a></li>
<li><a asp-area="" asp-controller="Resume" asp-action="Index">Resume();</a></li>
<li><a asp-area="" asp-controller="Projects" asp-action="Index">Projects();</a></li>
<li><a asp-area="" asp-controller="Gallery" asp-action="Index">Arts();</a></li>
<li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contacts();</a></li>
</menu>
<div>
<a asp-controller="Home" asp-action="SwitchLanguage" lang="ru">РУС &#xE12B;</a>
<a id="menu-toggle" onclick="ToggleMenu();">&#xE700;</a>
</div>
</nav>
<main onclick="document.querySelector('#main-menu').style.display = 'none'">
@RenderBody()
</main>
<footer>
<span class="comment">// Copyright &copy;@(DateTime.Today.Year) Michael "XFox" Gordeev</span>
</footer>
</body>
</html>
@@ -1,5 +1,4 @@
@using MyWebsite @using MyWebsite
@using MyWebsite.Models @using MyWebsite.Models
@using MyWebsite.ViewModels @using MyWebsite.ViewModels
@using MyWebsite.Areas.Admin.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -13,13 +13,13 @@ namespace MyWebsite.Areas.Projects.Controllers
[Area("Projects")] [Area("Projects")]
public class FoxTubeController : ExtendedController public class FoxTubeController : ExtendedController
{ {
readonly DatabaseContext db;
readonly List<string> paths = new List<string>(); readonly List<string> paths = new List<string>();
readonly List<string> files; readonly List<string> files;
public FoxTubeController(DatabaseContext context) : base(context) private FoxTubeDatabaseContext FoxTubeDatabaseContext { get; set; }
{
db = context;
public FoxTubeController(DatabaseContext context, FoxTubeDatabaseContext foxTubeDatabase) : base(context)
{
FoxTubeDatabaseContext = foxTubeDatabase;
Scan(Directory.GetCurrentDirectory() + "\\wwwroot\\assets\\FoxTube\\Screenshots\\" + CultureInfo.CurrentUICulture.ThreeLetterISOLanguageName, paths); Scan(Directory.GetCurrentDirectory() + "\\wwwroot\\assets\\FoxTube\\Screenshots\\" + CultureInfo.CurrentUICulture.ThreeLetterISOLanguageName, paths);
for (int i = 0; i < paths.Count; i++) for (int i = 0; i < paths.Count; i++)
@@ -29,14 +29,14 @@ namespace MyWebsite.Areas.Projects.Controllers
} }
public IActionResult Index() => public IActionResult Index() =>
View(new ScreenshotViewModel(db) View(new ScreenshotViewModel(Database)
{ {
Paths = paths, Paths = paths,
Names = files Names = files
}); });
public IActionResult Screenshot(string id) => public IActionResult Screenshot(string id) =>
View(new ScreenshotViewModel(db) View(new ScreenshotViewModel(Database)
{ {
Paths = paths, Paths = paths,
Names = files, Names = files,
@@ -44,7 +44,7 @@ namespace MyWebsite.Areas.Projects.Controllers
}); });
public IActionResult Privacy() => public IActionResult Privacy() =>
View(new ResumeViewModel(db, CultureInfo.CurrentCulture)); View(new ResumeViewModel(FoxTubeDatabaseContext.PrivacyPolicies.Find(CultureInfo.CurrentCulture.TwoLetterISOLanguageName) ?? FoxTubeDatabaseContext.PrivacyPolicies.Find("en"), Database));
void Scan(string path, List<string> files) void Scan(string path, List<string> files)
{ {
@@ -22,9 +22,7 @@
</ul> </ul>
<h1>Trailer</h1> <h1>Trailer</h1>
<div> <iframe class="video" src="https://www.youtube.com/embed/Mio9FbxmbhM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Mio9FbxmbhM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<h1>Description</h1> <h1>Description</h1>
<p> <p>
@@ -5,9 +5,9 @@
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to description</a></p> &#xE760; <a asp-action="Index">Back to description</a>
<h1>FoxTube privacy policy</h1> <h1>FoxTube privacy policy</h1>
<p>Last update: @Model?.Resume.LastUpdate</p> Last update: @Model?.Resume.LastUpdate
</header> </header>
<article> <article>
@@ -16,15 +16,34 @@
@section Imports { @section Imports {
<style type="text/css"> <style type="text/css">
header, article { article
{
margin: 0px 50px; margin: 0px 50px;
} }
header a {
header
{
margin: 16px 50px;
}
header a
{
color: black; color: black;
text-decoration: none; text-decoration: none;
} }
header a:hover {
header a:hover
{
text-decoration: underline; text-decoration: underline;
} }
@@media only screen and (max-width: 500px)
{
header, article
{
margin-right: 20px;
margin-left: 20px;
}
}
</style> </style>
} }
@@ -1,12 +1,10 @@
@using System.IO @model ScreenshotViewModel
@using System.Globalization
@model ScreenshotViewModel
@{ @{
ViewData["Title"] = "Screenshot"; ViewData["Title"] = "Screenshot";
Layout = "_Layout.cshtml"; Layout = "_Layout.cshtml";
} }
<article class="image-overview-block"> <header>
<p class="controls"> <p class="controls">
&#xE15C; <a asp-action="Index" class="back">Back to description</a> &#xE15C; <a asp-action="Index" class="back">Back to description</a>
@if (Model.Previous != null) @if (Model.Previous != null)
@@ -20,25 +18,28 @@
<a asp-action="Screenshot" asp-route-id="@Model.Next" class="back">Next</a> <a asp-action="Screenshot" asp-route-id="@Model.Next" class="back">Next</a>
} }
</p> </p>
<p>
<p class="title">
<b>@Model.Current</b> <b>@Model.Current</b>
</p> </p>
</header>
<img src="@Model.Paths[Model.Position]" id="image" onclick="ToggleImageSize();"/> <article class="image-overview-block">
<img src="@Model.Paths[Model.Position]" id="image" onclick="ToggleImageSize();" />
</article> </article>
@section Imports { @section Imports {
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" /> <link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<style type="text/css"> <style type="text/css">
#image { #image
max-height: initial; {
max-height: 75vh;
} }
.controls {
display: inline-block; header
} {
.title { display: grid;
float: right; grid-template-columns: 1fr auto;
margin: 0px 20px;
} }
</style> </style>
@@ -51,8 +52,8 @@
image.style = ""; image.style = "";
else else
{ {
image.style.maxHeight = "none"; image.style.maxHeight = "initial";
image.style.maxWidth = "none"; image.style.maxWidth = "initial";
image.style.cursor = "zoom-out"; image.style.cursor = "zoom-out";
} }
} }
@@ -6,13 +6,14 @@
<base href="~/assets/FoxTube/" /> <base href="~/assets/FoxTube/" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="~/css/Socicon.css" /> <link rel="stylesheet" href="https://s3.amazonaws.com/icomoon.io/114779/Socicon/style.css?u8vidh" />
<link rel="stylesheet" type="text/css" href="~/css/Fonts.css" />
<link rel="stylesheet" type="text/css" href="FoxTube.css" /> <link rel="stylesheet" type="text/css" href="FoxTube.css" />
<script type="text/javascript"> <script type="text/javascript">
function ToggleMenu() function ToggleMenu()
{ {
var menu = document.getElementById("main-menu"); var menu = document.querySelector("menu");
if (menu.style.display == "none") if (menu.style.display == "none")
menu.style.display = "initial"; menu.style.display = "initial";
@@ -30,7 +31,8 @@
<meta name="author" content="Michael 'XFox' Gordeev"> <meta name="author" content="Michael 'XFox' Gordeev">
<meta name="description" content="YouTube client for Windows 10 family devices. It's fast and convenient. It also supports live streams and 8K videos!"> <meta name="description" content="YouTube client for Windows 10 family devices. It's fast and convenient. It also supports live streams and 8K videos!">
<meta property="og:image" content="FoxTubeLogo.png"> <meta property="og:image" content="/assets/FoxTube/Logo.svg">
<meta property="og:site_name" content="FoxTube - New YouTube client for Windows 10" />
<meta property="og:video" content="//www.youtube.com/embed/Mio9FbxmbhM"> <meta property="og:video" content="//www.youtube.com/embed/Mio9FbxmbhM">
<meta property="og:video" content="https://www.youtube.com/embed/Mio9FbxmbhM"> <meta property="og:video" content="https://www.youtube.com/embed/Mio9FbxmbhM">
<meta property="og:video" content="https://www.youtube.com/v/Mio9FbxmbhM"> <meta property="og:video" content="https://www.youtube.com/v/Mio9FbxmbhM">
@@ -49,15 +51,11 @@
<img class="logo" src="Logo.svg" /> <img class="logo" src="Logo.svg" />
<a asp-action="Index"> <a asp-action="Index">
<b>FoxTube</b><br /> <b>FoxTube</b><br />
YouTube client for Windows 10 <span>YouTube client for Windows 10</span>
</a> </a>
<menu type="toolbar" id="main-menu" style="display:none"> <menu type="toolbar" style="display:none">
<li><a asp-area="" asp-controller="Home" asp-action="Index">Home();</a></li> <partial name="~/Views/Shared/TopBarMenu.cshtml" />
<li><a asp-area="" asp-controller="Resume" asp-action="Index">MyResume();</a></li>
<li><a asp-area="" asp-controller="Projects" asp-action="Index">Projects();</a></li>
<li><a asp-area="" asp-controller="Gallery" asp-action="Index">Arts();</a></li>
<li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contacts();</a></li>
</menu> </menu>
<p> <p>
@@ -67,7 +65,7 @@
</p> </p>
</nav> </nav>
<main> <main onclick="document.querySelector('menu').style.display = 'none'">
@RenderBody() @RenderBody()
</main> </main>
@@ -0,0 +1,14 @@
@model ResumeViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
ViewData["Title"] = "GUT.Schedule privacy policy";
}
<header>
<h1>GUT.Schedule privacy policy</h1>
<p>Last update: @Model?.Resume?.LastUpdate</p>
</header>
<article>
@Html.Raw(Model?.Resume?.Content)
</article>
@@ -39,11 +39,15 @@ namespace MyWebsite.Controllers
CredentialModel user = Database.Users.FirstOrDefault(i => i.Email == model.Credential.Email); CredentialModel user = Database.Users.FirstOrDefault(i => i.Email == model.Credential.Email);
if (user == null || !Encryptor.VerifyHash(model?.Credential.Password, user.Password)) if (user == null || !Encryptor.VerifyHash(model?.Credential.Password, user.Password))
{ {
if (!Database.Users.Any())
goto Authorize;
ModelState.AddModelError("Authorization error", "Invaild e-mail or password"); ModelState.AddModelError("Authorization error", "Invaild e-mail or password");
return View(new CredentialViewModel(Database, model)); return View(new CredentialViewModel(Database, model));
} }
Claim claim = new Claim(ClaimsIdentity.DefaultNameClaimType, user.Email); Authorize:
Claim claim = new Claim(ClaimsIdentity.DefaultNameClaimType, user?.Email ?? "root");
ClaimsIdentity id = new ClaimsIdentity(new Claim[] { claim }, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType); ClaimsIdentity id = new ClaimsIdentity(new Claim[] { claim }, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id)).ConfigureAwait(false); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id)).ConfigureAwait(false);
@@ -56,16 +60,5 @@ namespace MyWebsite.Controllers
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false);
return RedirectToAction("Login", "Admin"); return RedirectToAction("Login", "Admin");
} }
[AllowAnonymous]
public bool ResetPassword(string id)
{
CredentialModel user = Database.Users.Find("michael.xfox@outlook.com");
user.Password = Encryptor.ComputeHash(id);
Database.Users.Update(user);
Database.SaveChanges();
return true;
}
} }
} }
@@ -9,6 +9,8 @@ namespace MyWebsite.Models.Databases
public DbSet<Message> Messages { get; set; } public DbSet<Message> Messages { get; set; }
public DbSet<Changelog> Changelogs { get; set; } public DbSet<Changelog> Changelogs { get; set; }
public DbSet<ResumeModel> PrivacyPolicies { get; set; }
public FoxTubeDatabaseContext(DbContextOptions<FoxTubeDatabaseContext> options) : base(options) => public FoxTubeDatabaseContext(DbContextOptions<FoxTubeDatabaseContext> options) : base(options) =>
Database.EnsureCreated(); Database.EnsureCreated();
} }
@@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore;
namespace MyWebsite.Models.Databases
{
public class GUTScheduleDatabaseContext : DbContext
{
public DbSet<ResumeModel> PrivacyPolicies { get; set; }
public DbSet<CustomData> OffsetDates { get; set; }
public GUTScheduleDatabaseContext(DbContextOptions<GUTScheduleDatabaseContext> options) : base(options) =>
Database.EnsureCreated();
}
}
+1 -2
View File
@@ -9,7 +9,7 @@ namespace MyWebsite.Models
public class ImageModel public class ImageModel
{ {
[Key] [Key]
[Column(TypeName = "varchar(20)")] [Column(TypeName = "varchar(255)")]
[DisplayName("File name")] [DisplayName("File name")]
public string FileName { get; set; } public string FileName { get; set; }
@@ -27,7 +27,6 @@ namespace MyWebsite.Models
[DisplayName("Description")] [DisplayName("Description")]
public string Description => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianDescription) ? RussianDescription : EnglishDescription; public string Description => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianDescription) ? RussianDescription : EnglishDescription;
[Required]
[Column(TypeName = "text")] [Column(TypeName = "text")]
[DisplayName("Description (en)")] [DisplayName("Description (en)")]
public string EnglishDescription { get; set; } public string EnglishDescription { get; set; }
+10 -4
View File
@@ -1,4 +1,5 @@
using System.ComponentModel; using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization; using System.Globalization;
@@ -10,8 +11,14 @@ namespace MyWebsite.Models
{ {
[Key] [Key]
[Required] [Required]
[DisplayName("ID (Order)")] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public decimal Id { get; set; } [DisplayName("ID")]
public Guid Id { get; set; }
[Required]
[Column(TypeName = "int")]
[DisplayName("Order")]
public int Order { get; set; }
[DisplayName("Title")] [DisplayName("Title")]
public string Title => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianTitle : EnglishTitle; public string Title => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianTitle : EnglishTitle;
@@ -42,7 +49,6 @@ namespace MyWebsite.Models
[DisplayName("Link text caption")] [DisplayName("Link text caption")]
public string LinkCaption => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianLinkCaption : EnglishLinkCaption; public string LinkCaption => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianLinkCaption : EnglishLinkCaption;
[Required]
[Column(TypeName = "varchar(50)")] [Column(TypeName = "varchar(50)")]
[DisplayName("Link text caption (en)")] [DisplayName("Link text caption (en)")]
public string EnglishLinkCaption { get; set; } public string EnglishLinkCaption { get; set; }
+14 -24
View File
@@ -1,36 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<AssemblyName>XFox111dotNET</AssemblyName>
<Authors>Michael "XFox" Gordeev</Authors>
<Company>FoxDev Studio</Company>
<Product>XFox111.NET</Product>
<Description>This is my personal website written in ASP.NET MVC</Description>
<Copyright>©2020 Michael "XFox" Gordeev</Copyright>
<PackageProjectUrl>https://xfox111.net/</PackageProjectUrl>
<RepositoryUrl>https://github.com/xfox111/cvwebsite</RepositoryUrl>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="wwwroot\projects-assets\**" />
<Content Remove="wwwroot\projects-assets\**" />
<EmbeddedResource Remove="wwwroot\projects-assets\**" />
<None Remove="wwwroot\projects-assets\**" />
</ItemGroup>
<ItemGroup>
<None Remove="Areas\Admin\NewFile.txt" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8"> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.1" />
<PackageReference Include="Select.HtmlToPdf.NetCore" Version="19.2.0" /> <PackageReference Include="Select.HtmlToPdf.NetCore" Version="19.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\assets\FoxTube\Screenshots\" />
</ItemGroup>
</Project> </Project>
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Controller</Controller_SelectedScaffolderCategoryPath>
<WebStackScaffolding_ControllerDialogWidth>600</WebStackScaffolding_ControllerDialogWidth>
<WebStackScaffolding_IsLayoutPageSelected>False</WebStackScaffolding_IsLayoutPageSelected>
<WebStackScaffolding_IsPartialViewSelected>False</WebStackScaffolding_IsPartialViewSelected>
<WebStackScaffolding_IsReferencingScriptLibrariesSelected>False</WebStackScaffolding_IsReferencingScriptLibrariesSelected>
<WebStackScaffolding_IsAsyncSelected>False</WebStackScaffolding_IsAsyncSelected>
<WebStackScaffolding_ViewDialogWidth>600</WebStackScaffolding_ViewDialogWidth>
</PropertyGroup>
</Project>
+4 -4
View File
@@ -10,13 +10,10 @@ using MyWebsite.Models.Databases;
namespace MyWebsite namespace MyWebsite
{ {
// TODO: Add reordering for contact links
// TODO: Complete project admin page
// TODO: Complete artworks admin page
// TODO: FoxTube API admin page // TODO: FoxTube API admin page
// TODO: Complete homepage // TODO: Complete homepage
// TODO: Complete FoxTube page
// TODO: Add localization system // TODO: Add localization system
// TODO: Add blog
// TODO: Rid of JavaScript (use Blazor) // TODO: Rid of JavaScript (use Blazor)
public class Startup public class Startup
{ {
@@ -36,6 +33,9 @@ namespace MyWebsite
services.AddDbContext<FoxTubeDatabaseContext>(options => services.AddDbContext<FoxTubeDatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("FoxTubeDB"))); options.UseSqlServer(Configuration.GetConnectionString("FoxTubeDB")));
services.AddDbContext<GUTScheduleDatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("GUTScheduleDB")));
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
options.LoginPath = new PathString("/Admin/Login")); options.LoginPath = new PathString("/Admin/Login"));
@@ -8,6 +8,9 @@ namespace MyWebsite.ViewModels
{ {
public ResumeModel Resume { get; } public ResumeModel Resume { get; }
public ResumeViewModel(DatabaseContext context, CultureInfo language) : base(context) => public ResumeViewModel(DatabaseContext context, CultureInfo language) : base(context) =>
Resume = context.Resume.Find(language?.Name) ?? context.Resume.Find("en-US"); Resume = context.Resume.Find(language?.TwoLetterISOLanguageName) ?? context.Resume.Find("en");
public ResumeViewModel(ResumeModel model, DatabaseContext context) : base(context) =>
Resume = model;
} }
} }
@@ -10,6 +10,6 @@ namespace MyWebsite.ViewModels
public IEnumerable<LinkModel> Links { get; } public IEnumerable<LinkModel> Links { get; }
public ViewModelBase(DatabaseContext context) => public ViewModelBase(DatabaseContext context) =>
Links = context?.Links.ToList(); Links = context?.Links.OrderBy(i => i.Order);
} }
} }
+5 -5
View File
@@ -6,23 +6,23 @@
<h1>Administration</h1> <h1>Administration</h1>
</header> </header>
<article> <article class="admin-menu">
<p class="admin-menu"> <p>
<a asp-action="Gallery" class="comment">// Artworks</a><br /> <a asp-action="Gallery" class="comment">// Artworks</a><br />
<a asp-action="Projects" class="comment">// Projects</a><br /> <a asp-action="Projects" class="comment">// Projects</a><br />
<a asp-action="Badges" class="comment">// Badges</a><br /> <a asp-action="Badges" class="comment">// Badges</a><br />
<a asp-action="Resume" class="comment">// Resume</a><br /> <a asp-action="Resume" class="comment">// Resume</a><br />
<a asp-action="Contacts" class="comment">// Contact links</a> <a asp-action="Contacts" class="comment">// Contact links</a>
</p> </p>
<p class="admin-menu"> <p>
<a asp-action="FoxTube" class="comment">// FoxTube API</a><br /> <a asp-action="FoxTube" class="comment">// FoxTube API</a><br />
<a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a> <a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a>
</p> </p>
<p> <p>
<a asp-action="Credential" class="logout">// Change credential information</a> <a asp-action="Credential" class="comment logout">// Change credential information</a>
</p> </p>
<p> <p>
<a asp-action="Logout" class="logout">&#xE875; Logout</a> <a asp-action="Logout" class="comment logout">&#xE875; Logout</a>
</p> </p>
</article> </article>
@@ -4,7 +4,6 @@
} }
<header> <header>
<p>
@if (Model.Previous != null) @if (Model.Previous != null)
{ {
@:&#xE760; @:&#xE760;
@@ -17,11 +16,10 @@
@:| &#xE761; @:| &#xE761;
<a asp-action="Details" asp-route-id="@Model.Next">Next</a> <a asp-action="Details" asp-route-id="@Model.Next">Next</a>
} }
</p>
</header> </header>
<article class="image-overview-block"> <article class="image-overview-block">
<img src="~/images/Gallery/@Model.Current?.FileName" onclick="ToggleImageSize();" id="image" /> <img src="~/images/Gallery/@Model.Current?.FileName" onclick="ToggleImageSize();" />
<div> <div>
<h1>@Model.Current?.Title</h1> <h1>@Model.Current?.Title</h1>
@@ -32,13 +30,14 @@
</div> </div>
</article> </article>
@section Imports { @section Imports
{
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" /> <link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<script type="text/javascript"> <script type="text/javascript">
function ToggleImageSize() function ToggleImageSize()
{ {
var image = document.getElementById("image"); var image = document.querySelector("img");
if (image.style.cursor == "zoom-out") if (image.style.cursor == "zoom-out")
image.style = ""; image.style = "";
@@ -5,14 +5,14 @@
<header> <header>
<h1>My resume</h1> <h1>My resume</h1>
<p>Last update: @Model?.Resume.LastUpdate</p> <p>Last update: @Model?.Resume?.LastUpdate</p>
<a class="comment" asp-action="Download">// Download CV (.pdf) &#xE118;</a><br /> <a class="comment" asp-action="Download">// Download CV (.pdf) &#xE118;</a><br />
<a class="comment" asp-action="Print">// Print CV &#xE749;</a> <a class="comment" asp-action="Print">// Print CV &#xE749;</a>
</header> </header>
<article> <article>
@Html.Raw(Model?.Resume.Content) @Html.Raw(Model?.Resume?.Content)
</article> </article>
@section Footer @section Footer
@@ -12,6 +12,7 @@
<base href="~/assets/Construction/" /> <base href="~/assets/Construction/" />
<link rel="stylesheet" type="text/css" href="Construction.css"> <link rel="stylesheet" type="text/css" href="Construction.css">
<link rel="stylesheet" type="text/css" href="~/css/Fonts.css">
<script type="text/javascript" src="Construction.js"></script> <script type="text/javascript" src="Construction.js"></script>
<meta name="author" content="Michael 'XFox' Gordeev"> <meta name="author" content="Michael 'XFox' Gordeev">
@@ -89,7 +90,7 @@
@foreach (LinkModel item in Model.Links.Where(i => i.Name.Belongs("outlook", "linkedin", "vkontakte", "twitter", "github"))) @foreach (LinkModel item in Model.Links.Where(i => i.Name.Belongs("outlook", "linkedin", "vkontakte", "twitter", "github")))
{ {
<p>@item.Title: <a href="@item.Url">@((item.Url.ToString().StartsWith("mailto:") ? "" : "https:") + item.Url)</a></p> <p>@item.Title: <a href="@item.Url" target="_blank">@((item.Url.ToString().StartsWith("mailto:") ? "" : "https:") + item.Url)</a></p>
} }
<br /> <br />
@@ -5,8 +5,8 @@
<!-- TODO: Make Error page--> <!-- TODO: Make Error page-->
<header> <header>
<h1 class="text-danger">Error.</h1> <h1>Error</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2> <h2>An error occurred while processing your request.</h2>
</header> </header>
<article> <article>
+18 -1
View File
@@ -8,6 +8,23 @@
<article> <article>
<p> <p>
Homepage Hi guys! This is my website. And this is its home page. Usually, homepages should look as much glorious and cool as they can. But for some inspirational reasons I've done everything except homepage.
</p>
<p>
Well, maybe not everything... But the most of it. If I could I would leave it offline for a few more <s>years</s> <s>month</s> time. But I need it online now, so here we go. <br />
Lets consider it as 0.1.2020.03.08.666 prerelease beta technical preview demo pre-RTM version.
</p>
<p>
So you can lurk around and check other pages. I'm pretty sure they should be fine.
</p>
<dl>
<dt>Cheers,</dt>
<dd>Michael "XFox" Gordeev</dd>
</dl>
<p hidden>
But anyway I should tell some more about it, shouldn't I?
</p>
<p hidden>
My name is Michael, I'm C# Developer and CS student in Saint Petersburg (the russian one:)
</p> </p>
</article> </article>
@@ -14,7 +14,7 @@
<article> <article>
@if (Model.Projects.Count() > 0) @if (Model.Projects.Count() > 0)
{ {
@foreach (ProjectModel project in Model.Projects) @foreach (ProjectModel project in Model.Projects.OrderBy(i => i.Order))
{ {
<div class="project-item"> <div class="project-item">
<div> <div>
@@ -40,6 +40,7 @@
} }
</article> </article>
@section Imports { @section Imports
{
<link rel="stylesheet" type="text/css" href="~/css/Projects.css" /> <link rel="stylesheet" type="text/css" href="~/css/Projects.css" />
} }
@@ -0,0 +1,6 @@
<li><a asp-area="" asp-controller="Home" asp-action="Index">AboutMe();</a></li>
<li hidden><a href="//xfox111.blogspot.com/" target="_blank">MyBlog();</a></li>
<li><a asp-area="" asp-controller="Resume" asp-action="Index">MyResume();</a></li>
<li><a asp-area="" asp-controller="Projects" asp-action="Index">Projects();</a></li>
<li><a asp-area="" asp-controller="Gallery" asp-action="Index">Arts();</a></li>
<li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contacts();</a></li>
@@ -6,12 +6,13 @@
<link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="~/css/Style.css" /> <link rel="stylesheet" type="text/css" href="~/css/Style.css" />
<link rel="stylesheet" type="text/css" href="~/css/Socicon.css" /> <link rel="stylesheet" type="text/css" href="~/css/Fonts.css" />
<link rel="stylesheet" href="https://s3.amazonaws.com/icomoon.io/114779/Socicon/style.css?u8vidh" />
<script type="text/javascript"> <script type="text/javascript">
function ToggleMenu() function ToggleMenu()
{ {
var menu = document.getElementById("main-menu"); var menu = document.querySelector("nav menu");
if (menu.style.display == "none") if (menu.style.display == "none")
menu.style.display = "initial"; menu.style.display = "initial";
@@ -47,12 +48,8 @@
<nav> <nav>
<a asp-controller="Home" asp-action="Index" asp-area="">XFox111.NET</a> <a asp-controller="Home" asp-action="Index" asp-area="">XFox111.NET</a>
<menu type="toolbar" id="main-menu" style="display:none"> <menu type="toolbar" style="display:none">
<li><a asp-area="" asp-controller="Home" asp-action="Index">AboutMe();</a></li> <partial name="~/Views/Shared/TopBarMenu.cshtml" />
<li><a asp-area="" asp-controller="Resume" asp-action="Index">Resume();</a></li>
<li><a asp-area="" asp-controller="Projects" asp-action="Index">Projects();</a></li>
<li><a asp-area="" asp-controller="Gallery" asp-action="Index">Arts();</a></li>
<li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contacts();</a></li>
</menu> </menu>
<div> <div>
@@ -61,7 +58,7 @@
</div> </div>
</nav> </nav>
<main> <main onclick="document.querySelector('nav menu').style.display = 'none'">
@RenderBody() @RenderBody()
</main> </main>
@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
+2 -1
View File
@@ -9,6 +9,7 @@
"AllowedHosts": "*", "AllowedHosts": "*",
"ConnectionStrings": { "ConnectionStrings": {
"MainDB": "Server=(localdb)\\mssqllocaldb;Database=xfox111;Trusted_Connection=True;", "MainDB": "Server=(localdb)\\mssqllocaldb;Database=xfox111;Trusted_Connection=True;",
"FoxTubeDB": "Server=(localdb)\\mssqllocaldb;Database=foxtube;Trusted_Connection=True;" "FoxTubeDB": "Server=(localdb)\\mssqllocaldb;Database=foxtube;Trusted_Connection=True;",
"GUTScheduleDB": "Server=(localdb)\\mssqllocaldb;Database=gutSchedule;Trusted_Connection=True;"
} }
} }
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--Redirects-->
<system.webServer>
<rewrite>
<rules>
<!--Legacy redirects-->
<rule name="GutSchedule API redirect" stopProcessing="true">
<match url="schedule_offset.txt" />
<action type="Redirect" url="https://xfox111.net/API/GUTSchedule/SemesterOffsetDay" redirectType="Permanent" appendQueryString="false" />
</rule>
<rule name="GUT.Schedule Privacy policy" stopProcessing="true">
<match url="Projects/GUTSchedule/PrivacyPolicy.txt" />
<action type="Redirect" url="https://xfox111.net/Projects/GUTSchedule/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule>
<rule name="FoxTubr Privacy policy" stopProcessing="true">
<match url="Projects/FoxTube/PrivacyPolicy.txt" />
<action type="Redirect" url="https://xfox111.net/Projects/FoxTube/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule>
<!--/Legacy redirects-->
<!--Website maintainence redirect-->
<rule name="Construction redirect" stopProcessing="true" enabled="false">
<match url="^(?!Admin|API|Construction|css|assets|fonts|images)" />
<action type="Redirect" url="https://xfox111.net/Construction" redirectType="Temporary" appendQueryString="false" />
</rule>
<!--\Website maintainence redirect-->
</rules>
</rewrite>
</system.webServer>
<!--/Redirects-->
<location path="." inheritInChildApplications="false" allowOverride="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath=".\XFox111dotNET.exe" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
<system.web>
<trust level="Full" />
</system.web>
</location>
<system.web>
<compilation defaultLanguage="c#" />
<globalization fileEncoding="utf-8" />
</system.web>
</configuration>
<!--ProjectGuid: 9fb2b925-dc18-4081-ac91-96f2c49415f9-->
@@ -1,19 +1,4 @@
/* Declaring fonts */ /* Header */
@font-face
{
font-family: 'Consolas';
src: url("/fonts/Consolas/consolas.eot");
src: url("/fonts/Consolas/consolas.eot?#iefix") format("embedded-opentype"), url("/fonts/Consolas/consolas.otf") format("opentype"), url("/fonts/Consolas/consolas.svg") format("svg"), url("/fonts/Consolas/consolas.ttf") format("truetype"), url("/fonts/Consolas/consolas.woff") format("woff"), url("/fonts/Consolas/consolas.woff2") format("woff2");
}
@font-face
{
font-family: 'Segoe MDL2 Assets';
src: url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.eot");
src: url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.eot?#iefix") format("embedded-opentype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.otf") format("opentype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.svg") format("svg"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.ttf") format("truetype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.woff") format("woff"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.woff2") format("woff2");
}
/* Header */
nav nav
{ {
user-select: none; user-select: none;
@@ -68,6 +53,7 @@ nav
line-height: 26px; line-height: 26px;
text-align: center; text-align: center;
} }
nav > div:last-child select nav > div:last-child select
{ {
background-color: #333337; background-color: #333337;
@@ -78,9 +64,10 @@ nav
height: 21px; height: 21px;
margin: 2px 0px; margin: 2px 0px;
} }
nav > div:last-child img nav > div:last-child img
{ {
height: 100%; height: 26px;
} }
/* Body */ /* Body */
@@ -95,7 +82,7 @@ body
color: white; color: white;
font-size: 9pt; font-size: 9pt;
margin: 0px; margin: 0px;
font-family: 'Segoe UI Symbol'; font-family: 'SegoeUISymbol';
overflow: auto; overflow: auto;
display: grid; display: grid;
height: 100vh; height: 100vh;
@@ -103,7 +90,7 @@ body
main main
{ {
font-family: Consolas; font-family: 'Consolas';
margin: 50px 0px 50px 12px; margin: 50px 0px 50px 12px;
} }
@@ -111,6 +98,7 @@ main
{ {
margin: 0px; margin: 0px;
display: none; display: none;
white-space: nowrap;
} }
a a
@@ -223,3 +211,38 @@ footer
{ {
content: '\1F7A8'; content: '\1F7A8';
} }
@media only screen and (max-width: 560px)
{
nav > div:last-child
{
height: initial;
grid-template-columns: initial;
}
nav > div:last-child select
{
width: initial;
margin: 0px 5px;
}
nav > div > span
{
text-align: start !important;
}
main
{
margin-top: 110px;
}
.git-btn
{
min-width: 17px;
}
.git-btn > span
{
display: none;
}
}
@@ -1,19 +1,4 @@
/* Declaring fonts */ /* Header styles */
@font-face
{
font-family: 'Consolas';
src: url("/fonts/Consolas/consolas.eot");
src: url("/fonts/Consolas/consolas.eot?#iefix") format("embedded-opentype"), url("/fonts/Consolas/consolas.otf") format("opentype"), url("/fonts/Consolas/consolas.svg") format("svg"), url("/fonts/Consolas/consolas.ttf") format("truetype"), url("/fonts/Consolas/consolas.woff") format("woff"), url("/fonts/Consolas/consolas.woff2") format("woff2");
}
@font-face
{
font-family: 'Segoe MDL2 Assets';
src: url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.eot");
src: url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.eot?#iefix") format("embedded-opentype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.otf") format("opentype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.svg") format("svg"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.ttf") format("truetype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.woff") format("woff"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.woff2") format("woff2");
}
/* Header styles */
nav nav
{ {
display: grid; display: grid;
@@ -70,40 +55,6 @@ menu
display: block; display: block;
} }
/* Adaptive code */
@media only screen and (min-width: 1150px)
{
menu
{
display: initial !important;
grid-row: 1;
grid-column: 3;
margin: 0px;
align-self: end;
margin-bottom: 3px;
}
menu li
{
display: inline-block;
margin-right: 10px;
margin-top: 0px;
}
#menu-toggle
{
display: none;
}
}
@media only screen and (max-width: 700px) {
body
{
margin-top: 112px !important;
height: calc(100vh - 112px) !important;
}
}
/* Body styles */ /* Body styles */
html html
{ {
@@ -112,7 +63,7 @@ html
body body
{ {
font-family: 'Consolas', 'Segoe MDL2 Assets'; font-family: 'Consolas', 'SegoeMDL2Assets';
overflow: auto; overflow: auto;
margin: 0px; margin: 0px;
margin-top: 84px; margin-top: 84px;
@@ -123,7 +74,7 @@ body
main main
{ {
font-family: 'Calibri', 'Segoe MDL2 Assets'; font-family: 'Calibri', 'SegoeMDL2Assets';
} }
.back .back
@@ -152,6 +103,18 @@ article
color: #57a64a !important; color: #57a64a !important;
} }
.video
{
max-width: 560px;
height: 315px;
width: 100%;
}
*[hidden]
{
display: none;
}
/* Footer styles */ /* Footer styles */
footer footer
{ {
@@ -172,3 +135,56 @@ footer
{ {
color: orangered; color: orangered;
} }
/* Adaptive code */
@media only screen and (min-width: 1150px)
{
menu
{
display: initial !important;
grid-row: 1;
grid-column: 3;
margin: 0px;
align-self: end;
margin-bottom: 3px;
}
menu li
{
display: inline-block;
margin-right: 10px;
margin-top: 0px;
}
#menu-toggle
{
display: none;
}
}
@media only screen and (max-width: 700px) and (min-width: 480px)
{
body
{
margin-top: 112px !important;
height: calc(100vh - 112px) !important;
}
}
@media only screen and (max-width: 480px)
{
nav > a > span
{
display: none;
}
.video
{
height: 240px !important;
}
}
nav > p > a:nth-last-child(2)
{
visibility: hidden;
}
+75 -37
View File
@@ -1,7 +1,7 @@
input input:not([type="checkbox"])
{ {
padding: 10px; padding: 10px;
border-radius: 10px; border-radius: 5px;
border: 1px solid black; border: 1px solid black;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
@@ -12,32 +12,84 @@ select
{ {
padding: 10px; padding: 10px;
border: 1px solid black; border: 1px solid black;
border-radius: 10px; border-radius: 5px;
width: 100%; width: 100%;
-moz-appearance: none; /* Firefox */ -moz-appearance: none; /* Firefox */
-webkit-appearance: none; /* Safari and Chrome */ -webkit-appearance: none; /* Safari and Chrome */
} }
input:invalid
{
border: 3px solid red;
}
input[type="submit"],
input[type="button"],
button button
{ {
margin: 10px; border-radius: 5px;
border-radius: 10px;
border: 0px; border: 0px;
padding: 10px 20px; padding: 10px 20px;
background-color: #343434; background-color: #343434;
color: white; color: white;
cursor: pointer;
}
input[type="submit"]:disabled,
input[type="button"]:disabled,
button:disabled
{
background-color: gray;
cursor: initial;
}
input[type="submit"]:hover:not(:disabled),
input[type="button"]:hover:not(:disabled),
button:hover:not(:disabled)
{
background-color: #505050;
}
input[type="submit"]:active,
input[type="button"]:active,
button:active
{
background-color: black;
}
input[type="submit"][required],
input[type="button"][required]
{
background-color: red;
}
input[type="submit"][required]:hover,
input[type="button"][required]:hover
{
background-color: rgb(200, 0, 0);
}
input[type="submit"][required]:active,
input[type="button"][required]:active
{
background-color: darkred;
}
input[readonly]
{
background-color: lightgray;
} }
textarea textarea
{ {
resize: none; resize: none;
width: 100%; width: 100%;
border-radius: 10px; border-radius: 5px;
border: 1px solid black; border: 1px solid black;
padding: 10px; padding: 10px;
box-sizing: border-box; box-sizing: border-box;
height: 200px; height: 68vh;
font-family: Consolas; font-family: 'Consolas';
} }
.select-container::after .select-container::after
@@ -60,17 +112,17 @@ textarea
form form
{ {
max-width: 50%; max-width: 500px;
width: 100%;
} }
.admin-menu a .admin-menu > p a
{ {
font-size: 16pt; font-size: 16pt;
} }
.logout, .logout:link, .logout:link:visited .comment.logout
{ {
font-size: 16pt;
color: red !important; color: red !important;
} }
@@ -94,48 +146,34 @@ table
width: 100%; width: 100%;
} }
.form-group input
{
width: auto;
}
.text-danger .text-danger
{ {
text-decoration: solid; text-decoration: solid;
color: red; color: red;
} }
.btn
{
background-color: #343434;
color: white;
}
.btn-danger
{
background-color: red;
color: white;
}
.checkbox .checkbox
{ {
width: auto; width: auto;
} }
.readonly
{
background-color: lightgray;
}
.reorder-arrow:link .reorder-arrow:link
{ {
text-decoration: none; text-decoration: none;
} }
@media only screen and (max-width: 700px) @media only screen and (max-width: 1080px)
{ {
form .hide-l1
{ {
max-width: initial; display: none;
}
}
@media only screen and (max-width: 850px)
{
.hide-l2
{
display: none;
} }
} }
@@ -51,3 +51,11 @@
{ {
color: #d69d85; color: #d69d85;
} }
@media only screen and (max-width: 500px)
{
footer
{
padding: 20px;
}
}
+23
View File
@@ -0,0 +1,23 @@
@font-face
{
font-family: 'Consolas';
src: local("Consolas"), url("/fonts/Consolas/consolas.eot"), url("/fonts/Consolas/consolas.eot?#iefix") format("embedded-opentype"), url("/fonts/Consolas/consolas.otf") format("opentype"), url("/fonts/Consolas/consolas.svg") format("svg"), url("/fonts/Consolas/consolas.ttf") format("truetype"), url("/fonts/Consolas/consolas.woff") format("woff"), url("/fonts/Consolas/consolas.woff2") format("woff2");
}
@font-face
{
font-family: 'SegoeMDL2Assets';
src: local("Segoe MDL2 Assets"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.eot"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.eot?#iefix") format("embedded-opentype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.otf") format("opentype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.svg") format("svg"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.ttf") format("truetype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.woff") format("woff"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.woff2") format("woff2");
}
@font-face
{
font-family: 'Calibri';
src: local("Calibri"), url("/fonts/Calibri/calibri.eot"), url("/fonts/Calibri/calibri.eot?#iefix") format("embedded-opentype"), url("/fonts/Calibri/calibri.otf") format("opentype"), url("/fonts/Calibri/calibri.svg") format("svg"), url("/fonts/Calibri/calibri.ttf") format("truetype"), url("/fonts/Calibri/calibri.woff") format("woff"), url("/fonts/Calibri/calibri.woff2") format("woff2");
}
@font-face
{
font-family: 'SegoeUISymbol';
src: local("Segoe UI Symbol"), url("/fonts/Segoe UI Symbol/segoeUISymbol.eot"), url("/fonts/Segoe UI Symbol/segoeUISymbol.eot?#iefix") format("embedded-opentype"), url("/fonts/Segoe UI Symbol/segoeUISymbol.otf") format("opentype"), url("/fonts/Segoe UI Symbol/segoeUISymbol.svg") format("svg"), url("/fonts/Segoe UI Symbol/segoeUISymbol.ttf") format("truetype"), url("/fonts/Segoe UI Symbol/segoeUISymbol.woff") format("woff"), url("/fonts/Segoe UI Symbol/segoeUISymbol.woff2") format("woff2");
}
+26 -17
View File
@@ -14,6 +14,27 @@
transform: scale(1.1); transform: scale(1.1);
} }
/* Image details page styles */
.image-overview-block img
{
max-height: 50vh;
max-width: 100%;
float: left;
cursor: zoom-in;
}
.image-overview-block > div,
.image-overview-block > form
{
display: inline-block;
margin-left: 20px;
}
.image-overview-block h1
{
margin-bottom: 0px;
}
/* Adaptive code */ /* Adaptive code */
@media only screen and (max-width: 700px) @media only screen and (max-width: 700px)
{ {
@@ -28,22 +49,10 @@
} }
} }
/* Image details page styles */ @media only screen and (max-width: 500px)
.image-overview-block img
{ {
max-height: 50vh; .image-overview-block img
max-width: 100%; {
float: left; max-height: unset;
cursor: zoom-in; }
}
.image-overview-block div
{
display: inline-block;
margin-left: 20px;
}
.image-overview-block h1
{
margin-bottom: 0px;
} }
+2 -1
View File
@@ -4,7 +4,8 @@ header
display: grid; display: grid;
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto;
grid-column-gap: 20px; grid-column-gap: 20px;
margin: 20px 50px; margin-top: 20px;
margin-bottom: 20px;
} }
h1 h1
File diff suppressed because it is too large Load Diff
+59 -44
View File
@@ -2,21 +2,6 @@
This file contains main layout styles which applies to every View. In some views this rules are overrided in separate CSS files This file contains main layout styles which applies to every View. In some views this rules are overrided in separate CSS files
*/ */
/* Declaring fonts */
@font-face
{
font-family: 'Consolas';
src: url("/fonts/Consolas/consolas.eot");
src: url("/fonts/Consolas/consolas.eot?#iefix") format("embedded-opentype"), url("/fonts/Consolas/consolas.otf") format("opentype"), url("/fonts/Consolas/consolas.svg") format("svg"), url("/fonts/Consolas/consolas.ttf") format("truetype"), url("/fonts/Consolas/consolas.woff") format("woff"), url("/fonts/Consolas/consolas.woff2") format("woff2");
}
@font-face
{
font-family: 'Segoe MDL2 Assets';
src: url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.eot");
src: url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.eot?#iefix") format("embedded-opentype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.otf") format("opentype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.svg") format("svg"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.ttf") format("truetype"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.woff") format("woff"), url("/fonts/Segoe MDL2 Assets/segoeMLD2assets.woff2") format("woff2");
}
/* Header styles */ /* Header styles */
nav nav
{ {
@@ -33,6 +18,7 @@ nav
padding: 10px; padding: 10px;
min-height: 33px; min-height: 33px;
font-size: 26px; font-size: 26px;
user-select: none;
} }
nav a nav a
@@ -66,30 +52,6 @@ menu
margin-top: 10px; margin-top: 10px;
} }
/* Adaptive code */
@media only screen and (min-width: 1000px)
{
menu
{
display: initial !important;
grid-row: 1;
grid-column: 2;
margin: 0px;
}
menu li
{
display: inline-block;
margin-right: 10px;
margin-top: 0px;
}
#menu-toggle
{
display: none;
}
}
/* Footer styles */ /* Footer styles */
footer footer
{ {
@@ -123,7 +85,7 @@ body
overflow: auto; overflow: auto;
margin: 0px; margin: 0px;
margin-top: 53px; margin-top: 53px;
font-family: 'Consolas', 'Segoe MDL2 Assets'; font-family: 'Consolas', 'SegoeMDL2Assets';
/* This stuff is necessary for sticky footer */ /* This stuff is necessary for sticky footer */
display: grid; display: grid;
grid-template-rows: 1fr auto; grid-template-rows: 1fr auto;
@@ -136,12 +98,12 @@ header a
color: black; color: black;
} }
header a:hover a:link:hover
{ {
text-decoration: underline; text-decoration: underline;
} }
article, header article
{ {
margin: 0px 50px; margin: 0px 50px;
} }
@@ -151,7 +113,60 @@ article, header
color: blue; color: blue;
} }
header
{
margin: 16px 50px;
}
.comment, .comment:visited .comment, .comment:visited
{ {
color: #57a64a !important; color: #57a64a !important;
text-decoration: none;
}
*[hidden]
{
display: none;
}
/* Adaptive code */
@media only screen and (min-width: 980px)
{
menu
{
display: initial !important;
grid-row: 1;
grid-column: 2;
margin: 0px;
}
menu li
{
display: inline-block;
margin-right: 10px;
margin-top: 0px;
}
#menu-toggle
{
display: none;
}
}
@media only screen and (max-width: 500px)
{
article
{
margin: 0px 20px;
}
header
{
margin: 16px 20px;
}
}
nav > div > a:nth-last-child(2)
{
visibility: hidden;
} }
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 5.8 MiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 6.0 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 315 KiB

@@ -0,0 +1,41 @@
function Up(btn)
{
var item = btn.parentNode.parentNode;
var table = document.querySelector("tbody");
table.insertBefore(item, item.previousElementSibling);
document.querySelector("button").disabled = false;
}
function Down(btn)
{
var item = btn.parentNode.parentNode;
var table = document.querySelector("tbody");
table.insertBefore(item, item.nextElementSibling.nextElementSibling);
document.querySelector("button").disabled = false;
}
function ApplyReorder()
{
var table = document.querySelector("tbody");
var form = document.createElement("form");
form.method = "post";
form.hidden = true;
document.body.appendChild(form);
for (var k = 0; k < table.children.length; k++)
{
var item = document.createElement("input");
item.type = "text";
item.name = "reorderList";
item.value = table.children[k].id;
form.appendChild(item);
}
form.submit();
}