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.