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
@@ -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,6 +1,7 @@
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;
using MyWebsite.Models.Databases; using MyWebsite.Models.Databases;
using MyWebsite.ViewModels; using MyWebsite.ViewModels;
using System; using System;
@@ -12,9 +13,14 @@ namespace MyWebsite.Controllers
{ {
public HomeController(DatabaseContext context) : base(context) { } public HomeController(DatabaseContext context) : base(context) { }
[Route("")] [Route("/{id?}")]
public IActionResult Index() => public IActionResult Index(string id = "")
View(); {
if (!string.IsNullOrWhiteSpace(id) && Database.ShortLinks.Find(id) is ShortLinkModel link)
return Redirect(link.Uri.OriginalString);
else
return View();
}
[Route("Contacts")] [Route("Contacts")]
public IActionResult Contacts() => public IActionResult Contacts() =>
@@ -10,6 +10,7 @@ namespace MyWebsite.Models.Databases
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<ShortLinkModel> ShortLinks { get; set; }
public DbSet<CustomData> CustomData { get; set; } public DbSet<CustomData> CustomData { get; set; }
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) => public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) =>
@@ -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; }
}
}
@@ -14,6 +14,9 @@
<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>
<a asp-action="Shortener" class="comment">// Link shortener</a>
</p>
<p> <p>
<a asp-action="FoxTube" class="comment">// FoxTube API</a><br /> <a asp-action="FoxTube" class="comment">// FoxTube API</a><br />
<a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a> <a asp-action="GUTSchedule" class="comment">// GUT.Schedule API</a>
+1 -1
View File
@@ -88,7 +88,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>
+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>
@@ -21,7 +21,7 @@
<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>
@@ -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>
@@ -34,7 +34,7 @@
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>
@@ -3,7 +3,7 @@
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>
@@ -6,7 +6,7 @@
<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>
@@ -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>
+4 -4
View File
@@ -5,10 +5,6 @@
<rewrite> <rewrite>
<rules> <rules>
<!--Legacy redirects--> <!--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"> <rule name="GUT.Schedule Privacy policy" stopProcessing="true">
<match url="Projects/GUTSchedule/PrivacyPolicy.txt" /> <match url="Projects/GUTSchedule/PrivacyPolicy.txt" />
<action type="Redirect" url="/Projects/GUTSchedule/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" /> <action type="Redirect" url="/Projects/GUTSchedule/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
@@ -21,6 +17,10 @@
<match url="Projects/FoxTube/PrivacyPolicy.txt" /> <match url="Projects/FoxTube/PrivacyPolicy.txt" />
<action type="Redirect" url="/Projects/FoxTube/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" /> <action type="Redirect" url="/Projects/FoxTube/PrivacyPolicy" redirectType="Permanent" appendQueryString="false" />
</rule> </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--> <!--/Legacy redirects-->
<rule name="Tabs Aside redirect" stopProcessing="true"> <rule name="Tabs Aside redirect" stopProcessing="true">
+6 -1
View File
@@ -1,4 +1,9 @@
input:not([type="checkbox"]) html
{
user-select: unset;
}
input:not([type="checkbox"])
{ {
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
+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;