1
0

Added link shortener

Fixed text selection
This commit is contained in:
Michael Gordeev
2020-06-20 12:13:11 +03:00
parent 398a16b596
commit 4c3b1bf5da
19 changed files with 754 additions and 600 deletions
+25 -25
View File
@@ -1,25 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29519.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyWebsite", "MyWebsite\MyWebsite.csproj", "{9FB2B925-DC18-4081-AC91-96F2C49415F9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DBEA98A5-43F1-4B55-B5E0-F2913A985494}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29519.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyWebsite", "MyWebsite\MyWebsite.csproj", "{9FB2B925-DC18-4081-AC91-96F2C49415F9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DBEA98A5-43F1-4B55-B5E0-F2913A985494}
EndGlobalSection
EndGlobal
@@ -0,0 +1,72 @@
using System;
using System.Linq;
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 ShortenerController : ExtendedController
{
const string ViewPath = "Areas/Admin/Views/Shared/LinkShortener.cshtml";
public ShortenerController(DatabaseContext context) : base(context) { }
[HttpGet]
public IActionResult Index() =>
View(ViewPath, Database.ShortLinks.ToList());
[HttpPost]
public IActionResult Create(string url, string id = "")
{
if (!string.IsNullOrWhiteSpace(id) && Database.ShortLinks.Find(id) != null)
{
ModelState.AddModelError("Duplicate", "Link with such ID already exists");
return View(ViewPath, Database.ShortLinks.ToList());
}
if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out Uri uri))
{
ModelState.AddModelError("InvalidLink", "Provided link for shortening is invalid");
return View(ViewPath, Database.ShortLinks.ToList());
}
Database.ShortLinks.Add(new ShortLinkModel
{
Uri = uri,
LinkId = string.IsNullOrWhiteSpace(id) ? RandomString(6) : id
});
Database.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Delete(string id)
{
if (Database.ShortLinks.Find(id) is ShortLinkModel link)
{
Database.ShortLinks.Remove(link);
Database.SaveChanges();
}
return RedirectToAction("Index");
}
private string RandomString(int length)
{
string key = string.Empty;
Random random = new Random();
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
do
{
key = new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
} while (Database.ShortLinks.Any(i => i.LinkId == key));
return key;
}
}
}
@@ -27,7 +27,7 @@
<table>
<thead>
<tr>
<th>Langauge</th>
<th>Language</th>
<th>Last update</th>
<th>Actions</th>
</tr>
@@ -0,0 +1,46 @@
@model List<ShortLinkModel>
@{
ViewData["Title"] = "Link shortener";
}
<header>
&#xE760; <a asp-action="Index" asp-controller="Admin" asp-area="">Back to main menu</a>
<h1>Link shortener</h1>
</header>
<article>
<div asp-validation-summary="All" class="text-danger"></div>
<h2>Create new link</h2>
<form method="post" asp-action="Create" enctype="multipart/form-data">
<label for="original-link">Original link</label>
<input id="original-link" name="url" type="url" />
<label for="link-id">Short link identifier (optional)</label>
<input id="link-id" name="id" type="text" />
<input type="submit" value="Create" />
</form>
<h2>Shortened links</h2>
<table>
<thead>
<tr>
<th>Original link</th>
<th>Short link</th>
</tr>
</thead>
<tbody>
@foreach (ShortLinkModel link in Model)
{
<tr>
<td>@link.Uri.OriginalString</td>
<td>//xfox111.net/@link.LinkId</td>
<td>
<a asp-action="Delete" asp-route-id="@link.LinkId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
</article>
@@ -1,65 +1,71 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
using System;
using System.Globalization;
namespace MyWebsite.Controllers
{
public class HomeController : ExtendedController
{
public HomeController(DatabaseContext context) : base(context) { }
[Route("")]
public IActionResult Index() =>
View();
[Route("Contacts")]
public IActionResult Contacts() =>
View();
[Route("Projects")]
public IActionResult Projects() =>
View(new ProjectsViewModel(Database));
[Route("Construction")]
public IActionResult Construction() =>
View();
[Route("Error")]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error() =>
View();
[Route("GetError")]
public IActionResult GetError(int errorCode = 404) =>
StatusCode(errorCode);
[Route("SwitchLanguage")]
public IActionResult SwitchLanguage()
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(
CultureInfo.CurrentCulture.TwoLetterISOLanguageName.ToUpperInvariant() == "RU" ?
"en-US" : "ru"
)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) });
return Redirect(Extensions.CheckNullOrWhitespace(Request.Headers["Referer"], "/"));
}
[Route("ComicSans")]
public IActionResult ComicSans()
{
if (Request.Cookies.ContainsKey("useComicSans"))
Response.Cookies.Delete("useComicSans");
else
Response.Cookies.Append("useComicSans", "true");
return Redirect(Extensions.CheckNullOrWhitespace(Request.Headers["Referer"], "/"));
}
}
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models;
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
using System;
using System.Globalization;
namespace MyWebsite.Controllers
{
public class HomeController : ExtendedController
{
public HomeController(DatabaseContext context) : base(context) { }
[Route("/{id?}")]
public IActionResult Index(string id = "")
{
if (!string.IsNullOrWhiteSpace(id) && Database.ShortLinks.Find(id) is ShortLinkModel link)
return Redirect(link.Uri.OriginalString);
else
return View();
}
[Route("Contacts")]
public IActionResult Contacts() =>
View();
[Route("Projects")]
public IActionResult Projects() =>
View(new ProjectsViewModel(Database));
[Route("Construction")]
public IActionResult Construction() =>
View();
[Route("Error")]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error() =>
View();
[Route("GetError")]
public IActionResult GetError(int errorCode = 404) =>
StatusCode(errorCode);
[Route("SwitchLanguage")]
public IActionResult SwitchLanguage()
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(
CultureInfo.CurrentCulture.TwoLetterISOLanguageName.ToUpperInvariant() == "RU" ?
"en-US" : "ru"
)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) });
return Redirect(Extensions.CheckNullOrWhitespace(Request.Headers["Referer"], "/"));
}
[Route("ComicSans")]
public IActionResult ComicSans()
{
if (Request.Cookies.ContainsKey("useComicSans"))
Response.Cookies.Delete("useComicSans");
else
Response.Cookies.Append("useComicSans", "true");
return Redirect(Extensions.CheckNullOrWhitespace(Request.Headers["Referer"], "/"));
}
}
}
@@ -1,18 +1,19 @@
using Microsoft.EntityFrameworkCore;
namespace MyWebsite.Models.Databases
{
public class DatabaseContext : DbContext
{
public DbSet<LinkModel> Links { get; set; }
public DbSet<ImageModel> Gallery { get; set; }
public DbSet<ProjectModel> Projects { get; set; }
public DbSet<CredentialModel> Users { get; set; }
public DbSet<BadgeModel> Badges { get; set; }
public DbSet<ResumeModel> Resume { get; set; }
public DbSet<CustomData> CustomData { get; set; }
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) =>
Database.EnsureCreated();
}
using Microsoft.EntityFrameworkCore;
namespace MyWebsite.Models.Databases
{
public class DatabaseContext : DbContext
{
public DbSet<LinkModel> Links { get; set; }
public DbSet<ImageModel> Gallery { get; set; }
public DbSet<ProjectModel> Projects { get; set; }
public DbSet<CredentialModel> Users { get; set; }
public DbSet<BadgeModel> Badges { get; set; }
public DbSet<ResumeModel> Resume { get; set; }
public DbSet<ShortLinkModel> ShortLinks { get; set; }
public DbSet<CustomData> CustomData { get; set; }
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) =>
Database.EnsureCreated();
}
}
@@ -0,0 +1,18 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyWebsite.Models
{
public class ShortLinkModel
{
[Key]
[Required]
[Column(TypeName = "varchar(255)")]
public string LinkId { get; set; }
[Required]
[Column(TypeName = "varchar(255)")]
public Uri Uri { get; set; }
}
}
+34 -31
View File
@@ -1,32 +1,35 @@
@{
ViewData["Title"] = "Admin panel";
}
<header>
<h1>Administration</h1>
</header>
<article class="admin-menu">
<p>
<a asp-action="Gallery" class="comment">// Artworks</a><br />
<a asp-action="Projects" class="comment">// Projects</a><br />
<a asp-action="Badges" class="comment">// Badges</a><br />
<a asp-action="Resume" class="comment">// Resume</a><br />
<a asp-action="Contacts" class="comment">// Contact links</a>
</p>
<p>
<a asp-action="FoxTube" class="comment">// FoxTube API</a><br />
<a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a>
</p>
<p>
<a asp-action="Credential" class="comment logout">// Change credential information</a>
</p>
<p>
<a asp-action="Logout" class="comment logout">&#xE875; Logout</a>
</p>
</article>
@section Imports
{
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@{
ViewData["Title"] = "Admin panel";
}
<header>
<h1>Administration</h1>
</header>
<article class="admin-menu">
<p>
<a asp-action="Gallery" class="comment">// Artworks</a><br />
<a asp-action="Projects" class="comment">// Projects</a><br />
<a asp-action="Badges" class="comment">// Badges</a><br />
<a asp-action="Resume" class="comment">// Resume</a><br />
<a asp-action="Contacts" class="comment">// Contact links</a>
</p>
<p>
<a asp-action="Shortener" class="comment">// Link shortener</a>
</p>
<p>
<a asp-action="FoxTube" class="comment">// FoxTube API</a><br />
<a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a>
</p>
<p>
<a asp-action="Credential" class="comment logout">// Change credential information</a>
</p>
<p>
<a asp-action="Logout" class="comment logout">&#xE875; Logout</a>
</p>
</article>
@section Imports
{
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
}
+92 -92
View File
@@ -1,10 +1,10 @@
@model BlogListViewModel
@{
ViewData["Title"] = "Fox, Coffee and Science - " + SharedLocalizer["Blog"];
}
@section OpenGraph
{
@model BlogListViewModel
@{
ViewData["Title"] = "Fox, Coffee and Science - " + SharedLocalizer["Blog"];
}
@section OpenGraph
{
<meta name="author" content="@SharedLocalizer["Michael &apos;XFox&apos; Gordeev"]" />
<meta name="description" content="@SharedLocalizer["Hi, my name is Michael. And this is my blog. Here I write about software and hardware development, interesting things from CS and more"]" />
@@ -14,89 +14,89 @@
<meta property="og:locale" content="@SharedLocalizer["en"]" />
@*<meta property="og:image" content="https://xfox111.net/images/me.jpg" />*@
<meta property="og:description" content="@Localizer["Hi, my name is Michael. And this is my blog. Here I write about software and hardware development, interesting things from CS and more"]" />
<meta property="og:title" content="Fox, Coffee and Science" />
}
<header>
@if (string.IsNullOrWhiteSpace(Model.SearchTerm))
{
<h1>Fox, Coffee and Science - @SharedLocalizer["Blog"]</h1>
}
else
{
<h1>@SharedLocalizer["Search results for"] @Model.SearchTerm</h1>
}
@Localizer["Visit on"] <a target="_blank" rel="noopener noreferrer" href="@Model.Links.FirstOrDefault(i => i.Name == "blogger")?.Url">Blogspot</a>
</header>
<form method="get" action="https://xfox111.blogspot.com/search" target="_blank" rel="noopener noreferrer">
<input type="text" name="q" spellcheck="false" placeholder="@Localizer["Search"]" />
<input type="submit" value="&#xE094;" />
</form>
<article>
<div>
@if (Model.Posts.Items == null || Model.Posts.Items.Count < 1)
{
<p class="comment">// @SharedLocalizer["No content available"]</p>
}
else
foreach (Google.Apis.Blogger.v3.Data.Post post in Model.Posts.Items)
{
<div class="item">
@if (post.Images != null && post.Images.Count > 0)
{
<img src="@post.Images.FirstOrDefault()?.Url" title="@post.Title" onerror="this.parentElement.removeChild(this)" />
}
<div>
<p>
@DateTime.Parse(post.Published).ToShortDateString() | <a href="@post.Author.Url" target="_blank" rel="noopener noreferrer">@post.Author.DisplayName</a><br />
@if (post.Labels != null && post.Labels.Count > 0)
{
<span class="comment">// @(Html.Raw(string.Join(", ", (post.Labels ?? new string[0]).Select(i => $"<a class=\"comment\" href=\"Blog/Tags/{i}\">{i}</a>"))))</span>
}
</p>
<h2><a asp-action="Post" asp-route-id="@post.Id">@post.Title</a></h2>
</div>
</div>
}
</div>
@if ((string.IsNullOrWhiteSpace(Model.SearchTerm) || Model.SearchTerm.StartsWith("#")) && (Model.PageNumber > 0 || !string.IsNullOrWhiteSpace(Model.Posts.NextPageToken)))
{
<div class="page-navigation">
@if (Model.PageNumber > 0)
{
<a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber - 1)">&#xE00E; @Localizer["Prev"]</a>
}
<span>@(Model.PageNumber + 1)</span>
@if (!string.IsNullOrWhiteSpace(Model.Posts.NextPageToken))
{
<a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber + 1)">@Localizer["Next"] &#xE00F;</a>
}
</div>
}
</article>
<aside>
<a class="twitter-timeline" data-lang="@SharedLocalizer["en"]" data-width="300" data-height="600" data-theme="light" href="https://twitter.com/xfox111?ref_src=twsrc%5Etfw">Tweets by xfox111</a>
<h3>@SharedLocalizer["Follow me on"]</h3>
<div class="follow-list">
@foreach (LinkModel link in Model.Links.Where(i => new[] { "twitter", "blogger", "github" }.Contains(i.Name)).OrderBy(i => i.Order))
{
<a class="socicon-@(link.Name)" href="@(link.Url)" target="_blank" rel="noopener noreferrer" title="@(link.Title)"></a>
}
<a class="socicon-rss" href="//xfox111.blogspot.com/feeds/posts/default?alt=rss" target="_blank" rel="noopener noreferrer" title="RSS Feed"></a>
<a href="//buymeacoff.ee/xfox111" target="_blank" rel="noopener noreferrer" title="Buy me a coffee">
<img width="24" height="25" src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg">
</a>
</div>
</aside>
@section Imports
{
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="~/css/Blog.css" />
<meta property="og:title" content="Fox, Coffee and Science" />
}
<header>
@if (string.IsNullOrWhiteSpace(Model.SearchTerm))
{
<h1>Fox, Coffee and Science - @SharedLocalizer["Blog"]</h1>
}
else
{
<h1>@SharedLocalizer["Search results for"] @Model.SearchTerm</h1>
}
@Localizer["Visit on"] <a target="_blank" rel="noopener noreferrer" href="@Model.Links.FirstOrDefault(i => i.Name == "blogger")?.Url">Blogspot</a>
</header>
<form method="get" action="https://xfox111.blogspot.com/search" target="_blank" rel="noopener noreferrer">
<input type="text" name="q" spellcheck="false" placeholder="@Localizer["Search"]" />
<input type="submit" value="&#xE094;" />
</form>
<article>
<div>
@if (Model.Posts.Items == null || Model.Posts.Items.Count < 1)
{
<p class="comment">// @SharedLocalizer["No content available"]</p>
}
else
foreach (Google.Apis.Blogger.v3.Data.Post post in Model.Posts.Items)
{
<div class="item">
@if (post.Images != null && post.Images.Count > 0)
{
<img src="@post.Images.FirstOrDefault()?.Url" title="@post.Title" onerror="this.parentElement.removeChild(this)" />
}
<div>
<p>
@DateTime.Parse(post.Published).ToShortDateString() | <a href="@post.Author.Url" target="_blank" rel="noopener noreferrer">@post.Author.DisplayName</a><br />
@if (post.Labels != null && post.Labels.Count > 0)
{
<span class="comment">// @(Html.Raw(string.Join(", ", (post.Labels ?? new string[0]).Select(i => $"<a class=\"comment\" href=\"Blog/Tags/{i}\">{i}</a>"))))</span>
}
</p>
<h2><a asp-action="Post" asp-route-id="@post.Id">@post.Title</a></h2>
</div>
</div>
}
</div>
@if ((string.IsNullOrWhiteSpace(Model.SearchTerm) || Model.SearchTerm.StartsWith("#")) && (Model.PageNumber > 0 || !string.IsNullOrWhiteSpace(Model.Posts.NextPageToken)))
{
<div class="page-navigation">
@if (Model.PageNumber > 0)
{
<a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber - 1)">&#xE00E; @Localizer["Prev"]</a>
}
<span>@(Model.PageNumber + 1)</span>
@if (!string.IsNullOrWhiteSpace(Model.Posts.NextPageToken))
{
<a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber + 1)">@Localizer["Next"] &#xE00F;</a>
}
</div>
}
</article>
<aside>
<a class="twitter-timeline" data-lang="@SharedLocalizer["en"]" data-width="300" data-height="600" data-theme="light" href="https://twitter.com/xfox111?ref_src=twsrc%5Etfw">Tweets by xfox111</a>
<h3>@SharedLocalizer["Follow me on"]</h3>
<div class="follow-list">
@foreach (LinkModel link in Model.Links.Where(i => new[] { "twitter", "blogger", "github" }.Contains(i.Name)).OrderBy(i => i.Order))
{
<a class="socicon-@(link.Name)" href="@(link.Url)" target="_blank" rel="noopener noreferrer" title="@(link.Title)"></a>
}
<a class="socicon-rss" href="/Blog/RSS" target="_blank" rel="noopener noreferrer" title="RSS Feed"></a>
<a href="//buymeacoff.ee/xfox111" target="_blank" rel="noopener noreferrer" title="Buy me a coffee">
<img width="24" height="25" src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg">
</a>
</div>
</aside>
@section Imports
{
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="~/css/Blog.css" />
}
+2 -2
View File
@@ -26,7 +26,7 @@
}
<article>
<div class="post-body">
<div class="post-body" allow-select>
<div class="post-header">
<h2>@Model.Post.Title</h2>
<p>
@@ -90,7 +90,7 @@
{
<a class="socicon-@(link.Name)" href="@(link.Url)" target="_blank" rel="noopener noreferrer" title="@(link.Title)"></a>
}
<a class="socicon-rss" href="//xfox111.blogspot.com/feeds/posts/default?alt=rss" target="_blank" rel="noopener noreferrer" title="RSS Feed"></a>
<a class="socicon-rss" href="/Blog/RSS" target="_blank" rel="noopener noreferrer" title="RSS Feed"></a>
<a href="//buymeacoff.ee/xfox111" target="_blank" rel="noopener noreferrer" title="Buy me a coffee">
<img width="24" height="25" src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg">
</a>
@@ -1,52 +1,52 @@
@model ArtworkViewModel
@{
ViewData["Title"] = Model.Current?.Title;
}
<header>
@if (Model.Previous != null)
{
@:&#xE760;
<a asp-action="Details" asp-route-id="@Model.Previous">@Localizer["Previous"]</a>
@:|
}
&#xE8B9; <a asp-action="Index" asp-controller="Gallery">@Localizer["All artworks"]</a>
@if (Model.Next != null)
{
@:| &#xE761;
<a asp-action="Details" asp-route-id="@Model.Next">@Localizer["Next"]</a>
}
</header>
<article class="image-overview-block">
<img src="~/images/Gallery/@Model.Current?.FileName" onclick="ToggleImageSize();" />
<div>
<h1>@Model.Current?.Title</h1>
<span>@Localizer["Creation date"]: @Model.Current?.CreationDate.ToShortDateString()</span>
<p>
@Html.Raw(Model.Current?.Description?.Replace("\n", "<br />"))
</p>
</div>
</article>
@section Imports
{
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<script type="text/javascript">
function ToggleImageSize()
{
var image = document.querySelector("img");
if (image.style.cursor == "zoom-out")
image.style = "";
else
{
image.style.maxHeight = "none";
image.style.maxWidth = "none";
image.style.cursor = "zoom-out";
}
}
</script>
@model ArtworkViewModel
@{
ViewData["Title"] = Model.Current?.Title;
}
<header>
@if (Model.Previous != null)
{
@:&#xE760;
<a asp-action="Details" asp-route-id="@Model.Previous">@Localizer["Previous"]</a>
@:|
}
&#xE8B9; <a asp-action="Index" asp-controller="Gallery">@Localizer["All artworks"]</a>
@if (Model.Next != null)
{
@:| &#xE761;
<a asp-action="Details" asp-route-id="@Model.Next">@Localizer["Next"]</a>
}
</header>
<article class="image-overview-block">
<img src="~/images/Gallery/@Model.Current?.FileName" onclick="ToggleImageSize();" />
<div allow-select>
<h1>@Model.Current?.Title</h1>
<span>@Localizer["Creation date"]: @Model.Current?.CreationDate.ToShortDateString()</span>
<p>
@Html.Raw(Model.Current?.Description?.Replace("\n", "<br />"))
</p>
</div>
</article>
@section Imports
{
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<script type="text/javascript">
function ToggleImageSize()
{
var image = document.querySelector("img");
if (image.style.cursor == "zoom-out")
image.style = "";
else
{
image.style.maxHeight = "none";
image.style.maxWidth = "none";
image.style.cursor = "zoom-out";
}
}
</script>
}
@@ -27,7 +27,7 @@
<a class="comment" asp-action="Print">// @Localizer["Print resume"] &#xE749;</a>
</header>
<article>
<article allow-select>
@Html.Raw(Model.Resume.Content)
</article>
+39 -39
View File
@@ -1,40 +1,40 @@
@model ResumeViewModel
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>@SharedLocalizer["Michael (Mikhail) Gordeev - Resume"]</title>
<link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="~/css/Style.css" />
<style>
html
{
overflow: initial;
}
body
{
display: initial;
margin: initial;
height: initial;
}
</style>
<script type="text/javascript">
window.onafterprint = function ()
{
window.location = "/Resume";
}
if (window.location.hash != "#preview")
window.print();
</script>
</head>
<body>
@Html.Raw(Model.Resume.Content)
</body>
@model ResumeViewModel
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>@SharedLocalizer["Michael (Mikhail) Gordeev - Resume"]</title>
<link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="~/css/Style.css" />
<style>
html
{
overflow: initial;
}
body
{
display: initial;
margin: initial;
height: initial;
}
</style>
<script type="text/javascript">
window.onafterprint = function ()
{
window.location = "/Resume";
}
if (window.location.hash != "#preview")
window.print();
</script>
</head>
<body allow-select>
@Html.Raw(Model.Resume.Content)
</body>
</html>
+14 -14
View File
@@ -1,15 +1,15 @@
@using Microsoft.AspNetCore.WebUtilities
@{
ViewData["Title"] = "Error";
}
<header>
<h1>Error @Context.Response.StatusCode</h1>
<h2>@ReasonPhrases.GetReasonPhrase(Context.Response.StatusCode)</h2>
</header>
<article>
<p>
</p>
@using Microsoft.AspNetCore.WebUtilities
@{
ViewData["Title"] = "Error";
}
<header allow-select>
<h1>Error @Context.Response.StatusCode</h1>
<h2>@ReasonPhrases.GetReasonPhrase(Context.Response.StatusCode)</h2>
</header>
<article>
<p>
</p>
</article>
+29 -29
View File
@@ -1,30 +1,30 @@
@{
ViewData["Title"] = Localizer["Home Page"];
}
<header>
<h1>@Localizer["Hello, World!"]</h1>
</header>
<article>
<p>
@Localizer["Hi guys! This is my website. And this is its home page. Usually, homepages should look as much glorious and cool as they can. But for some inspirational reasons I've done everything except homepage."]
</p>
<p hidden>
Well, maybe not everything... But the most of it. If I could I would leave it offline for a few more <s>years</s> <s>month</s> time. But I need it online now, so here we go. <br />
Lets consider it as 0.1.2020.03.08.666 prerelease beta technical preview demo pre-RTM version.
</p>
<p>
@Localizer["But you still can lurk around and check other pages. I'm pretty sure they should be fine."]
</p>
<dl>
<dt>@Localizer["Cheers"],</dt>
<dd>@SharedLocalizer["Michael \"XFox\" Gordeev"]</dd>
</dl>
<p hidden>
But anyway I should tell some more about it, shouldn't I?
</p>
<p hidden>
My name is Michael, I'm C# Developer and CS student in Saint Petersburg (the russian one:)
</p>
@{
ViewData["Title"] = Localizer["Home Page"];
}
<header>
<h1>@Localizer["Hello, World!"]</h1>
</header>
<article allow-select>
<p>
@Localizer["Hi guys! This is my website. And this is its home page. Usually, homepages should look as much glorious and cool as they can. But for some inspirational reasons I've done everything except homepage."]
</p>
<p hidden>
Well, maybe not everything... But the most of it. If I could I would leave it offline for a few more <s>years</s> <s>month</s> time. But I need it online now, so here we go. <br />
Lets consider it as 0.1.2020.03.08.666 prerelease beta technical preview demo pre-RTM version.
</p>
<p>
@Localizer["But you still can lurk around and check other pages. I'm pretty sure they should be fine."]
</p>
<dl>
<dt>@Localizer["Cheers"],</dt>
<dd>@SharedLocalizer["Michael \"XFox\" Gordeev"]</dd>
</dl>
<p hidden>
But anyway I should tell some more about it, shouldn't I?
</p>
<p hidden>
My name is Michael, I'm C# Developer and CS student in Saint Petersburg (the russian one:)
</p>
</article>
@@ -18,8 +18,8 @@
{
<div class="project-item">
<div>
<h1>@project.Title</h1>
<p>
<h1 allow-select>@project.Title</h1>
<p allow-select>
@Html.Raw(project.Description?.Replace("\n", "<br />"))
</p>
<a href="@(project.Link)" target="_blank" rel="noopener noreferrer">@project.LinkCaption</a>
@@ -40,7 +40,7 @@
}
</article>
@section Imports
@section Imports
{
<link rel="stylesheet" type="text/css" href="~/css/Projects.css" />
}
+50 -50
View File
@@ -1,51 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--Redirects-->
<system.webServer>
<rewrite>
<rules>
<!--Legacy redirects-->
<rule name="GutSchedule API redirect" stopProcessing="true">
<match url="schedule_offset.txt" />
<action type="Redirect" url="/API/GUTSchedule/SemesterOffsetDay" redirectType="Permanent" appendQueryString="false" />
</rule>
<rule name="GUT.Schedule Privacy policy" stopProcessing="true">
<match url="Projects/GUTSchedule/PrivacyPolicy.txt" />
<action type="Redirect" url="/Projects/GUTSchedule/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule>
<rule name="GUT.Schedule download page" stopProcessing="true">
<match url="guttime/get" />
<action type="Redirect" url="/Projects/GUTSchedule" redirectType="Permanent" appendQueryString="false" />
</rule>
<rule name="FoxTubr Privacy policy" stopProcessing="true">
<match url="Projects/FoxTube/PrivacyPolicy.txt" />
<action type="Redirect" url="/Projects/FoxTube/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule>
<!--/Legacy redirects-->
<rule name="Tabs Aside redirect" stopProcessing="true">
<match url="TabsAside(?!(\.png))" />
<action type="Redirect" url="https://github.com/XFox111/ChromiumTabsAside" redirectType="Permanent" appendQueryString="false" />
</rule>
<!--Website maintainence redirect-->
<rule name="Construction redirect" stopProcessing="true" enabled="false">
<match url="^(?!Admin|API|Construction|css|assets|fonts|images)" />
<action type="Rewrite" url="/Construction" appendQueryString="false" />
</rule>
<!--\Website maintainence redirect-->
</rules>
</rewrite>
</system.webServer>
<!--/Redirects-->
<location path="." inheritInChildApplications="false" allowOverride="false">
<system.web>
<trust level="Full" />
</system.web>
</location>
<system.web>
<compilation defaultLanguage="c#" />
<globalization fileEncoding="utf-8" />
</system.web>
</configuration>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--Redirects-->
<system.webServer>
<rewrite>
<rules>
<!--Legacy redirects-->
<rule name="GUT.Schedule Privacy policy" stopProcessing="true">
<match url="Projects/GUTSchedule/PrivacyPolicy.txt" />
<action type="Redirect" url="/Projects/GUTSchedule/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule>
<rule name="GUT.Schedule download page" stopProcessing="true">
<match url="guttime/get" />
<action type="Redirect" url="/Projects/GUTSchedule" redirectType="Permanent" appendQueryString="false" />
</rule>
<rule name="FoxTubr Privacy policy" stopProcessing="true">
<match url="Projects/FoxTube/PrivacyPolicy.txt" />
<action type="Redirect" url="/Projects/FoxTube/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule>
<rule name="Blog RSS" stopProcessing="true">
<match url="/blog/rss"/>
<action type="Redirect" url="https://xfox111.blogspot.com/feeds/posts/default?alt=rss" redirectType="Permanent" appendQueryString="false"/>
</rule>
<!--/Legacy redirects-->
<rule name="Tabs Aside redirect" stopProcessing="true">
<match url="TabsAside(?!(\.png))" />
<action type="Redirect" url="https://github.com/XFox111/ChromiumTabsAside" redirectType="Permanent" appendQueryString="false" />
</rule>
<!--Website maintainence redirect-->
<rule name="Construction redirect" stopProcessing="true" enabled="false">
<match url="^(?!Admin|API|Construction|css|assets|fonts|images)" />
<action type="Rewrite" url="/Construction" appendQueryString="false" />
</rule>
<!--\Website maintainence redirect-->
</rules>
</rewrite>
</system.webServer>
<!--/Redirects-->
<location path="." inheritInChildApplications="false" allowOverride="false">
<system.web>
<trust level="Full" />
</system.web>
</location>
<system.web>
<compilation defaultLanguage="c#" />
<globalization fileEncoding="utf-8" />
</system.web>
</configuration>
<!--ProjectGuid: 9fb2b925-dc18-4081-ac91-96f2c49415f9-->
+183 -178
View File
@@ -1,179 +1,184 @@
input:not([type="checkbox"])
{
padding: 10px;
border-radius: 5px;
border: 1px solid black;
width: 100%;
box-sizing: border-box;
margin-bottom: 10px;
}
select
{
padding: 10px;
border: 1px solid black;
border-radius: 5px;
width: 100%;
-moz-appearance: none; /* Firefox */
-webkit-appearance: none; /* Safari and Chrome */
}
input:invalid
{
border: 3px solid red;
}
input[type="submit"],
input[type="button"],
button
{
border-radius: 5px;
border: 0px;
padding: 10px 20px;
background-color: #343434;
color: white;
cursor: pointer;
}
input[type="submit"]:disabled,
input[type="button"]:disabled,
button:disabled
{
background-color: gray;
cursor: initial;
}
input[type="submit"]:hover:not(:disabled),
input[type="button"]:hover:not(:disabled),
button:hover:not(:disabled)
{
background-color: #505050;
}
input[type="submit"]:active,
input[type="button"]:active,
button:active
{
background-color: black;
}
input[type="submit"][required],
input[type="button"][required]
{
background-color: red;
}
input[type="submit"][required]:hover,
input[type="button"][required]:hover
{
background-color: rgb(200, 0, 0);
}
input[type="submit"][required]:active,
input[type="button"][required]:active
{
background-color: darkred;
}
input[readonly]
{
background-color: lightgray;
}
textarea
{
resize: none;
width: 100%;
border-radius: 5px;
border: 1px solid black;
padding: 10px;
box-sizing: border-box;
height: 68vh;
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: 500px;
width: 100%;
}
.admin-menu > p a
{
font-size: 16pt;
}
.comment.logout
{
color: red !important;
}
.validation-summary-errors
{
color: red;
}
th
{
text-align: left;
}
td
{
text-align: start;
}
table
{
width: 100%;
}
.text-danger
{
text-decoration: solid;
color: red;
}
.checkbox
{
width: auto;
}
.reorder-arrow:link
{
text-decoration: none;
}
@media only screen and (max-width: 1080px)
{
.hide-l1
{
display: none;
}
}
@media only screen and (max-width: 850px)
{
.hide-l2
{
display: none;
}
html
{
user-select: unset;
}
input:not([type="checkbox"])
{
padding: 10px;
border-radius: 5px;
border: 1px solid black;
width: 100%;
box-sizing: border-box;
margin-bottom: 10px;
}
select
{
padding: 10px;
border: 1px solid black;
border-radius: 5px;
width: 100%;
-moz-appearance: none; /* Firefox */
-webkit-appearance: none; /* Safari and Chrome */
}
input:invalid
{
border: 3px solid red;
}
input[type="submit"],
input[type="button"],
button
{
border-radius: 5px;
border: 0px;
padding: 10px 20px;
background-color: #343434;
color: white;
cursor: pointer;
}
input[type="submit"]:disabled,
input[type="button"]:disabled,
button:disabled
{
background-color: gray;
cursor: initial;
}
input[type="submit"]:hover:not(:disabled),
input[type="button"]:hover:not(:disabled),
button:hover:not(:disabled)
{
background-color: #505050;
}
input[type="submit"]:active,
input[type="button"]:active,
button:active
{
background-color: black;
}
input[type="submit"][required],
input[type="button"][required]
{
background-color: red;
}
input[type="submit"][required]:hover,
input[type="button"][required]:hover
{
background-color: rgb(200, 0, 0);
}
input[type="submit"][required]:active,
input[type="button"][required]:active
{
background-color: darkred;
}
input[readonly]
{
background-color: lightgray;
}
textarea
{
resize: none;
width: 100%;
border-radius: 5px;
border: 1px solid black;
padding: 10px;
box-sizing: border-box;
height: 68vh;
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: 500px;
width: 100%;
}
.admin-menu > p a
{
font-size: 16pt;
}
.comment.logout
{
color: red !important;
}
.validation-summary-errors
{
color: red;
}
th
{
text-align: left;
}
td
{
text-align: start;
}
table
{
width: 100%;
}
.text-danger
{
text-decoration: solid;
color: red;
}
.checkbox
{
width: auto;
}
.reorder-arrow:link
{
text-decoration: none;
}
@media only screen and (max-width: 1080px)
{
.hide-l1
{
display: none;
}
}
@media only screen and (max-width: 850px)
{
.hide-l2
{
display: none;
}
}
+6 -3
View File
@@ -18,7 +18,6 @@ nav
padding: 10px;
min-height: 33px;
font-size: 26px;
user-select: none;
}
nav a
@@ -35,7 +34,6 @@ nav
nav div
{
grid-column: 3;
user-select: none;
cursor: pointer;
}
@@ -60,7 +58,6 @@ footer
align-items: center;
grid-template-columns: 1fr auto;
grid-column-gap: 10px;
user-select: none;
}
footer a
@@ -78,6 +75,7 @@ footer
html
{
overflow: hidden;
user-select: none;
}
body
@@ -92,6 +90,11 @@ body
height: calc(100vh - 53px);
}
*[allow-select]
{
user-select: text;
}
header a
{
text-decoration: none;