Added link shortener
Fixed text selection
This commit is contained in:
@@ -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>
|
||||||
|
 <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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"] </a>
|
<a class="comment" asp-action="Print">// @Localizer["Print resume"] </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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user