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 is less error prone then standard HTML. However, you can mix HTML and
Markdown if you need more control.
Pages are stored by default in the ``./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: |
My second text
And this is a plain html 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 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 an 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
one for blog pages.
Some extra contents
-------------------
Often you need more sophisticated pages; to do that, you can add content that
is not automatically recognized but can be rendered 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 and can also be used
the macro ``$home`` which contains 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 in the 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:
::
{{widget.text}}
This is the simplest widget; to see more sophisticated widgets, take a look at
the samples. With the power of Jinja2 template system, you can use conditions
and loops with a 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
its ``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 added before the widget and the second
added 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: ['','']
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 ``id`` is set it's easy to add some css style to the widget
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" -%}
{{widget.text}}
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")`` where
``hone`` is the name of the page.
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 ``My home page``, 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.