A static blog generator, written in C++17.
- You can use the Fossil client:
fossil clone https://code.rosaelefanten.org/blogcpp
. - You can use a Git client of your choice to download from the GitHub mirror:
git clone https://github.com/dertuxmalwieder/blogcpp
.
Because there are not enough static site generators yet.
- Just like other static site generators,
blogcpp
is virtually unbreakable by intruders as your server is not required to host anything but static HTML files - even JavaScript and CSS are, depending on your theme, entirely optional. - Unlike other static site generators,
blogcpp
is written in modern C++, resulting in a fast build speed, reducing the need for workarounds like incremental builds or advanced caching mechanisms while compiling. Still,blogcpp
tries to use as many CPU cores as possible to achieve the maximum speed on your machine. blogcpp
aims to provide a decent feature set without requiring too much configuration; in fact, it would work right out of the box if your website was example.com (which is, admittedly, rather unlikely).
The latest released version is version 9.
- Markdown support: Write your articles and pages in Markdown, get HTML.
- Theming support: You don't like the available themes? Well, nor do I. Go on and make a better one!
- OEmbed support: Just post an OEmbed-enabled link (e.g. Twitter or
YouTube) and
blogcpp
will embed the media for you. - RSS feeds for new articles, tags and categories.
- Configurable article permalinks.
- Sticky articles: Just put
Sticky: On
on top of your article file. - Emoji support: Write
:-)
and get a real smiling face instead. - Commenting support: Even a static blog needs a community. ;-)
- OpenGraph support if you feel like sharing stuff on Facebook or whatever.
- Syntax highlighting via highlight.js.
- Plug-in support: If anything's missing, just write a script.
- Article series: If you have articles which continue other articles,
you can just add them to the same series and
blogcpp
will make them refer to each other. - much more is planned - see the roadmap for a clue.
blogcpp
uses the following third-party libraries (in 3rdparty/
):
Some code was added from other people's suggestions:
- The HTTPDownloader class is based on this blog article.
Not included in this repository (because of their size), but still required external libraries:
Set up your blog.cfg
file first, follow the documentation in the
blog.cfg-example
file in this repository. Then type
blogcpp --new-article
to start a new article or
blogcpp --new-page
to start a new page. When you're done, run
blogcpp
and everything will be there soon.
Set up your blog.cfg
file first, follow the documentation in the
blog.cfg-example
file in this repository. Then start to add
Markdown-formatted posts (in /posts
) and/or pages (in /pages
)
formatted like this:
Author: Your name here. Date: 2016-01-01 12:00:00 Title: This is the title. Tags: tag 1;tag 2;tag 3 Categories: some;random;categories ---------- (Everything that follows will be a part of the contents.)
When you're done, run blogcpp
and everything will be there soon.
You mean, except the Title/Tags/Date part? Well:
- You can additionally set
Markdown: Off
to disable Markdown parsing for this specific post or page. Note that pages don't have categories, every category added to a page will be ignored. - If you want your newly created article (for logical reasons, only
posts are supported) to be "sticked" to the top of your index page,
just add
Sticky: On
into its header part. - Articles will always be sorted by their creation date; you can
indicate the latest change (which will be displayed or not,
depending on your theme) with
Changed: yyyy-mm-dd hh:mm:ss
somewhere above or below theDate:
field. - If you don't want to have emojis on this post or page without
removing them everywhere, just add
Emojis: Off
. - In case you want to have a specific OpenGraph image for this page or
article, you can set
OpenGraphImage: http://my/host/image.png
or whatever you want it to show. - You can also disable comments per post (pages don't have comments):
Comments: Off
. - If you use Content plug-ins but you want to disable them for this
item exclusively, you can set
Plugins: Off
. - If you want to add an article to a certain series, it is enough to
set
Series: Name Of Your Series
here. Note that currently only one series is supported per article. - Pages are usually sorted alphabetically. If you don't want to do that,
you can set the
Position
parameter with any number larger than 0. Note that the "start page" is not a real page, so the template decides where to put it.
If you're on Windows, you can just get a static
binary. There are also
precompiled builds for Debian GNU/Linux
which have, sadly, not been updated in a while.
It's not hard to compile blogcpp
yourself though:
- On Windows: You can refer to the
build_windows.cmd
file in this repository for details. Visual Studio 2015 or newer as well as Clang should work. - On Linux and Unix: Starting from version 3,
CMake
files are provided, tested on GNU/Linux and FreeBSD systems. (On other systems you might have to adjust the paths.) You'll need a C++17 compiler,blogcpp
makes heavy use of the new<filesystem>
header and other oddities.
blogcpp
has been proven to compile on:
- Windows 10 with Visual Studio 2017.
- Windows 10 with Clang 6.0.1.
- Debian GNU/Linux sid with
g++
6.3.0.
Also, the ICU libraries and headers as well as libcurl
need to be
in your include and library paths. (Visual Studio users can get the ICU
libraries
here.)
Manual compilation instructions:
- Compile
3rdparty/XML/tinyxml2.cpp
,3rdparty/duktape/duktape/src/duktape.c
,3rdparty/curl/HTTPDownloader.cpp
,EmojiParserClass.cpp
,MediaEmbedderClass.cpp
,MarkdownParserClass.cpp
,ContentsGeneratorClass.cpp
,ExcerptExtractorClass.cpp
,CommentIntegrationClass.cpp
,DebugClass.cpp
,PluginClass.cpp
,RSSGeneratorClass.cpp
,MarkdownWrapperClass.cpp
,TemplateWrapperClass.cpp
andblogcpp.cpp
into one object file. - Link to the
libcurl_a
,sicuin
,sicuio
,sicuuc
andsicudt
libraries. (Drop the leading "s" if you don't want to have a static linking.) - On Windows, additionally link to
shell32.lib
anduser32.lib
. - On POSIX systems, additionally link to
pthread
.
Optional preprocessor definitions while linking:
CURL_STATICLIB
andU_STATIC_IMPLEMENTATION
: Required for static linking of ICU andlibcurl
(highly recommended).WITH_DEBUGLOG
: Prints verbose debug output to/debuglog.txt
.WITH_PLUGINS
: Actually supports loading and evaluating plug-ins.WITH_OEMBED
:blogcpp
supports the OEmbed standard. This will - in some cases - affect the page generation speed. (If not used,blogcpp
supports simple replacements for YouTube and Vimeo only.)
Here's a site's minimal directory structure:
./blogcpp ./templates/TPLNAME/ ./INDIR/posts/ ./INDIR/pages/ ./OUTDIR/
Of course, the capital letters mean that the values are indeed
variables. By default, TPLNAME
is default
, INDIR
is site
and OUTDIR
is outdir
. Please use the configuration file when
building your site in order to change these values.
Well, I am a developer, not an economist. I do software, not shiny
product names. However, blogcpp
is path-agnostic (as long as it
finds its usual path structure). You think blogcpp
is a stupid name?
mv blogcpp klaus
and blog with klaus
!
Also, please consider to ask a real question next time.
Four spaces at the beginning of a line mark the beginning of a code
block. blogcpp
tries to determine the language of the code
automatically. If you want to set the language explicitly, you can use
the Pelican syntax for code blocks; example:
::::python print("This is Python code.")
You can call blogcpp
with the following parameters:
-h
/--help
: Prints this list of parameters.-v
/--version
: Prints the version number ofblogcpp
.-q
/--quiet
: Generates your site and shuts up while doing so.--new-article
: Generates a new article stub and opens it in your default editor.--new-page
: Generates a new page stub and opens it in your default editor.--config [filename]
: Uses a different configuration file than blog.cfg. The file must exist.
If compiled with WITH_PLUGINS
. three more parameters are supported:
--list-plugins
: Shows a list of all available plug-ins with their enabled/disabled status.--enable-plugin [name]
: Enables the given plug-in (if found).--disable-plugin [name]
: Disables the given plug-in (if found).
Given that you have actually enabled Emoji support in your
blog.cfg
file and not disabled it for the page or article you want
to process, the following smiley codes will automatically be replaced:
Code | Emoji |
---|---|
:-) |
☺ |
;-) |
😉 |
:-D |
😀 |
:-( |
☹ |
:'( |
😭 |
:-| |
😐 |
>:) |
😈 |
>:-) |
😈 |
>:( |
😠 |
>:-( |
😠 |
:-* |
😘 |
:-O |
😮 |
:-o |
😮 |
:-S |
😕 |
:-s |
😕 |
:-# |
😶 |
0:-) |
😇 |
:o) |
🤡 |
<_< |
😒 |
^^ |
😊 |
^_^ |
😊 |
<3 |
❤ |
m( |
🤦 |
While blogcpp
does not have its own commenting system, you can
easily integrate existing external commenting systems via your
blog.cfg
(please refer to the blog.cfg-example
file in this
repository). Currently supported are:
- Disqus (
comments = disqus
, you need to set yourcommentdata
to your Disqus shortname) - isso (
comments = isso
, you need to set yourcommentdata
to your isso folder) - Hashover
(
comments = hashover
, you need to set yourcommentdata
to your Hashover folder) -- currently, version 2.0 only - Google Plus comments (
comments = google
, no additional settings required) - Discourse (
comments = discourse
, you need to set yourcommentdata
to your Discourse domain) - Commento (
comments = commento
, you need to set yourcommentdata
to your Commento server URI)
Good news: blogcpp
plug-ins are actually JavaScript scripts,
meaning that it is rather easy to write one. Everyone can write
JavaScript today, right?
The plug-in mechanisms are still in an early phase, they will probably
be more extended in later releases. As of now, blogcpp
only supports
three kinds of plug-ins:
- Content plug-ins: Will be searched under
<plugindir>/contents
.blogcpp
will draw your contents (articles and pages) through all available scripts and spit out the results. Note that this happens during the initial reading stadium, so your scripts will see the raw Markdown text. Theprocess()
method is expected to take and return a string value. - Header plug-ins: Will be searched under
<plugindir>/header
. Theprocess()
method is expected to return a string value which will be attached to the{{ headscripts }}
template variable (if applicable). - Footer plug-ins: Similar to header plug-ins, but they should be
in
<plugindir>/footer
and fill the{{ footscripts }}
variable.
The standard naming scheme is something.plugin.js, blogcpp
will
gladly ignore any files which do not have a name like this. You are also
able to have a multi-part plug-in which affects multiple parts of the
blog: Plug-ins with an identical file name are considered to belong
together, blogcpp
will handle them as one big plug-in which affects
various parts of the software, e.g. <plugindir>/contents/moo.plugin.js
for moo'ing in the contents part, <plugindir>/header/moo.plugin.js
for moo'ing in the site header.
In order for this to work, you'll need to have blogcpp
compiled with
WITH_PLUGINS
and the configuration variable plugins
set to on
.
If you did so, blogcpp
will read all files in the particular folder
under your plug-in directory. If they are - more or less - valid JavaScript
(ECMAScript 5.1 is mostly supported), blogcpp
will try to find the
process()
method in them, fill it with the appropriate text and replace
it by the results of the process()
method before continuing as usual.
Plug-ins can also access certain parts of blogcpp
itself from the
BlogEngine
object. By the time of writing, those are the following one:
getVersion()
: Returns the current version ofblogcpp
as a number.
Please refer to the hello-world.plugin.js
example plug-in in this
repository for more or less information.
Yes, you can! Everything between <rawhtml>
and </rawhtml>
will
be ignored by blogcpp
's Markdown parser.
blogcpp
needs index.txt
, post.txt
, page.txt
and
archives.txt
in order to be able to process your site correctly. All
other template files are optional. CSS and image files can be put into a
subfolder named static
, blogcpp
will automatically copy this
folder to your output directory then.
Starting with version 9, blogcpp
's templates are mostly compatible
with the Jinja2 syntax, so porting your
existing Python themes should be rather easy.