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
@@ -7,38 +7,38 @@ using MyWebsite.Models.Databases;
namespace MyWebsite.Areas.API namespace MyWebsite.Areas.API
{ {
[ApiController] [ApiController]
[Route("API/[controller]")] [Route("API/[controller]")]
public class FoxTubeController : ControllerBase public class FoxTubeController : ControllerBase
{ {
readonly FoxTubeDatabaseContext db; readonly FoxTubeDatabaseContext db;
public FoxTubeController(FoxTubeDatabaseContext context) => public FoxTubeController(FoxTubeDatabaseContext context) =>
db = context; db = context;
[HttpPost] [HttpPost]
[Route("AddMetrics")] [Route("AddMetrics")]
public IActionResult AddMetrics(MetricsPackage package) public IActionResult AddMetrics(MetricsPackage package)
{ {
Guid id = db.Metrics.Add(package).Entity.Id; Guid id = db.Metrics.Add(package).Entity.Id;
db.SaveChanges(); db.SaveChanges();
return Accepted(value: id.ToString()); return Accepted(value: id.ToString());
} }
[HttpGet] [HttpGet]
[Route("Messages")] [Route("Messages")]
public IActionResult Messages(bool toast = false, DateTime? publishedAfter = null, string lang = "en-US") public IActionResult Messages(bool toast = false, DateTime? publishedAfter = null, string lang = "en-US")
{ {
if (toast) if (toast)
{ {
Message message = publishedAfter.HasValue ? Message message = publishedAfter.HasValue ?
db.Messages.FirstOrDefault(i => i.PublishedAt >= publishedAfter && i.PublishedAt <= DateTime.Now && i.Language == lang) : db.Messages.FirstOrDefault(i => i.PublishedAt >= publishedAfter && i.PublishedAt <= DateTime.Now && i.Language == lang) :
db.Messages.OrderByDescending(i => i.PublishedAt).FirstOrDefault(); db.Messages.OrderByDescending(i => i.PublishedAt).FirstOrDefault();
if (message == null) if (message == null)
return NoContent(); return NoContent();
return Ok($@"<toast activationType='foreground' launch='inbox|{message.Id}'> return Ok($@"<toast activationType='foreground' launch='inbox|{message.Id}'>
<visual> <visual>
<binding template='ToastGeneric'> <binding template='ToastGeneric'>
<image placement='hero' src='{message.HeroImage}'/> <image placement='hero' src='{message.HeroImage}'/>
@@ -48,22 +48,22 @@ namespace MyWebsite.Areas.API
</binding> </binding>
</visual> </visual>
</toast>"); </toast>");
} }
else else
return Ok(db.Messages.Where(i => i.PublishedAt <= DateTime.Now).ToList()); return Ok(db.Messages.Where(i => i.PublishedAt <= DateTime.Now).ToList());
} }
[HttpGet] [HttpGet]
[Route("Changelogs")] [Route("Changelogs")]
public IActionResult Changelogs(string version, bool toast = false, string lang = "en-US") public IActionResult Changelogs(string version, bool toast = false, string lang = "en-US")
{ {
if (toast) if (toast)
{ {
Changelog changelog = db.Changelogs.FirstOrDefault(i => i.Version == version && i.Language == lang); Changelog changelog = db.Changelogs.FirstOrDefault(i => i.Version == version && i.Language == lang);
if (changelog == null) if (changelog == null)
return NoContent(); return NoContent();
return Ok($@"<toast activationType='foreground' launch='changelog|{changelog.Id}'> return Ok($@"<toast activationType='foreground' launch='changelog|{changelog.Id}'>
<visual> <visual>
<binding template='ToastGeneric'> <binding template='ToastGeneric'>
<image placement='hero' src='{changelog.HeroImage}'/> <image placement='hero' src='{changelog.HeroImage}'/>
@@ -73,17 +73,17 @@ namespace MyWebsite.Areas.API
</binding> </binding>
</visual> </visual>
</toast>"); </toast>");
} }
else else
return Ok(db.Changelogs.Where(i => !IsVersionGreater(i.Version, version)).ToList()); return Ok(db.Changelogs.Where(i => !IsVersionGreater(i.Version, version)).ToList());
} }
private static bool IsVersionGreater(string versionOne, string versionTwo) private static bool IsVersionGreater(string versionOne, string versionTwo)
{ {
versionOne = versionOne.Replace(".", "", StringComparison.InvariantCulture); versionOne = versionOne.Replace(".", "", StringComparison.InvariantCulture);
versionTwo = versionTwo.Replace(".", "", StringComparison.InvariantCulture); versionTwo = versionTwo.Replace(".", "", StringComparison.InvariantCulture);
return short.Parse(versionOne, NumberStyles.Integer, CultureInfo.InvariantCulture) > short.Parse(versionTwo, NumberStyles.Integer, CultureInfo.InvariantCulture); return short.Parse(versionOne, NumberStyles.Integer, CultureInfo.InvariantCulture) > short.Parse(versionTwo, NumberStyles.Integer, CultureInfo.InvariantCulture);
} }
} }
} }
@@ -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,87 +7,116 @@ 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
{ {
[Area("Admin")] [Area("Admin")]
[Authorize] [Authorize]
public class BadgesController : ExtendedController public class BadgesController : ExtendedController
{ {
public BadgesController(DatabaseContext context) : base(context) { } public BadgesController(DatabaseContext context) : base(context) { }
public IActionResult Index() => [HttpGet]
View(Database.Badges); public IActionResult Index() =>
View(Database.Badges);
[HttpGet] [HttpPost]
public IActionResult Edit(string id) => public IActionResult Index(IFormFile badgeImage)
View(Database.Badges.Find(id)); {
if (badgeImage == null)
throw new ArgumentNullException(nameof(badgeImage));
[HttpPost] System.Drawing.Image image = System.Drawing.Image.FromStream(badgeImage.OpenReadStream());
public IActionResult Edit(BadgeModel model, IFormFile file = null) 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");
if (model == null) else if (image.Width != 64 || image.Height != 64 || !badgeImage.FileName.EndsWith(".PNG", true, CultureInfo.InvariantCulture))
throw new ArgumentNullException(nameof(model)); 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);
if (file != null) return View(Database.Badges);
UploadFile(file, model); }
Database.Badges.Update(model); [HttpGet]
Database.SaveChanges(); public IActionResult Edit(string id) =>
View(Database.Badges.Find(id));
return RedirectToAction("Index"); [HttpPost]
} public IActionResult Edit(BadgeModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
[HttpGet] if (!ModelState.IsValid)
public IActionResult Delete(string id) => {
View(Database.Badges.Find(id)); ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
[HttpPost] Database.Badges.Update(model);
public IActionResult Delete(BadgeModel model) Database.SaveChanges();
{
Database.Badges.Remove(model);
Database.SaveChanges();
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
[HttpGet] [HttpGet]
public IActionResult Create() => public IActionResult Delete(string id) =>
View(); View(Database.Badges.Find(id));
[HttpPost] [HttpPost]
public IActionResult Create(BadgeModel model, IFormFile file = null) public IActionResult Delete(BadgeModel model)
{ {
if (model == null) if (Database.Projects.ToList().Any(i => i.Badges.Contains(model.Name, StringComparison.InvariantCulture)))
throw new ArgumentNullException(nameof(model)); {
ModelState.Clear();
ModelState.AddModelError("Error", "Remove badge references from projects descriptions in order to delete the badge");
return View(Database.Badges.Find(model?.Name));
}
if (file != null) Database.Badges.Remove(model);
return UploadFile(file, model); Database.SaveChanges();
if (!ModelState.IsValid) return RedirectToAction("Index");
{ }
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
Database.Badges.Add(model); [HttpGet]
Database.SaveChanges(); public IActionResult Create() =>
View(model: null);
return RedirectToAction("Index"); [HttpPost]
} public IActionResult Create(BadgeModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
private IActionResult UploadFile(IFormFile file, BadgeModel model) if (!ModelState.IsValid)
{ {
System.Drawing.Image image = System.Drawing.Image.FromStream(file.OpenReadStream()); ModelState.AddModelError("Error", "Invalid data");
if (image.Width != 64 || image.Height != 64 || !file.FileName.EndsWith(".PNG", true, CultureInfo.InvariantCulture)) return View(model);
{ }
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); 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.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult DeleteBadgeImage(string id)
{
string path = Directory.GetCurrentDirectory() + "/wwwroot/images/Badges/" + id;
if (System.IO.File.Exists(path))
System.IO.File.Delete(path);
return RedirectToAction("Index");
}
}
} }
@@ -3,61 +3,99 @@ 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
{ {
[Area("Admin")] [Area("Admin")]
[Authorize] [Authorize]
public class ContactsController : ExtendedController public class ContactsController : ExtendedController
{ {
public ContactsController(DatabaseContext context) : base(context) { } public ContactsController(DatabaseContext context) : base(context) { }
public IActionResult Index() => public IActionResult Index() =>
View(Database.Links); View(Database.Links);
[HttpGet] [HttpPost]
public IActionResult Edit(string id) => public IActionResult Index(string[] reorderList)
View(Database.Links.Find(id)); {
if(reorderList?.Length != Database.Links.Count())
{
ModelState.AddModelError("Error", "Invalid or incomplete data recieved");
return View(Database.Links);
}
[HttpPost] for (int i = 0; i < reorderList.Length; i++)
public IActionResult Edit(LinkModel model) Database.Links.Find(reorderList[i]).Order = i;
{
Database.Links.Update(model); Database.SaveChanges();
Database.SaveChanges();
return RedirectToAction("Index"); return View(Database.Links);
} }
[HttpGet] [HttpGet]
public IActionResult Delete(string id) => public IActionResult Edit(string id) =>
View(Database.Links.Find(id)); View(Database.Links.Find(id));
[HttpPost] [HttpPost]
public IActionResult Delete(LinkModel model) public IActionResult Edit(LinkModel model)
{ {
Database.Links.Remove(model); if (model == null)
Database.SaveChanges(); throw new ArgumentNullException(nameof(model));
return RedirectToAction("Index"); if (!ModelState.IsValid)
} {
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
[HttpGet] Database.Links.Update(model);
public IActionResult Create() => Database.SaveChanges();
View();
[HttpPost] return RedirectToAction("Index");
public IActionResult Create(LinkModel model) }
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
Database.Links.Add(model); [HttpGet]
Database.SaveChanges(); public IActionResult Delete(string id) =>
View(Database.Links.Find(id));
return RedirectToAction("Index"); [HttpPost]
} public IActionResult Delete(LinkModel model)
} {
Database.Links.Remove(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Create() =>
View(model: null);
[HttpPost]
public IActionResult Create(LinkModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
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.SaveChanges();
return RedirectToAction("Index");
}
}
} }
@@ -2,51 +2,68 @@
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;
namespace MyWebsite.Areas.Admin.Controllers namespace MyWebsite.Areas.Admin.Controllers
{ {
[Area("Admin")] [Area("Admin")]
[Authorize] [Authorize]
public class CredentialController : ExtendedController public class CredentialController : ExtendedController
{ {
const string viewPath = "Areas/Admin/Views/Shared/Credential.cshtml"; const string viewPath = "Areas/Admin/Views/Shared/Credential.cshtml";
public CredentialController(DatabaseContext context) : base(context) { } public CredentialController(DatabaseContext context) : base(context) { }
[HttpGet] [HttpGet]
public IActionResult Index() => public IActionResult Index() =>
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))
{
ModelState.AddModelError("Validation error", "Unable to identify data to update");
return View(viewPath);
}
if (model == null || model.Current.Email != credential.Email || !Encryptor.VerifyHash(model.Current.Password, credential.Password)) if(string.IsNullOrWhiteSpace(value))
{ {
ModelState.AddModelError("Authorization error", "Invaild e-mail or password"); ModelState.AddModelError("Validation error", "No data provided");
return View(viewPath, model); return View(viewPath);
} }
if(string.IsNullOrWhiteSpace(model.Updated.Email) && string.IsNullOrWhiteSpace(model.Current.Password)) CredentialModel credential = Database.Users.FirstOrDefault();
{ Database.Users.RemoveRange(Database.Users);
ModelState.AddModelError("Validation error", "No data to update");
return View(viewPath, model);
}
Database.Users.Remove(credential); switch (key)
Database.SaveChanges(); {
if(!string.IsNullOrWhiteSpace(model.Current.Email)) case "password":
credential.Email = model.Updated.Email; Database.Users.Add(new CredentialModel
if(!string.IsNullOrWhiteSpace(model.Current.Password)) {
credential.Password = Encryptor.ComputeHash(model.Updated.Password); Email = credential.Email,
Database.Users.Add(credential); Password = Encryptor.ComputeHash(value)
});
break;
Database.SaveChanges(); case "email":
Database.Users.Add(new CredentialModel
{
Email = value,
Password = credential.Password
});
break;
return Redirect("~/Admin/Logout"); default:
} ModelState.AddModelError("Processing error", "Provided data is missing or read-only");
} return View(viewPath);
}
Database.SaveChanges();
return Redirect("~/Admin/Logout");
}
}
} }
@@ -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,38 +4,130 @@ 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
{ {
[Authorize] [Authorize]
[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;
[HttpGet] public GUTScheduleController(DatabaseContext context, GUTScheduleDatabaseContext databaseContext) : base(context) =>
public IActionResult Index() => db = databaseContext;
View(viewPath, Database.CustomData.Find(scheduleOffsetId) ?? new CustomData { Key = scheduleOffsetId, Value = "undefined" });
[HttpPost] [HttpGet]
public IActionResult Index(CustomData model) public IActionResult Index()
{ {
if(!ModelState.IsValid) ViewData["Policies"] = db.PrivacyPolicies;
{ return View(viewPath, db.OffsetDates.FirstOrDefault());
ModelState.AddModelError("Error", "Invalid data"); }
return View(model);
}
if(Database.CustomData.Any(i => i.Key == scheduleOffsetId))
Database.CustomData.Update(model);
else
Database.CustomData.Add(model);
Database.SaveChanges(); [HttpPost]
return View(viewPath, model); public IActionResult Index(CustomData model)
} {
} if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
db.OffsetDates.RemoveRange(db.OffsetDates);
db.OffsetDates.Add(model);
db.SaveChanges();
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;
@@ -9,90 +9,77 @@ using System.IO;
namespace MyWebsite.Areas.Admin.Controllers namespace MyWebsite.Areas.Admin.Controllers
{ {
[Area("Admin")] [Area("Admin")]
[Authorize] [Authorize]
public class GalleryController : ExtendedController public class GalleryController : ExtendedController
{ {
public GalleryController(DatabaseContext context) : base(context) { } public GalleryController(DatabaseContext context) : base(context) { }
public IActionResult Index() => public IActionResult Index() =>
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)
{ {
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
ModelState.AddModelError("Error", "Invalid data"); ModelState.AddModelError("Error", "Invalid data");
return View(model); return View(model);
} }
Database.Gallery.Update(model); Database.Gallery.Update(model);
Database.SaveChanges(); Database.SaveChanges();
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
[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)
{ {
Database.Gallery.Remove(model); Database.Gallery.Remove(model);
Database.SaveChanges(); Database.SaveChanges();
System.IO.File.SetAttributes(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.FileName, FileAttributes.Normal); System.IO.File.SetAttributes(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.FileName, FileAttributes.Normal);
System.IO.File.Delete(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.FileName); System.IO.File.Delete(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.FileName);
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
[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 (!ModelState.IsValid) if (model == null)
{ throw new ArgumentNullException(nameof(model));
ModelState.AddModelError("Error", "Invalid data"); if (file == null)
return View(model); throw new ArgumentNullException(nameof(file));
}
using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.File.FileName)) model.FileName = file.FileName;
model.File.CopyTo(stream);
ImageModel image = new ImageModel if (!ModelState.IsValid)
{ {
EnglishTitle = model.EnglishTitle, ModelState.AddModelError("Error", "Invalid data");
RussianTitle = model.RussianTitle, return View(model);
EnglishDescription = model.EnglishDescription, }
RussianDescription = model.RussianDescription,
CreationDate = model.CreationDate,
FileName = model.File.FileName
};
Database.Gallery.Add(image); using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + file.FileName))
Database.SaveChanges(); file.CopyTo(stream);
return RedirectToAction("Index"); Database.Gallery.Add(model);
} Database.SaveChanges();
}
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;
@@ -7,57 +9,98 @@ using MyWebsite.Models.Databases;
namespace MyWebsite.Areas.Admin.Controllers namespace MyWebsite.Areas.Admin.Controllers
{ {
[Area("Admin")] [Area("Admin")]
[Authorize] [Authorize]
public class ProjectsController : ExtendedController public class ProjectsController : ExtendedController
{ {
public ProjectsController(DatabaseContext context) : base(context) { } public ProjectsController(DatabaseContext context) : base(context) { }
public IActionResult Index() => [HttpGet]
View((Database.Projects as IEnumerable<ProjectModel>, Database.Badges as IEnumerable<BadgeModel>)); public IActionResult Index() =>
View((Database.Projects as IEnumerable<ProjectModel>, Database.Badges as IEnumerable<BadgeModel>));
[HttpGet] [HttpPost]
public IActionResult Delete(decimal id) => public IActionResult Index(Guid[] reorderList)
View(Database.Projects.Find(id)); {
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;
[HttpPost] Database.SaveChanges();
public IActionResult Delete(ProjectModel model) }
{
Database.Projects.Remove(model);
Database.SaveChanges();
return RedirectToAction("Index"); return View((Database.Projects as IEnumerable<ProjectModel>, Database.Badges as IEnumerable<BadgeModel>));
} }
[HttpGet] [HttpGet]
public IActionResult Create() => public IActionResult Delete(Guid id)
View(); {
ViewData["Badges"] = Database.Badges.ToList();
return View(Database.Projects.Find(id));
}
[HttpPost] [HttpPost]
public IActionResult Create(ProjectModel model) public IActionResult Delete(ProjectModel model)
{ {
if (!ModelState.IsValid) Database.Projects.Remove(model);
{ Database.SaveChanges();
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
Database.Projects.Add(model); return RedirectToAction("Index");
Database.SaveChanges(); }
return RedirectToAction("Index"); [HttpGet]
} public IActionResult Edit(Guid id)
} {
ViewData["Badges"] = Database.Badges.ToList();
return View(Database.Projects.Find(id));
}
public class ProjectEditorModel [HttpPost]
{ public IActionResult Edit(ProjectModel model)
public double Id { get; set; } {
public string EnglishTitle { get; set; } if (model == null)
public string RussianTitle { get; set; } throw new ArgumentNullException(nameof(model));
public string EnglishDescription { get; set; }
public string RussianDescription { get; set; } if (!ModelState.IsValid)
public string Link { get; set; } {
public string EnglishLinkCaption { get; set; } ModelState.AddModelError("Error", "Invalid data");
public string RussianLinkCaption { get; set; } 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]
public IActionResult Create(ProjectModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
model.Order = Database.Projects.Count();
Database.Projects.Add(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
}
} }
@@ -4,77 +4,84 @@ 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
{ {
[Authorize] [Authorize]
[Area("Admin")] [Area("Admin")]
public class ResumeController : ExtendedController public class ResumeController : ExtendedController
{ {
public ResumeController(DatabaseContext context) : base(context) { } public ResumeController(DatabaseContext context) : base(context) { }
public IActionResult Index() => public IActionResult Index() =>
View(Database.Resume); View(Database.Resume);
[HttpGet] [HttpGet]
public IActionResult Edit(string id) => public IActionResult Edit(string id) =>
View(Database.Resume.Find(id)); View(Database.Resume.Find(id));
[HttpPost] [HttpPost]
public IActionResult Edit(ResumeModel model) public IActionResult Edit(ResumeModel model)
{ {
if (model == null) if (model == null)
throw new ArgumentNullException(nameof(model)); 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.LastUpdate = DateTime.Now; model.LastUpdate = DateTime.Now;
Database.Resume.Update(model); Database.Resume.Update(model);
Database.SaveChanges(); Database.SaveChanges();
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
[HttpGet] [HttpGet]
public IActionResult Delete(string id) => public IActionResult Delete(string id) =>
View(Database.Resume.Find(id)); View(Database.Resume.Find(id));
[HttpPost] [HttpPost]
public IActionResult Delete(ResumeModel model) public IActionResult Delete(ResumeModel model)
{ {
Database.Resume.Remove(model); Database.Resume.Remove(model);
Database.SaveChanges(); Database.SaveChanges();
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
[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)
{ {
if (model == null) if (model == null)
throw new ArgumentNullException(nameof(model)); 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.LastUpdate = DateTime.Now; model.LastUpdate = DateTime.Now;
Database.Resume.Add(model); if(Database.Resume.Any(i => i.Language == model.Language))
Database.SaveChanges(); {
ModelState.AddModelError("Error", $"Resume with this language ({model.Language}) is already exists");
return View(model);
}
return RedirectToAction("Index"); Database.Resume.Add(model);
} Database.SaveChanges();
}
return RedirectToAction("Index");
}
}
} }
@@ -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,57 +1,47 @@
@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>
<label asp-for="RussianDescription"></label> <label asp-for="RussianDescription"></label>
<input asp-for="RussianDescription" type="text" /> <input asp-for="RussianDescription" type="text" />
<span asp-validation-for="RussianDescription" class="text-danger"></span> <span asp-validation-for="RussianDescription" class="text-danger"></span>
</div> </div>
<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>
<span>.png</span> @foreach (string path in System.IO.Directory.GetFiles(System.IO.Directory.GetCurrentDirectory() + "/wwwroot/images/Badges"))
</div> {
<span asp-validation-for="Image" class="text-danger"></span> string file = System.IO.Path.GetFileNameWithoutExtension(path);
</div> <option value="@file">@file</option>
<partial name="Preview.cshtml" /> }
</select>
<span>.png</span>
</div>
<span asp-validation-for="Image" class="text-danger"></span>
</div>
<partial name="Preview.cshtml" />
<input type="submit" value="Create" class="btn" /> <input type="submit" value="Create" />
</form> </form>
<hr /> </article>
<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>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -1,37 +1,41 @@
@model MyWebsite.Models.BadgeModel @model MyWebsite.Models.BadgeModel
@{ @{
ViewData["Title"] = "Delete badge"; ViewData["Title"] = "Delete badge";
} }
<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>
<p> <div asp-validation-summary="All" class="text-danger"></div>
<b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br /> <p>
<b>@Html.DisplayNameFor(model => model.EnglishDescription):</b> @Model.EnglishDescription<br /> <b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br />
<b>@Html.DisplayNameFor(model => model.RussianDescription):</b> @Model.RussianDescription<br /> <b>@Html.DisplayNameFor(model => model.EnglishDescription):</b> @Model.EnglishDescription<br />
<b>@Html.DisplayNameFor(model => model.Image):</b> @(Model.Image).png<br /> <b>@Html.DisplayNameFor(model => model.RussianDescription):</b> @Model.RussianDescription<br />
<b>@Html.DisplayNameFor(model => model.Image):</b> @(Model.Image).png<br />
<b>Preview:</b> <b>Preview:</b>
<div class="badge" style="background-image: url('/images/Badges/@(Model.Image).png')" title="@(Model.Description)"></div> <div class="badge" style="background-image: url('/images/Badges/@(Model.Image).png')" title="@(Model.Description)"></div>
</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" /> @section Imports
<style type="text/css"> {
.badge { <style type="text/css">
height: 50px; .badge
width: 50px; {
background-size: contain; height: 50px;
} width: 50px;
</style> background-size: contain;
}
</style>
}
@@ -1,57 +1,47 @@
@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>
<label asp-for="RussianDescription"></label> <label asp-for="RussianDescription"></label>
<input asp-for="RussianDescription" type="text" /> <input asp-for="RussianDescription" type="text" />
<span asp-validation-for="RussianDescription" class="text-danger"></span> <span asp-validation-for="RussianDescription" class="text-danger"></span>
</div> </div>
<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>
<span>.png</span> @foreach (string path in System.IO.Directory.GetFiles(System.IO.Directory.GetCurrentDirectory() + "/wwwroot/images/Badges"))
</div> {
<span asp-validation-for="Image" class="text-danger"></span> string file = System.IO.Path.GetFileNameWithoutExtension(path);
</div> <option value="@file">@file</option>
<partial name="Preview.cshtml" /> }
</select>
<span>.png</span>
</div>
<span asp-validation-for="Image" class="text-danger"></span>
</div>
<partial name="Preview.cshtml" />
<input type="submit" value="Save" class="btn" /> <input type="submit" value="Update" />
</form> </form>
<hr /> </article>
<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>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -1,62 +1,159 @@
@model IEnumerable<MyWebsite.Models.BadgeModel> @model IEnumerable<MyWebsite.Models.BadgeModel>
@{ @{
ViewData["Title"] = "Badges list"; ViewData["Title"] = "Badges list";
} }
<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>
@foreach (var item in Model) @foreach (var item in Model)
{ {
<tr> <tr>
<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>
</td> </td>
</tr> </tr>
} }
</tbody> </tbody>
</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>
height: 25px; </header>
width: 25px;
background-size: contain; <article>
} <h2>Upload new badge image</h2>
</style> <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;
width: 25px;
background-size: contain;
}
.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,55 +1,52 @@
@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>
<div> <input asp-for="Order" type="number" hidden value="-1" />
<label asp-for="Name"></label> <div>
<input asp-for="Name" type="text" /> <label asp-for="Name"></label>
<span asp-validation-for="Name" class="text-danger"></span> <a target="_blank" class="comment" href="//socicon.com/icons">// Socicon naming cheatsheet</a>
</div> <input asp-for="Name" type="text" required />
<div> <span asp-validation-for="Name" class="text-danger"></span>
<label asp-for="Order"></label> </div>
<input asp-for="Order" type="number" /> <div>
<span asp-validation-for="Order" class="text-danger"></span> <label asp-for="EnglishTitle"></label>
</div> <input asp-for="EnglishTitle" type="text" required />
<div> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
<label asp-for="EnglishTitle"></label> </div>
<input asp-for="EnglishTitle" type="text" /> <div>
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <label asp-for="RussianTitle"></label>
</div> <input asp-for="RussianTitle" type="text" />
<div> <span asp-validation-for="RussianTitle" class="text-danger"></span>
<label asp-for="RussianTitle"></label> </div>
<input asp-for="RussianTitle" type="text" /> <div>
<span asp-validation-for="RussianTitle" class="text-danger"></span> <label asp-for="Username"></label>
</div> <input asp-for="Username" type="text" required />
<div> <span asp-validation-for="Username" class="text-danger"></span>
<label asp-for="Username"></label> </div>
<input asp-for="Username" type="text" /> <div>
<span asp-validation-for="Username" class="text-danger"></span> <label asp-for="Url"></label>
</div> <input asp-for="Url" type="text" required />
<div> <span asp-validation-for="Url" class="text-danger"></span>
<label asp-for="Url"></label> </div>
<input asp-for="Url" type="text" /> <div>
<span asp-validation-for="Url" class="text-danger"></span> <input type="checkbox" class="checkbox" asp-for="CanContactMe" />
</div> <label asp-for="CanContactMe"></label> <br />
</div>
<div>
<input type="checkbox" class="checkbox" asp-for="DisplayInFooter" />
<label asp-for="DisplayInFooter"></label>
</div>
<label asp-for="CanContactMe"></label> <input type="submit" value="Create" />
<input type="checkbox" class="checkbox" asp-for="CanContactMe" /> <br /> </form>
<label asp-for="DisplayInFooter"></label> </article>
<input type="checkbox" class="checkbox" asp-for="DisplayInFooter" />
<input type="submit" value="Create" class="btn" />
</form>
</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" />
@@ -1,53 +1,50 @@
@model MyWebsite.Models.LinkModel @model MyWebsite.Models.LinkModel
@{ @{
ViewData["Title"] = "Edit link"; ViewData["Title"] = "Edit 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>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>
<div> <input asp-for="Order" type="number" hidden />
<label asp-for="Name"></label> <div>
<input asp-for="Name" type="text" class="readonly" readonly /> <label asp-for="Name"></label>
</div> <input asp-for="Name" type="text" readonly />
<div> </div>
<label asp-for="Order"></label> <div>
<input asp-for="Order" type="number" /> <label asp-for="EnglishTitle"></label>
<span asp-validation-for="Order" class="text-danger"></span> <input asp-for="EnglishTitle" type="text" required />
</div> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
<div> </div>
<label asp-for="EnglishTitle"></label> <div>
<input asp-for="EnglishTitle" type="text" /> <label asp-for="RussianTitle"></label>
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <input asp-for="RussianTitle" type="text" />
</div> <span asp-validation-for="RussianTitle" class="text-danger"></span>
<div> </div>
<label asp-for="RussianTitle"></label> <div>
<input asp-for="RussianTitle" type="text" /> <label asp-for="Username"></label>
<span asp-validation-for="RussianTitle" class="text-danger"></span> <input asp-for="Username" type="text" required />
</div> <span asp-validation-for="Username" class="text-danger"></span>
<div> </div>
<label asp-for="Username"></label> <div>
<input asp-for="Username" type="text" /> <label asp-for="Url"></label>
<span asp-validation-for="Username" class="text-danger"></span> <input asp-for="Url" type="text" required />
</div> <span asp-validation-for="Url" class="text-danger"></span>
<div> </div>
<label asp-for="Url"></label> <div>
<input asp-for="Url" type="text" /> <input type="checkbox" class="checkbox" asp-for="CanContactMe" />
<span asp-validation-for="Url" class="text-danger"></span> <label asp-for="CanContactMe"></label> <br />
</div> </div>
<div>
<input type="checkbox" class="checkbox" asp-for="DisplayInFooter" />
<label asp-for="DisplayInFooter"></label>
</div>
<label asp-for="CanContactMe"></label> <input type="submit" value="Save" />
<input type="checkbox" class="checkbox" asp-for="CanContactMe" /> <br /> </form>
<label asp-for="DisplayInFooter"></label> </article>
<input type="checkbox" class="checkbox" asp-for="DisplayInFooter" />
<input type="submit" value="Save" class="btn" />
</form>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -1,75 +1,90 @@
@model IEnumerable<MyWebsite.Models.LinkModel> @model IEnumerable<MyWebsite.Models.LinkModel>
@{ @{
ViewData["Title"] = "Links list"; ViewData["Title"] = "Links list";
} }
<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>
<table> <div asp-validation-summary="All" class="text-danger"></div>
<thead> <table>
<tr> <thead>
<th>Reorder</th> <tr>
<th> <th>&#xE700;</th>
@Html.DisplayNameFor(model => model.Name) <th>
</th> @Html.DisplayNameFor(model => model.Name)
<th> </th>
@Html.DisplayNameFor(model => model.EnglishTitle) <th class="hide-l2">
</th> @Html.DisplayNameFor(model => model.Title)
<th> </th>
@Html.DisplayNameFor(model => model.RussianTitle) <th class="hide-l1">
</th> @Html.DisplayNameFor(model => model.Username)
<th> </th>
@Html.DisplayNameFor(model => model.Username) <th class="hide-l2">
</th> @Html.DisplayNameFor(model => model.Url)
<th> </th>
@Html.DisplayNameFor(model => model.Url) <th class="hide-l1">
</th> @Html.DisplayNameFor(model => model.CanContactMe)
<th> </th>
@Html.DisplayNameFor(model => model.CanContactMe) <th class="hide-l1">
</th> @Html.DisplayNameFor(model => model.DisplayInFooter)
<th> </th>
@Html.DisplayNameFor(model => model.DisplayInFooter) <th>Actions</th>
</th> </tr>
<th></th> </thead>
</tr> <tbody>
</thead> @foreach (var item in Model.OrderBy(i => i.Order))
<tbody> {
@foreach (var item in Model.OrderBy(i => i.Order)) <tr id="@item.Name">
{ <td class="reorderingBtns">
<tr draggable="true" ondragover="onDragOver(event);"> <a onclick="Up(this)">&#xE010;</a><br />
<td>&#xE700;</td> <span>&#xE915;</span><br />
<td>@item.Name</td> <a onclick="Down(this)">&#xE011;</a>
<td>@item.EnglishTitle</td> </td>
<td>@item.RussianTitle</td> <td>@item.Name</td>
<td>@item.Username</td> <td class="hide-l2">
<td>@item.Url</td> @item.EnglishTitle (en)<br />
<td> @(item.RussianTitle ?? "<not_set>") (ru)
@Html.DisplayFor(modelItem => item.CanContactMe) </td>
</td> <td class="hide-l1">@item.Username</td>
<td> <td class="hide-l2"><a target="_blank" href="@item.Url">@item.Url</a></td>
@Html.DisplayFor(modelItem => item.DisplayInFooter) <td class="hide-l1">
</td> @Html.DisplayFor(modelItem => item.CanContactMe)
<td> </td>
<a asp-action="Edit" asp-route-id="@item.Name">Edit</a> | <td class="hide-l1">
<a asp-action="Delete" asp-route-id="@item.Name">Delete</a> @Html.DisplayFor(modelItem => item.DisplayInFooter)
</td> </td>
</tr> <td>
} <a asp-action="Edit" asp-route-id="@item.Name">Edit</a> |
</tbody> <a asp-action="Delete" asp-route-id="@item.Name">Delete</a>
</table> </td>
</tr>
}
</tbody>
</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>
@@ -1,34 +1,61 @@
@model MyWebsite.Models.ImageModel @model MyWebsite.Models.ImageModel
@{ @{
ViewData["Title"] = "Delete artwork"; ViewData["Title"] = "Delete 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>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>
<b>@Html.DisplayNameFor(model => model.Title):</b> @Model.Title<br /> <b>@Html.DisplayNameFor(model => model.Title):</b> @Model.Title<br />
<b>@Html.DisplayNameFor(model => model.CreationDate):</b> @Model.CreationDate<br /> <b>@Html.DisplayNameFor(model => model.CreationDate):</b> @Model.CreationDate<br />
<b>@Html.DisplayNameFor(model => model.FileName):</b> @Model.FileName<br /> <b>@Html.DisplayNameFor(model => model.FileName):</b> @Model.FileName<br />
</p> </p>
<p> <p>
<b>@Html.DisplayNameFor(model => model.Description):</b> @Html.Raw(Model.Description)<br /> <b>@Html.DisplayNameFor(model => model.Description):</b> @Html.Raw(Model.Description)<br />
</p> </p>
<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>
}
@@ -1,48 +1,97 @@
@model MyWebsite.Models.ImageModel @model MyWebsite.Models.ImageModel
@{ @{
ViewData["Title"] = "Edit artwork"; ViewData["Title"] = "Edit artwork";
} }
<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 file</a>
<a class="comment" href="~/images/Gallery/@(Model.FileName)" target="_blank">// Open artwork</a>
</p>
</header> </header>
<article> <article>
<form asp-action="Edit"> <img src="~/images/Gallery/@Model.FileName" onclick="ToggleImageSize();" />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input asp-for="FileName" hidden/>
<div>
<label asp-for="EnglishTitle"></label>
<input asp-for="EnglishTitle" type="text" />
<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"></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="CreationDate"></label>
<input asp-for="CreationDate" type="date" />
<span asp-validation-for="CreationDate" class="text-danger"></span>
</div>
<input type="submit" value="Save" class="btn" /> <form asp-action="Edit">
</form> <div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="FileName" hidden />
<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"></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="CreationDate"></label>
<input asp-for="CreationDate" type="date" required/>
<span asp-validation-for="CreationDate" class="text-danger"></span>
</div>
<input type="submit" value="Save" />
</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>
}
@@ -1,47 +1,53 @@
@model IEnumerable<MyWebsite.Models.ImageModel> @model IEnumerable<MyWebsite.Models.ImageModel>
@{ @{
ViewData["Title"] = "Gallery"; ViewData["Title"] = "Gallery";
} }
<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>
<table> <table>
@foreach (ImageModel item in Model) @foreach (ImageModel item in Model)
{ {
<tr> <tr>
<td> <td>
<img title="@item.Title" src="~/images/Gallery/@item.FileName" /> <a asp-area="" asp-controller="Gallery" asp-action="Details" asp-route-id="@item.FileName" target="_blank">
</td> <img title="@item.Title" src="~/images/Gallery/@item.FileName" />
<td> </a>
<p> </td>
<h3>@item.Title</h3> <td>
<span>File name: @item.FileName</span><br /> <p>
<span>Creation date: @item.CreationDate.ToShortDateString()</span><br /> <h3>@item.Title</h3>
<span> <span>File name: @item.FileName</span><br />
@Html.ActionLink("Edit", "Edit", new { id = item.FileName }) | <span>Creation date: @item.CreationDate.ToShortDateString()</span><br />
@Html.ActionLink("Delete", "Delete", new { id = item.FileName }) <span>
</span> <a asp-action="Edit" asp-route-id="@item.FileName">Edit</a> |
</p> <a asp-action="Delete" asp-route-id="@item.FileName">Delete</a> |
</td> <a asp-area="" asp-controller="Gallery" asp-action="Details" asp-route-id="@item.FileName" target="_blank">View</a>
</tr> </span>
} </p>
</table> </td>
</tr>
}
</table>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<style type="text/css"> {
img { <style type="text/css">
height: 200px; img
margin-right: 20px; {
} height: 200px;
table { margin-right: 20px;
width: initial; }
}
</style> table
{
width: initial;
}
</style>
}
@@ -1,49 +1,61 @@
@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>
<div> <label for="file">Artwork file</label>
<label asp-for="EnglishTitle"></label> <input type="file" accept="image" name="File" required />
<input asp-for="EnglishTitle" type="text" /> </div>
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <div asp-validation-summary="All" class="text-danger"></div>
</div> <div>
<div> <label asp-for="EnglishTitle"></label>
<label asp-for="RussianTitle"></label> <input asp-for="EnglishTitle" type="text" required />
<input asp-for="RussianTitle" type="text" /> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
<span asp-validation-for="RussianTitle" class="text-danger"></span> </div>
</div> <div>
<div> <label asp-for="RussianTitle"></label>
<label asp-for="EnglishDescription"></label> <input asp-for="RussianTitle" type="text" />
<textarea asp-for="EnglishDescription"></textarea> <span asp-validation-for="RussianTitle" class="text-danger"></span>
<span asp-validation-for="EnglishDescription" class="text-danger"></span> </div>
</div> <div>
<div> <label asp-for="EnglishDescription"></label>
<label asp-for="RussianDescription"></label> <textarea asp-for="EnglishDescription"></textarea>
<textarea asp-for="RussianDescription"></textarea> <span asp-validation-for="EnglishDescription" class="text-danger"></span>
<span asp-validation-for="RussianDescription" class="text-danger"></span> </div>
</div> <div>
<div> <label asp-for="RussianDescription"></label>
<label asp-for="CreationDate"></label> <textarea asp-for="RussianDescription"></textarea>
<input asp-for="CreationDate" type="date" /> <span asp-validation-for="RussianDescription" class="text-danger"></span>
<span asp-validation-for="CreationDate" class="text-danger"></span> </div>
</div> <div>
<div> <label asp-for="CreationDate"></label>
<label asp-for="File"></label> <input asp-for="CreationDate" type="date" required />
<input type="file" accept="image" asp-for="File" /> <span asp-validation-for="CreationDate" class="text-danger"></span>
<span asp-validation-for="File" class="text-danger"></span> </div>
</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>
}
@@ -1,64 +1,107 @@
@model MyWebsite.Models.ProjectModel @model MyWebsite.Models.ProjectModel
@{ @{
ViewData["Title"] = "New Project"; ViewData["Title"] = "New Project";
} }
<header> <header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p> <p>&#xE760; <a asp-action="Index">Back to the list</a></p>
<h1>Create new project</h1> <h1>Create new project</h1>
</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> <div>
<input asp-for="Id" type="number"/> <label asp-for="EnglishTitle"></label>
<span asp-validation-for="Id" class="text-danger"></span> <input asp-for="EnglishTitle" type="text" required />
</div> <span asp-validation-for="EnglishTitle" class="text-danger"></span>
<div> </div>
<label asp-for="EnglishTitle"></label> <div>
<input asp-for="EnglishTitle" type="text"/> <label asp-for="RussianTitle"></label>
<span asp-validation-for="EnglishTitle" class="text-danger"></span> <input asp-for="RussianTitle" type="text" />
</div> <span asp-validation-for="RussianTitle" class="text-danger"></span>
<div> </div>
<label asp-for="RussianTitle"></label> <div>
<input asp-for="RussianTitle" type="text"/> <label asp-for="EnglishDescription"></label>
<span asp-validation-for="RussianTitle" class="text-danger"></span> <textarea asp-for="EnglishDescription" required></textarea>
</div> <span asp-validation-for="EnglishDescription" class="text-danger"></span>
<div> </div>
<label asp-for="EnglishDescription"></label> <div>
<textarea asp-for="EnglishDescription"></textarea> <label asp-for="RussianDescription"></label>
<span asp-validation-for="EnglishDescription" class="text-danger"></span> <textarea asp-for="RussianDescription"></textarea>
</div> <span asp-validation-for="RussianDescription" class="text-danger"></span>
<div> </div>
<label asp-for="RussianDescription"></label> <div>
<textarea asp-for="RussianDescription"></textarea> <label asp-for="Link"></label>
<span asp-validation-for="RussianDescription" class="text-danger"></span> <input asp-for="Link" type="text" />
</div> <span asp-validation-for="Link" class="text-danger"></span>
<div> </div>
<label asp-for="Link"></label> <div>
<input asp-for="Link" type="url"/> <label asp-for="EnglishLinkCaption"></label>
<span asp-validation-for="Link" class="text-danger"></span> <input asp-for="EnglishLinkCaption" type="text" />
</div> <span asp-validation-for="EnglishLinkCaption" class="text-danger"></span>
<div> </div>
<label asp-for="EnglishLinkCaption"></label> <div>
<input asp-for="EnglishLinkCaption" type="text"/> <label asp-for="RussianLinkCaption"></label>
<span asp-validation-for="EnglishLinkCaption" class="text-danger"></span> <input asp-for="RussianLinkCaption" type="text" />
</div> <span asp-validation-for="RussianLinkCaption" class="text-danger"></span>
<div> </div>
<label asp-for="RussianLinkCaption"></label> <div>
<input asp-for="RussianLinkCaption" type="text"/> <label asp-for="Badges"></label>
<span asp-validation-for="RussianLinkCaption" class="text-danger"></span> <input asp-for="Badges" type="text" id="badges" hidden />
</div> <span asp-validation-for="Badges" class="text-danger"></span>
<div> <div class="badge-list">
<label asp-for="Badges"></label> @foreach (BadgeModel badge in ViewData["Badges"] as List<BadgeModel>)
<input asp-for="Badges" type="text" /> {
<span asp-validation-for="Badges" class="text-danger"></span> <input type="checkbox" id="@badge.Name"/>
</div> <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="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>
}
@@ -1,31 +1,63 @@
@model MyWebsite.Models.ProjectModel @model MyWebsite.Models.ProjectModel
@{ @{
ViewData["Title"] = "Delete project"; ViewData["Title"] = "Delete project";
} }
<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 />
<b>@Html.DisplayNameFor(model => model.EnglishTitle):</b> @Model.EnglishTitle<br /> </p>
<b>@Html.DisplayNameFor(model => model.RussianTitle):</b> @Model.RussianTitle<br /> <p>
<b>@Html.DisplayNameFor(model => model.EnglishDescription):</b> @Model.EnglishDescription<br /> <b>@Html.DisplayNameFor(model => model.EnglishTitle):</b> @Model.EnglishTitle<br />
<b>@Html.DisplayNameFor(model => model.RussianDescription):</b> @Model.RussianDescription<br /> <b>@Html.DisplayNameFor(model => model.RussianTitle):</b> @Model.RussianTitle<br />
<b>@Html.DisplayNameFor(model => model.Link):</b> @Model.Link<br /> </p>
<b>@Html.DisplayNameFor(model => model.EnglishLinkCaption):</b> @Model.EnglishLinkCaption<br /> <p>
<b>@Html.DisplayNameFor(model => model.RussianLinkCaption):</b> @Model.RussianLinkCaption<br /> <b>@Html.DisplayNameFor(model => model.EnglishLinkCaption):</b> @Model.EnglishLinkCaption<br />
<b>@Html.DisplayNameFor(model => model.Badges):</b> @Model.Badges<br /> <b>@Html.DisplayNameFor(model => model.RussianLinkCaption):</b> @Model.RussianLinkCaption<br />
</p> </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 />
<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>
<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>
}
@@ -1,79 +1,110 @@
@model (IEnumerable<MyWebsite.Models.ProjectModel> projects, IEnumerable<MyWebsite.Models.BadgeModel> badges) @model (IEnumerable<MyWebsite.Models.ProjectModel> projects, IEnumerable<MyWebsite.Models.BadgeModel> badges)
@{ @{
ViewData["Title"] = "Projects"; ViewData["Title"] = "Projects";
} }
<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>
<table> <div asp-validation-summary="All" class="text-danger"></div>
<thead> <table>
<tr> <thead>
<th> <tr>
@Html.DisplayNameFor(model => model.projects.First().Id) <th>&#xE700;</th>
</th> <th class="hide-l1">
<th> @Html.DisplayNameFor(model => model.projects.First().Id)
@Html.DisplayNameFor(model => model.projects.First().EnglishTitle) </th>
</th> <th>
<th> @Html.DisplayNameFor(model => model.projects.First().Title)
@Html.DisplayNameFor(model => model.projects.First().RussianTitle) </th>
</th> <th class="hide-l1">
<th> @Html.DisplayNameFor(model => model.projects.First().LinkCaption)
@Html.DisplayNameFor(model => model.projects.First().Link) </th>
</th> <th class="hide-l2">
<th> @Html.DisplayNameFor(model => model.projects.First().Link)
@Html.DisplayNameFor(model => model.projects.First().Badges) </th>
</th> <th class="hide-l2">
<th></th> @Html.DisplayNameFor(model => model.projects.First().Badges)
</tr> (<a asp-controller="Badges" asp-action="Index">Edit badges</a>)
</thead> </th>
<tbody> <th>Actions</th>
@foreach (var item in Model.projects.OrderByDescending(i => i.Id)) </tr>
{ </thead>
<tr> <tbody>
<td>@item.Id</td> @foreach (var item in Model.projects.OrderBy(i => i.Order))
<td>@item.EnglishTitle</td> {
<td>@item.RussianTitle</td> <tr id="@item.Id">
<td>@item.Link</td> <td class="reorderingBtns">
<td> <a onclick="Up(this)">&#xE010;</a><br />
<div class="badge-placeholder"> <span>&#xE915;</span><br />
@foreach (string badge in item.Badges.Split(',')) <a onclick="Down(this)">&#xE011;</a>
{ </td>
<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> <td class="hide-l1">@item.Id</td>
} <td>
</div> @item.EnglishTitle (en)<br />
</td> @(item.RussianTitle ?? "<not_set>") (ru)
<td> </td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> | <td class="hide-l1">
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a> @(item.EnglishLinkCaption ?? "<not_set>") (en)<br />
</td> @(item.RussianLinkCaption ?? "<not_set>") (ru)
</tr> </td>
} <td class="hide-l2"><a target="_blank" href="@item.Link">@item.Link</a></td>
</tbody> <td class="hide-l2">
</table> <div class="badge-placeholder">
@foreach (string b in item.Badges.Split(','))
{
BadgeModel badge = Model.badges.FirstOrDefault(i => i.Name == b);
<div style="background-image: url('../images/Badges/@(badge?.Image).png')" title="@(badge?.Description)"></div>
}
</div>
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</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"> {
.badge-placeholder <style type="text/css">
{ .badge-placeholder
display: grid; {
grid-column-gap: 10px; display: grid;
grid-auto-columns: max-content; grid-column-gap: 10px;
grid-auto-flow: column; grid-auto-columns: max-content;
} grid-auto-flow: column;
}
.badge-placeholder div .badge-placeholder div
{ {
height: 25px; height: 25px;
width: 25px; width: 25px;
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>
<span asp-validation-for="Language" class="text-danger"></span> @foreach (var culture in CultureInfo.GetCultures(CultureTypes.NeutralCultures))
</div> {
<div> <option value="@culture.Name">@culture.EnglishName</option>
<label asp-for="Content"></label> }
<textarea asp-for="Content" spellcheck="false"></textarea> </select>
<span asp-validation-for="Content" class="text-danger"></span> <span asp-validation-for="Language" class="text-danger"></span>
</div> </div>
<br /> <div>
<input type="submit" value="Save" class="btn"/> <label asp-for="Content"></label>
</form> <textarea asp-for="Content" spellcheck="false" required></textarea>
<span asp-validation-for="Content" class="text-danger"></span>
</div>
<br />
<input type="submit" value="Save" />
</form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<style type="text/css"> {
form { <style type="text/css">
max-width: initial; form
} {
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<br />
Previously updated on @Model.LastUpdate
</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 asp-validation-for="Content" class="text-danger"></span>
<span style="display: none" id="copied"> - Done</span><br />
<span asp-validation-for="Content" class="text-danger"></span> <br />
</div> <input type="submit" value="Save" />
<br /> </form>
<input type="submit" value="Save" class=" btn" />
</form>
</article> </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" /> @section Imports
<style type="text/css"> {
form { <style type="text/css">
max-width: initial; form
} {
textarea { max-width: initial;
min-height: 500px; }
}
</style> header .comment
<script type="text/javascript"> {
async function CopyToClipboard() { cursor: pointer;
text = document.getElementById("Content"); }
text.select();
document.execCommand("copy"); textarea
window.getSelection().removeAllRanges(); {
document.getElementById("copied").style.display = "initial"; min-height: 500px;
await new Promise(res => setTimeout(res, 3000)); }
document.getElementById("copied").style.display = "none"; </style>
} <script type="text/javascript">
</script> function CopyToClipboard()
{
document.querySelector("textarea").select();
document.execCommand("copy");
document.getSelection().removeAllRanges();
alert("CV content copied");
}
</script>
}
@@ -1,44 +1,39 @@
@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>
<table> <table>
<thead> <thead>
<tr> <tr>
<th> <th>
@Html.DisplayNameFor(model => model.Language) @Html.DisplayNameFor(model => model.Language)
</th> </th>
<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>
@foreach (var item in Model) @foreach (var item in Model)
{ {
<tr> <tr>
<td>@item.Language</td> <td>@item.Language</td>
<td>@item.LastUpdate</td> <td>@item.LastUpdate</td>
<td> <td>
<a asp-action="Edit" asp-route-id="@item.Language">Edit</a> | <a asp-action="Edit" asp-route-id="@item.Language">Edit</a> |
<a asp-action="Delete" asp-route-id="@item.Language">Delete</a> <a asp-action="Delete" asp-route-id="@item.Language">Delete</a>
</td> </td>
</tr> </tr>
} }
</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>
<label>Confirm password:</label> </form>
<input id="confirm-password" type="password" />
</p> <h2>Change password</h2>
<p> <form method="post" onsubmit="return ValidatePassword()">
<label asp-for="Current.Email">Current e-mail:</label> <label for="value">New password:</label>
<input asp-for="Current.Email" type="email" /> <input name="value" id="newPassword" type="password" required />
<span asp-validation-for="Current.Email" class="text-danger"></span><br />
<label asp-for="Current.Password">Current password:</label>
<input asp-for="Current.Password" type="password" /> <label>Confirm password:</label>
<span asp-validation-for="Current.Password" class="text-danger"></span> <input id="confirmPassword" type="password" required />
</p>
<input type="submit" value="Update" /> <span class="text-danger" id="passwordValidationError"></span>
</form>
<input name="key" value="password" hidden />
<button>Update password</button>
</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;
emailValidation.innerHTML = "";
passwordValidation.innerHTML = "";
if (newEmail.value != "" && newEmail.value != confirmEmail.value)
{ {
emailValidation.innerHTML = "Addresses don't match"; var newEmail = document.querySelector("#newEmail");
invalid = true; var confirmEmail = document.querySelector("#confirmEmail");
}
if (newPassword.value != "" && newPassword.value != confirmPassword.value) var emailValidation = document.querySelector("#emailValidationError");
{
passwordValidation.innerHTML = "Passwords don't match"; emailValidation.innerHTML = "";
invalid = true;
if (newEmail.value == confirmEmail.value)
return true;
emailValidation.innerHTML = "E-mail addresses are invalid or doesn't match";
return false;
} }
return !invalid; function ValidatePassword()
} {
</script> var newPassword = document.querySelector("#newPassword");
var confirmPassword = document.querySelector("#confirmPassword");
var passwordValidation = document.querySelector("#passwordValidationError");
passwordValidation.innerHTML = "";
if (newPassword.value == confirmPassword.value)
return true;
passwordValidation.innerHTML = "Passwords doesn't match";
return false;
}
</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
@@ -10,49 +10,49 @@ using MyWebsite.Areas.Projects.Models;
namespace MyWebsite.Areas.Projects.Controllers 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; private FoxTubeDatabaseContext FoxTubeDatabaseContext { get; set; }
public FoxTubeController(DatabaseContext context) : base(context)
{
db = context;
Scan(Directory.GetCurrentDirectory() + "\\wwwroot\\assets\\FoxTube\\Screenshots\\" + CultureInfo.CurrentUICulture.ThreeLetterISOLanguageName, paths); public FoxTubeController(DatabaseContext context, FoxTubeDatabaseContext foxTubeDatabase) : base(context)
{
FoxTubeDatabaseContext = foxTubeDatabase;
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++)
paths[i] = paths[i].Substring(paths[i].IndexOf("Screenshots", System.StringComparison.OrdinalIgnoreCase)); paths[i] = paths[i].Substring(paths[i].IndexOf("Screenshots", System.StringComparison.OrdinalIgnoreCase));
files = paths.Select(i => i.Substring(i.LastIndexOf('\\') + 1)).ToList(); files = paths.Select(i => i.Substring(i.LastIndexOf('\\') + 1)).ToList();
} }
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,
Current = id Current = id
}); });
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)
{ {
foreach (string p in Directory.GetFiles(path)) foreach (string p in Directory.GetFiles(path))
files.Add(p); files.Add(p);
foreach (string p in Directory.GetDirectories(path)) foreach (string p in Directory.GetDirectories(path))
Scan(p, files); Scan(p, files);
} }
} }
} }
@@ -4,16 +4,16 @@ using System.Collections.Generic;
namespace MyWebsite.Areas.Projects.Models namespace MyWebsite.Areas.Projects.Models
{ {
public class ScreenshotViewModel : ViewModelBase public class ScreenshotViewModel : ViewModelBase
{ {
public List<string> Paths { get; set; } public List<string> Paths { get; set; }
public List<string> Names { get; set; } public List<string> Names { get; set; }
public string Current { get; set; } public string Current { get; set; }
public int Position => Names.IndexOf(Current); public int Position => Names.IndexOf(Current);
public string Next => Names.Count == Position + 1 ? null : Names[Position + 1]; public string Next => Names.Count == Position + 1 ? null : Names[Position + 1];
public string Previous => Position > 0 ? Names[Position - 1] : null; public string Previous => Position > 0 ? Names[Position - 1] : null;
public ScreenshotViewModel(DatabaseContext context) : base(context) { } public ScreenshotViewModel(DatabaseContext context) : base(context) { }
} }
} }
@@ -1,100 +1,98 @@
@model ScreenshotViewModel @model ScreenshotViewModel
@{ @{
ViewData["Title"] = "Meet FoxTube!"; ViewData["Title"] = "Meet FoxTube!";
Layout = "_Layout.cshtml"; Layout = "_Layout.cshtml";
} }
<header> <header>
<div id="mspb-c71suduiy4sa" class="9NCQQXJTDLFH"></div> <div id="mspb-c71suduiy4sa" class="9NCQQXJTDLFH"></div>
</header> </header>
<article> <article>
<div class="description"> <div class="description">
<p>&#xE760; <a asp-action="Index" asp-controller="Projects" asp-area="" class="back">Back to other projects</a></p> <p>&#xE760; <a asp-action="Index" asp-controller="Projects" asp-area="" class="back">Back to other projects</a></p>
<h1>Available on</h1> <h1>Available on</h1>
<a>Requires Windows 10 April 2018 Update or higher</a> <a>Requires Windows 10 April 2018 Update or higher</a>
<ul> <ul>
<li>PC</li> <li>PC</li>
<li>Surface Hub</li> <li>Surface Hub</li>
<li>HoloLens</li> <li>HoloLens</li>
</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>
YouTube client for Windows 10 family devices. It's fast and convenient. It also supports live streams and 8K videos! Frequent updates will make you sure that all problems will be purged as soon as possible YouTube client for Windows 10 family devices. It's fast and convenient. It also supports live streams and 8K videos! Frequent updates will make you sure that all problems will be purged as soon as possible
</p> </p>
<h1>Features</h1> <h1>Features</h1>
<ul> <ul>
<li>New fresh view for YouTube</li> <li>New fresh view for YouTube</li>
<li>But so familiar...</li> <li>But so familiar...</li>
<li>Faster browsing: up to 40% faster than through You-know-who (Google Chrome)!</li> <li>Faster browsing: up to 40% faster than through You-know-who (Google Chrome)!</li>
<li>Nice dark theme for vampires</li> <li>Nice dark theme for vampires</li>
<li>Picture-in-picture mode for hard workers</li> <li>Picture-in-picture mode for hard workers</li>
<li>Download videos! Spread communism!*</li> <li>Download videos! Spread communism!*</li>
<li>8K support for big ones</li> <li>8K support for big ones</li>
<li>Fox on logo!</li> <li>Fox on logo!</li>
</ul> </ul>
<h1>Screenshots</h1> <h1>Screenshots</h1>
<div class="gallery"> <div class="gallery">
@for (int i = 0; i < Model.Paths.Count; i++) @for (int i = 0; i < Model.Paths.Count; i++)
{ {
<a asp-action="Screenshot" asp-route-id="@Model.Names[i]"><img src="@Model.Paths[i]" title="@Model.Names[i]"></a> <a asp-action="Screenshot" asp-route-id="@Model.Names[i]"><img src="@Model.Paths[i]" title="@Model.Names[i]"></a>
} }
</div> </div>
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" /> <link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<h1>Useufl links</h1> <h1>Useufl links</h1>
<ul> <ul>
<li><a asp-action="Privacy">Privacy policy</a></li> <li><a asp-action="Privacy">Privacy policy</a></li>
<li><a target="_blank" href="//youtube.com/t/privacy">YouTube privacy policy</a></li> <li><a target="_blank" href="//youtube.com/t/privacy">YouTube privacy policy</a></li>
<li><a target="_blank" href="//youtube.com/t/terms">YouTube terms of use</a></li> <li><a target="_blank" href="//youtube.com/t/terms">YouTube terms of use</a></li>
<li><a target="_blank" href="//youtube.com/t/community_guidelines">YouTube community guidelines</a></li> <li><a target="_blank" href="//youtube.com/t/community_guidelines">YouTube community guidelines</a></li>
</ul> </ul>
<h1>Credits</h1> <h1>Credits</h1>
<h2>Demo footage content</h2> <h2>Demo footage content</h2>
<h3>Trailer</h3> <h3>Trailer</h3>
<ul> <ul>
<li><a target="_blank" href="//microsoft.com/">Microsoft</a> (Soundtrack: <a target="_blank" href="//www.youtube.com/watch?v=dwK422sLD-s">Introducing Microsoft Surface Laptop 2</a>)</li> <li><a target="_blank" href="//microsoft.com/">Microsoft</a> (Soundtrack: <a target="_blank" href="//www.youtube.com/watch?v=dwK422sLD-s">Introducing Microsoft Surface Laptop 2</a>)</li>
<li><a target="_blank" href="//www.youtube.com/user/skullkruncher13">JT Music</a> (Comparasion video: <a target="_blank" href="//www.youtube.com/watch?v=AGfa24afEBM">METRO EXODUS SONG by JT Music (feat. Andrea Storm Kaden)</a>)</li> <li><a target="_blank" href="//www.youtube.com/user/skullkruncher13">JT Music</a> (Comparasion video: <a target="_blank" href="//www.youtube.com/watch?v=AGfa24afEBM">METRO EXODUS SONG by JT Music (feat. Andrea Storm Kaden)</a>)</li>
</ul> </ul>
<h3>Screenshots and live</h3> <h3>Screenshots and live</h3>
<ul> <ul>
<li><a target="_blank" href="//windowscentral.com/">Windows Central</a></li> <li><a target="_blank" href="//windowscentral.com/">Windows Central</a></li>
<li><a target="_blank" href="//www.youtube.com/user/ouramazingspace/about">Space Videos</a></li> <li><a target="_blank" href="//www.youtube.com/user/ouramazingspace/about">Space Videos</a></li>
<li><a target="_blank" href="//www.youtube.com/channel/UCS3pqiugq53HFPYiWLPtdeA">MSReview</a></li> <li><a target="_blank" href="//www.youtube.com/channel/UCS3pqiugq53HFPYiWLPtdeA">MSReview</a></li>
<li><a target="_blank" href="//www.youtube.com/channel/UC6cqazSR6CnVMClY0bJI0Lg">BadComedian</a></li> <li><a target="_blank" href="//www.youtube.com/channel/UC6cqazSR6CnVMClY0bJI0Lg">BadComedian</a></li>
<li><a target="_blank" href="//www.youtube.com/channel/UCf31Gf5nCU8J6eUlr7QSU0w">Marmok</a></li> <li><a target="_blank" href="//www.youtube.com/channel/UCf31Gf5nCU8J6eUlr7QSU0w">Marmok</a></li>
<li><a target="_blank" href="//www.youtube.com/channel/UCK7OXr0m5mnM1z9p7n_Bwfw">DAGames</a></li> <li><a target="_blank" href="//www.youtube.com/channel/UCK7OXr0m5mnM1z9p7n_Bwfw">DAGames</a></li>
<li><a target="_blank" href="//www.youtube.com/user/RandomEncountersEnt">Random Ecounters</a></li> <li><a target="_blank" href="//www.youtube.com/user/RandomEncountersEnt">Random Ecounters</a></li>
<li><a target="_blank" href="//www.youtube.com/channel/UCuXYmUOJSbEH1x88WUV1aMg">TheNafig</a></li> <li><a target="_blank" href="//www.youtube.com/channel/UCuXYmUOJSbEH1x88WUV1aMg">TheNafig</a></li>
</ul> </ul>
<p> <p>
© @(DateTime.Today.Year) Michael Gordeev<br /> © @(DateTime.Today.Year) Michael Gordeev<br />
© @(DateTime.Today.Year) YouTube LLC © @(DateTime.Today.Year) YouTube LLC
</p> </p>
</div> </div>
</article> </article>
@section Imports { @section Imports {
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Calibri:400,700,400italic,700italic" /> <link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Calibri:400,700,400italic,700italic" />
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" /> <link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<script src="https://storebadge.azureedge.net/src/badge-1.8.4.js"></script> <script src="https://storebadge.azureedge.net/src/badge-1.8.4.js"></script>
<script> <script>
mspb({ productId: '9NCQQXJTDLFH', badgeType: 'large' }, function (badge) mspb({ productId: '9NCQQXJTDLFH', badgeType: 'large' }, function (badge)
{ {
document.getElementById('mspb-c71suduiy4sa').innerHTML = badge; document.getElementById('mspb-c71suduiy4sa').innerHTML = badge;
}); });
</script> </script>
} }
@@ -1,30 +1,49 @@
@model ResumeViewModel @model ResumeViewModel
@{ @{
ViewData["Title"] = "Privacy policy"; ViewData["Title"] = "Privacy policy";
Layout = "_Layout.cshtml"; Layout = "_Layout.cshtml";
} }
<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>
@Html.Raw(Model?.Resume.Content) @Html.Raw(Model?.Resume.Content)
</article> </article>
@section Imports { @section Imports {
<style type="text/css"> <style type="text/css">
header, article { article
margin: 0px 50px; {
} margin: 0px 50px;
header a { }
color: black;
text-decoration: none; header
} {
header a:hover { margin: 16px 50px;
text-decoration: underline; }
}
</style> header a
{
color: black;
text-decoration: none;
}
header a:hover
{
text-decoration: underline;
}
@@media only screen and (max-width: 500px)
{
header, article
{
margin-right: 20px;
margin-left: 20px;
}
}
</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,41 +18,44 @@
<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>
<script type="text/javascript"> <script type="text/javascript">
function ToggleImageSize() function ToggleImageSize()
{ {
var image = document.getElementById("image"); var image = document.getElementById("image");
if (image.style.cursor == "zoom-out") if (image.style.cursor == "zoom-out")
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";
} }
} }
</script> </script>
} }
@@ -2,91 +2,89 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>@ViewData["Title"] - FoxTube - XFox111.NET</title> <title>@ViewData["Title"] - FoxTube - XFox111.NET</title>
<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="FoxTube.css" /> <link rel="stylesheet" type="text/css" href="~/css/Fonts.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";
else else
menu.style.display = "none"; menu.style.display = "none";
} }
</script> </script>
@RenderSection("Imports", false) @RenderSection("Imports", false)
@if (IsSectionDefined("OpenGraph")) @if (IsSectionDefined("OpenGraph"))
RenderSection("OpenGraph"); RenderSection("OpenGraph");
else else
{ {
<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:video" content="//www.youtube.com/embed/Mio9FbxmbhM"> <meta property="og:site_name" content="FoxTube - New YouTube client for Windows 10" />
<meta property="og:video" content="https://www.youtube.com/embed/Mio9FbxmbhM"> <meta property="og:video" content="//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/embed/Mio9FbxmbhM">
<meta property="ya:ovs:allow_embed" content="true" /> <meta property="og:video" content="https://www.youtube.com/v/Mio9FbxmbhM">
<meta property="og:type" content="article"> <meta property="ya:ovs:allow_embed" content="true" />
<meta property="og:locale" content="en_US"> <meta property="og:type" content="article">
<meta property="og: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:locale" content="en_US">
<meta property="og:title" content="Meet FoxTube! - New YouTube client for Windows 10"> <meta property="og: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:title" content="Meet FoxTube! - New YouTube client for Windows 10">
}
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head> </head>
<body> <body>
<nav> <nav>
<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> </menu>
<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>
<p> <p>
<a asp-controller="Home" asp-action="Index" asp-area="">XFox111.NET</a><br /> <a asp-controller="Home" asp-action="Index" asp-area="">XFox111.NET</a><br />
<a asp-controller="Home" asp-action="SwitchLanguage" lang="ru">РУС &#xE12B;</a> <a asp-controller="Home" asp-action="SwitchLanguage" lang="ru">РУС &#xE12B;</a>
<a id="menu-toggle" onclick="ToggleMenu();">&#xE700;</a> <a id="menu-toggle" onclick="ToggleMenu();">&#xE700;</a>
</p> </p>
</nav> </nav>
<main> <main onclick="document.querySelector('menu').style.display = 'none'">
@RenderBody() @RenderBody()
</main> </main>
@{ @{
if (IsSectionDefined("Footer")) if (IsSectionDefined("Footer"))
RenderSection("Footer"); RenderSection("Footer");
else else
{ {
<footer> <footer>
<span class="comment">// Copyright &copy;@(DateTime.Today.Year) Michael "XFox" Gordeev</span> <span class="comment">// Copyright &copy;@(DateTime.Today.Year) Michael "XFox" Gordeev</span>
<div> <div>
@foreach (LinkModel link in Model.Links.Where(i => i.DisplayInFooter).OrderBy(i => i.Order)) @foreach (LinkModel link in Model.Links.Where(i => i.DisplayInFooter).OrderBy(i => i.Order))
{ {
<a class="socicon-@(link.Name)" href="@(link.Url)" target="_blank" title="@(link.Title)"></a> <a class="socicon-@(link.Name)" href="@(link.Url)" target="_blank" title="@(link.Title)"></a>
} }
</div> </div>
</footer> </footer>
} }
} }
</body> </body>
</html> </html>
@@ -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>
@@ -3,4 +3,4 @@
@using MyWebsite.Areas.Projects.Models @using MyWebsite.Areas.Projects.Models
@using MyWebsite.Models @using MyWebsite.Models
@using MyWebsite.ViewModels @using MyWebsite.ViewModels
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -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;
}
} }
} }
@@ -8,6 +8,8 @@ namespace MyWebsite.Models.Databases
public DbSet<MetricsPackage> Metrics { get; set; } public DbSet<MetricsPackage> Metrics { get; set; }
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; }
+15 -25
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>
</Project>
<ItemGroup>
<Folder Include="wwwroot\assets\FoxTube\Screenshots\" />
</ItemGroup>
</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,24 +4,22 @@
} }
<header> <header>
<p> @if (Model.Previous != null)
@if (Model.Previous != null) {
{ @:&#xE760;
@:&#xE760; <a asp-action="Details" asp-route-id="@Model.Previous">Previous</a>
<a asp-action="Details" asp-route-id="@Model.Previous">Previous</a> @:|
@:| }
} &#xE8B9; <a asp-action="Index" asp-controller="Gallery">All artworks</a>
&#xE8B9; <a asp-action="Index" asp-controller="Gallery">All artworks</a> @if (Model.Next != null)
@if (Model.Next != null) {
{ @:| &#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;
} }
} }
@@ -50,4 +50,12 @@
footer span a:hover footer span a:hover
{ {
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
+61 -46
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();
}