1
0

Updated project

This commit is contained in:
Michael Gordeev
2020-03-03 16:49:41 +03:00
parent cfe38cda16
commit ad8b23f727
196 changed files with 8041 additions and 2450 deletions
@@ -1,50 +1,44 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Areas.API.Models;
using System.Globalization;
using MyWebsite.Models.Databases;
namespace MyWebsite.Areas.API
{
[Route("API/[controller]")]
[Area("API")]
[ApiController]
[Route("API/[controller]")]
public class FoxTubeController : ControllerBase
{
FoxTubeDatabaseContext db;
readonly FoxTubeDatabaseContext db;
public FoxTubeController(FoxTubeDatabaseContext context) =>
db = context;
[HttpPost("AddMetrics")]
public string AddMetrics()
[HttpPost]
[Route("AddMetrics")]
public IActionResult AddMetrics(MetricsPackage package)
{
MetricsPackage metrics = new MetricsPackage
{
Title = Request.Form["Title"],
Content = Request.Form["Content"],
Version = Request.Form["Version"],
TimeStamp = DateTime.Now
};
db.Metrics.Add(metrics);
Guid id = db.Metrics.Add(package).Entity.Id;
db.SaveChanges();
return db.Metrics.FirstOrDefault(i => i.TimeStamp == metrics.TimeStamp)?.Id.ToString();
return Accepted(value: id.ToString());
}
[HttpGet("Messages")]
public object Messages(bool toast = false, DateTime? publishedAfter = null, string lang = "en-US")
[HttpGet]
[Route("Messages")]
public IActionResult Messages(bool toast = false, DateTime? publishedAfter = null, string lang = "en-US")
{
if(toast)
if (toast)
{
Message message = publishedAfter.HasValue ?
db.Messages.FirstOrDefault(i => i.PublishedAt >= publishedAfter && i.PublishedAt <= DateTime.Now && i.Language == lang) :
Message message = publishedAfter.HasValue ?
db.Messages.FirstOrDefault(i => i.PublishedAt >= publishedAfter && i.PublishedAt <= DateTime.Now && i.Language == lang) :
db.Messages.OrderByDescending(i => i.PublishedAt).FirstOrDefault();
if (message == null)
return NoContent();
return $@"<toast activationType='foreground' launch='inbox|{message.Id}'>
return Ok($@"<toast activationType='foreground' launch='inbox|{message.Id}'>
<visual>
<binding template='ToastGeneric'>
<image placement='hero' src='{message.HeroImage}'/>
@@ -53,29 +47,23 @@ namespace MyWebsite.Areas.API
<text hint-maxLines='5'>{message.Description}</text>
</binding>
</visual>
</toast>";
</toast>");
}
else
{
List<Message> messages = db.Messages.Where(i => i.PublishedAt <= DateTime.Now).ToList();
return messages;
}
return Ok(db.Messages.Where(i => i.PublishedAt <= DateTime.Now).ToList());
}
[HttpGet("Changelogs")]
public object Changelogs(bool toast = false, string version = null, string lang = "en-US")
[HttpGet]
[Route("Changelogs")]
public IActionResult Changelogs(string version, bool toast = false, string lang = "en-US")
{
if (string.IsNullOrWhiteSpace(version))
throw new ArgumentNullException("You must specify required version number");
if(toast)
if (toast)
{
Changelog changelog = db.Changelogs.FirstOrDefault(i => i.Version == version && i.Language == lang);
if (changelog == null)
return NoContent();
return $@"<toast activationType='foreground' launch='changelog|{changelog.Id}'>
return Ok($@"<toast activationType='foreground' launch='changelog|{changelog.Id}'>
<visual>
<binding template='ToastGeneric'>
<image placement='hero' src='{changelog.HeroImage}'/>
@@ -84,26 +72,18 @@ namespace MyWebsite.Areas.API
<text>{changelog.Title}</text>
</binding>
</visual>
</toast>";
</toast>");
}
else
{
List<Changelog> changelogs = db.Changelogs.Where(i => !IsVersionGreater(i.Version, version)).ToList();
return changelogs;
}
return Ok(db.Changelogs.Where(i => !IsVersionGreater(i.Version, version)).ToList());
}
private static bool IsVersionGreater(string versionOne, string versionTwo)
{
string[] v1 = versionOne.Split('.');
string[] v2 = versionTwo.Split('.');
versionOne = versionOne.Replace(".", "", StringComparison.InvariantCulture);
versionTwo = versionTwo.Replace(".", "", StringComparison.InvariantCulture);
for (int i = 0; i < v1.Length && i < v2.Length; i++)
if (byte.Parse(v1[i]) > byte.Parse(v2[i]))
return true;
return false;
return short.Parse(versionOne, NumberStyles.Integer, CultureInfo.InvariantCulture) > short.Parse(versionTwo, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
}
}
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers;
using MyWebsite.Models.Databases;
namespace MyWebsite.Areas.API
{
[ApiController]
[Route("API/[controller]")]
public class GUTScheduleController : ExtendedController
{
public GUTScheduleController(DatabaseContext context) : base(context) { }
[Route("SemesterOffsetDay")]
public string SemesterOffsetDay() =>
Database.CustomData.Find("gut.schedule.semester.offset")?.Value ?? "undefined";
}
}
@@ -1,16 +0,0 @@
using Microsoft.EntityFrameworkCore;
namespace MyWebsite.Areas.API.Models
{
public class FoxTubeDatabaseContext : DbContext
{
public DbSet<MetricsPackage> Metrics { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<Changelog> Changelogs { get; set; }
public FoxTubeDatabaseContext(DbContextOptions options) : base(options)
{
Database.EnsureCreated();
}
}
}
@@ -1,42 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyWebsite.Areas.API.Models
{
public class InboxItem
{
// Metadata
[Key]
[Required]
[Column(TypeName = "varchae(20)")]
public string Id { get; set; }
[Required]
public DateTime PublishedAt { get; set; } = DateTime.Now;
// Content
[Required]
[Column(TypeName = "varchar(255)")]
public string Title { get; set; }
[Column(TypeName = "varchar(255)")]
public string Description { get; set; }
[Required]
[Column(TypeName = "text")]
public string Content { get; set; }
// Media
[Column(TypeName = "varchar(255")]
public string Icon { get; set; }
[Column(TypeName = "varchar(255")]
public string HeroImage { get; set; }
[Column(TypeName = "varchar(255)")]
public string HtmlEmbed { get; set; }
}
}
@@ -33,10 +33,10 @@ namespace MyWebsite.Areas.API.Models
// Media
[Column(TypeName = "varchar(255")]
[Column(TypeName = "varchar(255)")]
public string Icon { get; set; } = "https://xfox111.net/projects-assets/FoxTube/DefaultIcon.png";
[Column(TypeName = "varchar(255")]
[Column(TypeName = "varchar(255)")]
public string HeroImage { get; set; } = "https://xfox111.net/projects-assets/FoxTube/DefaultHero.png";
[Column(TypeName = "varchar(255)")]
@@ -15,7 +15,7 @@ namespace MyWebsite.Areas.API.Models
public string Title { get; set; }
[Required]
[Column(TypeName = "mediumtext")]
[Column(TypeName = "varchar(max)")]
public string Content { get; set; }
[Required]
@@ -23,6 +23,6 @@ namespace MyWebsite.Areas.API.Models
public string Version { get; set; }
[Required]
public DateTime TimeStamp { get; set; }
public DateTime TimeStamp { get; set; } = DateTime.Now;
}
}
@@ -1,57 +1,52 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using System;
using System.Globalization;
using System.IO;
namespace MyWebsite.Areas.Admin.Controllers
{
[Area("Admin")]
[Authorize]
public class BadgesController : Controller
public class BadgesController : ExtendedController
{
public BadgesController(DatabaseContext context) =>
Startup.Database = context;
public BadgesController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View(Startup.Database.Badges);
View(Database.Badges);
[HttpGet]
public IActionResult Edit(string id) =>
View(Startup.Database.Badges.Find(id));
View(Database.Badges.Find(id));
[HttpPost]
public IActionResult Edit(Badge model, IFormFile file = null)
public IActionResult Edit(BadgeModel model, IFormFile file = null)
{
if(file != null)
{
System.Drawing.Image image = System.Drawing.Image.FromStream(file.OpenReadStream());
if (image.Width != 64 || image.Height != 64 || !file.FileName.ToLower().EndsWith(".png"))
{
ViewData["UploadException"] = "error";
return View(Startup.Database.Badges.Find(model.Name));
}
using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Badges/" + file.FileName))
file.CopyTo(stream);
if (model == null)
throw new ArgumentNullException(nameof(model));
return Redirect(Request.Path.Value);
}
if (file != null)
UploadFile(file, model);
Startup.Database.Badges.Update(model);
Startup.Database.SaveChanges();
Database.Badges.Update(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Delete(string id) =>
View(Startup.Database.Badges.Find(id));
View(Database.Badges.Find(id));
[HttpPost]
public IActionResult Delete(Badge model)
public IActionResult Delete(BadgeModel model)
{
Startup.Database.Badges.Remove(model);
Startup.Database.SaveChanges();
Database.Badges.Remove(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
@@ -61,21 +56,13 @@ namespace MyWebsite.Areas.Admin.Controllers
View();
[HttpPost]
public IActionResult Create(Badge model, IFormFile file = null)
public IActionResult Create(BadgeModel model, IFormFile file = null)
{
if (file != null)
{
System.Drawing.Image image = System.Drawing.Image.FromStream(file.OpenReadStream());
if (image.Width != 64 || image.Height != 64 || !file.FileName.ToLower().EndsWith(".png"))
{
ViewData["UploadException"] = "error";
return View(Startup.Database.Badges.Find(model.Name));
}
using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Badges/" + file.FileName))
file.CopyTo(stream);
if (model == null)
throw new ArgumentNullException(nameof(model));
return Redirect(Request.Path.Value);
}
if (file != null)
return UploadFile(file, model);
if (!ModelState.IsValid)
{
@@ -83,10 +70,24 @@ namespace MyWebsite.Areas.Admin.Controllers
return View(model);
}
Startup.Database.Badges.Add(model);
Startup.Database.SaveChanges();
Database.Badges.Add(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
private IActionResult UploadFile(IFormFile file, BadgeModel model)
{
System.Drawing.Image image = System.Drawing.Image.FromStream(file.OpenReadStream());
if (image.Width != 64 || image.Height != 64 || !file.FileName.EndsWith(".PNG", true, CultureInfo.InvariantCulture))
{
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);
}
}
}
@@ -1,41 +1,42 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
namespace MyWebsite.Areas.Admin.Controllers
{
[Area("Admin")]
[Authorize]
public class ContactsController : Controller
public class ContactsController : ExtendedController
{
public ContactsController(DatabaseContext context) =>
Startup.Database = context;
public ContactsController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View(Startup.Database.Links);
View(Database.Links);
[HttpGet]
public IActionResult Edit(string id) =>
View(Startup.Database.Links.Find(id));
View(Database.Links.Find(id));
[HttpPost]
public IActionResult Edit(Link model)
public IActionResult Edit(LinkModel model)
{
Startup.Database.Links.Update(model);
Startup.Database.SaveChanges();
Database.Links.Update(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Delete(string id) =>
View(Startup.Database.Links.Find(id));
View(Database.Links.Find(id));
[HttpPost]
public IActionResult Delete(Link link)
public IActionResult Delete(LinkModel model)
{
Startup.Database.Links.Remove(link);
Startup.Database.SaveChanges();
Database.Links.Remove(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
@@ -45,16 +46,16 @@ namespace MyWebsite.Areas.Admin.Controllers
View();
[HttpPost]
public IActionResult Create(Link link)
public IActionResult Create(LinkModel model)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(link);
return View(model);
}
Startup.Database.Links.Add(link);
Startup.Database.SaveChanges();
Database.Links.Add(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
@@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers;
using MyWebsite.Helpers;
using MyWebsite.Models.Databases;
using System.Linq;
namespace MyWebsite.Areas.Admin.Controllers
{
[Area("Admin")]
[Authorize]
public class CredentialController : ExtendedController
{
const string viewPath = "Areas/Admin/Views/Shared/Credential.cshtml";
public CredentialController(DatabaseContext context) : base(context) { }
[HttpGet]
public IActionResult Index() =>
View(viewPath);
[HttpPost]
public IActionResult Index(Models.CredentialModel model)
{
MyWebsite.Models.CredentialModel credential = Database.Users.FirstOrDefault();
if (model == null || model.Current.Email != credential.Email || !Encryptor.VerifyHash(model.Current.Password, credential.Password))
{
ModelState.AddModelError("Authorization error", "Invaild e-mail or password");
return View(viewPath, model);
}
if(string.IsNullOrWhiteSpace(model.Updated.Email) && string.IsNullOrWhiteSpace(model.Current.Password))
{
ModelState.AddModelError("Validation error", "No data to update");
return View(viewPath, model);
}
Database.Users.Remove(credential);
Database.SaveChanges();
if(!string.IsNullOrWhiteSpace(model.Current.Email))
credential.Email = model.Updated.Email;
if(!string.IsNullOrWhiteSpace(model.Current.Password))
credential.Password = Encryptor.ComputeHash(model.Updated.Password);
Database.Users.Add(credential);
Database.SaveChanges();
return Redirect("~/Admin/Logout");
}
}
}
@@ -0,0 +1,41 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using System.Linq;
namespace MyWebsite.Areas.Admin.Controllers
{
[Authorize]
[Area("Admin")]
public class GUTScheduleController : ExtendedController
{
const string scheduleOffsetId = "gut.schedule.semester.offset";
const string viewPath = "Areas/Admin/Views/Shared/GUTSchedule.cshtml";
public GUTScheduleController(DatabaseContext context) : base(context) { }
[HttpGet]
public IActionResult Index() =>
View(viewPath, Database.CustomData.Find(scheduleOffsetId) ?? new CustomData { Key = scheduleOffsetId, Value = "undefined" });
[HttpPost]
public IActionResult Index(CustomData model)
{
if(!ModelState.IsValid)
{
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();
return View(viewPath, model);
}
}
}
@@ -1,19 +1,98 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Areas.Admin.Models;
using MyWebsite.Controllers;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using System;
using System.IO;
namespace MyWebsite.Areas.Admin.Controllers
{
[Area("Admin")]
[Authorize]
public class GalleryController : Controller
public class GalleryController : ExtendedController
{
public GalleryController(DatabaseContext context) =>
Startup.Database = context;
public GalleryController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View(Startup.Database.Gallery);
View(Database.Gallery);
[HttpGet]
public IActionResult Edit(Guid id)
{
if (Database.Gallery.Find(id) is ImageModel model)
return View(model);
else
return NotFound();
}
[HttpPost]
public IActionResult Edit(ImageModel model)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
Database.Gallery.Update(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Delete(Guid id)
{
if (Database.Gallery.Find(id) is ImageModel model)
return View(model);
else
return NotFound();
}
[HttpPost]
public IActionResult Delete(ImageModel model)
{
Database.Gallery.Remove(model);
Database.SaveChanges();
System.IO.File.SetAttributes(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.FileName, FileAttributes.Normal);
System.IO.File.Delete(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.FileName);
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Upload() =>
View();
[HttpPost]
public IActionResult Upload(ArtworkModel model)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
using (var stream = System.IO.File.Create(Directory.GetCurrentDirectory() + "/wwwroot/images/Gallery/" + model?.File.FileName))
model.File.CopyTo(stream);
ImageModel image = new ImageModel
{
EnglishTitle = model.EnglishTitle,
RussianTitle = model.RussianTitle,
EnglishDescription = model.EnglishDescription,
RussianDescription = model.RussianDescription,
CreationDate = model.CreationDate,
FileName = model.File.FileName
};
Database.Gallery.Add(image);
Database.SaveChanges();
return RedirectToAction("Index");
}
}
}
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
namespace MyWebsite.Areas.Admin.Controllers
{
[Area("Admin")]
[Authorize]
public class ProjectsController : ExtendedController
{
public ProjectsController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View((Database.Projects as IEnumerable<ProjectModel>, Database.Badges as IEnumerable<BadgeModel>));
[HttpGet]
public IActionResult Delete(decimal id) =>
View(Database.Projects.Find(id));
[HttpPost]
public IActionResult Delete(ProjectModel model)
{
Database.Projects.Remove(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Create() =>
View();
[HttpPost]
public IActionResult Create(ProjectModel model)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
Database.Projects.Add(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
}
public class ProjectEditorModel
{
public double Id { get; set; }
public string EnglishTitle { get; set; }
public string RussianTitle { get; set; }
public string EnglishDescription { get; set; }
public string RussianDescription { get; set; }
public string Link { get; set; }
public string EnglishLinkCaption { get; set; }
public string RussianLinkCaption { get; set; }
}
}
@@ -1,43 +1,54 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using System;
namespace MyWebsite.Areas.Admin.Controllers
{
[Authorize]
[Area("Admin")]
public class ResumeController : Controller
public class ResumeController : ExtendedController
{
public ResumeController(DatabaseContext context) =>
Startup.Database = context;
public ResumeController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View(Startup.Database.Resume);
View(Database.Resume);
[HttpGet]
public IActionResult Edit(string id) =>
View(Startup.Database.Resume.Find(id));
View(Database.Resume.Find(id));
[HttpPost]
public IActionResult Edit(Resume model)
public IActionResult Edit(ResumeModel model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
model.LastUpdate = DateTime.Now;
Startup.Database.Resume.Update(model);
Startup.Database.SaveChanges();
Database.Resume.Update(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Delete(string id) =>
View(Startup.Database.Resume.Find(id));
View(Database.Resume.Find(id));
[HttpPost]
public IActionResult Delete(Resume model)
public IActionResult Delete(ResumeModel model)
{
Startup.Database.Resume.Remove(model);
Startup.Database.SaveChanges();
Database.Resume.Remove(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
@@ -47,17 +58,21 @@ namespace MyWebsite.Areas.Admin.Controllers
View();
[HttpPost]
public IActionResult Create(Resume model)
public IActionResult Create(ResumeModel model)
{
model.LastUpdate = DateTime.Now;
if (model == null)
throw new ArgumentNullException(nameof(model));
if (!ModelState.IsValid)
{
ModelState.AddModelError("Error", "Invalid data");
return View(model);
}
Startup.Database.Resume.Add(model);
Startup.Database.SaveChanges();
model.LastUpdate = DateTime.Now;
Database.Resume.Add(model);
Database.SaveChanges();
return RedirectToAction("Index");
}
@@ -0,0 +1,29 @@
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; }
}
}
@@ -0,0 +1,14 @@
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();
}
}
@@ -0,0 +1,14 @@
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,4 +1,4 @@
@model MyWebsite.Models.Badge
@model MyWebsite.Models.BadgeModel
@using System.IO;
@{
ViewData["Title"] = "Create badge";
@@ -1,4 +1,4 @@
@model MyWebsite.Models.Badge
@model MyWebsite.Models.BadgeModel
@{
ViewData["Title"] = "Delete badge";
@@ -11,14 +11,14 @@
</header>
<article>
<p class="form-group">
<p>
<b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br />
<b>@Html.DisplayNameFor(model => model.EnglishDescription):</b> @Model.EnglishDescription<br />
<b>@Html.DisplayNameFor(model => model.RussianDescription):</b> @Model.RussianDescription<br />
<b>@Html.DisplayNameFor(model => model.Image):</b> @(Model.Image).png<br />
<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>
<form asp-action="Delete">
@@ -1,5 +1,5 @@
@using System.IO;
@model MyWebsite.Models.Badge
@model MyWebsite.Models.BadgeModel
@{
ViewData["Title"] = "Edit badge";
List<SelectListItem> files = new List<SelectListItem>();
@@ -1,4 +1,4 @@
@model IEnumerable<MyWebsite.Models.Badge>
@model IEnumerable<MyWebsite.Models.BadgeModel>
@{
ViewData["Title"] = "Badges list";
}
@@ -1,4 +1,4 @@
@model MyWebsite.Models.Link
@model MyWebsite.Models.LinkModel
@{
ViewData["Title"] = "Create link";
@@ -1,4 +1,4 @@
@model MyWebsite.Models.Link
@model MyWebsite.Models.LinkModel
@{
ViewData["Title"] = "Delete link";
@@ -1,4 +1,4 @@
@model MyWebsite.Models.Link
@model MyWebsite.Models.LinkModel
@{
ViewData["Title"] = "Edit link";
}
@@ -1,4 +1,4 @@
@model IEnumerable<MyWebsite.Models.Link>
@model IEnumerable<MyWebsite.Models.LinkModel>
@{
ViewData["Title"] = "Links list";
}
@@ -15,9 +15,7 @@
<table>
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Order)
</th>
<th>Reorder</th>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
@@ -45,8 +43,8 @@
<tbody>
@foreach (var item in Model.OrderBy(i => i.Order))
{
<tr>
<td>@item.Order</td>
<tr draggable="true" ondragover="onDragOver(event);">
<td>&#xE700;</td>
<td>@item.Name</td>
<td>@item.EnglishTitle</td>
<td>@item.RussianTitle</td>
@@ -68,4 +66,10 @@
</table>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
<script type="text/javascript">
function onDragOver(event)
{
this.style.marginTop = '20px';
}
</script>
@@ -0,0 +1,34 @@
@model MyWebsite.Models.ImageModel
@{
ViewData["Title"] = "Delete artwork";
}
<header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p>
<h1>Delete artwork</h1>
<h3>Are you sure you want to delete this?</h3>
</header>
<article class="image-overview-block">
<img src="~/images/Gallery/@Model.FileName" onclick="ToggleImageSize();" id="image" />
<div>
<p>
<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.FileName):</b> @Model.FileName<br />
</p>
<p>
<b>@Html.DisplayNameFor(model => model.Description):</b> @Html.Raw(Model.Description)<br />
</p>
<form asp-action="Delete">
<input hidden asp-for="FileName" />
<input type="submit" value="Delete" class="btn-danger" />
</form>
</div>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
<link type="text/css" rel="stylesheet" href="~/css/Gallery.css" />
@@ -0,0 +1,48 @@
@model MyWebsite.Models.ImageModel
@{
ViewData["Title"] = "Edit artwork";
}
<header>
<p>
&#xE760; <a asp-action="Index">Back to the list</a> <br />
<h1>Edit artwork</h1>
<a class="comment" href="~/images/Gallery/@(Model.FileName)" target="_blank">// Open artwork</a>
</p>
</header>
<article>
<form asp-action="Edit">
<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>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -1,4 +1,4 @@
@model IEnumerable<MyWebsite.Models.Image>
@model IEnumerable<MyWebsite.Models.ImageModel>
@{
ViewData["Title"] = "Gallery";
}
@@ -13,25 +13,22 @@
<article>
<table>
@foreach (Image item in Model)
@foreach (ImageModel item in Model)
{
<tr>
<td>
<img title="@item.Title" src="~/images/Gallery/@item.FileName" />
</td>
<td>
<div>
<p>
<h3>@item.Title</h3>
<span>ID: @item.Id</span> |
<span>File name: @item.FileName</span> |
<span>Language: @item.Language</span><br />
<span>File name: @item.FileName</span><br />
<span>Creation date: @item.CreationDate.ToShortDateString()</span><br />
<span>
@Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
@Html.ActionLink("Delete", "Delete", new { id = item.Id })
@Html.ActionLink("Edit", "Edit", new { id = item.FileName }) |
@Html.ActionLink("Delete", "Delete", new { id = item.FileName })
</span>
<p>@Html.Raw(item.Description)</p>
</div>
</p>
</td>
</tr>
}
@@ -42,5 +39,9 @@
<style type="text/css">
img {
height: 200px;
margin-right: 20px;
}
table {
width: initial;
}
</style>
@@ -0,0 +1,49 @@
@model ArtworkModel
@{
ViewData["Title"] = "Upload artwork";
}
<header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p>
<h1>Upload an artwork</h1>
</header>
<article>
<form asp-action="Upload">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<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>
<div>
<label asp-for="File"></label>
<input type="file" accept="image" asp-for="File" />
<span asp-validation-for="File" class="text-danger"></span>
</div>
<input type="submit" value="Upload" class="btn" />
</form>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -0,0 +1,64 @@
@model MyWebsite.Models.ProjectModel
@{
ViewData["Title"] = "New Project";
}
<header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p>
<h1>Create new project</h1>
</header>
<article>
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div>
<label asp-for="Id"></label>
<input asp-for="Id" type="number"/>
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<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="Link"></label>
<input asp-for="Link" type="url"/>
<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" />
<span asp-validation-for="Badges" class="text-danger"></span>
</div>
<input type="submit" value="Create" class="btn" />
</form>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -0,0 +1,31 @@
@model MyWebsite.Models.ProjectModel
@{
ViewData["Title"] = "Delete project";
}
<header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p>
<h1>Delete project entry</h1>
<h3>Are you sure you want to delete this?</h3>
</header>
<article>
<p class="form-group">
<b>@Html.DisplayNameFor(model => model.Id):</b> @Model.Id<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.EnglishDescription):</b> @Model.EnglishDescription<br />
<b>@Html.DisplayNameFor(model => model.RussianDescription):</b> @Model.RussianDescription<br />
<b>@Html.DisplayNameFor(model => model.Link):</b> @Model.Link<br />
<b>@Html.DisplayNameFor(model => model.EnglishLinkCaption):</b> @Model.EnglishLinkCaption<br />
<b>@Html.DisplayNameFor(model => model.RussianLinkCaption):</b> @Model.RussianLinkCaption<br />
<b>@Html.DisplayNameFor(model => model.Badges):</b> @Model.Badges<br />
</p>
<form asp-action="Delete">
<input hidden asp-for="Id" />
<input type="submit" value="Delete" class="btn-danger" />
</form>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@@ -0,0 +1,79 @@
@model (IEnumerable<MyWebsite.Models.ProjectModel> projects, IEnumerable<MyWebsite.Models.BadgeModel> badges)
@{
ViewData["Title"] = "Projects";
}
<header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p>
<h1>Projects list</h1>
<p>
<a asp-action="Create" class="comment">// + Add New</a>
</p>
</header>
<article>
<table>
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.projects.First().Id)
</th>
<th>
@Html.DisplayNameFor(model => model.projects.First().EnglishTitle)
</th>
<th>
@Html.DisplayNameFor(model => model.projects.First().RussianTitle)
</th>
<th>
@Html.DisplayNameFor(model => model.projects.First().Link)
</th>
<th>
@Html.DisplayNameFor(model => model.projects.First().Badges)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.projects.OrderByDescending(i => i.Id))
{
<tr>
<td>@item.Id</td>
<td>@item.EnglishTitle</td>
<td>@item.RussianTitle</td>
<td>@item.Link</td>
<td>
<div class="badge-placeholder">
@foreach (string badge in item.Badges.Split(','))
{
<div style="background-image: @("../images/Badges/" + Model.badges.FirstOrDefault(i => i.Name == badge)?.Image + ".png")" title="@(Model.badges.FirstOrDefault(i => i.Name == badge)?.Description)"></div>
}
</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>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
<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>
@@ -1,5 +1,5 @@
@model MyWebsite.Models.Resume
@using System.Globalization
@model MyWebsite.Models.ResumeModel
@{
ViewData["Title"] = "Create resume";
}
@@ -23,7 +23,7 @@
<span asp-validation-for="Content" class="text-danger"></span>
</div>
<br />
<input type="submit" value="Save" />
<input type="submit" value="Save" class="btn"/>
</form>
</article>
@@ -1,4 +1,4 @@
@model MyWebsite.Models.Resume
@model MyWebsite.Models.ResumeModel
@{
ViewData["Title"] = "Delete resume";
}
@@ -11,7 +11,7 @@
<article>
<p class="form-group">
<b>@Html.DisplayNameFor(model => model.Language):</b> @Model.Language<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 />
</p>
@@ -1,4 +1,4 @@
@model MyWebsite.Models.Resume
@model MyWebsite.Models.ResumeModel
@{
ViewData["Title"] = "Edit resume";
@@ -7,24 +7,24 @@
<header>
<p>&#xE760; <a asp-action="Index">Back to the list</a></p>
<h1>Edit resume</h1>
<p>
Language: @(new System.Globalization.CultureInfo(Model.Language).DisplayName)<br />
Previously updated on @Model.LastUpdate
</p>
</header>
<article>
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="text" asp-for="Language" hidden />
<div>
<label asp-for="Language"></label>
<input type="text" asp-for="Language" readonly class="readonly" />
</div>
<div>
<label asp-for="Content"></label>
<textarea asp-for="Content" spellcheck="false"></textarea>
<a class="comment" onclick="CopyToClipboard()" href="#">// &#xE16D; Copy to clipboard</a>
<span style="display: none" id="copied"> - Done</span><br />
<span asp-validation-for="Content" class="text-danger"></span>
</div>
<br />
<input type="submit" value="Save" />
<input type="submit" value="Save" class=" btn" />
</form>
</article>
@@ -44,7 +44,7 @@
document.execCommand("copy");
window.getSelection().removeAllRanges();
document.getElementById("copied").style.display = "initial";
await delay(3000);
await new Promise(res => setTimeout(res, 3000));
document.getElementById("copied").style.display = "none";
}
</script>
@@ -1,4 +1,4 @@
@model IEnumerable<MyWebsite.Models.Resume>
@model IEnumerable<MyWebsite.Models.ResumeModel>
@{
ViewData["Title"] = "Resumes";
@@ -0,0 +1,69 @@
@model MyWebsite.Areas.Admin.Models.CredentialModel
@{
ViewData["Title"] = "Edit credential";
}
<header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p>
<h1>Change credential information</h1>
</header>
<article>
<form asp-action="Index" onsubmit="return Validate();">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<p>
<label asp-for="Updated.Email">New e-mail:</label>
<input asp-for="Updated.Email" id="new-email" type="email" />
<span asp-validation-for="Updated.Email" class="text-danger" id="email-validation"></span><br />
<label>Confirm e-mail:</label>
<input id="confirm-email" type="email" />
</p>
<p>
<label asp-for="Updated.Password">New password:</label>
<input asp-for="Updated.Password" id="new-password" type="password" />
<span asp-validation-for="Updated.Password" class="text-danger" id="password-validation"></span><br />
<label>Confirm password:</label>
<input id="confirm-password" type="password" />
</p>
<p>
<label asp-for="Current.Email">Current e-mail:</label>
<input asp-for="Current.Email" type="email" />
<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" />
<span asp-validation-for="Current.Password" class="text-danger"></span>
</p>
<input type="submit" value="Update" />
</form>
</article>
<link href="~/css/Admin.css" type="text/css" rel="stylesheet" />
<script type="text/javascript">
var newEmail = document.getElementById("new-email");
var confirmEmail = document.getElementById("confirm-email");
var newPassword = document.getElementById("new-password");
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";
invalid = true;
}
if (newPassword.value != "" && newPassword.value != confirmPassword.value)
{
passwordValidation.innerHTML = "Passwords don't match";
invalid = true;
}
return !invalid;
}
</script>
@@ -0,0 +1,22 @@
@model CustomData
@{
ViewData["Title"] = "GUTSchedule";
}
<header>
<p>&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a></p>
<h1>GUTSchedule</h1>
</header>
<article>
<form asp-action="Index">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input asp-for="Key" hidden />
<label asp-for="Value">First work day in the semester: (Current: @Model.Value)</label>
<input type="number" asp-for="Value" />
<span asp-validation-for="Value" class="text-danger"></span>
<input type="submit" value="Update" class="btn"/>
</form>
</article>
<link href="~/css/Admin.css" type="text/css" rel="stylesheet" />
@@ -1,3 +1,5 @@
@using MyWebsite
@using MyWebsite.Models
@using MyWebsite.ViewModels
@using MyWebsite.Areas.Admin.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -1,15 +1,58 @@
using System;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Controllers;
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Globalization;
using MyWebsite.Areas.Projects.Models;
namespace MyWebsite.Areas.Projects.Controllers
{
[Area("Projects")]
public class FoxTubeController : Controller
public class FoxTubeController : ExtendedController
{
readonly DatabaseContext db;
readonly List<string> paths = new List<string>();
readonly List<string> files;
public FoxTubeController(DatabaseContext context) : base(context)
{
db = context;
Scan(Directory.GetCurrentDirectory() + "\\wwwroot\\assets\\FoxTube\\Screenshots\\" + CultureInfo.CurrentUICulture.ThreeLetterISOLanguageName, paths);
for (int i = 0; i < paths.Count; i++)
paths[i] = paths[i].Substring(paths[i].IndexOf("Screenshots", System.StringComparison.OrdinalIgnoreCase));
files = paths.Select(i => i.Substring(i.LastIndexOf('\\') + 1)).ToList();
}
public IActionResult Index() =>
View("Areas/Projects/Views/FoxTube.cshtml");
View(new ScreenshotViewModel(db)
{
Paths = paths,
Names = files
});
public IActionResult Screenshot(string id) =>
View(new ScreenshotViewModel(db)
{
Paths = paths,
Names = files,
Current = id
});
public IActionResult Privacy() =>
View(new ResumeViewModel(db, CultureInfo.CurrentCulture));
void Scan(string path, List<string> files)
{
foreach (string p in Directory.GetFiles(path))
files.Add(p);
foreach (string p in Directory.GetDirectories(path))
Scan(p, files);
}
}
}
@@ -0,0 +1,19 @@
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
using System.Collections.Generic;
namespace MyWebsite.Areas.Projects.Models
{
public class ScreenshotViewModel : ViewModelBase
{
public List<string> Paths { get; set; }
public List<string> Names { get; set; }
public string Current { get; set; }
public int Position => Names.IndexOf(Current);
public string Next => Names.Count == Position + 1 ? null : Names[Position + 1];
public string Previous => Position > 0 ? Names[Position - 1] : null;
public ScreenshotViewModel(DatabaseContext context) : base(context) { }
}
}
@@ -1,7 +0,0 @@
@{
ViewData["Title"] = "FoxTube";
}
<h1>FoxTube</h1>
@@ -0,0 +1,100 @@
@model ScreenshotViewModel
@{
ViewData["Title"] = "Meet FoxTube!";
Layout = "_Layout.cshtml";
}
<header>
<div id="mspb-c71suduiy4sa" class="9NCQQXJTDLFH"></div>
</header>
<article>
<div class="description">
<p>&#xE760; <a asp-action="Index" asp-controller="Projects" asp-area="" class="back">Back to other projects</a></p>
<h1>Available on</h1>
<a>Requires Windows 10 April 2018 Update or higher</a>
<ul>
<li>PC</li>
<li>Surface Hub</li>
<li>HoloLens</li>
</ul>
<h1>Trailer</h1>
<div>
<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>
<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
</p>
<h1>Features</h1>
<ul>
<li>New fresh view for YouTube</li>
<li>But so familiar...</li>
<li>Faster browsing: up to 40% faster than through You-know-who (Google Chrome)!</li>
<li>Nice dark theme for vampires</li>
<li>Picture-in-picture mode for hard workers</li>
<li>Download videos! Spread communism!*</li>
<li>8K support for big ones</li>
<li>Fox on logo!</li>
</ul>
<h1>Screenshots</h1>
<div class="gallery">
@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>
}
</div>
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<h1>Useufl links</h1>
<ul>
<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/terms">YouTube terms of use</a></li>
<li><a target="_blank" href="//youtube.com/t/community_guidelines">YouTube community guidelines</a></li>
</ul>
<h1>Credits</h1>
<h2>Demo footage content</h2>
<h3>Trailer</h3>
<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="//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>
<h3>Screenshots and live</h3>
<ul>
<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/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/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/user/RandomEncountersEnt">Random Ecounters</a></li>
<li><a target="_blank" href="//www.youtube.com/channel/UCuXYmUOJSbEH1x88WUV1aMg">TheNafig</a></li>
</ul>
<p>
© @(DateTime.Today.Year) Michael Gordeev<br />
© @(DateTime.Today.Year) YouTube LLC
</p>
</div>
</article>
@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="~/css/Gallery.css" />
<script src="https://storebadge.azureedge.net/src/badge-1.8.4.js"></script>
<script>
mspb({ productId: '9NCQQXJTDLFH', badgeType: 'large' }, function (badge)
{
document.getElementById('mspb-c71suduiy4sa').innerHTML = badge;
});
</script>
}
@@ -0,0 +1,30 @@
@model ResumeViewModel
@{
ViewData["Title"] = "Privacy policy";
Layout = "_Layout.cshtml";
}
<header>
<p>&#xE760; <a asp-action="Index">Back to description</a></p>
<h1>FoxTube privacy policy</h1>
<p>Last update: @Model?.Resume.LastUpdate</p>
</header>
<article>
@Html.Raw(Model?.Resume.Content)
</article>
@section Imports {
<style type="text/css">
header, article {
margin: 0px 50px;
}
header a {
color: black;
text-decoration: none;
}
header a:hover {
text-decoration: underline;
}
</style>
}
@@ -0,0 +1,60 @@
@using System.IO
@using System.Globalization
@model ScreenshotViewModel
@{
ViewData["Title"] = "Screenshot";
Layout = "_Layout.cshtml";
}
<article class="image-overview-block">
<p class="controls">
&#xE15C; <a asp-action="Index" class="back">Back to description</a>
@if (Model.Previous != null)
{
@:| &#xE760;
<a asp-action="Screenshot" asp-route-id="@Model.Previous" class="back">Previous</a>
}
@if (Model.Next != null)
{
@:| &#xE761;
<a asp-action="Screenshot" asp-route-id="@Model.Next" class="back">Next</a>
}
</p>
<p class="title">
<b>@Model.Current</b>
</p>
<img src="@Model.Paths[Model.Position]" id="image" onclick="ToggleImageSize();"/>
</article>
@section Imports {
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<style type="text/css">
#image {
max-height: initial;
}
.controls {
display: inline-block;
}
.title {
float: right;
}
</style>
<script type="text/javascript">
function ToggleImageSize()
{
var image = document.getElementById("image");
if (image.style.cursor == "zoom-out")
image.style = "";
else
{
image.style.maxHeight = "none";
image.style.maxWidth = "none";
image.style.cursor = "zoom-out";
}
}
</script>
}
@@ -0,0 +1,92 @@
@model ViewModelBase
<!DOCTYPE html>
<html>
<head>
<title>@ViewData["Title"] - FoxTube - XFox111.NET</title>
<base href="~/assets/FoxTube/" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="~/css/Socicon.css" />
<link rel="stylesheet" type="text/css" href="FoxTube.css" />
<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="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:video" content="//www.youtube.com/embed/Mio9FbxmbhM">
<meta property="og:video" content="https://www.youtube.com/embed/Mio9FbxmbhM">
<meta property="og:video" content="https://www.youtube.com/v/Mio9FbxmbhM">
<meta property="ya:ovs:allow_embed" content="true" />
<meta property="og:type" content="article">
<meta property="og:locale" content="en_US">
<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 name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<nav>
<img class="logo" src="Logo.svg" />
<a asp-action="Index">
<b>FoxTube</b><br />
YouTube client for Windows 10
</a>
<menu type="toolbar" id="main-menu" style="display:none">
<li><a asp-area="" asp-controller="Home" asp-action="Index">Home();</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>
</menu>
<p>
<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 id="menu-toggle" onclick="ToggleMenu();">&#xE700;</a>
</p>
</nav>
<main>
@RenderBody()
</main>
@{
if (IsSectionDefined("Footer"))
RenderSection("Footer");
else
{
<footer>
<span class="comment">// Copyright &copy;@(DateTime.Today.Year) Michael "XFox" Gordeev</span>
<div>
@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>
}
</div>
</footer>
}
}
</body>
</html>
@@ -0,0 +1,6 @@
@using MyWebsite
@using MyWebsite.Areas.Projects
@using MyWebsite.Areas.Projects.Models
@using MyWebsite.Models
@using MyWebsite.ViewModels
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -7,53 +7,65 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Helpers;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
namespace MyWebsite.Controllers
{
[Authorize]
public class AdminController : Controller
{
public AdminController(DatabaseContext context) =>
Startup.Database = context;
[Authorize]
public class AdminController : ExtendedController
{
public AdminController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View();
public IActionResult Index() =>
View();
[AllowAnonymous]
[HttpGet]
public IActionResult Login() =>
View();
[AllowAnonymous]
[HttpGet]
public IActionResult Login() =>
View(new CredentialViewModel(Database));
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("Authorization error", "Invalid data");
return View(model);
}
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(CredentialViewModel model)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("Authorization error", "Invalid data");
return View(new CredentialViewModel(Database, model));
}
LoginModel user = Startup.Database.Users.FirstOrDefault(i => i.Email == model.Email);
if (user == null || !Cryptography.VerifyHash(model.Password, "SHA512", user.Password))
{
ModelState.AddModelError("Authorization error", "Invaild e-mail or password");
return View(model);
}
CredentialModel user = Database.Users.FirstOrDefault(i => i.Email == model.Credential.Email);
if (user == null || !Encryptor.VerifyHash(model?.Credential.Password, user.Password))
{
ModelState.AddModelError("Authorization error", "Invaild e-mail or password");
return View(new CredentialViewModel(Database, model));
}
Claim claim = new Claim(ClaimsIdentity.DefaultNameClaimType, user.Email);
Claim claim = new Claim(ClaimsIdentity.DefaultNameClaimType, user.Email);
ClaimsIdentity id = new ClaimsIdentity(new Claim[] { claim }, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id));
ClaimsIdentity id = new ClaimsIdentity(new Claim[] { claim }, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id)).ConfigureAwait(false);
return RedirectToAction("Index", "Admin");
}
return RedirectToAction("Index", "Admin");
}
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Login", "Admin");
}
}
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false);
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;
}
}
}
@@ -1,18 +1,17 @@
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models;
using System.Linq;
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
namespace MyWebsite.Controllers
{
public class GalleryController : Controller
{
public GalleryController(DatabaseContext context) =>
Startup.Database = context;
public class GalleryController : ExtendedController
{
public GalleryController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View(Startup.Database.Gallery);
public IActionResult Index() =>
View(new ArtworkViewModel(Database));
public IActionResult Details(string id) =>
View(Startup.Database.Gallery.FirstOrDefault(i => i.FileName == id));
}
public IActionResult Details(string id) =>
View(new ArtworkViewModel(Database, id));
}
}
@@ -1,29 +1,33 @@
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
namespace MyWebsite.Controllers
{
public class HomeController : Controller
{
public HomeController(DatabaseContext context) =>
Startup.Database = context;
public class HomeController : ExtendedController
{
public HomeController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View();
[Route("")]
public IActionResult Index() =>
View();
[Route("Contacts")]
public IActionResult Contacts() =>
View(Startup.Database.Links.OrderBy(i => i.Order));
[Route("Contacts")]
public IActionResult Contacts() =>
View();
[Route("Projects")]
public IActionResult Projects() =>
View(Startup.Database.Projects.OrderByDescending(i => i.Id));
[Route("Projects")]
public IActionResult Projects() =>
View(new ProjectsViewModel(Database));
[Route("Error")]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error() =>
View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
[Route("Construction")]
public IActionResult Construction() =>
View();
[Route("Error")]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error() =>
View(new ErrorViewModel(Database) { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
@@ -1,37 +1,41 @@
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
using SelectPdf;
using System.Globalization;
namespace MyWebsite.Controllers
{
public class ResumeController : Controller
{
public ResumeController(DatabaseContext context) =>
Startup.Database = context;
public class ResumeController : ExtendedController
{
public ResumeController(DatabaseContext context) : base(context) { }
public IActionResult Index() =>
View(Startup.Database.Resume.Find(CultureInfo.CurrentUICulture.Name) ?? Startup.Database.Resume.Find("en-US"));
public IActionResult Index()
{
ResumeViewModel model = new ResumeViewModel(Database, CultureInfo.CurrentUICulture);
model.Resume.Content = model.Resume.Content
.Replace("%WEBSITE%", $"{Request.Scheme}://{Request.Host}/", true, CultureInfo.InvariantCulture)
.Replace("%PHONE_NUMBER%", "+7 (996) 929-19-69", true, CultureInfo.InvariantCulture)
.Replace("%EMAIL%", Database.Links.Find("outlook").Username, true, CultureInfo.InvariantCulture);
return View(model);
}
public IActionResult Print() =>
View(Startup.Database.Resume.Find(CultureInfo.CurrentUICulture.Name) ?? Startup.Database.Resume.Find("en-US"));
public IActionResult Print() =>
Index();
public IActionResult Download()
{
HtmlToPdf converter = new HtmlToPdf();
converter.Options.MarginTop = 25;
converter.Options.MarginBottom = 25;
converter.Options.MarginLeft = 25;
converter.Options.MarginRight = 25;
PdfDocument doc = converter.ConvertHtmlString(
$@"<html style=""margin-top: -50px"">
{(Startup.Database.Resume.Find(CultureInfo.CurrentUICulture.Name) ?? Startup.Database.Resume.Find("en-US")).Content}
public IActionResult Download()
{
HtmlToPdf converter = new HtmlToPdf();
converter.Options.MarginTop = 25;
converter.Options.MarginBottom = 25;
converter.Options.MarginLeft = 25;
converter.Options.MarginRight = 25;
<link rel=""stylesheet"" type=""text/css"" href=""{Request.Scheme}://{Request.Host}/css/Style.css"" />
</html>");
byte[] data = doc.Save();
doc.Close();
return File(data, "application/pdf", "[Michael Gordeev] CV.pdf");
}
}
PdfDocument doc = converter.ConvertUrl($"{Request.Scheme}://{Request.Host}/Resume/Print#preview");
byte[] data = doc.Save();
doc.Close();
return File(data, "application/pdf", "[Michael Gordeev] CV.pdf");
}
}
}
-144
View File
@@ -1,144 +0,0 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace MyWebsite.Helpers
{
public class Cryptography
{
public static string ComputeHash(string plainText, string hashAlgorithm, byte[] saltBytes)
{
// If salt is not specified, generate it.
if (saltBytes == null)
{
// Define min and max salt sizes.
int minSaltSize = 4;
int maxSaltSize = 8;
// Generate a random number for the size of the salt.
Random random = new Random();
int saltSize = random.Next(minSaltSize, maxSaltSize);
// Allocate a byte array, which will hold the salt.
saltBytes = new byte[saltSize];
// Initialize a random number generator.
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
// Fill the salt with cryptographically strong byte values.
rng.GetNonZeroBytes(saltBytes);
}
// Convert plain text into a byte array.
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
// Allocate array, which will hold plain text and salt.
byte[] plainTextWithSaltBytes =
new byte[plainTextBytes.Length + saltBytes.Length];
// Copy plain text bytes into resulting array.
for (int i = 0; i < plainTextBytes.Length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];
// Append salt bytes to the resulting array.
for (int i = 0; i < saltBytes.Length; i++)
plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
HashAlgorithm hash;
// Make sure hashing algorithm name is specified.
if (hashAlgorithm == null)
hashAlgorithm = "";
// Initialize appropriate hashing algorithm class.
switch (hashAlgorithm.ToUpper())
{
case "SHA384":
hash = new SHA384Managed();
break;
case "SHA512":
hash = new SHA512Managed();
break;
default:
hash = new MD5CryptoServiceProvider();
break;
}
// Compute hash value of our plain text with appended salt.
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
// Create array which will hold hash and original salt bytes.
byte[] hashWithSaltBytes = new byte[hashBytes.Length +
saltBytes.Length];
// Copy hash bytes into resulting array.
for (int i = 0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];
// Append salt bytes to the result.
for (int i = 0; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
// Convert result into a base64-encoded string.
string hashValue = Convert.ToBase64String(hashWithSaltBytes);
// Return the result.
return hashValue;
}
public static bool VerifyHash(string plainText, string hashAlgorithm, string hashValue)
{
// Convert base64-encoded hash value into a byte array.
byte[] hashWithSaltBytes = Convert.FromBase64String(hashValue);
// We must know size of hash (without salt).
int hashSizeInBits, hashSizeInBytes;
// Make sure that hashing algorithm name is specified.
if (hashAlgorithm == null)
hashAlgorithm = "";
// Size of hash is based on the specified algorithm.
switch (hashAlgorithm.ToUpper())
{
case "SHA384":
hashSizeInBits = 384;
break;
case "SHA512":
hashSizeInBits = 512;
break;
default: // Must be MD5
hashSizeInBits = 128;
break;
}
// Convert size of hash from bits to bytes.
hashSizeInBytes = hashSizeInBits / 8;
// Make sure that the specified hash value is long enough.
if (hashWithSaltBytes.Length < hashSizeInBytes)
return false;
// Allocate array to hold original salt bytes retrieved from hash.
byte[] saltBytes = new byte[hashWithSaltBytes.Length - hashSizeInBytes];
// Copy salt from the end of the hash to the new array.
for (int i = 0; i < saltBytes.Length; i++)
saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];
// Compute a new hash string.
string expectedHashString = ComputeHash(plainText, hashAlgorithm, saltBytes);
// If the computed hash matches the specified hash,
// the plain text value must be correct.
return (hashValue == expectedHashString);
}
}
}
+83
View File
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace MyWebsite.Helpers
{
/// <summary>
/// SHA512 Encryption algorithm
/// </summary>
public static class Encryptor
{
/// <summary>
/// Computes string hash string using SHA512 encryption
/// </summary>
/// <param name="plainText">String which hash is to be computed</param>
/// <param name="saltBytes">Salt bytes array (for hash verification)</param>
/// <returns>Encrypted hash string</returns>
public static string ComputeHash(string plainText, byte[] saltBytes = null)
{
// Generate salt in it's not provided.
if (saltBytes == null)
{
saltBytes = new byte[new Random().Next(4, 8)];
// Initialize a random number generator.
using RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
// Fill the salt with cryptographically strong byte values.
rng.GetNonZeroBytes(saltBytes);
}
// Initializing array, which will hold plain text and salt.
List<byte> plainBytes = new List<byte>();
plainBytes.AddRange(Encoding.UTF8.GetBytes(plainText));
plainBytes.AddRange(saltBytes);
// Initialize hashing algorithm class.
using HashAlgorithm encryptor = new SHA512Managed();
// Create array which will hold hash and original salt bytes.
List<byte> hash = new List<byte>();
// Compute hash value of our plain text with appended salt.
hash.AddRange(encryptor.ComputeHash(plainBytes.ToArray()));
hash.AddRange(saltBytes);
// Convert result into a base64-encoded string.
return Convert.ToBase64String(hash.ToArray());
}
/// <summary>
/// Verifies that <paramref name="hash"/> belongs to <paramref name="plainText"/> string
/// </summary>
/// <param name="plainText">Oringinal text string</param>
/// <param name="hash">SHA512 encrypted hash string</param>
/// <returns><see cref="true"/> if hash belong to <paramref name="plainText"/> otherwise returns <see cref="false"/></returns>
public static bool VerifyHash(string plainText, string hash)
{
// Convert base64-encoded hash value into a byte array.
byte[] hashBytes = Convert.FromBase64String(hash);
// Make sure that the specified hash value is long enough.
if (hashBytes.Length < 64)
return false;
// Allocate array to hold original salt bytes retrieved from hash.
byte[] saltBytes = new byte[hashBytes.Length - 64];
// Copy salt from the end of the hash to the new array.
for (int i = 0; i < saltBytes.Length; i++)
saltBytes[i] = hashBytes[64 + i];
// Compute a new hash string.
string expectedHashString = ComputeHash(plainText, saltBytes);
// If the computed hash matches the specified hash,
// the plain text value must be correct.
return hash == expectedHashString;
}
}
}
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Mvc;
using MyWebsite.ViewModels;
using MyWebsite.Models.Databases;
namespace MyWebsite.Controllers
{
public class ExtendedController : Controller
{
public DatabaseContext Database { get; }
public ExtendedController(DatabaseContext context) =>
Database = context;
new public IActionResult View() =>
base.View(new ViewModelBase(Database));
}
}
+11
View File
@@ -0,0 +1,11 @@
using System;
using System.Linq;
namespace MyWebsite
{
public static class Extensions
{
public static bool Belongs<T>(this T item, params T[] array) =>
array?.Contains(item) ?? false;
}
}
-31
View File
@@ -1,31 +0,0 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
namespace MyWebsite.Models
{
public class Badge
{
[Key]
[Column(TypeName = "varchar(10)")]
[DisplayName("ID")]
public string Name { get; set; }
[DisplayName("Caption")]
public string Description => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianDescription) ? RussianDescription : EnglishDescription;
[Required]
[Column(TypeName = "varchar(50)")]
[DisplayName("Caption (en)")]
public string EnglishDescription { get; set; }
[Column(TypeName = "varchar(50)")]
[DisplayName("Caption (ru)")]
public string RussianDescription { get; set; }
[Column(TypeName = "varchar(20)")]
[Required]
[DisplayName("Image name")]
public string Image { get; set; }
}
}
+31
View File
@@ -0,0 +1,31 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
namespace MyWebsite.Models
{
public class BadgeModel
{
[Key]
[Column(TypeName = "varchar(10)")]
[DisplayName("ID")]
public string Name { get; set; }
[DisplayName("Caption")]
public string Description => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianDescription) ? RussianDescription : EnglishDescription;
[Required]
[Column(TypeName = "varchar(50)")]
[DisplayName("Caption (en)")]
public string EnglishDescription { get; set; }
[Column(TypeName = "varchar(50)")]
[DisplayName("Caption (ru)")]
public string RussianDescription { get; set; }
[Column(TypeName = "varchar(20)")]
[Required]
[DisplayName("Image")]
public string Image { get; set; }
}
}
@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace MyWebsite.Models
{
public class CredentialModel
{
[Key]
[Required(ErrorMessage = "This field is required")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "This field is required")]
[DataType(DataType.Password)]
public string Password { get; set; }
}
}
+16
View File
@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyWebsite.Models
{
public class CustomData
{
[Key]
[Required]
[Column(TypeName = "varchar(255)")]
public string Key { get; set; }
[Required]
[Column(TypeName = "varchar(255)")]
public string Value { get; set; }
}
}
@@ -1,19 +0,0 @@
using Microsoft.EntityFrameworkCore;
namespace MyWebsite.Models
{
public class DatabaseContext : DbContext
{
public DbSet<Link> Links { get; set; }
public DbSet<Image> Gallery { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<LoginModel> Users { get; set; }
public DbSet<Badge> Badges { get; set; }
public DbSet<Resume> Resume { get; set; }
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
{
Database.EnsureCreated();
}
}
}
@@ -0,0 +1,18 @@
using Microsoft.EntityFrameworkCore;
namespace MyWebsite.Models.Databases
{
public class DatabaseContext : DbContext
{
public DbSet<LinkModel> Links { get; set; }
public DbSet<ImageModel> Gallery { get; set; }
public DbSet<ProjectModel> Projects { get; set; }
public DbSet<CredentialModel> Users { get; set; }
public DbSet<BadgeModel> Badges { get; set; }
public DbSet<ResumeModel> Resume { get; set; }
public DbSet<CustomData> CustomData { get; set; }
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) =>
Database.EnsureCreated();
}
}
@@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
using MyWebsite.Areas.API.Models;
namespace MyWebsite.Models.Databases
{
public class FoxTubeDatabaseContext : DbContext
{
public DbSet<MetricsPackage> Metrics { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<Changelog> Changelogs { get; set; }
public FoxTubeDatabaseContext(DbContextOptions<FoxTubeDatabaseContext> options) : base(options) =>
Database.EnsureCreated();
}
}
@@ -1,11 +0,0 @@
using System;
namespace MyWebsite.Models
{
public class ErrorViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
-34
View File
@@ -1,34 +0,0 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyWebsite.Models
{
public class Image
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[DisplayName("ID")]
public Guid Id { get; set; }
[Required]
[Column(TypeName = "varchar(100)")]
[DisplayName("Title")]
public string Title { get; set; }
[Required]
[Column(TypeName = "varchar(255)")]
[DisplayName("Description")]
public string Description { get; set; }
[Required]
[DisplayName("Created")]
public DateTime CreationDate { get; set; }
[Required]
[Column(TypeName = "varchar(20)")]
[DisplayName("File name")]
public string FileName { get; set; }
[Required]
[Column(TypeName = "varchar(10)")]
[DisplayName("Language")]
public string Language { get; set; }
}
}
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
namespace MyWebsite.Models
{
public class ImageModel
{
[Key]
[Column(TypeName = "varchar(20)")]
[DisplayName("File name")]
public string FileName { get; set; }
[DisplayName("Title")]
public string Title => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianTitle : EnglishTitle;
[Required]
[Column(TypeName = "varchar(100)")]
[DisplayName("Title (en)")]
public string EnglishTitle { get; set; }
[Column(TypeName = "varchar(100)")]
[DisplayName("Title (ru)")]
public string RussianTitle { get; set; }
[DisplayName("Description")]
public string Description => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianDescription) ? RussianDescription : EnglishDescription;
[Required]
[Column(TypeName = "text")]
[DisplayName("Description (en)")]
public string EnglishDescription { get; set; }
[Column(TypeName = "text")]
[DisplayName("Description (ru)")]
public string RussianDescription { get; set; }
[Required]
[DisplayName("Created")]
public DateTime CreationDate { get; set; }
}
}
-45
View File
@@ -1,45 +0,0 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
namespace MyWebsite.Models
{
public class Link
{
[Key]
[Required]
[Column(TypeName = "varchar(20)")]
[DisplayName("Name")]
public string Name { get; set; }
[Required]
[DisplayName("Order")]
public int Order { get; set; }
[DisplayName("Title")]
public string Title => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianTitle : EnglishTitle;
[Required]
[Column(TypeName = "varchar(20)")]
[DisplayName("Title (en)")]
public string EnglishTitle { get; set; }
[Column(TypeName = "varchar(20)")]
[DisplayName("Title (ru)")]
public string RussianTitle { get; set; }
[Required]
[Column(TypeName = "varchar(50)")]
[DisplayName("Username")]
public string Username { get; set; }
[Required]
[Column(TypeName = "varchar(255)")]
[DisplayName("URL")]
public string Url { get; set; }
[Required]
[DisplayName("May contact")]
public bool CanContactMe { get; set; } = false;
[Required]
[DisplayName("Footer")]
public bool DisplayInFooter { get; set; } = false;
}
}
+46
View File
@@ -0,0 +1,46 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
namespace MyWebsite.Models
{
public class LinkModel
{
[Key]
[Required]
[Column(TypeName = "varchar(20)")]
[DisplayName("Name")]
public string Name { get; set; }
[Required]
[DisplayName("Order")]
public int Order { get; set; }
[DisplayName("Title")]
public string Title => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianTitle : EnglishTitle;
[Required]
[Column(TypeName = "varchar(20)")]
[DisplayName("Title (en)")]
public string EnglishTitle { get; set; }
[Column(TypeName = "varchar(20)")]
[DisplayName("Title (ru)")]
public string RussianTitle { get; set; }
[Required]
[Column(TypeName = "varchar(50)")]
[DisplayName("Username")]
public string Username { get; set; }
[Required]
[Column(TypeName = "varchar(255)")]
[DisplayName("URL")]
public Uri Url { get; set; }
[Required]
[DisplayName("May contact")]
public bool CanContactMe { get; set; } = false;
[Required]
[DisplayName("Footer")]
public bool DisplayInFooter { get; set; } = false;
}
}
-16
View File
@@ -1,16 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace MyWebsite.Models
{
public class LoginModel
{
[Key]
[Required(ErrorMessage = "This field is required")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "This field is required")]
[DataType(DataType.Password)]
public string Password { get; set; }
}
}
-26
View File
@@ -1,26 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyWebsite.Models
{
public class Project
{
[Key]
public decimal Id { get; set; }
[Required]
[Column(TypeName = "varchar(100)")]
public string Title { get; set; }
[Column(TypeName = "text")]
public string Description { get; set; }
[Column(TypeName = "varchar(50)")]
public string Link { get; set; }
[Column(TypeName = "varchar(50)")]
public string LinkCaption { get; set; }
[Column(TypeName = "varchar(100)")]
public string Badges { get; set; }
}
}
@@ -0,0 +1,57 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
#pragma warning disable CA1724
namespace MyWebsite.Models
{
public class ProjectModel
{
[Key]
[Required]
[DisplayName("ID (Order)")]
public decimal Id { get; set; }
[DisplayName("Title")]
public string Title => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianTitle : EnglishTitle;
[Required]
[Column(TypeName = "varchar(100)")]
[DisplayName("Title (en)")]
public string EnglishTitle { get; set; }
[Column(TypeName = "varchar(100)")]
[DisplayName("Title (ru)")]
public string RussianTitle { get; set; }
[DisplayName("Caption")]
public string Description => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianDescription) ? RussianDescription : EnglishDescription;
[Required]
[Column(TypeName = "text")]
[DisplayName("Description (en)")]
public string EnglishDescription { get; set; }
[Column(TypeName = "text")]
[DisplayName("Description (ru)")]
public string RussianDescription { get; set; }
[Column(TypeName = "varchar(50)")]
[DisplayName("Link")]
public string Link { get; set; }
[DisplayName("Link text caption")]
public string LinkCaption => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ru" && !string.IsNullOrWhiteSpace(RussianTitle) ? RussianLinkCaption : EnglishLinkCaption;
[Required]
[Column(TypeName = "varchar(50)")]
[DisplayName("Link text caption (en)")]
public string EnglishLinkCaption { get; set; }
[Column(TypeName = "varchar(50)")]
[DisplayName("Link text caption (ru)")]
public string RussianLinkCaption { get; set; }
[Column(TypeName = "varchar(100)")]
[DisplayName("Badges")]
public string Badges { get; set; }
}
}
-23
View File
@@ -1,23 +0,0 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyWebsite.Models
{
public class Resume
{
[Key]
[Required]
[Column(TypeName = "varchar(10)")]
[DisplayName("Language")]
public string Language { get; set; }
[Required]
[Column(TypeName = "text")]
[DisplayName("Content")]
public string Content { get; set; }
[DisplayName("Last chagnge")]
public DateTime LastUpdate { get; set; }
}
}
+23
View File
@@ -0,0 +1,23 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyWebsite.Models
{
public class ResumeModel
{
[Key]
[Required]
[Column(TypeName = "varchar(10)")]
[DisplayName("Language")]
public string Language { get; set; }
[Required]
[Column(TypeName = "text")]
[DisplayName("Content")]
public string Content { get; set; }
[DisplayName("Last chagnge")]
public DateTime LastUpdate { get; set; }
}
}
+5 -1
View File
@@ -16,6 +16,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.0" />
@@ -24,7 +28,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Areas\Admin\Views\Shared\" />
<Folder Include="wwwroot\assets\FoxTube\Screenshots\" />
</ItemGroup>
+2 -4
View File
@@ -4,11 +4,9 @@
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Controller</Controller_SelectedScaffolderCategoryPath>
<WebStackScaffolding_ControllerDialogWidth>600</WebStackScaffolding_ControllerDialogWidth>
<WebStackScaffolding_IsLayoutPageSelected>True</WebStackScaffolding_IsLayoutPageSelected>
<WebStackScaffolding_IsPartialViewSelected>True</WebStackScaffolding_IsPartialViewSelected>
<WebStackScaffolding_IsLayoutPageSelected>False</WebStackScaffolding_IsLayoutPageSelected>
<WebStackScaffolding_IsPartialViewSelected>False</WebStackScaffolding_IsPartialViewSelected>
<WebStackScaffolding_IsReferencingScriptLibrariesSelected>False</WebStackScaffolding_IsReferencingScriptLibrariesSelected>
<WebStackScaffolding_LayoutPageFile>
</WebStackScaffolding_LayoutPageFile>
<WebStackScaffolding_IsAsyncSelected>False</WebStackScaffolding_IsAsyncSelected>
<WebStackScaffolding_ViewDialogWidth>600</WebStackScaffolding_ViewDialogWidth>
</PropertyGroup>
+12 -14
View File
@@ -3,18 +3,16 @@ using Microsoft.Extensions.Hosting;
namespace MyWebsite
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static class Program
{
public static void Main(string[] args) =>
CreateHostBuilder(args).Build().Run();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
+63 -58
View File
@@ -4,76 +4,81 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using MyWebsite.Models;
using MyWebsite.Areas.API.Models;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using MyWebsite.Models.Databases;
namespace MyWebsite
{
public class Startup
{
public static DatabaseContext Database { get; set; }
// TODO: Add reordering for contact links
// TODO: Complete project admin page
// TODO: Complete artworks admin page
// TODO: FoxTube API admin page
// TODO: Complete homepage
// TODO: Complete FoxTube page
// TODO: Add localization system
// TODO: Rid of JavaScript (use Blazor)
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MainDB")));
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<FoxTubeDatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("FoxTubeDB")));
services.AddDbContext<FoxTubeDatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("FoxTubeAPI")));
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
options.LoginPath = new PathString("/Admin/Login"));
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
options.LoginPath = new PathString("/Admin/Login"));
services.AddControllersWithViews();
}
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute(
name: "projects",
areaName: "Projects",
pattern: "Projects/{controller=Projects}/{action=Index}/{id?}");
endpoints.MapAreaControllerRoute(
name: "admin",
areaName: "Admin",
pattern: "Admin/{controller}/{action=Index}/{id?}");
endpoints.MapAreaControllerRoute(
name: "api",
areaName: "API",
pattern: "API/{controller}/{action}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute(
name: "projects",
areaName: "Projects",
pattern: "Projects/{controller=Projects}/{action=Index}/{id?}");
endpoints.MapAreaControllerRoute(
name: "admin",
areaName: "Admin",
pattern: "Admin/{controller}/{action=Index}/{id?}");
endpoints.MapAreaControllerRoute(
name: "api",
areaName: "API",
pattern: "API/{controller}/{action}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
@@ -0,0 +1,32 @@
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using System.Collections.Generic;
using System.Linq;
namespace MyWebsite.ViewModels
{
public class ArtworkViewModel : ViewModelBase
{
public List<ImageModel> Images { get; }
public string CurrentId { get; set; }
public ImageModel Current => Images.FirstOrDefault(i => i.FileName == CurrentId);
public string Next { get; }
public string Previous { get; }
public ArtworkViewModel(DatabaseContext context) : base(context) =>
Images = context.Gallery.OrderByDescending(i => i.CreationDate).ToList();
public ArtworkViewModel(DatabaseContext context, string id) : base(context)
{
Images = context.Gallery.OrderByDescending(i => i.CreationDate).ToList();
CurrentId = id;
if (Images.IndexOf(Current) != Images.Count - 1)
Previous = Images[Images.IndexOf(Current) + 1].FileName;
if (Images.IndexOf(Current) != 0)
Next = Images[Images.IndexOf(Current) - 1].FileName;
}
}
}
@@ -0,0 +1,15 @@
using MyWebsite.Models;
using MyWebsite.Models.Databases;
namespace MyWebsite.ViewModels
{
public class CredentialViewModel : ViewModelBase
{
public CredentialModel Credential { get; set; }
public CredentialViewModel(DatabaseContext context) : base(context) { }
public CredentialViewModel() : base(null) { }
public CredentialViewModel(DatabaseContext context, CredentialViewModel model) : base(context) =>
Credential = model?.Credential;
}
}
@@ -0,0 +1,12 @@
using MyWebsite.Models.Databases;
namespace MyWebsite.ViewModels
{
public class ErrorViewModel : ViewModelBase
{
public ErrorViewModel(DatabaseContext context) : base(context) { }
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
@@ -0,0 +1,18 @@
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using System.Collections.Generic;
using System.Linq;
namespace MyWebsite.ViewModels
{
public class ProjectsViewModel : ViewModelBase
{
public IEnumerable<ProjectModel> Projects { get; }
public IEnumerable<BadgeModel> Badges { get; }
public ProjectsViewModel(DatabaseContext context) : base(context)
{
Projects = context.Projects.OrderByDescending(i => i.Id).ToList();
Badges = context.Badges.ToList();
}
}
}
@@ -0,0 +1,13 @@
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using System.Globalization;
namespace MyWebsite.ViewModels
{
public class ResumeViewModel : ViewModelBase
{
public ResumeModel Resume { get; }
public ResumeViewModel(DatabaseContext context, CultureInfo language) : base(context) =>
Resume = context.Resume.Find(language?.Name) ?? context.Resume.Find("en-US");
}
}
@@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Linq;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
namespace MyWebsite.ViewModels
{
public class ViewModelBase
{
public IEnumerable<LinkModel> Links { get; }
public ViewModelBase(DatabaseContext context) =>
Links = context?.Links.ToList();
}
}
+24 -16
View File
@@ -1,24 +1,32 @@
@{
ViewData["Title"] = "Admin panel";
@{
ViewData["Title"] = "Admin panel";
}
<header>
<h1>Administration</h1>
<h1>Administration</h1>
</header>
<article>
<p class="admin-menu">
<a asp-action="FoxTube" class="comment">// FoxTube API</a><br />
<a asp-action="Gallery" class="comment">// Artworks</a><br />
<a asp-action="Projects" class="comment">// Projects</a><br />
<a asp-action="Badges" class="comment">// Badges</a><br />
<a asp-action="Resume" class="comment">// Resume</a><br />
<a asp-action="Contacts" class="comment">// Contact links</a><br />
</p>
<p>
<a asp-action="Logout" class="logout">&#xE875; Logout</a>
</p>
<p class="admin-menu">
<a asp-action="Gallery" class="comment">// Artworks</a><br />
<a asp-action="Projects" class="comment">// Projects</a><br />
<a asp-action="Badges" class="comment">// Badges</a><br />
<a asp-action="Resume" class="comment">// Resume</a><br />
<a asp-action="Contacts" class="comment">// Contact links</a>
</p>
<p class="admin-menu">
<a asp-action="FoxTube" class="comment">// FoxTube API</a><br />
<a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a>
</p>
<p>
<a asp-action="Credential" class="logout">// Change credential information</a>
</p>
<p>
<a asp-action="Logout" class="logout">&#xE875; Logout</a>
</p>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@section Imports
{
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
}
+21 -18
View File
@@ -1,27 +1,30 @@
@model LoginModel;
@model CredentialViewModel
@{
ViewData["Title"] = "Login";
ViewData["Title"] = "Login";
}
<header>
<h1>Administration panel - Login</h1>
<h1>Administration panel - Login</h1>
</header>
<article>
<form asp-action="Login" asp-antiforgery="true">
<div asp-validation-summary="All" class="validation-error"></div>
<div>
<label asp-for="Email">E-mail</label>
<input type="email" asp-for="Email" />
<span asp-validation-for="Email"></span>
</div>
<div>
<label asp-for="Password">Password</label>
<input type="password" asp-for="Password" />
<span asp-validation-for="Password"></span>
</div>
<input class="btn" style="margin-top:10px" type="submit" value="Login" />
</form>
<form asp-action="Login" asp-antiforgery="true">
<div asp-validation-summary="All" class="validation-error"></div>
<div>
<label asp-for="Credential.Email">E-mail</label>
<input type="email" asp-for="Credential.Email" />
<span asp-validation-for="Credential.Email"></span>
</div>
<div>
<label asp-for="Credential.Password">Password</label>
<input type="password" asp-for="Credential.Password" />
<span asp-validation-for="Credential.Password"></span>
</div>
<input class="btn" style="margin-top:10px" type="submit" value="Login" />
</form>
</article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@section Imports
{
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
}
@@ -1,40 +1,53 @@
@model Image
@model ArtworkViewModel
@{
ViewData["Title"] = Model.Title;
ViewData["Title"] = Model.Current?.Title;
}
<header>
<p>
&#xE760; <a asp-action="Index" asp-controller="Gallery">Back to gallery</a>
</p>
<p>
@if (Model.Previous != null)
{
@:&#xE760;
<a asp-action="Details" asp-route-id="@Model.Previous">Previous</a>
@:|
}
&#xE8B9; <a asp-action="Index" asp-controller="Gallery">All artworks</a>
@if (Model.Next != null)
{
@:| &#xE761;
<a asp-action="Details" asp-route-id="@Model.Next">Next</a>
}
</p>
</header>
<article class="image-overview-block">
<img src="~/images/Gallery/@Model.FileName" onclick="ToggleImageSize();" id="image" />
<img src="~/images/Gallery/@Model.Current?.FileName" onclick="ToggleImageSize();" id="image" />
<div>
<h1>@Model.Title</h1>
<span>Creation date: @Model.CreationDate.ToShortDateString()</span>
<p>
@Html.Raw(Model.Description)
</p>
</div>
<div>
<h1>@Model.Current?.Title</h1>
<span>Creation date: @Model.Current?.CreationDate.ToShortDateString()</span>
<p>
@Html.Raw(Model.Current?.Description)
</p>
</div>
</article>
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
@section Imports {
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<script type="text/javascript">
function ToggleImageSize()
{
var image = document.getElementById("image");
<script type="text/javascript">
function ToggleImageSize()
{
var image = document.getElementById("image");
if (image.style.cursor == "zoom-out")
image.style = "";
else
{
image.style.maxHeight = "none";
image.style.maxWidth = "none";
image.style.cursor = "zoom-out";
}
}
</script>
if (image.style.cursor == "zoom-out")
image.style = "";
else
{
image.style.maxHeight = "none";
image.style.maxWidth = "none";
image.style.cursor = "zoom-out";
}
}
</script>
}
+16 -13
View File
@@ -1,22 +1,25 @@
@model IEnumerable<Image>
@model ArtworkViewModel
@{
ViewData["Title"] = "My artworks";
ViewData["Title"] = "My artworks";
}
<header>
<h1>My arts</h1>
<h1>My arts</h1>
</header>
<article class="gallery">
@if (Model.Count() > 0)
foreach (Image image in Model.OrderByDescending(i => i.CreationDate))
{
<a asp-action="Details" asp-route-id="@image.FileName"><img title="@image.Title" src="~/images/Gallery/@image.FileName" /></a>
}
else
{
<p class="comment">// No content available</p>
}
@if (Model.Images.Count > 0)
foreach (ImageModel image in Model.Images)
{
<a asp-action="Details" asp-route-id="@image.FileName"><img title="@image.Title" src="~/images/Gallery/@image.FileName" /></a>
}
else
{
<p class="comment">// No content available</p>
}
</article>
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
@section Imports
{
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
}
+11 -8
View File
@@ -1,18 +1,21 @@
@model Resume
@model ResumeViewModel
@{
ViewData["Title"] = "My resume";
ViewData["Title"] = "My resume";
}
<header>
<h1>My resume</h1>
<p>Last update: @Model?.LastUpdate</p>
<h1>My resume</h1>
<p>Last update: @Model?.Resume.LastUpdate</p>
<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="Download">// Download CV (.pdf) &#xE118;</a><br />
<a class="comment" asp-action="Print">// Print CV &#xE749;</a>
</header>
<article>
@Html.Raw(Model?.Content)
@Html.Raw(Model?.Resume.Content)
</article>
<partial name="~/Views/Shared/ContactsBlock.cshtml" />
@section Footer
{
<partial name="~/Views/Shared/ContactsBlock.cshtml" />
}
+34 -22
View File
@@ -1,28 +1,40 @@
@model Resume
@model ResumeViewModel
@{
Layout = null;
ViewData["Title"] = "Print";
Layout = null;
}
@Html.Raw(Model.Content)
<!DOCTYPE html>
<html>
<head>
<title>Michael (Mikhail) Gordeev - Resume</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/Style.css" />
<style>
html {
overflow: initial;
}
body {
display: initial;
margin: initial;
height: initial;
}
</style>
<style>
html
{
overflow: initial;
}
<script type="text/javascript">
window.print();
window.onfocus = function ()
{
window.location = "/Resume";
}
</script>
body
{
display: initial;
margin: initial;
height: initial;
}
</style>
<script type="text/javascript">
window.onafterprint = function ()
{
window.location = "/Resume";
}
if (window.location.hash != "#preview")
window.print();
</script>
</head>
<body>
@Html.Raw(Model.Resume.Content)
</body>
</html>
@@ -0,0 +1,118 @@
@model ViewModelBase
@using Microsoft.AspNetCore.Http
@{
Layout = null;
HttpRequest request = ViewContext.HttpContext.Request;
}
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Ready - XFox111</title>
<base href="~/assets/Construction/" />
<link rel="stylesheet" type="text/css" href="Construction.css">
<script type="text/javascript" src="Construction.js"></script>
<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:locale" content="en_US">
<meta property="og:image" content="//xfox111.net/pics/me.jpg">
<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 onload="Load();">
<nav>
<div>
<span>Output</span>
<div></div>
<div>
<div class="btn hide" title="Window Position"></div>
<div class="btn maximize" title="Maximize"></div>
<div class="btn close" title="Close"></div>
</div>
</div>
<div>
<span>Show output from:</span>
<select>
<option selected>Build</option>
</select>
<img draggable="false" src="Images/tools.png" />
</div>
</nav>
<main id="output">
<p>1>------ Build started: Project: xfox111.net, Configuration: Any CPU ------</p>
<p>1> xfox111.net -> @request.Scheme://@request.Host/MyWebsite.dll </p>
<p>2>------ Deploy started: Project: xfox111.net, Configuration: Any CPU ------</p>
<p>Updating the layout...</p>
<p>Copying files: Total &#x3C;1 mb to layout...</p>
<p>Checking whether required frameworks are installed...</p>
<p>Registering the website to run from layout...</p>
<br />
<p class="err">========== Deployment failed ==========</p>
<p>Error message:</p>
<p class="err">========== Site is under construction ==========</p>
<br />
<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/‾\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;_&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;╭-╮\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;----╰-╯&nbsp;\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;///&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;///&nbsp;&nbsp;/||&nbsp;&nbsp;&nbsp;&nbsp;\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;>&nbsp;&nbsp;/&nbsp;||&nbsp;&nbsp;___\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;/&nbsp;^&nbsp;\՟||_/&nbsp;&nbsp;&nbsp;\\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;/&nbsp;/&nbsp;|&nbsp;\&nbsp;-/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;/&nbsp;/&nbsp;&nbsp;|&nbsp;|&nbsp;/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\\&nbsp;&nbsp;\<br />
&nbsp;&nbsp;/&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp;/_/&nbsp;&nbsp;&nbsp;|_|/_________\\&nbsp;&nbsp;\<br />
&nbsp;/&nbsp;&nbsp;/_________________________\&nbsp;&nbsp;\<br />
|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br />
&nbsp;‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
</p>
<br />
<p>It will be done soon</p>
<p>For now you can check my other links:</p>
<br />
@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>
}
<br />
<p>========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========</p>
<p>========== Deploy: 0 succeeded, 1 failed, 0 skipped ==========</p>
</main>
<footer>
<div class="status-bar-btn btn task" title="Background tasks"></div>
<span id="status">Ready</span>
<div class="git-btn btn push" title="4 unpushed commits">
<span>4</span>
</div>
<div class="git-btn btn commit" title="11 changes">
<span>11</span>
</div>
<div class="git-btn btn git" title="Path: //xfox111.net">
<span>xfox111</span>
</div>
<div class="git-btn btn branch" title="Name: master">
<span>master</span>
</div>
<div class="status-bar-btn btn notification" title="No Notifications"></div>
</footer>
</body>
</html>
@@ -1,23 +1,23 @@
@model IEnumerable<Link>
@model ViewModelBase
@{
ViewData["Title"] = "Contact info";
ViewData["Title"] = "Contact info";
}
<header>
<h1>Contact information</h1>
<h1>Contact information</h1>
</header>
<article>
<p>
@foreach (Link link in Model.Where(i => i.CanContactMe))
{
<a class="socicon-@(link.Name)"></a> @(link.Title) <a href="@(link.Url)" target="_blank">@(link.Username)</a><br />
}
</p>
<p>
@foreach (Link link in Model.Where(i => !i.CanContactMe))
{
<a class="socicon-@(link.Name)"></a> @(link.Title) <a href="@(link.Url)" target="_blank">@(link.Username)</a><br />
}
</p>
<p>
@foreach (LinkModel link in Model.Links.Where(i => i.CanContactMe))
{
<a class="socicon-@(link.Name)"></a> @(link.Title) <a href="@(link.Url)" target="_blank">@(link.Username)</a><br />
}
</p>
<p>
@foreach (LinkModel link in Model.Links.Where(i => !i.CanContactMe))
{
<a class="socicon-@(link.Name)"></a> @(link.Title) <a href="@(link.Url)" target="_blank">@(link.Username)</a><br />
}
</p>
</article>
@@ -1,35 +1,36 @@
@{
Link email = Startup.Database.Links.FirstOrDefault(i => i.Name == "outlook");
List<Link> links = Startup.Database.Links.Where(i => new string[] { "linkedin", "github", "twitter", "vkontakte" }.Contains(i.Name)).OrderBy(i => i.Order).ToList();
@model ViewModelBase
@{
LinkModel email = Model.Links.FirstOrDefault(i => i.Name == "outlook" || i.Name == "email");
List<LinkModel> links = Model.Links.Where(i => new string[] { "linkedin", "github", "twitter", "vkontakte" }.Contains(i.Name)).OrderBy(i => i.Order).ToList();
}
<div id="contact-me">
<var>if</var> (<var class="class">You</var>.InsterestedInMe)<br />
<div>
<var class="method">ContactMe</var>();<br />
</div>
<br />
<a class="comment">// All links are clickable</a><br />
<var>public void</var> <var class="method">ConatactMe</var>()<br />
{<br />
<div>
@if (email != null)
{
<span><var>string</var> email = <a href="@email.Url">@email.Username</a>;<br /></span>
}
<var class="class">Link</var>[] socialNetworks = <var>new</var> <var class="class">Link</var>[]<br />
{<br />
<div>
@foreach (Link link in links)
{
<span><img /><img /><var>new</var> <var class="class">Link</var>(<a>@(link.Title)</a>, <a target="_blank" href="@(link.Url)">https:@(link.Url)</a>),</span><br />
}
</div>
}<br />
</div>
}<br />
<br />
<a class="comment">// Copyright &copy;@(DateTime.Today.Year) Michael "XFox" Gordeev</a>
</div>
<footer>
<var>if</var> (<var class="class">You</var>.InsterestedInMe)<br />
<div>
<var class="method">ContactMe</var>();<br />
</div>
<br />
<a class="comment">// All links are clickable</a><br />
<var>public void</var> <var class="method">ConatactMe</var>()<br />
{<br />
<div>
@if (email != null)
{
<span><var>string</var> email = <a href="@email.Url">@email.Username</a>;<br /></span>
}
<var class="class">Link</var>[] socialNetworks = <var>new</var> <var class="class">Link</var>[]<br />
{<br />
<div>
@foreach (LinkModel link in links)
{
<span><img /><img /><var>new</var> <var class="class">Link</var>(<a>@(link.Title)</a>, <a target="_blank" href="@(link.Url)">https:@(link.Url)</a>),</span><br />
}
</div>
}<br />
</div>
}<br />
<br />
<a class="comment">// Copyright &copy;@(DateTime.Today.Year) Michael "XFox" Gordeev</a>
</footer>
<link rel="stylesheet" type="text/css" href="~/css/ContactsBlock.css" />
@@ -1,19 +1,19 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
ViewData["Title"] = "Error";
}
<!-- TODO: Make Error page-->
<header>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
</header>
<article>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
</article>
@@ -1,13 +1,13 @@
@{
ViewData["Title"] = "Home Page";
ViewData["Title"] = "Home Page";
}
<header>
<h1>Hello, World!</h1>
<h1>Hello, World!</h1>
</header>
<article>
<p>
Homepage
</p>
<p>
Homepage
</p>
</article>
@@ -1,36 +1,45 @@
@model IEnumerable<Project>
@model ProjectsViewModel
@{
ViewData["Title"] = "My projects";
List<Badge> badges = Startup.Database.Badges.ToList();
ViewData["Title"] = "My projects";
}
<header>
<div>
<h1>My projects</h1>
<h3>Here is presented the most of projects I worked on</h3>
</div>
<iframe src="//githubbadge.appspot.com/xfox111" class="github-stats" frameborder="0"></iframe>
<div>
<h1>My projects</h1>
<h3>Here is presented the most of projects I worked on</h3>
</div>
<iframe src="//githubbadge.appspot.com/xfox111" class="github-stats" frameborder="0"></iframe>
</header>
<article>
@foreach (Project project in Model)
{
<div class="project-item">
<div>
<h1>@project.Title</h1>
<p class="description">
@Html.Raw(project.Description)
</p>
<a href="@(project.Link)" target="_blank">@project.LinkCaption</a>
</div>
<div class="badge-placeholder">
@foreach (string badge in project.Badges.Split(','))
{
<div style="background-image: url(../images/Badges/@(badges.FirstOrDefault(i => i.Name == badge)?.Image).png)" title="@(badges.FirstOrDefault(i => i.Name == badge)?.Description)"></div>
}
</div>
</div>
}
@if (Model.Projects.Count() > 0)
{
@foreach (ProjectModel project in Model.Projects)
{
<div class="project-item">
<div>
<h1>@project.Title</h1>
<p>
@Html.Raw(project.Description)
</p>
<a href="@(project.Link)" target="_blank">@project.LinkCaption</a>
</div>
<div class="badge-placeholder">
@foreach (string b in project.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>
</div>
}
}
else
{
<p style="margin: 0px 40px" class="comment">// No content available</p>
}
</article>
<link rel="stylesheet" type="text/css" href="~/css/Projects.css" />
@section Imports {
<link rel="stylesheet" type="text/css" href="~/css/Projects.css" />
}
+73 -57
View File
@@ -1,70 +1,86 @@
<!DOCTYPE html>
@model ViewModelBase
<!DOCTYPE html>
<html>
<head>
<title>@ViewData["Title"] - XFox111.NET</title>
<link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" />
<head>
<title>@ViewData["Title"] - 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" href="https://d1azc1qln24ryf.cloudfront.net/114779/Socicon/style-cf.css?9ukd8d">
<link rel="stylesheet" type="text/css" href="~/css/Style.css" />
<link rel="stylesheet" type="text/css" href="~/css/Socicon.css" />
<script src="~/js/site.js" type="text/javascript"></script>
<script type="text/javascript">
function ToggleMenu() {
var menu = document.getElementById("main-menu");
<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>
if (menu.style.display == "none")
menu.style.display = "initial";
else
menu.style.display = "none";
}
</script>
<!-- TODO: Make OpenGraph tags more specific for each page -->
<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!" />
@RenderSection("Imports", false)
<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" />
@{
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 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>
<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" />
}
}
<menu type="toolbar" id="main-menu" style="display:none">
<li><a asp-area="" asp-controller="Home" asp-action="Index">AboutMe();</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>
<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>
<div>
<a asp-controller="Home" asp-action="SwitchLanguage" lang="ru">РУС &#xE12B;</a>
<a id="menu-toggle" onclick="ToggleMenu();">&#xE700;</a>
</div>
</nav>
<menu type="toolbar" id="main-menu" style="display:none">
<li><a asp-area="" asp-controller="Home" asp-action="Index">AboutMe();</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>
<main>
@RenderBody()
</main>
<div>
<a asp-controller="Home" asp-action="SwitchLanguage" lang="ru">РУС &#xE12B;</a>
<a id="menu-toggle" onclick="ToggleMenu();">&#xE700;</a>
</div>
</nav>
<footer>
<span class="comment">// Copyright &copy;@(DateTime.Today.Year) Michael "XFox" Gordeev</span>
<main>
@RenderBody()
</main>
<div>
@foreach (Link link in Startup.Database.Links.Where(i => i.DisplayInFooter).OrderBy(i => i.Order))
{
<a class="socicon-@(link.Name)" href="@(link.Url)" target="_blank" title="@(link.Title)"></a>
}
</div>
</footer>
</body>
@{
if (IsSectionDefined("Footer"))
RenderSection("Footer");
else
{
<footer>
<span class="comment">// Copyright &copy;@(DateTime.Today.Year) Michael "XFox" Gordeev</span>
<div>
@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>
}
</div>
</footer>
}
}
</body>
</html>
@@ -1,3 +1,4 @@
@using MyWebsite
@using MyWebsite.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using MyWebsite.ViewModels
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+2 -2
View File
@@ -1,3 +1,3 @@
@{
Layout = "_Layout";
}
Layout = "_Layout";
}
@@ -1,9 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
+13 -13
View File
@@ -1,14 +1,14 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=xfox111;Trusted_Connection=True;",
"FoxTubeAPI": "Server=(localdb)\\mssqllocaldb;Database=foxtube;Trusted_Connection=True;"
}
}
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MainDB": "Server=(localdb)\\mssqllocaldb;Database=xfox111;Trusted_Connection=True;",
"FoxTubeDB": "Server=(localdb)\\mssqllocaldb;Database=foxtube;Trusted_Connection=True;"
}
}
@@ -0,0 +1,225 @@
/* 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 */
nav
{
user-select: none;
position: fixed;
right: 0px;
left: 0px;
}
nav > div:first-child
{
display: grid;
grid-template-columns: auto 1fr auto;
background-color: #007acc;
height: 22px;
background-image: url('Images/strip.png');
}
nav > div:first-child span
{
background-color: inherit;
padding: 2px 5px;
}
nav > div:first-child > div:last-child
{
background-color: inherit;
padding: 0px 2px;
}
nav > div:first-child > div div
{
display: inline-block;
margin: 2px 0px;
height: 17px;
width: 17px;
text-align: center;
line-height: 17px;
font-size: 11pt;
}
nav > div:last-child
{
display: grid;
grid-template-columns: auto auto 1fr;
background-color: #2d2d30;
height: 26px;
}
nav > div:last-child span
{
margin: 0px 6px;
line-height: 26px;
text-align: center;
}
nav > div:last-child select
{
background-color: #333337;
color: white;
border: 1px solid #434346;
border-radius: 0px;
width: 300px;
height: 21px;
margin: 2px 0px;
}
nav > div:last-child img
{
height: 100%;
}
/* Body */
html
{
overflow: hidden;
}
body
{
background-color: #252526;
color: white;
font-size: 9pt;
margin: 0px;
font-family: 'Segoe UI Symbol';
overflow: auto;
display: grid;
height: 100vh;
}
main
{
font-family: Consolas;
margin: 50px 0px 50px 12px;
}
main p
{
margin: 0px;
display: none;
}
a
{
color: #569cd6;
}
a:visited
{
color: #569cd6;
}
.err
{
color: #e51400;
}
/* Footer */
footer
{
background-color: #007acc;
color: white;
height: 26px;
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding-right: 12px;
display: grid;
grid-template-columns: auto 1fr auto auto auto auto auto;
user-select: none;
}
.status-bar-btn
{
width: 26px;
height: 26px;
display: inline-block;
text-align: center;
font-size: 12pt;
}
.git-btn
{
padding: 0px 5px;
margin: 4px;
height: 17px;
min-width: 26px;
}
.btn
{
cursor: pointer;
}
.btn:hover
{
background-color: #ffffff33;
}
#status
{
margin: 0px 5px;
vertical-align: central;
line-height: 26px;
}
/* Button icons */
.push:before
{
content: '\e1fe';
}
.commit:before
{
content: '\e104';
}
.branch:before
{
content: '\E14B';
}
.git:before
{
content: '\1F4D3';
}
.notification:before
{
content: '\E1FA';
}
.task:before
{
content: '\23E5';
}
.hide:before
{
content: '\23F7';
}
.maximize:before
{
content: '\1F5D7';
}
.close:before
{
content: '\1F7A8';
}
@@ -0,0 +1,58 @@
async function Load()
{
var output = document.getElementById("output");
await delay(500);
output.children[0].style.display = 'block';
document.getElementById('status').innerText = "Build started...";
document.title = "Build started... - XFox111";
await delay(3000);
output.children[1].style.display = 'block';
await delay(1000);
document.getElementById('status').innerText = "Deploy started...";
document.title = "Deploy started... - XFox111";
output.children[2].style.display = 'block';
await delay(3000);
output.children[3].style.display = 'block';
await delay(5);
output.children[4].style.display = 'block';
await delay(5);
output.children[5].style.display = 'block';
await delay(5);
output.children[6].style.display = 'block';
await delay(2000);
document.getElementById('status').innerText = "Deploy failed.";
document.title = "Deploy failed... - XFox111";
output.children[8].style.display = 'block';
await delay(5);
output.children[9].style.display = 'block';
await delay(5);
output.children[10].style.display = 'block';
await delay(5);
output.children[12].style.display = 'block';
await delay(5);
output.children[14].style.display = 'block';
await delay(5);
output.children[15].style.display = 'block';
await delay(200);
var k = 17;
for (; k < output.children.length - 3; k++)
{
output.children[k].style.display = 'block';
await delay(5);
}
await delay(2000);
output.children[++k].style.display = 'block';
await delay(5);
output.children[++k].style.display = 'block';
document.title = "Site is under construction - XFox111";
document.getElementById('status').innerText = "Site is under construction";
}
const delay = ms => new Promise(res => setTimeout(res, ms));

Some files were not shown because too many files have changed in this diff Show More