Writing pages¶
The content of pages are written into file in yaml format.
Technically speaking Python translate yaml files in dictionaries and lists, so within Almoststatic, pages are dictionaries of data.
To facilitate the writing of pages, the html contents can be written in markdown format which is easy to write and read and less error prone then standard html. But you can mix html and markdown if you need more control.
Pages are stored by default into ./content/pages
folder, and we can arrange
pages into subfolders.
Minimal page¶
Let see a minimal page:
---
title: My page
author: Me
content:
- type: text_html
id: row1
text: |
## My text
This is a **Markdown** text
- type: text_html
id: row2
text: |
<h2>My second text</h2>
And this is a plain <b>html</b> text
Every page can have some metadata values, but are not required. If omitted, default values are kept. Metadata recognized are:
key |
value |
default |
---|---|---|
filename |
full pathname of file |
Automatically calculated |
pagename |
name of page (same of url) |
Automatically calculated |
title |
title of page |
Should be present |
description |
description of page |
Configurable in config.yaml |
author |
author of page |
Configurable in config.yaml |
templ_name |
template name used to render the page |
Configurable in config.yaml |
extends |
base page to extend |
Configurable in config.yaml |
cacheable |
if page is cacheable it is rendered only once |
Set to False it page must be recalculated at every call. |
widgets_envelope |
list of two strings with code surrounding widgets |
Empty |
date |
date of page |
If not declared the last date of modification |
blog |
special section with all data for blog pages |
Empty for not blog pages |
If needed, you can also add other fields which are available to all widgets,
i.e.: if you add menu: EnglishMenu
you can read it in pages as page.EnglishMenu
Every page must have a content
key which contain a list of widgets
which are rendered by templates. In the previous sample we have 2 widgets of type
text_html
, the first render some Markdown text and the second some html.
Each page can have a widgets_envelope
which is the default envelope for each
widget (see documentation for widgets below).
With this structure, the page content is send to the templ_name
that
extends the extends
base page (see details in Jinja documentation).
In this way the resulting page is a html page which contain some fixed contents
like headers, navbars and footers whit a list of widgets
in the middle.
With templ_name
and extends
metadata filed is possible to change the
base look of classes of pages, i.e. you can have a look for standard pages and
blog pages.
Some extra contents¶
Often you need more sophisticated pages, to do that you can add contents that is not automatically recognized, but that can be renderer into templates:
---
title: My page
author: Me
macros:
$home: "[Home]({{get_url(^^index^^)}})"
hero:
id: hero
image: hero/hero1.jpg
text: My content
aside:
- type: text_html
id: intro
class: info-box
text: |
## Aside
This content is inserted out of main content flow.
content:
- type: text_html
id: row1
text: |
## My text
This is a **Markdown** text
In this case, content
is automatically recognized, but hero
and
aside
can be intercepted by templates, into the page can also be used the
macro $home
with the link to home page.
Widgets¶
So content
list is composed by widgets
and the same can be for extra
contents, but what is a widget?
A widget is a portion of html. His data is declared into page and the look is given by a template, so the widget:
- type: text_html
disabled: false
text: |
## My text
This is a **Markdown** text
is rendered by text_html.html
template which look like:
<div>
{{widget.text}}
</div>
This is really the simplest widget, to see more sophisticated widgets take a look at samples. With the power of Jinja2 template system, you can use conditions and loops with full level of complexity.
Note
in {{widget.text}}
, "widget.text"
is the field to write on page
Note
Markdown text is rendered by Python-Markdown
which can be configured a little with extensions (see documentation).
By default enabled extensions are: ['tables', 'fenced_code', 'attr_list','admonition']
but you can add more in your source code with:
fas.md_extensions.append('abbr')
See the source of flaskapp.py
sample.
You can add official extension or third part extension or even write your extensions, see documentation.
Widgets fields¶
Almoststatic has few rules. To add a widget within a page you have to declare
is type
so Almoststatic can find the right template. Within a widget the
only field recognized is text
which is rendered as html.
You can optionally surround the widget into an envelope
which is a list
of 2 snippets of html code the first addedd before the widget and the second
addedd after the widget. If not present a widgets_envelope
at page level is
searched.
Then you can add as many fields as your template requires. The following is a widget a little more complex:
- type: carousel
id: carousel1
class: carousel-fade
envelope: ['<span>','</span>']
btn_class: "btn btn-primary btn-lg"
slides:
- image: [carousel1/slide1.jpg, "image_class", "image_alt"]
align: text-start
text: |
# My first image
some text
buttons: [["Learn more", "#"]]
- image: [carousel1/slide2.jpg, "image_class1", "image_alt1"]
text: |
# My second image
Another wonderful text, inline.
buttons:
[
["Gallery", "#", "btn btn-info"],
["Once more", "#", "btn btn-primary"],
]
- image: [carousel1/slide3.jpg, "image_class2", "image_alt2"]
align: text-end
text: |
# lorem text
buttons: [["Learn more", "#", "btn btn-info"]]
In this case we have a carousel with several slides with images, text and buttons
If you like you can disable a widget, for example to choose between different
solutions. Each widget can have the disabled
field which excludes it from
rendering.
- type: text_html
disabled: True
text: |
## This will never be rendered
Widgets best practices¶
With widgets you have a full freedom in fields. But we advise to use a consistent style of naming fields and use at least some of this
key |
value |
---|---|
id |
Every widget should have a unique id into the page, this helps to apply custom style |
class |
This can apply a css class to the widget |
style |
If |
text |
is the main content field. It is also used to embed and include widgets |
In the same way, use a clean style in your templates.
{# text_html
The simplest widget, display the text entered as html or markdown
parameters:
id: give a unique ID to every widget in the page
style: additional css style rules for this ID
class: set additional classes for the widget
text: text or content
-#}
{%include "include/widget_style.html" -%}
<div id="{{widget.id}}" class="{{widget.class}}">
{{widget.text}}
</div>
Markdown links¶
In markdown it is very handy to use the syntax [MyLink](http://mysite.org)
to represent links, it is short and readable but it has a problem with
Almoststatic internal links which are defined by get_url("home")
.
This because the python markdown implementation uses the quotes to render
the title
attribute like in: [MyLink](http://mysite.org "mytitle")
and then the standard syntax with internal link [MyHome]({{get_url("home")}})
rise an error.
So you have two options, you can embed in you markdown standard html such
as <a href="{{get_url(\'home\')}}">My home page</a>
, or use the default
escape sequence to substitute quotes which is ^^ (two times caret)
in get_url command. [MyHome]({{get_url(^^home^^)}})
Note
This mean that if you use the sequence "^^"
in your pages it will be
substituted with a single quote. I think that this is very unusual, but if
you need you can change it with a sequence of your choice in the
quote_escape
variable of almoststatic.
Standard markdown syntax for links doesn’t let you to indicate the target, but
python implementation with help of addr_list
extension lets you to use this
syntax:
[Flask](https://flask.palletsprojects.com){: target="_blank"}
Blog pages¶
Almoststatic has some blog capabilities.
Blog pages are ordinary pages rendered in the same way used for other pages.
The only difference is that them have the blog
section which contain blog
metadata used to group and search blog pages.
---
title: My page
author: Me
blog:
image: blog/blog1.jpg
show_image: true
disabled: false
intro: |
some intro info
tags: [blog, article]
categories: [python, test]
tag_order: { blog: 98 }
categ_order: { python: 99, flask: 3 }
content:
- type: text_html
id: row1
text: |
## My blog page
This is a **Markdown** text
Blog section can contain:
key |
value |
---|---|
image |
image used as intro or to list blog pages |
show_image |
flag used to switch off image display |
disabled |
the page is excluded from blog metadata until is not ready |
intro |
a brief intro text describing the content of the article |
tags |
list of tags for the page |
categories |
list of categories of the page |
tag_order |
tags can be listed by priority and date |
categ_order |
categories can be listed by priority and date |
Blog info are stored in metadata information which can be queried with the
query_blog
function to discover all pages with same categories or tags of
the current page or to get arbitrary blog pages.
Note
Blog functions are easy and useful, but not that powerful. If you plan to write a blog with hundreds of pages, maybe Almoststatic is not the right solution, better chose not static system which can query databases.