changeset 147:33c7ce833d3f

install: setup.py fix + moved "default" dir in a "sat_templates" dir: the merge request at https://bugs.goffi.org/mr/view/3 was a good basis, but not fully working ("default" dir was removed), this patch fixes it, and do some improvments: - moved "default" in "sat_templates" dir, which correspond to the python module, so it can be found easily from python - added VERSION, and mercurial hash detection, in the same way as for Cagou and backend - slight modification of classifiers - replaces tabs coming from MR by spaces
author Goffi <goffi@goffi.org>
date Sat, 02 Jun 2018 17:25:43 +0200
parents 7dc00829c32f
children a4b6b8b6fc58
files default/app/app.html default/base/base.html default/blog/articles.html default/blog/atom.xml default/blog/discover.html default/blog/item.html default/blog/macros.html default/chat/chat.html default/chat/message.html default/chat/select.html default/components/avatar.html default/components/block.html default/components/common.html default/components/images.html default/error/401.html default/error/404.html default/error/base.html default/event/attendance.html default/event/invitation.html default/file/discover.html default/file/overview.html default/forum/overview.html default/forum/view.html default/forum/view_topics.html default/input/field.html default/input/form.html default/input/navigation.html default/input/textbox.html default/input/xmlui.html default/invitation/welcome.html default/login/logged.html default/login/login.html default/login/register.html default/merge-request/create.html default/merge-request/discover.html default/merge-request/edit.html default/merge-request/item.html default/photo/album.html default/photo/discover.html default/session/disconnect.html default/static/app.css default/static/blog.css default/static/chat.css default/static/chat.js default/static/chat_select.css default/static/common.js default/static/event.css default/static/file.css default/static/fonts.css default/static/fonts/sat_base_font/README.txt default/static/fonts/sat_base_font/SIL Open Font License.txt default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff2 default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff2 default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff2 default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff2 default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff2 default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff2 default/static/forum.css default/static/highlight.css default/static/invitation.css default/static/login.css default/static/merge-request.css default/static/merge-request_item.css default/static/photo.css default/static/styles.css default/static/ticket.css default/static/websocket.js default/ticket/create.html default/ticket/discover.html default/ticket/edit.html default/ticket/item.html default/ticket/overview.html default/ticket/tickets.html sat_templates/VERSION sat_templates/__init__.py sat_templates/default/app/app.html sat_templates/default/base/base.html sat_templates/default/blog/articles.html sat_templates/default/blog/atom.xml sat_templates/default/blog/discover.html sat_templates/default/blog/item.html sat_templates/default/blog/macros.html sat_templates/default/chat/chat.html sat_templates/default/chat/message.html sat_templates/default/chat/select.html sat_templates/default/components/avatar.html sat_templates/default/components/block.html sat_templates/default/components/common.html sat_templates/default/components/images.html sat_templates/default/error/401.html sat_templates/default/error/404.html sat_templates/default/error/base.html sat_templates/default/event/attendance.html sat_templates/default/event/invitation.html sat_templates/default/file/discover.html sat_templates/default/file/overview.html sat_templates/default/forum/overview.html sat_templates/default/forum/view.html sat_templates/default/forum/view_topics.html sat_templates/default/input/field.html sat_templates/default/input/form.html sat_templates/default/input/navigation.html sat_templates/default/input/textbox.html sat_templates/default/input/xmlui.html sat_templates/default/invitation/welcome.html sat_templates/default/login/logged.html sat_templates/default/login/login.html sat_templates/default/login/register.html sat_templates/default/merge-request/create.html sat_templates/default/merge-request/discover.html sat_templates/default/merge-request/edit.html sat_templates/default/merge-request/item.html sat_templates/default/photo/album.html sat_templates/default/photo/discover.html sat_templates/default/session/disconnect.html sat_templates/default/static/app.css sat_templates/default/static/blog.css sat_templates/default/static/chat.css sat_templates/default/static/chat.js sat_templates/default/static/chat_select.css sat_templates/default/static/common.js sat_templates/default/static/event.css sat_templates/default/static/file.css sat_templates/default/static/fonts.css sat_templates/default/static/fonts/sat_base_font/README.txt sat_templates/default/static/fonts/sat_base_font/SIL Open Font License.txt sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff2 sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff2 sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff2 sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff2 sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff2 sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff2 sat_templates/default/static/forum.css sat_templates/default/static/highlight.css sat_templates/default/static/invitation.css sat_templates/default/static/login.css sat_templates/default/static/merge-request.css sat_templates/default/static/merge-request_item.css sat_templates/default/static/photo.css sat_templates/default/static/styles.css sat_templates/default/static/ticket.css sat_templates/default/static/websocket.js sat_templates/default/ticket/create.html sat_templates/default/ticket/discover.html sat_templates/default/ticket/edit.html sat_templates/default/ticket/item.html sat_templates/default/ticket/overview.html sat_templates/default/ticket/tickets.html setup.py
diffstat 160 files changed, 3884 insertions(+), 3863 deletions(-) [+]
line wrap: on
line diff
--- a/default/app/app.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-{% extends 'base/base.html' %}
-{% block body %}
-<article id="web_app_box" class="box">
-    <p>
-        {% trans app_name=C.APP_NAME %}
-            The following link allows you to access {{app_name}} web application, which is a more feature rich way to use this software (but it is also a bit more heavy and complex to use).
-        {% endtrans %}
-    </p>
-    <p class="warning_text">
-        {% trans %}
-            Please note that current version of web application use legacy technologies and will be rewritten for next version of Salut à Toi (0.8). It does contain some bugs and it is under minimal maintenance.
-        {% endtrans %}
-    </p>
-    <p id="link_section">
-        <a href="/libervia.html">{% trans %}Click here to access the web application{% endtrans %}</a>.
-    </p>
-    <p>
-        {% trans %}
-            Next time you can go directly to the above URL.
-        {% endtrans %}
-    </p>
-</article>
-{% endblock body %}
--- a/default/base/base.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-{% set embedded = True %} {# embedded is set to avoid including base.html several times if a generic page is included (e.g. blog/articles.html) #}
-{% import 'components/common.html' as component with context %}
-{{ script.include('common', '') }} {# common.js is a common script, so it's useful to import it here #}
-<!DOCTYPE html>
-<html>
-<head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-
-    {% if norobots %}
-        <meta name="robots" content="noindex, nofollow">
-    {% endif %}
-
-    <title>{% block title %}{{C.APP_NAME}}{% endblock %}</title>
-
-    {% if css_content is defined %}
-        <style type="text/css">
-            {{css_content}}
-        </style>
-    {% else %}
-        {% for css_file in css_files %}
-            <link rel='stylesheet' type="text/css" href='{{root_path}}{{css_file}}'>
-        {% endfor %}
-    {% endif %}
-
-    {% if xmpp_uri is defined %}
-        <link rel="alternate" type="application/atom+xml" href="{{xmpp_uri}}" >
-    {% endif %}
-
-    {% if dynamic_style is defined %}
-    {# be extra careful about dynamic style, insure escaping if you use untrusted values ! #}
-        <style type="text/css">
-        {{dynamic_style}}
-        </style>
-    {% endif %}
-
-    {# JS handling #}
-    {% if websocket is defined %}
-        {{ script.include('websocket', '') }}
-    {% endif %}
-    {{ script.generate_scripts() }}
-    {% if websocket is defined %}
-        <script>var socket=new WSHandler("{{websocket.url}}", "{{websocket.token}}", {{websocket.debug}});</script>
-    {% endif %}
-
-    {% block favicon %}
-        <link rel="icon" href="{{media_path}}icons/apps/64/sat.png">
-    {% endblock favicon %}
-</head>
-<body>
-    {% if main_menu is defined %}
-        {% block main_menu %}
-            {{ component.menu(main_menu, class="main_menu") }}
-        {% endblock main_menu %}
-    {% endif %}
-
-    <main id='main_area'>
-        <header>
-            {% if confirm %}
-            {# confirmation message used when post data has been handled correctly #}
-                {% block confirm %}
-                    <div class="box post_confirm">
-                        {% block confirm_message %}
-                            {% trans %}Your data has been sent correctly.{% endtrans %}
-                        {% endblock confirm_message %}
-                    </div>
-                {% endblock confirm %}
-            {% endif %}
-
-        </header>
-
-        <div id="body">
-        {% block category_menu scoped %}
-            {% if category_menu is defined %}
-                {{ component.menu(category_menu, class="category_menu") }}
-            {% endif %}
-        {% endblock category_menu %}
-        {% block body %}
-        {% endblock body %}
-        </div>
-        <footer>{% block footer %}<span>{% trans app_name=C.APP_NAME %}Powered by {{app_name}}{% endtrans %}</span>{% endblock %}</footer>
-    </main>
-</body>
-</html>
--- a/default/blog/articles.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% set single = items|length == 1 %}
-{% set dates_format='relative' if single else 'short' %}
-{% import 'blog/macros.html' as blog with context %}
-{% import 'input/navigation.html' as navigation with context %}
-
-{% block title scoped -%}
-    {%- if single -%}
-        {{- items[0].title|default(items[0].content, true)|truncate(60, True, '…') + ' - ' + C.APP_NAME -}}
-    {%- else -%}
-        {{C.APP_NAME}}
-        {# {{- super() -}}
-           FIXME: super() is failing if blog is embedded (i.e. base/base.html is not its direct parent)
-                  not sure what's the best way to avoid that, so just using C.APP_NAME for now #}
-    {%- endif -%}
-{%- endblock title -%}
-
-{% block body %}
-{% if items %}
-    <div id="blog_items">
-        {{ blog.show_items(items, expanded=single) }}
-    </div>
-{% else %}
-    <p class="message--info">{% trans %}No articles found in this blog!{% endtrans %}</p>
-{% endif %}
-
-{{ navigation.prev_next(_("newer articles"), _("older articles")) }}
-{% endblock body %}
--- a/default/blog/atom.xml	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<feed xmlns='http://www.w3.org/2005/Atom'>
-    {% if title is defined %}
-        <title>{{title}}</title>
-    {% elif target_profile is defined %}
-        <title>{% trans name=target_profile%}{{name}}'s blog{% endtrans %}</title>
-    {% else %}
-        <title>{% trans app_name=C.APP_NAME%}{{app_name}} blog{% endtrans %}</title>
-    {% endif %}
-    <link href='{{request_uri}}' type='application/atom+xml' rel='self'/>
-    <link href='{{http_uri}}' type='text/html' rel='alternate'/>
-    <link href='{{xmpp_uri}}' type='application/atom+xml' rel='alternate'/>
-    <id>{{xmpp_uri}}</id>
-    <updated>{{updated|date_fmt('iso')}}</updated>
-    {% for item in items %}
-        <entry>
-            {% if item.title_xhtml %}
-                <title type='xhtml'>{{item.title_xhtml}}</title>
-            {% else %}
-                <title>{{item.title|default(item.content|truncate(80, True, '…'), True)}}</title>
-            {% endif %}
-            <link href='{{items_http_uri[item.id]}}' type='text/html' rel='alternate'/>
-            <link href='{{item.uri}}' type='application/atom+xml' rel='alternate'/>
-            <id>{{item.uri}}</id>
-            <updated>{{item.updated|date_fmt('iso')}}</updated>
-            <published>{{item.published|date_fmt('iso')}}</published>
-            <author>
-                <name>{{item.author}}</name>
-                <uri>xmpp:{{item.author_jid}}</uri>
-            </author>
-            {% for tag in item.tags %}
-                <category term="{{tag}}"/>
-            {% endfor %}
-            {% if item.content_xhtml %}
-                <content type='xhtml'>
-                    <div xmlns='http://www.w3.org/1999/xhtml'>
-                    {{item.content_xhtml}}
-                    </div>
-                </content>
-            {% else %}
-                <content type='text'>
-                    {{item.content_txt}}
-                </content>
-            {% endif %}
-        </entry>
-    {% endfor %}
-</feed>
--- a/default/blog/discover.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-{% extends 'base/base.html' %}
-{% import 'components/block.html' as block %}
-{% import 'components/images.html' as images with context %}
-{% import 'components/avatar.html' as avatar with context %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-{% block body %}
-{{ icon_defs('blog') }}
-<p class="instructions--head">
-    {% trans %}
-    Please select the blog you want to consult
-    {% endtrans %}
-</p>
-<div class="disco_blogs">
-    <ul class="grid grid--center">
-        {% for entity in disco_entities %}
-            <li class='grid__item grid__item--medium grid__item--selectable'>
-                <a href="{{entities_url[entity]}}" class="items_vert--centered">
-                    {{ avatar.avatar(entity, class="avatar--medium") }}
-                    <span>{{ identities[entity].nick|default(entity, true) }}</span>
-                </a>
-            </li>
-        {% endfor %}
-    </ul>
-</div>
-
-{% if disco_entities %}
-    <p class="instructions--alt">{% trans %}Or enter the jid of a blog writer{% endtrans %}</p>
-{% endif %}
-{% call form.form(class="form_single") %}
-    {{ field.text("jid", _("blog writer jid"), required=true)}}
-    {{ field.submit(_("Consult")) }}
-{% endcall %}
-{% endblock body %}
--- a/default/blog/item.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-{# display a blog item which can be expanded/retracted by clicking on it
-
-    if the locale differs from item language, it will be totally reduced, and will need a click to be displayed
-
-    @variable item(data_object.BlogItem): item to display
-    @variable identities(data_object.Identities): identities which can be used to display info on item author
-    @variable dates_format(unicode): format of the date to use (see date_fmt filter)
-#}
-
-{% block item %}
-
-{% if item.language and locale and locale.language != item.language %}
-    {# we may display items in different language in a specific way #}
-    {% set other_lang = " other_lang" %}
-{% endif %}
-
-{% if expanded %}
-    {# FIXME: the style attribute is not nice, but due to the use of clicked_mh_fix. A cleaner way would be welcomed #}
-    <article id="{{item.id}}" class="init box{{other_lang}} clicked" style="max-height: none">
-{% else %}
-    <article id="{{item.id}}" class="init box{{other_lang}}" >
-{% endif %}
-
-    {# following message is displayed if item lang is different from page locale #}
-    {% if other_lang is defined %}
-        <div class="info"><p>{% trans language=locale.language_name %}This message is not in {{language}}, click to display anyway{% endtrans %}</p></div>
-    {% endif %}
-
-    {# we put a reduce button at the top #}
-    <div class="expand_box box_top" onclick="clicked_mh_fix('{{item.id}}')">
-        <p>
-            <span class='hide'>{% trans %}Click to reduce…{% endtrans %}</span>
-        </p>
-    </div>
-
-    <header>
-        {% block header %}
-        {# title and publication date link to a HTTP page if items_http_uri is set #}
-        {% set item_http_uri = items_http_uri.get(item.id) if items_http_uri is defined else none %}
-
-        <div class="title">
-            {% block blog_title scoped %}
-                {% set title = item.title_xhtml or item.title or '' %}
-                {% if item_http_uri %}
-                    <a href="{{item_http_uri}}">{{title}}</a>
-                {% else %}
-                    {{title}}
-                {% endif %}
-            {% endblock %}
-        </div>
-            {% block metadata scoped %}
-            <div class="metadata">
-            {% if identities is defined %}
-                <span class="author">{{identities[item.author_jid].nick | default(item.author)}}</span>
-            {% else %}
-                <span class="author">{{item.author}}</span>
-            {% endif %}
-            {% set published = item.published|date_fmt(fmt=dates_format) %}
-            {% if item_http_uri %}
-                <span class="blog_data"><a href="{{item_http_uri}}">{{published}}</a></span>
-            {% else %}
-                {{- published -}}
-            {% endif %}
-            </div>
-            {% if item.tags %}
-                <div class="labels">
-                    {% if tags_http_uri is defined %}
-                        {% for tag in item.tags %}
-                            <a href="{{tags_http_uri[tag]}}"><span>{{tag}}</span></a>
-                        {% endfor %}
-                    {% else %}
-                        {% for tag in item.tags %}
-                            <span>{{tag}}</span>
-                        {% endfor %}
-                    {% endif %}
-                </div>
-            {% endif %}
-            {% endblock metadata %}
-        {% endblock header %}
-    </header>
-
-    <div class="content{{' text' if not item.content_xhtml}}">
-        {% block content %}
-        {{- item.content_xhtml or item.content|urlize or '' -}}
-        {% endblock content %}
-    </div>
-
-    {# and the bottom button to expand/reduce the article #}
-    <div class="expand_box box_bottom" onclick="clicked_mh_fix('{{item.id}}')">
-        <p>
-            <span class='show'>{% trans %}Click to expand…{% endtrans %}</span>
-            <span class='hide'>{% trans %}Click to reduce…{% endtrans %}</span>
-        </p>
-    </div>
-
-</article>
-
-{% endblock item %}
--- a/default/blog/macros.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-{% import 'input/textbox.html' as textbox with context %}
-
-{% macro show_items(items, comments=False, expanded=false, dates_fmt=none) %}
-    {# show items and comments items if present after each item,
-        then post form if allow_commenting is set
-        @param items(BlogItems): items to show
-        @param comments(bool): True items are comments
-            if False, a div with "main_article" class will be added
-        @param expanded(bool): initial state of items
-    #}
-    {% if dates_format is undefined %}
-        {% set dates_format = dates_fmt or 'short' %}
-    {% endif %}
-    {% for item in items %}
-        {% if not comments %}<div class="main_article">{% endif %}
-            {% include 'blog/item.html' %}
-        {% if not comments %}</div>{% endif %}
-
-        {# we recursively display comments for all comments nodes (usually there's only one) #}
-        {% for comments_items in item.comments_items_list %}
-            <button class="comments_btn" onclick="clicked_mh_fix('{{'comments_panel'|next_gidx}}');clicked_cls(this)">
-                <span class='show'>{% trans %}show comments{% endtrans %}</span>
-                <span class='hide'>{% trans %}hide comments{% endtrans %}</span>
-                ({{comments_items|count}})
-            </button>
-            <div id="{{'comments_panel'|cur_gidx}}" class="comments_panel">
-                {% if allow_commenting %}
-                    <div class="comment_post">
-                        {{- textbox.comment(service=comments_items.service, node=comments_items.node) -}}
-                    </div>
-                {% endif %}
-
-                <div class="comments">
-                    {{show_items(comments_items, comments=True)}}
-                </div>
-            </div>
-
-        {% endfor %}
-
-    {% endfor %}
-{% endmacro %}
--- a/default/chat/chat.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-{{ script.include('chat') }}
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% block title %}{{ target_jid }} - {{ super() }}{% endblock %}
-{% block body %}
-<div class="chat_widget">
-    <div id="messages">
-    {% if subject is defined %}
-        <div id="subject">
-            {{- subject|urlize(nofollow=true,target='_blank') -}}
-        </div>
-    {% endif %}
-    {% for msg in messages %}
-        {% include 'chat/message.html' %}
-    {% endfor %}
-    </div>
-    <div class="message_box">
-        <textarea id="message_input" name="message" type="text" placeholder="{{_("enter your message")}}"></textarea>
-    </div>
-</div>
-{% endblock body %}
-
-{% block footer %}{% endblock footer %}
--- a/default/chat/message.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-{% import 'components/avatar.html' as avatar with context %}
-
-<p id="{{msg.id}}" class="msg_{{msg.type}} {{'own_msg' if msg.from_ == own_jid.full()}}">
-    {%- if msg.type != C.MESS_TYPE_INFO %}
-        {%- set author = identities[msg.from_].nick | default(msg.from_) -%}
-        {{ avatar.avatar(msg.from_) }}
-        <span class="msg_header">
-            <span class="author">{{author}}</span>
-            <span class="date">{{msg.timestamp|date_fmt('auto_day')}}</span>
-        </span>
-    {% endif -%}
-    <span class="msg_body">
-        {{- msg.html or (msg.text|urlize(nofollow=true, target="_blank")) -}}
-    </span>
-</p>
--- a/default/chat/select.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-{% block body %}
-<p class="instructions--head">{% trans %}Please select the chat room you want to enter{% endtrans %}</p>
-<ul class="rooms_list">
-{% for room in rooms %}
-    <li><a href="{{room.url}}">{{room.name}}</a></li>
-{% endfor %}
-</ul>
-<p class="instructions--alt">{% trans %}Or enter a room address{% endtrans %}</p>
-{% call form.form(class="form_single") %}
-    {{ field.text("jid", _("Room address (JID)"), required=true)}}
-    {{ field.submit(_("Join")) }}
-{% endcall %}
-{% endblock body %}
--- a/default/components/avatar.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-{% macro avatar(jid, class) %}
-    {%- if identities is defined -%}
-        {%- if identities[jid].avatar_basename is defined %}
-            <img class="avatar {{class}}" src="{{cache_path}}{{identities[jid].avatar_basename}}">
-        {% else %}
-            <span class="avatar avatar--generated {{class}}"><span class="avatar__content">{{identities[jid].nick|first|upper}}</span></span>
-        {%- endif -%}
-    {%- endif -%}
-{% endmacro %}
--- a/default/components/block.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-{% macro separator(label, align='center') %}
-{# display a bloc separator
-    @param label(unicode): label to show
-    @param align(unicode): one of "left", "center", "right"
-#}
-    <div class="block_separator">
-        {% if align in ('center', 'right') %}
-            <div class="block_separator__line"></div>
-        {% endif %}
-        <div class="block_separator__label">
-            {{label}}
-        </div>
-        {% if align in ('center', 'left') %}
-            <div class="block_separator__line"></div>
-        {% endif %}
-    </div>
-{% endmacro %}
-
-{% macro disco_icon_grid(disco_entities, icon_name) %}
-{# display discovered entities in a grid
-    @param disco_entities: entities which mush have a name and url key or attribute
-    @param icon_name: name of a defined icon
-#}
-    <ul class="grid grid--center">
-        {% for disco_entity in disco_entities %}
-            <li class='grid__item grid__item--medium grid__item--selectable'>
-                <a href="{{disco_entity.url}}" class="items_vert--centered">
-                    {{ icon(icon_name, cls='icon--medium') }}
-                    <span>{{ disco_entity.name }}</span>
-                </a>
-            </li>
-        {% endfor %}
-    </ul>
-{% endmacro %}
--- a/default/components/common.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-{# menu labels, map from menu names to labels #}
-{% set ml = {
-    'login': _('Session') if profile else _('Log in'),
-    'blog': _('Blog'),
-    'forums': _('Forums'),
-    'merge-requests': _('Merge requests'),
-    'merge-request_new': _('Create new merge request'),
-    'tickets': _('Tickets'),
-    'tickets_list': _('List tickets'),
-    'ticket_new': _('Create new ticket'),
-    'chat': _('Chat'),
-    'files': _('Files sharing'),
-    'photos': _('Photos albums'),
-    'app': _('Application'),
-} %}
-
-{% macro menu(menus, class='') %}
-    <nav class="menu {{class}}">
-        <ul>
-        {% for name,url in menus %}
-            <li><a class="menu_item {{name}}" {{ {'href': url}|xmlattr }}>{{ml[name]}}</a></li>
-        {% endfor %}
-        </ul>
-    </nav>
-{% endmacro %}
--- a/default/components/images.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-{% macro get_icon_client(ident) %}
-    {% if ident.client %}
-        {% if ident.client.pc %}
-            {{ icon('desktop', cls='file__icon') }}
-        {% elif ident.client.phone %}
-            {{ icon('mobile', cls='file__icon') }}
-        {% elif ident.client.web %}
-            {{ icon('globe', cls='file__icon') }}
-        {% elif ident.client.console %}
-            {{ icon('terminal', cls='file__icon') }}
-        {% else %}
-            {{ icon('desktop', cls='file__icon') }}
-        {% endif %}
-    {% endif %}
-{% endmacro %}
--- a/default/error/401.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-{% extends 'error/base.html' %}
-
-{% block body %}
-<h1>{% trans %}Unauthorized{% endtrans %}</h1>
-<p>{% trans %}Sorry, you are not allowed to access this page.{% endtrans %}</p>
-{% endblock body %}
--- a/default/error/404.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-{% extends 'error/base.html' %}
-
-{% block body %}
-<h1>{% trans %}Not Found{% endtrans %}</h1>
-<p>{% trans %}Sorry, we can't find the resource you are trying to access.{% endtrans %}</p>
-{% endblock body %}
--- a/default/error/base.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-{% extends 'base/base.html' %}
-
-{% block title %}{% trans %}Error {{error_code}}{% endtrans %}{% endblock %}
-{% block body %}
-{% trans %}An error occured while trying to access the resource.{% endtrans %}
-{% endblock body %}
-{% block footer %}{% endblock %}
--- a/default/event/attendance.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-<div class="attendance box">
-    <p class="poll_instructions">{% trans %}Please indicate if you plan to attend the event:{% endtrans %}</p>
-    {% call form.form() %}
-        {{ field.meta("type", "attendance") }}
-        {{ field.meta("service", event.invitees_service) }}
-        {{ field.meta("node", event.invitees_node) }}
-        <div class="attending">
-            {{ field.choices("attend", (("yes", _("yes")), ("no", _("no")), ("maybe", _("maybe"))), checked=invitee.attend) }}
-        </div>
-        <div class="guests">
-            {{ field.int("guests", label=_("How many people will come (including you)?"), init=invitee.get("guests", 1)) }}
-        </div>
-        <div class="submit">
-            {{ field.submit() }}
-        </div>
-    {% endcall %}
-</div>
--- a/default/event/invitation.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-{% extends 'base/base.html' %}
-
-{% block body %}
-<div class='invitation_header box'>
-    <h1>{% trans %}Welcome {{name}}{% endtrans %}</h1>
-    <p class='instructions'>{% trans %}You have been invited to participate to an event{% endtrans %}</p>
-    {% if event.image is defined %}
-        <p><img id='event_picture' src="{{event.image}}"></p>
-    {% endif %}
-</div>
-
-{% if days_left is defined %}
-    {% if days_left > 0 %}
-        <div id="event_counter" class="box">{% trans %}{{days_left}} days left{% endtrans %}</div>
-    {% else %}
-        <div id="event_counter" class="box">{% trans %}the event is finished{% endtrans %}</div>
-    {% endif %}
-
-{% endif %}
-
-{% include 'event/attendance.html' %}
-
-{% if items is defined %}
-    {% include 'blog/articles.html' %}
-{% endif %}
-
-{% endblock body %}
--- a/default/file/discover.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-{% extends 'base/base.html' %}
-{% import 'components/block.html' as block %}
-{% import 'components/images.html' as images with context %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-{% block body %}
-{{ icon_defs('server', 'desktop', 'mobile', 'globe', 'terminal') }}
-<p class="instructions--head">
-    {% trans %}
-    Please select the device you want to access
-    {% endtrans %}
-</p>
-<div class="disco_files">
-    {% if disco_service_entities %}
-        <div class="files__services">
-            {{ block.separator(_("services")) }}
-            <ul class="grid grid--center">
-                {% for entity,ident in disco_service_entities.iteritems() %}
-                    <li class='grid__item grid__item--medium grid__item--selectable'>
-                        <a href="{{entities_url[entity]}}">
-                            {{ icon('server', cls='file__icon') }}
-                            <span>
-                                {% if disco_service_entities|count == 1 %}
-                                    {% trans %}your server{% endtrans %}
-                                {% else %}
-                                    {{ ident.values()[0].values()[0][0] }}
-                                {% endif %}
-                            </span>
-                        </a>
-                    </li>
-                {% endfor %}
-            </ul>
-        </div>
-    {% endif %}
-    {% if disco_own_entities %}
-        <div class="files__own">
-            {{ block.separator(_("your devices")) }}
-            <ul class="grid grid--center">
-                {% for entity,ident in disco_own_entities.iteritems() %}
-                    <li class='grid__item grid__item--medium grid__item--selectable'>
-                        <a href="{{entities_url[entity]}}">
-                            {{images.get_icon_client(ident)}}
-                            <span>{{ ident.values()[0].values()[0][0] }}</span>
-                        </a>
-                    </li>
-                {% endfor %}
-            </ul>
-        </div>
-    {% endif %}
-    {% if disco_roster_entities %}
-        <div class="files__roster">
-            {{ block.separator(_("your contacts devices")) }}
-            <ul class="grid grid--center">
-                {% for entity,ident in disco_roster_entities.iteritems() %}
-                    <li class='grid__item grid__item--medium grid__item--selectable'>
-                        <a href="{{entities_url[entity]}}">
-                            {{images.get_icon_client(ident)}}
-                            <span>{{entity.userhost()}}</span>
-                        </a>
-                    </li>
-                {% endfor %}
-            </ul>
-        </div>
-    {% endif %}
-</div>
-
-<p class="instructions--alt">{% trans %}Or enter a full jid of a device{% endtrans %}</p>
-{% call form.form(class="form_single") %}
-    {{ field.text("jid", _("device full jid"), required=true)}}
-    {{ field.submit(_("Access")) }}
-{% endcall %}
-{% endblock body %}
--- a/default/file/overview.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-{% extends 'base/base.html' %}
-
-{% block body %}
-{{ icon_defs('level-up', 'doc','folder-open-empty', 'comment-empty') }}
-<ul id="files">
-    {% if parent_url is defined  %}
-        <li class="file">
-            <a href="{{ parent_url }}">
-                {{ icon('level-up', cls='file__icon') }}
-                {% trans %}parent dir{% endtrans %}
-            </a>
-        </li>
-    {% endif %}
-
-    {% for file in files_data %}
-        {% if file.type == C.FILE_TYPE_DIRECTORY %}
-            <li class="file file_{{file.type}}">
-                <a href="{{file.url}}">
-                    {{ icon('folder-open-empty', cls='file__icon') }}
-                    {{ file.name }}
-                </a>
-            </li>
-        {% else %}
-            <li class="file file_{{file.type}}">
-                <a href="{{file.url}}">
-                    {% if file.thumb_url is defined %}
-                        <img src="{{file.thumb_url}}" class="file__thumbnail" alt="{{file.name}}">
-                    {% else %}
-                        {{ icon('doc', cls='file__icon icon--soft') }}
-                    {% endif %}
-                    {{ file.name }}
-                </a>
-            </li>
-        {% endif %}
-    {% endfor %}
-</ul>
-{% if not files_data %}
-    <p class="message--info">{% trans %}No files are shared in this directory!{% endtrans %}</p>
-{% endif%}
-{% endblock body %}
--- a/default/forum/overview.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-{% extends 'base/base.html' %}
-
-{% macro generate_forums(forums_data) %}
-    <ul class="forum">
-    {% for forum in forums_data %}
-        <li>
-            {% if 'http_url' in forum %}
-                <a href="{{forum['http_url']}}" class="forum_actif">
-            {% else %}
-                <a>
-            {% endif %}
-                <span class="forum_title">{{ forum.title }}</span>
-                {% if 'short-desc' in forum %}
-                    <p class="forum_short-desc">{{ forum['short-desc'] }}</p>
-                {% endif %}
-            </a>
-
-            {% if 'sub-forums' in forum %}
-                {{ generate_forums(forum['sub-forums']) }}
-            {% endif %}
-        </li>
-    {% endfor %}
-    </ul>
-{% endmacro %}
-
-{% block body %}
-{% if not forums %}
-    <p class="message--info">{% trans %}No forums found on this server!{% endtrans %}</p>
-{% else %}
-    <div id="forums">
-        {{ generate_forums(forums) }}
-    </div>
-{% endif %}
-{% endblock body %}
--- a/default/forum/view.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% set dates_format='relative' if single else 'short' %}
-{% import 'blog/macros.html' as blog with context %}
-{% import 'input/textbox.html' as textbox with context %}
-
-{% block body %}
-
-<div id="blog_items">
-    {{ blog.show_items(items, expanded=true) }}
-</div>
-<div class="forum_topic_answer">
-    {{- textbox.comment_or_login(service=service, node=node, placeholder=_("Enter your message here")) -}}
-</div>
-<nav class="prev_next_links">
-    <ul>
-        {% if newer_url is defined %}
-            <li class="newer_items">
-                <a href="{{newer_url}}">
-                    <img src="{{media_path}}icons/tango/actions/32/go-previous.png">
-                    {% trans %}newer messages{% endtrans %}
-                </a>
-            </li>
-        {% endif %}
-        {% if older_url is defined %}
-            <li class="older_items">
-                <a href="{{older_url}}">
-                    <img src="{{media_path}}icons/tango/actions/32/go-next.png">
-                    {% trans %}older messages{% endtrans %}
-                </a>
-            </li>
-        {% endif %}
-    </ul>
-</nav>
-{% endblock body %}
--- a/default/forum/view_topics.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% import 'input/field.html' as field with context%}
-{% import 'input/textbox.html' as textbox with context %}
-{% import 'input/navigation.html' as navigation with context %}
-{% import 'components/avatar.html' as avatar with context %}
-
-{% block body %}
-
-    {% if not topics %}
-        <div class="message--info">
-            {% trans %}There is not message yet in this forum.{% endtrans %}
-            {% if profile %}
-                {% trans %}You can start a topic of interest by filling this form.{% endtrans %}
-            {% else %}
-                {% trans %}You can login to create a new topic.{% endtrans %}
-            {% endif %}
-        </div>
-    {% endif %}
-    {% if profile %}
-        <div class="forum__topic_create">
-            {% call textbox.textbox(service, node, placeholder=_("Your message"), submit_label=_("Create topic"), type="new_topic") %}
-                {{ field.text("title", placeholder=_("Your topic (try to be short and explicit)"), required=True) }}
-            {% endcall %}
-        </div>
-    {% endif %}
-
-    <div class="forum__topics">
-        {% for topic in topics %}
-            <div>
-                <a href="{{topic.http_uri}}">
-                    {{ avatar.avatar(topic.author) }}
-                    {{topic.title}}
-                </a>
-            </div>
-        {% endfor %}
-    </div>
-
-    {{ navigation.prev_next(_("older topics"), _("newer topics")) }}
-{% endblock body %}
--- a/default/input/field.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-{% macro choices(name, choices_list, checked=none) %}
-    {% for choice, label in choices_list %}
-        <div class="form_input">
-            <input id="{{name|next_gidx}}" type="radio" name="{{name}}" value="{{choice}}"{{" checked" if checked==choice}}><label for="{{name|cur_gidx}}">{{label}}</label>
-        </div>
-    {% endfor %}
-{% endmacro %}
-
-{% macro int(name, label="", init=0) %}
-    <span class="form_input">
-        <label for="{{name|next_gidx}}">{{label}}</label><input id="{{name|cur_gidx}}" type="number" name="{{name}}" value="{{init}}" step="1" min="0">
-    </span>
-{% endmacro %}
-
-{% macro text(name, label="", placeholder="", required=false) %}
-    {# single line text field
-       additional kwargs will be passed as attributes #}
-    <span class="form_input">
-        <label for="{{name|next_gidx}}">{{label}}</label><input id="{{name|cur_gidx}}" type="text" name="{{name}}" placeholder="{{placeholder}}" {{"required" if required}} {{kwargs|xmlattr}}>
-    </span>
-{% endmacro %}
-
-{% macro password(name, label="", required=false) %}
-    {# password field
-       additional kwargs will be passed as attributes #}
-    <span class="form_input">
-        <label for="{{name|next_gidx}}">{{label}}</label><input id="{{name|cur_gidx}}" type="password" name="{{name}}" {{"required" if required}} {{kwargs|xmlattr}}>
-    </span>
-{% endmacro %}
-
-{% macro email(name, label="", required=false) %}
-    {# email field
-       additional kwargs will be passed as attributes #}
-    <span class="form_input">
-        <label for="{{name|next_gidx}}">{{label}}</label><input id="{{name|cur_gidx}}" type="email" name="{{name}}" {{"required" if required}} {{kwargs|xmlattr}}>
-    </span>
-{% endmacro %}
-
-{% macro textarea(name, rows=10, cols=50, placeholder='', required=false) %}
-    <textarea name="{{name}}" rows="{{rows}}" cols="{{cols}}" placeholder="{{placeholder}}" {{"required" if required}}></textarea>
-{% endmacro %}
-
-{% macro meta(name, value) %}
-    <input type="hidden" name="{{name}}" value="{{value}}">
-{% endmacro %}
-
-{% macro submit(text=_("Send")) %}
-    <input class="form_submit button" type="submit" value="{{text}}">
-{% endmacro %}
--- a/default/input/form.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-{% macro form(action='', class='') %}
-<form method="post" action="{{action}}" {{ {'class': class}|xmlattr }}>
-    {% if csrf_token is defined %}
-        <input type="hidden" name="csrf_token" value="{{csrf_token}}">
-    {% endif %}
-    {{ caller() }}
-</form>
-{% endmacro %}
--- a/default/input/navigation.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-{% macro prev_next(prev_label=_("newer"), next_label=_("older")) %}
-    <nav class="prev_next_links">
-        <ul>
-            {% if newer_url is defined %}
-                <li class="newer_items">
-                    <a href="{{newer_url}}">
-                        <img src="{{media_path}}icons/tango/actions/32/go-previous.png">
-                        {{prev_label}}
-                    </a>
-                </li>
-            {% endif %}
-            {% if older_url is defined %}
-                <li class="older_items">
-                    <a href="{{older_url}}">
-                        <img src="{{media_path}}icons/tango/actions/32/go-next.png">
-                        {{next_label}}
-                    </a>
-                </li>
-            {% endif %}
-        </ul>
-    </nav>
-{% endmacro %}
--- a/default/input/textbox.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field %}
-
-{% macro head(service, node, type="textbox") %}
-    {# include data needed to identify the node to use for commenting #}
-    <input type="hidden" name="type" value="{{type}}">
-    <input type="hidden" name="service" value="{{service}}">
-    <input type="hidden" name="node" value="{{node}}">
-{% endmacro %}
-
-{% macro submit(label=_("Send")) %}
-    <input type="submit" value="{{label}}">
-{% endmacro %}
-
-{% macro textbox(service, node, action='', placeholder='', submit_label=_("Send"), type="textbox") %}
-    {# generic content area for comments/blog posts/etc. Only a body by default, but new elements can be
-       added by using this macro with call #}
-    {% set extra_content = caller() if caller is defined else '' %}
-    {% call form.form(action=action, class="textbox") %}
-        {{ head(service, node, type) }}
-        {{ extra_content }}
-        {{ field.textarea("body", placeholder=placeholder, required=True) }}
-        {{ submit(label=submit_label) }}
-    {% endcall %}
-{% endmacro %}
-
-{% macro comment(service, node, action='', placeholder=_("Your comment")) %}
-    {{ textbox(service, node, action=action, placeholder=placeholder, type="comment") }}
-{% endmacro %}
-
-{% macro comment_or_login(service, node, action='', placeholder=none) %}
-    {# show comment form a a message asking to log in
-       login is checked using profile #}
-    {% if profile %}
-        {% if placeholder is none %}
-            {{ comment(service, node, action) }}
-        {% else %}
-            {{ comment(service, node, action, placeholder=placeholder) }}
-        {% endif %}
-    {% else %}
-        <div class="log_request">
-        <p class="not_logged">{% trans %}You are not logged. You need to log in to comment.{% endtrans %}</p>
-        {% if login_url is defined %}
-            <p class="log_in_url">
-                {% trans link_start=('<a href="',login_url,'">')|join|safe, link_end='</a>'|safe %}
-                    To log in {{link_start}}follow this link{{link_end}}
-                {% endtrans %}
-            </p>
-        {% endif %}
-        </div>
-    {% endif %}
-{% endmacro %}
--- a/default/input/xmlui.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,237 +0,0 @@
-{% import 'input/field.html' as field %}
-
-{# generate methods #}
-
-{% macro generate_container(cont, config) %}
-    {% if cont.type == 'vertical' %}
-        {{ vertical_container(cont, config) }}
-    {% elif cont.type == 'pairs' %}
-        {{ pairs_container(cont, config) }}
-    {% elif cont.type == 'label' %}
-        {{ label_container(cont, config) }}
-    {% endif %}
-{% endmacro %}
-
-{% macro generate_widget(wid, config, id=none) %}
-    {% if wid.type == 'text' %}
-        {{ text_widget(wid, config, id=id) }}
-    {% elif wid.type == 'label' %}
-        {{ label_widget(wid, config) }}
-    {% elif wid.type == 'string' %}
-        {{ string_widget(wid, config, id=id) }}
-    {% elif wid.type == 'jid' %}
-        {# TODO: proper JID widget #}
-        {{ string_widget(wid, config, id=id) }}
-    {% elif wid.type == 'textbox' %}
-        {{ textbox_widget(wid, config, id=id) }}
-    {% elif wid.type == 'list' %}
-        {{ list_widget(wid, config, id=id) }}
-    {% endif %}
-{% endmacro %}
-
-{% macro generate_children(cont, config) %}
-    {% for child in cont.children %}
-        {% if child.category == 'container' %}
-            {{ generate_container(child, config) }}
-        {% else %}
-            {{ generate_widget(child, config) }}
-        {% endif %}
-    {% endfor %}
-
-{% endmacro %}
-
-{% macro generate(xmlui, form=true, filters=none, attributes=none) %}
-{# generate HTML from XMLUI
-    @param xmlui(template_xmlui.XMLUIPanel): xmlui to use
-    @param form(bool): if true will generate form elements
-    @param filters(dict,none): filters as expected by item_filter
-    @param attributes(dict,none): extra attributes to put on named widgets
-#}
-    {% set config = {'form':form, 'filters':filters or {}, 'attrs': attributes or {}} %}
-    {{ generate_container(xmlui.main_cont, config) }}
-{% endmacro %}
-
-{% macro generate_table(xmlui_items, fields, formatters, tr_class_fields, on_click) %}
-{# generate a HTML table from requested widgets names
-    @param xmlui_items(iterable[unicode]): list of xmlui to show (one per row)
-    @param fields(tuple[unicode,unicode]): fields to show (name, label)
-    @param formatters(dict): dictionary of templates to format values:
-        field_name => template
-        if no formatter is set (or None is used) for a field, it will be used unmodified.
-        current xmlui items will be set as "item" key
-    @param tr_class_fields(iterable[unicode]): name of fields to use as class
-        class will be "{name}_{value}" where name is field name, and value field value
-        all lowercase/stripped
-    @param on_click(data_objects.OnClick): thing to do when clicking on a row
-#}
-    {% if formatters is undefined %}
-        {% set formatters = {} %}
-    {% endif %}
-    {% if on_click is undefined %}
-        {% set on_click = {} %}
-    {% endif %}
-    <table>
-        <thead>
-            <tr>
-                {% for name,label in fields %}
-                    <th>{{ label }}</th>
-                {% endfor %}
-            </tr>
-        </thead>
-        <tbody>
-            {% for xmlui in xmlui_items %}
-                {% set link=on_click.formatUrl(item=xmlui.widget_value) if on_click.url else none %}
-                <tr {{ {'class': xmlui|xmlui_class(tr_class_fields)}|xmlattr }}>
-
-                    {% for name,label in fields %}
-                        <td {{ {'class': 'td_'+name}|xmlattr }}>
-                            {% for value in xmlui.widgets[name].values %}
-                                <a {{ {'href':link}|xmlattr }}>{{ value|adv_format(formatters.get(name),item=xmlui.widget_value) }}</a>
-                            {% endfor %}
-                        </td>
-                    {% endfor %}
-                </tr>
-            {% endfor %}
-        </tbody>
-    </table>
-{% endmacro %}
-
-
-
-
-{% macro generate_list(xmlui_items, fields, formatters, item_class_fields, on_click) %}
-{# generate a list of rendered XMLUI from requested widgets names
-    very similar to generate_table but generate a list instead of a tabme
-    @param xmlui_items(iterable[unicode]): list of xmlui to show
-    @param fields(tuple[unicode,unicode]): fields to show (name, label)
-    @param formatters(dict): dictionary of templates to format values:
-        field_name => template
-        if no formatter is set (or None is used) for a field, it will be used unmodified.
-        current xmlui items will be set as "item" key for the template
-    @param item_class_fields(iterable[unicode]): name of fields to use as class
-        class will be "{name}_{value}" where name is field name, and value field value
-        all lowercase/stripped
-    @param on_click(data_objects.OnClick): thing to do when clicking on a row
-#}
-    {% if formatters is undefined %}
-        {% set formatters = {} %}
-    {% endif %}
-    {% if on_click is undefined %}
-        {% set on_click = {} %}
-    {% endif %}
-    <ul class="xmlui_list">
-        {% for xmlui in xmlui_items %}
-            <li>
-            {% set link=on_click.formatUrl(item=xmlui.widget_value) if on_click.url else none %}
-                <a {{ {'class': xmlui|xmlui_class(item_class_fields),
-                        'href':link}|xmlattr }}>
-                    {% for name,label in fields %}
-                        <span {{ {'class': 'xmlui_field__'+name}|xmlattr }}>
-                            {% for label in xmlui.widgets.get(name, {}).labels %}
-                                <span>{{ label|adv_format(formatters.get(name),item=xmlui.widget_value) }}</span>
-                            {% endfor %}
-                        </span>
-                    {% endfor %}
-                </a>
-            </li>
-        {% endfor %}
-    </ul>
-{% endmacro %}
-
-
-
-
-
-{# containers #}
-
-{% macro vertical_container(cont, config) %}
-    <div class="xmlui_cont xmlui_cont_vertical">
-        {{ generate_children(cont, config) }}
-    </div>
-{% endmacro %}
-
-{% macro pairs_container(cont, config) %}
-    {# TODO: proper impelmentation (do the same as vertical container for now #}
-    <div class="xmlui_cont xmlui_cont_vertical">
-        {{ generate_children(cont, config) }}
-    </div>
-{% endmacro %}
-
-{% macro label_container(cont, config) %}
-    <div class="xmlui_cont xmlui_cont_vertical">
-        {% for child in cont.children %}
-            {% if loop.index is odd %}
-                {# label #}
-                {% if child.type == 'label' %}
-                    {% set for_ = ('wid_' + (child.for_name or child.name or '_noname'))|next_gidx %}
-                    {{ label_widget(child, config, for=for_) }}
-                {% endif %}
-            {% else %}
-                {# widget #}
-                {% set id = ('wid_' + (child.name or '_noname'))|cur_gidx %}
-                {{ generate_widget(child, config, id=id) }}
-            {% endif %}
-        {% endfor %}
-    </div>
-{% endmacro %}
-
-
-{# widgets #}
-
-{% macro text_widget(wid, config, id=none) %}
-    <p class="xmlui_widget xmlui_text" {{ {'id':id}|xmlattr }}>
-        {{- wid|item_filter(config.filters)|default('\u00A0',true) -}}
-    </p>
-{% endmacro%}
-
-{% macro label_widget(wid, config, for=none) %}
-    {% if config.form %}
-        <label class="xmlui_widget xmlui_label" {{ {'for':for}|xmlattr }}>
-            {{wid|item_filter(config.filters)}}
-        </label>
-    {% else %}
-        <span class="xmlui_widget xmlui_label" {{ {'id':none if not for else 'label_%s'|format(for)}|xmlattr }}>{{wid|item_filter(config.filters)}}</span>
-    {% endif %}
-{% endmacro%}
-
-{% macro string_widget(wid, config, id=none) %}
-    {% if config.form %}
-        <input class="xmlui_widget xmlui_string" type="text" {{ {'name':wid.name, 'id':id, 'value':wid|item_filter(config.filters)}|dict_ext(config.attrs, wid.name)|xmlattr }}>
-    {% else %}
-        <div class="xmlui_widget xmlui_string"  {{ {'id':id}|xmlattr }}>
-            {{- wid|item_filter(config.filters)|default('\u00A0',true) -}}
-        </div>
-    {% endif %}
-{% endmacro%}
-
-{% macro textbox_widget(wid, config, id=none) %}
-    {% if config.form %}
-        <textarea class="xmlui_widget xmlui_textbox" rows="10" cols="50" {{ {'name':wid.name, 'id':id}|dict_ext(config.attrs, wid.name)|xmlattr }}>
-            {{- wid|item_filter(config.filters) -}}
-        </textarea>
-    {% else %}
-        <p class="xmlui_widget xmlui_textbox" {{ {'id':id}|xmlattr }}>
-            {{- wid|item_filter(config.filters) -}}
-        </p>
-    {% endif %}
-{% endmacro%}
-
-{% macro list_widget(wid, config, id=none) %}
-    {% if config.form %}
-        <select class="xmlui_widget xmlui_list" {{ {'name':wid.name, 'id':id}|dict_ext(config.attrs, wid.name)|xmlattr }}>
-            {% for value,label in wid.options %}
-                <option {{ {'value':value}|xmlattr }} {{ 'selected' if value in wid.selected }}>
-                    {{- label -}}
-                </option>
-            {% endfor %}
-        </select>
-    {% else %}
-        <div class="xmlui_widget xmlui_list" {{ {'id':id}|xmlattr }}>
-            {% for value,label in wid.items %}
-                <span class="xmlui_list_item value_{{value|attr_escape}}">
-                    {{- label -}}
-                </span>
-            {% endfor %}
-        </div>
-    {% endif %}
-{% endmacro%}
--- a/default/invitation/welcome.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-{% extends 'base/base.html' %}
-
-{% block body %}
-<div class='invitation_header'>
-    <h1>{% trans %}Welcome {{name}}{% endtrans %}</h1>
-    <p class='instructions'>{% trans %}You have been invited to participate with the community, please choose an action below{% endtrans %}</p>
-</div>
-
-{% if include_url is defined %}
-<iframe id='include_main' src='{{include_url}}'></iframe>
-{% endif %}
-
-{% endblock body %}
--- a/default/login/logged.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-{% set post_confirm_message=_("You have been logged correctly") %}
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-{% block title %}{{C.APP_NAME}}{% endblock %}
-
-{% block confirm_message %}
-    {% trans %}You have been logged correctly{% endtrans %}
-{% endblock confirm_message %}
-
-{% block body %}
-<div id='logged'>
-    <div class='logged_box box'>
-        {% if guest_session %}
-            <p>{% trans %}You are logged as a guest{% endtrans %}</p>
-        {% else %}
-            <p>{% trans name='<span class="logged_profile">'|safe + profile + '</span>'|safe %}You are logged under the account {{name}} {% endtrans %}</p>
-        {% endif %}
-        <p>{% trans session_started='<span class="logged_time">'|safe + session_started|date_fmt('relative') + '</span>'|safe %}You logged {{session_started}}{% endtrans %}</p>
-    </div>
-    {% call form.form() %}
-        {{ field.meta('type', 'disconnect') }}
-        {{ field.submit(_("Disconnect")) }}
-    {% endcall %}
-</div>
-{% endblock body %}
--- a/default/login/login.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-{% block title %}{{C.APP_NAME}} login{% endblock %}
-
-{% block body %}
-<div id="login_container">
-<div id="login_box">
-    <div id="login_left">
-        <img src="{{media_path}}libervia/register_left.png">
-    </div>
-    <div id="login_right">
-        {% block login_right_top %}{% endblock %}
-        {% if login_error is defined %}
-            {# error messages displayed in case of failing attempt to login #}
-            <div id="login_error">
-                <p>
-                {% block login_error_message %}
-                {% if login_error == S_C.PROFILE_AUTH_ERROR %}
-                    {%- trans %}Your login and/or password is incorrect. Please try again.{% endtrans -%}
-                {% elif login_error == S_C.XMPP_AUTH_ERROR %}
-                    {%- trans %}Your XMPP account failed to connect. Did you enter the good password? If you have changed your XMPP password since your last connection on Libervia, please use another SàT frontend to update your profile.{% endtrans -%}
-                {% elif login_error == S_C.NO_REPLY %}
-                    {%- trans %}Did not received a reply (the timeout expired or the connection is broken).{% endtrans -%}
-                {% else %}
-                    {%- trans %}An unknown error occurred, please contact your service administrator.{% endtrans -%}
-                {% endif %}
-                {% endblock login_error_message %}
-                </p>
-            </div>
-        {% endif %}
-
-        <div id="login_form">
-            {% block login_form %}
-            {% call form.form() %}
-                {{ field.meta('type', 'login') }}
-                {{ field.text("login", _("Login"),
-                              required=true,
-                              value=login,
-                              )}}
-                {{ field.password("password", _("Password"), required=not empty_password_allowed) }}
-                {{ field.submit(_("Log in")) }}
-            {% endcall %}
-            {% endblock login_form %}
-        </div>
-        {% block login_right_bottom %}
-        {% if register_url is defined %}
-            <div id="create_account_link">
-                <p>
-                    <a href="{{register_url}}">{% trans %}No account yet? Create a new one!{% endtrans %}</a>
-                </p>
-            </div>
-        {% endif %}
-        {% endblock login_right_bottom %}
-    </div>
-</div>
-</div>
-{% endblock body %}
--- a/default/login/register.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-{% extends 'login/login.html' %}
-
-{% block title %}{{C.APP_NAME}} register new account{% endblock %}
-
-{% block login_error_message %}
-{% if login_error == S_C.ALREADY_EXISTS %}
-    {%- trans %}This login already exists, please choose another one.{% endtrans -%}
-{% elif login_error == S_C.INVALID_INPUT %}
-    {%- trans %}The data you entered are nod valid.{% endtrans -%}
-{% elif login_error == S_C.BAD_REQUEST %}
-    {%- trans %}Bad request, please contact your service administrator{% endtrans -%}
-{% else %}
-    {%- trans %}An unknown error occurred, please contact your service administrator.{% endtrans -%}
-{% endif %}
-{% endblock login_error_message %}
-
-{% block login_right_top %}
-{% if login_url is defined %}
-    <div id="login_link">
-        <a href="{{login_url}}">
-            {%- trans %}Go to login page{% endtrans -%}
-        </a>
-    </div>
-{% endif %}
-{% endblock login_right_top %}
-
-{% block login_form %}
-{% call form.form(class='register') %}
-    {{ field.meta('type', 'register') }}
-    {{ field.text("login", _("Login"),
-                  required=true,
-                  pattern=S_C.REG_LOGIN_RE,
-                  title=_("Login must be lower case, with only plain letters (a-z), numbers (0-9) or underscore(_)"),
-                  value=login,
-                  )}}
-    {{ field.email("email", _("Email"),
-                   required=true,
-                   value=email,
-                   )}}
-    {{ field.password("password", _("Password"),
-                      required=true,
-                      minlength=S_C.PASSWORD_MIN_LENGTH,
-                      value=password,
-                      )}}
-    {{ field.submit(_("Register new account")) }}
-{% endcall %}
-{% endblock login_form %}
-
-{% block login_right_bottom %}{% endblock %}
--- a/default/merge-request/create.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-{# creata a new ticket #}
-
-{% set category_menu = [('merge-requests', url_tickets_list)] %}
-{% extends 'base/base.html' %}
-{% block body %}
-<div class="box message--info">
-    <p>{% trans %}It is not yet possible to create a merge request from inside Libervia, please use <pre>jp merge-request set</pre> for now. Merge requests welcome ;){% endtrans %}</p>
-</div>
-{% endblock body %}
--- a/default/merge-request/discover.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-{% extends 'base/base.html' %}
-{% import 'components/block.html' as block with context %}
-{% import 'components/images.html' as images with context %}
-{% import 'components/avatar.html' as avatar with context %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-{% block body %}
-{{ icon_defs('merge') }}
-<p class="instructions--head">
-    {% trans %}
-    Please select a merge-requests handler
-    {% endtrans %}
-</p>
-{% if mr_handlers is defined %}
-    <div class="disco_tickets">
-        {{block.disco_icon_grid(mr_handlers, 'merge')}}
-    </div>
-{% endif %}
-
-{% call form.form(class="form_single") %}
-    {{ field.text("jid", _("handler jid"), required=true)}}
-    {{ field.submit(_("Access")) }}
-{% endcall %}
-{% endblock body %}
--- a/default/merge-request/edit.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-{# edit an existing ticket #}
-
-{% set category_menu = [('merge-requests', url_tickets_list),
-                        ('merge-request_new', url_tickets_new)] %}
-{% extends 'base/base.html' %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-{% import 'input/xmlui.html' as xmlui with context %}
-
-{% block body %}
-<div class='instructions'>
-    <p><span class="box">{% trans app_name=C.APP_NAME%}Note: to modify content of the merge request, you'll have to use command line (with jp){% endtrans %}</span></p>
-</div>
-<div class="create single ticket box">
-{% call form.form() %}
-    {{ xmlui.generate(new_ticket_xmlui,
-                      attributes = {'title': {'required': 'required',
-                                              'placeholder': _("Short description of your issue/request")},
-                                    'body': {'required': 'required',
-                                             'placeholder': _("Please describe your issue/request with as much details as possible")},
-                                    'labels': {'placeholder': _("You can enter one or several labels separated by commas")},
-                                    })}}
-    {{ field.submit(_("Modify ticket")) }}
-{% endcall %}
-</div>
-{% endblock body %}
--- a/default/merge-request/item.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-{# display a single ticket
-
-    @variable item(xmlui_item): ticket to display
-    @variable comments(data_object.BlogItems): comments of the ticket
-    @variable comments_service(unicode): service for adding comments
-    @variable comments_node(unicode): node for adding comments
-#}
-
-{% set category_menu = [('merge-requests', url_tickets_list),
-                        ('merge-request_new', url_tickets_new)] %}
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% import 'input/xmlui.html' as xmlui with context %}
-{% import 'blog/macros.html' as blog with context %}
-{% import 'input/textbox.html' as textbox with context %}
-
-{% block title %}{{item|adv_format('[{value.widget_value.id}] {value.widget_value.title}') }}{% endblock %}
-
-{% block confirm_message %}
-    {% trans %}Your comment has been sent{% endtrans %}
-{% endblock confirm_message %}
-
-{% block body %}
-{{ icon_defs('pencil') }}
-<div class="tab_container">
-    <div class="tab_header">
-        <ul>
-            <li class="tab_button clicked" onclick='toggle_clicked_class_sel(".tab_button,.tab_page")'>{% trans %}Description{% endtrans %}</li>
-            <li class="tab_button" onclick='toggle_clicked_class_sel(".tab_button,.tab_page")'>{% trans %}Patches{% endtrans %}</li>
-        </ul>
-    </div>
-    <div class="tab_page clicked" id="tab_description">
-        <div id="{{ item.widget_value['id'] }}" class="view single ticket box">
-            {% if url_ticket_edit is defined %}
-                <p class="box__tools">
-                    <a href="{{url_ticket_edit}}">
-                    {{ icon('pencil', cls='icon--small') }}
-                    edit
-                    </a>
-                </p>
-            {% endif %}
-            {{ xmlui.generate(item,
-                form=false,
-                filters={'created': {'filters': ['date_fmt'], 'filters_args':[{'fmt': 'short'}]},
-                         'updated': {'filters': ['date_fmt'], 'filters_args':[{'fmt': 'short'}]},
-                         'body': {'filters': ['urlize'], 'filters_args':[{'nofollow': True, 'rel': 'noopener noreferrer'}]}},
-                )}}
-        </div>
-        {% if comments is defined %}
-        <div id="blog_items">
-            {{ blog.show_items(comments|reverse, expanded=true) }}
-        </div>
-        {% endif %}
-        {% if comments_node is defined %}
-            <div class="comment_post">
-                {{- textbox.comment_or_login(service=comments_service, node=comments_node) -}}
-            </div>
-        {% endif %}
-    </div>
-    <div class="tab_page" id="tab_patches">
-        {% for patch in patches %}
-            <div class="patch">
-                <header class="box">
-                    <div class="author"><label>{% trans %}author:{% endtrans %} </label>{{patch.author}}</div>
-                    <p class="commit_msg">{{patch.commit_msg}}</p>
-                </header>
-                <div class="diff">
-                    {{- patch.diff|highlight('diff') -}}
-                </div>
-            </div>
-        {% endfor %}
-    </div>
-</div>
-{% endblock body %}
--- a/default/photo/album.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-{% extends 'base/base.html' %}
-{% import 'input/textbox.html' as textbox with context %}
-{% import 'blog/macros.html' as blog with context %}
-
-{% block body %}
-{{ icon_defs('comment-empty') }}
-<div class="album">
-    {% for file in files_data %}
-        {% if file.type == C.FILE_TYPE_FILE %}
-            <div class="album__item">
-                <div class="album__vignette">
-                    <img src="{{file.thumb_url}}" class="album__thumbnail" alt="{{file.name}}">
-                </div>
-                {% if file.comments_url is defined %}
-                    <span class='album__comments-bar' onclick="clicked_mh_fix('{{'comments_panel'|next_gidx}}')">
-                        {% trans %}comments{% endtrans %}
-                        {% if file.comments_count %}
-                            <span class='comments__count'>({{file.comments_count}})</span>
-                        {% endif %}
-                        {{ icon('comment-empty', cls='icon--small') }}
-                    </span>
-                    <div id='{{'comments_panel'|cur_gidx}}' class="panel-drawer">
-                        {{ blog.show_items(file.comments, expanded=true, dates_fmt='relative') }}
-                        <div class="comment_post">
-                            {{- textbox.comment_or_login(service=file.comments_service, node=file.comments_node) -}}
-                        </div>
-                    </div>
-                {% endif %}
-            </div>
-        {% endif %}
-    {% endfor %}
-</div>
-{% endblock body %}
--- a/default/photo/discover.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-{% extends 'base/base.html' %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-{% block body %}
-<p class="instructions--alt">{% trans %}Photo discovery is not implemented yet, however you can enter a jid below to find its albums{% endtrans %}</p>
-{% call form.form(class="form_single") %}
-    {{ field.text("jid", _("device full jid"), required=true)}}
-    {{ field.submit(_("Access")) }}
-{% endcall %}
-{% endblock body %}
--- a/default/session/disconnect.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-{% extends 'base/base.html' %}
-
-{% block body %}
-<p>{% trans %}You have been disconnected, have a nice day!{% endtrans %}</p>
-{% endblock body %}
--- a/default/static/app.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-#body {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-}
-
-#web_app_box {
-    width: 80%;
-    padding: 2em;
-    text-align: justify;
-}
-
-#link_section {
-    font-weight: bold;
-    text-align: center;
-    font-size: 1.5rem;
-}
-
-article p:last-child {
-    text-align: center;
-}
--- a/default/static/blog.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,313 +0,0 @@
-/*** articles ***/
-
-article.box {
-    position: relative;
-    margin: 2% auto;
-    border-style: solid none none;
-    border-width: 1px;
-    padding: 2%;
-    min-height: 9em;
-    max-height: 20em;
-    overflow: hidden;
-    transition: max-height 0.5s;
-}
-
-article video {
-    max-width: 100%;
-    height: auto;
-}
-
-.main_article article.box:not(.clicked) {
-    border-bottom-right-radius: 0;
-    border-bottom-left-radius: 0;
-}
-
-/** header **/
-
-header a {
-    color: inherit;
-    text-decoration: inherit;
-}
-
-header .metadata {
-    text-align: right;
-}
-
-article .author {
-    font-weight: bold;
-}
-
-article .author::after {
-    content: ", ";
-}
-
-/*** labels ***/
-
-.labels {
-    text-align: right;
-    white-space: nowrap;
-}
-
-.labels a {
-    text-decoration: none;
-    color: inherit;
-}
-
-.labels span {
-    font-variant: small-caps;
-    font-size: 0.8rem;
-    background-color: #ddd;
-    border-radius: 0.4em;
-    padding: 0 0.5em;
-    transition: all 0.5s;
-}
-
-.labels a>span {
-    cursor: pointer;
-}
-
-.labels a>span:hover {
-    box-shadow: 0px 0px 6px 1px #000;
-}
-
-
-/** content **/
-
-article div.content {
-    text-align: justify;
-    font-size: 0.9em;
-}
-
-article div.content.text {
-    white-space: pre-wrap;
-}
-
-
-article img {
-    max-width: 100%;
-    margin: 0;
-}
-
-/** reduce/expand buttons **/
-
-.expand_box {
-    cursor: pointer;
-}
-
-.box_top {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-}
-
-.box_bottom {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    width: 100%;
-}
-
-.expand_box.box_bottom::before {
-    /* we do a gradient to show that text can be expanded */
-    background-image: linear-gradient(to bottom,rgba(0,0,0,0),#ffffff);
-    display: block;
-    content: "";
-    width: 100%;
-    height: 4em;
-    border: none;
-}
-
-.expand_box p {
-    background-color: white;
-    margin: 0;
-    text-align: center;
-    font-weight: bold;
-    font-size: 0.8em;
-    border-style: solid none dotted none;
-    border-width: 1px 0 1px;
-    border-bottom-color: gray;
-}
-
-article .expand_box .hide {
-    /* when not clicked, we only display .show */
-    display: none;
-}
-
-article .expand_box.box_top {
-    /* top expand box only show a reduce button
-     * so it should be displayed only when article is .clicked */
-    display: none;
-}
-
-/** other language **/
-
-/* we hide everything just to display a message informing that
- * the item is in an other language, and asking to click
- * to display it */
-
-article.other_lang>div.info {
-    display: none;
-}
-
-article.other_lang.init>* {
-    display: none;
-}
-
-article.other_lang.init>div.info {
-    display: initial;
-}
-
-article.other_lang.init>div.expand_box.box_top {
-    /* as we need to have the message clickable, we cheat and use
-     * expand box on the whole surface but fully transparent */
-    display: initial;
-    height: 100%;
-    opacity: 0;
-}
-
-article.other_lang.init>div.info>p {
-    margin: 0;
-    padding: 0.2em;
-    text-align: center;
-    font-style: italic;
-}
-
-article.other_lang.init {
-    border: none;
-    border-radius: 0;
-    padding: 0;
-    min-height: 0;
-}
-
-/*** comments ***/
-
-button.comments_btn {
-    border: none;
-    font-weight: bold;
-    display: block;
-    margin: 0 10% 0 auto;
-    border-radius: 1em;
-    background:  #b8bcc4;
-    color: #4d4d4d;
-}
-
-button.comments_btn:active {
-    background: #4d4d4d;
-    color: #b8bcc4;
-}
-
-button.comments_btn.clicked span.show {
-    display: none;
-}
-
-button.comments_btn:not(.clicked) span.hide {
-    display: none;
-}
-
-.comments_panel {
-    max-height: 0;
-    opacity: 0;
-    transition: max-height 1s, opacity 2s;
-    overflow: hidden;
-}
-
-.comments_panel.clicked {
-    opacity: 1;
-}
-
-.comments article {
-    background-color: #9ca0a8;
-    border: none;
-    max-height: none;
-}
-
-.comment_post {
-    text-align: center;
-}
-
-.comment_post textarea {
-    border-style: solid;
-    border-width: 1px  0;
-    border-color: black;
-    max-width: 100%;
-}
-
-.comments_panel article .expand_box {
-    /* no expand box in comments */
-    display: none;
-}
-
-/*** media queries ***/
-
-@media (min-width: 500px) {
-    /*** general ***/
-
-    #main_area {
-        background-image: var(--bg-img);
-        background-repeat: no-repeat;
-        background-size: cover;
-    }
-
-    article.box {
-        width: 80%;
-        border-style: solid solid none solid;
-    }
-    .comments article.box {
-        width: 30rem;
-        margin: 2% auto;
-        border: none;
-    }
-    .comment_post textarea {
-        border-width: 1px;
-        border-radius: 1em;
-        border: solid 1px;
-        padding: 0.5em;
-    }
-
-    footer span {
-        background: rgba(200,200,200,0.6);
-        border-radius: 0.5em 0.5em 0 0;
-        padding: 0 0.5em;
-        margin-top: 1em;
-    }
-
-}
-
-/*** clicked ***/
-
-.main_article article.clicked {
-    border-bottom-style: solid;
-}
-
-.main_article article.clicked .expand_box.box_top {
-    display: initial;
-}
-
-.main_article article.clicked .expand_box {
-    opacity: 0;
-    transition: opacity 0.5s;
-}
-
-.main_article article.clicked .expand_box p {
-    background-color: transparent;
-    border: none;
-}
-
-.main_article article.clicked .expand_box:hover {
-    opacity: 1;
-    color: grey;
-}
-
-.main_article article.clicked .expand_box::before {
-    background-image: none;
-    display: none;
-}
-
-.main_article article.clicked .expand_box .hide {
-    display: inline;
-}
-
-.main_article article.clicked .expand_box .show {
-    display: none;
-}
--- a/default/static/chat.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-:root {
-  --message_input-height: 2rem;
-}
-
-#main_area {
-    overflow: hidden;
-}
-
-.chat_widget {
-	position: absolute;
-    height: 100%;
-    width: 100%;
-    box-sizing: border-box;
-    display: flex;
-    flex-direction: column;
-}
-
-#messages {
-    height: 100%;
-    min-height: 150px;
-    overflow: auto;
-    box-sizing: border-box;
-    resize: vertical;
-    transition: height 0.6s;
-}
-
-#subject {
-	padding: 1em;
-	text-align: center;
-	background: #eee;
-	font-style: italic;
-}
-
-#messages > p {
-    margin: 0;
-    padding: 0.5em 0 0 0.5em;
-}
-
-#messages .msg_body {
-    white-space: pre-wrap;
-}
-
-#message_input {
-    width: 100%;
-    height: 100%;
-    padding-top: 0.5rem;
-    margin: 0;
-    box-sizing: border-box;
-    resize: none;
-}
-
-.message_box {
-    flex: 1 1 calc(var(--message_input-height));
-    position: relative;
-    min-height: 1rem;
-    box-sizing: border-box;
-}
-
-
-#messages > p.msg_info {
-    white-space: pre-wrap;
-    font-family: monospace;
-    color: #049282;
-}
-
-.msg_header {
-	display: block;
-	font-size: 0.9em;
-}
-
-.author {
-    font-weight: bold;
-}
-
-.date {
-	color: #777;
-}
-
-@media (min-width: 800px) {
-    #messages > p {
-        padding-left: 1.5em;
-    }
-}
--- a/default/static/chat.js	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/* SàT Template: Chat page handling
- *
- * Copyright (C) 2017 Jérôme Poisson (goffi@goffi.org)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-var msgInput = document.getElementById('message_input');
-var messages = document.getElementById('messages');
-const messagesTransitionOri = messages.style.transition;
-
-msgInput.addEventListener('keypress', function(event) {
-    if (event.which == 13 && !event.shiftKey) {
-        if (messages.style.height !== '100%') {
-            messages.style.transition = messagesTransitionOri;
-            setTimeout(function() {
-                messages.style.transition = 'initial';
-                messages.scrollTop = messages.scrollHeight;
-            }, 1000);
-            messages.style.height = "100%";
-        }
-        if (!this.value.trim()) {
-            return;
-        }
-        socket.send({'type': 'msg',
-                     'body': this.value});
-        this.value = '';
-        event.preventDefault();
-    }}
-);
-
-var mutationCb = function(mutationsList) {
-    scrollPos = messages.scrollTop + messages.clientHeight;
-    if (messages.lastChild.offsetTop - scrollPos - 10 <= 0) {
-        // we auto scroll only if we are at the bottom of the page
-        // else the use is probably checking history
-        // Note thas this doesn't take margin into account,
-        // we suppose margin to be 0 for messages children
-        messages.scrollTop = messages.scrollHeight;
-    }
-};
-
-var observer = new MutationObserver(mutationCb);
-
-observer.observe(messages, { childList: true });
-// we want to start with scrolling at bottom
-messages.scrollTop = messages.scrollHeight;
-messages.style.transition = 'initial';
--- a/default/static/chat_select.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-#main_area {
-    overflow: auto;
-}
-
-.rooms_list {
-	list-style: none;
-    display: grid;
-    text-align: center;
-    padding: 0;
-}
-
-.rooms_list a {
-	text-decoration: none;
-	color: inherit;
-	font-variant: small-caps;
-	font-weight: bold;
-}
-
-.rooms_list li {
-    margin-top: 1em;
-}
-
-.rooms_list li:first-child {
-    margin-top: 0;
-}
-
-.rooms_list li:hover {
-    background-color: #efefef;
-}
-
-@media (min-width: 600px) {
-    .rooms_list {
-        grid-template-columns: 1fr 1fr 1fr;
-    }
-
-    .rooms_list li {
-        margin-top: 0;
-    }
-}
--- a/default/static/common.js	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-var __session_storage_available;
-var __local_storage_available;
-
-function storageAvailable(type) {
-    /* check if session or local storage is available
-     *
-     * @param type(string): "session" or "storage"
-     * @return (boolean): true if requested storage is available
-     */
-    console.assert(type == 'session' || type == 'storage', "bad storage type (%s)", type);
-    const var_name = '__' + type + '_storage_available';
-    var available = window[var_name];
-    if (available === undefined) {
-        // test method from https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
-        var storage = window[type + 'Storage'];
-        try {
-            x = '__storage_test__';
-            storage.setItem(x, x);
-            storage.removeItem(x);
-            available = true;
-        }
-        catch(e) {
-            available = e instanceof DOMException && (
-                    // everything except Firefox
-                    e.code === 22 ||
-                    // Firefox
-                    e.code === 1014 ||
-                    // test name field too, because code might not be present
-                    // everything except Firefox
-                    e.name === 'QuotaExceededError' ||
-                    // Firefox
-                    e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
-                // acknowledge QuotaExceededError only if there's something already stored
-                storage.length !== 0;
-        }
-
-        if (!available) {
-            console.warn("%s storage not available", type);
-        }
-        window[var_name] = available;
-    }
-
-    return available;
-}
-
-function toggle_clicked_class_tag(tag_name, class_name='clicked') {
-    for (let elt of document.getElementsByTagName(tag_name)) {
-        elt.classList.toggle(class_name);
-    }
-}
-
-function toggle_clicked_class_sel(selectors, class_name='clicked') {
-    for (let elt of document.querySelectorAll(selectors)) {
-        elt.classList.toggle(class_name);
-    }
-}
-
-function set_clicked_class_id(trigger_elem_id, target_elem_id=null, class_name='clicked') {
-    if (target_elem_id === null) { target_elem_id = trigger_elem_id; }
-    document.getElementById(trigger_elem_id).addEventListener(
-        "click",
-        function() {
-            document.getElementById(target_elem_id).classList.toggle(class_name);
-        }
-    );
-}
-
-function get_elt(arg) {
-    if (typeof arg === 'string') {
-        // we should have an id
-        return document.getElementById(arg);
-    }
-    else {
-        // we should have an element
-        return arg;
-    }
-}
-
-function clicked_cls(elt) {
-    /* toggle "clicked" class on each click, and remove "init" class if present */
-    // init
-    if (elt.classList.contains("init")) {
-        elt.classList.remove("init");
-    }
-
-    // clicked
-    elt.classList.toggle("clicked");
-}
-
-function clicked_mh_fix(arg) {
-    /* toggle clicked, and fix max-height on transitionend
-     *
-     * needed to workaround transition issue with max-height:none
-     * inspired from https://css-tricks.com/using-css-transitions-auto-dimensions,
-     * thanks to Brandon Smith
-     *
-     * @param arg: element to toggle (id as string, or element itself)
-     * */
-    elt = get_elt(arg);
-
-    if (!elt.classList.contains("clicked")) {
-        /* expand */
-        var fix_expand = function(event) {
-            elt.removeEventListener("transitionend", fix_expand, false);
-            if (elt.classList.contains("clicked")) {
-                /* if event is clicked quicker than transition time,
-                 * this callback can be called on reduce */
-                elt.style.maxHeight = "none";
-            }
-        };
-
-        elt.setAttribute('max_height_init', elt.clientHeight);
-        elt.addEventListener("transitionend", fix_expand, false);
-        clicked_cls(elt);
-        elt.style.maxHeight = elt.scrollHeight + 'px';
-    }
-    else {
-        /* reduce */
-        var transition_save = elt.style.transition;
-        elt.style.transition = '';
-        requestAnimationFrame(function() {
-            elt.style.maxHeight = elt.scrollHeight + 'px';
-            elt.style.transition = transition_save;
-
-            requestAnimationFrame(function() {
-                elt.style.maxHeight = elt.getAttribute('max_height_init') + 'px';
-                elt.removeAttribute('max_height_init');
-                elt.style.maxHeight = null;
-            });
-        });
-
-        clicked_cls(elt);
-    }
-}
--- a/default/static/event.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-@import 'blog.css'; /* needed as blog/articles.html is included */
-
-.invitation_header {
-    text-align: center;
-    font-variant: small-caps;
-    border: none;
-}
-
-.instructions {
-    font-style: italic;
-    margin: 0;
-}
-
-#event_picture {
-    max-width: 100%;
-    border-radius: 2em;
-}
-
-#event_counter {
-    font-weight: bold;
-    font-size: 1.3em;
-    text-align: center;
-    width: 15em;
-    margin: 1em auto;
-    border: 2px solid #aab6c4;
-    background-color: #f3e7e7;
-    color: #2f3943;
-}
-
-.attendance {
-    background-color: #9ca0a8;
-    padding: 0 0.3em;
-    max-width: 580px;
-    margin: 0 auto;
-    color: #3A3A3A;
-}
-
-.attending {
-    width: 6em;
-    margin: 0 auto;
-}
-
-.attending label {
-    /* display: inline; */
-    margin: 0.8em 0;
-}
-
-.guests label {
-    display: block;
-    margin: 1em 0;
-}
-
-.guests input {
-    width: 2.5em;
-    margin: 0 1em;
-}
-
-.poll_instructions {
-    font-weight: bold;
-    font-size: 0.9em;
-    font-size: 1.1em;
-    text-align: center;
-}
-
-.guests label{
-    text-align: center;
-    /* font-style: italic */
-}
-
-.guests input {
-    display: block;
-    margin: 0 auto;
-}
-
-.submit {
-    width: 15em;
-    margin: 0 auto;
-    padding: 1em 0;
-}
-
-.submit input {
-    width: 100%;
-}
-
-@media (min-width: 500px) {
-    .invitation_header {
-        width: 94%;
-        margin: 0 auto;
-        border: solid 1px;
-        border-color: #9ca0a8;
-    }
-}
--- a/default/static/file.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#files {
-    list-style: none;
-    display: flex;
-    flex-wrap: wrap;
-}
-
-.file {
-    padding: 0 0 0.5em;
-    width: 100px;
-    text-overflow: ellipsis;
-    overflow: hidden;
-    text-align: center;
-    overflow-wrap: break-word;
-}
-
-.file:hover {
-    background: #ddd;
-    overflow: visible;
-}
-
-.file a {
-    text-decoration: none;
-    outline: none;
-}
-
-.file a:focus img {
-    outline: 3px solid #bbb;
-}
-
-.file__icon {
-    height: 3em;
-    display: block;
-    margin: 0 auto;
-}
-
-.file__thumbnail {
-    max-width: 300px;
-    height: 125px;
-    display: block;
-    margin: 0 auto;
-}
-
-
-@media (min-width: 500px) {
-    .file {
-        padding: 1em;
-        width: 170px;
-    }
-}
--- a/default/static/fonts.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/* generated from https://google-webfonts-helper.herokuapp.com */
-
-/* *** sat_base_font *** */
-
-/* sat_base_font-regular - latin-ext_latin */
-@font-face {
-  font-family: 'sat-base-font';
-  font-style: normal;
-  font-weight: 400;
-  src: local('sat-base-font'), local('sat-base-font-Regular'),
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-
-/* sat_base_font-italic - latin-ext_latin */
-@font-face {
-  font-family: 'sat-base-font';
-  font-style: italic;
-  font-weight: 400;
-  src: local('sat-base-font Italic'), local('sat-base-font-Italic'),
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-
-/* sat_base_font-700 - latin-ext_latin */
-@font-face {
-  font-family: 'sat-base-font';
-  font-style: normal;
-  font-weight: 700;
-  src: local('sat-base-font Bold'), local('sat-base-font-Bold'),
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-
-/* sat_base_font-900 - latin-ext_latin */
-@font-face {
-  font-family: 'sat-base-font';
-  font-style: normal;
-  font-weight: 900;
-  src: local('sat-base-font Black'), local('sat-base-font-Black'),
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-
-/* sat_base_font-700italic - latin-ext_latin */
-@font-face {
-  font-family: 'sat-base-font';
-  font-style: italic;
-  font-weight: 700;
-  src: local('sat-base-font Bold Italic'), local('sat-base-font-BoldItalic'),
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-
-/* sat_base_font-900italic - latin-ext_latin */
-@font-face {
-  font-family: 'sat-base-font';
-  font-style: italic;
-  font-weight: 900;
-  src: local('sat-base-font Black Italic'), local('sat-base-font-BlackItalic'),
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
-       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
--- a/default/static/fonts/sat_base_font/README.txt	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-This font is based on the Alegreya font by Juan Pablo del Peral (juan@huertatipografica.com.ar), it has been modified to be compressed using https://google-webfonts-helper.herokuapp.com, and put here for being embedded in Salut à Toi templates, to avoid privacy issues with third party website download.
-
-As the name is a reserved name and the compression make the font a derivative, this font has been renamed to "sat_base_font", according to the recommandation read at http://scripts.sil.org/cms/scripts/page.php?item_id=OFL-FAQ_web#1db14da7
-
-Thanks to Juan Pable del Peral for his work!
-
-The license "SIL Open Font License, Version 1.1" only apply to this directory.
--- a/default/static/fonts/sat_base_font/SIL Open Font License.txt	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-Copyright (c) 2011, Juan Pablo del Peral (juan@huertatipografica.com.ar), 
-with Reserved Font Names "Alegreya" "Alegreya SC"
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
-
-"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
-
-5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff2 has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff2 has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff2 has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff2 has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff2 has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff has changed
Binary file default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff2 has changed
--- a/default/static/forum.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-@import 'blog.css'; /* needed as blog/articles.html is included */
-
-#forums {
-    margin: 2em auto 0 auto;
-    width: 90%;
-}
-
-ul.forum {
-    box-sizing: border-box;
-    list-style: none;
-    display: flex;
-    margin: 0 auto;
-}
-
-ul.forum>li {
-    width: 100%;
-}
-
-ul.forum>li>a {
-    border: 0.7rem solid #ddd;
-    border-radius: 0.5em;
-    display: block;
-    padding: 1rem;
-    margin-bottom: 1rem;
-    text-decoration: none;
-    color: inherit;
-}
-
-a.forum_actif:hover {
-    background-color: #43d2f6;
-}
-
-p.forum_short-desc {
-    color: #666;
-    margin: 0;
-    font-size: 0.8em;
-}
-
-.forum__topics {
-    margin-top: 3em;
-}
-
-.forum__topics>div {
-    box-sizing: border-box;
-    width: 80%;
-    margin: 0.5em auto;
-    padding: 0.2em 2em;
-    background: #eee;
-
-}
-
-.forum__topics>:hover {
-    background-color: #43d2f6;
-}
-
-.forum__topics img.avatar {
-    vertical-align: middle;
-    margin-right: 1em;
-}
-
-.forum__topics a {
-    display: block;
-    text-decoration: none;
-    color: inherit;
-}
-
-.textbox input {
-    width: 100%;
-    box-sizing: border-box;
-}
-
-@media (min-width: 500px) {
-    .textbox input {
-        min-width: 26em;
-        width: auto;
-    }
-}
--- a/default/static/highlight.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/* This file as been generated from Pygments using
- * f.write(HtmlFormatter().get_style_defs('.highlight'))
- * where f was an open file.
- * It is used in Libervia for highlighting code or markup
- * If changes are needed, they should be done in a separate CSS file
- * so this one keep Pygments' defaults
- */
-.highlight .hll { background-color: #ffffcc }
-.highlight  { background: #f8f8f8; }
-.highlight .c { color: #408080; font-style: italic } /* Comment */
-.highlight .err { border: 1px solid #FF0000 } /* Error */
-.highlight .k { color: #008000; font-weight: bold } /* Keyword */
-.highlight .o { color: #666666 } /* Operator */
-.highlight .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
-.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
-.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
-.highlight .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
-.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
-.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
-.highlight .gd { color: #A00000 } /* Generic.Deleted */
-.highlight .ge { font-style: italic } /* Generic.Emph */
-.highlight .gr { color: #FF0000 } /* Generic.Error */
-.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
-.highlight .gi { color: #00A000 } /* Generic.Inserted */
-.highlight .go { color: #888888 } /* Generic.Output */
-.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
-.highlight .gs { font-weight: bold } /* Generic.Strong */
-.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
-.highlight .gt { color: #0044DD } /* Generic.Traceback */
-.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
-.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
-.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
-.highlight .kp { color: #008000 } /* Keyword.Pseudo */
-.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
-.highlight .kt { color: #B00040 } /* Keyword.Type */
-.highlight .m { color: #666666 } /* Literal.Number */
-.highlight .s { color: #BA2121 } /* Literal.String */
-.highlight .na { color: #7D9029 } /* Name.Attribute */
-.highlight .nb { color: #008000 } /* Name.Builtin */
-.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
-.highlight .no { color: #880000 } /* Name.Constant */
-.highlight .nd { color: #AA22FF } /* Name.Decorator */
-.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
-.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
-.highlight .nf { color: #0000FF } /* Name.Function */
-.highlight .nl { color: #A0A000 } /* Name.Label */
-.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
-.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
-.highlight .nv { color: #19177C } /* Name.Variable */
-.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
-.highlight .w { color: #bbbbbb } /* Text.Whitespace */
-.highlight .mb { color: #666666 } /* Literal.Number.Bin */
-.highlight .mf { color: #666666 } /* Literal.Number.Float */
-.highlight .mh { color: #666666 } /* Literal.Number.Hex */
-.highlight .mi { color: #666666 } /* Literal.Number.Integer */
-.highlight .mo { color: #666666 } /* Literal.Number.Oct */
-.highlight .sa { color: #BA2121 } /* Literal.String.Affix */
-.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
-.highlight .sc { color: #BA2121 } /* Literal.String.Char */
-.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */
-.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
-.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
-.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
-.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
-.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
-.highlight .sx { color: #008000 } /* Literal.String.Other */
-.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
-.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
-.highlight .ss { color: #19177C } /* Literal.String.Symbol */
-.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
-.highlight .fm { color: #0000FF } /* Name.Function.Magic */
-.highlight .vc { color: #19177C } /* Name.Variable.Class */
-.highlight .vg { color: #19177C } /* Name.Variable.Global */
-.highlight .vi { color: #19177C } /* Name.Variable.Instance */
-.highlight .vm { color: #19177C } /* Name.Variable.Magic */
-.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
-
--- a/default/static/invitation.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-#body {
-    display: flex;
-    flex-direction: column;
-}
-
-.invitation_header {
-    flex: 1
-}
-
-.invitation_header h1,p {
-    text-align: center;
-}
-
-#include_main {
-    flex: 9
-}
-
-#include_main {
-    width: 100%;
-    margin: 0;
-    border: none;
-}
-
-footer {
-    /* footer should already be displayed in included page */
-    display: none;
-}
--- a/default/static/login.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,197 +0,0 @@
-body {
-    background: #111;
-}
-
-footer {
-    color: #999;
-}
-
-#login_container {
-    display: flex;
-    height: 100%;
-    align-items: center;
-    justify-content: center;
-}
-
-#login_box {
-    display: flex;
-    background: #111;
-    color: #aaa;
-    width: 600px;
-    margin: auto;
-}
-
-#login_left {
-    display: none;
-}
-
-#login_right {
-    width: 100%;
-    position: relative;
-}
-
-#login_form {
-    margin-top: 60px;
-}
-
-#login_form label {
-    font-weight: bold;
-    display: block;
-    text-align: center;
-}
-
-#login_form input:not(.form_submit) {
-    display: block;
-    margin: 0 auto;
-    height: 25px;
-    line-height: 25px;
-    width: 200px;
-    text-indent: 11px;
-    background: #000;
-    color: #ccc;
-    border: 1px solid #555;
-    border-radius: 15px 15px 15px 15px;
-}
-
-#login_form input:not(.form_submit):focus {
-	box-shadow: 0 0 2px 2px #43d2f6;
-}
-
-#login_form input:hover {
-    background-color: #222;
-}
-
-#login_form input:focus {
-	border: 1px solid #999;
-	outline: none;
-}
-
-#login_box .form_input {
-    display: block;
-    margin-bottom: 1em;
-}
-
-#login_form .form_submit,
-#logged .form_submit {
-    /* FIXME: as above /media is not good.
-     * url should be replaced by real gradient */
-    color: #fff;
-    background: #222 url('/media/libervia/gradient.png') repeat-x;
-    font-weight: bold;
-    line-height: 1;
-    text-shadow: 0 -1px 1px rgba(0,0,0,0.25);
-    padding: 7px 10px 8px;
-    border: 0;
-    border-radius: 6px 6px 6px 6px;
-    cursor: pointer;
-    margin-top: 30px;
-}
-
-#login_error {
-    position: absolute;
-    margin: -90px 0 0;
-    width: 100%;
-    height: 150px;
-    overflow: auto;
-    font-size: 0.9em;
-	font-weight: bold;
-	color: lightcoral;
-	text-align: center;
-}
-
-#login_error p {
-    position: absolute;
-    bottom: 0;
-    margin-left: 46px;
-    margin-right: 46px;
-}
-
-#create_account_link {
-    width: 100%;
-    margin-top: 3em;
-}
-
-#login_link {
-    margin-top: 1em;
-}
-
-#create_account_link a,
-#login_link a {
-    text-decoration: none;
-    text-align: center;
-    font-size: 0.8em;
-    cursor: pointer;
-    color: #fff;
-    display: block;
-    text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.2);
-}
-
-.logged_box {
-    margin: 3em;
-	padding: 1em;
-	text-align: center;
-	font-style: italic;
-}
-
-.logged_profile {
-    font-style: normal;
-    font-weight: bold;
-}
-
-#login_form form.register input[type=submit] {
-    margin-top: 0.5em;
-}
-
-@media (min-width: 600px) {
-    body {
-        background: transparent;
-    }
-
-    footer {
-        color: black;
-    }
-
-    #login_left {
-        display: inline;
-    }
-
-    #login_right {
-        /* FIXME: there is not way to correctly select images from
-         * media path at the moment */
-        background: url('/media/libervia/register_right.png');
-    }
-
-    #login_error {
-        width: auto;
-        max-height: 50px;
-        margin: 10px 46px 0;
-    }
-     
-    #login_error p {
-        position: static;
-        margin: 0;
-    }
-
-    #login_form label {
-        text-align: initial;
-        position: relative;
-        left: 50px;
-    }
-
-    #login_form label::after {
-        content: ':'
-    }
-
-    #login_form input:not(.form_submit) {
-        display: inline;
-        margin: 0;
-        position: relative;
-        left: 50px;
-    }
-
-    #create_account_link {
-        margin-top: 0;
-        bottom: 0;
-        position: absolute;
-    }
-}
--- a/default/static/merge-request.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-ticket.css
\ No newline at end of file
--- a/default/static/merge-request_item.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-@import 'highlight.css';
-
-.highlight {
-    overflow: auto;
-}
-
-.box {
-    border-radius: 0;
-    box-shadow: none;
-}
-
-.ticket {
-    max-width: none;
-}
-
-.xmlui_cont.xmlui_cont_vertical {
-    max-width: 780px;
-    margin: 0 auto;
-}
-
-.view #wid_body {
-    border: 1px solid silver;
-}
-
-#tab_patches {
-    overflow: auto;
-}
-
-#tab_patches header {
-    border-top: 1px solid silver;
-}
-
-#tab_patches header label {
-    font-weight: bold;
-}
-
-.commit_msg {
-    font-style: italic;
-    white-space: pre-wrap;
-}
-
-.diff {
-    white-space: pre;
-}
--- a/default/static/photo.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-@import 'blog.css'; /* needed for comments */
-
-.album {
-    display: flex;
-    flex-wrap: wrap;
-}
-
-.album__item {
-    width: 400px;
-	margin: 0 0.1em 2em 0.1em;
-}
-
-.album__vignette {
-    background-color: #444;
-    height: 300px;
-    position: relative;
-}
-
-.album__thumbnail {
-    height: 100%;
-    display: block;
-    margin: 0 auto;
-}
-
-.album__comments-bar {
-    display: block;
-    font-size: 0.8em;
-    color: black;
-    text-align: right;
-    cursor: pointer;
-}
-
-.album__comments-bar:hover {
-    background-color: #ddd;
-    font-weight: bold;
-}
-
-.comments__count {
-    font-weight: bold;
-}
-
-.panel-drawer {
-    max-height: 0;
-    opacity: 0;
-    transition: max-height 1s, opacity 2s;
-    overflow: hidden;
-}
-
-.panel-drawer.clicked {
-    opacity: 1;
-}
--- a/default/static/styles.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,535 +0,0 @@
-:root {
-  --select-bg-color: #ddd;
-  --size-medium: 3em;
-}
-
-
-html {
-    font-family: "sat-base-font";
-}
-
-body {
-    margin: 0;
-    padding: 0;
-    display: flex;
-    height: 100vh;
-    flex-direction: column;
-    box-sizing: border-box;
-}
-
-ul {
-    padding: 0;
-}
-
-#main_side_bar {
-
-}
-
-#main_area {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    box-sizing: border-box;
-}
-
-#body {
-    flex: 1;
-    position: relative;
-}
-
-footer {
-    text-align: center;
-    font-size: 0.7em;
-    font-weight: bold;
-}
-
-.box {
-    background-color: #edf2ff;
-    border-radius: 0;
-    border-color: silver;
-}
-
-.box__tools {
-    margin: 0;
-    padding: 0;
-}
-
-.box__tools a {
-    text-decoration: none;
-    color: inherit;
-    padding: 0.2em;
-    border-radius: 0.4em;
-}
-
-.box__tools a:hover {
-    background-color: var(--select-bg-color);
-}
-
-.title {
-    font-weight: bold;
-    text-align: center;
-}
-
-.post_confirm {
-    text-align: center;
-    background-color: lightgreen;
-    padding: 1em;
-    font-size: 1.2em;
-    font-weight: bold;
-    width: 60%;
-    margin: 1.5em auto;
-}
-
-/*** Generic ***/
-
-.button {
-    padding: 0.5em 1em;
-    background: #333;
-    color: #ccc;
-    border: 1px solid #555;
-    border-radius: 0.8em;
-    font-weight: bold;
-}
-
-
-.button:hover {
-    background-color: #bc0000;
-}
-
-.instructions--head {
-    font-size: 1.5em;
-    text-align: center;
-}
-
-.instructions--alt {
-    text-align: center;
-    font-style: italic;
-}
-
-.items_vert--centered {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-}
-
-/*** blocks ***/
-
-.block_separator {
-    font-size: 1.4em;
-    display: flex;
-}
-
-.block_separator__label {
-    display: inline-block;
-    margin: 0 0.2em;
-}
-
-.block_separator__line {
-    height: 1px;
-    background: #ccc;
-    flex: 1;
-    margin-top: 0.7em;
-}
-
-/*** grids ***/
-
-.grid {
-    list-style: none;
-    display: flex;
-    flex-wrap: wrap;
-}
-
-.grid--center {
-    justify-content: center;
-}
-
-.grid__item>a {
-    text-decoration: none;
-    color: inherit;
-}
-
-.grid__item--medium {
-   width: 170px;
-   padding: 0.2em;
-   text-align: center;
-}
-
-.grid__item--selectable {
-    cursor: pointer;
-}
-
-.grid__item--selectable:hover {
-    background-color: var(--select-bg-color);
-}
-
-/*** avatars ***/
-
-.avatar {
-    height: 2rem;
-    width: 2rem;
-    display: inline-flex;
-    flex-direction: column;
-    align-content: center;
-    justify-content: center;
-    background: #ccc;
-    border-radius: 0.2rem;
-}
-
-.avatar__content {
-
-}
-
-.avatar--medium {
-    height: var(--size-medium);
-    width: var(--size-medium);
-    border-radius: 0.5rem;
-}
-
-.avatar--generated {
-    text-align: center;
-    background:  #43d2f6;
-}
-
-
-/*** icons ***/
-
-.icon--medium {
-    height: var(--size-medium);
-    display: block;
-    margin: 0 auto;
-}
-
-.icon--small {
-    height: 1em;
-}
-
-.icon--soft {
-    fill: #777;
-}
-
-.icon__name {
-
-}
-
-/*** Messages ***/
-
-.message--info {
-    max-width: 500px;
-    margin: 0 auto;
-    padding: 1em;
-    text-align: justify;
-}
-
-.message--info pre {
-    background: #ddd;
-    padding: 1em;
-}
-
-/*** Menus ***/
-
-.menu ul {
-    display: flex;
-    margin-top: 8px;
-    list-style: none;
-}
-
-.menu a {
-    display: block;
-    color: inherit;
-    text-decoration: none;
-    font-variant: small-caps;
-}
-
-.main_menu {
-    min-width: 200px;
-    /* background-color: #eaeaea; */
-    background-color: #333;
-    color: white;
-}
-
-.main_menu ul {
-    flex-direction: row;
-    flex-wrap: wrap;
-}
-
-.main_menu li {
-    flex: 1;
-    padding: 0;
-    margin: 0 0.5em;
-}
-
-.main_menu a {
-    display: inline;
-    white-space: nowrap;
-}
-
-.main_menu a:hover {
-    background-color: initial;
-    text-shadow: 1px 1px 2px;
-    font-weight: bold;
-}
-
-.category_menu ul {
-    justify-content: center;
-}
-
-.category_menu li {
-    margin: 0.5em;
-    text-align: center;
-}
-
-.category_menu a {
-    border: solid 1px;
-    padding: 0.5em;
-    border-radius: 0.2em;
-    background: #eee;
-}
-
-/*** containers ***/
-
-/* tabs */
-
-.tab_container {
-    max-width: 1000px;
-    margin: 0 auto;
-}
-
-.tab_header {
-    background-color: white;
-    border-bottom: 1px solid lightgrey;
-}
-
-.tab_header ul {
-    display: flex;
-    margin: 0;
-    padding: 0;
-    list-style: none;
-    background-color: white;
-}
-
-.tab_page {
-    box-sizing: border-box;
-    padding-top: 2em;
-    border: 1px solid lightgrey;
-    border-top: none;
-    display: None;
-}
-
-.tab_page.clicked {
-    display: block;
-}
-
-.tab_button {
-    display: inline;
-    color: grey;
-    background-color: white;
-    border-top: 1px solid lightgrey;
-    border-left: 1px solid lightgrey;
-    border-bottom: 1px solid lightgrey;
-    padding: 0 1em;
-    cursor: pointer;
-    /* we go down by 1px to remove bottom border from .tab_header */
-    margin-bottom: -1px;
-}
-
-.tab_button.clicked {
-    /* background: lightgrey; */
-    color: inherit;
-    border-bottom: none;
-}
-
-li.tab_button:last-child {
-    border-right: 1px solid lightgrey;
-}
-
-.tab_button input {
-    display: None;
-}
-
-.tab_button label {
-    margin: 1em;
-}
-
-.tab_button input:checked + label {
-    font-weight: bold;
-}
-
-/*** Forms ***/
-
-/* a form with only one field */
-
-.form_single {
-    text-align: center;
-}
-
-.form_single input:not([type="submit"]) {
-    margin: 0 1em;
-    width: 15em;
-    border-radius: 0.7em;
-    outline: none;
-    border: 1px solid black;
-    padding: 0.4em;
-    box-shadow: none;
-}
-
-
-.form_submit {
-    margin: 1em auto 0;
-    display: block;
-}
-
-.form_jid {
-    text-align: center;
-}
-
-/* Textboxes */
-
-form.textbox>* {
-    display: block;
-    margin: 1em auto;
-    text-align: center;
-    max-width: 100%;
-    box-sizing: border-box;
-}
-
-form.textbox>textarea {
-    text-align: left;
-}
-
-.log_request {
-    text-align: center;
-}
-
-/*** Navigation ***/
-
-.prev_next_links ul {
-    list-style: none;
-    display: flex;
-    padding: 0 2em;
-    margin: 0;
-}
-
-.prev_next_links li {
-    flex: 1;
-}
-
-.prev_next_links li.older_items {
-    text-align: right;
-}
-
-.prev_next_links img {
-    display: block;
-}
-
-.prev_next_links .older_items img {
-    display: block;
-    margin-left: auto;
-    margin-right: 0;
-}
-
-.prev_next_links a {
-    display: inline-block;
-    margin-top: 1em;
-    padding: 0.2em;
-    text-decoration: None;
-    color: inherit;
-    font-variant: small-caps;
-    background: rgba(200,200,200,0.6);
-    border-radius: 0.5em;
-}
-
-.prev_next_links a:hover {
-    background-color: #ddd;
-}
-
-/*** XMLUI ***/
-
-.xmlui_cont_vertical>* {
-    display: block;
-    box-sizing: border-box;
-}
-
-.xmlui_cont_vertical>.xmlui_widget {
-    width: 100%;
-    min-height: 1em;
-    min-width: 1px;
-}
-
-label.xmlui_label {
-    font-weight: bold;
-}
-
-td a {
-  /* we use <a> for non JS links in table
-   * so we don't want specific color/text-decoration by default
-   */
-  color: inherit;
-  text-decoration: inherit;
-}
-
-
-/*** Notifications ***/
-
-.notification.retry {
-    position: fixed;
-    top: 1rem;
-    margin: auto;
-    width: 80%;
-    background: #DB1616;
-    border: 3px solid silver;
-    left: 10%;
-    text-align: center;
-}
-
-#retry_counter {
-    font-weight: bold;
-}
-
-#retry_now {
-    color: blue;
-    text-decoration: underline;
-    cursor: pointer;
-}
-
-@media (min-width: 800px) {
-    html {
-        background-size: auto;
-    }
-
-    body {
-        flex-direction: row;
-    }
-
-    #main_area {
-        overflow: auto;
-    }
-
-    .box {
-        border-radius: 1em;
-        box-shadow: 10px 10px 16px -5px rgba(0,0,0,0.5);
-    }
-
-    .main_menu a {
-        display: block;
-        padding: 2em 0;
-    }
-
-    .main_menu ul {
-        flex-direction: column;
-        padding-left: 2em;
-    }
-
-    .prev_next_links ul {
-        padding: 0 6em;
-    }
-
-    .instructions--alt {
-        padding-top: 3rem;
-    }
-
-    /*** forms ***/
-
-    .form_single .form_submit.button {
-        display: inline;
-    }
-}
--- a/default/static/ticket.css	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-@import 'blog.css'; /* needed as blog/articles.html is included */
-
-.instructions {
-    font-style: italic;
-    text-align: center;
-}
-
-.instructions span {
-    padding: 0.3em;
-}
-
-ul.xmlui_list {
-    list-style: none;
-    display: flex;
-    flex-direction: column;
-    padding: 0;
-}
-
-.xmlui_list li {
-    display: inline-flex;
-    background: #eee;
-    padding: 0.5em;
-    margin: 0.5em 0;
-    border: 1px solid silver;
-}
-
-.xmlui_list li:hover {
-    background: yellow;
-}
-
-.xmlui_list a {
-    display: flex;
-    width: 100%;
-    text-decoration: none;
-    color: inherit;
-}
-
-.xmlui_list a:visited {
-    color: inherit;
-}
-
-.tickets a.status_closed {
-    text-decoration: line-through;
-    color: grey;
-}
-
-.xmlui_field__id {
-    font-style: italic;
-    padding-right: 1em;
-}
-
-.xmlui_field__title {
-    padding-right: 1em;
-}
-
-.tickets a.severity_major .xmlui_field__title {
-    font-weight: bold;
-    color: red;
-}
-
-.tickets tbody tr.severity_major .td_title a::before {
-    content: '⚠ ';
-    color: red;
-}
-
-/* single ticket */
-
-.ticket {
-    padding: 20px;
-    max-width: 500px;
-    margin: 0 auto;
-}
-
-.view .xmlui_widget {
-    width: auto;
-}
-
-.view div.xmlui_cont div.xmlui_cont {
-    display: grid;
-    grid-template-columns: min-content 1fr;
-}
-
-.view #label_wid_publisher,
-.view #wid_publisher,
-.view #label_wid_title,
-.view #label_wid_body,
-.view #label_wid_id,
-.view #label_wid_comments_uri,
-.view #wid_comments_uri {
-    display: none
-}
-
-.view .xmlui_label {
-    padding-right: 2em;
-}
-
-.view #wid_id {
-    margin: 0;
-    font-style: italic;
-    grid-column-start: 1;
-    grid-column-end: 3;
-    text-align: right;
-}
-
-.view .xmlui_label {
-    font-weight: bold;
-    float: left;
-    color: #808080cc;
-}
-
-.view #wid_title {
-    font-weight: bold;
-    display: block;
-    text-align: center;
-    grid-column-start: 1;
-    grid-column-end: 3;
-}
-.view #wid_title::first-letter {
-    text-transform: uppercase;
-}
-
-.view #wid_labels span, .xmlui_field__labels span {
-    font-size: 0.8em;
-    background: #eae3e3;
-    font-variant: small-caps;
-    border: 1px solid black;
-    border-radius: 0.5em;
-    padding: 0 2px;
-    white-space: nowrap;
-}
-
-.view #wid_labels span.value_work_in_progress {
-    background: yellow;
-}
-
-.view #wid_type {
-    font-weight: bold;
-}
-
-.view #wid_type span.value_bug::after {
-    content: ' 🐛';
-    color: red;
-}
-
-.view #wid_severity span.value_major {
-    font-weight: bold;
-    color: red;
-}
-
-.view #wid_severity span.value_major::after {
-    content: '⚠ ';
-}
-
-.view #wid_body {
-    white-space: pre-wrap;
-    max-height: 500px;
-    overflow: auto;
-    resize: both;
-    background-color: white;
-    padding: 5px;
-    text-align: justify;
-    border: 1px solid black;
-    border-radius: 5px;
-    grid-column-start: 1;
-    grid-column-end: 3;
-    display: block;
-}
-
-.comment_post {
-    margin-top: 3em;
-}
-
-@media (min-width: 800px) {
-    ul.xmlui_list {
-        padding: 0 2em;
-    }
-    .xmlui_list li {
-        border-radius: 0.3em;
-    }
-}
--- a/default/static/websocket.js	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/*
-SàT templates: suit of templates for Salut à Toi
-Copyright (C) 2017 Jérôme Poisson (goffi@goffi.org)
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/* websocket handler */
-
-
-//TODO: retry websocket instead of reload
-function WSHandler(url, token, debug=false) {
-    var socket = new WebSocket(url, 'libervia_page_' + token );
-    var retried = 0;
-
-    var errorHandler = function(error) {
-        if (retried > 20) {
-            console.error("Too many tries, can't start websocket");
-            alert("Dynamic connection with server can't be established, please try to reload this page in a while or contact your service administrator");
-            return;
-        }
-        var delay = Math.floor((Math.random() * 10) + 1) + 30 * Math.min(retried, 6);
-        notifyRetry(delay, function() {
-            retried++;
-            socket = new WebSocket(url, 'libervia_page_' + token );
-            socket.addEventListener('error', errorHandler);
-        });
-    };
-
-    socket.addEventListener('error', errorHandler);
-
-    if (debug) {
-        socket.addEventListener('message', function(event) {
-            console.log('WS in <== ', JSON.parse(event.data));
-        });
-    }
-
-    socket.addEventListener('message', function(event) {
-        try {data = JSON.parse(event.data);}
-        catch (e) {
-            console.warn('invalid websocket message received: %s', e);
-            return;
-        }
-        switch (data.type) {
-            case 'reload':
-                location.reload(true);
-                break;
-            case 'dom':
-                selected_element = document.body.querySelector(data.selectors);
-                switch (data.update_type) {
-                    case 'append':
-                        var template = document.createElement('template');
-                        template.innerHTML = data.html.trim();
-                        new_element = template.content.firstChild;
-                        selected_element.appendChild(new_element);
-                        break;
-                    default:
-                        console.warn('Unknown DOM update type: %s', data.update_type);
-                }
-                break;
-            default:
-                console.warn('Unknown data type: %s', data.type);
-        }
-    });
-
-    socket.addEventListener('open', function (event) {
-        console.log('Websocket opened');
-        retried = 0;
-    }.bind(this));
-
-    this.send = function(data) {
-        if (debug) {
-            console.log('WS out ==> ', data);
-        }
-        socket.send(JSON.stringify(data));
-    };
-
-    function notifyRetry(timeout, retryCb) {
-        /* Show a notification dialog informing the user that server can't be reach
-         * and call retryCb after timeout seconds.
-         * A "retry now" link allows to retry immediately"
-         *
-         * @param timeout(int): delay before retrying, in seconds
-         * @param retryCb(function): function to call when retrying
-         */
-        var startTime = Date.now() / 1000;
-        var retryIntervalID;
-        var notif = document.createElement("div");
-        notif.setAttribute('class', 'notification retry');
-        //FIXME: we use English without translation for now, must be changed when we can use gettext in Libervia pages
-        notif.innerHTML = "<p>Can't reach the server, retrying in <span id='retry_counter'></span> seconds</p><p><a id='retry_now'>retry now</a></p>";
-        document.body.appendChild(notif);
-        var retryCounter = document.getElementById('retry_counter');
-        retryCounter.textContent = timeout;
-
-        var retry = function () {
-            clearInterval(retryIntervalID);
-            notif.parentNode.removeChild(notif);
-            retryCb();
-        };
-
-        var updateTimer = function () {
-            var elapsed = Math.floor(Date.now() / 1000 - startTime);
-            var remaining = timeout - elapsed;
-            if (remaining < 0) {
-                retry();
-            }
-            else {
-                retryCounter.textContent = remaining;
-            }
-        };
-
-        var retryNow = document.getElementById('retry_now');
-        retryNow.addEventListener('click', function(){
-            retry();
-        });
-
-        retryIntervalID = setInterval(updateTimer, 1000);
-    }
-}
--- a/default/ticket/create.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-{# create a new ticket #}
-
-{% set category_menu = [('tickets_list', url_tickets_list)] %}
-{% extends 'base/base.html' %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-{% import 'input/xmlui.html' as xmlui with context %}
-
-{% block body %}
-<div class='instructions'>
-    <p><span class="box">{% trans app_name=C.APP_NAME%}This page allows you to report an issue or ask/suggest a new feature for {{app_name}}{% endtrans %}</span></p>
-</div>
-<div class="create single ticket box">
-{% call form.form() %}
-    {{ xmlui.generate(new_ticket_xmlui,
-                      attributes = {'title': {'required': 'required',
-                                              'placeholder': _("Short description of your issue/request")},
-                                    'body': {'required': 'required',
-                                             'placeholder': _("Please describe your issue/request with as much details as possible")},
-                                    'labels': {'placeholder': _("You can enter one or several labels separated by commas")},
-                                    })}}
-    {{ field.submit(_("Create ticket")) }}
-{% endcall %}
-</div>
-{% endblock body %}
--- a/default/ticket/discover.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-{% extends 'base/base.html' %}
-{% import 'components/block.html' as block with context %}
-{% import 'components/images.html' as images with context %}
-{% import 'components/avatar.html' as avatar with context %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-
-{% block body %}
-{{ icon_defs('clipboard') }}
-<p class="instructions--head">
-    {% trans %}
-    Please select a tickets tracker
-    {% endtrans %}
-</p>
-{% if tickets_trackers is defined %}
-    <div class="disco_tickets">
-        {{block.disco_icon_grid(tickets_trackers, 'clipboard')}}
-    </div>
-{% endif %}
-
-{% call form.form(class="form_single") %}
-    {{ field.text("jid", _("tickets tracker jid"), required=true)}}
-    {{ field.submit(_("Access")) }}
-{% endcall %}
-{% endblock body %}
--- a/default/ticket/edit.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-{# edit an existing ticket #}
-
-{% set category_menu = [('tickets_list', url_tickets_list)] %}
-{% extends 'base/base.html' %}
-{% import 'input/form.html' as form with context %}
-{% import 'input/field.html' as field with context %}
-{% import 'input/xmlui.html' as xmlui with context %}
-
-{% block body %}
-<div class="create single ticket box">
-{% call form.form() %}
-    {{ xmlui.generate(new_ticket_xmlui,
-                      attributes = {'title': {'required': 'required',
-                                              'placeholder': _("Short description of your issue/request")},
-                                    'body': {'required': 'required',
-                                             'placeholder': _("Please describe your issue/request with as much details as possible")},
-                                    'labels': {'placeholder': _("You can enter one or several labels separated by commas")},
-                                    })}}
-    {{ field.submit(_("Modify ticket")) }}
-{% endcall %}
-</div>
-{% endblock body %}
--- a/default/ticket/item.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-{# display a single ticket
-
-    @variable item(xmlui_item): ticket to display
-    @variable comments(data_object.BlogItems): comments of the ticket
-    @variable comments_service(unicode): service for adding comments
-    @variable comments_node(unicode): node for adding comments
-#}
-
-{% set category_menu = [('tickets', url_tickets_list),
-                        ('ticket_new', url_tickets_new),
-                        ] %}
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% import 'input/xmlui.html' as xmlui with context %}
-{% import 'blog/macros.html' as blog with context %}
-{% import 'input/textbox.html' as textbox with context %}
-
-{% block title %}{{item|adv_format('[{value.widget_value.id}] {value.widget_value.title}') }}{% endblock %}
-
-{% block confirm_message %}
-    {% trans %}Your comment has been sent{% endtrans %}
-{% endblock confirm_message %}
-
-{% block body %}
-{{ icon_defs('pencil') }}
-<div id="{{ item.widget_value['id'] }}" class="view single ticket box">
-    {% if url_ticket_edit is defined %}
-        <p class="box__tools">
-            <a href="{{url_ticket_edit}}">
-            {{ icon('pencil', cls='icon--small') }}
-            edit
-            </a>
-        </p>
-    {% endif %}
-    {{ xmlui.generate(item,
-        form=false,
-        filters={'created': {'filters': ['date_fmt'], 'filters_args':[{'fmt': 'short'}]},
-                 'updated': {'filters': ['date_fmt'], 'filters_args':[{'fmt': 'short'}]},
-                 'body': {'filters': ['urlize'], 'filters_args':[{'nofollow': True, 'rel': 'noopener noreferrer'}]}},
-        )}}
-</div>
-{% if comments is defined %}
-<div id="blog_items">
-    {{ blog.show_items(comments|reverse, expanded=true) }}
-</div>
-{% endif %}
-{% if comments_node is defined %}
-    <div class="comment_post">
-        {{- textbox.comment_or_login(service=comments_service, node=comments_node) -}}
-    </div>
-{% endif %}
-{% endblock body %}
--- a/default/ticket/overview.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-{# display the list of tickets #}
-
-{% set category_menu = [('ticket_new', url_tickets_new)] %}
-{% extends 'base/base.html' %}
-{% import 'input/xmlui.html' as xmlui with context %}
-
-{% block body %}
-<div id="tickets" class="view tickets overview">
-    {{ xmlui.generate_list(tickets, (('id', _('Id')),
-                                     ('title', _('Title')),
-                                     ('labels', _('Labels'))),
-                          {'id': '[{value}]'},
-                           item_class_fields=['status', 'priority', 'severity'],
-                           on_click=on_ticket_click) }}
-</div>
-{% endblock body %}
--- a/default/ticket/tickets.html	Sat Dec 30 14:13:09 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-{% extends 'base/base.html' %}
-{% import 'input/xmlui.html' as xmlui with context %}
-
-{% block body %}
-<div id="tickets">
-    {% for ticket in tickets %}
-        <div class="ticket_full">
-            {{ xmlui.generate(ticket) }}
-        </div>
-    {% endfor %}
-</div>
-{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/VERSION	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,1 @@
+0.7.0D
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/app/app.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,23 @@
+{% extends 'base/base.html' %}
+{% block body %}
+<article id="web_app_box" class="box">
+    <p>
+        {% trans app_name=C.APP_NAME %}
+            The following link allows you to access {{app_name}} web application, which is a more feature rich way to use this software (but it is also a bit more heavy and complex to use).
+        {% endtrans %}
+    </p>
+    <p class="warning_text">
+        {% trans %}
+            Please note that current version of web application use legacy technologies and will be rewritten for next version of Salut à Toi (0.8). It does contain some bugs and it is under minimal maintenance.
+        {% endtrans %}
+    </p>
+    <p id="link_section">
+        <a href="/libervia.html">{% trans %}Click here to access the web application{% endtrans %}</a>.
+    </p>
+    <p>
+        {% trans %}
+            Next time you can go directly to the above URL.
+        {% endtrans %}
+    </p>
+</article>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/base/base.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,84 @@
+{% set embedded = True %} {# embedded is set to avoid including base.html several times if a generic page is included (e.g. blog/articles.html) #}
+{% import 'components/common.html' as component with context %}
+{{ script.include('common', '') }} {# common.js is a common script, so it's useful to import it here #}
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    {% if norobots %}
+        <meta name="robots" content="noindex, nofollow">
+    {% endif %}
+
+    <title>{% block title %}{{C.APP_NAME}}{% endblock %}</title>
+
+    {% if css_content is defined %}
+        <style type="text/css">
+            {{css_content}}
+        </style>
+    {% else %}
+        {% for css_file in css_files %}
+            <link rel='stylesheet' type="text/css" href='{{root_path}}{{css_file}}'>
+        {% endfor %}
+    {% endif %}
+
+    {% if xmpp_uri is defined %}
+        <link rel="alternate" type="application/atom+xml" href="{{xmpp_uri}}" >
+    {% endif %}
+
+    {% if dynamic_style is defined %}
+    {# be extra careful about dynamic style, insure escaping if you use untrusted values ! #}
+        <style type="text/css">
+        {{dynamic_style}}
+        </style>
+    {% endif %}
+
+    {# JS handling #}
+    {% if websocket is defined %}
+        {{ script.include('websocket', '') }}
+    {% endif %}
+    {{ script.generate_scripts() }}
+    {% if websocket is defined %}
+        <script>var socket=new WSHandler("{{websocket.url}}", "{{websocket.token}}", {{websocket.debug}});</script>
+    {% endif %}
+
+    {% block favicon %}
+        <link rel="icon" href="{{media_path}}icons/apps/64/sat.png">
+    {% endblock favicon %}
+</head>
+<body>
+    {% if main_menu is defined %}
+        {% block main_menu %}
+            {{ component.menu(main_menu, class="main_menu") }}
+        {% endblock main_menu %}
+    {% endif %}
+
+    <main id='main_area'>
+        <header>
+            {% if confirm %}
+            {# confirmation message used when post data has been handled correctly #}
+                {% block confirm %}
+                    <div class="box post_confirm">
+                        {% block confirm_message %}
+                            {% trans %}Your data has been sent correctly.{% endtrans %}
+                        {% endblock confirm_message %}
+                    </div>
+                {% endblock confirm %}
+            {% endif %}
+
+        </header>
+
+        <div id="body">
+        {% block category_menu scoped %}
+            {% if category_menu is defined %}
+                {{ component.menu(category_menu, class="category_menu") }}
+            {% endif %}
+        {% endblock category_menu %}
+        {% block body %}
+        {% endblock body %}
+        </div>
+        <footer>{% block footer %}<span>{% trans app_name=C.APP_NAME %}Powered by {{app_name}}{% endtrans %}</span>{% endblock %}</footer>
+    </main>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/blog/articles.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,28 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% set single = items|length == 1 %}
+{% set dates_format='relative' if single else 'short' %}
+{% import 'blog/macros.html' as blog with context %}
+{% import 'input/navigation.html' as navigation with context %}
+
+{% block title scoped -%}
+    {%- if single -%}
+        {{- items[0].title|default(items[0].content, true)|truncate(60, True, '…') + ' - ' + C.APP_NAME -}}
+    {%- else -%}
+        {{C.APP_NAME}}
+        {# {{- super() -}}
+           FIXME: super() is failing if blog is embedded (i.e. base/base.html is not its direct parent)
+                  not sure what's the best way to avoid that, so just using C.APP_NAME for now #}
+    {%- endif -%}
+{%- endblock title -%}
+
+{% block body %}
+{% if items %}
+    <div id="blog_items">
+        {{ blog.show_items(items, expanded=single) }}
+    </div>
+{% else %}
+    <p class="message--info">{% trans %}No articles found in this blog!{% endtrans %}</p>
+{% endif %}
+
+{{ navigation.prev_next(_("newer articles"), _("older articles")) }}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/blog/atom.xml	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns='http://www.w3.org/2005/Atom'>
+    {% if title is defined %}
+        <title>{{title}}</title>
+    {% elif target_profile is defined %}
+        <title>{% trans name=target_profile%}{{name}}'s blog{% endtrans %}</title>
+    {% else %}
+        <title>{% trans app_name=C.APP_NAME%}{{app_name}} blog{% endtrans %}</title>
+    {% endif %}
+    <link href='{{request_uri}}' type='application/atom+xml' rel='self'/>
+    <link href='{{http_uri}}' type='text/html' rel='alternate'/>
+    <link href='{{xmpp_uri}}' type='application/atom+xml' rel='alternate'/>
+    <id>{{xmpp_uri}}</id>
+    <updated>{{updated|date_fmt('iso')}}</updated>
+    {% for item in items %}
+        <entry>
+            {% if item.title_xhtml %}
+                <title type='xhtml'>{{item.title_xhtml}}</title>
+            {% else %}
+                <title>{{item.title|default(item.content|truncate(80, True, '…'), True)}}</title>
+            {% endif %}
+            <link href='{{items_http_uri[item.id]}}' type='text/html' rel='alternate'/>
+            <link href='{{item.uri}}' type='application/atom+xml' rel='alternate'/>
+            <id>{{item.uri}}</id>
+            <updated>{{item.updated|date_fmt('iso')}}</updated>
+            <published>{{item.published|date_fmt('iso')}}</published>
+            <author>
+                <name>{{item.author}}</name>
+                <uri>xmpp:{{item.author_jid}}</uri>
+            </author>
+            {% for tag in item.tags %}
+                <category term="{{tag}}"/>
+            {% endfor %}
+            {% if item.content_xhtml %}
+                <content type='xhtml'>
+                    <div xmlns='http://www.w3.org/1999/xhtml'>
+                    {{item.content_xhtml}}
+                    </div>
+                </content>
+            {% else %}
+                <content type='text'>
+                    {{item.content_txt}}
+                </content>
+            {% endif %}
+        </entry>
+    {% endfor %}
+</feed>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/blog/discover.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,35 @@
+{% extends 'base/base.html' %}
+{% import 'components/block.html' as block %}
+{% import 'components/images.html' as images with context %}
+{% import 'components/avatar.html' as avatar with context %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{% block body %}
+{{ icon_defs('blog') }}
+<p class="instructions--head">
+    {% trans %}
+    Please select the blog you want to consult
+    {% endtrans %}
+</p>
+<div class="disco_blogs">
+    <ul class="grid grid--center">
+        {% for entity in disco_entities %}
+            <li class='grid__item grid__item--medium grid__item--selectable'>
+                <a href="{{entities_url[entity]}}" class="items_vert--centered">
+                    {{ avatar.avatar(entity, class="avatar--medium") }}
+                    <span>{{ identities[entity].nick|default(entity, true) }}</span>
+                </a>
+            </li>
+        {% endfor %}
+    </ul>
+</div>
+
+{% if disco_entities %}
+    <p class="instructions--alt">{% trans %}Or enter the jid of a blog writer{% endtrans %}</p>
+{% endif %}
+{% call form.form(class="form_single") %}
+    {{ field.text("jid", _("blog writer jid"), required=true)}}
+    {{ field.submit(_("Consult")) }}
+{% endcall %}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/blog/item.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,98 @@
+{# display a blog item which can be expanded/retracted by clicking on it
+
+    if the locale differs from item language, it will be totally reduced, and will need a click to be displayed
+
+    @variable item(data_object.BlogItem): item to display
+    @variable identities(data_object.Identities): identities which can be used to display info on item author
+    @variable dates_format(unicode): format of the date to use (see date_fmt filter)
+#}
+
+{% block item %}
+
+{% if item.language and locale and locale.language != item.language %}
+    {# we may display items in different language in a specific way #}
+    {% set other_lang = " other_lang" %}
+{% endif %}
+
+{% if expanded %}
+    {# FIXME: the style attribute is not nice, but due to the use of clicked_mh_fix. A cleaner way would be welcomed #}
+    <article id="{{item.id}}" class="init box{{other_lang}} clicked" style="max-height: none">
+{% else %}
+    <article id="{{item.id}}" class="init box{{other_lang}}" >
+{% endif %}
+
+    {# following message is displayed if item lang is different from page locale #}
+    {% if other_lang is defined %}
+        <div class="info"><p>{% trans language=locale.language_name %}This message is not in {{language}}, click to display anyway{% endtrans %}</p></div>
+    {% endif %}
+
+    {# we put a reduce button at the top #}
+    <div class="expand_box box_top" onclick="clicked_mh_fix('{{item.id}}')">
+        <p>
+            <span class='hide'>{% trans %}Click to reduce…{% endtrans %}</span>
+        </p>
+    </div>
+
+    <header>
+        {% block header %}
+        {# title and publication date link to a HTTP page if items_http_uri is set #}
+        {% set item_http_uri = items_http_uri.get(item.id) if items_http_uri is defined else none %}
+
+        <div class="title">
+            {% block blog_title scoped %}
+                {% set title = item.title_xhtml or item.title or '' %}
+                {% if item_http_uri %}
+                    <a href="{{item_http_uri}}">{{title}}</a>
+                {% else %}
+                    {{title}}
+                {% endif %}
+            {% endblock %}
+        </div>
+            {% block metadata scoped %}
+            <div class="metadata">
+            {% if identities is defined %}
+                <span class="author">{{identities[item.author_jid].nick | default(item.author)}}</span>
+            {% else %}
+                <span class="author">{{item.author}}</span>
+            {% endif %}
+            {% set published = item.published|date_fmt(fmt=dates_format) %}
+            {% if item_http_uri %}
+                <span class="blog_data"><a href="{{item_http_uri}}">{{published}}</a></span>
+            {% else %}
+                {{- published -}}
+            {% endif %}
+            </div>
+            {% if item.tags %}
+                <div class="labels">
+                    {% if tags_http_uri is defined %}
+                        {% for tag in item.tags %}
+                            <a href="{{tags_http_uri[tag]}}"><span>{{tag}}</span></a>
+                        {% endfor %}
+                    {% else %}
+                        {% for tag in item.tags %}
+                            <span>{{tag}}</span>
+                        {% endfor %}
+                    {% endif %}
+                </div>
+            {% endif %}
+            {% endblock metadata %}
+        {% endblock header %}
+    </header>
+
+    <div class="content{{' text' if not item.content_xhtml}}">
+        {% block content %}
+        {{- item.content_xhtml or item.content|urlize or '' -}}
+        {% endblock content %}
+    </div>
+
+    {# and the bottom button to expand/reduce the article #}
+    <div class="expand_box box_bottom" onclick="clicked_mh_fix('{{item.id}}')">
+        <p>
+            <span class='show'>{% trans %}Click to expand…{% endtrans %}</span>
+            <span class='hide'>{% trans %}Click to reduce…{% endtrans %}</span>
+        </p>
+    </div>
+
+</article>
+
+{% endblock item %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/blog/macros.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,41 @@
+{% import 'input/textbox.html' as textbox with context %}
+
+{% macro show_items(items, comments=False, expanded=false, dates_fmt=none) %}
+    {# show items and comments items if present after each item,
+        then post form if allow_commenting is set
+        @param items(BlogItems): items to show
+        @param comments(bool): True items are comments
+            if False, a div with "main_article" class will be added
+        @param expanded(bool): initial state of items
+    #}
+    {% if dates_format is undefined %}
+        {% set dates_format = dates_fmt or 'short' %}
+    {% endif %}
+    {% for item in items %}
+        {% if not comments %}<div class="main_article">{% endif %}
+            {% include 'blog/item.html' %}
+        {% if not comments %}</div>{% endif %}
+
+        {# we recursively display comments for all comments nodes (usually there's only one) #}
+        {% for comments_items in item.comments_items_list %}
+            <button class="comments_btn" onclick="clicked_mh_fix('{{'comments_panel'|next_gidx}}');clicked_cls(this)">
+                <span class='show'>{% trans %}show comments{% endtrans %}</span>
+                <span class='hide'>{% trans %}hide comments{% endtrans %}</span>
+                ({{comments_items|count}})
+            </button>
+            <div id="{{'comments_panel'|cur_gidx}}" class="comments_panel">
+                {% if allow_commenting %}
+                    <div class="comment_post">
+                        {{- textbox.comment(service=comments_items.service, node=comments_items.node) -}}
+                    </div>
+                {% endif %}
+
+                <div class="comments">
+                    {{show_items(comments_items, comments=True)}}
+                </div>
+            </div>
+
+        {% endfor %}
+
+    {% endfor %}
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/chat/chat.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,22 @@
+{{ script.include('chat') }}
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% block title %}{{ target_jid }} - {{ super() }}{% endblock %}
+{% block body %}
+<div class="chat_widget">
+    <div id="messages">
+    {% if subject is defined %}
+        <div id="subject">
+            {{- subject|urlize(nofollow=true,target='_blank') -}}
+        </div>
+    {% endif %}
+    {% for msg in messages %}
+        {% include 'chat/message.html' %}
+    {% endfor %}
+    </div>
+    <div class="message_box">
+        <textarea id="message_input" name="message" type="text" placeholder="{{_("enter your message")}}"></textarea>
+    </div>
+</div>
+{% endblock body %}
+
+{% block footer %}{% endblock footer %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/chat/message.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,15 @@
+{% import 'components/avatar.html' as avatar with context %}
+
+<p id="{{msg.id}}" class="msg_{{msg.type}} {{'own_msg' if msg.from_ == own_jid.full()}}">
+    {%- if msg.type != C.MESS_TYPE_INFO %}
+        {%- set author = identities[msg.from_].nick | default(msg.from_) -%}
+        {{ avatar.avatar(msg.from_) }}
+        <span class="msg_header">
+            <span class="author">{{author}}</span>
+            <span class="date">{{msg.timestamp|date_fmt('auto_day')}}</span>
+        </span>
+    {% endif -%}
+    <span class="msg_body">
+        {{- msg.html or (msg.text|urlize(nofollow=true, target="_blank")) -}}
+    </span>
+</p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/chat/select.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,17 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{% block body %}
+<p class="instructions--head">{% trans %}Please select the chat room you want to enter{% endtrans %}</p>
+<ul class="rooms_list">
+{% for room in rooms %}
+    <li><a href="{{room.url}}">{{room.name}}</a></li>
+{% endfor %}
+</ul>
+<p class="instructions--alt">{% trans %}Or enter a room address{% endtrans %}</p>
+{% call form.form(class="form_single") %}
+    {{ field.text("jid", _("Room address (JID)"), required=true)}}
+    {{ field.submit(_("Join")) }}
+{% endcall %}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/components/avatar.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,9 @@
+{% macro avatar(jid, class) %}
+    {%- if identities is defined -%}
+        {%- if identities[jid].avatar_basename is defined %}
+            <img class="avatar {{class}}" src="{{cache_path}}{{identities[jid].avatar_basename}}">
+        {% else %}
+            <span class="avatar avatar--generated {{class}}"><span class="avatar__content">{{identities[jid].nick|first|upper}}</span></span>
+        {%- endif -%}
+    {%- endif -%}
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/components/block.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,34 @@
+{% macro separator(label, align='center') %}
+{# display a bloc separator
+    @param label(unicode): label to show
+    @param align(unicode): one of "left", "center", "right"
+#}
+    <div class="block_separator">
+        {% if align in ('center', 'right') %}
+            <div class="block_separator__line"></div>
+        {% endif %}
+        <div class="block_separator__label">
+            {{label}}
+        </div>
+        {% if align in ('center', 'left') %}
+            <div class="block_separator__line"></div>
+        {% endif %}
+    </div>
+{% endmacro %}
+
+{% macro disco_icon_grid(disco_entities, icon_name) %}
+{# display discovered entities in a grid
+    @param disco_entities: entities which mush have a name and url key or attribute
+    @param icon_name: name of a defined icon
+#}
+    <ul class="grid grid--center">
+        {% for disco_entity in disco_entities %}
+            <li class='grid__item grid__item--medium grid__item--selectable'>
+                <a href="{{disco_entity.url}}" class="items_vert--centered">
+                    {{ icon(icon_name, cls='icon--medium') }}
+                    <span>{{ disco_entity.name }}</span>
+                </a>
+            </li>
+        {% endfor %}
+    </ul>
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/components/common.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,25 @@
+{# menu labels, map from menu names to labels #}
+{% set ml = {
+    'login': _('Session') if profile else _('Log in'),
+    'blog': _('Blog'),
+    'forums': _('Forums'),
+    'merge-requests': _('Merge requests'),
+    'merge-request_new': _('Create new merge request'),
+    'tickets': _('Tickets'),
+    'tickets_list': _('List tickets'),
+    'ticket_new': _('Create new ticket'),
+    'chat': _('Chat'),
+    'files': _('Files sharing'),
+    'photos': _('Photos albums'),
+    'app': _('Application'),
+} %}
+
+{% macro menu(menus, class='') %}
+    <nav class="menu {{class}}">
+        <ul>
+        {% for name,url in menus %}
+            <li><a class="menu_item {{name}}" {{ {'href': url}|xmlattr }}>{{ml[name]}}</a></li>
+        {% endfor %}
+        </ul>
+    </nav>
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/components/images.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,15 @@
+{% macro get_icon_client(ident) %}
+    {% if ident.client %}
+        {% if ident.client.pc %}
+            {{ icon('desktop', cls='file__icon') }}
+        {% elif ident.client.phone %}
+            {{ icon('mobile', cls='file__icon') }}
+        {% elif ident.client.web %}
+            {{ icon('globe', cls='file__icon') }}
+        {% elif ident.client.console %}
+            {{ icon('terminal', cls='file__icon') }}
+        {% else %}
+            {{ icon('desktop', cls='file__icon') }}
+        {% endif %}
+    {% endif %}
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/error/401.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,6 @@
+{% extends 'error/base.html' %}
+
+{% block body %}
+<h1>{% trans %}Unauthorized{% endtrans %}</h1>
+<p>{% trans %}Sorry, you are not allowed to access this page.{% endtrans %}</p>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/error/404.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,6 @@
+{% extends 'error/base.html' %}
+
+{% block body %}
+<h1>{% trans %}Not Found{% endtrans %}</h1>
+<p>{% trans %}Sorry, we can't find the resource you are trying to access.{% endtrans %}</p>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/error/base.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,7 @@
+{% extends 'base/base.html' %}
+
+{% block title %}{% trans %}Error {{error_code}}{% endtrans %}{% endblock %}
+{% block body %}
+{% trans %}An error occured while trying to access the resource.{% endtrans %}
+{% endblock body %}
+{% block footer %}{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/event/attendance.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,20 @@
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+<div class="attendance box">
+    <p class="poll_instructions">{% trans %}Please indicate if you plan to attend the event:{% endtrans %}</p>
+    {% call form.form() %}
+        {{ field.meta("type", "attendance") }}
+        {{ field.meta("service", event.invitees_service) }}
+        {{ field.meta("node", event.invitees_node) }}
+        <div class="attending">
+            {{ field.choices("attend", (("yes", _("yes")), ("no", _("no")), ("maybe", _("maybe"))), checked=invitee.attend) }}
+        </div>
+        <div class="guests">
+            {{ field.int("guests", label=_("How many people will come (including you)?"), init=invitee.get("guests", 1)) }}
+        </div>
+        <div class="submit">
+            {{ field.submit() }}
+        </div>
+    {% endcall %}
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/event/invitation.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,27 @@
+{% extends 'base/base.html' %}
+
+{% block body %}
+<div class='invitation_header box'>
+    <h1>{% trans %}Welcome {{name}}{% endtrans %}</h1>
+    <p class='instructions'>{% trans %}You have been invited to participate to an event{% endtrans %}</p>
+    {% if event.image is defined %}
+        <p><img id='event_picture' src="{{event.image}}"></p>
+    {% endif %}
+</div>
+
+{% if days_left is defined %}
+    {% if days_left > 0 %}
+        <div id="event_counter" class="box">{% trans %}{{days_left}} days left{% endtrans %}</div>
+    {% else %}
+        <div id="event_counter" class="box">{% trans %}the event is finished{% endtrans %}</div>
+    {% endif %}
+
+{% endif %}
+
+{% include 'event/attendance.html' %}
+
+{% if items is defined %}
+    {% include 'blog/articles.html' %}
+{% endif %}
+
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/file/discover.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,73 @@
+{% extends 'base/base.html' %}
+{% import 'components/block.html' as block %}
+{% import 'components/images.html' as images with context %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{% block body %}
+{{ icon_defs('server', 'desktop', 'mobile', 'globe', 'terminal') }}
+<p class="instructions--head">
+    {% trans %}
+    Please select the device you want to access
+    {% endtrans %}
+</p>
+<div class="disco_files">
+    {% if disco_service_entities %}
+        <div class="files__services">
+            {{ block.separator(_("services")) }}
+            <ul class="grid grid--center">
+                {% for entity,ident in disco_service_entities.iteritems() %}
+                    <li class='grid__item grid__item--medium grid__item--selectable'>
+                        <a href="{{entities_url[entity]}}">
+                            {{ icon('server', cls='file__icon') }}
+                            <span>
+                                {% if disco_service_entities|count == 1 %}
+                                    {% trans %}your server{% endtrans %}
+                                {% else %}
+                                    {{ ident.values()[0].values()[0][0] }}
+                                {% endif %}
+                            </span>
+                        </a>
+                    </li>
+                {% endfor %}
+            </ul>
+        </div>
+    {% endif %}
+    {% if disco_own_entities %}
+        <div class="files__own">
+            {{ block.separator(_("your devices")) }}
+            <ul class="grid grid--center">
+                {% for entity,ident in disco_own_entities.iteritems() %}
+                    <li class='grid__item grid__item--medium grid__item--selectable'>
+                        <a href="{{entities_url[entity]}}">
+                            {{images.get_icon_client(ident)}}
+                            <span>{{ ident.values()[0].values()[0][0] }}</span>
+                        </a>
+                    </li>
+                {% endfor %}
+            </ul>
+        </div>
+    {% endif %}
+    {% if disco_roster_entities %}
+        <div class="files__roster">
+            {{ block.separator(_("your contacts devices")) }}
+            <ul class="grid grid--center">
+                {% for entity,ident in disco_roster_entities.iteritems() %}
+                    <li class='grid__item grid__item--medium grid__item--selectable'>
+                        <a href="{{entities_url[entity]}}">
+                            {{images.get_icon_client(ident)}}
+                            <span>{{entity.userhost()}}</span>
+                        </a>
+                    </li>
+                {% endfor %}
+            </ul>
+        </div>
+    {% endif %}
+</div>
+
+<p class="instructions--alt">{% trans %}Or enter a full jid of a device{% endtrans %}</p>
+{% call form.form(class="form_single") %}
+    {{ field.text("jid", _("device full jid"), required=true)}}
+    {{ field.submit(_("Access")) }}
+{% endcall %}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/file/overview.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,40 @@
+{% extends 'base/base.html' %}
+
+{% block body %}
+{{ icon_defs('level-up', 'doc','folder-open-empty', 'comment-empty') }}
+<ul id="files">
+    {% if parent_url is defined  %}
+        <li class="file">
+            <a href="{{ parent_url }}">
+                {{ icon('level-up', cls='file__icon') }}
+                {% trans %}parent dir{% endtrans %}
+            </a>
+        </li>
+    {% endif %}
+
+    {% for file in files_data %}
+        {% if file.type == C.FILE_TYPE_DIRECTORY %}
+            <li class="file file_{{file.type}}">
+                <a href="{{file.url}}">
+                    {{ icon('folder-open-empty', cls='file__icon') }}
+                    {{ file.name }}
+                </a>
+            </li>
+        {% else %}
+            <li class="file file_{{file.type}}">
+                <a href="{{file.url}}">
+                    {% if file.thumb_url is defined %}
+                        <img src="{{file.thumb_url}}" class="file__thumbnail" alt="{{file.name}}">
+                    {% else %}
+                        {{ icon('doc', cls='file__icon icon--soft') }}
+                    {% endif %}
+                    {{ file.name }}
+                </a>
+            </li>
+        {% endif %}
+    {% endfor %}
+</ul>
+{% if not files_data %}
+    <p class="message--info">{% trans %}No files are shared in this directory!{% endtrans %}</p>
+{% endif%}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/forum/overview.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,34 @@
+{% extends 'base/base.html' %}
+
+{% macro generate_forums(forums_data) %}
+    <ul class="forum">
+    {% for forum in forums_data %}
+        <li>
+            {% if 'http_url' in forum %}
+                <a href="{{forum['http_url']}}" class="forum_actif">
+            {% else %}
+                <a>
+            {% endif %}
+                <span class="forum_title">{{ forum.title }}</span>
+                {% if 'short-desc' in forum %}
+                    <p class="forum_short-desc">{{ forum['short-desc'] }}</p>
+                {% endif %}
+            </a>
+
+            {% if 'sub-forums' in forum %}
+                {{ generate_forums(forum['sub-forums']) }}
+            {% endif %}
+        </li>
+    {% endfor %}
+    </ul>
+{% endmacro %}
+
+{% block body %}
+{% if not forums %}
+    <p class="message--info">{% trans %}No forums found on this server!{% endtrans %}</p>
+{% else %}
+    <div id="forums">
+        {{ generate_forums(forums) }}
+    </div>
+{% endif %}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/forum/view.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,34 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% set dates_format='relative' if single else 'short' %}
+{% import 'blog/macros.html' as blog with context %}
+{% import 'input/textbox.html' as textbox with context %}
+
+{% block body %}
+
+<div id="blog_items">
+    {{ blog.show_items(items, expanded=true) }}
+</div>
+<div class="forum_topic_answer">
+    {{- textbox.comment_or_login(service=service, node=node, placeholder=_("Enter your message here")) -}}
+</div>
+<nav class="prev_next_links">
+    <ul>
+        {% if newer_url is defined %}
+            <li class="newer_items">
+                <a href="{{newer_url}}">
+                    <img src="{{media_path}}icons/tango/actions/32/go-previous.png">
+                    {% trans %}newer messages{% endtrans %}
+                </a>
+            </li>
+        {% endif %}
+        {% if older_url is defined %}
+            <li class="older_items">
+                <a href="{{older_url}}">
+                    <img src="{{media_path}}icons/tango/actions/32/go-next.png">
+                    {% trans %}older messages{% endtrans %}
+                </a>
+            </li>
+        {% endif %}
+    </ul>
+</nav>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/forum/view_topics.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,39 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% import 'input/field.html' as field with context%}
+{% import 'input/textbox.html' as textbox with context %}
+{% import 'input/navigation.html' as navigation with context %}
+{% import 'components/avatar.html' as avatar with context %}
+
+{% block body %}
+
+    {% if not topics %}
+        <div class="message--info">
+            {% trans %}There is not message yet in this forum.{% endtrans %}
+            {% if profile %}
+                {% trans %}You can start a topic of interest by filling this form.{% endtrans %}
+            {% else %}
+                {% trans %}You can login to create a new topic.{% endtrans %}
+            {% endif %}
+        </div>
+    {% endif %}
+    {% if profile %}
+        <div class="forum__topic_create">
+            {% call textbox.textbox(service, node, placeholder=_("Your message"), submit_label=_("Create topic"), type="new_topic") %}
+                {{ field.text("title", placeholder=_("Your topic (try to be short and explicit)"), required=True) }}
+            {% endcall %}
+        </div>
+    {% endif %}
+
+    <div class="forum__topics">
+        {% for topic in topics %}
+            <div>
+                <a href="{{topic.http_uri}}">
+                    {{ avatar.avatar(topic.author) }}
+                    {{topic.title}}
+                </a>
+            </div>
+        {% endfor %}
+    </div>
+
+    {{ navigation.prev_next(_("older topics"), _("newer topics")) }}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/input/field.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,49 @@
+{% macro choices(name, choices_list, checked=none) %}
+    {% for choice, label in choices_list %}
+        <div class="form_input">
+            <input id="{{name|next_gidx}}" type="radio" name="{{name}}" value="{{choice}}"{{" checked" if checked==choice}}><label for="{{name|cur_gidx}}">{{label}}</label>
+        </div>
+    {% endfor %}
+{% endmacro %}
+
+{% macro int(name, label="", init=0) %}
+    <span class="form_input">
+        <label for="{{name|next_gidx}}">{{label}}</label><input id="{{name|cur_gidx}}" type="number" name="{{name}}" value="{{init}}" step="1" min="0">
+    </span>
+{% endmacro %}
+
+{% macro text(name, label="", placeholder="", required=false) %}
+    {# single line text field
+       additional kwargs will be passed as attributes #}
+    <span class="form_input">
+        <label for="{{name|next_gidx}}">{{label}}</label><input id="{{name|cur_gidx}}" type="text" name="{{name}}" placeholder="{{placeholder}}" {{"required" if required}} {{kwargs|xmlattr}}>
+    </span>
+{% endmacro %}
+
+{% macro password(name, label="", required=false) %}
+    {# password field
+       additional kwargs will be passed as attributes #}
+    <span class="form_input">
+        <label for="{{name|next_gidx}}">{{label}}</label><input id="{{name|cur_gidx}}" type="password" name="{{name}}" {{"required" if required}} {{kwargs|xmlattr}}>
+    </span>
+{% endmacro %}
+
+{% macro email(name, label="", required=false) %}
+    {# email field
+       additional kwargs will be passed as attributes #}
+    <span class="form_input">
+        <label for="{{name|next_gidx}}">{{label}}</label><input id="{{name|cur_gidx}}" type="email" name="{{name}}" {{"required" if required}} {{kwargs|xmlattr}}>
+    </span>
+{% endmacro %}
+
+{% macro textarea(name, rows=10, cols=50, placeholder='', required=false) %}
+    <textarea name="{{name}}" rows="{{rows}}" cols="{{cols}}" placeholder="{{placeholder}}" {{"required" if required}}></textarea>
+{% endmacro %}
+
+{% macro meta(name, value) %}
+    <input type="hidden" name="{{name}}" value="{{value}}">
+{% endmacro %}
+
+{% macro submit(text=_("Send")) %}
+    <input class="form_submit button" type="submit" value="{{text}}">
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/input/form.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,8 @@
+{% macro form(action='', class='') %}
+<form method="post" action="{{action}}" {{ {'class': class}|xmlattr }}>
+    {% if csrf_token is defined %}
+        <input type="hidden" name="csrf_token" value="{{csrf_token}}">
+    {% endif %}
+    {{ caller() }}
+</form>
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/input/navigation.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,22 @@
+{% macro prev_next(prev_label=_("newer"), next_label=_("older")) %}
+    <nav class="prev_next_links">
+        <ul>
+            {% if newer_url is defined %}
+                <li class="newer_items">
+                    <a href="{{newer_url}}">
+                        <img src="{{media_path}}icons/tango/actions/32/go-previous.png">
+                        {{prev_label}}
+                    </a>
+                </li>
+            {% endif %}
+            {% if older_url is defined %}
+                <li class="older_items">
+                    <a href="{{older_url}}">
+                        <img src="{{media_path}}icons/tango/actions/32/go-next.png">
+                        {{next_label}}
+                    </a>
+                </li>
+            {% endif %}
+        </ul>
+    </nav>
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/input/textbox.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,52 @@
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field %}
+
+{% macro head(service, node, type="textbox") %}
+    {# include data needed to identify the node to use for commenting #}
+    <input type="hidden" name="type" value="{{type}}">
+    <input type="hidden" name="service" value="{{service}}">
+    <input type="hidden" name="node" value="{{node}}">
+{% endmacro %}
+
+{% macro submit(label=_("Send")) %}
+    <input type="submit" value="{{label}}">
+{% endmacro %}
+
+{% macro textbox(service, node, action='', placeholder='', submit_label=_("Send"), type="textbox") %}
+    {# generic content area for comments/blog posts/etc. Only a body by default, but new elements can be
+       added by using this macro with call #}
+    {% set extra_content = caller() if caller is defined else '' %}
+    {% call form.form(action=action, class="textbox") %}
+        {{ head(service, node, type) }}
+        {{ extra_content }}
+        {{ field.textarea("body", placeholder=placeholder, required=True) }}
+        {{ submit(label=submit_label) }}
+    {% endcall %}
+{% endmacro %}
+
+{% macro comment(service, node, action='', placeholder=_("Your comment")) %}
+    {{ textbox(service, node, action=action, placeholder=placeholder, type="comment") }}
+{% endmacro %}
+
+{% macro comment_or_login(service, node, action='', placeholder=none) %}
+    {# show comment form a a message asking to log in
+       login is checked using profile #}
+    {% if profile %}
+        {% if placeholder is none %}
+            {{ comment(service, node, action) }}
+        {% else %}
+            {{ comment(service, node, action, placeholder=placeholder) }}
+        {% endif %}
+    {% else %}
+        <div class="log_request">
+        <p class="not_logged">{% trans %}You are not logged. You need to log in to comment.{% endtrans %}</p>
+        {% if login_url is defined %}
+            <p class="log_in_url">
+                {% trans link_start=('<a href="',login_url,'">')|join|safe, link_end='</a>'|safe %}
+                    To log in {{link_start}}follow this link{{link_end}}
+                {% endtrans %}
+            </p>
+        {% endif %}
+        </div>
+    {% endif %}
+{% endmacro %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/input/xmlui.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,237 @@
+{% import 'input/field.html' as field %}
+
+{# generate methods #}
+
+{% macro generate_container(cont, config) %}
+    {% if cont.type == 'vertical' %}
+        {{ vertical_container(cont, config) }}
+    {% elif cont.type == 'pairs' %}
+        {{ pairs_container(cont, config) }}
+    {% elif cont.type == 'label' %}
+        {{ label_container(cont, config) }}
+    {% endif %}
+{% endmacro %}
+
+{% macro generate_widget(wid, config, id=none) %}
+    {% if wid.type == 'text' %}
+        {{ text_widget(wid, config, id=id) }}
+    {% elif wid.type == 'label' %}
+        {{ label_widget(wid, config) }}
+    {% elif wid.type == 'string' %}
+        {{ string_widget(wid, config, id=id) }}
+    {% elif wid.type == 'jid' %}
+        {# TODO: proper JID widget #}
+        {{ string_widget(wid, config, id=id) }}
+    {% elif wid.type == 'textbox' %}
+        {{ textbox_widget(wid, config, id=id) }}
+    {% elif wid.type == 'list' %}
+        {{ list_widget(wid, config, id=id) }}
+    {% endif %}
+{% endmacro %}
+
+{% macro generate_children(cont, config) %}
+    {% for child in cont.children %}
+        {% if child.category == 'container' %}
+            {{ generate_container(child, config) }}
+        {% else %}
+            {{ generate_widget(child, config) }}
+        {% endif %}
+    {% endfor %}
+
+{% endmacro %}
+
+{% macro generate(xmlui, form=true, filters=none, attributes=none) %}
+{# generate HTML from XMLUI
+    @param xmlui(template_xmlui.XMLUIPanel): xmlui to use
+    @param form(bool): if true will generate form elements
+    @param filters(dict,none): filters as expected by item_filter
+    @param attributes(dict,none): extra attributes to put on named widgets
+#}
+    {% set config = {'form':form, 'filters':filters or {}, 'attrs': attributes or {}} %}
+    {{ generate_container(xmlui.main_cont, config) }}
+{% endmacro %}
+
+{% macro generate_table(xmlui_items, fields, formatters, tr_class_fields, on_click) %}
+{# generate a HTML table from requested widgets names
+    @param xmlui_items(iterable[unicode]): list of xmlui to show (one per row)
+    @param fields(tuple[unicode,unicode]): fields to show (name, label)
+    @param formatters(dict): dictionary of templates to format values:
+        field_name => template
+        if no formatter is set (or None is used) for a field, it will be used unmodified.
+        current xmlui items will be set as "item" key
+    @param tr_class_fields(iterable[unicode]): name of fields to use as class
+        class will be "{name}_{value}" where name is field name, and value field value
+        all lowercase/stripped
+    @param on_click(data_objects.OnClick): thing to do when clicking on a row
+#}
+    {% if formatters is undefined %}
+        {% set formatters = {} %}
+    {% endif %}
+    {% if on_click is undefined %}
+        {% set on_click = {} %}
+    {% endif %}
+    <table>
+        <thead>
+            <tr>
+                {% for name,label in fields %}
+                    <th>{{ label }}</th>
+                {% endfor %}
+            </tr>
+        </thead>
+        <tbody>
+            {% for xmlui in xmlui_items %}
+                {% set link=on_click.formatUrl(item=xmlui.widget_value) if on_click.url else none %}
+                <tr {{ {'class': xmlui|xmlui_class(tr_class_fields)}|xmlattr }}>
+
+                    {% for name,label in fields %}
+                        <td {{ {'class': 'td_'+name}|xmlattr }}>
+                            {% for value in xmlui.widgets[name].values %}
+                                <a {{ {'href':link}|xmlattr }}>{{ value|adv_format(formatters.get(name),item=xmlui.widget_value) }}</a>
+                            {% endfor %}
+                        </td>
+                    {% endfor %}
+                </tr>
+            {% endfor %}
+        </tbody>
+    </table>
+{% endmacro %}
+
+
+
+
+{% macro generate_list(xmlui_items, fields, formatters, item_class_fields, on_click) %}
+{# generate a list of rendered XMLUI from requested widgets names
+    very similar to generate_table but generate a list instead of a tabme
+    @param xmlui_items(iterable[unicode]): list of xmlui to show
+    @param fields(tuple[unicode,unicode]): fields to show (name, label)
+    @param formatters(dict): dictionary of templates to format values:
+        field_name => template
+        if no formatter is set (or None is used) for a field, it will be used unmodified.
+        current xmlui items will be set as "item" key for the template
+    @param item_class_fields(iterable[unicode]): name of fields to use as class
+        class will be "{name}_{value}" where name is field name, and value field value
+        all lowercase/stripped
+    @param on_click(data_objects.OnClick): thing to do when clicking on a row
+#}
+    {% if formatters is undefined %}
+        {% set formatters = {} %}
+    {% endif %}
+    {% if on_click is undefined %}
+        {% set on_click = {} %}
+    {% endif %}
+    <ul class="xmlui_list">
+        {% for xmlui in xmlui_items %}
+            <li>
+            {% set link=on_click.formatUrl(item=xmlui.widget_value) if on_click.url else none %}
+                <a {{ {'class': xmlui|xmlui_class(item_class_fields),
+                        'href':link}|xmlattr }}>
+                    {% for name,label in fields %}
+                        <span {{ {'class': 'xmlui_field__'+name}|xmlattr }}>
+                            {% for label in xmlui.widgets.get(name, {}).labels %}
+                                <span>{{ label|adv_format(formatters.get(name),item=xmlui.widget_value) }}</span>
+                            {% endfor %}
+                        </span>
+                    {% endfor %}
+                </a>
+            </li>
+        {% endfor %}
+    </ul>
+{% endmacro %}
+
+
+
+
+
+{# containers #}
+
+{% macro vertical_container(cont, config) %}
+    <div class="xmlui_cont xmlui_cont_vertical">
+        {{ generate_children(cont, config) }}
+    </div>
+{% endmacro %}
+
+{% macro pairs_container(cont, config) %}
+    {# TODO: proper impelmentation (do the same as vertical container for now #}
+    <div class="xmlui_cont xmlui_cont_vertical">
+        {{ generate_children(cont, config) }}
+    </div>
+{% endmacro %}
+
+{% macro label_container(cont, config) %}
+    <div class="xmlui_cont xmlui_cont_vertical">
+        {% for child in cont.children %}
+            {% if loop.index is odd %}
+                {# label #}
+                {% if child.type == 'label' %}
+                    {% set for_ = ('wid_' + (child.for_name or child.name or '_noname'))|next_gidx %}
+                    {{ label_widget(child, config, for=for_) }}
+                {% endif %}
+            {% else %}
+                {# widget #}
+                {% set id = ('wid_' + (child.name or '_noname'))|cur_gidx %}
+                {{ generate_widget(child, config, id=id) }}
+            {% endif %}
+        {% endfor %}
+    </div>
+{% endmacro %}
+
+
+{# widgets #}
+
+{% macro text_widget(wid, config, id=none) %}
+    <p class="xmlui_widget xmlui_text" {{ {'id':id}|xmlattr }}>
+        {{- wid|item_filter(config.filters)|default('\u00A0',true) -}}
+    </p>
+{% endmacro%}
+
+{% macro label_widget(wid, config, for=none) %}
+    {% if config.form %}
+        <label class="xmlui_widget xmlui_label" {{ {'for':for}|xmlattr }}>
+            {{wid|item_filter(config.filters)}}
+        </label>
+    {% else %}
+        <span class="xmlui_widget xmlui_label" {{ {'id':none if not for else 'label_%s'|format(for)}|xmlattr }}>{{wid|item_filter(config.filters)}}</span>
+    {% endif %}
+{% endmacro%}
+
+{% macro string_widget(wid, config, id=none) %}
+    {% if config.form %}
+        <input class="xmlui_widget xmlui_string" type="text" {{ {'name':wid.name, 'id':id, 'value':wid|item_filter(config.filters)}|dict_ext(config.attrs, wid.name)|xmlattr }}>
+    {% else %}
+        <div class="xmlui_widget xmlui_string"  {{ {'id':id}|xmlattr }}>
+            {{- wid|item_filter(config.filters)|default('\u00A0',true) -}}
+        </div>
+    {% endif %}
+{% endmacro%}
+
+{% macro textbox_widget(wid, config, id=none) %}
+    {% if config.form %}
+        <textarea class="xmlui_widget xmlui_textbox" rows="10" cols="50" {{ {'name':wid.name, 'id':id}|dict_ext(config.attrs, wid.name)|xmlattr }}>
+            {{- wid|item_filter(config.filters) -}}
+        </textarea>
+    {% else %}
+        <p class="xmlui_widget xmlui_textbox" {{ {'id':id}|xmlattr }}>
+            {{- wid|item_filter(config.filters) -}}
+        </p>
+    {% endif %}
+{% endmacro%}
+
+{% macro list_widget(wid, config, id=none) %}
+    {% if config.form %}
+        <select class="xmlui_widget xmlui_list" {{ {'name':wid.name, 'id':id}|dict_ext(config.attrs, wid.name)|xmlattr }}>
+            {% for value,label in wid.options %}
+                <option {{ {'value':value}|xmlattr }} {{ 'selected' if value in wid.selected }}>
+                    {{- label -}}
+                </option>
+            {% endfor %}
+        </select>
+    {% else %}
+        <div class="xmlui_widget xmlui_list" {{ {'id':id}|xmlattr }}>
+            {% for value,label in wid.items %}
+                <span class="xmlui_list_item value_{{value|attr_escape}}">
+                    {{- label -}}
+                </span>
+            {% endfor %}
+        </div>
+    {% endif %}
+{% endmacro%}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/invitation/welcome.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,13 @@
+{% extends 'base/base.html' %}
+
+{% block body %}
+<div class='invitation_header'>
+    <h1>{% trans %}Welcome {{name}}{% endtrans %}</h1>
+    <p class='instructions'>{% trans %}You have been invited to participate with the community, please choose an action below{% endtrans %}</p>
+</div>
+
+{% if include_url is defined %}
+<iframe id='include_main' src='{{include_url}}'></iframe>
+{% endif %}
+
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/login/logged.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,27 @@
+{% set post_confirm_message=_("You have been logged correctly") %}
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{% block title %}{{C.APP_NAME}}{% endblock %}
+
+{% block confirm_message %}
+    {% trans %}You have been logged correctly{% endtrans %}
+{% endblock confirm_message %}
+
+{% block body %}
+<div id='logged'>
+    <div class='logged_box box'>
+        {% if guest_session %}
+            <p>{% trans %}You are logged as a guest{% endtrans %}</p>
+        {% else %}
+            <p>{% trans name='<span class="logged_profile">'|safe + profile + '</span>'|safe %}You are logged under the account {{name}} {% endtrans %}</p>
+        {% endif %}
+        <p>{% trans session_started='<span class="logged_time">'|safe + session_started|date_fmt('relative') + '</span>'|safe %}You logged {{session_started}}{% endtrans %}</p>
+    </div>
+    {% call form.form() %}
+        {{ field.meta('type', 'disconnect') }}
+        {{ field.submit(_("Disconnect")) }}
+    {% endcall %}
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/login/login.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,59 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{% block title %}{{C.APP_NAME}} login{% endblock %}
+
+{% block body %}
+<div id="login_container">
+<div id="login_box">
+    <div id="login_left">
+        <img src="{{media_path}}libervia/register_left.png">
+    </div>
+    <div id="login_right">
+        {% block login_right_top %}{% endblock %}
+        {% if login_error is defined %}
+            {# error messages displayed in case of failing attempt to login #}
+            <div id="login_error">
+                <p>
+                {% block login_error_message %}
+                {% if login_error == S_C.PROFILE_AUTH_ERROR %}
+                    {%- trans %}Your login and/or password is incorrect. Please try again.{% endtrans -%}
+                {% elif login_error == S_C.XMPP_AUTH_ERROR %}
+                    {%- trans %}Your XMPP account failed to connect. Did you enter the good password? If you have changed your XMPP password since your last connection on Libervia, please use another SàT frontend to update your profile.{% endtrans -%}
+                {% elif login_error == S_C.NO_REPLY %}
+                    {%- trans %}Did not received a reply (the timeout expired or the connection is broken).{% endtrans -%}
+                {% else %}
+                    {%- trans %}An unknown error occurred, please contact your service administrator.{% endtrans -%}
+                {% endif %}
+                {% endblock login_error_message %}
+                </p>
+            </div>
+        {% endif %}
+
+        <div id="login_form">
+            {% block login_form %}
+            {% call form.form() %}
+                {{ field.meta('type', 'login') }}
+                {{ field.text("login", _("Login"),
+                              required=true,
+                              value=login,
+                              )}}
+                {{ field.password("password", _("Password"), required=not empty_password_allowed) }}
+                {{ field.submit(_("Log in")) }}
+            {% endcall %}
+            {% endblock login_form %}
+        </div>
+        {% block login_right_bottom %}
+        {% if register_url is defined %}
+            <div id="create_account_link">
+                <p>
+                    <a href="{{register_url}}">{% trans %}No account yet? Create a new one!{% endtrans %}</a>
+                </p>
+            </div>
+        {% endif %}
+        {% endblock login_right_bottom %}
+    </div>
+</div>
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/login/register.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,49 @@
+{% extends 'login/login.html' %}
+
+{% block title %}{{C.APP_NAME}} register new account{% endblock %}
+
+{% block login_error_message %}
+{% if login_error == S_C.ALREADY_EXISTS %}
+    {%- trans %}This login already exists, please choose another one.{% endtrans -%}
+{% elif login_error == S_C.INVALID_INPUT %}
+    {%- trans %}The data you entered are nod valid.{% endtrans -%}
+{% elif login_error == S_C.BAD_REQUEST %}
+    {%- trans %}Bad request, please contact your service administrator{% endtrans -%}
+{% else %}
+    {%- trans %}An unknown error occurred, please contact your service administrator.{% endtrans -%}
+{% endif %}
+{% endblock login_error_message %}
+
+{% block login_right_top %}
+{% if login_url is defined %}
+    <div id="login_link">
+        <a href="{{login_url}}">
+            {%- trans %}Go to login page{% endtrans -%}
+        </a>
+    </div>
+{% endif %}
+{% endblock login_right_top %}
+
+{% block login_form %}
+{% call form.form(class='register') %}
+    {{ field.meta('type', 'register') }}
+    {{ field.text("login", _("Login"),
+                  required=true,
+                  pattern=S_C.REG_LOGIN_RE,
+                  title=_("Login must be lower case, with only plain letters (a-z), numbers (0-9) or underscore(_)"),
+                  value=login,
+                  )}}
+    {{ field.email("email", _("Email"),
+                   required=true,
+                   value=email,
+                   )}}
+    {{ field.password("password", _("Password"),
+                      required=true,
+                      minlength=S_C.PASSWORD_MIN_LENGTH,
+                      value=password,
+                      )}}
+    {{ field.submit(_("Register new account")) }}
+{% endcall %}
+{% endblock login_form %}
+
+{% block login_right_bottom %}{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/merge-request/create.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,9 @@
+{# creata a new ticket #}
+
+{% set category_menu = [('merge-requests', url_tickets_list)] %}
+{% extends 'base/base.html' %}
+{% block body %}
+<div class="box message--info">
+    <p>{% trans %}It is not yet possible to create a merge request from inside Libervia, please use <pre>jp merge-request set</pre> for now. Merge requests welcome ;){% endtrans %}</p>
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/merge-request/discover.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,25 @@
+{% extends 'base/base.html' %}
+{% import 'components/block.html' as block with context %}
+{% import 'components/images.html' as images with context %}
+{% import 'components/avatar.html' as avatar with context %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{% block body %}
+{{ icon_defs('merge') }}
+<p class="instructions--head">
+    {% trans %}
+    Please select a merge-requests handler
+    {% endtrans %}
+</p>
+{% if mr_handlers is defined %}
+    <div class="disco_tickets">
+        {{block.disco_icon_grid(mr_handlers, 'merge')}}
+    </div>
+{% endif %}
+
+{% call form.form(class="form_single") %}
+    {{ field.text("jid", _("handler jid"), required=true)}}
+    {{ field.submit(_("Access")) }}
+{% endcall %}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/merge-request/edit.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,26 @@
+{# edit an existing ticket #}
+
+{% set category_menu = [('merge-requests', url_tickets_list),
+                        ('merge-request_new', url_tickets_new)] %}
+{% extends 'base/base.html' %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+{% import 'input/xmlui.html' as xmlui with context %}
+
+{% block body %}
+<div class='instructions'>
+    <p><span class="box">{% trans app_name=C.APP_NAME%}Note: to modify content of the merge request, you'll have to use command line (with jp){% endtrans %}</span></p>
+</div>
+<div class="create single ticket box">
+{% call form.form() %}
+    {{ xmlui.generate(new_ticket_xmlui,
+                      attributes = {'title': {'required': 'required',
+                                              'placeholder': _("Short description of your issue/request")},
+                                    'body': {'required': 'required',
+                                             'placeholder': _("Please describe your issue/request with as much details as possible")},
+                                    'labels': {'placeholder': _("You can enter one or several labels separated by commas")},
+                                    })}}
+    {{ field.submit(_("Modify ticket")) }}
+{% endcall %}
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/merge-request/item.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,73 @@
+{# display a single ticket
+
+    @variable item(xmlui_item): ticket to display
+    @variable comments(data_object.BlogItems): comments of the ticket
+    @variable comments_service(unicode): service for adding comments
+    @variable comments_node(unicode): node for adding comments
+#}
+
+{% set category_menu = [('merge-requests', url_tickets_list),
+                        ('merge-request_new', url_tickets_new)] %}
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% import 'input/xmlui.html' as xmlui with context %}
+{% import 'blog/macros.html' as blog with context %}
+{% import 'input/textbox.html' as textbox with context %}
+
+{% block title %}{{item|adv_format('[{value.widget_value.id}] {value.widget_value.title}') }}{% endblock %}
+
+{% block confirm_message %}
+    {% trans %}Your comment has been sent{% endtrans %}
+{% endblock confirm_message %}
+
+{% block body %}
+{{ icon_defs('pencil') }}
+<div class="tab_container">
+    <div class="tab_header">
+        <ul>
+            <li class="tab_button clicked" onclick='toggle_clicked_class_sel(".tab_button,.tab_page")'>{% trans %}Description{% endtrans %}</li>
+            <li class="tab_button" onclick='toggle_clicked_class_sel(".tab_button,.tab_page")'>{% trans %}Patches{% endtrans %}</li>
+        </ul>
+    </div>
+    <div class="tab_page clicked" id="tab_description">
+        <div id="{{ item.widget_value['id'] }}" class="view single ticket box">
+            {% if url_ticket_edit is defined %}
+                <p class="box__tools">
+                    <a href="{{url_ticket_edit}}">
+                    {{ icon('pencil', cls='icon--small') }}
+                    edit
+                    </a>
+                </p>
+            {% endif %}
+            {{ xmlui.generate(item,
+                form=false,
+                filters={'created': {'filters': ['date_fmt'], 'filters_args':[{'fmt': 'short'}]},
+                         'updated': {'filters': ['date_fmt'], 'filters_args':[{'fmt': 'short'}]},
+                         'body': {'filters': ['urlize'], 'filters_args':[{'nofollow': True, 'rel': 'noopener noreferrer'}]}},
+                )}}
+        </div>
+        {% if comments is defined %}
+        <div id="blog_items">
+            {{ blog.show_items(comments|reverse, expanded=true) }}
+        </div>
+        {% endif %}
+        {% if comments_node is defined %}
+            <div class="comment_post">
+                {{- textbox.comment_or_login(service=comments_service, node=comments_node) -}}
+            </div>
+        {% endif %}
+    </div>
+    <div class="tab_page" id="tab_patches">
+        {% for patch in patches %}
+            <div class="patch">
+                <header class="box">
+                    <div class="author"><label>{% trans %}author:{% endtrans %} </label>{{patch.author}}</div>
+                    <p class="commit_msg">{{patch.commit_msg}}</p>
+                </header>
+                <div class="diff">
+                    {{- patch.diff|highlight('diff') -}}
+                </div>
+            </div>
+        {% endfor %}
+    </div>
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/photo/album.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,33 @@
+{% extends 'base/base.html' %}
+{% import 'input/textbox.html' as textbox with context %}
+{% import 'blog/macros.html' as blog with context %}
+
+{% block body %}
+{{ icon_defs('comment-empty') }}
+<div class="album">
+    {% for file in files_data %}
+        {% if file.type == C.FILE_TYPE_FILE %}
+            <div class="album__item">
+                <div class="album__vignette">
+                    <img src="{{file.thumb_url}}" class="album__thumbnail" alt="{{file.name}}">
+                </div>
+                {% if file.comments_url is defined %}
+                    <span class='album__comments-bar' onclick="clicked_mh_fix('{{'comments_panel'|next_gidx}}')">
+                        {% trans %}comments{% endtrans %}
+                        {% if file.comments_count %}
+                            <span class='comments__count'>({{file.comments_count}})</span>
+                        {% endif %}
+                        {{ icon('comment-empty', cls='icon--small') }}
+                    </span>
+                    <div id='{{'comments_panel'|cur_gidx}}' class="panel-drawer">
+                        {{ blog.show_items(file.comments, expanded=true, dates_fmt='relative') }}
+                        <div class="comment_post">
+                            {{- textbox.comment_or_login(service=file.comments_service, node=file.comments_node) -}}
+                        </div>
+                    </div>
+                {% endif %}
+            </div>
+        {% endif %}
+    {% endfor %}
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/photo/discover.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,11 @@
+{% extends 'base/base.html' %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{% block body %}
+<p class="instructions--alt">{% trans %}Photo discovery is not implemented yet, however you can enter a jid below to find its albums{% endtrans %}</p>
+{% call form.form(class="form_single") %}
+    {{ field.text("jid", _("device full jid"), required=true)}}
+    {{ field.submit(_("Access")) }}
+{% endcall %}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/session/disconnect.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,5 @@
+{% extends 'base/base.html' %}
+
+{% block body %}
+<p>{% trans %}You have been disconnected, have a nice day!{% endtrans %}</p>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/app.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,21 @@
+#body {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+#web_app_box {
+    width: 80%;
+    padding: 2em;
+    text-align: justify;
+}
+
+#link_section {
+    font-weight: bold;
+    text-align: center;
+    font-size: 1.5rem;
+}
+
+article p:last-child {
+    text-align: center;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/blog.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,313 @@
+/*** articles ***/
+
+article.box {
+    position: relative;
+    margin: 2% auto;
+    border-style: solid none none;
+    border-width: 1px;
+    padding: 2%;
+    min-height: 9em;
+    max-height: 20em;
+    overflow: hidden;
+    transition: max-height 0.5s;
+}
+
+article video {
+    max-width: 100%;
+    height: auto;
+}
+
+.main_article article.box:not(.clicked) {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+}
+
+/** header **/
+
+header a {
+    color: inherit;
+    text-decoration: inherit;
+}
+
+header .metadata {
+    text-align: right;
+}
+
+article .author {
+    font-weight: bold;
+}
+
+article .author::after {
+    content: ", ";
+}
+
+/*** labels ***/
+
+.labels {
+    text-align: right;
+    white-space: nowrap;
+}
+
+.labels a {
+    text-decoration: none;
+    color: inherit;
+}
+
+.labels span {
+    font-variant: small-caps;
+    font-size: 0.8rem;
+    background-color: #ddd;
+    border-radius: 0.4em;
+    padding: 0 0.5em;
+    transition: all 0.5s;
+}
+
+.labels a>span {
+    cursor: pointer;
+}
+
+.labels a>span:hover {
+    box-shadow: 0px 0px 6px 1px #000;
+}
+
+
+/** content **/
+
+article div.content {
+    text-align: justify;
+    font-size: 0.9em;
+}
+
+article div.content.text {
+    white-space: pre-wrap;
+}
+
+
+article img {
+    max-width: 100%;
+    margin: 0;
+}
+
+/** reduce/expand buttons **/
+
+.expand_box {
+    cursor: pointer;
+}
+
+.box_top {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+}
+
+.box_bottom {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+}
+
+.expand_box.box_bottom::before {
+    /* we do a gradient to show that text can be expanded */
+    background-image: linear-gradient(to bottom,rgba(0,0,0,0),#ffffff);
+    display: block;
+    content: "";
+    width: 100%;
+    height: 4em;
+    border: none;
+}
+
+.expand_box p {
+    background-color: white;
+    margin: 0;
+    text-align: center;
+    font-weight: bold;
+    font-size: 0.8em;
+    border-style: solid none dotted none;
+    border-width: 1px 0 1px;
+    border-bottom-color: gray;
+}
+
+article .expand_box .hide {
+    /* when not clicked, we only display .show */
+    display: none;
+}
+
+article .expand_box.box_top {
+    /* top expand box only show a reduce button
+     * so it should be displayed only when article is .clicked */
+    display: none;
+}
+
+/** other language **/
+
+/* we hide everything just to display a message informing that
+ * the item is in an other language, and asking to click
+ * to display it */
+
+article.other_lang>div.info {
+    display: none;
+}
+
+article.other_lang.init>* {
+    display: none;
+}
+
+article.other_lang.init>div.info {
+    display: initial;
+}
+
+article.other_lang.init>div.expand_box.box_top {
+    /* as we need to have the message clickable, we cheat and use
+     * expand box on the whole surface but fully transparent */
+    display: initial;
+    height: 100%;
+    opacity: 0;
+}
+
+article.other_lang.init>div.info>p {
+    margin: 0;
+    padding: 0.2em;
+    text-align: center;
+    font-style: italic;
+}
+
+article.other_lang.init {
+    border: none;
+    border-radius: 0;
+    padding: 0;
+    min-height: 0;
+}
+
+/*** comments ***/
+
+button.comments_btn {
+    border: none;
+    font-weight: bold;
+    display: block;
+    margin: 0 10% 0 auto;
+    border-radius: 1em;
+    background:  #b8bcc4;
+    color: #4d4d4d;
+}
+
+button.comments_btn:active {
+    background: #4d4d4d;
+    color: #b8bcc4;
+}
+
+button.comments_btn.clicked span.show {
+    display: none;
+}
+
+button.comments_btn:not(.clicked) span.hide {
+    display: none;
+}
+
+.comments_panel {
+    max-height: 0;
+    opacity: 0;
+    transition: max-height 1s, opacity 2s;
+    overflow: hidden;
+}
+
+.comments_panel.clicked {
+    opacity: 1;
+}
+
+.comments article {
+    background-color: #9ca0a8;
+    border: none;
+    max-height: none;
+}
+
+.comment_post {
+    text-align: center;
+}
+
+.comment_post textarea {
+    border-style: solid;
+    border-width: 1px  0;
+    border-color: black;
+    max-width: 100%;
+}
+
+.comments_panel article .expand_box {
+    /* no expand box in comments */
+    display: none;
+}
+
+/*** media queries ***/
+
+@media (min-width: 500px) {
+    /*** general ***/
+
+    #main_area {
+        background-image: var(--bg-img);
+        background-repeat: no-repeat;
+        background-size: cover;
+    }
+
+    article.box {
+        width: 80%;
+        border-style: solid solid none solid;
+    }
+    .comments article.box {
+        width: 30rem;
+        margin: 2% auto;
+        border: none;
+    }
+    .comment_post textarea {
+        border-width: 1px;
+        border-radius: 1em;
+        border: solid 1px;
+        padding: 0.5em;
+    }
+
+    footer span {
+        background: rgba(200,200,200,0.6);
+        border-radius: 0.5em 0.5em 0 0;
+        padding: 0 0.5em;
+        margin-top: 1em;
+    }
+
+}
+
+/*** clicked ***/
+
+.main_article article.clicked {
+    border-bottom-style: solid;
+}
+
+.main_article article.clicked .expand_box.box_top {
+    display: initial;
+}
+
+.main_article article.clicked .expand_box {
+    opacity: 0;
+    transition: opacity 0.5s;
+}
+
+.main_article article.clicked .expand_box p {
+    background-color: transparent;
+    border: none;
+}
+
+.main_article article.clicked .expand_box:hover {
+    opacity: 1;
+    color: grey;
+}
+
+.main_article article.clicked .expand_box::before {
+    background-image: none;
+    display: none;
+}
+
+.main_article article.clicked .expand_box .hide {
+    display: inline;
+}
+
+.main_article article.clicked .expand_box .show {
+    display: none;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/chat.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,83 @@
+:root {
+  --message_input-height: 2rem;
+}
+
+#main_area {
+    overflow: hidden;
+}
+
+.chat_widget {
+	position: absolute;
+    height: 100%;
+    width: 100%;
+    box-sizing: border-box;
+    display: flex;
+    flex-direction: column;
+}
+
+#messages {
+    height: 100%;
+    min-height: 150px;
+    overflow: auto;
+    box-sizing: border-box;
+    resize: vertical;
+    transition: height 0.6s;
+}
+
+#subject {
+	padding: 1em;
+	text-align: center;
+	background: #eee;
+	font-style: italic;
+}
+
+#messages > p {
+    margin: 0;
+    padding: 0.5em 0 0 0.5em;
+}
+
+#messages .msg_body {
+    white-space: pre-wrap;
+}
+
+#message_input {
+    width: 100%;
+    height: 100%;
+    padding-top: 0.5rem;
+    margin: 0;
+    box-sizing: border-box;
+    resize: none;
+}
+
+.message_box {
+    flex: 1 1 calc(var(--message_input-height));
+    position: relative;
+    min-height: 1rem;
+    box-sizing: border-box;
+}
+
+
+#messages > p.msg_info {
+    white-space: pre-wrap;
+    font-family: monospace;
+    color: #049282;
+}
+
+.msg_header {
+	display: block;
+	font-size: 0.9em;
+}
+
+.author {
+    font-weight: bold;
+}
+
+.date {
+	color: #777;
+}
+
+@media (min-width: 800px) {
+    #messages > p {
+        padding-left: 1.5em;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/chat.js	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,60 @@
+/* SàT Template: Chat page handling
+ *
+ * Copyright (C) 2017 Jérôme Poisson (goffi@goffi.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+var msgInput = document.getElementById('message_input');
+var messages = document.getElementById('messages');
+const messagesTransitionOri = messages.style.transition;
+
+msgInput.addEventListener('keypress', function(event) {
+    if (event.which == 13 && !event.shiftKey) {
+        if (messages.style.height !== '100%') {
+            messages.style.transition = messagesTransitionOri;
+            setTimeout(function() {
+                messages.style.transition = 'initial';
+                messages.scrollTop = messages.scrollHeight;
+            }, 1000);
+            messages.style.height = "100%";
+        }
+        if (!this.value.trim()) {
+            return;
+        }
+        socket.send({'type': 'msg',
+                     'body': this.value});
+        this.value = '';
+        event.preventDefault();
+    }}
+);
+
+var mutationCb = function(mutationsList) {
+    scrollPos = messages.scrollTop + messages.clientHeight;
+    if (messages.lastChild.offsetTop - scrollPos - 10 <= 0) {
+        // we auto scroll only if we are at the bottom of the page
+        // else the use is probably checking history
+        // Note thas this doesn't take margin into account,
+        // we suppose margin to be 0 for messages children
+        messages.scrollTop = messages.scrollHeight;
+    }
+};
+
+var observer = new MutationObserver(mutationCb);
+
+observer.observe(messages, { childList: true });
+// we want to start with scrolling at bottom
+messages.scrollTop = messages.scrollHeight;
+messages.style.transition = 'initial';
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/chat_select.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,39 @@
+#main_area {
+    overflow: auto;
+}
+
+.rooms_list {
+	list-style: none;
+    display: grid;
+    text-align: center;
+    padding: 0;
+}
+
+.rooms_list a {
+	text-decoration: none;
+	color: inherit;
+	font-variant: small-caps;
+	font-weight: bold;
+}
+
+.rooms_list li {
+    margin-top: 1em;
+}
+
+.rooms_list li:first-child {
+    margin-top: 0;
+}
+
+.rooms_list li:hover {
+    background-color: #efefef;
+}
+
+@media (min-width: 600px) {
+    .rooms_list {
+        grid-template-columns: 1fr 1fr 1fr;
+    }
+
+    .rooms_list li {
+        margin-top: 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/common.js	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,134 @@
+var __session_storage_available;
+var __local_storage_available;
+
+function storageAvailable(type) {
+    /* check if session or local storage is available
+     *
+     * @param type(string): "session" or "storage"
+     * @return (boolean): true if requested storage is available
+     */
+    console.assert(type == 'session' || type == 'storage', "bad storage type (%s)", type);
+    const var_name = '__' + type + '_storage_available';
+    var available = window[var_name];
+    if (available === undefined) {
+        // test method from https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
+        var storage = window[type + 'Storage'];
+        try {
+            x = '__storage_test__';
+            storage.setItem(x, x);
+            storage.removeItem(x);
+            available = true;
+        }
+        catch(e) {
+            available = e instanceof DOMException && (
+                    // everything except Firefox
+                    e.code === 22 ||
+                    // Firefox
+                    e.code === 1014 ||
+                    // test name field too, because code might not be present
+                    // everything except Firefox
+                    e.name === 'QuotaExceededError' ||
+                    // Firefox
+                    e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
+                // acknowledge QuotaExceededError only if there's something already stored
+                storage.length !== 0;
+        }
+
+        if (!available) {
+            console.warn("%s storage not available", type);
+        }
+        window[var_name] = available;
+    }
+
+    return available;
+}
+
+function toggle_clicked_class_tag(tag_name, class_name='clicked') {
+    for (let elt of document.getElementsByTagName(tag_name)) {
+        elt.classList.toggle(class_name);
+    }
+}
+
+function toggle_clicked_class_sel(selectors, class_name='clicked') {
+    for (let elt of document.querySelectorAll(selectors)) {
+        elt.classList.toggle(class_name);
+    }
+}
+
+function set_clicked_class_id(trigger_elem_id, target_elem_id=null, class_name='clicked') {
+    if (target_elem_id === null) { target_elem_id = trigger_elem_id; }
+    document.getElementById(trigger_elem_id).addEventListener(
+        "click",
+        function() {
+            document.getElementById(target_elem_id).classList.toggle(class_name);
+        }
+    );
+}
+
+function get_elt(arg) {
+    if (typeof arg === 'string') {
+        // we should have an id
+        return document.getElementById(arg);
+    }
+    else {
+        // we should have an element
+        return arg;
+    }
+}
+
+function clicked_cls(elt) {
+    /* toggle "clicked" class on each click, and remove "init" class if present */
+    // init
+    if (elt.classList.contains("init")) {
+        elt.classList.remove("init");
+    }
+
+    // clicked
+    elt.classList.toggle("clicked");
+}
+
+function clicked_mh_fix(arg) {
+    /* toggle clicked, and fix max-height on transitionend
+     *
+     * needed to workaround transition issue with max-height:none
+     * inspired from https://css-tricks.com/using-css-transitions-auto-dimensions,
+     * thanks to Brandon Smith
+     *
+     * @param arg: element to toggle (id as string, or element itself)
+     * */
+    elt = get_elt(arg);
+
+    if (!elt.classList.contains("clicked")) {
+        /* expand */
+        var fix_expand = function(event) {
+            elt.removeEventListener("transitionend", fix_expand, false);
+            if (elt.classList.contains("clicked")) {
+                /* if event is clicked quicker than transition time,
+                 * this callback can be called on reduce */
+                elt.style.maxHeight = "none";
+            }
+        };
+
+        elt.setAttribute('max_height_init', elt.clientHeight);
+        elt.addEventListener("transitionend", fix_expand, false);
+        clicked_cls(elt);
+        elt.style.maxHeight = elt.scrollHeight + 'px';
+    }
+    else {
+        /* reduce */
+        var transition_save = elt.style.transition;
+        elt.style.transition = '';
+        requestAnimationFrame(function() {
+            elt.style.maxHeight = elt.scrollHeight + 'px';
+            elt.style.transition = transition_save;
+
+            requestAnimationFrame(function() {
+                elt.style.maxHeight = elt.getAttribute('max_height_init') + 'px';
+                elt.removeAttribute('max_height_init');
+                elt.style.maxHeight = null;
+            });
+        });
+
+        clicked_cls(elt);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/event.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,92 @@
+@import 'blog.css'; /* needed as blog/articles.html is included */
+
+.invitation_header {
+    text-align: center;
+    font-variant: small-caps;
+    border: none;
+}
+
+.instructions {
+    font-style: italic;
+    margin: 0;
+}
+
+#event_picture {
+    max-width: 100%;
+    border-radius: 2em;
+}
+
+#event_counter {
+    font-weight: bold;
+    font-size: 1.3em;
+    text-align: center;
+    width: 15em;
+    margin: 1em auto;
+    border: 2px solid #aab6c4;
+    background-color: #f3e7e7;
+    color: #2f3943;
+}
+
+.attendance {
+    background-color: #9ca0a8;
+    padding: 0 0.3em;
+    max-width: 580px;
+    margin: 0 auto;
+    color: #3A3A3A;
+}
+
+.attending {
+    width: 6em;
+    margin: 0 auto;
+}
+
+.attending label {
+    /* display: inline; */
+    margin: 0.8em 0;
+}
+
+.guests label {
+    display: block;
+    margin: 1em 0;
+}
+
+.guests input {
+    width: 2.5em;
+    margin: 0 1em;
+}
+
+.poll_instructions {
+    font-weight: bold;
+    font-size: 0.9em;
+    font-size: 1.1em;
+    text-align: center;
+}
+
+.guests label{
+    text-align: center;
+    /* font-style: italic */
+}
+
+.guests input {
+    display: block;
+    margin: 0 auto;
+}
+
+.submit {
+    width: 15em;
+    margin: 0 auto;
+    padding: 1em 0;
+}
+
+.submit input {
+    width: 100%;
+}
+
+@media (min-width: 500px) {
+    .invitation_header {
+        width: 94%;
+        margin: 0 auto;
+        border: solid 1px;
+        border-color: #9ca0a8;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/file.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,49 @@
+#files {
+    list-style: none;
+    display: flex;
+    flex-wrap: wrap;
+}
+
+.file {
+    padding: 0 0 0.5em;
+    width: 100px;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    text-align: center;
+    overflow-wrap: break-word;
+}
+
+.file:hover {
+    background: #ddd;
+    overflow: visible;
+}
+
+.file a {
+    text-decoration: none;
+    outline: none;
+}
+
+.file a:focus img {
+    outline: 3px solid #bbb;
+}
+
+.file__icon {
+    height: 3em;
+    display: block;
+    margin: 0 auto;
+}
+
+.file__thumbnail {
+    max-width: 300px;
+    height: 125px;
+    display: block;
+    margin: 0 auto;
+}
+
+
+@media (min-width: 500px) {
+    .file {
+        padding: 1em;
+        width: 170px;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/fonts.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,63 @@
+/* generated from https://google-webfonts-helper.herokuapp.com */
+
+/* *** sat_base_font *** */
+
+/* sat_base_font-regular - latin-ext_latin */
+@font-face {
+  font-family: 'sat-base-font';
+  font-style: normal;
+  font-weight: 400;
+  src: local('sat-base-font'), local('sat-base-font-Regular'),
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* sat_base_font-italic - latin-ext_latin */
+@font-face {
+  font-family: 'sat-base-font';
+  font-style: italic;
+  font-weight: 400;
+  src: local('sat-base-font Italic'), local('sat-base-font-Italic'),
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* sat_base_font-700 - latin-ext_latin */
+@font-face {
+  font-family: 'sat-base-font';
+  font-style: normal;
+  font-weight: 700;
+  src: local('sat-base-font Bold'), local('sat-base-font-Bold'),
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* sat_base_font-900 - latin-ext_latin */
+@font-face {
+  font-family: 'sat-base-font';
+  font-style: normal;
+  font-weight: 900;
+  src: local('sat-base-font Black'), local('sat-base-font-Black'),
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* sat_base_font-700italic - latin-ext_latin */
+@font-face {
+  font-family: 'sat-base-font';
+  font-style: italic;
+  font-weight: 700;
+  src: local('sat-base-font Bold Italic'), local('sat-base-font-BoldItalic'),
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* sat_base_font-900italic - latin-ext_latin */
+@font-face {
+  font-family: 'sat-base-font';
+  font-style: italic;
+  font-weight: 900;
+  src: local('sat-base-font Black Italic'), local('sat-base-font-BlackItalic'),
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+       url('fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/fonts/sat_base_font/README.txt	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,7 @@
+This font is based on the Alegreya font by Juan Pablo del Peral (juan@huertatipografica.com.ar), it has been modified to be compressed using https://google-webfonts-helper.herokuapp.com, and put here for being embedded in Salut à Toi templates, to avoid privacy issues with third party website download.
+
+As the name is a reserved name and the compression make the font a derivative, this font has been renamed to "sat_base_font", according to the recommandation read at http://scripts.sil.org/cms/scripts/page.php?item_id=OFL-FAQ_web#1db14da7
+
+Thanks to Juan Pable del Peral for his work!
+
+The license "SIL Open Font License, Version 1.1" only apply to this directory.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/fonts/sat_base_font/SIL Open Font License.txt	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,44 @@
+Copyright (c) 2011, Juan Pablo del Peral (juan@huertatipografica.com.ar), 
+with Reserved Font Names "Alegreya" "Alegreya SC"
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
+
+5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700.woff2 has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-700italic.woff2 has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900.woff2 has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-900italic.woff2 has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-italic.woff2 has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff has changed
Binary file sat_templates/default/static/fonts/sat_base_font/sat_base_font-v7-latin-ext_latin-regular.woff2 has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/forum.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,77 @@
+@import 'blog.css'; /* needed as blog/articles.html is included */
+
+#forums {
+    margin: 2em auto 0 auto;
+    width: 90%;
+}
+
+ul.forum {
+    box-sizing: border-box;
+    list-style: none;
+    display: flex;
+    margin: 0 auto;
+}
+
+ul.forum>li {
+    width: 100%;
+}
+
+ul.forum>li>a {
+    border: 0.7rem solid #ddd;
+    border-radius: 0.5em;
+    display: block;
+    padding: 1rem;
+    margin-bottom: 1rem;
+    text-decoration: none;
+    color: inherit;
+}
+
+a.forum_actif:hover {
+    background-color: #43d2f6;
+}
+
+p.forum_short-desc {
+    color: #666;
+    margin: 0;
+    font-size: 0.8em;
+}
+
+.forum__topics {
+    margin-top: 3em;
+}
+
+.forum__topics>div {
+    box-sizing: border-box;
+    width: 80%;
+    margin: 0.5em auto;
+    padding: 0.2em 2em;
+    background: #eee;
+
+}
+
+.forum__topics>:hover {
+    background-color: #43d2f6;
+}
+
+.forum__topics img.avatar {
+    vertical-align: middle;
+    margin-right: 1em;
+}
+
+.forum__topics a {
+    display: block;
+    text-decoration: none;
+    color: inherit;
+}
+
+.textbox input {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+@media (min-width: 500px) {
+    .textbox input {
+        min-width: 26em;
+        width: auto;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/highlight.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,77 @@
+/* This file as been generated from Pygments using
+ * f.write(HtmlFormatter().get_style_defs('.highlight'))
+ * where f was an open file.
+ * It is used in Libervia for highlighting code or markup
+ * If changes are needed, they should be done in a separate CSS file
+ * so this one keep Pygments' defaults
+ */
+.highlight .hll { background-color: #ffffcc }
+.highlight  { background: #f8f8f8; }
+.highlight .c { color: #408080; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #008000; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
+.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
+.highlight .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
+.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #008000 } /* Keyword.Pseudo */
+.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #B00040 } /* Keyword.Type */
+.highlight .m { color: #666666 } /* Literal.Number */
+.highlight .s { color: #BA2121 } /* Literal.String */
+.highlight .na { color: #7D9029 } /* Name.Attribute */
+.highlight .nb { color: #008000 } /* Name.Builtin */
+.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+.highlight .no { color: #880000 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #0000FF } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #19177C } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mb { color: #666666 } /* Literal.Number.Bin */
+.highlight .mf { color: #666666 } /* Literal.Number.Float */
+.highlight .mh { color: #666666 } /* Literal.Number.Hex */
+.highlight .mi { color: #666666 } /* Literal.Number.Integer */
+.highlight .mo { color: #666666 } /* Literal.Number.Oct */
+.highlight .sa { color: #BA2121 } /* Literal.String.Affix */
+.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
+.highlight .sc { color: #BA2121 } /* Literal.String.Char */
+.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */
+.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #008000 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
+.highlight .ss { color: #19177C } /* Literal.String.Symbol */
+.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
+.highlight .fm { color: #0000FF } /* Name.Function.Magic */
+.highlight .vc { color: #19177C } /* Name.Variable.Class */
+.highlight .vg { color: #19177C } /* Name.Variable.Global */
+.highlight .vi { color: #19177C } /* Name.Variable.Instance */
+.highlight .vm { color: #19177C } /* Name.Variable.Magic */
+.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/invitation.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,27 @@
+#body {
+    display: flex;
+    flex-direction: column;
+}
+
+.invitation_header {
+    flex: 1
+}
+
+.invitation_header h1,p {
+    text-align: center;
+}
+
+#include_main {
+    flex: 9
+}
+
+#include_main {
+    width: 100%;
+    margin: 0;
+    border: none;
+}
+
+footer {
+    /* footer should already be displayed in included page */
+    display: none;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/login.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,197 @@
+body {
+    background: #111;
+}
+
+footer {
+    color: #999;
+}
+
+#login_container {
+    display: flex;
+    height: 100%;
+    align-items: center;
+    justify-content: center;
+}
+
+#login_box {
+    display: flex;
+    background: #111;
+    color: #aaa;
+    width: 600px;
+    margin: auto;
+}
+
+#login_left {
+    display: none;
+}
+
+#login_right {
+    width: 100%;
+    position: relative;
+}
+
+#login_form {
+    margin-top: 60px;
+}
+
+#login_form label {
+    font-weight: bold;
+    display: block;
+    text-align: center;
+}
+
+#login_form input:not(.form_submit) {
+    display: block;
+    margin: 0 auto;
+    height: 25px;
+    line-height: 25px;
+    width: 200px;
+    text-indent: 11px;
+    background: #000;
+    color: #ccc;
+    border: 1px solid #555;
+    border-radius: 15px 15px 15px 15px;
+}
+
+#login_form input:not(.form_submit):focus {
+	box-shadow: 0 0 2px 2px #43d2f6;
+}
+
+#login_form input:hover {
+    background-color: #222;
+}
+
+#login_form input:focus {
+	border: 1px solid #999;
+	outline: none;
+}
+
+#login_box .form_input {
+    display: block;
+    margin-bottom: 1em;
+}
+
+#login_form .form_submit,
+#logged .form_submit {
+    /* FIXME: as above /media is not good.
+     * url should be replaced by real gradient */
+    color: #fff;
+    background: #222 url('/media/libervia/gradient.png') repeat-x;
+    font-weight: bold;
+    line-height: 1;
+    text-shadow: 0 -1px 1px rgba(0,0,0,0.25);
+    padding: 7px 10px 8px;
+    border: 0;
+    border-radius: 6px 6px 6px 6px;
+    cursor: pointer;
+    margin-top: 30px;
+}
+
+#login_error {
+    position: absolute;
+    margin: -90px 0 0;
+    width: 100%;
+    height: 150px;
+    overflow: auto;
+    font-size: 0.9em;
+	font-weight: bold;
+	color: lightcoral;
+	text-align: center;
+}
+
+#login_error p {
+    position: absolute;
+    bottom: 0;
+    margin-left: 46px;
+    margin-right: 46px;
+}
+
+#create_account_link {
+    width: 100%;
+    margin-top: 3em;
+}
+
+#login_link {
+    margin-top: 1em;
+}
+
+#create_account_link a,
+#login_link a {
+    text-decoration: none;
+    text-align: center;
+    font-size: 0.8em;
+    cursor: pointer;
+    color: #fff;
+    display: block;
+    text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.2);
+}
+
+.logged_box {
+    margin: 3em;
+	padding: 1em;
+	text-align: center;
+	font-style: italic;
+}
+
+.logged_profile {
+    font-style: normal;
+    font-weight: bold;
+}
+
+#login_form form.register input[type=submit] {
+    margin-top: 0.5em;
+}
+
+@media (min-width: 600px) {
+    body {
+        background: transparent;
+    }
+
+    footer {
+        color: black;
+    }
+
+    #login_left {
+        display: inline;
+    }
+
+    #login_right {
+        /* FIXME: there is not way to correctly select images from
+         * media path at the moment */
+        background: url('/media/libervia/register_right.png');
+    }
+
+    #login_error {
+        width: auto;
+        max-height: 50px;
+        margin: 10px 46px 0;
+    }
+     
+    #login_error p {
+        position: static;
+        margin: 0;
+    }
+
+    #login_form label {
+        text-align: initial;
+        position: relative;
+        left: 50px;
+    }
+
+    #login_form label::after {
+        content: ':'
+    }
+
+    #login_form input:not(.form_submit) {
+        display: inline;
+        margin: 0;
+        position: relative;
+        left: 50px;
+    }
+
+    #create_account_link {
+        margin-top: 0;
+        bottom: 0;
+        position: absolute;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/merge-request.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,1 @@
+ticket.css
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/merge-request_item.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,44 @@
+@import 'highlight.css';
+
+.highlight {
+    overflow: auto;
+}
+
+.box {
+    border-radius: 0;
+    box-shadow: none;
+}
+
+.ticket {
+    max-width: none;
+}
+
+.xmlui_cont.xmlui_cont_vertical {
+    max-width: 780px;
+    margin: 0 auto;
+}
+
+.view #wid_body {
+    border: 1px solid silver;
+}
+
+#tab_patches {
+    overflow: auto;
+}
+
+#tab_patches header {
+    border-top: 1px solid silver;
+}
+
+#tab_patches header label {
+    font-weight: bold;
+}
+
+.commit_msg {
+    font-style: italic;
+    white-space: pre-wrap;
+}
+
+.diff {
+    white-space: pre;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/photo.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,51 @@
+@import 'blog.css'; /* needed for comments */
+
+.album {
+    display: flex;
+    flex-wrap: wrap;
+}
+
+.album__item {
+    width: 400px;
+	margin: 0 0.1em 2em 0.1em;
+}
+
+.album__vignette {
+    background-color: #444;
+    height: 300px;
+    position: relative;
+}
+
+.album__thumbnail {
+    height: 100%;
+    display: block;
+    margin: 0 auto;
+}
+
+.album__comments-bar {
+    display: block;
+    font-size: 0.8em;
+    color: black;
+    text-align: right;
+    cursor: pointer;
+}
+
+.album__comments-bar:hover {
+    background-color: #ddd;
+    font-weight: bold;
+}
+
+.comments__count {
+    font-weight: bold;
+}
+
+.panel-drawer {
+    max-height: 0;
+    opacity: 0;
+    transition: max-height 1s, opacity 2s;
+    overflow: hidden;
+}
+
+.panel-drawer.clicked {
+    opacity: 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/styles.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,535 @@
+:root {
+  --select-bg-color: #ddd;
+  --size-medium: 3em;
+}
+
+
+html {
+    font-family: "sat-base-font";
+}
+
+body {
+    margin: 0;
+    padding: 0;
+    display: flex;
+    height: 100vh;
+    flex-direction: column;
+    box-sizing: border-box;
+}
+
+ul {
+    padding: 0;
+}
+
+#main_side_bar {
+
+}
+
+#main_area {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    box-sizing: border-box;
+}
+
+#body {
+    flex: 1;
+    position: relative;
+}
+
+footer {
+    text-align: center;
+    font-size: 0.7em;
+    font-weight: bold;
+}
+
+.box {
+    background-color: #edf2ff;
+    border-radius: 0;
+    border-color: silver;
+}
+
+.box__tools {
+    margin: 0;
+    padding: 0;
+}
+
+.box__tools a {
+    text-decoration: none;
+    color: inherit;
+    padding: 0.2em;
+    border-radius: 0.4em;
+}
+
+.box__tools a:hover {
+    background-color: var(--select-bg-color);
+}
+
+.title {
+    font-weight: bold;
+    text-align: center;
+}
+
+.post_confirm {
+    text-align: center;
+    background-color: lightgreen;
+    padding: 1em;
+    font-size: 1.2em;
+    font-weight: bold;
+    width: 60%;
+    margin: 1.5em auto;
+}
+
+/*** Generic ***/
+
+.button {
+    padding: 0.5em 1em;
+    background: #333;
+    color: #ccc;
+    border: 1px solid #555;
+    border-radius: 0.8em;
+    font-weight: bold;
+}
+
+
+.button:hover {
+    background-color: #bc0000;
+}
+
+.instructions--head {
+    font-size: 1.5em;
+    text-align: center;
+}
+
+.instructions--alt {
+    text-align: center;
+    font-style: italic;
+}
+
+.items_vert--centered {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+
+/*** blocks ***/
+
+.block_separator {
+    font-size: 1.4em;
+    display: flex;
+}
+
+.block_separator__label {
+    display: inline-block;
+    margin: 0 0.2em;
+}
+
+.block_separator__line {
+    height: 1px;
+    background: #ccc;
+    flex: 1;
+    margin-top: 0.7em;
+}
+
+/*** grids ***/
+
+.grid {
+    list-style: none;
+    display: flex;
+    flex-wrap: wrap;
+}
+
+.grid--center {
+    justify-content: center;
+}
+
+.grid__item>a {
+    text-decoration: none;
+    color: inherit;
+}
+
+.grid__item--medium {
+   width: 170px;
+   padding: 0.2em;
+   text-align: center;
+}
+
+.grid__item--selectable {
+    cursor: pointer;
+}
+
+.grid__item--selectable:hover {
+    background-color: var(--select-bg-color);
+}
+
+/*** avatars ***/
+
+.avatar {
+    height: 2rem;
+    width: 2rem;
+    display: inline-flex;
+    flex-direction: column;
+    align-content: center;
+    justify-content: center;
+    background: #ccc;
+    border-radius: 0.2rem;
+}
+
+.avatar__content {
+
+}
+
+.avatar--medium {
+    height: var(--size-medium);
+    width: var(--size-medium);
+    border-radius: 0.5rem;
+}
+
+.avatar--generated {
+    text-align: center;
+    background:  #43d2f6;
+}
+
+
+/*** icons ***/
+
+.icon--medium {
+    height: var(--size-medium);
+    display: block;
+    margin: 0 auto;
+}
+
+.icon--small {
+    height: 1em;
+}
+
+.icon--soft {
+    fill: #777;
+}
+
+.icon__name {
+
+}
+
+/*** Messages ***/
+
+.message--info {
+    max-width: 500px;
+    margin: 0 auto;
+    padding: 1em;
+    text-align: justify;
+}
+
+.message--info pre {
+    background: #ddd;
+    padding: 1em;
+}
+
+/*** Menus ***/
+
+.menu ul {
+    display: flex;
+    margin-top: 8px;
+    list-style: none;
+}
+
+.menu a {
+    display: block;
+    color: inherit;
+    text-decoration: none;
+    font-variant: small-caps;
+}
+
+.main_menu {
+    min-width: 200px;
+    /* background-color: #eaeaea; */
+    background-color: #333;
+    color: white;
+}
+
+.main_menu ul {
+    flex-direction: row;
+    flex-wrap: wrap;
+}
+
+.main_menu li {
+    flex: 1;
+    padding: 0;
+    margin: 0 0.5em;
+}
+
+.main_menu a {
+    display: inline;
+    white-space: nowrap;
+}
+
+.main_menu a:hover {
+    background-color: initial;
+    text-shadow: 1px 1px 2px;
+    font-weight: bold;
+}
+
+.category_menu ul {
+    justify-content: center;
+}
+
+.category_menu li {
+    margin: 0.5em;
+    text-align: center;
+}
+
+.category_menu a {
+    border: solid 1px;
+    padding: 0.5em;
+    border-radius: 0.2em;
+    background: #eee;
+}
+
+/*** containers ***/
+
+/* tabs */
+
+.tab_container {
+    max-width: 1000px;
+    margin: 0 auto;
+}
+
+.tab_header {
+    background-color: white;
+    border-bottom: 1px solid lightgrey;
+}
+
+.tab_header ul {
+    display: flex;
+    margin: 0;
+    padding: 0;
+    list-style: none;
+    background-color: white;
+}
+
+.tab_page {
+    box-sizing: border-box;
+    padding-top: 2em;
+    border: 1px solid lightgrey;
+    border-top: none;
+    display: None;
+}
+
+.tab_page.clicked {
+    display: block;
+}
+
+.tab_button {
+    display: inline;
+    color: grey;
+    background-color: white;
+    border-top: 1px solid lightgrey;
+    border-left: 1px solid lightgrey;
+    border-bottom: 1px solid lightgrey;
+    padding: 0 1em;
+    cursor: pointer;
+    /* we go down by 1px to remove bottom border from .tab_header */
+    margin-bottom: -1px;
+}
+
+.tab_button.clicked {
+    /* background: lightgrey; */
+    color: inherit;
+    border-bottom: none;
+}
+
+li.tab_button:last-child {
+    border-right: 1px solid lightgrey;
+}
+
+.tab_button input {
+    display: None;
+}
+
+.tab_button label {
+    margin: 1em;
+}
+
+.tab_button input:checked + label {
+    font-weight: bold;
+}
+
+/*** Forms ***/
+
+/* a form with only one field */
+
+.form_single {
+    text-align: center;
+}
+
+.form_single input:not([type="submit"]) {
+    margin: 0 1em;
+    width: 15em;
+    border-radius: 0.7em;
+    outline: none;
+    border: 1px solid black;
+    padding: 0.4em;
+    box-shadow: none;
+}
+
+
+.form_submit {
+    margin: 1em auto 0;
+    display: block;
+}
+
+.form_jid {
+    text-align: center;
+}
+
+/* Textboxes */
+
+form.textbox>* {
+    display: block;
+    margin: 1em auto;
+    text-align: center;
+    max-width: 100%;
+    box-sizing: border-box;
+}
+
+form.textbox>textarea {
+    text-align: left;
+}
+
+.log_request {
+    text-align: center;
+}
+
+/*** Navigation ***/
+
+.prev_next_links ul {
+    list-style: none;
+    display: flex;
+    padding: 0 2em;
+    margin: 0;
+}
+
+.prev_next_links li {
+    flex: 1;
+}
+
+.prev_next_links li.older_items {
+    text-align: right;
+}
+
+.prev_next_links img {
+    display: block;
+}
+
+.prev_next_links .older_items img {
+    display: block;
+    margin-left: auto;
+    margin-right: 0;
+}
+
+.prev_next_links a {
+    display: inline-block;
+    margin-top: 1em;
+    padding: 0.2em;
+    text-decoration: None;
+    color: inherit;
+    font-variant: small-caps;
+    background: rgba(200,200,200,0.6);
+    border-radius: 0.5em;
+}
+
+.prev_next_links a:hover {
+    background-color: #ddd;
+}
+
+/*** XMLUI ***/
+
+.xmlui_cont_vertical>* {
+    display: block;
+    box-sizing: border-box;
+}
+
+.xmlui_cont_vertical>.xmlui_widget {
+    width: 100%;
+    min-height: 1em;
+    min-width: 1px;
+}
+
+label.xmlui_label {
+    font-weight: bold;
+}
+
+td a {
+  /* we use <a> for non JS links in table
+   * so we don't want specific color/text-decoration by default
+   */
+  color: inherit;
+  text-decoration: inherit;
+}
+
+
+/*** Notifications ***/
+
+.notification.retry {
+    position: fixed;
+    top: 1rem;
+    margin: auto;
+    width: 80%;
+    background: #DB1616;
+    border: 3px solid silver;
+    left: 10%;
+    text-align: center;
+}
+
+#retry_counter {
+    font-weight: bold;
+}
+
+#retry_now {
+    color: blue;
+    text-decoration: underline;
+    cursor: pointer;
+}
+
+@media (min-width: 800px) {
+    html {
+        background-size: auto;
+    }
+
+    body {
+        flex-direction: row;
+    }
+
+    #main_area {
+        overflow: auto;
+    }
+
+    .box {
+        border-radius: 1em;
+        box-shadow: 10px 10px 16px -5px rgba(0,0,0,0.5);
+    }
+
+    .main_menu a {
+        display: block;
+        padding: 2em 0;
+    }
+
+    .main_menu ul {
+        flex-direction: column;
+        padding-left: 2em;
+    }
+
+    .prev_next_links ul {
+        padding: 0 6em;
+    }
+
+    .instructions--alt {
+        padding-top: 3rem;
+    }
+
+    /*** forms ***/
+
+    .form_single .form_submit.button {
+        display: inline;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/ticket.css	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,180 @@
+@import 'blog.css'; /* needed as blog/articles.html is included */
+
+.instructions {
+    font-style: italic;
+    text-align: center;
+}
+
+.instructions span {
+    padding: 0.3em;
+}
+
+ul.xmlui_list {
+    list-style: none;
+    display: flex;
+    flex-direction: column;
+    padding: 0;
+}
+
+.xmlui_list li {
+    display: inline-flex;
+    background: #eee;
+    padding: 0.5em;
+    margin: 0.5em 0;
+    border: 1px solid silver;
+}
+
+.xmlui_list li:hover {
+    background: yellow;
+}
+
+.xmlui_list a {
+    display: flex;
+    width: 100%;
+    text-decoration: none;
+    color: inherit;
+}
+
+.xmlui_list a:visited {
+    color: inherit;
+}
+
+.tickets a.status_closed {
+    text-decoration: line-through;
+    color: grey;
+}
+
+.xmlui_field__id {
+    font-style: italic;
+    padding-right: 1em;
+}
+
+.xmlui_field__title {
+    padding-right: 1em;
+}
+
+.tickets a.severity_major .xmlui_field__title {
+    font-weight: bold;
+    color: red;
+}
+
+.tickets tbody tr.severity_major .td_title a::before {
+    content: '⚠ ';
+    color: red;
+}
+
+/* single ticket */
+
+.ticket {
+    padding: 20px;
+    max-width: 500px;
+    margin: 0 auto;
+}
+
+.view .xmlui_widget {
+    width: auto;
+}
+
+.view div.xmlui_cont div.xmlui_cont {
+    display: grid;
+    grid-template-columns: min-content 1fr;
+}
+
+.view #label_wid_publisher,
+.view #wid_publisher,
+.view #label_wid_title,
+.view #label_wid_body,
+.view #label_wid_id,
+.view #label_wid_comments_uri,
+.view #wid_comments_uri {
+    display: none
+}
+
+.view .xmlui_label {
+    padding-right: 2em;
+}
+
+.view #wid_id {
+    margin: 0;
+    font-style: italic;
+    grid-column-start: 1;
+    grid-column-end: 3;
+    text-align: right;
+}
+
+.view .xmlui_label {
+    font-weight: bold;
+    float: left;
+    color: #808080cc;
+}
+
+.view #wid_title {
+    font-weight: bold;
+    display: block;
+    text-align: center;
+    grid-column-start: 1;
+    grid-column-end: 3;
+}
+.view #wid_title::first-letter {
+    text-transform: uppercase;
+}
+
+.view #wid_labels span, .xmlui_field__labels span {
+    font-size: 0.8em;
+    background: #eae3e3;
+    font-variant: small-caps;
+    border: 1px solid black;
+    border-radius: 0.5em;
+    padding: 0 2px;
+    white-space: nowrap;
+}
+
+.view #wid_labels span.value_work_in_progress {
+    background: yellow;
+}
+
+.view #wid_type {
+    font-weight: bold;
+}
+
+.view #wid_type span.value_bug::after {
+    content: ' 🐛';
+    color: red;
+}
+
+.view #wid_severity span.value_major {
+    font-weight: bold;
+    color: red;
+}
+
+.view #wid_severity span.value_major::after {
+    content: '⚠ ';
+}
+
+.view #wid_body {
+    white-space: pre-wrap;
+    max-height: 500px;
+    overflow: auto;
+    resize: both;
+    background-color: white;
+    padding: 5px;
+    text-align: justify;
+    border: 1px solid black;
+    border-radius: 5px;
+    grid-column-start: 1;
+    grid-column-end: 3;
+    display: block;
+}
+
+.comment_post {
+    margin-top: 3em;
+}
+
+@media (min-width: 800px) {
+    ul.xmlui_list {
+        padding: 0 2em;
+    }
+    .xmlui_list li {
+        border-radius: 0.3em;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/static/websocket.js	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,131 @@
+/*
+SàT templates: suit of templates for Salut à Toi
+Copyright (C) 2017 Jérôme Poisson (goffi@goffi.org)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* websocket handler */
+
+
+//TODO: retry websocket instead of reload
+function WSHandler(url, token, debug=false) {
+    var socket = new WebSocket(url, 'libervia_page_' + token );
+    var retried = 0;
+
+    var errorHandler = function(error) {
+        if (retried > 20) {
+            console.error("Too many tries, can't start websocket");
+            alert("Dynamic connection with server can't be established, please try to reload this page in a while or contact your service administrator");
+            return;
+        }
+        var delay = Math.floor((Math.random() * 10) + 1) + 30 * Math.min(retried, 6);
+        notifyRetry(delay, function() {
+            retried++;
+            socket = new WebSocket(url, 'libervia_page_' + token );
+            socket.addEventListener('error', errorHandler);
+        });
+    };
+
+    socket.addEventListener('error', errorHandler);
+
+    if (debug) {
+        socket.addEventListener('message', function(event) {
+            console.log('WS in <== ', JSON.parse(event.data));
+        });
+    }
+
+    socket.addEventListener('message', function(event) {
+        try {data = JSON.parse(event.data);}
+        catch (e) {
+            console.warn('invalid websocket message received: %s', e);
+            return;
+        }
+        switch (data.type) {
+            case 'reload':
+                location.reload(true);
+                break;
+            case 'dom':
+                selected_element = document.body.querySelector(data.selectors);
+                switch (data.update_type) {
+                    case 'append':
+                        var template = document.createElement('template');
+                        template.innerHTML = data.html.trim();
+                        new_element = template.content.firstChild;
+                        selected_element.appendChild(new_element);
+                        break;
+                    default:
+                        console.warn('Unknown DOM update type: %s', data.update_type);
+                }
+                break;
+            default:
+                console.warn('Unknown data type: %s', data.type);
+        }
+    });
+
+    socket.addEventListener('open', function (event) {
+        console.log('Websocket opened');
+        retried = 0;
+    }.bind(this));
+
+    this.send = function(data) {
+        if (debug) {
+            console.log('WS out ==> ', data);
+        }
+        socket.send(JSON.stringify(data));
+    };
+
+    function notifyRetry(timeout, retryCb) {
+        /* Show a notification dialog informing the user that server can't be reach
+         * and call retryCb after timeout seconds.
+         * A "retry now" link allows to retry immediately"
+         *
+         * @param timeout(int): delay before retrying, in seconds
+         * @param retryCb(function): function to call when retrying
+         */
+        var startTime = Date.now() / 1000;
+        var retryIntervalID;
+        var notif = document.createElement("div");
+        notif.setAttribute('class', 'notification retry');
+        //FIXME: we use English without translation for now, must be changed when we can use gettext in Libervia pages
+        notif.innerHTML = "<p>Can't reach the server, retrying in <span id='retry_counter'></span> seconds</p><p><a id='retry_now'>retry now</a></p>";
+        document.body.appendChild(notif);
+        var retryCounter = document.getElementById('retry_counter');
+        retryCounter.textContent = timeout;
+
+        var retry = function () {
+            clearInterval(retryIntervalID);
+            notif.parentNode.removeChild(notif);
+            retryCb();
+        };
+
+        var updateTimer = function () {
+            var elapsed = Math.floor(Date.now() / 1000 - startTime);
+            var remaining = timeout - elapsed;
+            if (remaining < 0) {
+                retry();
+            }
+            else {
+                retryCounter.textContent = remaining;
+            }
+        };
+
+        var retryNow = document.getElementById('retry_now');
+        retryNow.addEventListener('click', function(){
+            retry();
+        });
+
+        retryIntervalID = setInterval(updateTimer, 1000);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/ticket/create.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,25 @@
+{# create a new ticket #}
+
+{% set category_menu = [('tickets_list', url_tickets_list)] %}
+{% extends 'base/base.html' %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+{% import 'input/xmlui.html' as xmlui with context %}
+
+{% block body %}
+<div class='instructions'>
+    <p><span class="box">{% trans app_name=C.APP_NAME%}This page allows you to report an issue or ask/suggest a new feature for {{app_name}}{% endtrans %}</span></p>
+</div>
+<div class="create single ticket box">
+{% call form.form() %}
+    {{ xmlui.generate(new_ticket_xmlui,
+                      attributes = {'title': {'required': 'required',
+                                              'placeholder': _("Short description of your issue/request")},
+                                    'body': {'required': 'required',
+                                             'placeholder': _("Please describe your issue/request with as much details as possible")},
+                                    'labels': {'placeholder': _("You can enter one or several labels separated by commas")},
+                                    })}}
+    {{ field.submit(_("Create ticket")) }}
+{% endcall %}
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/ticket/discover.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,25 @@
+{% extends 'base/base.html' %}
+{% import 'components/block.html' as block with context %}
+{% import 'components/images.html' as images with context %}
+{% import 'components/avatar.html' as avatar with context %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{% block body %}
+{{ icon_defs('clipboard') }}
+<p class="instructions--head">
+    {% trans %}
+    Please select a tickets tracker
+    {% endtrans %}
+</p>
+{% if tickets_trackers is defined %}
+    <div class="disco_tickets">
+        {{block.disco_icon_grid(tickets_trackers, 'clipboard')}}
+    </div>
+{% endif %}
+
+{% call form.form(class="form_single") %}
+    {{ field.text("jid", _("tickets tracker jid"), required=true)}}
+    {{ field.submit(_("Access")) }}
+{% endcall %}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/ticket/edit.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,22 @@
+{# edit an existing ticket #}
+
+{% set category_menu = [('tickets_list', url_tickets_list)] %}
+{% extends 'base/base.html' %}
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+{% import 'input/xmlui.html' as xmlui with context %}
+
+{% block body %}
+<div class="create single ticket box">
+{% call form.form() %}
+    {{ xmlui.generate(new_ticket_xmlui,
+                      attributes = {'title': {'required': 'required',
+                                              'placeholder': _("Short description of your issue/request")},
+                                    'body': {'required': 'required',
+                                             'placeholder': _("Please describe your issue/request with as much details as possible")},
+                                    'labels': {'placeholder': _("You can enter one or several labels separated by commas")},
+                                    })}}
+    {{ field.submit(_("Modify ticket")) }}
+{% endcall %}
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/ticket/item.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,51 @@
+{# display a single ticket
+
+    @variable item(xmlui_item): ticket to display
+    @variable comments(data_object.BlogItems): comments of the ticket
+    @variable comments_service(unicode): service for adding comments
+    @variable comments_node(unicode): node for adding comments
+#}
+
+{% set category_menu = [('tickets', url_tickets_list),
+                        ('ticket_new', url_tickets_new),
+                        ] %}
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% import 'input/xmlui.html' as xmlui with context %}
+{% import 'blog/macros.html' as blog with context %}
+{% import 'input/textbox.html' as textbox with context %}
+
+{% block title %}{{item|adv_format('[{value.widget_value.id}] {value.widget_value.title}') }}{% endblock %}
+
+{% block confirm_message %}
+    {% trans %}Your comment has been sent{% endtrans %}
+{% endblock confirm_message %}
+
+{% block body %}
+{{ icon_defs('pencil') }}
+<div id="{{ item.widget_value['id'] }}" class="view single ticket box">
+    {% if url_ticket_edit is defined %}
+        <p class="box__tools">
+            <a href="{{url_ticket_edit}}">
+            {{ icon('pencil', cls='icon--small') }}
+            edit
+            </a>
+        </p>
+    {% endif %}
+    {{ xmlui.generate(item,
+        form=false,
+        filters={'created': {'filters': ['date_fmt'], 'filters_args':[{'fmt': 'short'}]},
+                 'updated': {'filters': ['date_fmt'], 'filters_args':[{'fmt': 'short'}]},
+                 'body': {'filters': ['urlize'], 'filters_args':[{'nofollow': True, 'rel': 'noopener noreferrer'}]}},
+        )}}
+</div>
+{% if comments is defined %}
+<div id="blog_items">
+    {{ blog.show_items(comments|reverse, expanded=true) }}
+</div>
+{% endif %}
+{% if comments_node is defined %}
+    <div class="comment_post">
+        {{- textbox.comment_or_login(service=comments_service, node=comments_node) -}}
+    </div>
+{% endif %}
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/ticket/overview.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,16 @@
+{# display the list of tickets #}
+
+{% set category_menu = [('ticket_new', url_tickets_new)] %}
+{% extends 'base/base.html' %}
+{% import 'input/xmlui.html' as xmlui with context %}
+
+{% block body %}
+<div id="tickets" class="view tickets overview">
+    {{ xmlui.generate_list(tickets, (('id', _('Id')),
+                                     ('title', _('Title')),
+                                     ('labels', _('Labels'))),
+                          {'id': '[{value}]'},
+                           item_class_fields=['status', 'priority', 'severity'],
+                           on_click=on_ticket_click) }}
+</div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/default/ticket/tickets.html	Sat Jun 02 17:25:43 2018 +0200
@@ -0,0 +1,12 @@
+{% extends 'base/base.html' %}
+{% import 'input/xmlui.html' as xmlui with context %}
+
+{% block body %}
+<div id="tickets">
+    {% for ticket in tickets %}
+        <div class="ticket_full">
+            {{ xmlui.generate(ticket) }}
+        </div>
+    {% endfor %}
+</div>
+{% endblock body %}
--- a/setup.py	Sat Dec 30 14:13:09 2017 +0100
+++ b/setup.py	Sat Jun 02 17:25:43 2018 +0200
@@ -1,7 +1,7 @@
 #!/usr/bin/env python2
 # -*- coding: utf-8 -*-
 
-# SàT templates: collection of templates 
+# SàT templates: collection of templates
 # Copyright (C) 2017  Xavier Maillard (xavier@maillard.im)
 
 # This program is free software: you can redistribute it and/or modify
@@ -19,7 +19,7 @@
 
 import os
 import sys
-from setuptools import setup, find_packages
+from setuptools import setup
 
 base = None
 NAME = 'sat_templates'
@@ -27,14 +27,32 @@
 
 # https://stackoverflow.com/a/36693250
 
-def get_data_templates_files(directory):
+def get_package_data(directory):
     paths = []
     for (path, directories, filenames) in os.walk(directory):
-	for filename in filenames:
-		paths.append(os.path.join('..', path, filename))
+        for filename in filenames:
+            paths.append(os.path.join('..', path, filename))
     return paths
 
-data_templates_files = get_data_templates_files('default')
+
+with open(os.path.join(NAME, 'VERSION')) as f:
+    VERSION = f.read().strip()
+is_dev_version = VERSION.endswith('D')
+
+
+def sat_templates_dev_version():
+    """Use mercurial data to compute version"""
+    def version_scheme(version):
+        return VERSION.replace('D', '.dev0')
+
+    def local_scheme(version):
+        return "+{rev}.{distance}".format(
+            rev=version.node[1:],
+            distance=version.distance)
+
+    return {'version_scheme': version_scheme,
+            'local_scheme': local_scheme}
+
 
 setup_info = dict(
     name=NAME,
@@ -43,13 +61,15 @@
     author_email='contact@salut-a-toi.org',
     url='https://salut-a-toi.org',
     classifiers=['Development Status :: 3 - Alpha',
+                 'Programming Language :: Python :: 2.7',
+                 'Programming Language :: Python :: 2 :: Only',
                  'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)',
-                 'Operating System :: POSIX :: Linux',
-                 'Topic :: Communications :: Chat'],
+                 ],
     install_requires=[],
+    setup_requires=['setuptools_scm'] if is_dev_version else [],
+    use_scm_version=sat_templates_dev_version if is_dev_version else False,
     packages=['sat_templates'],
-    package_dir={'sat_templates':'default'},
-    package_data={'': data_templates_files },
+    package_data={'sat_templates': get_package_data('sat_templates') },
     data_files=[(os.path.join(sys.prefix, 'share/locale/fr/LC_MESSAGES'), ['i18n/fr/LC_MESSAGES/sat.mo']),
                   ('share/doc/%s' % NAME, ['COPYING']),
                   ],