diff options
Diffstat (limited to 'CoreWiki')
23 files changed, 334 insertions, 97 deletions
diff --git a/CoreWiki/Migrations/20230209093124_initial.Designer.cs b/CoreWiki/Migrations/20231222134125_Initial.Designer.cs index 11af7b7..8c6556e 100644 --- a/CoreWiki/Migrations/20230209093124_initial.Designer.cs +++ b/CoreWiki/Migrations/20231222134125_Initial.Designer.cs @@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace CoreWiki.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20230209093124_initial")] - partial class initial + [Migration("20231222134125_Initial")] + partial class Initial { /// <inheritdoc /> protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -22,19 +22,34 @@ namespace CoreWiki.Migrations modelBuilder.Entity("CoreWiki.Models.Article", b => { - b.Property<string>("Topic") + b.Property<string>("Slug") .HasColumnType("TEXT"); b.Property<string>("Content") .IsRequired() .HasColumnType("TEXT"); - b.Property<DateTime>("Published") + b.Property<DateTime>("PublishedDateTime") + .HasColumnType("TEXT") + .HasColumnName("Published"); + + b.Property<string>("Topic") + .IsRequired() + .HasMaxLength(100) .HasColumnType("TEXT"); - b.HasKey("Topic"); + b.HasKey("Slug"); b.ToTable("Articles"); + + b.HasData( + new + { + Slug = "home-page", + Content = "Welcome to your new CoreWiki installation", + PublishedDateTime = new DateTime(2023, 12, 22, 13, 41, 24, 978, DateTimeKind.Utc).AddTicks(110), + Topic = "Home Page" + }); }); #pragma warning restore 612, 618 } diff --git a/CoreWiki/Migrations/20230209093124_initial.cs b/CoreWiki/Migrations/20231222134125_Initial.cs index 84a7453..80c0c6e 100644 --- a/CoreWiki/Migrations/20230209093124_initial.cs +++ b/CoreWiki/Migrations/20231222134125_Initial.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace CoreWiki.Migrations { /// <inheritdoc /> - public partial class initial : Migration + public partial class Initial : Migration { /// <inheritdoc /> protected override void Up(MigrationBuilder migrationBuilder) @@ -15,14 +15,20 @@ namespace CoreWiki.Migrations name: "Articles", columns: table => new { - Topic = table.Column<string>(type: "TEXT", nullable: false), + Slug = table.Column<string>(type: "TEXT", nullable: false), + Topic = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false), Published = table.Column<DateTime>(type: "TEXT", nullable: false), Content = table.Column<string>(type: "TEXT", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_Articles", x => x.Topic); + table.PrimaryKey("PK_Articles", x => x.Slug); }); + + migrationBuilder.InsertData( + table: "Articles", + columns: new[] { "Slug", "Content", "Published", "Topic" }, + values: new object[] { "home-page", "Welcome to your new CoreWiki installation", new DateTime(2023, 12, 22, 13, 41, 24, 978, DateTimeKind.Utc).AddTicks(110), "Home Page" }); } /// <inheritdoc /> diff --git a/CoreWiki/Migrations/ApplicationDbContextModelSnapshot.cs b/CoreWiki/Migrations/ApplicationDbContextModelSnapshot.cs index fb063f5..4808c70 100644 --- a/CoreWiki/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/CoreWiki/Migrations/ApplicationDbContextModelSnapshot.cs @@ -19,19 +19,34 @@ namespace CoreWiki.Migrations modelBuilder.Entity("CoreWiki.Models.Article", b => { - b.Property<string>("Topic") + b.Property<string>("Slug") .HasColumnType("TEXT"); b.Property<string>("Content") .IsRequired() .HasColumnType("TEXT"); - b.Property<DateTime>("Published") + b.Property<DateTime>("PublishedDateTime") + .HasColumnType("TEXT") + .HasColumnName("Published"); + + b.Property<string>("Topic") + .IsRequired() + .HasMaxLength(100) .HasColumnType("TEXT"); - b.HasKey("Topic"); + b.HasKey("Slug"); b.ToTable("Articles"); + + b.HasData( + new + { + Slug = "home-page", + Content = "Welcome to your new CoreWiki installation", + PublishedDateTime = new DateTime(2023, 12, 22, 13, 41, 24, 978, DateTimeKind.Utc).AddTicks(110), + Topic = "Home Page" + }); }); #pragma warning restore 612, 618 } diff --git a/CoreWiki/Models/ApplicationDbContext.cs b/CoreWiki/Models/ApplicationDbContext.cs index 48b5dcf..38c3787 100644 --- a/CoreWiki/Models/ApplicationDbContext.cs +++ b/CoreWiki/Models/ApplicationDbContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using NodaTime; namespace CoreWiki.Models; @@ -8,7 +9,13 @@ public class ApplicationDbContext : DbContext public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { - } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity<Article>().HasData( + new Article { Topic = "Home Page", Slug = "home-page", Content = "Welcome to your new CoreWiki installation" } + ); + } }
\ No newline at end of file diff --git a/CoreWiki/Models/Article.cs b/CoreWiki/Models/Article.cs index 53c2706..b989573 100644 --- a/CoreWiki/Models/Article.cs +++ b/CoreWiki/Models/Article.cs @@ -2,23 +2,32 @@ using NodaTime; using NodaTime.Extensions; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using CoreWiki.Utils; namespace CoreWiki.Models; public class Article { - [Required, Key] + [Key] + public string Slug { get; set; } + + [Required, MaxLength(100)] public string Topic { get; set; } + [NotMapped] public Instant Published { get; set; } = SystemClock.Instance.GetCurrentInstant(); + [Obsolete("This property is only for serialization")] [DataType(DataType.DateTime)] [Column("Published")] + [Required] public DateTime PublishedDateTime { get => Published.ToDateTimeUtc(); set => Published = DateTime.SpecifyKind(value, DateTimeKind.Utc).ToInstant(); } + [DataType(DataType.MultilineText)] - public string Content { get; set; } + [Required] + public string Content { get; set; } = default!; }
\ No newline at end of file diff --git a/CoreWiki/Pages/All.cshtml b/CoreWiki/Pages/All.cshtml new file mode 100644 index 0000000..ca06cb5 --- /dev/null +++ b/CoreWiki/Pages/All.cshtml @@ -0,0 +1,48 @@ +@page +@model CoreWiki.Pages.All + +@{ + ViewData["Title"] = "All articles"; +} + +<h2>All articles</h2> + +@foreach (var item in Model.Articles) +{ + <div class="card border-primary my-2 mx-1"> + <div class="card-body"> + <h3 class="card-title"> + <a href="~/@item.Slug">@item.Topic</a> + </h3> + <h6 class="card-subtitle mb-2 text-muted"> + <span data-value="@item.Published" class="timeStampValue">@item.Published</span> + </h6> + + <a class="card-link" asp-page="./Edit" asp-route-slug="@item.Slug">Edit</a> + <a class="card-link" asp-page="./Delete" asp-route-slug="@item.Slug">Delete</a> + </div> + </div> +} + +<div class="col-md-12 d-flex"> + <nav class="flex-grow-1"> + <ul class="pagination"> + @for (var i = 1; i <= Model.TotalPages; i++) + { + var i1 = i; + <li class="page-item @(i1 == Model.PageNumber ? "active" : "")"> + <a class="page-link" asp-page="All" asp-route-PageNumber="@i1" asp-route-PageSize="@Model.PageSize">@i1</a> + </li> + } + </ul> + </nav> + <form class="ms-auto" method="get" asp-page="All"> + <input hidden name="@nameof(Model.PageNumber)" value="@Model.PageNumber"/> + <select class="form-select" asp-for="@Model.PageSize" onchange="this.form.submit()"> + <option value="2">2</option> + <option value="10">10</option> + <option value="25">25</option> + <option value="50">50</option> + </select> + </form> +</div>
\ No newline at end of file diff --git a/CoreWiki/Pages/All.cshtml.cs b/CoreWiki/Pages/All.cshtml.cs new file mode 100644 index 0000000..c41e216 --- /dev/null +++ b/CoreWiki/Pages/All.cshtml.cs @@ -0,0 +1,39 @@ +using CoreWiki.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace CoreWiki.Pages; + +public class All : PageModel +{ + private readonly ApplicationDbContext _context; + + [BindProperty(SupportsGet = true)] + public int PageNumber { get; set; } = 1; + [BindProperty(SupportsGet = true)] + public int PageSize { get; set; } = 25; + public IEnumerable<Article>? Articles { get; set; } + public int TotalPages { get; set; } + + public All(ApplicationDbContext context) + { + _context = context; + } + + public async Task<IActionResult> OnGetAsync() + { + TotalPages = (int)Math.Ceiling(await _context.Articles.CountAsync() / (float)PageSize); + if (PageNumber > TotalPages) PageNumber = 1; + + Articles = await _context.Articles + .AsNoTracking() + .OrderBy(a => a.PublishedDateTime) + .Skip(PageSize * (PageNumber - 1)) + .Take(PageSize) + .ToArrayAsync(); + + return Page(); + } +}
\ No newline at end of file diff --git a/CoreWiki/Pages/Create.cshtml b/CoreWiki/Pages/Create.cshtml index fc74317..4abbf11 100644 --- a/CoreWiki/Pages/Create.cshtml +++ b/CoreWiki/Pages/Create.cshtml @@ -9,8 +9,8 @@ <h4>Article</h4>
<hr />
-<div class="row">
- <div class="col-md-4">
+<div class="row d-flex justify-content-center">
+ <div class="col-md-8">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
diff --git a/CoreWiki/Pages/Create.cshtml.cs b/CoreWiki/Pages/Create.cshtml.cs index d89b97b..0bf9327 100644 --- a/CoreWiki/Pages/Create.cshtml.cs +++ b/CoreWiki/Pages/Create.cshtml.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using CoreWiki.Models;
+using CoreWiki.Utils;
using NodaTime;
namespace CoreWiki.Pages
@@ -30,19 +31,26 @@ namespace CoreWiki.Pages public Article Article { get; set; } = default!;
- // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
public async Task<IActionResult> OnPostAsync()
{
- if (!ModelState.IsValid || _context.Articles == null || Article == null)
+ ModelState.Remove("Article.Slug");
+ if (!ModelState.IsValid)
{
return Page();
}
+ Article.Slug = SafeUrl.Create(true, Article.Topic);
Article.Published = _clock.GetCurrentInstant();
+ if (_context.Articles.Any(a => a.Slug == Article.Slug))
+ {
+ ModelState.AddModelError("Article.Topic", "Article already exists");
+ return Page();
+ }
+
_context.Articles.Add(Article);
await _context.SaveChangesAsync();
- return Redirect($"/{Article.Topic}");
+ return Redirect($"/{Article.Slug}");
}
}
}
diff --git a/CoreWiki/Pages/Delete.cshtml b/CoreWiki/Pages/Delete.cshtml index ec40822..0b680b8 100644 --- a/CoreWiki/Pages/Delete.cshtml +++ b/CoreWiki/Pages/Delete.cshtml @@ -1,4 +1,4 @@ -@page
+@page "/Delete/{slug}"
@model CoreWiki.Pages.DeleteModel
@{
@@ -27,8 +27,8 @@ </dl>
<form method="post">
- <input type="hidden" asp-for="Article.Topic" />
+ <input type="hidden" asp-for="Article.Slug" />
<input type="submit" value="Delete" class="btn btn-danger" /> |
- <a asp-page="./Index">Back to List</a>
+ <a href="/">Back to Home Page</a>
</form>
</div>
diff --git a/CoreWiki/Pages/Delete.cshtml.cs b/CoreWiki/Pages/Delete.cshtml.cs index 9de563e..584dc48 100644 --- a/CoreWiki/Pages/Delete.cshtml.cs +++ b/CoreWiki/Pages/Delete.cshtml.cs @@ -19,42 +19,39 @@ namespace CoreWiki.Pages }
[BindProperty]
- public Article Article { get; set; } = default!;
+ public Article Article { get; set; } = default!;
- public async Task<IActionResult> OnGetAsync(string id)
+ public async Task<IActionResult> OnGetAsync(string slug)
{
- if (id == null || _context.Articles == null)
+ if (slug == "home-page")
{
return NotFound();
}
-
- var article = await _context.Articles.FirstOrDefaultAsync(m => m.Topic == id);
+
+ var article = await _context.Articles.FirstOrDefaultAsync(a => a.Slug == slug);
if (article == null)
{
return NotFound();
}
- else
- {
- Article = article;
- }
+
+ Article = article;
return Page();
}
- public async Task<IActionResult> OnPostAsync(string id)
+ public async Task<IActionResult> OnPostAsync(string slug)
{
- if (id == null || _context.Articles == null)
+ if (slug == "home-page")
{
return NotFound();
}
- var article = await _context.Articles.FindAsync(id);
+
+ var article = await _context.Articles.FirstOrDefaultAsync(a => a.Slug == slug);
- if (article != null)
- {
- Article = article;
- _context.Articles.Remove(Article);
- await _context.SaveChangesAsync();
- }
+ if (article == null) return NotFound();
+ Article = article;
+ _context.Articles.Remove(Article);
+ await _context.SaveChangesAsync();
return Redirect("/");
}
diff --git a/CoreWiki/Pages/Details.cshtml b/CoreWiki/Pages/Details.cshtml index bf1a616..9f48f9f 100644 --- a/CoreWiki/Pages/Details.cshtml +++ b/CoreWiki/Pages/Details.cshtml @@ -1,8 +1,8 @@ -@page "{topicName?}"
+@page "/{slug?}"
@model CoreWiki.Pages.DetailsModel
@{
- ViewData["Title"] = "Details";
+ ViewData["Title"] = Model.Article.Topic;
}
<h1>@Model.Article.Topic</h1>
@@ -11,8 +11,8 @@ <markdown markdown="Article.Content"/>
<div>
- <a asp-page="./Edit" asp-route-id="@Model.Article?.Topic">Edit</a>
- @if (Model.Article.Topic != "HomePage")
+ <a asp-page="./Edit" asp-route-slug="@Model.Article.Slug">Edit</a>
+ @if (Model.Article.Slug != "home-page")
{
<text>| </text> <a href="~/">Back to Home</a>
}
diff --git a/CoreWiki/Pages/Details.cshtml.cs b/CoreWiki/Pages/Details.cshtml.cs index 25ff6c0..e4100ac 100644 --- a/CoreWiki/Pages/Details.cshtml.cs +++ b/CoreWiki/Pages/Details.cshtml.cs @@ -11,33 +11,26 @@ namespace CoreWiki.Pages {
public class DetailsModel : PageModel
{
- private readonly CoreWiki.Models.ApplicationDbContext _context;
+ private readonly ApplicationDbContext _context;
- public DetailsModel(CoreWiki.Models.ApplicationDbContext context)
+ public DetailsModel(ApplicationDbContext context)
{
_context = context;
}
- public Article Article { get; set; } = default!;
+ public Article Article { get; set; } = default!;
- public async Task<IActionResult> OnGetAsync(string? topicName)
+ public async Task<IActionResult> OnGetAsync(string? slug)
{
- topicName ??= "HomePage";
-
- if (_context.Articles == null)
- {
- return NotFound();
- }
+ slug ??= "home-page";
- var article = await _context.Articles.FirstOrDefaultAsync(m => m.Topic == topicName);
+ var article = await _context.Articles.FirstOrDefaultAsync(a => a.Slug == slug);
if (article == null)
{
return NotFound();
}
- else
- {
- Article = article;
- }
+
+ Article = article;
return Page();
}
}
diff --git a/CoreWiki/Pages/Edit.cshtml b/CoreWiki/Pages/Edit.cshtml index 7609348..8793034 100644 --- a/CoreWiki/Pages/Edit.cshtml +++ b/CoreWiki/Pages/Edit.cshtml @@ -1,4 +1,4 @@ -@page
+@page "/Edit/{slug}"
@using CoreWiki.Models
@model CoreWiki.Pages.EditModel
@@ -8,17 +8,15 @@ <h1>Edit</h1>
-<h3>@Model.Article.Topic</h3>
-<hr />
+<br />
<div class="row">
- <div class="col-md-4">
+ <div class="col-md-12">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
+ <input type="hidden" asp-for="Article.Slug" />
<input type="hidden" asp-for="Article.Topic" />
<div class="form-group">
- <label asp-for="Article.Published" class="control-label"></label>
- <input disabled asp-for="Article.Published" class="form-control" />
- <span asp-validation-for="Article.Published" class="text-danger"></span>
+ <h3>@Model.Article.Topic</h3>
</div>
<div class="form-group">
<label asp-for="Article.Content" class="control-label"></label>
diff --git a/CoreWiki/Pages/Edit.cshtml.cs b/CoreWiki/Pages/Edit.cshtml.cs index e3b11c2..46920cb 100644 --- a/CoreWiki/Pages/Edit.cshtml.cs +++ b/CoreWiki/Pages/Edit.cshtml.cs @@ -25,14 +25,9 @@ namespace CoreWiki.Pages [BindProperty]
public Article Article { get; set; } = default!;
- public async Task<IActionResult> OnGetAsync(string id)
+ public async Task<IActionResult> OnGetAsync(string slug)
{
- if (id == null || _context.Articles == null)
- {
- return NotFound();
- }
-
- var article = await _context.Articles.FirstOrDefaultAsync(m => m.Topic == id);
+ var article = await _context.Articles.FirstOrDefaultAsync(m => m.Slug == slug);
if (article == null)
{
return NotFound();
@@ -40,9 +35,7 @@ namespace CoreWiki.Pages Article = article;
return Page();
}
-
- // To protect from overposting attacks, enable the specific properties you want to bind to.
- // For more details, see https://aka.ms/RazorPagesCRUD.
+
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
@@ -69,12 +62,12 @@ namespace CoreWiki.Pages }
}
- return Redirect($"./{(Article.Topic == "HomePage" ? "" : Article.Topic)}");
+ return Redirect($"/{(Article.Slug == "home-page" ? "" : Article.Slug)}");
}
private bool ArticleExists(string id)
{
- return (_context.Articles?.Any(e => e.Topic == id)).GetValueOrDefault();
+ return (_context.Articles?.Any(e => e.Slug == id)).GetValueOrDefault();
}
}
}
diff --git a/CoreWiki/Pages/LatestChanges.cshtml b/CoreWiki/Pages/LatestChanges.cshtml index 390c8ab..677887b 100644 --- a/CoreWiki/Pages/LatestChanges.cshtml +++ b/CoreWiki/Pages/LatestChanges.cshtml @@ -15,11 +15,11 @@ {
<div class="card border-primary m-1">
<div class="card-body">
- <h3 class="card-title"><a href="~/@item.Topic">@item.Topic</a></h3>
+ <h3 class="card-title"><a href="~/@item.Slug">@item.Topic</a></h3>
<h6 class="card-subtitle mb-2 text-muted"><span data-value="@item.Published" class="timeStampValue">@item.Published</span></h6>
- <a class="card-link" asp-page="./Edit" asp-route-id="@item.Topic">Edit</a>
- <a class="card-link" asp-page="./Delete" asp-route-id="@item.Topic">Delete</a>
+ <a class="card-link" asp-page="./Edit" asp-route-slug="@item.Slug">Edit</a>
+ <a class="card-link" asp-page="./Delete" asp-route-slug="@item.Slug">Delete</a>
</div>
</div>
}
\ No newline at end of file diff --git a/CoreWiki/Pages/Shared/_Layout.cshtml b/CoreWiki/Pages/Shared/_Layout.cshtml index dbe5e47..ac74eba 100644 --- a/CoreWiki/Pages/Shared/_Layout.cshtml +++ b/CoreWiki/Pages/Shared/_Layout.cshtml @@ -7,14 +7,14 @@ <link rel="stylesheet" href="~/lib/bootstrap/css/bootstrap.min.css"/> <link rel="stylesheet" href="~/css/site.css" asp-append-version="true"/> <link rel="stylesheet" href="~/CoreWiki.styles.css" asp-append-version="true"/> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" referrerpolicy="no-referrer" /> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" referrerpolicy="no-referrer"/> @await RenderSectionAsync("Styles", required: false) </head> <body> <header> - <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-dark bg-dark border-bottom box-shadow mb-3"> + <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-light border-bottom box-shadow mb-3"> <div class="container"> - <a class="navbar-brand" asp-area="" asp-page="/Index">CoreWiki</a> + <a class="navbar-brand" asp-page="Details">CoreWiki</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> @@ -22,13 +22,16 @@ <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between"> <ul class="navbar-nav flex-grow-1"> <li class="nav-item"> - <a class="nav-link text-light" href="~/Index">Home</a> + <a class="nav-link" asp-page="Details">Home</a> </li> <li class="nav-item"> - <a class="nav-link text-light" asp-area="" asp-page="/Create">Create</a> + <a class="nav-link" asp-area="" asp-page="Create">Create</a> </li> <li class="nav-item"> - <a class="nav-link text-light" href="~/LatestChanges">Latest Changes</a> + <a class="nav-link" asp-page="All">All Articles</a> + </li> + <li class="nav-item"> + <a class="nav-link" asp-page="LatestChanges">Latest Changes</a> </li> </ul> </div> diff --git a/CoreWiki/Program.cs b/CoreWiki/Program.cs index d31ad67..2a26753 100644 --- a/CoreWiki/Program.cs +++ b/CoreWiki/Program.cs @@ -8,18 +8,20 @@ var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); -builder.Services.AddEntityFrameworkSqlite() +builder.Services .AddDbContext<ApplicationDbContext>(options => options.UseSqlite("Data Source=./wiki.db")); - +builder.Services.AddRouting(options => +{ + options.LowercaseUrls = true; +}); builder.Services.AddSingleton<IClock>(SystemClock.Instance); builder.Services.AddMvc().AddRazorPagesOptions(options => { - options.Conventions.AddPageRoute("/Details", "{topicName?}"); - options.Conventions.AddPageRoute("/Details", "Index"); + options.Conventions.AddPageRoute("/Details", "/"); }); - var app = builder.Build(); +var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) @@ -40,4 +42,4 @@ app.UseAuthorization(); app.MapRazorPages(); -app.Run(); +app.Run();
\ No newline at end of file diff --git a/CoreWiki/Utils/SafeUrl.cs b/CoreWiki/Utils/SafeUrl.cs new file mode 100644 index 0000000..b2de803 --- /dev/null +++ b/CoreWiki/Utils/SafeUrl.cs @@ -0,0 +1,104 @@ +using System.Text; + +namespace CoreWiki.Utils; + +public class SafeUrl +{ + public static string Create(bool toLower, params string[] values) + { + return Create(toLower, String.Join("-", values)); + } + + public static string Create(bool toLower, string value) + { + var normalised = value.Normalize(NormalizationForm.FormKD); + + const int maxlen = 80; + int len = normalised.Length; + bool prevDash = false; + var sb = new StringBuilder(len); + char c; + + for (int i = 0; i < len; i++) + { + c = normalised[i]; + if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) + { + if (prevDash) + { + sb.Append('-'); + prevDash = false; + } + sb.Append(c); + } + else if (c >= 'A' && c <= 'Z') + { + if (prevDash) + { + sb.Append('-'); + prevDash = false; + } + // Tricky way to convert to lowercase + if (toLower) + sb.Append((char)(c | 32)); + else + sb.Append(c); + } + else if (c == ' ' || c == ',' || c == '.' || c == '/' || c == '\\' || c == '-' || c == '_' || c == '=') + { + if (!prevDash && sb.Length > 0) + { + prevDash = true; + } + } + else + { + string swap = ConvertEdgeCases(c, toLower); + + if (swap != null) + { + if (prevDash) + { + sb.Append('-'); + prevDash = false; + } + sb.Append(swap); + } + } + + if (sb.Length == maxlen) + break; + } + return sb.ToString(); + } + + static string ConvertEdgeCases(char c, bool toLower) + { + string swap = null; + switch (c) + { + case 'ı': + swap = "i"; + break; + case 'ł': + swap = "l"; + break; + case 'Ł': + swap = toLower ? "l" : "L"; + break; + case 'đ': + swap = "d"; + break; + case 'ß': + swap = "ss"; + break; + case 'ø': + swap = "o"; + break; + case 'Þ': + swap = "th"; + break; + } + return swap; + } +}
\ No newline at end of file diff --git a/CoreWiki/wiki.db b/CoreWiki/wiki.db Binary files differindex 4722ef3..fe3d8a0 100644 --- a/CoreWiki/wiki.db +++ b/CoreWiki/wiki.db diff --git a/CoreWiki/wiki.db-shm b/CoreWiki/wiki.db-shm Binary files differdeleted file mode 100644 index c7890fa..0000000 --- a/CoreWiki/wiki.db-shm +++ /dev/null diff --git a/CoreWiki/wiki.db-wal b/CoreWiki/wiki.db-wal Binary files differdeleted file mode 100644 index 2cce672..0000000 --- a/CoreWiki/wiki.db-wal +++ /dev/null diff --git a/CoreWiki/wwwroot/js/site.js b/CoreWiki/wwwroot/js/site.js index 02fb148..244a5c5 100644 --- a/CoreWiki/wwwroot/js/site.js +++ b/CoreWiki/wwwroot/js/site.js @@ -3,10 +3,10 @@ // Write your JavaScript code. (function () { - var timeStamps = document.querySelectorAll(".timeStampValue"); - for (var ts of timeStamps) { - var thisTimeStamp = ts.getAttribute("data-value"); - var date = new Date(thisTimeStamp); + const timeStamps = document.querySelectorAll(".timeStampValue"); + for (const ts of timeStamps) { + const thisTimeStamp = ts.getAttribute("data-value"); + const date = new Date(thisTimeStamp); ts.textContent = moment(date).format('LLL'); } })();
\ No newline at end of file |
