Compare commits

...

19 commits

Author SHA1 Message Date
Chris Josten 7bcc91a2fa Use fallback date when there are no visible articles
This fixes a crash
2024-10-12 23:11:20 +02:00
Chris Josten 0e7aa0451f atom: add atom feed to website for posts 2024-10-12 22:31:23 +02:00
Chris Josten 63f177475b old style; update for new grid layout
It still looks terrible, but I suppose that was the entire idea of this
optional style
2024-10-12 20:54:03 +02:00
Chris Josten c68094cd11 gitignore: ignore result
This file is created by running nix build
2024-10-12 20:51:23 +02:00
Chris Josten 6874b14916 style: move to grid layout from flex
This allows to reposition items without hiding/showing classes
2024-10-12 20:37:26 +02:00
Chris Josten c1dfa1f065 Update copyright year 2024-10-12 14:04:51 +02:00
Chris Josten 0ad53b1a24 Update dub selections 2023-12-08 22:59:09 +01:00
Chris Josten 7bb5c65d90 Bump version to 0.0.3 2023-12-08 22:57:15 +01:00
Chris Josten 651668526d Update dmd version + dependencies 2023-12-08 22:52:11 +01:00
Chris Josten bdb7615044 Fix building on NixOS 2022-09-15 20:29:06 +02:00
Chris Josten 9a5aeb1526 Upgrade version number 2022-09-15 17:43:07 +02:00
Chris Josten cc526e88f0 Upgrade dependencies 2022-09-15 17:41:15 +02:00
Chris Josten 40eeb9d25a Fix whitespace 2022-05-30 12:48:57 +02:00
Chris Josten 16f25d8297 Update nix deps 2022-05-25 14:49:54 +02:00
Chris Josten 223ca8bc29 Workaround broken l10n, upgrade deps 2022-05-25 14:46:37 +02:00
Chris Josten 1d0d1a54b1 Fix crash caused by lastIndexOf returning negative numbers
The ArticleParser would crash when a negative number was returned by
lastIndexOf, since it was casted to an unsigned integer. This unsigned
integer then was used to allocate memory, which would allocate way to
much memory, causing a MemoryException and crashing the process.
2021-10-13 14:07:27 +02:00
Chris Josten 48b95c4a13 Article: parse document separators properly
Document separators should start with a newline, followed by three
dashes, followed by a newline again.
2021-10-11 15:15:12 +02:00
Chris Josten 75f5d447d6 Update dependencies Nix 2021-08-17 10:04:36 +02:00
Chris Josten e806dbf1cd Fix NixOS build 2021-06-27 12:50:21 +02:00
22 changed files with 376 additions and 150 deletions

1
.gitignore vendored
View file

@ -16,3 +16,4 @@ mijnblog-test-*
/articles/
/pages/
/projects/
/result

View file

@ -14,6 +14,14 @@ function get_default_code_class(meta)
end
end
function make_image_url_absolute (img)
if img.src:sub(1,1) == '/' then
img.src = os.getenv 'WEBROOT' .. img.src
end
return img
end
return {{Meta = get_default_code_class},
{Code = add_default_code_class},
{CodeBlock = add_default_code_class}}
{CodeBlock = add_default_code_class},
{Image = make_image_url_absolute}}

View file

@ -11,5 +11,6 @@
"license": "AGPLv3",
"name": "mijnblog",
"targetType": "executable",
"stringImportPaths": ["views", "translations"]
"stringImportPaths": ["views", "translations"],
"versions": ["DeimosOpenSSL_3_0"]
}

View file

@ -3,19 +3,21 @@
"versions": {
"botan": "1.12.19",
"botan-math": "1.0.3",
"diet-ng": "1.7.5",
"dyaml": "0.8.3",
"eventcore": "0.9.13",
"diet-ng": "1.8.1",
"dyaml": "0.8.6",
"eventcore": "0.9.26",
"fswatch": "0.5.0",
"libasync": "0.8.6",
"libevent": "2.0.2+2.0.16",
"memutils": "1.0.4",
"memutils": "1.0.9",
"mir-linux-kernel": "1.0.1",
"openssl": "1.1.6+1.0.1g",
"openssl": "3.3.3",
"openssl-static": "1.0.2+3.0.8",
"stdx-allocator": "2.77.5",
"taggedalgebraic": "0.11.19",
"taggedalgebraic": "0.11.22",
"tinyendian": "0.2.0",
"vibe-core": "1.13.0",
"vibe-d": "0.9.3"
"vibe-container": "1.0.1",
"vibe-core": "2.5.1",
"vibe-d": "0.9.7"
}
}

View file

@ -51,49 +51,61 @@
fetch = {
type = "git";
url = "https://github.com/etcimon/memutils.git";
rev = "v1.0.4";
sha256 = "1m65iy03yl5km61ijk5ysapxc5mks9b15hmaqxpn111981qrqx9z";
rev = "v1.0.9";
sha256 = "08j9grn3l6gr275m392l0dfsx5ma6hs08gjfhh48hpy3v0bvb0ny";
fetchSubmodules = false;
date = "2020-02-02T20:53:12-05:00";
date = "2023-03-02T15:19:28-05:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/3pmdm9szxsqfvi2dv8b58vk7856g3gd8-memutils";
path = "/nix/store/glnf0ljd84mv6gzc9wgs1qq0vqqxvmzh-memutils";
};
} {
fetch = {
type = "git";
url = "https://github.com/s-ludwig/taggedalgebraic.git";
rev = "v0.11.19";
sha256 = "1mb4l9hhkzhwwj2v3m9l4g59q66msy15ky762wk5dv11viyfwrqb";
rev = "v0.11.22";
sha256 = "02iy90nwy0zzy25hwdqbcgd0w0lwzramcvi3pgyljhq0w0vl5hkq";
fetchSubmodules = false;
date = "2021-01-13T16:58:20+01:00";
date = "2021-05-20T21:00:02+02:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/qlw3bv0xqd0dl01hd9lcyk9cx7v9qhvi-taggedalgebraic";
path = "/nix/store/p8id0qb13j8pjdczflj2x35w6v63q4cx-taggedalgebraic";
};
} {
fetch = {
type = "git";
url = "https://github.com/vibe-d/vibe-core.git";
rev = "v1.13.0";
sha256 = "1zbx861dwmkp14pbzr4qyq71xsnlksx248x0a1q6afjmvvxiys8w";
url = "https://github.com/vibe-d/vibe-container.git";
rev = "v1.0.1";
sha256 = "0miynjmfzz340z3qw9mclaj6cyirzrpk528idr5yfmvl3jsi6wh8";
fetchSubmodules = false;
date = "2021-01-15T21:35:13+01:00";
date = "2023-11-27T09:12:47+01:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/zngy3z8hmdgjg8wl31d2qy194mhz0i09-vibe-core";
path = "/nix/store/2vnn20q8k335h5k078yk4lrxkp4y63pd-vibe-container";
};
} {
fetch = {
type = "git";
url = "https://github.com/vibe-d/vibe.d.git";
rev = "v0.9.3";
sha256 = "10a5njn2nq1z6gknmkg6m6wrbndzj19mpg5860ky6v12b9ks76z3";
rev = "v0.9.7";
sha256 = "1q4yvcaf36lmf29izg01x888v40snpwiph310nrlpr4gyhd834av";
fetchSubmodules = false;
date = "2021-01-29T11:37:00+01:00";
date = "2023-08-29T13:24:09+02:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/8r3h7npkhj9wq9lg64i3fih19hibpmg5-vibe.d";
path = "/nix/store/7fq089i7zib7m9hxyl75mlfy264d9gnx-vibe.d";
};
} {
fetch = {
type = "git";
url = "https://github.com/vibe-d/vibe-core.git";
rev = "v2.5.1";
sha256 = "1g66vyn9hivy8rmsdl1xg1vz41csq5k91c04yvp4wqbil9cwgqdr";
fetchSubmodules = false;
date = "2023-11-24T17:25:20+01:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/iddq1amvzic8fn4xa2317ykmkrkdr51y-vibe-core";
};
} {
fetch = {
@ -123,13 +135,13 @@
fetch = {
type = "git";
url = "https://github.com/rejectedsoftware/diet-ng.git";
rev = "v1.7.5";
sha256 = "1cymg3v924d499sbjagjf5dqv1pj196c55a2282knxgq18a3gynf";
rev = "v1.8.1";
sha256 = "11hrbvsxhipvcz9m1qlq92iaw0h35pzdy5rf7z8c4vx3hwjrnhni";
fetchSubmodules = false;
date = "2021-02-07T14:20:30+01:00";
date = "2022-04-22T11:38:43+02:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/5r6v9f7y9kwkb90ihgimqqryf6ls3kqq-diet-ng";
path = "/nix/store/xziliy9ah3s3ah8mslnxsw47n4w6wkca-diet-ng";
};
} {
fetch = {
@ -147,37 +159,37 @@
fetch = {
type = "git";
url = "https://github.com/D-Programming-Deimos/openssl.git";
rev = "v1.1.6+1.0.1g";
sha256 = "0ramqjyq4v7xpqwf4nf4ddmsg6yk2fbzn2d1yj4b6dla3q5lv9i3";
rev = "v3.3.3";
sha256 = "1634j4psp3qwgwhk2sa3jj6gvwv3i96hpg2wrdy9ihjhabnszn0f";
fetchSubmodules = false;
date = "2017-11-05T20:15:26+01:00";
date = "2023-09-14T12:05:32+00:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/jbfb5in6gzqs2byn1hdc1625yh8sx6j2-openssl";
path = "/nix/store/i3pgh154r80xvh4vsnl6srg6khmk5dg7-openssl";
};
} {
fetch = {
type = "git";
url = "https://github.com/vibe-d/eventcore.git";
rev = "v0.9.13";
sha256 = "0frxifhjwzyi35cv4pvv8k11a966fg76gqxdpmx9bsqbx750lrvz";
rev = "v0.9.26";
sha256 = "13bjs5v5l1387vi2ss4gvqlslhq33v9sn4pg7nis4r0wdw0zmlk1";
fetchSubmodules = false;
date = "2021-01-12T19:20:28+01:00";
date = "2023-09-16T09:46:45+02:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/yikgbvj61xhk6r3x8pc8lb7sp5smcn5q-eventcore";
path = "/nix/store/zy7ydasg4fwnim2m1mi4gzdg9l087wpb-eventcore";
};
} {
fetch = {
type = "git";
url = "https://github.com/kiith-sa/D-YAML.git";
rev = "v0.8.3";
sha256 = "13wy304xjbwkpgg7ilql1lkxkm83s87jm59ffnrg26slp7cx149q";
rev = "v0.8.6";
sha256 = "1bvidcxp1n65r4wmiqakyl8vjvhqh3gln9wbsmbxrx9mf6k0zv8h";
fetchSubmodules = false;
date = "2020-09-19T23:46:57+02:00";
date = "2022-05-15T16:18:02-03:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/3i8i56lkmw2xq3lxr5h66v909waq2mqg-D-YAML";
path = "/nix/store/zd0yayd1j11809j95sjs4gdqcngvkhbg-D-YAML";
};
} {
fetch = {
@ -191,4 +203,16 @@
leaveDotGit = false;
path = "/nix/store/6l82qf5nav5pkbvnrhcs5v9vwj5xycq3-libasync";
};
} {
fetch = {
type = "git";
url = "https://github.com/bildhuus/deimos-openssl-static.git";
rev = "v1.0.2+3.0.8";
sha256 = "00wllmfrjpq5ln3zs9qcqf4kq2i6bqbjxwj0jnxwwz125kfm55sy";
fetchSubmodules = false;
date = "2023-02-24T13:23:37+01:00";
deepClone = false;
leaveDotGit = false;
path = "/nix/store/g9shdkm450yg8c15rvqms8v09vql8z0l-deimos-openssl-static";
};
} ]

View file

@ -6,7 +6,11 @@ mkDubDerivation {
src = ./.;
dubJSON = ./dub.json;
selections = ./dub.selections.nix;
version = "0.0.1";
version = "0.0.3";
buildInputs = [ pkgs.openssl ];
propagatedBuildInputs = [ pkgs.nix-prefetch-git ];
extraDubFlags = "--override-config openssl/library-manual-version";
preBuild = ''
export DC=${pkgs.dmd}/bin/dmd
'';
}

View file

@ -1,7 +1,9 @@
{ pkgs ? import <nixpkgs> {},
stdenv ? pkgs.stdenv,
rdmd ? pkgs.rdmd,
lib ? pkgs.lib,
dtools ? pkgs.dtools or pkgs.rdmd,
dmd ? pkgs.dmd,
dcompiler ? dmd,
dub ? pkgs.dub }:
with stdenv;
@ -20,7 +22,7 @@ let
fromDub = dubDep: mkDerivation rec {
name = "${src.name}-${version}";
version = rev-to-version dubDep.fetch.rev;
nativeBuildInputs = [ rdmd dmd dub ];
nativeBuildInputs = [ dcompiler dtools dub ];
src = dep2src dubDep;
buildPhase = ''
@ -46,10 +48,18 @@ let
targetOf = package: "${package.targetPath or "."}/${package.targetName or package.name}";
# Remove reference to build tools and library sources
disallowedReferences = deps: [ dmd rdmd dub ] ++ builtins.map dep2src deps;
disallowedReferences = deps: [ dcompiler dtools dub ] ++ builtins.map dep2src deps;
removeExpr = refs: ''remove-references-to ${lib.concatMapStrings (ref: " -t ${ref}") refs}'';
# Like split, but only keep the matches
matches = regex: str: builtins.filter lib.isList (builtins.split regex str);
# Very primitive parsing of SDL files, but suffices for name, description, homepage, etc.
importSDL = path: builtins.foldl' (a: l: a // {"${lib.elemAt l 1}"=lib.elemAt l 2;}) {} (matches "(^|\n)([a-z]+) \"([^\"]+)\"" (builtins.readFile path));
importPackage = sdl: json: if builtins.pathExists sdl then importSDL sdl else lib.importJSON json;
in {
inherit fromDub;
@ -57,20 +67,23 @@ in {
src,
nativeBuildInputs ? [],
dubJSON ? src + "/dub.json",
dubSDL ? src + "/dub.sdl",
buildType ? "release",
extraDubFlags ? "",
selections ? src + "/dub.selections.nix",
deps ? import selections,
package ? importPackage dubSDL dubJSON,
passthru ? {},
package ? lib.importJSON dubJSON,
...
} @ attrs: stdenv.mkDerivation (attrs // {
} @ attrs: stdenv.mkDerivation ((removeAttrs attrs ["package" "deps" "selections" "dubJSON" "dubSDL"]) // {
pname = package.name;
nativeBuildInputs = [ rdmd dmd dub pkgs.removeReferencesTo ] ++ nativeBuildInputs;
nativeBuildInputs = [ dcompiler dtools dub pkgs.removeReferencesTo ] ++ nativeBuildInputs;
disallowedReferences = disallowedReferences deps;
passthru = passthru // {
inherit dub dmd rdmd pkgs;
inherit dub dcompiler dtools pkgs;
};
src = lib.cleanSourceWith {
@ -87,7 +100,7 @@ in {
export HOME=$PWD
${lib.concatMapStringsSep "\n" dub-add-local deps}
dub build -b release --combined --skip-registry=all
dub build -b ${buildType} --combined --skip-registry=all ${extraDubFlags}
runHook postBuild
'';
@ -97,7 +110,7 @@ in {
export HOME=$PWD
${lib.concatMapStringsSep "\n" dub-add-local deps}
dub test --combined --skip-registry=all
dub test --combined --skip-registry=all ${extraDubFlags}
runHook postCheck
'';
@ -113,6 +126,8 @@ in {
meta = lib.optionalAttrs (package ? description) {
description = package.description;
} // lib.optionalAttrs (package ? homepage) {
homepage = package.homepage;
} // attrs.meta or {};
} // lib.optionalAttrs (!(attrs ? version)) {
# Use name from dub.json, unless pname and version are specified

View file

@ -1,32 +1,3 @@
@media (max-width: 850px) {
body {
flex-wrap: wrap;
}
body > main {
box-sizing: border-box;
min-width: 100%;
}
body > section.header-navigation, body > footer {
width: 100%;
flex-grow: 1 !important;
}
.hide-small {
display: none;
}
.hide-big {
display: block !important;
}
}
.hide-big {
display: none;
}
:root {
font-family: "sans-serif";
--colour-bg: #f0f0f0;
@ -62,48 +33,54 @@ body {
background-color: #f0f0f0;
background-color: var(--colour-bg);
margin: 0;
display: flex;
display: grid;
grid-template-columns: 2em 200px 600px min-content;
grid-column-gap: 2em;
grid-template-rows: 2em min-content min-content 1fr;
grid-template-areas:
". . main . "
". header main sidebar"
". menu main sidebar"
". footer main sidebar";
justify-content: center;
min-height: 100vh;
color: #000000;
color: var(--colour-fg);
}
body > section.header-navigation {
flex: 0 0 200px;
padding: 2em;
}
@media (pointer: coarse) {
body > section.header-navigation li {
padding: 0.25em 0;
}
body > nav {
grid-area: menu;
}
body > footer {
flex: 0 0 200px;
padding: 2em;
border: none;
grid-area: footer;
margin-top: 0;
}
body > section.header-navigation > header {
body > header {
grid-area: header;
text-align: center;
margin-bottom: 0;
}
body > section.header-navigation > header > img {
body > header > img {
width: 100%;
max-width: 160px;
}
body > section.main-sidebar {
grid-area: sidebar;
}
body > main {
flex: 1 1;
grid-area: main;
padding: 2em;
/* width: 600px; */
border-left: #7f0602 dotted 1px;
border-right: #7f0602 dotted 1px;
border-left: var(--colour-fg-highlight) dotted 1px;
border-right: var(--colour-fg-highlight) dotted 1px;
max-width: 600px;
max-width: calc(600px);
background-color: #ddd;
background-color: var(--colour-bg-main);
}
@ -274,11 +251,65 @@ a {
text-decoration: underline dotted;
}
a:hover {
a:hover, a:focus {
color: var(--colour-fg);
text-decoration: underline solid;
}
@media (max-width: calc(800px + 8em)) {
body {
grid-template-columns: 100%;
grid-column-gap: 2em;
grid-template-rows: repeat(4, min-content);
grid-template-areas:
"header"
"menu"
"main"
"footer";
}
body > .main-sidebar {
display: none;
}
body > main {
box-sizing: border-box;
min-width: 100%;
border: none;
border-top: #7f0602 dotted 1px;
border-bottom: #7f0602 dotted 1px;
border-top: var(--colour-fg-highlight) dotted 1px;
border-bottom: var(--colour-fg-highlight) dotted 1px;
}
body > nav, body > footer {
width: calc(100% - 4em);
padding: 2em;
}
}
@media (pointer: coarse) {
body > nav li a {
display: inline-block;
padding: 1em 2em;
width: calc(100% - 4em);
}
body > nav > ul {
width: 100%;
}
body > nav li:not(:last-child) {
border-bottom: 1px dotted #000000;
border-bottom: 1px dotted var(--colour-fg);
}
body > nav {
padding: 1em 0em;
width: 100%;
}
}
@media print {
body {
background-color: initial;

View file

@ -2,6 +2,17 @@ body {
background-image: url(old/chip.jpg);
cursor: url(old/cursor.gif), auto;
justify-content: flex-start;
grid-column-gap: 0em;
grid-template-columns: 200px 600px;
grid-template-rows: min-content min-content 1fr;
grid-template-areas:
"header main"
"menu main"
"footer main";
}
.main-sidebar {
display: none;
}
header {
@ -16,14 +27,15 @@ footer {
a:hover {
cursor: url(old/cursor-over.gif), auto;
color: red;
}
.header-navigation {
body > nav, body > header, body > footer {
background-color: cyan;
color: black;
}
.header-navigation> nav ul {
body > nav ul {
padding-left: 2em;
list-style-image: url(old/bullet.gif);
}

View file

@ -5,6 +5,7 @@ import article;
import page;
import project;
@safe:
/**
* Default ordering and list with pointers to ordered articles.

View file

@ -1,8 +1,10 @@
/**
* Constants which are passed to templates while rendering.
*/
class Constants {
struct Constants {
public static immutable string SITE_NAME = "Chris Josten's site";
public static immutable string SITE_HOST = "chris.netsoj.nl";
public static immutable string SITE_URL = "https://chris.netsoj.nl";
public static immutable string SITE_AUTHOR = "Chris Josten";
public static immutable string COPYRIGHT = "&copy; Chris Josten, 2020";
}

View file

@ -1,6 +1,11 @@
import std.algorithm : map, maxElement;
import std.format : format;
import std.string: join;
import vibe.d;
import cache;
import constants;
import article;
import page;
import project;
@ -58,7 +63,7 @@ struct TranslateContext {
mixin translationModule!"mijnblog";
static string determineLanguage(scope HTTPServerRequest req) {
if ("lang" !in req.query) return req.determineLanguageByHeader(languages); // default behaviour using "Accept-Language" header
return req.query.get("lang", "en_GB");
return determineLanguageByHeader(req.query.get("lang", "en_GB"), languages);
}
}
@ -80,7 +85,7 @@ string singleResponseMixin(string arrayName, string templateName) {
break;
default:
case HTML:
render!("` ~ templateName ~ `", content);
res.render!("` ~ templateName ~ `", content);
break;
}`;
}
@ -141,7 +146,61 @@ public:
// If no slug is supplied, it will be adjusted to "index"
req.params.addField("slug", "index");
mixin(singleResponseMixin("pages", "pages/page.dt"));
}
}
@path("/feeds/posts.atom")
void getPostFeed(HTTPServerRequest req, HTTPServerResponse res) {
Article[] articleList = articles.sortedList;
DateTime lastUpdated = articleList.length > 0
? articleList.map!"a.firstPublished()".maxElement
: DateTime(2019, 06, 30);
string response = q"EOS
<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom">
<updated>%s</updated>
<title>%s</title>
<icon>%s</icon>
<link href="%s/feeds/posts.atom" rel="self" />
<link href="%s" />
<id>urn:uuid:036f1087-7fcd-466d-866d-78a0c60038cd</id>
<author><name>%s</name></author>
%s
</feed>
EOS"
.format(
lastUpdated.toISOExtString() ~ "Z",
trWeb("template.feed.title").format(trWeb("template.feed.posts.title")),
Constants.SITE_URL ~ "/static/img/logo.png",
Constants.SITE_URL,
Constants.SITE_URL,
Constants.SITE_AUTHOR,
articleList.map!((article) {
return q"EOS
<entry>
<title>%s</title>
<link href="%s" />
<id>%s</id>
<published>%s</published>
<updated>%s</updated>
<summary>%s</summary>
<content type="html">%s</content>
</entry>
EOS"
.format(article.title,
Constants.SITE_URL ~ "/posts/" ~ article.slug,
"tag:" ~ Constants.SITE_HOST ~ ",2024:blog:posts:" ~ article.slug,
article.firstPublished().toISOExtString() ~ "Z",
article.updated().toISOExtString() ~ "Z",
article.excerpt(),
htmlEscape(article.content())
);
}).join()
);
res.writeBody(response, "application/atom+xml");
}
}
/**

View file

@ -9,19 +9,29 @@ import cache;
import http;
import watcher;
void watchTask(T, C)(C *cache, string directory) @safe nothrow {
do {
try {
initPages!T(cache, directory);
} catch(Exception e) {
logWarn("Error while watching pages: " ~ e.msg);
}
} while(Task.getThis().running);
}
void main() {
startHTTPServer();
// Start indexing pages.
runTask({
initPages!Page(&pages, "pages");
watchTask!Page(&pages, "pages");
});
runTask({
initPages!Article(&articles, "articles");
watchTask!Article(&articles, "articles");
});
runTask({
initPages!Project(&projects, "projects");
watchTask!Project(&projects, "projects");
});
runApplication();
}

View file

@ -10,6 +10,7 @@ import vibe.d;
import page;
import utils;
@safe:
/**
* Represents an article on the blog
@ -34,9 +35,9 @@ class Article : Page {
// Find the first header and mark everything up to that as
if (m_excerpt is null) {
// an excerpt, used in search results.
const uint seperatorIndex = cast(uint) indexOf(m_contentSource, "---\n");
const long seperatorIndex = cast(long) lastIndexOf(m_contentSource, "---\n");
this.m_excerpt = this.m_contentSource[seperatorIndex + 4..$];
const uint firstHeaderIndex = cast(uint) indexOf(this.m_excerpt, '#');
const long firstHeaderIndex = indexOf(this.m_excerpt, '#');
if (firstHeaderIndex >= 0) {
this.m_excerpt = this.m_excerpt[0..firstHeaderIndex];
}

View file

@ -7,8 +7,10 @@ import std.stdio;
import dyaml;
import vibe.vibe;
import constants;
import utils;
@safe:
/**
* Exception thrown when a page has syntax errors e.g.
@ -51,11 +53,11 @@ class Page {
/**
* Creates a page from a file. This will read from the file and parse it.
*/
this(string file) {
this(string file) @safe {
this.m_name = file;
this.m_contentSource = readText(file);
// Find the seperator and split the string in two
const uint seperatorIndex = cast(uint) lastIndexOf(m_contentSource, "---\n");
const long seperatorIndex = lastIndexOf(m_contentSource, "\n---\n");
enforce!ArticleParseException(seperatorIndex >= 0);
string header = m_contentSource[0..seperatorIndex];
@ -106,10 +108,12 @@ class Page {
"-f", "markdown",
"-t", "html",
"--lua-filter", "defaultClasses.lua"];
string[string] env;
env["WEBROOT"] = Constants.SITE_URL;
if (shiftHeader != 0) args ~= "--shift-heading-level-by=" ~ to!string(shiftHeader);
ProcessPipes pandoc = pipeProcess(args);
ProcessPipes pandoc = pipeProcess(args, Redirect.all, env);
pandoc.stdin.write(source);
pandoc.stdin.writeln();
pandoc.stdin.flush();

View file

@ -9,6 +9,8 @@ import page;
import utils;
import staticpaths;
@safe:
/**
* Represents a project, like an unfinished application
*/

View file

@ -1,6 +1,6 @@
import std.array;
import std.algorithm;
import std.experimental.logger;
//import std.experimental.logger;
import std.file;
import std.stdio;
import std.traits;
@ -13,27 +13,35 @@ import page;
/**
* Loads pages into memory and sets up a "watcher" to watch a directory for file changes.
*/
void initPages(T, C)(C *cache, const string directory)
void initPages(T, C)(C *cache, const string directory) @trusted
if (isImplicitlyConvertible!(T, Page)) {
NativePath watchingDir;
try {
watchingDir = getWorkingDirectory() ~ directory;
} catch(PathValidationException) {
logError("Cannot watch path " ~ directory);
return;
}
bool addPage(string path) {
try {
T newPage = new T(path);
logf("Added %s", newPage.slug);
logInfo("Added %s", newPage.slug);
cache.addItem(newPage);
return true;
} catch (page.ArticleParseException e) {
logf("Could not parse %s: %s", path, e);
logWarn("Could not parse %s: %s", path, e);
return false;
} catch (Exception e) {
logf("Other exception while parsing %s: %s", path, e);
logWarn("Other exception while parsing %s: %s", path, e);
return false;
}
}
// Initial scan
void scan(NativePath path, int level = 0) {
logf("Scanning %s", path.toString());
logInfo("Scanning %s", path.toString());
foreach(file; iterateDirectory(path)) {
if (file.isDirectory) {
scan(path ~ file.name, level + 1);
@ -43,11 +51,11 @@ void initPages(T, C)(C *cache, const string directory)
}
}
if (!existsFile(getWorkingDirectory() ~ directory)) {
createDirectory(getWorkingDirectory() ~ directory);
if (!existsFile(watchingDir)) {
createDirectory(watchingDir);
}
scan(getWorkingDirectory() ~ directory);
DirectoryWatcher watcher = watchDirectory(getWorkingDirectory() ~ directory, true);
scan(watchingDir);
DirectoryWatcher watcher = watchDirectory(watchingDir, true);
bool shouldStop = false;
while (!shouldStop) {
@ -55,16 +63,16 @@ void initPages(T, C)(C *cache, const string directory)
DirectoryChange[] changes;
shouldStop = !watcher.readChanges(changes);
foreach(change; changes) {
logf("=======[New changes]======");
logInfo("=======[New changes]======");
string[] changeTypes = ["added", "removed", "modified"];
logf("Path: %s, type: %s", change.path.toString(), changeTypes[change.type]);
logInfo("Path: %s, type: %s", change.path.toString(), changeTypes[change.type]);
if (endsWith(change.path.toString(), ".kate-swp")) continue;
switch (change.type) with (DirectoryChangeType){
case added:
try {
addPage(change.path.toString());
} catch(Exception e) {
warningf("Error while updating %s: %s", change.path.toString(), e.msg);
logWarn("Error while updating %s: %s", change.path.toString(), e.msg);
}
break;
case modified:
@ -73,16 +81,16 @@ void initPages(T, C)(C *cache, const string directory)
newPage = new T(change.path.toString());
cache.changeItem(newPage);
} catch(page.ArticleParseException e) {
warningf("Could not parse %s", change.path.toString());
logWarn("Could not parse %s", change.path.toString());
} catch (Exception e) {
warningf("Error while updating %s: %s", change.path.toString(), e.msg);
logWarn("Error while updating %s: %s", change.path.toString(), e.msg);
}
break;
case removed:
try {
cache.removeItemByName(change.path.toString());
} catch(Exception e) {
logf("Error while trying to remove %s: %s", T.stringof, e.msg);
logInfo("Error while trying to remove %s: %s", T.stringof, e.msg);
}
break;
default: break;

View file

@ -17,6 +17,12 @@ msgid "template.menu.contact"
msgstr "Contact"
msgid "template.page.copyright"
msgstr "&copy; Chris Josten, 2021. If not specified otherwise, all content on this"
msgstr "&copy; Chris Josten, 2024. If not specified otherwise, all content on this "
"website is <a rel=\"license\" href=\"https://creativecommons.org/licenses/by/4.0/\">"
"licensed under the CC-BY 4.0</a>"
msgid "template.feed.title"
msgstr "%s | Chris's website"
msgid "template.feed.posts.title"
msgstr "Posts"

View file

@ -30,5 +30,11 @@ msgid "template.menu.contact"
msgstr "Contact"
msgid "template.page.copyright"
msgstr "&copy; Chris Josten, 2021. Tenzij anders vermeld staat, valt alle inhoud op deze webstek"
msgstr "&copy; Chris Josten, 2024. Tenzij anders vermeld staat, valt alle inhoud op deze webstek "
"<a rel=\"license\" href=\"https://creativecommons.org/licenses/by/4.0/\"> onder de CC-BY 4.0</a>"
msgid "template.feed.title"
msgstr "%s | Chris z'n webstekkie"
msgid "template.feed.posts.title"
msgstr "Berichten"

View file

@ -1,2 +1,28 @@
msgid "page.header-bottom-text"
msgstr ""
msgid "template.page.name"
msgstr "Chris's website"
msgid "template.page.html_language"
msgstr "en-GB"
msgid "template.menu.home"
msgstr "Home"
msgid "template.menu.posts"
msgstr "Posts"
msgid "template.menu.projects"
msgstr "Projects"
msgid "template.menu.contact"
msgstr "Contact"
msgid "template.page.copyright"
msgstr "&copy; Chris Josten, 2024. If not specified otherwise, all content on this "
"website is <a rel=\"license\" href=\"https://creativecommons.org/licenses/by/4.0/\">"
"licensed under the CC-BY 4.0</a>"
msgid "template.feed.title"
msgstr "%s | Chris's website"
msgid "template.feed.posts.title"
msgstr "Posts"

View file

@ -1,2 +1,2 @@
p
small& template.page.copyright
small !{trWeb("template.page.copyright")}

View file

@ -6,6 +6,7 @@ html(prefix="og: http://ogp.me/ns#")
//- Kick off loading the css as fast as possible
meta(name="viewport", content="width=device-width; initial-scale=1")
link(rel="stylesheet", href="/static/style/base.css")
link(rel="alternate", href="/feeds/posts.atom", type="application/atom+xml", title=trWeb("template.feed.title").format(trWeb("template.feed.posts.title")))
- string page_image = "/static/img/logo.png";
- string page_title;
@ -43,23 +44,25 @@ html(prefix="og: http://ogp.me/ns#")
li
a(href="#{link}") #{trWeb(text)}
section.header-navigation
header
img.logo(src="/static/img/logo.png", alt="The logo of the website: the letter C drawn in an unprofessional manner with wobbly eyes on put on top")
p& template.page.name
nav
ul
- menuItem("template.menu.home", "/");
- menuItem("template.menu.posts", "/posts/");
- menuItem("template.menu.projects", "/projects/");
- menuItem("template.menu.contact", "/contact");
header
img.logo(src="/static/img/logo.png", alt="The logo of the website: the letter C drawn in an unprofessional manner with wobbly eyes on put on top")
p #{trWeb("template.page.name")}
section.main-sidebar
block sidebar
footer.hide-small
block footer
include parts/footer
nav
ul
- menuItem("template.menu.home", "/");
- menuItem("template.menu.posts", "/posts/");
- menuItem("template.menu.projects", "/projects/");
- menuItem("template.menu.contact", "/contact");
main
block content
footer.hide-big
footer
block footer
include parts/footer