diff --git a/articles/second.yamd b/articles/second.yamd index 49b90d7..cac3b99 100644 --- a/articles/second.yamd +++ b/articles/second.yamd @@ -6,3 +6,11 @@ Hi there, lovely people! # Test ![Foo bar](/static/img/logo.png) + +Hiep hiep hiep hiep hiep hahaaha warararararwararara flop adder flappppppp +Smerige lap met smeer. Doe daar je handen niet aan af. De maan is een grote +bal die zowat in de hemel hangt en daar dingen doet. Spinazie is een groente. +De maan is in tegenstelling tot spinazie geen groente. + +Sjeetje, Vuurvos. Hoeveel letters moet ik nog tikken totdat je de leesmodus inschakeld? +Heel veel, heb ik zowat het idee. Wat verschrikkelijk. diff --git a/source/app.d b/source/app.d index 840999e..46bcef6 100644 --- a/source/app.d +++ b/source/app.d @@ -12,12 +12,16 @@ import project; import watcher; /** - * Internal list of articles by slug. + * Internal list of articles, pages and projects by slug. */ Article[string] articles; Page[string] pages; Project[string] projects; +/** + * Default ordering and list with pointers to ordered articles. + * (Note: this is code which will actually be compiled and passed on!) + */ immutable string articleSortPred = "a.firstPublished > b.firstPublished"; Article*[] articleList; @@ -27,6 +31,9 @@ Page*[] pageList; immutable string projectSortPred = "a.title < b.title"; Project*[] projectList; +/** + * Output types for the content. + */ enum OutputType { HTML, MARKDOWN @@ -38,7 +45,8 @@ const string MIME_MARKDOWN = "text/markdown"; /** * Get's the document type for the given slug based on extension - * and returns the slug without extension and the document type. + * and returns the slug without extension and the document type. Also removes the extension from the + * slug. */ private OutputType getOutputType(ref string slug) { if (slug.endsWith(".md")) { @@ -56,6 +64,16 @@ private OutputType getOutputType(ref string slug) { } } +/** + * Template method for fetching a single page for a subclass of page. + * Params: + * T = the data structure/class to be passed as template parameter. Must have a slug parameter. + * templ = The template to use when rendering. + * array = An associative array where the keys are slugs for parameters and the values the template parameter. + * req = The server request to consume. Assumes there is an slug parameter. + * 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); @@ -82,13 +100,16 @@ void articleGetSingle(HTTPServerRequest req, HTTPServerResponse res) { } /** - * Generates response for /posts/ and /palen/ + * Generates response for /posts and /palen */ void articleGetOverview(HTTPServerRequest req, HTTPServerResponse res) { res.headers["Cache-Control"] = "public"; render!("pages/article-list.dt", articleList)(res); } +/** + * Generates response for /projects and /projecten + */ void projectGetOverview(HTTPServerRequest req, HTTPServerResponse res) { res.headers["Cache-Control"] = "public"; render!("pages/project-list.dt", projectList)(res); @@ -122,8 +143,6 @@ void errorPage(HTTPServerRequest req, HTTPServerResponse res, HTTPServerErrorInf } void main() { - //articles["hello-world"] = new Article("hello-world.yamd"); - HTTPServerSettings settings = new HTTPServerSettings; settings.bindAddresses = ["0.0.0.0"]; settings.port = 3465; @@ -147,6 +166,8 @@ void main() { router.get("/", &pageGet); listenHTTP(settings, router); + + // Start indexing pages. runTask({ initPages!(Page, pageSortPred)(pages, pageList, "pages"); }); diff --git a/source/article.d b/source/article.d index 32d305c..6893404 100644 --- a/source/article.d +++ b/source/article.d @@ -5,9 +5,11 @@ import std.datetime.date; import std.experimental.logger; import dyaml; -import page; import vibe.d; +import page; +import utils; + /** * Represents an article on the blog @@ -47,35 +49,13 @@ class Article : Page { @safe override protected void loadHeader(Node headerNode) { super.loadHeader(headerNode); - if (headerNode.containsKey("author")) { - this.m_author = headerNode["author"].as!string; - } else { - this.m_author = ""; - } + this.m_author = headerNode.getOr!string("author", ""); + this.m_author = headerNode.getOr!string("excerpt", null); - if ("excerpt" in headerNode) { - this.m_excerpt = headerNode["excerpt"].as!string; - } - - if ("firstPublished" in headerNode) { - try { - this.m_firstPublished = cast(DateTime) headerNode["firstPublished"].as!SysTime; - } catch(DateTimeException e) { - warningf("%s: invalid date format", this.m_slug); - } - } else { - this.m_firstPublished= DateTime.fromSimpleString("1970-Jan-01 00:00:00"); - } - - if ("updated" in headerNode) { - try { - this.m_updated = cast(DateTime) headerNode["updated"].as!SysTime(); - } catch(DateTimeException e) { - warningf("%s: invalid date format", this.m_slug); - } - } else { - this.m_updated = this.m_firstPublished; - } + SysTime firstPublished; + firstPublished = headerNode.getOr!SysTime("firstPublished", SysTime(DateTime.fromSimpleString("0001-Jan-01 00:00:00"))); + this.m_firstPublished = cast(DateTime) firstPublished; + this.m_updated = cast(DateTime) headerNode.getOr!SysTime("updated", firstPublished); } @property string excerpt() { return m_excerpt; } diff --git a/source/constants.d b/source/constants.d index 4a955e9..99b3740 100644 --- a/source/constants.d +++ b/source/constants.d @@ -1,3 +1,6 @@ +/** + * Constants which are passed to templates while rendering. + */ class Constants { public static immutable string SITE_NAME = "Chris Netsoj.nl"; public static immutable string COPYRIGHT = "© Chris Josten, 2020"; diff --git a/source/page.d b/source/page.d index 18861c6..73a6006 100644 --- a/source/page.d +++ b/source/page.d @@ -17,7 +17,8 @@ class ArticleParseException : Exception { /** - * Represents a page on the blog + * Represents a page on the blog. Every other page, including blog articles, + * projects and so on derrive from this class. */ class Page { /** @@ -43,7 +44,7 @@ class Page { private bool hasCalledSuper = false; /** - * Creates a page from a file + * Creates a page from a file. This will read from the file and parse it. */ this(string file) { this.m_name = file; @@ -63,7 +64,8 @@ class Page { /** - * Parse metadata from the header. + * Parse metadata from the header. Subclasses should override this method, + * to parse their own metadata and call super. * Params: * headerNode = the YAML node to parse the header metadata from. */ diff --git a/source/staticpaths.d b/source/staticpaths.d index 2d068b4..663cc5d 100644 --- a/source/staticpaths.d +++ b/source/staticpaths.d @@ -1,2 +1,6 @@ +/** + * Paths to static data. + */ immutable string STATIC_DIR = "/static/"; immutable string IMG_DIR = STATIC_DIR ~ "img/"; +immutable string SCRIPT_DIR = STATIC_DIR ~ "script/"; diff --git a/source/utils.d b/source/utils.d index 174a637..d4359fd 100644 --- a/source/utils.d +++ b/source/utils.d @@ -11,7 +11,11 @@ string toHumanString(DateTime value) { T getOr(T)(Node node, string key, T or) { if (key in node) { - return node[key].get!T; + try { + return node[key].get!T; + } catch (Exception e) { + return or; + } } else { return or; } diff --git a/views/pages/article.dt b/views/pages/article.dt index 7c3d434..75f061d 100644 --- a/views/pages/article.dt +++ b/views/pages/article.dt @@ -8,12 +8,13 @@ block header block sidebar block content - article + article(itemscope, itemtype="https://schema.org/BlogPosting") - import utils; header - h1.title #{content.title} + h1.title(itemprop="headline") #{content.title} p.subtitle - | By #{content.author} on #{content.firstPublished.toHumanString} + | By + | on - if (content.firstPublished != content.updated) - |, updated on #{content.updated.toHumanString} - | !{content.content} + |, updated on #{content.updated.toHumanString} + section(itemprop="articleBody") !{content.content}