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).
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.
- 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)..
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.
A basic blog example with templates is included in the /resources/example/
directory. Just run Blogator on it to generate everything.
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.
//TODO in the future...
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
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.
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
andsource/{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
andsource/{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)
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}"/>
.
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
.
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.html 1 |
(Override) index list for post years | /index/by_year |
year_index.html 1 |
(Override) index pages for post years | /index/by_year |
tag_list.html 1 |
(Override) index list for post categories/tags | /index/by_tag |
tag_index.html 1 |
(Override) index pages for post categories/tags | /index/by_tag |
author_list.html 1 |
(Override) index list for post authors | /index/by_author |
author_index.html 1 |
(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 |
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
)
The landing page entries (in newest-posts
and featured-posts
) use the index entry template.
<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.
<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>
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.
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.
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
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
post-number
Number of the article when arranged by date (
1..n
wheren
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.
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
;
.
site-url = "http://www.domain.com";
The root URL for the site Blogator is generating for.
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.
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.
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
to3
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.
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-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>";
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-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>
.
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-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
andtop-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 = 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";
- 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
- See above first!
- Turn on the debug messages from the command line (
--debug
) to get a bit more information.
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.
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).
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).
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):