Skip to content

An7ar35/blogator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

97 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

alt text


alt text

What is this?

It's a static blog/news template-driven site generator sans-javascript requirements and with minimal software library dependencies (written in C++ 💖).

It will produce a blog/news site whose navigation works just fine with js disabled at the cost of a bit of duplication in the HTML (that amount can range from minimal to hog-wild based on your requirements/options set).

Who is it for?

If you:

  • still enjoys crafting their website by hand-coding it old-school but just wish you could have a blog in there too

  • are targeting a reader-base that runs a script blocker by default on their browser (i.e.: security conscious types and anyone that can't stand ads and scroll wheel hijacking),

  • are someone that just wants to keep things simple on your site

then keep reading.

Features:

  • HTML5/CSS only so works with JS disabled (e.g. with NoScript),
  • Template driven (offers some flexibility in design)
  • Index generated by date and, optionally tags, years and/or authors,
  • Master indices broken up into pages,
  • Custom number of posts per page in the indices,
  • Per-page navigation,
  • Breadcrumbs (🍞 nom nom nom),
  • Optional post-specific custom stylesheets,
  • Custom month strings for dates (to "localise" month names on the index),
  • and more (check out the configuration options)..

Fair Warning

There is an expectation of a reasonable level of HTML/CSS proficiency from the operator as well as some debugging skills. The software will not sanitise/check your HTML/CSS. That's on you.

Example

A basic blog example with templates is included in the /resources/example/ directory. Just run Blogator on it to generate everything.

Building

If you just want to build the application without installing it:

  • First clone the repo inside the directory where you want it to reside then
  • cd blogator
  • cmake .
  • cmake --build .

You will find the executable inside the build/ folder.

Installing

On Arch Linux

//TODO in the future...

CLI arguments

Run with: blogator [options] <working directory path>

Flag(s) Description
-c, --create-config Creates a default configuration file in the working directory and then stops
-h, --help Shows help and stops
-d, --debug Turns on the debug messages and continues
Argument(s) Description
{path} Working directory

Example

  blogator -c           //Creates a sample configuration file in the current directory
  blogator -c ~/mysite  //Creates a sample configuration file in `~/mysite`  
  blogator              //Runs Blogator to generate site in current directory
  blogator -d ~/mysite  //Runs Blogator to generate site in `~/mysite` with debug messages enabled

Input/Output overview

Here's a quick bird's eye view of what Blogator takes in and what it outputs and where. Everything in bold in the Input section is a hard requirement.

Files

Posts/Articles

All posts/articles must be placed in the source folder. The folder hierachy can be whatever you want. The only hard restrictions are as follow:

  • A custom CSS stylesheet for a particular post must be named the same as its post and be in the same folder.
    i.e.: {source/{filename}.html and source/{filename}.css.
    The stylesheet will be copied and named based on the generated post's file name and a link will be inserted into the <head></head> section of the generated post.
  • A custom index-entry template (the file used as the index entry for the associated post) must be named the same as its associated post's file name + _index and be in the same folder.
    i.e.: {source/{filename}.html and source/{filename}_index.html.

Example:

source/
├── post1/
│   ├── post.html           //<time datetime="2019-01-15">
│   ├── post.css
│   └── post_index.html
├── post2/
│   ├── awesome.html        //<time datetime="2019-03-15">
│   └── awesome_index.html
├── post3/
│   ├── radical.html        //<time datetime="2019-02-15">
│   └── radical.css

The software recursively looks into the source folder for any .html/.htm files and their matching index entry and stylesheets if any. The posts, after processing, are named by their chronological index number. So in the above example it will result with the following inside the post output folder:

posts/
├── 1.html                  //i.e.: post1/post.html (15 January 2019)
├── 1.css
├── 2.html                  //i.e.: post3/radical.html (15 February 2019)
├── 2.css
└── 3.html                  //i.e.: post2/awesome.html (15 March 2019)

CSS Stylesheets

Globally, there are no hard requirements on stylesheets. The software will generate 2 blank files: css/blog.css and css/index.css if one or both do not exist for convenience. It is not necessary to use either. CSS stylesheets can be declared for each templates like in any run-of-the-mill HTML file with the usual <link rel="stylesheet" type="text/css" href="{your CSS file path}"/>.

Important file paths

To use when hyperlinks to the different files need to be added in user created html in the site.

Description File Path Notes
Landing page /index.html
Index by date /index/by_date/0.html first page of the reverse chronological index of all posts
Index by year /index/by_year/years.html generated if index-by-year = true; is set in the configuration file
Index by tags /index/by_tag/tags.html generated if index-by-tag = true; is set in the configuration file
Index by authors /index/by_author/authors.html generated if index-by-author = true; is set in the configuration file
Blog-wide stylesheet /css/blog.css
Index-specific stylesheet /css/index.css

A note on the indices: Index files for categorised indices (year/tag/author) are names based on their category's ID and the page number.

e.g.: Let's say a year is given an ID of 3, there are 30 articles for that year and, in the options, the number of entries per page is set to 10. The end result would be:

by_year/
├── ...
├── 3_0.html
├── 3_1.html
├── 3_2.html
└── ...

In the case of the chronological index (by_date), the files are named by their page number starting from 0.

Templates

File Name Description Target directory path(s)
landing.html Landing/start page of the site/blog /
post.html Blog post/Article /posts
index.html Index page used for all indices except lists /index/by_date, /index/by_tag, /index/by_author
index_list.html Index list page used for all index lists /index/by_date, /index/by_tag, /index/by_author
year_list.html1 (Override) index list for post years /index/by_year
year_index.html1 (Override) index pages for post years /index/by_year
tag_list.html1 (Override) index list for post categories/tags /index/by_tag
tag_index.html1 (Override) index pages for post categories/tags /index/by_tag
author_list.html1 (Override) index list for post authors /index/by_author
author_index.html1 (Override) index pages for post authors /index/by_author
index_entry.html Index/Landing page's entry for a post/article /, /index/by_date, /index/by_tag, /index/by_author

Template tag classes

Landing page

  • breadcrumb

Provides a visual cue as to where in the blog hierarchy the user is currently.

  • newest-posts

Most recent posts (entry count can be set in the configuration file's landing-most-recent)

  • top-tags

Most used categories/tags (list item count can be set in the configuration file's landing-top-tags)

  • top-authors

Most prolific authors (list item count can be set in the configuration file's landing-top_authors)

  • featured-posts

List of feature posts (source HTML files can be set in the configuration file's landing-featured)

Note:

The landing page entries (in newest-posts and featured-posts) use the index entry template.

Post/Article page

Input tags
  • <span class="title"></span>

Title of the post. Anything between the first occurrence of these tags will be used at the title.

  • <time datetime="yyyy-mm-dd"></time>

Date stamp. The yyyy-mm-dd formatted date-time will be used as the date of the post. Visually, on the user side, that date can be expressed between the tags.
e.g.: <time datetime="2019-06-05">5 June 2019</time>

  • <span class="tag"></span>

(Optional) If a tag index is required (see config file), then tag(s) must be given to each posts. You can add as many per posts as required. So, for example, if the post is about a river in Italy then you can have the following tags:
<span class="tag">Rivers</span>
<span class="tag">Italy</span>

  • <span class="author"></span>

(Optional) If an author index is required (see config file), then author(s) must be given to each posts. Multiple authors per post is supported. Each author must be placed inside a tag.
e.g.: <span class="author">John Doe</span>

  • <span class="summary"></span>

(Optional) Anything inside these tags will be used as a summary in the index entry for the post. If the index entry text needs to be slightly different than the content text then one approach could be to put your entry text inside of a summary span and use CSS to hide it in posts.

Sample source post example:
<div>
    <h1>Lorem Ipsum Title</h1>
    <time datetime="2019-06-05">5 Juin 2019</time>
    <span class="tag">Sample text</span> 
    <span class="tag">Ipsum</span>                      
    <span class="author">L. Ipsum</span> 
    <span class="author">J. Smith</span>
</div>
<div>
    <p>
        <span class="summary"> Lorem ipsum dolor sit amet consectetur 
        adipiscing elit, non purus a etiam quam integer, maecenas 
        habitasse neque quisque iaculis sollicitudin.</span> 
        Luctus aliquam in maecenas quis cubilia urna vulputate 
        fusce eros, nam mauris proin torquent pulvinar fringilla 
        ultrices.
    </p>
    <p>
        Condimentum viverra duis donec consectetur et morbi ac 
        purus libero parturient turpis quisque torquent euismod 
        amet, cubilia aenean sociosqu pharetra facilisis metus 
        habitasse ante dictum sed magna vel convallis fermentum.
    </p>
</div>
Output template
  • breadcrumb

Provides a visual cue as to where in the blog hierarchy the user is currently.

  • page-nav

Page-by-page navigation (fwd/back).

  • post-content

Where to write out the article source to on the page.

  • index-pane-dates

Displays a hierarchy tree index of year > month > day for each article.

  • index-pane-tags

Displays a hierarchy tree index of article tags.

Post content output

This section deals with special tags that can be used inside posts in the source files.

  • auto-toc

Placement of the auto-generated table of contents based on the set options in the configuration files and the headings found within the post.

Index page (Chronological/Years/Tags/Authors)

  • page-name

Name of the page as specified in the configuration file's breadcrumb strings

  • breadcrumb

Provides a visual cue as to where in the blog hierarchy the user is currently.

  • page-nav
  • index-entries

Index List page (Years/Tags/Authors)

  • page-name

Name of the page as specified in the configuration file's breadcrumb strings

  • breadcrumb

Provides a visual cue as to where in the blog hierarchy the user is currently.

  • index-list

Plain and flat <ul></ul> list of categories arranged in numerical/alphabetical order.

  • index-list-hierarchy

Hierarchy list <ul></ul> of categories arranged grouped numerically/alphabetically

Index entry

  • post-number

Number of the article when arranged by date (1..n where n is the newest).

  • title

Title of the article.

  • authors

Authors (each author will be inserted in its own generated <span class="author"></span> html tag)

  • tags

Tags/Categories (each tag will be inserted in its own generated <span class="tag"></span> html tag)

  • date-stamp

Date for the post

  • summary

Summary text from the post.

Note: Make sure not to put hyperlinks (<a></a>) in the index/landing page templates. It will result in nested hyperlinks. If hyperlinks are inside the <span class="summary"></span> in the post's sources, they will be removed automatically though.

Configuration file

File: blogator.cfg

A default config file can be generated from the command line with ./blogator -c in the root of the target site's root folder (see CLI Arguments).

Note: all options keep to 1 line each and are terminated with a semi-colon ;.

General settings

site-url = "http://www.domain.com";

The root URL for the site Blogator is generating for.

Templates

template-change-paths = false;

Switch to true if you are using working relative paths in the templates for development/preview purposes. This will modify them based on the target html file location it is used to generate. Absolute paths (inc those that start with a /) will not be touched.

Month strings

months = ["January", "February", "Mars", "April", "May", "June", "July", "August", "September", "October", "November", "December"];

Used on the tree indices on the post pages and indices. If it's not given or the array is not complete (12 strings for the 12 months), blogator will default to english names.

Posts

build-future = false;

Flag to enable inclusion of future dated posts in the build. Future means any time stamp whose day/month/year is after the day of the build (i.e. when generator is run).

safe-purge = true;

Flag enabling deletion in the output post folder (/posts) of just html and css files (*.htm, *.html and .css) only whilst leaving any other file types and folders in the structure intact.
Good when resources are placed in there for whatever reasons and you don't want them to get nuked during the built process.

posts-change-paths = false;

Switch to true if you are using working relative paths in the posts (source/ folder) for development/preview purposes. This will modify them based on the target html file location it is used to generate. Absolute paths (inc those that start with a /) will not be touched.

toc-auto-generate = 0;

Specify levels of headings from 0 to 3 to be used for generating a table of content in posts that include <div class="auto-toc"></div>. 0 turns off the feature. E.g.: 3 will generate a TOC that include all headings <h2>, <h3> and <h4> found in the post. Max value = 6;

toc-level-offset = 1;

Specify the heading offset for the ToC. Max value = 5. E.g.: When offset = 1, the H2 headings will be treated as first level headings, H3 as second level, etc...

toc-auto-numerate = true;

Enables auto numbering for the ToC. Numbering will be applied to both the ToC and the matching headings in the text.

toc-heading = "<h2>Table of Contents</h2>"

Specifies a heading line to be inserted within the ToC block for all auto-generated Tables of Contents. The line will be inserted just before the "

    " listing for the headings. Leave the double quotation marks empty if no heading line should be inserted.

    Index

    show-post-numbers = true;
    

    Flag to enable the insertion of the post's publication number in the index entries.

    show-summary = false;
    

    Enables/Disables showing summary snippets in the index entries for posts.

    post-summary-pads = ["<p>", "</p>"];
    

    Pads any captured spans for the summary text with the given text when writing an index entry.
    e.g.: if the summary text is "Lorem ipsum dolor sit adipiscing." then , with the given padding above, the ouput would be <p>Lorem ipsum dolor sit adipiscing.</p>

    featured-css-class = "";
    

    Adds a CSS class name to the index/landing page hyperlink (<a class="{CLASS_NAME} href=".."></a>) for any articles that are in the featured list. Make sure to put a valid formatted CSS class name as whatever is between the string quotation marks will be copied verbatim.

    items-per-page = 10;
    

    Sets the Number of post entries per page in the index

    index-by-year = true;
    

    Flag to enable the creation of an extra index that groups posts by year

    index-by-tag = true;
    

    Flag to enable the creation of an extra index that groups posts by tags/categories

    index-by-author = true;
    

    Flag to enable the creation of an extra index that groups posts by authors

    json-index = true;
    

    Generates a JSON index of all the articles and, if enabled, the authors, tags and years for use in dynamic search scripts. The JSON file is will be located in the root of the index folder. All indices include target hrefs with an absolute path from the root of the blog (e.g.: for a tag - "index/by_tags/10_0.html", and for a post - "posts/25.html"). Note that article headings will only be included in the event the auto-toc feature is used on the article.

    json-index-append = [ "path/appendthis.json", ... ];
    

    List of filenames of source JSON files whose content is to be appended to the JSON index file. Useful when another category needs to be added to the search source. E.g.: personal projects. Important: JSON sources must be valid and be in the same root structure as the JSON index.

    Page navigation

    page-nav-separator = " / ";
    page-nav-forward   = ">>";
    page-nav-backwards = "<<";
    page-nav-first     = "First";
    page-nav-last      = "Last";
    

    Used for the per-page navigation on the generated html. These strings will be copied verbatim into their respective <a></a> so custom html tags can be written inside these string values for more flexibility. e.g.: page-nav-forward = "<div class="fwd-button"></div>";

    Note:

    If a path is given in a nesting tag for one of the page navigation strings it must be an absolute path for it to work across all generated pages.

    Breadcrumb

    breadcrumb-landing-page   = "Welcome page";
    breadcrumb-by-date-page   = "Index";
    breadcrumb-by-year-page   = "Years";
    breadcrumb-by-tag-page    = "Categories";
    breadcrumb-by-author-page = "Authors";
    breadcrumb-index-page     = "Page ";
    

    Used for the breadcrumb navigation on the generated html. These strings will be copied verbatim into their respective <a></a>.

    Note:

    Custom html tags can be written inside these string values if you want to get creative. For example: breadcrumb-landing-page = "<img src="img/start.png" alt="landing page">";. Though, if a path is given in a nesting tag for one or more of breadcrumb strings it must be an absolute path for it to work across all generated pages.

    Landing page

    landing-most-recent = 5;
    

    Number of entries to the most recent posts/articles to place inside the newest-posts section of the page.

    landing-top-tags    = 5;
    landing-top_authors = 5;
    

    Top used tags/authors in the posts/articles to display inside the top-tags and top-authors sections respectively of the page.

    landing-featured = ["0.html", "1.html", "2.html"];
    

    List of filenames of source posts to display inside the featured-posts section of the page.

    landing-duplicates = true;
    

    Allow/Disallow duplicate entries in the landing page's featured posts and newest posts. i.e.: In the case when a recent article which is in the newest posts section also is set as featured in the configuration.

    RSS feed

    rss             = true;
    rss-item-count  = 5;
    rss-title       = "My site";
    rss-description = "An awesome retro-futuristic site";
    rss-copyright   = "V 2077";
    rss-img-url     = "img/logo.svg";
    rss-img-link    = "http://www.domain.com/";
    rss-img-width   = "50px";
    rss-img-height  = "50px";
    rss-img-alt     = "Site logo";
    

    Support

    Assumptions made

    • All tags are closed properly (check you source HTML if the generated output is incorrect).
    • hyperlinks tags are opened/closed on the same line
    • hyperlinks start with <a and finish with </a>
    • heading/date/tag/author in source posts are individually opened/closed on the same line

    Troubleshooting

    • See above first!
    • Turn on the debug messages from the command line (--debug) to get a bit more information.

    Language/Character sets

    As my original use-case for this software dealt with a site written in english, the implementation uses standard strings (std::string) and not wide character strings (std::wstring).

    Officially only english is supported for the moment but your mileage in different languages/character sets may vary. I had no problem generating for a french language based site with all the wonderful é ê è ç types of characters that come with it. :)

    TL/DR: UTF-8 should not pose a problem on Linux but UTF-16 (and/or Windows) will.

    UTF-16 is something that might be supported in version 2.0 if it gets to that. I do not plan to deal with this for any 1.x versions.

    Edit: after some reflection and research on the whole character encoding subject I have decided not to bother with Windows and UTF16 support. Character encoding conversion without an external library (like ICU) is going to be such a time sink I'm not going to bother. So, UTF8/Linux only going forward.

    Platform/Requirements

    Officially, Linux and a compiler that supports C++17.

    Note that I make heavy use of std::filesystem and some libraries might not have a complete implementation of that yet (GCC 1.9.0 on Arch linux works fine for reference).

    Contributing

    I'm open to:

    • bug reports including a copy of your terminal output with the --debug flag enabled (source html files that can reproduce the issue would be appreciated when relevant),
    • suggestions for improvements (docs and features).

    License

    Software is provided as-is (i.e.: {insert std. disclaimer here}, backup you sh*t before running the software, etc...).

    Blogator is licensed under GNU AGPLv3

    The example templates (except anything in img/*.*) is licensed for personal use only. You can also base your own template from it if you like as long as you don´t make money off it.

    The following images that are included in the example template are under copyright @An7ar35 2019:

    • resources/example/img/hedgegog.png,
    • resources/example/img/butterfly.png

    The SVG icon included in the example template is from svgrepo.com (Creative Commons BY 4.0):

    • resources/example/img/smiley.svg source
    • resources/example/img/rss.svg source

    Footnotes

    1. Optional 2 3 4 5 6