Mkblog
This mkdocs plugin gives you the ability to write blog posts in
blog_dir
(defaults to blog/
) separate from docs_dir
.
However files are arranged in blog_dir
, they will be published under
/<blog_dir>/<release_date>/<title>/
.
For determening a release date markdown metadata date
is used.
Examples:
blog/first-post.md:
---
author: JD
date: 1990-01-01
---
# My first Blogpost
## intro
Hi
## Conclusion
Bye
This will be published under /blog/1990/01/01/first-post/
Source code in mkblog/plugin.py
class MkBlog(BasePlugin):
"""
This mkdocs plugin gives you the ability to write blog posts in
`blog_dir` (defaults to `blog/`) separate from `docs_dir`.
However files are arranged in `blog_dir`, they will be published under
`/<blog_dir>/<release_date>/<title>/`.
For determening a release date markdown metadata `date` is used.
Example:
blog/first-post.md:
```markdown
---
author: JD
date: 1990-01-01
---
# My first Blogpost
## intro
Hi
## Conclusion
Bye
```
This will be published under /blog/1990/01/01/first-post/
"""
config_scheme = (
('blog_dir', Type(str, default='blog')),
)
def __init__(self):
self._blog_md_file = None
self._blog_path = None
def get_blog_md(self):
"""
searches for markdown files in `blog_dir`.
Returns:
files collection from all blogposts as list
"""
self._blog_path = (
f'{Path(self.config["docs_dir"]).parent}/'
f'{self.config["blog_dir"]}'
)
blog_md_files = list(Path(self._blog_path).rglob('*.md'))
return blog_md_files
def get_blog_post_date(self):
"""
Search markdown metadata for a releasedate from given file.
If no matching metadata is present, the oldest commit is used instead.
As a last resort the file's ctime is used as release date.
Returns:
blogpost's release date as uri path
"""
m_d = markdown.Markdown(extensions=['meta'])
m_d.convert(self._blog_md_file.read_text())
metadata = m_d.Meta # pylint: disable=E1101 # false positive
if 'date' in metadata:
rawdate = metadata['date'][0]
else:
rawdate = Git().log(
self._blog_md_file,
date='short',
format='%ad'
).split('\n')[-1]
try:
release_date = datetime.strptime(rawdate, '%Y-%m-%d')
except ValueError:
release_date = datetime.fromtimestamp(
self._blog_md_file.stat().st_ctime)
return str(release_date.strftime('%Y/%m/%d'))
def build_blog_dest(self):
"""
Before appending new file object to mkdocs, this method
rewrites the destination path and url for the blogpost.
Returns:
new destination for html file and url
"""
path = (
f'{self.config["blog_dir"]}/'
f'{self.get_blog_post_date()}/'
f'{self._blog_md_file.stem}')
blog_dest = {
path: dict(
path=f'{path}.md',
src_dir=self._blog_md_file.resolve(),
dest_dir=self.config['site_dir'],
url=f'{path}/'
)
}
log.debug('Rewritten Blog Destination: %s', blog_dest)
return blog_dest
def on_files(self, files: Files, config: Config):
"""
From `https://www.mkdocs.org/user-guide/plugins/#on_files`:
The files event is called after the files collection is populated from
the docs_dir. Use this event to add, remove, or alter files in the
collection. Note that Page objects have not yet been associated with
the file objects in the collection.
Use Page Events to manipulate page specific data.
Parameters:
files: global files collection
config: global configuration object
Returns:
global files collection
"""
_posts_by_date = dict()
self.config.update(dict(
docs_dir=config['docs_dir'],
site_dir=config['site_dir']
))
for self._blog_md_file in self.get_blog_md():
_posts_by_date.update(self.build_blog_dest())
for post in sorted(_posts_by_date):
post = _posts_by_date[post]
blogpost = File(
path=post['path'],
src_dir=post['src_dir'],
dest_dir=post['dest_dir'],
use_directory_urls=config['use_directory_urls']
)
blogpost.abs_src_path = post['src_dir']
files.append(blogpost)
return files
def on_serve(self, server, config, builder):
"""
From `https://www.mkdocs.org/user-guide/plugins/#on_serve`:
The serve event is only called when the serve command is used during
development. It is passed the Server instance which can be modified
before it is activated.
For example, additional files or directories could be added to the list
of "watched" files for auto-reloading.
Parameters:
server: livereload.Server instance
config: global configuration object
builder: a callable which gets passed to each call to server.watch
Returns:
livereload.Server instance
"""
# docs_dir and configuration will still be watched
# no need for adding them here (again)
server.watch(self._blog_path, builder)
build_blog_dest(self)
¶
Before appending new file object to mkdocs, this method rewrites the destination path and url for the blogpost.
new destination for html file and url
Source code in mkblog/plugin.py
def build_blog_dest(self):
"""
Before appending new file object to mkdocs, this method
rewrites the destination path and url for the blogpost.
Returns:
new destination for html file and url
"""
path = (
f'{self.config["blog_dir"]}/'
f'{self.get_blog_post_date()}/'
f'{self._blog_md_file.stem}')
blog_dest = {
path: dict(
path=f'{path}.md',
src_dir=self._blog_md_file.resolve(),
dest_dir=self.config['site_dir'],
url=f'{path}/'
)
}
log.debug('Rewritten Blog Destination: %s', blog_dest)
return blog_dest
get_blog_md(self)
¶
searches for markdown files in blog_dir
.
files collection from all blogposts as list
Source code in mkblog/plugin.py
def get_blog_md(self):
"""
searches for markdown files in `blog_dir`.
Returns:
files collection from all blogposts as list
"""
self._blog_path = (
f'{Path(self.config["docs_dir"]).parent}/'
f'{self.config["blog_dir"]}'
)
blog_md_files = list(Path(self._blog_path).rglob('*.md'))
return blog_md_files
get_blog_post_date(self)
¶
Search markdown metadata for a releasedate from given file. If no matching metadata is present, the oldest commit is used instead. As a last resort the file's ctime is used as release date.
blogpost's release date as uri path
Source code in mkblog/plugin.py
def get_blog_post_date(self):
"""
Search markdown metadata for a releasedate from given file.
If no matching metadata is present, the oldest commit is used instead.
As a last resort the file's ctime is used as release date.
Returns:
blogpost's release date as uri path
"""
m_d = markdown.Markdown(extensions=['meta'])
m_d.convert(self._blog_md_file.read_text())
metadata = m_d.Meta # pylint: disable=E1101 # false positive
if 'date' in metadata:
rawdate = metadata['date'][0]
else:
rawdate = Git().log(
self._blog_md_file,
date='short',
format='%ad'
).split('\n')[-1]
try:
release_date = datetime.strptime(rawdate, '%Y-%m-%d')
except ValueError:
release_date = datetime.fromtimestamp(
self._blog_md_file.stat().st_ctime)
return str(release_date.strftime('%Y/%m/%d'))
on_files(self, files, config)
¶
From https://www.mkdocs.org/user-guide/plugins/#on_files
:
The files event is called after the files collection is populated from the docs_dir. Use this event to add, remove, or alter files in the collection. Note that Page objects have not yet been associated with the file objects in the collection. Use Page Events to manipulate page specific data.
global files collection
Source code in mkblog/plugin.py
def on_files(self, files: Files, config: Config):
"""
From `https://www.mkdocs.org/user-guide/plugins/#on_files`:
The files event is called after the files collection is populated from
the docs_dir. Use this event to add, remove, or alter files in the
collection. Note that Page objects have not yet been associated with
the file objects in the collection.
Use Page Events to manipulate page specific data.
Parameters:
files: global files collection
config: global configuration object
Returns:
global files collection
"""
_posts_by_date = dict()
self.config.update(dict(
docs_dir=config['docs_dir'],
site_dir=config['site_dir']
))
for self._blog_md_file in self.get_blog_md():
_posts_by_date.update(self.build_blog_dest())
for post in sorted(_posts_by_date):
post = _posts_by_date[post]
blogpost = File(
path=post['path'],
src_dir=post['src_dir'],
dest_dir=post['dest_dir'],
use_directory_urls=config['use_directory_urls']
)
blogpost.abs_src_path = post['src_dir']
files.append(blogpost)
return files
on_serve(self, server, config, builder)
¶
From https://www.mkdocs.org/user-guide/plugins/#on_serve
:
The serve event is only called when the serve command is used during development. It is passed the Server instance which can be modified before it is activated. For example, additional files or directories could be added to the list of "watched" files for auto-reloading.
livereload.Server instance
Source code in mkblog/plugin.py
def on_serve(self, server, config, builder):
"""
From `https://www.mkdocs.org/user-guide/plugins/#on_serve`:
The serve event is only called when the serve command is used during
development. It is passed the Server instance which can be modified
before it is activated.
For example, additional files or directories could be added to the list
of "watched" files for auto-reloading.
Parameters:
server: livereload.Server instance
config: global configuration object
builder: a callable which gets passed to each call to server.watch
Returns:
livereload.Server instance
"""
# docs_dir and configuration will still be watched
# no need for adding them here (again)
server.watch(self._blog_path, builder)