Updated project
This commit is contained in:
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.29306.81
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyWebsite", "MyWebsite\MyWebsite.csproj", "{11629863-00FC-468B-B82A-78BFEB3C676E}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{11629863-00FC-468B-B82A-78BFEB3C676E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{11629863-00FC-468B-B82A-78BFEB3C676E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{11629863-00FC-468B-B82A-78BFEB3C676E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{11629863-00FC-468B-B82A-78BFEB3C676E}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {D886244E-2CDB-48D2-9E99-F449E46F405C}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
|
||||||
{
|
|
||||||
public class AboutController : Controller
|
|
||||||
{
|
|
||||||
public IActionResult Index() =>
|
|
||||||
View();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using MyWebsite.Models;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
|
||||||
{
|
|
||||||
public class AdminController : Controller
|
|
||||||
{
|
|
||||||
public IActionResult Index()
|
|
||||||
{
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IActionResult> Projects(int? id)
|
|
||||||
{
|
|
||||||
if (id == null)
|
|
||||||
return View();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Project[] projects = JsonConvert.DeserializeObject<Project[]>(await new HttpClient().GetStringAsync($"{Request.Scheme}://{Request.Host}/Projects.json"));
|
|
||||||
|
|
||||||
ViewData["Project"] = projects[id.Value];
|
|
||||||
ViewData["Badges"] = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "csharp", "C# Programming language" },
|
|
||||||
{ "dotnet", ".NET Framework" },
|
|
||||||
{ "xamarin", "Xamarin Framework" },
|
|
||||||
{ "unity", "Unity Engine" },
|
|
||||||
{ "uwp", "Universal Windows Platform" },
|
|
||||||
{ "windows", "Windows Platform" },
|
|
||||||
{ "win32", "Windows Platform (Win32)" },
|
|
||||||
{ "android", "Android Platform" },
|
|
||||||
{ "ios", "iOS Platform" }
|
|
||||||
};
|
|
||||||
|
|
||||||
return View("Views/Admin/ProjectEditor.cshtml");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using SelectPdf;
|
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
|
||||||
{
|
|
||||||
public class CVController : Controller
|
|
||||||
{
|
|
||||||
public IActionResult Index() =>
|
|
||||||
View();
|
|
||||||
|
|
||||||
public IActionResult Download()
|
|
||||||
{
|
|
||||||
HtmlToPdf converter = new HtmlToPdf();
|
|
||||||
converter.Options.MarginTop = 25;
|
|
||||||
converter.Options.MarginBottom = 25;
|
|
||||||
PdfDocument doc = converter.ConvertUrl($"{Request.Scheme}://{Request.Host}/CV/PrintCV?pdfPreview=true");
|
|
||||||
byte[] data = doc.Save();
|
|
||||||
doc.Close();
|
|
||||||
return File(data, "application/pdf", "[Michael Gordeev] CV.pdf");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult PrintCV(bool pdfPreview = false)
|
|
||||||
{
|
|
||||||
ViewData["pdfPreview"] = pdfPreview;
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using MyWebsite.Models;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
|
||||||
{
|
|
||||||
public class ContactsController : Controller
|
|
||||||
{
|
|
||||||
public IActionResult Index()
|
|
||||||
{
|
|
||||||
Dictionary<string, Link> links = JsonConvert.DeserializeObject<Dictionary<string, Link>>(new WebClient().DownloadString($"{Request.Scheme}://{Request.Host}/Links.json"));
|
|
||||||
ViewData["contactLinks"] = links.Values.ToList().FindAll(i => i.CanContactMe);
|
|
||||||
ViewData["otherLinks"] = links.Values.ToList().FindAll(i => !i.CanContactMe);
|
|
||||||
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using MyWebsite.Models;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
|
||||||
{
|
|
||||||
public class GalleryController : Controller
|
|
||||||
{
|
|
||||||
[HttpGet("Arts")]
|
|
||||||
public async Task<IActionResult> Index()
|
|
||||||
{
|
|
||||||
return View(JsonConvert.DeserializeObject<Image[]>(await new HttpClient().GetStringAsync($"{Request.Scheme}://{Request.Host}/Gallery.json")));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("Image")]
|
|
||||||
public async Task<IActionResult> Details(string id)
|
|
||||||
{
|
|
||||||
return View(JsonConvert.DeserializeObject<Image[]>(await new HttpClient().GetStringAsync($"https://{Request.Host}/Gallery.json")).First(i => i.FileName == id));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using MyWebsite.Models;
|
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
|
||||||
{
|
|
||||||
public class HomeController : Controller
|
|
||||||
{
|
|
||||||
// TODO: Add more specific OpenGraph meta tags
|
|
||||||
// TODO: Create custom error page
|
|
||||||
// TODO: Update Projects.json and Gallery.json (Add this site, BSI)
|
|
||||||
// TODO: Complete About page
|
|
||||||
// TODO: Localize application
|
|
||||||
// TODO: Make authorization system and ability to update website through GUI
|
|
||||||
// TODO: Consider a database connection
|
|
||||||
// TODO: Add more detailed parsing of artwork descriptions
|
|
||||||
public IActionResult Index() =>
|
|
||||||
View();
|
|
||||||
|
|
||||||
[HttpGet("Error")]
|
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
|
||||||
public IActionResult Error() =>
|
|
||||||
View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using MyWebsite.Models;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
|
||||||
{
|
|
||||||
public class ProjectsController : Controller
|
|
||||||
{
|
|
||||||
public async Task<IActionResult> Index()
|
|
||||||
{
|
|
||||||
Project[] projects = JsonConvert.DeserializeObject<Project[]>(await new HttpClient().GetStringAsync($"{Request.Scheme}://{Request.Host}/Projects.json"));
|
|
||||||
|
|
||||||
ViewData["Projects"] = projects;
|
|
||||||
ViewData["Badges"] = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "csharp", "C# Programming language" },
|
|
||||||
{ "dotnet", ".NET Framework" },
|
|
||||||
{ "xamarin", "Xamarin Framework" },
|
|
||||||
{ "unity", "Unity Engine" },
|
|
||||||
{ "uwp", "Universal Windows Platform" },
|
|
||||||
{ "windows", "Windows Platform" },
|
|
||||||
{ "win32", "Windows Platform (Win32)" },
|
|
||||||
{ "android", "Android Platform" },
|
|
||||||
{ "ios", "iOS Platform" }
|
|
||||||
};
|
|
||||||
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MyWebsite.Models
|
|
||||||
{
|
|
||||||
public class ErrorViewModel
|
|
||||||
{
|
|
||||||
public string RequestId { get; set; }
|
|
||||||
|
|
||||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace MyWebsite.Models
|
|
||||||
{
|
|
||||||
public class Image
|
|
||||||
{
|
|
||||||
public string Title { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
public DateTime CreationDate { get; set; }
|
|
||||||
public string FileName { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MyWebsite.Models
|
|
||||||
{
|
|
||||||
public class Link
|
|
||||||
{
|
|
||||||
public string Title { get; set; }
|
|
||||||
public string Socicon { get; set; }
|
|
||||||
public string Username { get; set; }
|
|
||||||
public string Url { get; set; }
|
|
||||||
public bool CanContactMe { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MyWebsite.Models
|
|
||||||
{
|
|
||||||
public class Project
|
|
||||||
{
|
|
||||||
public string Title { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
public string ImageName { get; set; }
|
|
||||||
public string Link { get; set; }
|
|
||||||
public string LinkCaption { get; set; }
|
|
||||||
public string[] Badges { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.10" />
|
|
||||||
<PackageReference Include="Select.HtmlToPdf.NetCore" Version="19.2.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="wwwroot\fonts\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
|
|
||||||
<Controller_SelectedScaffolderCategoryPath>root/Controller</Controller_SelectedScaffolderCategoryPath>
|
|
||||||
<WebStackScaffolding_ControllerDialogWidth>600</WebStackScaffolding_ControllerDialogWidth>
|
|
||||||
<WebStackScaffolding_IsLayoutPageSelected>True</WebStackScaffolding_IsLayoutPageSelected>
|
|
||||||
<WebStackScaffolding_IsPartialViewSelected>False</WebStackScaffolding_IsPartialViewSelected>
|
|
||||||
<WebStackScaffolding_IsReferencingScriptLibrariesSelected>False</WebStackScaffolding_IsReferencingScriptLibrariesSelected>
|
|
||||||
<WebStackScaffolding_IsAsyncSelected>False</WebStackScaffolding_IsAsyncSelected>
|
|
||||||
<WebStackScaffolding_ViewDialogWidth>600</WebStackScaffolding_ViewDialogWidth>
|
|
||||||
<NameOfLastUsedPublishProfile>FolderProfile</NameOfLastUsedPublishProfile>
|
|
||||||
<WebStackScaffolding_LayoutPageFile />
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using Microsoft.AspNetCore;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
|
|
||||||
namespace MyWebsite
|
|
||||||
{
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public static void Main(string[] args) =>
|
|
||||||
CreateWebHostBuilder(args).Build().Run();
|
|
||||||
|
|
||||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
|
||||||
WebHost.CreateDefaultBuilder(args)
|
|
||||||
.UseStartup<Startup>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
|
|
||||||
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
|
|
||||||
-->
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<WebPublishMethod>FileSystem</WebPublishMethod>
|
|
||||||
<PublishProvider>FileSystem</PublishProvider>
|
|
||||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
|
||||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
|
||||||
<SiteUrlToLaunchAfterPublish />
|
|
||||||
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
|
|
||||||
<ExcludeApp_Data>False</ExcludeApp_Data>
|
|
||||||
<ProjectGuid>11629863-00fc-468b-b82a-78bfeb3c676e</ProjectGuid>
|
|
||||||
<publishUrl>bin\Release\netcoreapp2.1\publish\</publishUrl>
|
|
||||||
<DeleteExistingFiles>False</DeleteExistingFiles>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
|
|
||||||
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
|
|
||||||
-->
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TimeStampOfAssociatedLegacyPublishXmlFile />
|
|
||||||
<_PublishTargetUrl>C:\Users\micha\Repositories\CVWebsite\MyWebsite\MyWebsite\bin\Release\netcoreapp2.1\publish\</_PublishTargetUrl>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:51478",
|
|
||||||
"sslPort": 44308
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MyWebsite": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace MyWebsite
|
|
||||||
{
|
|
||||||
public class Startup
|
|
||||||
{
|
|
||||||
public Startup(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
Configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IConfiguration Configuration { get; }
|
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.Configure<CookiePolicyOptions>(options =>
|
|
||||||
{
|
|
||||||
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
|
|
||||||
options.CheckConsentNeeded = context => true;
|
|
||||||
options.MinimumSameSitePolicy = SameSiteMode.None;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
|
||||||
{
|
|
||||||
if (env.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseDeveloperExceptionPage();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/Home/Error");
|
|
||||||
app.UseHsts();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
app.UseStaticFiles();
|
|
||||||
app.UseCookiePolicy();
|
|
||||||
|
|
||||||
app.UseMvc(routes =>
|
|
||||||
{
|
|
||||||
routes.MapRoute(
|
|
||||||
name: "default",
|
|
||||||
template: "{controller=Home}/{action=Index}/{id?}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Admin panel";
|
|
||||||
}
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>Administration</h1>
|
|
||||||
<h3><b style="color: red">Note:</b> Any write/read operations in this section will require an admin password</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article>
|
|
||||||
<form>
|
|
||||||
<input type="password" placeholder="Password" /><br />
|
|
||||||
<label for="area">Go to section:</label><br />
|
|
||||||
<div class="select-container">
|
|
||||||
<select id="area">
|
|
||||||
<option>Contact links</option>
|
|
||||||
<option>Artworks</option>
|
|
||||||
<option>Projects</option>
|
|
||||||
<option>Resume</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<button>Proceed</button>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/Admin.css" />
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Project editor";
|
|
||||||
Project project = ViewData["Project"] as Project;
|
|
||||||
}
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>Project editor</h1>
|
|
||||||
<h2>@project.Title</h2>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article>
|
|
||||||
<form>
|
|
||||||
<label for="langSelector">Language:</label>
|
|
||||||
<div class="select-container">
|
|
||||||
<select id="langSelector">
|
|
||||||
<option>English</option>
|
|
||||||
<option>Russian</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label for="title">Title:</label>
|
|
||||||
<input id="title" placeholder="Title" type="text" value="@project.Title"/>
|
|
||||||
|
|
||||||
<label for="description">Description:</label>
|
|
||||||
<textarea id="description" placeholder="Description" type="text">@project.Description</textarea>
|
|
||||||
|
|
||||||
<label for="thumbnail">Thumbnail:</label>
|
|
||||||
<input id="thumbnail" placeholder="Thumbnail" type="file" />
|
|
||||||
|
|
||||||
<button>Submit</button>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/Admin.css" />
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Projects list";
|
|
||||||
}
|
|
||||||
|
|
||||||
<h2>Projects</h2>
|
|
||||||
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
<article id="cv">
|
|
||||||
<h3>Michael (Mikhail) A. Gordeev</h3>
|
|
||||||
<hr>
|
|
||||||
<p>
|
|
||||||
Saint Petersburg,<br>
|
|
||||||
Russia<br>
|
|
||||||
Phone (Russia): +7 (996) 929-19-69<br>
|
|
||||||
Email: <a href="mailto:michael.xfox@outlook.com">michael.xfox@outlook.com</a><br>
|
|
||||||
Personal Website: <a href="//xfox111.net/" target="_blank">https://www.xfox111.net/</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4>Overall Summary:</h4>
|
|
||||||
<p>
|
|
||||||
Self- directed, detail-oriented, and professional C# programmer with more than 3 years of experience in designing, developing, analyzing, and implementing client-server, web and desktop-based applications using C# language. Expertise in system designing as well as in testing, debugging and modifying related application code. Capable of learning new programming languages and technologies and complete projects within specified deadlines. Possess excellent communication, problem-solving, documentation, analytical, and decision solving skills.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4>Summary of Skills:</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Thorough knowledge of C# programming concepts, OOP, SOILD, SDLC, testing and debugging methods, system design and implementation, database system, including DB2 and relational databases, program documentation, web and desktop application development
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Proficient in Object oriented programming such as C#, C++, Java as well as experience with .NET, ASP.NET Core, MVC, Xamarin and Unity frameworks
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Strong understanding of HTML, CSS, JavaScript, Visual Studio, design and architectural patterns
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Have a medium knowledge of Computer Vision and Machine learning
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Ability to analyze and understand complex problems, and generate appropriate technical solutions independently
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Effective communication and interpersonal skills with the ability to maintain good relations and share technical ideas with users or clients, technical and management staff
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Ability to work independently and within teams
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Strong organizational skills along with the ability to accomplish multiple tasks under extreme pressure, and meet specific deadlines
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Ability to grasp and apply new concepts quickly and stay updated with latest trends and technical advancements
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h4>TECHNICAL SKILLS:</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Knowledge of Microsoft Windows Operating System
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Technologies stack: .NET, ASP.NET MVC, Unity, Xamarin, Azure DevOps
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Languages: C#, JavaScript, Java, C++, Pascal, XAML, HTML, XML, CSS, SQL
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Platforms: UWP, Win32, Web, Android, iOS
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Software: Visual Studio, Adobe Photoshop, Illustrator, DaVinci Resolve, Sony Vegas Pro, FL Studio, Microsoft Office, 3Ds Max, Kompass 3D
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Database: MySQL, Microsoft Access
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
English knowledge: C1 (Advanced)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Very energetic and ready to take new challenges
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Ready to learn anything new
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Excellent communication and presentation skills
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h4>Work Experience:</h4>
|
|
||||||
<p>
|
|
||||||
C# Software Developer<br />
|
|
||||||
Technologies of Informational and Educational Systems Research Facility, <a title="Saint Petersburg State University of Telecommunications">SPbSUT</a>, St. Petersburg, Russia<br />
|
|
||||||
September 2019 – Present
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Developing a <a asp-controller="Projects" asp-action="Index">cross-platform application</a> for University's students and professors
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Supporting and maintaining existing .NET solutions
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Providing UI and processing solutions for internal research projects
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Consulting in Software development projects
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
C# Software Developer<br />
|
|
||||||
Self-Employed, Lipetsk, Russia<br />
|
|
||||||
2015 - September 2019
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Developing a <a asp-controller="Projects" asp-action="Index">YouTube client</a> application for Universal Windows Platform
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Comming out as a head of research group which was developing a <a asp-controller="Projects" asp-action="Index">Computer Vision based software</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Doing small freelance tasks for Unity projects
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
Phone salesman<br />
|
|
||||||
DOM.RU, Lipetsk, Russia<br />
|
|
||||||
2014 - 2015
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Promoting and selling company's infocommunication services to end-users
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h4>Education:</h4>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
Bachelor's Degree in Computer Science (11.03.02 Infocommunication Systems)<br>
|
|
||||||
State University of Telecommunications, Saint Petersburg, Russia<br>
|
|
||||||
2019 – (Present) – 2023
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
High School 3.5/4.0 GPA<br />
|
|
||||||
Lyceum №66, Lipetsk, Russia<br />
|
|
||||||
2017-2019
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
Secondary specialized education in Fortepiano<br />
|
|
||||||
Regional College of Arts, Lipetsk, Russia<br />
|
|
||||||
2007-2015
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<!--<hr>
|
|
||||||
<h4>Reference:</h4>
|
|
||||||
<p>On request.</p>-->
|
|
||||||
</article>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Curriculum vitae";
|
|
||||||
}
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>My resume</h1>
|
|
||||||
<a class="comment" asp-action="Download">// Download CV (.pdf) </a><br />
|
|
||||||
<a class="comment" asp-action="PrintCV" target="_blank">// Print CV </a>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<partial name="~/Views/CV/CVContent.cshtml" />
|
|
||||||
|
|
||||||
<partial name="~/Views/Shared/ContactsBlock.cshtml" />
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
@{
|
|
||||||
Layout = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>CV print preview page - XFox111.NET</title>
|
|
||||||
<link rel="shortcut icon" href="~/images/favicon.png" type="image/png" />
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/Style.css" />
|
|
||||||
<style type="text/css">
|
|
||||||
body { margin: 0px; }
|
|
||||||
</style>
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<partial name="~/Views/CV/CVContent.cshtml" />
|
|
||||||
|
|
||||||
@if (!(bool)ViewData["pdfPreview"])
|
|
||||||
{
|
|
||||||
<style type="text/css">
|
|
||||||
article {
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script type="text/javascript">
|
|
||||||
print();
|
|
||||||
close();
|
|
||||||
</script>
|
|
||||||
}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Contact info";
|
|
||||||
}
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>Contact information</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article>
|
|
||||||
<p>
|
|
||||||
@foreach(Link link in ViewData["contactLinks"] as List<Link>)
|
|
||||||
{
|
|
||||||
<a class="socicon-@(link.Socicon)"></a> @(link.Title) <a href="@(link.Url)" target="_blank">@(link.Username)</a><br />
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
@foreach (Link link in ViewData["otherLinks"] as List<Link>)
|
|
||||||
{
|
|
||||||
<a class="socicon-@(link.Socicon)"></a> @(link.Title) <a href="@(link.Url)" target="_blank">@(link.Username)</a><br />
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = Model.Title;
|
|
||||||
Image image = Model;
|
|
||||||
}
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<p> <a asp-action="Index" asp-controller="Gallery">Back to gallery</a></p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article class="image-overview-block">
|
|
||||||
<img src="~/images/Gallery/@image.FileName" onclick="ToggleImageSize();" id="image" />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h1>@image.Title</h1>
|
|
||||||
<span>Creation date: @image.CreationDate.ToShortDateString()</span>
|
|
||||||
<p>
|
|
||||||
@foreach(string line in image.Description.Split("\n"))
|
|
||||||
{
|
|
||||||
@line<br />
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "My artworks";
|
|
||||||
}
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>My arts</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article class="info-block gallery">
|
|
||||||
@foreach (Image image in Model)
|
|
||||||
{
|
|
||||||
<a asp-action="Details" asp-route-id="@image.FileName"><img title="@image.Title" src="~/images/Gallery/@image.FileName"/></a>
|
|
||||||
}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
@{
|
|
||||||
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>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article>
|
|
||||||
@foreach (Project p in ViewData["Projects"] as Project[])
|
|
||||||
{
|
|
||||||
<div class="project-item">
|
|
||||||
<div>
|
|
||||||
<h1>@p.Title</h1>
|
|
||||||
<p class="description">
|
|
||||||
@foreach(string line in p.Description.Split("\n"))
|
|
||||||
{
|
|
||||||
@line<br />
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
<a href="@(p.Link)" target="_blank">@p.LinkCaption</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
@foreach (string i in p.Badges)
|
|
||||||
{
|
|
||||||
<div class="badge @i" title="@((ViewData["Badges"] as dynamic)[i])"></div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/Projects.css" />
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
@using Newtonsoft.Json
|
|
||||||
@using System.Net
|
|
||||||
@{
|
|
||||||
Dictionary<string, Link> links = JsonConvert.DeserializeObject<Dictionary<string, Link>>(new WebClient().DownloadString($"{Context.Request.Scheme}://{Context.Request.Host}/Links.json"));
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="contact-me">
|
|
||||||
<code>
|
|
||||||
<var>if</var> (<var class="class">You</var>.InsterestedInMe)<br />
|
|
||||||
<span class="t1"></span><var class="method">ContactMe</var>();<br />
|
|
||||||
<br />
|
|
||||||
<a class="comment">// All links are clickable</a><br />
|
|
||||||
<var>public void</var> <var class="method">ConatactMe</var>()<br />
|
|
||||||
{<br />
|
|
||||||
<span class="t1"></span><var>string</var> email = <a class="string" href="mailto:michael.xfox@outlook.com">"mihcael.xfox@outlook.com"</a>;<br />
|
|
||||||
<span class="t1"></span><var class="class">Link</var>[] socialNetworks = <var>new</var> <var class="class">Link</var>[]<br />
|
|
||||||
<span class="t1"></span>{<br />
|
|
||||||
<span class="t2"></span><var>new</var> <var class="class">Link</var>(<a class="string">"LinkedIn"</a>, <a class="string" target="_blank" href="@(links["linkedin"].Url)">"https:@(links["linkedin"].Url)"</a>),<br />
|
|
||||||
<span class="t2"></span><var>new</var> <var class="class">Link</var>(<a class="string">"GitHub"</a>, <a class="string" target="_blank" href="@(links["github"].Url)">"https:@(links["github"].Url)"</a>),<br />
|
|
||||||
<span class="t2"></span><var>new</var> <var class="class">Link</var>(<a class="string">"Twitter"</a>, <a class="string" target="_blank" href="@(links["twitter"].Url)">"https:@(links["twitter"].Url)"</a>),<br />
|
|
||||||
<span class="t2"></span><var>new</var> <var class="class">Link</var>(<a class="string">"Vkontakte"</a>, <a class="string" target="_blank" href="@(links["vk"].Url)">"https:@(links["vk"].Url)"</a>)<br />
|
|
||||||
<span class="t1"></span>}<br />
|
|
||||||
}<br />
|
|
||||||
<br />
|
|
||||||
<a class="comment">// Copyright ©@(DateTime.Today.Year) Michael "XFox" Gordeev</a>
|
|
||||||
</code>
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/ContactsBlock.css" />
|
|
||||||
</div>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
@model ErrorViewModel
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Error";
|
|
||||||
}
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>Error.</h1>
|
|
||||||
<h2>An error occurred while processing your request.</h2>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article class="info-block">
|
|
||||||
@if (Model.ShowRequestId)
|
|
||||||
{
|
|
||||||
<p>
|
|
||||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
<h3>Development Mode</h3>
|
|
||||||
<p>
|
|
||||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Hello, World!";
|
|
||||||
}
|
|
||||||
|
|
||||||
<article>
|
|
||||||
<div class="block intro">
|
|
||||||
<div class="content">
|
|
||||||
<p style="font-family: 'Press Start 2P'">Hello, World!</p>
|
|
||||||
<p>My name is Michael<br/>
|
|
||||||
I'm a C# developer<br />
|
|
||||||
and this is my website.</p>
|
|
||||||
</div>
|
|
||||||
<img class="background" src="~/images/cvbg.png"/>
|
|
||||||
</div>
|
|
||||||
<div class="block sut">
|
|
||||||
<div class="content">
|
|
||||||
<p>Now I'm studying at The Bonch-Bruevich Saint-Petersburg State University of Telecommunications on Infocommunication Systems bachelor's degree</p>
|
|
||||||
</div>
|
|
||||||
<img class="background" src="~/images/sut.jpg"/>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/AboutMe.css" />
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Press+Start+2P&display=swap&subset=cyrillic">
|
|
||||||
|
|
||||||
<partial name="~/Views/Shared/ContactsBlock.cshtml"/>
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
@using Newtonsoft.Json
|
|
||||||
@using System.Net
|
|
||||||
@{
|
|
||||||
Dictionary<string, Link> links = JsonConvert.DeserializeObject<Dictionary<string, Link>>(new WebClient().DownloadString($"{Context.Request.Scheme}://{Context.Request.Host}/Links.json"));
|
|
||||||
}
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>@ViewData["Title"] - XFox111.NET</title>
|
|
||||||
<link rel="shortcut icon" href="~/images/favicon.png" type="image/png" />
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="~/css/style.css" />
|
|
||||||
<link rel="stylesheet" href="https://d1azc1qln24ryf.cloudfront.net/114779/Socicon/style-cf.css?9ukd8d">
|
|
||||||
|
|
||||||
<script src="~/js/site.js" type="text/javascript"></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:site_name" content="XFox111.NET" />
|
|
||||||
<meta property="og:url" content="//XFox111.NET/" />
|
|
||||||
<meta property="og:locale" content="en_US" />
|
|
||||||
<meta property="og:image" content="~/images/me.png" />
|
|
||||||
<meta property="og:description" content="Hi, my name is Michael. I'm C# Developer and this is my personal website. Here you can find info about me, my projects and more. Check it out!" />
|
|
||||||
<meta property="og:title" content="Michael 'XFox' Gordeev - Personal website" />
|
|
||||||
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav class="navbar">
|
|
||||||
<a asp-controller="Home" asp-action="Index">XFox111.NET</a>
|
|
||||||
|
|
||||||
<menu type="toolbar" class="main-menu" style="display:none">
|
|
||||||
<li><a asp-controller="About" asp-action="Index">AboutMe();</a></li>
|
|
||||||
<li><a asp-controller="CV" asp-action="Index">CV();</a></li>
|
|
||||||
<li><a asp-controller="Projects" asp-action="Index">Projects();</a></li>
|
|
||||||
<li><a asp-controller="Gallery" asp-action="Index">Arts();</a></li>
|
|
||||||
<li><a asp-controller="Contacts" asp-action="Index">Contacts();</a></li>
|
|
||||||
</menu>
|
|
||||||
|
|
||||||
<a class="language-switch" asp-controller="Home" asp-action="SwitchLanguage" lang="ru">РУС </a>
|
|
||||||
<a class="menu-toggle" onclick="ToggleMenu();"></a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
@RenderBody()
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<span class="comment">// Copyright ©@(DateTime.Today.Year) <b>Michael "XFox" Gordeev</b></span>
|
|
||||||
|
|
||||||
@{
|
|
||||||
foreach (Link link in new List<Link> { links["email"], links["linkedin"], links["github"] })
|
|
||||||
{
|
|
||||||
<a class="socicon-@(link.Socicon)" href="@(link.Url)" target="_blank" title="@(link.Title)"></a>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
@using MyWebsite
|
|
||||||
@using MyWebsite.Models
|
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
@{
|
|
||||||
Layout = "_Layout";
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Debug",
|
|
||||||
"System": "Information",
|
|
||||||
"Microsoft": "Information"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
article {
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: -10px;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
.block .content {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.block .background {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.intro {
|
|
||||||
color: white;
|
|
||||||
background-color: #00a7dc;
|
|
||||||
font-size: 36px;
|
|
||||||
text-shadow: 2px 2px 1px black;
|
|
||||||
}
|
|
||||||
.intro .content {
|
|
||||||
bottom: 50px;
|
|
||||||
left: 50px;
|
|
||||||
max-width: 65%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sut {
|
|
||||||
color: white;
|
|
||||||
background-color: #ff8000;
|
|
||||||
font-size: 28px;
|
|
||||||
text-shadow: 2px 2px 1px black;
|
|
||||||
}
|
|
||||||
.sut .content {
|
|
||||||
margin: 50px 50px;
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
input {
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid black;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
padding: 10px;
|
|
||||||
border: 1px solid black;
|
|
||||||
border-radius: 10px;
|
|
||||||
width: 100%;
|
|
||||||
-moz-appearance: none; /* Firefox */
|
|
||||||
-webkit-appearance: none; /* Safari and Chrome */
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 0px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background-color: #343434;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: none;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid black;
|
|
||||||
padding: 10px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 200px;
|
|
||||||
font-family: Consolas;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-container::after {
|
|
||||||
content: '>';
|
|
||||||
transform: rotate(90deg);
|
|
||||||
-webkit-transform: rotate(90deg);
|
|
||||||
-moz-transform: rotate(90deg);
|
|
||||||
-ms-transform: rotate(90deg);
|
|
||||||
right: 11px;
|
|
||||||
top: 11px;
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
max-width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 700px) {
|
|
||||||
form {
|
|
||||||
max-width: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
footer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-me {
|
|
||||||
padding: 30px 50px;
|
|
||||||
background-color: #1e1e1e;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
var {
|
|
||||||
color: #569cd6;
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.class {
|
|
||||||
color: #4ec9b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.string, .string:visited {
|
|
||||||
color: #d69d85;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.string:link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.method {
|
|
||||||
color: #dcdcaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.t1 { margin-right: 25px; }
|
|
||||||
.t2 { margin-right: 50px; }
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
.gallery img {
|
|
||||||
object-fit: cover;
|
|
||||||
max-height: 300px;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 2px;
|
|
||||||
transition: .25s;
|
|
||||||
}
|
|
||||||
.gallery img:hover {
|
|
||||||
filter: brightness(125%);
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-overview-block img {
|
|
||||||
max-height: 50vh;
|
|
||||||
max-width: 100%;
|
|
||||||
float: left;
|
|
||||||
cursor: zoom-in;
|
|
||||||
}
|
|
||||||
.image-overview-block div {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
.image-overview-block h1 {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
.gallery img {
|
|
||||||
max-height: none;
|
|
||||||
}
|
|
||||||
.gallery img:hover {
|
|
||||||
filter: brightness(125%);
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
header {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr auto;
|
|
||||||
grid-template-rows: auto auto;
|
|
||||||
grid-column-gap: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
article {
|
|
||||||
margin: 0px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-stats {
|
|
||||||
margin-top: 20px;
|
|
||||||
width: 200px;
|
|
||||||
height: 110px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-item {
|
|
||||||
display: grid;
|
|
||||||
padding: 20px;
|
|
||||||
padding-top: 0px;
|
|
||||||
grid-template-rows: auto auto;
|
|
||||||
grid-row-gap: 20px;
|
|
||||||
background-color: whitesmoke;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
height: 25px;
|
|
||||||
width: 25px;
|
|
||||||
margin-right: 10px;
|
|
||||||
display: inline-block;
|
|
||||||
background-size: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.csharp { background-image: url(../images/Badges/csharp.png); }
|
|
||||||
.dotnet { background-image: url("../images/Badges/dotnet.png"); }
|
|
||||||
.xamarin { background-image: url("../images/Badges/xamarin.png"); }
|
|
||||||
.unity { background-image: url("../images/Badges/unity.png"); }
|
|
||||||
.android { background-image: url("../images/Badges/android.png"); }
|
|
||||||
.uwp { background-image: url("../images/Badges/windows.png"); }
|
|
||||||
.win32 { background-image: url("../images/Badges/windows.png"); }
|
|
||||||
.windows { background-image: url("../images/Badges/windows.png"); }
|
|
||||||
.ios { background-image: url("../images/Badges/ios.png"); }
|
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
.github-stats {
|
|
||||||
grid-row: 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Consolas';
|
|
||||||
src: url("/fonts/consolas.eot");
|
|
||||||
src: url("/fonts/consolas.eot?#iefix") format("embedded-opentype"), url("/fonts/consolas.otf") format("opentype"), url("/fonts/consolas.svg") format("svg"), url("/fonts/consolas.ttf") format("truetype"), url("/fonts/consolas.woff") format("woff"), url("/fonts/consolas.woff2") format("woff2");
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Segoe MDL2 Assets';
|
|
||||||
src: url("/fonts/segoeMLD2assets.eot");
|
|
||||||
src: url("/fonts/segoeMLD2assets.eot?#iefix") format("embedded-opentype"), url("/fonts/segoeMLD2assets.otf") format("opentype"), url("/fonts/segoeMLD2assets.svg") format("svg"), url("/fonts/segoeMLD2assets.ttf") format("truetype"), url("/fonts/segoeMLD2assets.woff") format("woff"), url("/fonts/segoeMLD2assets.woff2") format("woff2");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Header styles*/
|
|
||||||
.navbar {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto 1fr auto auto;
|
|
||||||
grid-column-gap: 10px;
|
|
||||||
grid-template-rows: auto auto;
|
|
||||||
background-color: #343434;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 10;
|
|
||||||
padding: 10px;
|
|
||||||
min-height: 33px;
|
|
||||||
font-size: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.navbar a:hover {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-menu {
|
|
||||||
margin: 26px auto 26px auto;
|
|
||||||
grid-row: 2;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
.main-menu li {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-switch {
|
|
||||||
grid-column: 3;
|
|
||||||
user-select: none
|
|
||||||
}
|
|
||||||
.menu-toggle {
|
|
||||||
grid-column: 4;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Footer styles*/
|
|
||||||
footer {
|
|
||||||
padding: 10px;
|
|
||||||
display: grid;
|
|
||||||
align-items: center;
|
|
||||||
grid-template-columns: 1fr auto auto auto;
|
|
||||||
grid-column-gap: 10px;
|
|
||||||
}
|
|
||||||
footer a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
footer a:hover {
|
|
||||||
color: orangered;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Body styles*/
|
|
||||||
body {
|
|
||||||
margin: 0px;
|
|
||||||
margin-top: 53px;
|
|
||||||
font-family: Consolas, 'Segoe MDL2 Assets';
|
|
||||||
|
|
||||||
/*This stuff is necessary for sticky footer*/
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: 1fr auto;
|
|
||||||
height: calc(100vh - 53px);
|
|
||||||
}
|
|
||||||
|
|
||||||
header a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
header a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
article, header {
|
|
||||||
margin: 0px 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
article a:visited, article a:link {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment, .comment:visited {
|
|
||||||
color: #57a64a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Adaptive code*/
|
|
||||||
@media only screen and (min-width: 1000px) {
|
|
||||||
.main-menu {
|
|
||||||
display: initial !important;
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 2;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
.main-menu li {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 10px;
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
.menu-toggle {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# CA1303: Do not pass literals as localized parameters
|
||||||
|
dotnet_diagnostic.CA1303.severity = none
|
||||||
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 16
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 16.0.29519.181
|
VisualStudioVersion = 16.0.29519.181
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyWebsite", "MyWebsite\MyWebsite.csproj", "{9FB2B925-DC18-4081-AC91-96F2C49415F9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyWebsite", "MyWebsite\MyWebsite.csproj", "{9FB2B925-DC18-4081-AC91-96F2C49415F9}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|||||||
@@ -1,50 +1,44 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MyWebsite.Areas.API.Models;
|
using MyWebsite.Areas.API.Models;
|
||||||
|
using System.Globalization;
|
||||||
|
using MyWebsite.Models.Databases;
|
||||||
|
|
||||||
namespace MyWebsite.Areas.API
|
namespace MyWebsite.Areas.API
|
||||||
{
|
{
|
||||||
[Route("API/[controller]")]
|
|
||||||
[Area("API")]
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
[Route("API/[controller]")]
|
||||||
public class FoxTubeController : ControllerBase
|
public class FoxTubeController : ControllerBase
|
||||||
{
|
{
|
||||||
FoxTubeDatabaseContext db;
|
readonly FoxTubeDatabaseContext db;
|
||||||
public FoxTubeController(FoxTubeDatabaseContext context) =>
|
public FoxTubeController(FoxTubeDatabaseContext context) =>
|
||||||
db = context;
|
db = context;
|
||||||
|
|
||||||
[HttpPost("AddMetrics")]
|
[HttpPost]
|
||||||
public string AddMetrics()
|
[Route("AddMetrics")]
|
||||||
|
public IActionResult AddMetrics(MetricsPackage package)
|
||||||
{
|
{
|
||||||
MetricsPackage metrics = new MetricsPackage
|
Guid id = db.Metrics.Add(package).Entity.Id;
|
||||||
{
|
|
||||||
Title = Request.Form["Title"],
|
|
||||||
Content = Request.Form["Content"],
|
|
||||||
Version = Request.Form["Version"],
|
|
||||||
TimeStamp = DateTime.Now
|
|
||||||
};
|
|
||||||
|
|
||||||
db.Metrics.Add(metrics);
|
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
|
||||||
return db.Metrics.FirstOrDefault(i => i.TimeStamp == metrics.TimeStamp)?.Id.ToString();
|
return Accepted(value: id.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Messages")]
|
[HttpGet]
|
||||||
public object Messages(bool toast = false, DateTime? publishedAfter = null, string lang = "en-US")
|
[Route("Messages")]
|
||||||
|
public IActionResult Messages(bool toast = false, DateTime? publishedAfter = null, string lang = "en-US")
|
||||||
{
|
{
|
||||||
if(toast)
|
if (toast)
|
||||||
{
|
{
|
||||||
Message message = publishedAfter.HasValue ?
|
Message message = publishedAfter.HasValue ?
|
||||||
db.Messages.FirstOrDefault(i => i.PublishedAt >= publishedAfter && i.PublishedAt <= DateTime.Now && i.Language == lang) :
|
db.Messages.FirstOrDefault(i => i.PublishedAt >= publishedAfter && i.PublishedAt <= DateTime.Now && i.Language == lang) :
|
||||||
db.Messages.OrderByDescending(i => i.PublishedAt).FirstOrDefault();
|
db.Messages.OrderByDescending(i => i.PublishedAt).FirstOrDefault();
|
||||||
|
|
||||||
if (message == null)
|
if (message == null)
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
|
||||||
return $@"<toast activationType='foreground' launch='inbox|{message.Id}'>
|
return Ok($@"<toast activationType='foreground' launch='inbox|{message.Id}'>
|
||||||
<visual>
|
<visual>
|
||||||
<binding template='ToastGeneric'>
|
<binding template='ToastGeneric'>
|
||||||
<image placement='hero' src='{message.HeroImage}'/>
|
<image placement='hero' src='{message.HeroImage}'/>
|
||||||
@@ -53,29 +47,23 @@ namespace MyWebsite.Areas.API
|
|||||||
<text hint-maxLines='5'>{message.Description}</text>
|
<text hint-maxLines='5'>{message.Description}</text>
|
||||||
</binding>
|
</binding>
|
||||||
</visual>
|
</visual>
|
||||||
</toast>";
|
</toast>");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
return Ok(db.Messages.Where(i => i.PublishedAt <= DateTime.Now).ToList());
|
||||||
List<Message> messages = db.Messages.Where(i => i.PublishedAt <= DateTime.Now).ToList();
|
|
||||||
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Changelogs")]
|
[HttpGet]
|
||||||
public object Changelogs(bool toast = false, string version = null, string lang = "en-US")
|
[Route("Changelogs")]
|
||||||
|
public IActionResult Changelogs(string version, bool toast = false, string lang = "en-US")
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(version))
|
if (toast)
|
||||||
throw new ArgumentNullException("You must specify required version number");
|
|
||||||
|
|
||||||
if(toast)
|
|
||||||
{
|
{
|
||||||
Changelog changelog = db.Changelogs.FirstOrDefault(i => i.Version == version && i.Language == lang);
|
Changelog changelog = db.Changelogs.FirstOrDefault(i => i.Version == version && i.Language == lang);
|
||||||
if (changelog == null)
|
if (changelog == null)
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
|
||||||
return $@"<toast activationType='foreground' launch='changelog|{changelog.Id}'>
|
return Ok($@"<toast activationType='foreground' launch='changelog|{changelog.Id}'>
|
||||||
<visual>
|
<visual>
|
||||||
<binding template='ToastGeneric'>
|
<binding template='ToastGeneric'>
|
||||||
<image placement='hero' src='{changelog.HeroImage}'/>
|
<image placement='hero' src='{changelog.HeroImage}'/>
|
||||||
@@ -84,26 +72,18 @@ namespace MyWebsite.Areas.API
|
|||||||
<text>{changelog.Title}</text>
|
<text>{changelog.Title}</text>
|
||||||
</binding>
|
</binding>
|
||||||
</visual>
|
</visual>
|
||||||
</toast>";
|
</toast>");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
return Ok(db.Changelogs.Where(i => !IsVersionGreater(i.Version, version)).ToList());
|
||||||
List<Changelog> changelogs = db.Changelogs.Where(i => !IsVersionGreater(i.Version, version)).ToList();
|
|
||||||
|
|
||||||
return changelogs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsVersionGreater(string versionOne, string versionTwo)
|
private static bool IsVersionGreater(string versionOne, string versionTwo)
|
||||||
{
|
{
|
||||||
string[] v1 = versionOne.Split('.');
|
versionOne = versionOne.Replace(".", "", StringComparison.InvariantCulture);
|
||||||
string[] v2 = versionTwo.Split('.');
|
versionTwo = versionTwo.Replace(".", "", StringComparison.InvariantCulture);
|
||||||
|
|
||||||
for (int i = 0; i < v1.Length && i < v2.Length; i++)
|
return short.Parse(versionOne, NumberStyles.Integer, CultureInfo.InvariantCulture) > short.Parse(versionTwo, NumberStyles.Integer, CultureInfo.InvariantCulture);
|
||||||
if (byte.Parse(v1[i]) > byte.Parse(v2[i]))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
// Media
|
||||||
[Column(TypeName = "varchar(255")]
|
[Column(TypeName = "varchar(255)")]
|
||||||
public string Icon { get; set; } = "https://xfox111.net/projects-assets/FoxTube/DefaultIcon.png";
|
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";
|
public string HeroImage { get; set; } = "https://xfox111.net/projects-assets/FoxTube/DefaultHero.png";
|
||||||
|
|
||||||
[Column(TypeName = "varchar(255)")]
|
[Column(TypeName = "varchar(255)")]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace MyWebsite.Areas.API.Models
|
|||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[Column(TypeName = "mediumtext")]
|
[Column(TypeName = "varchar(max)")]
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
@@ -23,6 +23,6 @@ namespace MyWebsite.Areas.API.Models
|
|||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public DateTime TimeStamp { get; set; }
|
public DateTime TimeStamp { get; set; } = DateTime.Now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,52 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MyWebsite.Controllers;
|
||||||
using MyWebsite.Models;
|
using MyWebsite.Models;
|
||||||
|
using MyWebsite.Models.Databases;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace MyWebsite.Areas.Admin.Controllers
|
namespace MyWebsite.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
[Area("Admin")]
|
[Area("Admin")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class BadgesController : Controller
|
public class BadgesController : ExtendedController
|
||||||
{
|
{
|
||||||
public BadgesController(DatabaseContext context) =>
|
public BadgesController(DatabaseContext context) : base(context) { }
|
||||||
Startup.Database = context;
|
|
||||||
|
|
||||||
public IActionResult Index() =>
|
public IActionResult Index() =>
|
||||||
View(Startup.Database.Badges);
|
View(Database.Badges);
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Edit(string id) =>
|
public IActionResult Edit(string id) =>
|
||||||
View(Startup.Database.Badges.Find(id));
|
View(Database.Badges.Find(id));
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Edit(Badge model, IFormFile file = null)
|
public IActionResult Edit(BadgeModel model, IFormFile file = null)
|
||||||
{
|
{
|
||||||
if(file != null)
|
if (model == null)
|
||||||
{
|
throw new ArgumentNullException(nameof(model));
|
||||||
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);
|
|
||||||
|
|
||||||
return Redirect(Request.Path.Value);
|
if (file != null)
|
||||||
}
|
UploadFile(file, model);
|
||||||
|
|
||||||
Startup.Database.Badges.Update(model);
|
Database.Badges.Update(model);
|
||||||
Startup.Database.SaveChanges();
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Delete(string id) =>
|
public IActionResult Delete(string id) =>
|
||||||
View(Startup.Database.Badges.Find(id));
|
View(Database.Badges.Find(id));
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Delete(Badge model)
|
public IActionResult Delete(BadgeModel model)
|
||||||
{
|
{
|
||||||
Startup.Database.Badges.Remove(model);
|
Database.Badges.Remove(model);
|
||||||
Startup.Database.SaveChanges();
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@@ -61,21 +56,13 @@ namespace MyWebsite.Areas.Admin.Controllers
|
|||||||
View();
|
View();
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Create(Badge model, IFormFile file = null)
|
public IActionResult Create(BadgeModel model, IFormFile file = null)
|
||||||
{
|
{
|
||||||
if (file != null)
|
if (model == null)
|
||||||
{
|
throw new ArgumentNullException(nameof(model));
|
||||||
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);
|
|
||||||
|
|
||||||
return Redirect(Request.Path.Value);
|
if (file != null)
|
||||||
}
|
return UploadFile(file, model);
|
||||||
|
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
@@ -83,10 +70,24 @@ namespace MyWebsite.Areas.Admin.Controllers
|
|||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
Startup.Database.Badges.Add(model);
|
Database.Badges.Add(model);
|
||||||
Startup.Database.SaveChanges();
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
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.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MyWebsite.Controllers;
|
||||||
using MyWebsite.Models;
|
using MyWebsite.Models;
|
||||||
|
using MyWebsite.Models.Databases;
|
||||||
|
|
||||||
namespace MyWebsite.Areas.Admin.Controllers
|
namespace MyWebsite.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
[Area("Admin")]
|
[Area("Admin")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class ContactsController : Controller
|
public class ContactsController : ExtendedController
|
||||||
{
|
{
|
||||||
public ContactsController(DatabaseContext context) =>
|
public ContactsController(DatabaseContext context) : base(context) { }
|
||||||
Startup.Database = context;
|
|
||||||
|
|
||||||
public IActionResult Index() =>
|
public IActionResult Index() =>
|
||||||
View(Startup.Database.Links);
|
View(Database.Links);
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Edit(string id) =>
|
public IActionResult Edit(string id) =>
|
||||||
View(Startup.Database.Links.Find(id));
|
View(Database.Links.Find(id));
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Edit(Link model)
|
public IActionResult Edit(LinkModel model)
|
||||||
{
|
{
|
||||||
Startup.Database.Links.Update(model);
|
Database.Links.Update(model);
|
||||||
Startup.Database.SaveChanges();
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Delete(string id) =>
|
public IActionResult Delete(string id) =>
|
||||||
View(Startup.Database.Links.Find(id));
|
View(Database.Links.Find(id));
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Delete(Link link)
|
public IActionResult Delete(LinkModel model)
|
||||||
{
|
{
|
||||||
Startup.Database.Links.Remove(link);
|
Database.Links.Remove(model);
|
||||||
Startup.Database.SaveChanges();
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@@ -45,16 +46,16 @@ namespace MyWebsite.Areas.Admin.Controllers
|
|||||||
View();
|
View();
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Create(Link link)
|
public IActionResult Create(LinkModel model)
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("Error", "Invalid data");
|
ModelState.AddModelError("Error", "Invalid data");
|
||||||
return View(link);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
Startup.Database.Links.Add(link);
|
Database.Links.Add(model);
|
||||||
Startup.Database.SaveChanges();
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
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.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MyWebsite.Areas.Admin.Models;
|
||||||
|
using MyWebsite.Controllers;
|
||||||
using MyWebsite.Models;
|
using MyWebsite.Models;
|
||||||
|
using MyWebsite.Models.Databases;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace MyWebsite.Areas.Admin.Controllers
|
namespace MyWebsite.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
[Area("Admin")]
|
[Area("Admin")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class GalleryController : Controller
|
public class GalleryController : ExtendedController
|
||||||
{
|
{
|
||||||
public GalleryController(DatabaseContext context) =>
|
public GalleryController(DatabaseContext context) : base(context) { }
|
||||||
Startup.Database = context;
|
|
||||||
|
|
||||||
public IActionResult Index() =>
|
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.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MyWebsite.Controllers;
|
||||||
using MyWebsite.Models;
|
using MyWebsite.Models;
|
||||||
|
using MyWebsite.Models.Databases;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MyWebsite.Areas.Admin.Controllers
|
namespace MyWebsite.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[Area("Admin")]
|
[Area("Admin")]
|
||||||
public class ResumeController : Controller
|
public class ResumeController : ExtendedController
|
||||||
{
|
{
|
||||||
public ResumeController(DatabaseContext context) =>
|
public ResumeController(DatabaseContext context) : base(context) { }
|
||||||
Startup.Database = context;
|
|
||||||
|
|
||||||
public IActionResult Index() =>
|
public IActionResult Index() =>
|
||||||
View(Startup.Database.Resume);
|
View(Database.Resume);
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Edit(string id) =>
|
public IActionResult Edit(string id) =>
|
||||||
View(Startup.Database.Resume.Find(id));
|
View(Database.Resume.Find(id));
|
||||||
|
|
||||||
[HttpPost]
|
[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;
|
model.LastUpdate = DateTime.Now;
|
||||||
Startup.Database.Resume.Update(model);
|
|
||||||
Startup.Database.SaveChanges();
|
Database.Resume.Update(model);
|
||||||
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Delete(string id) =>
|
public IActionResult Delete(string id) =>
|
||||||
View(Startup.Database.Resume.Find(id));
|
View(Database.Resume.Find(id));
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Delete(Resume model)
|
public IActionResult Delete(ResumeModel model)
|
||||||
{
|
{
|
||||||
Startup.Database.Resume.Remove(model);
|
Database.Resume.Remove(model);
|
||||||
Startup.Database.SaveChanges();
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@@ -47,17 +58,21 @@ namespace MyWebsite.Areas.Admin.Controllers
|
|||||||
View();
|
View();
|
||||||
|
|
||||||
[HttpPost]
|
[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)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("Error", "Invalid data");
|
ModelState.AddModelError("Error", "Invalid data");
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
Startup.Database.Resume.Add(model);
|
model.LastUpdate = DateTime.Now;
|
||||||
Startup.Database.SaveChanges();
|
|
||||||
|
Database.Resume.Add(model);
|
||||||
|
Database.SaveChanges();
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
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;
|
@using System.IO;
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Create badge";
|
ViewData["Title"] = "Create badge";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model MyWebsite.Models.Badge
|
@model MyWebsite.Models.BadgeModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Delete badge";
|
ViewData["Title"] = "Delete badge";
|
||||||
@@ -11,14 +11,14 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<p class="form-group">
|
<p>
|
||||||
<b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br />
|
<b>@Html.DisplayNameFor(model => model.Name):</b> @Model.Name<br />
|
||||||
<b>@Html.DisplayNameFor(model => model.EnglishDescription):</b> @Model.EnglishDescription<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.RussianDescription):</b> @Model.RussianDescription<br />
|
||||||
<b>@Html.DisplayNameFor(model => model.Image):</b> @(Model.Image).png<br />
|
<b>@Html.DisplayNameFor(model => model.Image):</b> @(Model.Image).png<br />
|
||||||
|
|
||||||
<b>Preview:</b>
|
<b>Preview:</b>
|
||||||
<div class="badge" style="background-image: url(/images/Badges/@(Model.Image).png)" title="@(Model.Description)"></div>
|
<div class="badge" style="background-image: url('/images/Badges/@(Model.Image).png')" title="@(Model.Description)"></div>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form asp-action="Delete">
|
<form asp-action="Delete">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@using System.IO;
|
@using System.IO;
|
||||||
@model MyWebsite.Models.Badge
|
@model MyWebsite.Models.BadgeModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Edit badge";
|
ViewData["Title"] = "Edit badge";
|
||||||
List<SelectListItem> files = new List<SelectListItem>();
|
List<SelectListItem> files = new List<SelectListItem>();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model IEnumerable<MyWebsite.Models.Badge>
|
@model IEnumerable<MyWebsite.Models.BadgeModel>
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Badges list";
|
ViewData["Title"] = "Badges list";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model MyWebsite.Models.Link
|
@model MyWebsite.Models.LinkModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Create link";
|
ViewData["Title"] = "Create link";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model MyWebsite.Models.Link
|
@model MyWebsite.Models.LinkModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Delete link";
|
ViewData["Title"] = "Delete link";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model MyWebsite.Models.Link
|
@model MyWebsite.Models.LinkModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Edit link";
|
ViewData["Title"] = "Edit link";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model IEnumerable<MyWebsite.Models.Link>
|
@model IEnumerable<MyWebsite.Models.LinkModel>
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Links list";
|
ViewData["Title"] = "Links list";
|
||||||
}
|
}
|
||||||
@@ -15,9 +15,7 @@
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>Reorder</th>
|
||||||
@Html.DisplayNameFor(model => model.Order)
|
|
||||||
</th>
|
|
||||||
<th>
|
<th>
|
||||||
@Html.DisplayNameFor(model => model.Name)
|
@Html.DisplayNameFor(model => model.Name)
|
||||||
</th>
|
</th>
|
||||||
@@ -45,8 +43,8 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var item in Model.OrderBy(i => i.Order))
|
@foreach (var item in Model.OrderBy(i => i.Order))
|
||||||
{
|
{
|
||||||
<tr>
|
<tr draggable="true" ondragover="onDragOver(event);">
|
||||||
<td>@item.Order</td>
|
<td></td>
|
||||||
<td>@item.Name</td>
|
<td>@item.Name</td>
|
||||||
<td>@item.EnglishTitle</td>
|
<td>@item.EnglishTitle</td>
|
||||||
<td>@item.RussianTitle</td>
|
<td>@item.RussianTitle</td>
|
||||||
@@ -68,4 +66,10 @@
|
|||||||
</table>
|
</table>
|
||||||
</article>
|
</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> <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>
|
||||||
|
 <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";
|
ViewData["Title"] = "Gallery";
|
||||||
}
|
}
|
||||||
@@ -13,25 +13,22 @@
|
|||||||
|
|
||||||
<article>
|
<article>
|
||||||
<table>
|
<table>
|
||||||
@foreach (Image item in Model)
|
@foreach (ImageModel item in Model)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img title="@item.Title" src="~/images/Gallery/@item.FileName" />
|
<img title="@item.Title" src="~/images/Gallery/@item.FileName" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>
|
<p>
|
||||||
<h3>@item.Title</h3>
|
<h3>@item.Title</h3>
|
||||||
<span>ID: @item.Id</span> |
|
<span>File name: @item.FileName</span><br />
|
||||||
<span>File name: @item.FileName</span> |
|
|
||||||
<span>Language: @item.Language</span><br />
|
|
||||||
<span>Creation date: @item.CreationDate.ToShortDateString()</span><br />
|
<span>Creation date: @item.CreationDate.ToShortDateString()</span><br />
|
||||||
<span>
|
<span>
|
||||||
@Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
|
@Html.ActionLink("Edit", "Edit", new { id = item.FileName }) |
|
||||||
@Html.ActionLink("Delete", "Delete", new { id = item.Id })
|
@Html.ActionLink("Delete", "Delete", new { id = item.FileName })
|
||||||
</span>
|
</span>
|
||||||
<p>@Html.Raw(item.Description)</p>
|
</p>
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
@@ -42,5 +39,9 @@
|
|||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
img {
|
img {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: initial;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
@model ArtworkModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Upload artwork";
|
||||||
|
}
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<p> <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> <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> <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> <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";
|
ViewData["Title"] = "Create resume";
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<span asp-validation-for="Content" class="text-danger"></span>
|
<span asp-validation-for="Content" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<input type="submit" value="Save" />
|
<input type="submit" value="Save" class="btn"/>
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model MyWebsite.Models.Resume
|
@model MyWebsite.Models.ResumeModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Delete resume";
|
ViewData["Title"] = "Delete resume";
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<article>
|
<article>
|
||||||
<p class="form-group">
|
<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 />
|
<b>@Html.DisplayNameFor(model => model.LastUpdate):</b> @Model.LastUpdate<br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model MyWebsite.Models.Resume
|
@model MyWebsite.Models.ResumeModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Edit resume";
|
ViewData["Title"] = "Edit resume";
|
||||||
@@ -7,24 +7,24 @@
|
|||||||
<header>
|
<header>
|
||||||
<p> <a asp-action="Index">Back to the list</a></p>
|
<p> <a asp-action="Index">Back to the list</a></p>
|
||||||
<h1>Edit resume</h1>
|
<h1>Edit resume</h1>
|
||||||
|
<p>
|
||||||
|
Language: @(new System.Globalization.CultureInfo(Model.Language).DisplayName)<br />
|
||||||
|
Previously updated on @Model.LastUpdate
|
||||||
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<form asp-action="Edit">
|
<form asp-action="Edit">
|
||||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||||
|
<input type="text" asp-for="Language" hidden />
|
||||||
<div>
|
<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>
|
<textarea asp-for="Content" spellcheck="false"></textarea>
|
||||||
<a class="comment" onclick="CopyToClipboard()" href="#">//  Copy to clipboard</a>
|
<a class="comment" onclick="CopyToClipboard()" href="#">//  Copy to clipboard</a>
|
||||||
<span style="display: none" id="copied"> - Done</span><br />
|
<span style="display: none" id="copied"> - Done</span><br />
|
||||||
<span asp-validation-for="Content" class="text-danger"></span>
|
<span asp-validation-for="Content" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<input type="submit" value="Save" />
|
<input type="submit" value="Save" class=" btn" />
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
document.execCommand("copy");
|
document.execCommand("copy");
|
||||||
window.getSelection().removeAllRanges();
|
window.getSelection().removeAllRanges();
|
||||||
document.getElementById("copied").style.display = "initial";
|
document.getElementById("copied").style.display = "initial";
|
||||||
await delay(3000);
|
await new Promise(res => setTimeout(res, 3000));
|
||||||
document.getElementById("copied").style.display = "none";
|
document.getElementById("copied").style.display = "none";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
@model IEnumerable<MyWebsite.Models.Resume>
|
@model IEnumerable<MyWebsite.Models.ResumeModel>
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Resumes";
|
ViewData["Title"] = "Resumes";
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
@model MyWebsite.Areas.Admin.Models.CredentialModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Edit credential";
|
||||||
|
}
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<p> <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> <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
|
||||||
@using MyWebsite.Models
|
@using MyWebsite.Models
|
||||||
|
@using MyWebsite.ViewModels
|
||||||
|
@using MyWebsite.Areas.Admin.Models
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using System.Globalization;
|
||||||
|
using MyWebsite.Areas.Projects.Models;
|
||||||
|
|
||||||
namespace MyWebsite.Areas.Projects.Controllers
|
namespace MyWebsite.Areas.Projects.Controllers
|
||||||
{
|
{
|
||||||
[Area("Projects")]
|
[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() =>
|
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> <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> <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">
|
||||||
|
 <a asp-action="Index" class="back">Back to description</a>
|
||||||
|
@if (Model.Previous != null)
|
||||||
|
{
|
||||||
|
@:| 
|
||||||
|
<a asp-action="Screenshot" asp-route-id="@Model.Previous" class="back">Previous</a>
|
||||||
|
}
|
||||||
|
@if (Model.Next != null)
|
||||||
|
{
|
||||||
|
@:| 
|
||||||
|
<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">РУС </a>
|
||||||
|
<a id="menu-toggle" onclick="ToggleMenu();"></a>
|
||||||
|
</p>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
@RenderBody()
|
||||||
|
</main>
|
||||||
|
|
||||||
|
@{
|
||||||
|
if (IsSectionDefined("Footer"))
|
||||||
|
RenderSection("Footer");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<footer>
|
||||||
|
<span class="comment">// Copyright ©@(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 Microsoft.AspNetCore.Mvc;
|
||||||
using MyWebsite.Helpers;
|
using MyWebsite.Helpers;
|
||||||
using MyWebsite.Models;
|
using MyWebsite.Models;
|
||||||
|
using MyWebsite.Models.Databases;
|
||||||
|
using MyWebsite.ViewModels;
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
namespace MyWebsite.Controllers
|
||||||
{
|
{
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class AdminController : Controller
|
public class AdminController : ExtendedController
|
||||||
{
|
{
|
||||||
public AdminController(DatabaseContext context) =>
|
public AdminController(DatabaseContext context) : base(context) { }
|
||||||
Startup.Database = context;
|
|
||||||
|
|
||||||
public IActionResult Index() =>
|
public IActionResult Index() =>
|
||||||
View();
|
View();
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Login() =>
|
public IActionResult Login() =>
|
||||||
View();
|
View(new CredentialViewModel(Database));
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Login(LoginModel model)
|
public async Task<IActionResult> Login(CredentialViewModel model)
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("Authorization error", "Invalid data");
|
ModelState.AddModelError("Authorization error", "Invalid data");
|
||||||
return View(model);
|
return View(new CredentialViewModel(Database, model));
|
||||||
}
|
}
|
||||||
|
|
||||||
LoginModel user = Startup.Database.Users.FirstOrDefault(i => i.Email == model.Email);
|
CredentialModel user = Database.Users.FirstOrDefault(i => i.Email == model.Credential.Email);
|
||||||
if (user == null || !Cryptography.VerifyHash(model.Password, "SHA512", user.Password))
|
if (user == null || !Encryptor.VerifyHash(model?.Credential.Password, user.Password))
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("Authorization error", "Invaild e-mail or password");
|
ModelState.AddModelError("Authorization error", "Invaild e-mail or password");
|
||||||
return View(model);
|
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);
|
ClaimsIdentity id = new ClaimsIdentity(new Claim[] { claim }, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
|
||||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id));
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id)).ConfigureAwait(false);
|
||||||
|
|
||||||
return RedirectToAction("Index", "Admin");
|
return RedirectToAction("Index", "Admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> Logout()
|
public async Task<IActionResult> Logout()
|
||||||
{
|
{
|
||||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false);
|
||||||
return RedirectToAction("Login", "Admin");
|
return RedirectToAction("Login", "Admin");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
public bool ResetPassword(string id)
|
||||||
|
{
|
||||||
|
CredentialModel user = Database.Users.Find("michael.xfox@outlook.com");
|
||||||
|
user.Password = Encryptor.ComputeHash(id);
|
||||||
|
Database.Users.Update(user);
|
||||||
|
Database.SaveChanges();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MyWebsite.Models;
|
using MyWebsite.Models.Databases;
|
||||||
using System.Linq;
|
using MyWebsite.ViewModels;
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
namespace MyWebsite.Controllers
|
||||||
{
|
{
|
||||||
public class GalleryController : Controller
|
public class GalleryController : ExtendedController
|
||||||
{
|
{
|
||||||
public GalleryController(DatabaseContext context) =>
|
public GalleryController(DatabaseContext context) : base(context) { }
|
||||||
Startup.Database = context;
|
|
||||||
|
|
||||||
public IActionResult Index() =>
|
public IActionResult Index() =>
|
||||||
View(Startup.Database.Gallery);
|
View(new ArtworkViewModel(Database));
|
||||||
|
|
||||||
public IActionResult Details(string id) =>
|
public IActionResult Details(string id) =>
|
||||||
View(Startup.Database.Gallery.FirstOrDefault(i => i.FileName == id));
|
View(new ArtworkViewModel(Database, id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,29 +1,33 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MyWebsite.Models;
|
using MyWebsite.Models.Databases;
|
||||||
|
using MyWebsite.ViewModels;
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
namespace MyWebsite.Controllers
|
||||||
{
|
{
|
||||||
public class HomeController : Controller
|
public class HomeController : ExtendedController
|
||||||
{
|
{
|
||||||
public HomeController(DatabaseContext context) =>
|
public HomeController(DatabaseContext context) : base(context) { }
|
||||||
Startup.Database = context;
|
|
||||||
|
|
||||||
public IActionResult Index() =>
|
[Route("")]
|
||||||
View();
|
public IActionResult Index() =>
|
||||||
|
View();
|
||||||
|
|
||||||
[Route("Contacts")]
|
[Route("Contacts")]
|
||||||
public IActionResult Contacts() =>
|
public IActionResult Contacts() =>
|
||||||
View(Startup.Database.Links.OrderBy(i => i.Order));
|
View();
|
||||||
|
|
||||||
[Route("Projects")]
|
[Route("Projects")]
|
||||||
public IActionResult Projects() =>
|
public IActionResult Projects() =>
|
||||||
View(Startup.Database.Projects.OrderByDescending(i => i.Id));
|
View(new ProjectsViewModel(Database));
|
||||||
|
|
||||||
[Route("Error")]
|
[Route("Construction")]
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
public IActionResult Construction() =>
|
||||||
public IActionResult Error() =>
|
View();
|
||||||
View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
|
||||||
}
|
[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 Microsoft.AspNetCore.Mvc;
|
||||||
using MyWebsite.Models;
|
using MyWebsite.Models.Databases;
|
||||||
|
using MyWebsite.ViewModels;
|
||||||
using SelectPdf;
|
using SelectPdf;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace MyWebsite.Controllers
|
namespace MyWebsite.Controllers
|
||||||
{
|
{
|
||||||
public class ResumeController : Controller
|
public class ResumeController : ExtendedController
|
||||||
{
|
{
|
||||||
public ResumeController(DatabaseContext context) =>
|
public ResumeController(DatabaseContext context) : base(context) { }
|
||||||
Startup.Database = context;
|
|
||||||
|
|
||||||
public IActionResult Index() =>
|
public IActionResult Index()
|
||||||
View(Startup.Database.Resume.Find(CultureInfo.CurrentUICulture.Name) ?? Startup.Database.Resume.Find("en-US"));
|
{
|
||||||
|
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() =>
|
public IActionResult Print() =>
|
||||||
View(Startup.Database.Resume.Find(CultureInfo.CurrentUICulture.Name) ?? Startup.Database.Resume.Find("en-US"));
|
Index();
|
||||||
|
|
||||||
public IActionResult Download()
|
public IActionResult Download()
|
||||||
{
|
{
|
||||||
HtmlToPdf converter = new HtmlToPdf();
|
HtmlToPdf converter = new HtmlToPdf();
|
||||||
converter.Options.MarginTop = 25;
|
converter.Options.MarginTop = 25;
|
||||||
converter.Options.MarginBottom = 25;
|
converter.Options.MarginBottom = 25;
|
||||||
converter.Options.MarginLeft = 25;
|
converter.Options.MarginLeft = 25;
|
||||||
converter.Options.MarginRight = 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}
|
|
||||||
|
|
||||||
<link rel=""stylesheet"" type=""text/css"" href=""{Request.Scheme}://{Request.Host}/css/Style.css"" />
|
PdfDocument doc = converter.ConvertUrl($"{Request.Scheme}://{Request.Host}/Resume/Print#preview");
|
||||||
</html>");
|
byte[] data = doc.Save();
|
||||||
byte[] data = doc.Save();
|
doc.Close();
|
||||||
doc.Close();
|
|
||||||
return File(data, "application/pdf", "[Michael Gordeev] CV.pdf");
|
return File(data, "application/pdf", "[Michael Gordeev] CV.pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user