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>
|
||||
<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>
|
||||
 <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.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MyWebsite.Models;
|
||||
using MyWebsite.Models.Databases;
|
||||
using MyWebsite.ViewModels;
|
||||
using System;
|
||||
@@ -12,9 +13,14 @@ namespace MyWebsite.Controllers
|
||||
{
|
||||
public HomeController(DatabaseContext context) : base(context) { }
|
||||
|
||||
[Route("")]
|
||||
public IActionResult Index() =>
|
||||
View();
|
||||
[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() =>
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace MyWebsite.Models.Databases
|
||||
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) =>
|
||||
|
||||
@@ -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="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>
|
||||
|
||||
@@ -88,7 +88,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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<article class="image-overview-block">
|
||||
<img src="~/images/Gallery/@Model.Current?.FileName" onclick="ToggleImageSize();" />
|
||||
|
||||
<div>
|
||||
<div allow-select>
|
||||
<h1>@Model.Current?.Title</h1>
|
||||
<span>@Localizer["Creation date"]: @Model.Current?.CreationDate.ToShortDateString()</span>
|
||||
<p>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<a class="comment" asp-action="Print">// @Localizer["Print resume"] </a>
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<article allow-select>
|
||||
@Html.Raw(Model.Resume.Content)
|
||||
</article>
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
window.print();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<body allow-select>
|
||||
@Html.Raw(Model.Resume.Content)
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,7 +3,7 @@
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<header>
|
||||
<header allow-select>
|
||||
<h1>Error @Context.Response.StatusCode</h1>
|
||||
<h2>@ReasonPhrases.GetReasonPhrase(Context.Response.StatusCode)</h2>
|
||||
</header>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<h1>@Localizer["Hello, World!"]</h1>
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
<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" />
|
||||
@@ -21,6 +17,10 @@
|
||||
<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">
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
input:not([type="checkbox"])
|
||||
html
|
||||
{
|
||||
user-select: unset;
|
||||
}
|
||||
|
||||
input:not([type="checkbox"])
|
||||
{
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user