Merge branch 'master' of netsoj.nl:chris/chris-website
This commit is contained in:
commit
5cdd5b322c
2
dub.json
2
dub.json
|
@ -5,7 +5,7 @@
|
||||||
"copyright": "Copyright © 2019, Chris Josten",
|
"copyright": "Copyright © 2019, Chris Josten",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dyaml": "~>0.8.0",
|
"dyaml": "~>0.8.0",
|
||||||
"vibe-d": "~>0.8.6"
|
"vibe-d": "~>0.9.0"
|
||||||
},
|
},
|
||||||
"description": "A blog based on Markdown and JSON",
|
"description": "A blog based on Markdown and JSON",
|
||||||
"license": "AGPLv3",
|
"license": "AGPLv3",
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
{
|
{
|
||||||
"fileVersion": 1,
|
"fileVersion": 1,
|
||||||
"versions": {
|
"versions": {
|
||||||
"botan": "1.12.10",
|
"botan": "1.12.19",
|
||||||
"botan-math": "1.0.3",
|
"botan-math": "1.0.3",
|
||||||
"diet-ng": "1.6.0",
|
"diet-ng": "1.7.5",
|
||||||
"dyaml": "0.8.0",
|
"dyaml": "0.8.3",
|
||||||
"eventcore": "0.8.48",
|
"eventcore": "0.9.13",
|
||||||
"fswatch": "0.5.0",
|
"fswatch": "0.5.0",
|
||||||
"libasync": "0.8.4",
|
"libasync": "0.8.6",
|
||||||
"libevent": "2.0.2+2.0.16",
|
"libevent": "2.0.2+2.0.16",
|
||||||
"memutils": "0.4.13",
|
"memutils": "1.0.4",
|
||||||
"mir-linux-kernel": "1.0.1",
|
"mir-linux-kernel": "1.0.1",
|
||||||
"openssl": "1.1.6+1.0.1g",
|
"openssl": "1.1.6+1.0.1g",
|
||||||
"stdx-allocator": "2.77.5",
|
"stdx-allocator": "2.77.5",
|
||||||
"taggedalgebraic": "0.11.7",
|
"taggedalgebraic": "0.11.19",
|
||||||
"tinyendian": "0.2.0",
|
"tinyendian": "0.2.0",
|
||||||
"vibe-core": "1.7.0",
|
"vibe-core": "1.13.0",
|
||||||
"vibe-d": "0.8.6"
|
"vibe-d": "0.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,21 +4,32 @@ body {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
border-bottom: solid 40px;
|
||||||
|
border-image: url(old/flames.gif) 0 0 200 0 round round;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
border-top: solid 40px;
|
||||||
|
border-image: url(old/flames.gif) 200 0 0 round round;
|
||||||
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
cursor: url(old/cursor-over.gif), auto;
|
cursor: url(old/cursor-over.gif), auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > nav {
|
.header-navigation {
|
||||||
background-color: yellow;
|
background-color: cyan;
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > nav ul {
|
.header-navigation> nav ul {
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
list-style-image: url(old/bullet.gif);
|
list-style-image: url(old/bullet.gif);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > main {
|
body main {
|
||||||
background-color: black;
|
background-color: green;
|
||||||
color: white;
|
color: white;
|
||||||
border-image: url(old/skull-border.gif) 33% / 2em round;
|
border-image: url(old/skull-border.gif) 33% / 2em round;
|
||||||
/*border-left-width: 10px !important;
|
/*border-left-width: 10px !important;
|
||||||
|
@ -27,7 +38,7 @@ body > main {
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: green;
|
color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
|
@ -35,7 +46,7 @@ pre {
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote, .screenshots, code{
|
blockquote, .screenshots, code{
|
||||||
background: #0000ff;
|
background: darkgreen;
|
||||||
border-left: #0000cc 6px solid;
|
border-left: #0000cc 6px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
public/static/style/old/flames.gif
Normal file
BIN
public/static/style/old/flames.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 173 KiB |
182
source/app.d
182
source/app.d
|
@ -75,6 +75,101 @@ void addCachingHeader(bool publicCache = true)(ref HTTPServerResponse res) {
|
||||||
res.headers["Cache-Control"] = header;
|
res.headers["Cache-Control"] = header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct TranslateContext {
|
||||||
|
import std.typetuple;
|
||||||
|
|
||||||
|
alias languages = TypeTuple!("en_GB", "nl_NL");
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates boilerplate code for a single response.
|
||||||
|
* params:
|
||||||
|
* arrayName = The name of the associative array to take the items from.
|
||||||
|
* templateName = The name of the template to render.
|
||||||
|
*/
|
||||||
|
string singleResponseMixin(string arrayName, string templateName) {
|
||||||
|
return `string slug = req.params["slug"];
|
||||||
|
OutputType outputType = getOutputType(slug);
|
||||||
|
|
||||||
|
enforceHTTP(slug in ` ~ arrayName ~ `, HTTPStatus.notFound, "Page not found");
|
||||||
|
auto content = ` ~ arrayName ~ `[slug];
|
||||||
|
switch(outputType) with (OutputType) {
|
||||||
|
case MARKDOWN:
|
||||||
|
res.writeBody(content.contentSource, MIME_MARKDOWN);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case HTML:
|
||||||
|
render!("` ~ templateName ~ `", content);
|
||||||
|
break;
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
@translationContext!TranslateContext
|
||||||
|
class MijnBlog {
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Generates response for /posts/:slug and /palen/:slug.
|
||||||
|
*/
|
||||||
|
@path("/posts/:slug")
|
||||||
|
void getArticleSingle(string _slug, HTTPServerRequest req, HTTPServerResponse res) {
|
||||||
|
//getSingle!(Article, "pages/article.dt")(articles, req, res);
|
||||||
|
mixin(singleResponseMixin("articles", "pages/article.dt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates response for /posts and /palen
|
||||||
|
*/
|
||||||
|
@path("/posts/")
|
||||||
|
void getArticleOverview(HTTPServerRequest req, HTTPServerResponse res) {
|
||||||
|
addCachingHeader(res);
|
||||||
|
render!("pages/article-list.dt", articleList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates response for /projects and /projecten
|
||||||
|
*/
|
||||||
|
@path("/projects/")
|
||||||
|
void getProjectOverview(HTTPServerRequest req, HTTPServerResponse res) {
|
||||||
|
addCachingHeader(res);
|
||||||
|
render!("pages/project-list.dt", projectList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate response for a project page
|
||||||
|
*/
|
||||||
|
@path("/projects/:slug")
|
||||||
|
void getProject(HTTPServerRequest req, HTTPServerResponse res) {
|
||||||
|
res.headers["Cache-Control"] = "public";
|
||||||
|
//getSingle!(Project, "pages/project.dt")(projects, req, res);
|
||||||
|
mixin(singleResponseMixin("projects", "pages/project.dt"));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Generate response for a page
|
||||||
|
*/
|
||||||
|
@path("/:slug")
|
||||||
|
void getPage(HTTPServerRequest req, HTTPServerResponse res) {
|
||||||
|
addCachingHeader(res);
|
||||||
|
//getSingle!(Page, "pages/page.dt")(pages, req, res);
|
||||||
|
mixin(singleResponseMixin("pages", "pages/page.dt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@path("/")
|
||||||
|
void getIndexPage(HTTPServerRequest req, HTTPServerResponse res) {
|
||||||
|
addCachingHeader(res);
|
||||||
|
// If no slug is supplied, it will be adjusted to "index"
|
||||||
|
req.params.addField("slug", "index");
|
||||||
|
//getSingle!(Page, "pages/page.dt")(pages, req, res);
|
||||||
|
mixin(singleResponseMixin("pages", "pages/page.dt"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template method for fetching a single page for a subclass of page.
|
* Template method for fetching a single page for a subclass of page.
|
||||||
* Params:
|
* Params:
|
||||||
|
@ -85,84 +180,6 @@ void addCachingHeader(bool publicCache = true)(ref HTTPServerResponse res) {
|
||||||
* res = The server response to write to.
|
* res = The server response to write to.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void getSingle(T, string templ)(ref T[string] array, HTTPServerRequest req, HTTPServerResponse res) {
|
|
||||||
string slug = req.params["slug"];
|
|
||||||
OutputType outputType = getOutputType(slug);
|
|
||||||
|
|
||||||
enforceHTTP(slug in array, HTTPStatus.notFound, "Page not found");
|
|
||||||
T content = array[slug];
|
|
||||||
switch(outputType) with (OutputType) {
|
|
||||||
case MARKDOWN:
|
|
||||||
res.writeBody(content.contentSource, MIME_MARKDOWN);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case HTML:
|
|
||||||
res.render!(templ, content);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TranslationContext {
|
|
||||||
import std.typetuple;
|
|
||||||
enum enforceExistingKeys = true;
|
|
||||||
alias languages = TypeTuple!("en_GB", "nl_NL");
|
|
||||||
mixin translationModule!"mijnblog";
|
|
||||||
}
|
|
||||||
|
|
||||||
@translationContext!TranslationContext
|
|
||||||
class MijnBlog {
|
|
||||||
/**
|
|
||||||
* Generates response for /posts/:slug and /palen/:slug.
|
|
||||||
*/
|
|
||||||
@path("/posts/:slug")
|
|
||||||
void getArticleSingle(string _slug, HTTPServerRequest req, HTTPServerResponse res) {
|
|
||||||
getSingle!(Article, "pages/article.dt")(articles, req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates response for /posts and /palen
|
|
||||||
*/
|
|
||||||
@path("/posts/")
|
|
||||||
void getArticleOverview(HTTPServerRequest req, HTTPServerResponse res) {
|
|
||||||
addCachingHeader(res);
|
|
||||||
render!("pages/article-list.dt", articleList)(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates response for /projects and /projecten
|
|
||||||
*/
|
|
||||||
@path("/projects/")
|
|
||||||
void getProjectOverview(HTTPServerRequest req, HTTPServerResponse res) {
|
|
||||||
addCachingHeader(res);
|
|
||||||
render!("pages/project-list.dt", projectList)(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate response for a project page
|
|
||||||
*/
|
|
||||||
@path("/projects/:slug")
|
|
||||||
void getProject(HTTPServerRequest req, HTTPServerResponse res) {
|
|
||||||
res.headers["Cache-Control"] = "public";
|
|
||||||
getSingle!(Project, "pages/project.dt")(projects, req, res);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Generate response for a page
|
|
||||||
*/
|
|
||||||
@path("/:slug")
|
|
||||||
void getPage(HTTPServerRequest req, HTTPServerResponse res) {
|
|
||||||
addCachingHeader(res);
|
|
||||||
getSingle!(Page, "pages/page.dt")(pages, req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
@path("/")
|
|
||||||
void getIndexPage(HTTPServerRequest req, HTTPServerResponse res) {
|
|
||||||
addCachingHeader(res);
|
|
||||||
// If no slug is supplied, it will be adjusted to "index"
|
|
||||||
req.params.addField("slug", "index");
|
|
||||||
getSingle!(Page, "pages/page.dt")(pages, req, res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,15 +206,6 @@ void main() {
|
||||||
URLRouter router = new URLRouter;
|
URLRouter router = new URLRouter;
|
||||||
router.get("/static/*", serveStaticFiles("./public/", fSettings));
|
router.get("/static/*", serveStaticFiles("./public/", fSettings));
|
||||||
router.registerWebInterface(new MijnBlog);
|
router.registerWebInterface(new MijnBlog);
|
||||||
/*router.get("/posts/:slug", &articleGetSingle);
|
|
||||||
router.get("/palen/:slug", &articleGetSingle);
|
|
||||||
router.get("/posts/", &articleGetOverview);
|
|
||||||
router.get("/palen/", &articleGetOverview);
|
|
||||||
router.get("/projects/", &projectGetOverview);
|
|
||||||
router.get("/projects/:slug", &projectGet);
|
|
||||||
router.get("/projecten/:slug", &projectGet);
|
|
||||||
router.get("/:slug", &pageGet);
|
|
||||||
router.get("/", &pageGet);*/
|
|
||||||
|
|
||||||
listenHTTP(settings, router);
|
listenHTTP(settings, router);
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,7 @@
|
||||||
msgid "page.header-bottom-text"
|
msgid "template.page.name"
|
||||||
msgstr "Chris's website"
|
msgstr "Chris's website"
|
||||||
|
|
||||||
|
msgid "template.page.copyright"
|
||||||
|
msgstr "© Chris Josten, 2021. 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>"
|
||||||
|
|
|
@ -11,5 +11,9 @@ msgstr ""
|
||||||
"Language: nl\n"
|
"Language: nl\n"
|
||||||
"X-Generator: Poedit 2.3.1\n"
|
"X-Generator: Poedit 2.3.1\n"
|
||||||
|
|
||||||
msgid "Chris's website"
|
msgid "template.page.name"
|
||||||
msgstr "Chris z'n webstekkie"
|
msgstr "Chris z'n webstekkie"
|
||||||
|
|
||||||
|
msgid "template.page.copyright"
|
||||||
|
msgstr "© Chris Josten, 2021. 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>"
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
extends /parts/page.dt
|
|
||||||
|
|
||||||
block header
|
|
||||||
title Posts - Netsoj.nl
|
|
||||||
|
|
||||||
block sidebar
|
|
||||||
|
|
||||||
block content
|
|
||||||
header
|
|
||||||
h1.title Post list
|
|
||||||
- import vibe.d;
|
|
||||||
- if (articleList.length == 0)
|
|
||||||
p No posts found
|
|
||||||
- else
|
|
||||||
- import utils;
|
|
||||||
- foreach(article; articleList)
|
|
||||||
article
|
|
||||||
header
|
|
||||||
a(href="/posts/#{article.slug}", rel="bookmark")
|
|
||||||
h2.title #{article.title}
|
|
||||||
p.subtitle
|
|
||||||
| By #{article.author} on #{article.firstPublished.toHumanString}
|
|
||||||
- if (article.firstPublished != article.updated)
|
|
||||||
|, updated on #{article.updated.toHumanString}
|
|
||||||
p #{article.excerpt}…
|
|
|
@ -1,25 +0,0 @@
|
||||||
extends /parts/page
|
|
||||||
|
|
||||||
block meta_data
|
|
||||||
- page_type = "article";
|
|
||||||
- page_title = content.title;
|
|
||||||
- page_url = "/post/" ~ content.slug;
|
|
||||||
- page_description = content.excerpt;
|
|
||||||
|
|
||||||
block extra_meta_data
|
|
||||||
meta(name="og:article:author:username", content=content.author)
|
|
||||||
meta(name="og:article:published_time", content=content.firstPublished.toISOExtString)
|
|
||||||
- if (content.isModified)
|
|
||||||
meta(name="og:article:modified_time", content=content.updated.toISOExtString)
|
|
||||||
|
|
||||||
block content
|
|
||||||
article(itemscope, itemtype="https://schema.org/BlogPosting")
|
|
||||||
- import utils;
|
|
||||||
header
|
|
||||||
h1.title(itemprop="headline") #{content.title}
|
|
||||||
p.subtitle
|
|
||||||
| By <span itemprop="author">#{content.author}</span>
|
|
||||||
| on <time datetime="#{content.firstPublished.toISOExtString}" itemprop="datePublished">#{content.firstPublished.toHumanString}</time>
|
|
||||||
- if (content.isModified)
|
|
||||||
|, updated on <time datetime="#{content.updated.toISOExtString}" itemprop="dateModified">#{content.updated.toHumanString}</time>
|
|
||||||
section(itemprop="articleBody") !{content.content}
|
|
|
@ -1,11 +0,0 @@
|
||||||
extends /parts/page.dt
|
|
||||||
|
|
||||||
block header
|
|
||||||
title #{error.message} - Netsoj.nl
|
|
||||||
|
|
||||||
block sidebar
|
|
||||||
|
|
||||||
block content
|
|
||||||
header
|
|
||||||
h1.title #{error.message} (#{error.code})
|
|
||||||
pre #{error.debugMessage}
|
|
|
@ -1,4 +1,4 @@
|
||||||
extends /parts/page.dt
|
extends parts/page
|
||||||
|
|
||||||
block meta_data
|
block meta_data
|
||||||
- page_title = content.title;
|
- page_title = content.title;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
extends /parts/page.dt
|
extends parts/page
|
||||||
|
|
||||||
block header
|
block header
|
||||||
title Projects - Chris Netsoj.nl
|
title Projects - Chris Netsoj.nl
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
extends /parts/page
|
extends parts/page
|
||||||
block meta_data
|
block meta_data
|
||||||
- page_title = content.title;
|
- page_title = content.title;
|
||||||
- page_url = "/project/" ~ content.slug;
|
- page_url = "/project/" ~ content.slug;
|
||||||
|
|
|
@ -1,5 +1,2 @@
|
||||||
p
|
p
|
||||||
small
|
small& template.page.copyright
|
||||||
| © Chris Josten, 2020. 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>.
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ html(prefix="og: http://ogp.me/ns#")
|
||||||
section.header-navigation
|
section.header-navigation
|
||||||
header
|
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")
|
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& Chris's website
|
p& template.page.name
|
||||||
nav
|
nav
|
||||||
ul
|
ul
|
||||||
- menuItem("home", "/");
|
- menuItem("home", "/");
|
||||||
|
@ -54,10 +54,10 @@ html(prefix="og: http://ogp.me/ns#")
|
||||||
block sidebar
|
block sidebar
|
||||||
footer.hide-small
|
footer.hide-small
|
||||||
block footer
|
block footer
|
||||||
include /parts/footer.dt
|
include parts/footer
|
||||||
main
|
main
|
||||||
block content
|
block content
|
||||||
|
|
||||||
footer.hide-big
|
footer.hide-big
|
||||||
block footer
|
block footer
|
||||||
include /parts/footer.dt
|
include parts/footer
|
||||||
|
|
Loading…
Reference in a new issue