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 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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {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}.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.ActiveCfg = Release|Any CPU
{9FB2B925-DC18-4081-AC91-96F2C49415F9}.Release|Any CPU.Build.0 = Release|Any CPU {9FB2B925-DC18-4081-AC91-96F2C49415F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DBEA98A5-43F1-4B55-B5E0-F2913A985494} SolutionGuid = {DBEA98A5-43F1-4B55-B5E0-F2913A985494}
EndGlobalSection EndGlobalSection
EndGlobal 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> <table>
<thead> <thead>
<tr> <tr>
<th>Langauge</th> <th>Language</th>
<th>Last update</th> <th>Last update</th>
<th>Actions</th> <th>Actions</th>
</tr> </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.Http;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models.Databases; using MyWebsite.Models;
using MyWebsite.ViewModels; using MyWebsite.Models.Databases;
using System; using MyWebsite.ViewModels;
using System.Globalization; using System;
using System.Globalization;
namespace MyWebsite.Controllers
{ namespace MyWebsite.Controllers
public class HomeController : ExtendedController {
{ public class HomeController : ExtendedController
public HomeController(DatabaseContext context) : base(context) { } {
public HomeController(DatabaseContext context) : base(context) { }
[Route("")]
public IActionResult Index() => [Route("/{id?}")]
View(); public IActionResult Index(string id = "")
{
[Route("Contacts")] if (!string.IsNullOrWhiteSpace(id) && Database.ShortLinks.Find(id) is ShortLinkModel link)
public IActionResult Contacts() => return Redirect(link.Uri.OriginalString);
View(); else
return View();
[Route("Projects")] }
public IActionResult Projects() =>
View(new ProjectsViewModel(Database)); [Route("Contacts")]
public IActionResult Contacts() =>
[Route("Construction")] View();
public IActionResult Construction() =>
View(); [Route("Projects")]
public IActionResult Projects() =>
[Route("Error")] View(new ProjectsViewModel(Database));
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error() => [Route("Construction")]
View(); public IActionResult Construction() =>
View();
[Route("GetError")]
public IActionResult GetError(int errorCode = 404) => [Route("Error")]
StatusCode(errorCode); [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error() =>
[Route("SwitchLanguage")] View();
public IActionResult SwitchLanguage()
{ [Route("GetError")]
Response.Cookies.Append( public IActionResult GetError(int errorCode = 404) =>
CookieRequestCultureProvider.DefaultCookieName, StatusCode(errorCode);
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(
CultureInfo.CurrentCulture.TwoLetterISOLanguageName.ToUpperInvariant() == "RU" ? [Route("SwitchLanguage")]
"en-US" : "ru" public IActionResult SwitchLanguage()
)), {
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }); Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
return Redirect(Extensions.CheckNullOrWhitespace(Request.Headers["Referer"], "/")); CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(
} CultureInfo.CurrentCulture.TwoLetterISOLanguageName.ToUpperInvariant() == "RU" ?
"en-US" : "ru"
[Route("ComicSans")] )),
public IActionResult ComicSans() new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) });
{
if (Request.Cookies.ContainsKey("useComicSans")) return Redirect(Extensions.CheckNullOrWhitespace(Request.Headers["Referer"], "/"));
Response.Cookies.Delete("useComicSans"); }
else
Response.Cookies.Append("useComicSans", "true"); [Route("ComicSans")]
public IActionResult ComicSans()
return Redirect(Extensions.CheckNullOrWhitespace(Request.Headers["Referer"], "/")); {
} 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; using Microsoft.EntityFrameworkCore;
namespace MyWebsite.Models.Databases namespace MyWebsite.Models.Databases
{ {
public class DatabaseContext : DbContext public class DatabaseContext : DbContext
{ {
public DbSet<LinkModel> Links { get; set; } public DbSet<LinkModel> Links { get; set; }
public DbSet<ImageModel> Gallery { get; set; } public DbSet<ImageModel> Gallery { get; set; }
public DbSet<ProjectModel> Projects { get; set; } public DbSet<ProjectModel> Projects { get; set; }
public DbSet<CredentialModel> Users { get; set; } public DbSet<CredentialModel> Users { get; set; }
public DbSet<BadgeModel> Badges { get; set; } public DbSet<BadgeModel> Badges { get; set; }
public DbSet<ResumeModel> Resume { get; set; } public DbSet<ResumeModel> Resume { get; set; }
public DbSet<CustomData> CustomData { get; set; } public DbSet<ShortLinkModel> ShortLinks { get; set; }
public DbSet<CustomData> CustomData { get; set; }
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) =>
Database.EnsureCreated(); 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"; ViewData["Title"] = "Admin panel";
} }
<header> <header>
<h1>Administration</h1> <h1>Administration</h1>
</header> </header>
<article class="admin-menu"> <article class="admin-menu">
<p> <p>
<a asp-action="Gallery" class="comment">// Artworks</a><br /> <a asp-action="Gallery" class="comment">// Artworks</a><br />
<a asp-action="Projects" class="comment">// Projects</a><br /> <a asp-action="Projects" class="comment">// Projects</a><br />
<a asp-action="Badges" class="comment">// Badges</a><br /> <a asp-action="Badges" class="comment">// Badges</a><br />
<a asp-action="Resume" class="comment">// Resume</a><br /> <a asp-action="Resume" class="comment">// Resume</a><br />
<a asp-action="Contacts" class="comment">// Contact links</a> <a asp-action="Contacts" class="comment">// Contact links</a>
</p> </p>
<p> <p>
<a asp-action="FoxTube" class="comment">// FoxTube API</a><br /> <a asp-action="Shortener" class="comment">// Link shortener</a>
<a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a> </p>
</p> <p>
<p> <a asp-action="FoxTube" class="comment">// FoxTube API</a><br />
<a asp-action="Credential" class="comment logout">// Change credential information</a> <a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a>
</p> </p>
<p> <p>
<a asp-action="Logout" class="comment logout">&#xE875; Logout</a> <a asp-action="Credential" class="comment logout">// Change credential information</a>
</p> </p>
</article> <p>
<a asp-action="Logout" class="comment logout">&#xE875; Logout</a>
@section Imports </p>
{ </article>
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
@section Imports
{
<link type="text/css" rel="stylesheet" href="~/css/Admin.css" />
} }
+92 -92
View File
@@ -1,10 +1,10 @@
@model BlogListViewModel @model BlogListViewModel
@{ @{
ViewData["Title"] = "Fox, Coffee and Science - " + SharedLocalizer["Blog"]; ViewData["Title"] = "Fox, Coffee and Science - " + SharedLocalizer["Blog"];
} }
@section OpenGraph @section OpenGraph
{ {
<meta name="author" content="@SharedLocalizer["Michael &apos;XFox&apos; Gordeev"]" /> <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"]" /> <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:locale" content="@SharedLocalizer["en"]" />
@*<meta property="og:image" content="https://xfox111.net/images/me.jpg" />*@ @*<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: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" /> <meta property="og:title" content="Fox, Coffee and Science" />
} }
<header> <header>
@if (string.IsNullOrWhiteSpace(Model.SearchTerm)) @if (string.IsNullOrWhiteSpace(Model.SearchTerm))
{ {
<h1>Fox, Coffee and Science - @SharedLocalizer["Blog"]</h1> <h1>Fox, Coffee and Science - @SharedLocalizer["Blog"]</h1>
} }
else else
{ {
<h1>@SharedLocalizer["Search results for"] @Model.SearchTerm</h1> <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> @Localizer["Visit on"] <a target="_blank" rel="noopener noreferrer" href="@Model.Links.FirstOrDefault(i => i.Name == "blogger")?.Url">Blogspot</a>
</header> </header>
<form method="get" action="https://xfox111.blogspot.com/search" target="_blank" rel="noopener noreferrer"> <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="text" name="q" spellcheck="false" placeholder="@Localizer["Search"]" />
<input type="submit" value="&#xE094;" /> <input type="submit" value="&#xE094;" />
</form> </form>
<article> <article>
<div> <div>
@if (Model.Posts.Items == null || Model.Posts.Items.Count < 1) @if (Model.Posts.Items == null || Model.Posts.Items.Count < 1)
{ {
<p class="comment">// @SharedLocalizer["No content available"]</p> <p class="comment">// @SharedLocalizer["No content available"]</p>
} }
else else
foreach (Google.Apis.Blogger.v3.Data.Post post in Model.Posts.Items) foreach (Google.Apis.Blogger.v3.Data.Post post in Model.Posts.Items)
{ {
<div class="item"> <div class="item">
@if (post.Images != null && post.Images.Count > 0) @if (post.Images != null && post.Images.Count > 0)
{ {
<img src="@post.Images.FirstOrDefault()?.Url" title="@post.Title" onerror="this.parentElement.removeChild(this)" /> <img src="@post.Images.FirstOrDefault()?.Url" title="@post.Title" onerror="this.parentElement.removeChild(this)" />
} }
<div> <div>
<p> <p>
@DateTime.Parse(post.Published).ToShortDateString() | <a href="@post.Author.Url" target="_blank" rel="noopener noreferrer">@post.Author.DisplayName</a><br /> @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) @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> <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> </p>
<h2><a asp-action="Post" asp-route-id="@post.Id">@post.Title</a></h2> <h2><a asp-action="Post" asp-route-id="@post.Id">@post.Title</a></h2>
</div> </div>
</div> </div>
} }
</div> </div>
@if ((string.IsNullOrWhiteSpace(Model.SearchTerm) || Model.SearchTerm.StartsWith("#")) && (Model.PageNumber > 0 || !string.IsNullOrWhiteSpace(Model.Posts.NextPageToken))) @if ((string.IsNullOrWhiteSpace(Model.SearchTerm) || Model.SearchTerm.StartsWith("#")) && (Model.PageNumber > 0 || !string.IsNullOrWhiteSpace(Model.Posts.NextPageToken)))
{ {
<div class="page-navigation"> <div class="page-navigation">
@if (Model.PageNumber > 0) @if (Model.PageNumber > 0)
{ {
<a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber - 1)">&#xE00E; @Localizer["Prev"]</a> <a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber - 1)">&#xE00E; @Localizer["Prev"]</a>
} }
<span>@(Model.PageNumber + 1)</span> <span>@(Model.PageNumber + 1)</span>
@if (!string.IsNullOrWhiteSpace(Model.Posts.NextPageToken)) @if (!string.IsNullOrWhiteSpace(Model.Posts.NextPageToken))
{ {
<a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber + 1)">@Localizer["Next"] &#xE00F;</a> <a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber + 1)">@Localizer["Next"] &#xE00F;</a>
} }
</div> </div>
} }
</article> </article>
<aside> <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> <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> <h3>@SharedLocalizer["Follow me on"]</h3>
<div class="follow-list"> <div class="follow-list">
@foreach (LinkModel link in Model.Links.Where(i => new[] { "twitter", "blogger", "github" }.Contains(i.Name)).OrderBy(i => i.Order)) @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-@(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"> <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"> <img width="24" height="25" src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg">
</a> </a>
</div> </div>
</aside> </aside>
@section Imports @section Imports
{ {
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="~/css/Blog.css" /> <link rel="stylesheet" type="text/css" href="~/css/Blog.css" />
} }
+2 -2
View File
@@ -26,7 +26,7 @@
} }
<article> <article>
<div class="post-body"> <div class="post-body" allow-select>
<div class="post-header"> <div class="post-header">
<h2>@Model.Post.Title</h2> <h2>@Model.Post.Title</h2>
<p> <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-@(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"> <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"> <img width="24" height="25" src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg">
</a> </a>
@@ -1,52 +1,52 @@
@model ArtworkViewModel @model ArtworkViewModel
@{ @{
ViewData["Title"] = Model.Current?.Title; ViewData["Title"] = Model.Current?.Title;
} }
<header> <header>
@if (Model.Previous != null) @if (Model.Previous != null)
{ {
@:&#xE760; @:&#xE760;
<a asp-action="Details" asp-route-id="@Model.Previous">@Localizer["Previous"]</a> <a asp-action="Details" asp-route-id="@Model.Previous">@Localizer["Previous"]</a>
@:| @:|
} }
&#xE8B9; <a asp-action="Index" asp-controller="Gallery">@Localizer["All artworks"]</a> &#xE8B9; <a asp-action="Index" asp-controller="Gallery">@Localizer["All artworks"]</a>
@if (Model.Next != null) @if (Model.Next != null)
{ {
@:| &#xE761; @:| &#xE761;
<a asp-action="Details" asp-route-id="@Model.Next">@Localizer["Next"]</a> <a asp-action="Details" asp-route-id="@Model.Next">@Localizer["Next"]</a>
} }
</header> </header>
<article class="image-overview-block"> <article class="image-overview-block">
<img src="~/images/Gallery/@Model.Current?.FileName" onclick="ToggleImageSize();" /> <img src="~/images/Gallery/@Model.Current?.FileName" onclick="ToggleImageSize();" />
<div> <div allow-select>
<h1>@Model.Current?.Title</h1> <h1>@Model.Current?.Title</h1>
<span>@Localizer["Creation date"]: @Model.Current?.CreationDate.ToShortDateString()</span> <span>@Localizer["Creation date"]: @Model.Current?.CreationDate.ToShortDateString()</span>
<p> <p>
@Html.Raw(Model.Current?.Description?.Replace("\n", "<br />")) @Html.Raw(Model.Current?.Description?.Replace("\n", "<br />"))
</p> </p>
</div> </div>
</article> </article>
@section Imports @section Imports
{ {
<link rel="stylesheet" type="text/css" href="~/css/Gallery.css" /> <link rel="stylesheet" type="text/css" href="~/css/Gallery.css" />
<script type="text/javascript"> <script type="text/javascript">
function ToggleImageSize() function ToggleImageSize()
{ {
var image = document.querySelector("img"); var image = document.querySelector("img");
if (image.style.cursor == "zoom-out") if (image.style.cursor == "zoom-out")
image.style = ""; image.style = "";
else else
{ {
image.style.maxHeight = "none"; image.style.maxHeight = "none";
image.style.maxWidth = "none"; image.style.maxWidth = "none";
image.style.cursor = "zoom-out"; image.style.cursor = "zoom-out";
} }
} }
</script> </script>
} }
@@ -27,7 +27,7 @@
<a class="comment" asp-action="Print">// @Localizer["Print resume"] &#xE749;</a> <a class="comment" asp-action="Print">// @Localizer["Print resume"] &#xE749;</a>
</header> </header>
<article> <article allow-select>
@Html.Raw(Model.Resume.Content) @Html.Raw(Model.Resume.Content)
</article> </article>
+39 -39
View File
@@ -1,40 +1,40 @@
@model ResumeViewModel @model ResumeViewModel
@{ @{
Layout = null; Layout = null;
} }
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>@SharedLocalizer["Michael (Mikhail) Gordeev - Resume"]</title> <title>@SharedLocalizer["Michael (Mikhail) Gordeev - Resume"]</title>
<link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="~/css/Style.css" /> <link rel="stylesheet" type="text/css" href="~/css/Style.css" />
<style> <style>
html html
{ {
overflow: initial; overflow: initial;
} }
body body
{ {
display: initial; display: initial;
margin: initial; margin: initial;
height: initial; height: initial;
} }
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
window.onafterprint = function () window.onafterprint = function ()
{ {
window.location = "/Resume"; window.location = "/Resume";
} }
if (window.location.hash != "#preview") if (window.location.hash != "#preview")
window.print(); window.print();
</script> </script>
</head> </head>
<body> <body allow-select>
@Html.Raw(Model.Resume.Content) @Html.Raw(Model.Resume.Content)
</body> </body>
</html> </html>
+14 -14
View File
@@ -1,15 +1,15 @@
@using Microsoft.AspNetCore.WebUtilities @using Microsoft.AspNetCore.WebUtilities
@{ @{
ViewData["Title"] = "Error"; ViewData["Title"] = "Error";
} }
<header> <header allow-select>
<h1>Error @Context.Response.StatusCode</h1> <h1>Error @Context.Response.StatusCode</h1>
<h2>@ReasonPhrases.GetReasonPhrase(Context.Response.StatusCode)</h2> <h2>@ReasonPhrases.GetReasonPhrase(Context.Response.StatusCode)</h2>
</header> </header>
<article> <article>
<p> <p>
</p> </p>
</article> </article>
+29 -29
View File
@@ -1,30 +1,30 @@
@{ @{
ViewData["Title"] = Localizer["Home Page"]; ViewData["Title"] = Localizer["Home Page"];
} }
<header> <header>
<h1>@Localizer["Hello, World!"]</h1> <h1>@Localizer["Hello, World!"]</h1>
</header> </header>
<article> <article allow-select>
<p> <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."] @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>
<p hidden> <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 /> 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. Lets consider it as 0.1.2020.03.08.666 prerelease beta technical preview demo pre-RTM version.
</p> </p>
<p> <p>
@Localizer["But you still can lurk around and check other pages. I'm pretty sure they should be fine."] @Localizer["But you still can lurk around and check other pages. I'm pretty sure they should be fine."]
</p> </p>
<dl> <dl>
<dt>@Localizer["Cheers"],</dt> <dt>@Localizer["Cheers"],</dt>
<dd>@SharedLocalizer["Michael \"XFox\" Gordeev"]</dd> <dd>@SharedLocalizer["Michael \"XFox\" Gordeev"]</dd>
</dl> </dl>
<p hidden> <p hidden>
But anyway I should tell some more about it, shouldn't I? But anyway I should tell some more about it, shouldn't I?
</p> </p>
<p hidden> <p hidden>
My name is Michael, I'm C# Developer and CS student in Saint Petersburg (the russian one:) My name is Michael, I'm C# Developer and CS student in Saint Petersburg (the russian one:)
</p> </p>
</article> </article>
@@ -18,8 +18,8 @@
{ {
<div class="project-item"> <div class="project-item">
<div> <div>
<h1>@project.Title</h1> <h1 allow-select>@project.Title</h1>
<p> <p allow-select>
@Html.Raw(project.Description?.Replace("\n", "<br />")) @Html.Raw(project.Description?.Replace("\n", "<br />"))
</p> </p>
<a href="@(project.Link)" target="_blank" rel="noopener noreferrer">@project.LinkCaption</a> <a href="@(project.Link)" target="_blank" rel="noopener noreferrer">@project.LinkCaption</a>
@@ -40,7 +40,7 @@
} }
</article> </article>
@section Imports @section Imports
{ {
<link rel="stylesheet" type="text/css" href="~/css/Projects.css" /> <link rel="stylesheet" type="text/css" href="~/css/Projects.css" />
} }
+50 -50
View File
@@ -1,51 +1,51 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<!--Redirects--> <!--Redirects-->
<system.webServer> <system.webServer>
<rewrite> <rewrite>
<rules> <rules>
<!--Legacy redirects--> <!--Legacy redirects-->
<rule name="GutSchedule API redirect" stopProcessing="true"> <rule name="GUT.Schedule Privacy policy" stopProcessing="true">
<match url="schedule_offset.txt" /> <match url="Projects/GUTSchedule/PrivacyPolicy.txt" />
<action type="Redirect" url="/API/GUTSchedule/SemesterOffsetDay" redirectType="Permanent" appendQueryString="false" /> <action type="Redirect" url="/Projects/GUTSchedule/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule> </rule>
<rule name="GUT.Schedule Privacy policy" stopProcessing="true"> <rule name="GUT.Schedule download page" stopProcessing="true">
<match url="Projects/GUTSchedule/PrivacyPolicy.txt" /> <match url="guttime/get" />
<action type="Redirect" url="/Projects/GUTSchedule/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" /> <action type="Redirect" url="/Projects/GUTSchedule" redirectType="Permanent" appendQueryString="false" />
</rule> </rule>
<rule name="GUT.Schedule download page" stopProcessing="true"> <rule name="FoxTubr Privacy policy" stopProcessing="true">
<match url="guttime/get" /> <match url="Projects/FoxTube/PrivacyPolicy.txt" />
<action type="Redirect" url="/Projects/GUTSchedule" redirectType="Permanent" appendQueryString="false" /> <action type="Redirect" url="/Projects/FoxTube/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule> </rule>
<rule name="FoxTubr Privacy policy" stopProcessing="true"> <rule name="Blog RSS" stopProcessing="true">
<match url="Projects/FoxTube/PrivacyPolicy.txt" /> <match url="/blog/rss"/>
<action type="Redirect" url="/Projects/FoxTube/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" /> <action type="Redirect" url="https://xfox111.blogspot.com/feeds/posts/default?alt=rss" redirectType="Permanent" appendQueryString="false"/>
</rule> </rule>
<!--/Legacy redirects--> <!--/Legacy redirects-->
<rule name="Tabs Aside redirect" stopProcessing="true"> <rule name="Tabs Aside redirect" stopProcessing="true">
<match url="TabsAside(?!(\.png))" /> <match url="TabsAside(?!(\.png))" />
<action type="Redirect" url="https://github.com/XFox111/ChromiumTabsAside" redirectType="Permanent" appendQueryString="false" /> <action type="Redirect" url="https://github.com/XFox111/ChromiumTabsAside" redirectType="Permanent" appendQueryString="false" />
</rule> </rule>
<!--Website maintainence redirect--> <!--Website maintainence redirect-->
<rule name="Construction redirect" stopProcessing="true" enabled="false"> <rule name="Construction redirect" stopProcessing="true" enabled="false">
<match url="^(?!Admin|API|Construction|css|assets|fonts|images)" /> <match url="^(?!Admin|API|Construction|css|assets|fonts|images)" />
<action type="Rewrite" url="/Construction" appendQueryString="false" /> <action type="Rewrite" url="/Construction" appendQueryString="false" />
</rule> </rule>
<!--\Website maintainence redirect--> <!--\Website maintainence redirect-->
</rules> </rules>
</rewrite> </rewrite>
</system.webServer> </system.webServer>
<!--/Redirects--> <!--/Redirects-->
<location path="." inheritInChildApplications="false" allowOverride="false"> <location path="." inheritInChildApplications="false" allowOverride="false">
<system.web> <system.web>
<trust level="Full" /> <trust level="Full" />
</system.web> </system.web>
</location> </location>
<system.web> <system.web>
<compilation defaultLanguage="c#" /> <compilation defaultLanguage="c#" />
<globalization fileEncoding="utf-8" /> <globalization fileEncoding="utf-8" />
</system.web> </system.web>
</configuration> </configuration>
<!--ProjectGuid: 9fb2b925-dc18-4081-ac91-96f2c49415f9--> <!--ProjectGuid: 9fb2b925-dc18-4081-ac91-96f2c49415f9-->
+183 -178
View File
@@ -1,179 +1,184 @@
input:not([type="checkbox"]) html
{ {
padding: 10px; user-select: unset;
border-radius: 5px; }
border: 1px solid black;
width: 100%; input:not([type="checkbox"])
box-sizing: border-box; {
margin-bottom: 10px; padding: 10px;
} border-radius: 5px;
border: 1px solid black;
select width: 100%;
{ box-sizing: border-box;
padding: 10px; margin-bottom: 10px;
border: 1px solid black; }
border-radius: 5px;
width: 100%; select
-moz-appearance: none; /* Firefox */ {
-webkit-appearance: none; /* Safari and Chrome */ padding: 10px;
} border: 1px solid black;
border-radius: 5px;
input:invalid width: 100%;
{ -moz-appearance: none; /* Firefox */
border: 3px solid red; -webkit-appearance: none; /* Safari and Chrome */
} }
input[type="submit"], input:invalid
input[type="button"], {
button border: 3px solid red;
{ }
border-radius: 5px;
border: 0px; input[type="submit"],
padding: 10px 20px; input[type="button"],
background-color: #343434; button
color: white; {
cursor: pointer; border-radius: 5px;
} border: 0px;
padding: 10px 20px;
input[type="submit"]:disabled, background-color: #343434;
input[type="button"]:disabled, color: white;
button:disabled cursor: pointer;
{ }
background-color: gray;
cursor: initial; input[type="submit"]:disabled,
} input[type="button"]:disabled,
button:disabled
input[type="submit"]:hover:not(:disabled), {
input[type="button"]:hover:not(:disabled), background-color: gray;
button:hover:not(:disabled) cursor: initial;
{ }
background-color: #505050;
} input[type="submit"]:hover:not(:disabled),
input[type="button"]:hover:not(:disabled),
input[type="submit"]:active, button:hover:not(:disabled)
input[type="button"]:active, {
button:active background-color: #505050;
{ }
background-color: black;
} input[type="submit"]:active,
input[type="button"]:active,
input[type="submit"][required], button:active
input[type="button"][required] {
{ background-color: black;
background-color: red; }
}
input[type="submit"][required],
input[type="submit"][required]:hover, input[type="button"][required]
input[type="button"][required]:hover {
{ background-color: red;
background-color: rgb(200, 0, 0); }
}
input[type="submit"][required]:hover,
input[type="submit"][required]:active, input[type="button"][required]:hover
input[type="button"][required]:active {
{ background-color: rgb(200, 0, 0);
background-color: darkred; }
}
input[type="submit"][required]:active,
input[readonly] input[type="button"][required]:active
{ {
background-color: lightgray; background-color: darkred;
} }
textarea input[readonly]
{ {
resize: none; background-color: lightgray;
width: 100%; }
border-radius: 5px;
border: 1px solid black; textarea
padding: 10px; {
box-sizing: border-box; resize: none;
height: 68vh; width: 100%;
font-family: 'Consolas'; border-radius: 5px;
} border: 1px solid black;
padding: 10px;
.select-container::after box-sizing: border-box;
{ height: 68vh;
content: '>'; font-family: 'Consolas';
transform: rotate(90deg); }
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg); .select-container::after
-ms-transform: rotate(90deg); {
right: 11px; content: '>';
top: 11px; transform: rotate(90deg);
position: absolute; -webkit-transform: rotate(90deg);
pointer-events: none; -moz-transform: rotate(90deg);
} -ms-transform: rotate(90deg);
right: 11px;
.select-container top: 11px;
{ position: absolute;
position: relative; pointer-events: none;
} }
form .select-container
{ {
max-width: 500px; position: relative;
width: 100%; }
}
form
.admin-menu > p a {
{ max-width: 500px;
font-size: 16pt; width: 100%;
} }
.comment.logout .admin-menu > p a
{ {
color: red !important; font-size: 16pt;
} }
.validation-summary-errors .comment.logout
{ {
color: red; color: red !important;
} }
th .validation-summary-errors
{ {
text-align: left; color: red;
} }
td th
{ {
text-align: start; text-align: left;
} }
table td
{ {
width: 100%; text-align: start;
} }
.text-danger table
{ {
text-decoration: solid; width: 100%;
color: red; }
}
.text-danger
.checkbox {
{ text-decoration: solid;
width: auto; color: red;
} }
.reorder-arrow:link .checkbox
{ {
text-decoration: none; width: auto;
} }
@media only screen and (max-width: 1080px) .reorder-arrow:link
{ {
.hide-l1 text-decoration: none;
{ }
display: none;
} @media only screen and (max-width: 1080px)
} {
.hide-l1
@media only screen and (max-width: 850px) {
{ display: none;
.hide-l2 }
{ }
display: none;
} @media only screen and (max-width: 850px)
{
.hide-l2
{
display: none;
}
} }
+6 -3
View File
@@ -18,7 +18,6 @@ nav
padding: 10px; padding: 10px;
min-height: 33px; min-height: 33px;
font-size: 26px; font-size: 26px;
user-select: none;
} }
nav a nav a
@@ -35,7 +34,6 @@ nav
nav div nav div
{ {
grid-column: 3; grid-column: 3;
user-select: none;
cursor: pointer; cursor: pointer;
} }
@@ -60,7 +58,6 @@ footer
align-items: center; align-items: center;
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto;
grid-column-gap: 10px; grid-column-gap: 10px;
user-select: none;
} }
footer a footer a
@@ -78,6 +75,7 @@ footer
html html
{ {
overflow: hidden; overflow: hidden;
user-select: none;
} }
body body
@@ -92,6 +90,11 @@ body
height: calc(100vh - 53px); height: calc(100vh - 53px);
} }
*[allow-select]
{
user-select: text;
}
header a header a
{ {
text-decoration: none; text-decoration: none;