Zulip-archive proposal - Use jinja2 templating as config instead of telling the user to edit the internal code
- Currently the way of how zulip-archive's users to change the looks of their website is to fork the project then change the internal library, especially
lib/website.py
- This method of forking the project and then changing the code only to change how the rendered page will look like is I think would be unsustainable.
- For example if there is an update from the upstream like making url sanitizer more robust or security patch like https://github.com/zulip/zulip-archive/commit/5da606feac0fa69445de717c406f79dd81e82e59, the user can't update the code directly.
- This method of forking the project and then changing the code only to change how the rendered page will look like is I think would be unsustainable.
- I think it would be better to give the responsibility of handling the rendering part to a more reliable and mature library (such as Jinja2) rather than managing the implementation ourselves as zulip-archive becoming a more complex project.
- Use templating library like jinja2 to handle how the page will render rather than rendering the pages with a bunch of
outfile.write()
in the code.- It is possible for 1 template to be used by many pages with the use of conditional tags in the template library
- Blogger.com has used this technique from long time ago, https://ultimatebloggerguide.blogspot.com/2016/07/blogger-conditional-tags-for-page-types.html
- The alternative would be to split the template using functions provided by Jinja2, like
extends
,include
, but only 1 template file will be used as the main template
- The core code then will run the render function providing all of the data needed (like topic name, chat, url, etc), how it is positioned and how it looks will all be the responsibility of the template file provided by the user (we can set a default template when the user didn't provide anything though)
- It is possible for 1 template to be used by many pages with the use of conditional tags in the template library
- A YAML configuration
- The render data can also be customized through a yaml configuration
- For example when a user wants the topic list in the stream to be sorted in ascending/descending order based on date/number of topic
- User can specify a template path to use if they want to use another template than the default
- The render data can also be customized through a yaml configuration
Example template: Default template
<html>
# We can also make it modular
# Essentially we can use all of what Jinja2 provides
{% include 'header.jinja2' %}
<body>
# All of the data `page.type` `stream_name`, etc are provided by the renderer (e.g current `write_main_page()`)
{% if page.type == "index" %}
{% include index.jinja2 %}
{% elif page.type == "stream" %}
{% include stream.jinja2 %}
{% elif page.type == "topic" %}
<h2>Stream: <a href="{{stream_url}}">{{stream_name}}</a></h2>
<h3>Topic: <a href="{{topic_url}}">{{topic_name}}</a></h3>
<hr>
{% for chat_item in chats %}
<a name="{{ chat_item.id }}"></a>
<h4><a href="{{ chat_item.zulip_near_url }}" class="zl"><img src="{{ global_profile_image }}" alt="view this post on Zulip"></a> {{ chat_item.sender }} <a href="{{ chat_item.archive_url }}">({{ chat_item.sent_time }})</a>:</h4>
{{ chat_item.message_rendered }}
{% endfor %}
{% endif %}
<hr><p>Last updated: {{last_updated_date}}</p>
</body>
</html>
- Users can now just edit the jinja2 template that's used to render the html and submit the template to the software as configuration instead of editing the internal code
- This will make it easier for the users to upgrade zulip-archive from upstream because they won't have to fork the project
- We can finally distribute the binary (and the lib) through pip or host it in docker container but still have the convenience of configurability to the users
- Developer can develop the template easier because the rendered page is guaranteed to be similar to the template
- Mainly modifying the implementation of
write_main_page
,write_stream_topics
,write_topic_messages
inlib/website.py
from using for loops andoutfile.write()
to just pass the data to Jinja2 renderer- All of the data must be html-escaped to prevent injection
- Creating a default template with the CSS.
- I think it will be much easier to develop because the rendered page's html will be similar to the jinja2 template
- Document what data that can be used in the Jinja2 template
- Document how user can modify the template and inputting it to zulip-archive
- Make the design on how it will be configurable
- I'm currently thinking the user can place a directory like
/.zarchive_config
that contains:template/
main.jinja2
- // other jinja2 files provided that's needed by
main.jinja2
, but the main template that will be rendered ismain.jinja2
assets/
- contains js, css, and images
config.yaml
- I chose
yaml
because we can give comments in it - It contains the all of the config from
streams.yaml
and all config fromsettings.py
(except the html configs likepage_footer_html
because it is the responsibility of the jinja2 template now)
- I chose
- Then we can specify the config directory as parameter like
archive.py -c ~/.zarchive_config
- I'm currently thinking the user can place a directory like
- zulip/zulip-archive#62 (comment)
- Someone already asked a feature similar to this, but with mustache template
- https://mattecapu.github.io/ct-zulip-archive/stream/229111-general/topic/Dedication.html
- This archive has a reasonably simple yet "nicer" design
- zulip/zulip-archive#79
- This issue would be easy to solve if we used templating engine