changeset 413:0190a0d32909 default tip

Forum: Major redesign of forums: Forums have been redesigned. They follow the new general design with 2 or 3 panels, allowing to have directly a forum if one is found/set up, and a panel on the left to search/discover other ones. Categories have been rewritten to be usable with pubsub relationships, a XEP-0277 type node is used for topics, and each item has a comments node for the threads. The thread view is set in `forum/show_messages.html` template. It has a header with a search box and a button to (un)subscribe. Items are displayed with the same macros as for the blog items. Below a room is set for editor, tags and attachments. rel 463
author Goffi <goffi@goffi.org>
date Fri, 05 Sep 2025 21:54:09 +0200
parents c1d33d7e4b96
children
files sat_templates/templates/bulma/base/base.html sat_templates/templates/bulma/chat/attachment_preview.html sat_templates/templates/bulma/components/attachment_preview.html sat_templates/templates/bulma/forum/categories.html sat_templates/templates/bulma/forum/discover_panel.html sat_templates/templates/bulma/forum/forum.html sat_templates/templates/bulma/forum/overview.html sat_templates/templates/bulma/forum/show_messages.html sat_templates/templates/bulma/forum/show_topics.html sat_templates/templates/bulma/forum/subscribe_button.html sat_templates/templates/bulma/forum/view.html sat_templates/templates/bulma/forum/view_messages.html sat_templates/templates/bulma/forum/view_topics.html sat_templates/templates/bulma/input/field.html sat_templates/templates/bulma/static/chat.css sat_templates/templates/bulma/static/forum.css sat_templates/templates/bulma/static/styles.css
diffstat 17 files changed, 581 insertions(+), 178 deletions(-) [+]
line wrap: on
line diff
--- a/sat_templates/templates/bulma/base/base.html	Sun Aug 31 12:38:49 2025 +0200
+++ b/sat_templates/templates/bulma/base/base.html	Fri Sep 05 21:54:09 2025 +0200
@@ -80,6 +80,7 @@
         <script{{ {'src': script.src, 'type': script.type} | xmlattr }}>{{script.content|safe}}</script>
     {% endfor %}
 
+
     {% block favicon %}
         <link rel="icon" href="{{media_path}}icons/apps/64/sat.png">
     {% endblock favicon %}
--- a/sat_templates/templates/bulma/chat/attachment_preview.html	Sun Aug 31 12:38:49 2025 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-<div class="attachment-preview is-flex is-flex-direction-column is-align-items-center mx-2">
-    {% if file.mime_type|media_type_main == 'image' %}
-        <!-- Show image thumbnail -->
-        <div class="thumbnail-container">
-            <img src="{{ file.url }}" class="thumbnail-image" alt="{{file.name}}">
-        </div>
-    {% elif file.mime_type|media_type_main == 'video' %}
-        <!-- Show video thumbnail with play icon -->
-        <a href="{{file.url}}" class="is-video-thumbnail-wrapper is-wrapping photo_thumb_click">
-            <img class="is-photo-thumbnail" src="{{file.thumb_url}}">
-            <div class="media_overlay_play is-flex has-items-centered">
-                <span class="icon">
-                    <i class="icon-play-circled"></i>
-                </span>
-            </div>
-        </a>
-    {% else %}
-        <!-- Show a generic file icon if not an image or video -->
-        {{ icon('doc', cls='icon is-large') }}
-    {% endif %}
-    <div class="attachment-name mx-2 has-text-weight-bold" title="{{ file.name }}">{{ file.name }}</div>
-    <button class="delete is-small attachment-delete-button action_delete"></button>
-    {% if uploading %}
-        <progress class="progress is-info has-background-white" value="0" max="100">0%</progress>
-    {% endif %}
-</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/components/attachment_preview.html	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,26 @@
+<div class="attachment-preview is-flex is-flex-direction-column is-align-items-center mx-2">
+    {% if file.mime_type|media_type_main == 'image' %}
+        <!-- Show image thumbnail -->
+        <div class="thumbnail-container">
+            <img src="{{ file.url }}" class="thumbnail-image" alt="{{file.name}}">
+        </div>
+    {% elif file.mime_type|media_type_main == 'video' %}
+        <!-- Show video thumbnail with play icon -->
+        <a href="{{file.url}}" class="is-video-thumbnail-wrapper is-wrapping photo_thumb_click">
+            <img class="is-photo-thumbnail" src="{{file.thumb_url}}">
+            <div class="media_overlay_play is-flex has-items-centered">
+                <span class="icon">
+                    <i class="icon-play-circled"></i>
+                </span>
+            </div>
+        </a>
+    {% else %}
+        <!-- Show a generic file icon if not an image or video -->
+        {{ icon('doc', cls='icon is-large') }}
+    {% endif %}
+    <div class="attachment-name mx-2 has-text-weight-bold" title="{{ file.name }}">{{ file.name }}</div>
+    <button class="delete is-small attachment-delete-button action_delete"></button>
+    {% if uploading %}
+        <progress class="progress is-info has-background-white" value="0" max="100">0%</progress>
+    {% endif %}
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/forum/categories.html	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,112 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+
+{% macro generate_forums(forums_data, level=0) %}
+  <div class="forum-list">
+    {% for forum in forums_data %}
+      <div class="forum-item mb-4">
+        {% if forum.forum_type == "main_category" %}
+
+          {# Main Categories #}
+          <div class="forum-category box has-background-primary">
+          <div class="media {% if level == 0 %}is-aligned-center{% else %}is-aligned-left{% endif %}">
+            <div class="media-left">
+            <span class="icon {% if level == 0 %}is-large{% else %}is-medium{% endif %} has-text-white">
+              <i class="fas fa-layer-group {% if level == 0 %}fa-2x{% endif %}"></i>
+            </span>
+            </div>
+            <div class="media-content">
+              {% if 'http_url' in forum %}
+                <a href="{{ forum.http_url }}" class="has-text-white">
+                {% endif %}
+              <h3 class="title {% if level == 0 %}is-3{% else %}is-4{% endif %} has-text-white">
+                {{ forum.title }}
+              </h3>
+              {% if forum.description %}
+                <p class="subtitle is-5 has-text-white-bis">{{ forum.description }}</p>
+              {% endif %}
+              {% if 'http_url' in forum %}
+                  </a>
+                {% endif %}
+            </div>
+            {% if level == 0 %}
+              <div class="media-right">
+                <span class="tag is-light is-medium">
+                  <span class="icon"><i class="fas fa-folder"></i></span>
+                  <span>{{ forum.children|length }} Forums</span>
+                </span>
+              </div>
+            {% endif %}
+          </div>
+          </div>
+
+          {% if forum.children %}
+            <div class="forum-children pl-5 pr-3 mt-2">
+              {{ generate_forums(forum.children, level+1) }}
+            </div>
+          {% endif %}
+
+        {% else %}
+
+          {# Subcategories #}
+          <a {% if 'http_url' in forum %}href="{{ forum.http_url }}"{% endif %}
+                 class="forum-subcategory box is-hoverable">
+            <div class="media">
+              <div class="media-left">
+                <span class="icon is-medium has-text-primary">
+                  <i class="fas fa-folder fa-2x"></i>
+                </span>
+              </div>
+              <div class="media-content">
+                <div class="content">
+                  <strong class="title is-4 mb-1">{{ forum.title }}</strong>
+                  {% if forum.description %}
+                    <p class="subtitle is-6 has-text-grey">{{ forum.description }}</p>
+                  {% endif %}
+                </div>
+                <div class="forum-stats">
+                  {% if 'nb_items' in forum %}
+                    <span class="tag is-light is-rounded">
+                      <span class="icon"><i class="fas fa-comment"></i></span>
+                      <span>{{ forum.nb_items }} Topics</span>
+                    </span>
+                  {% endif %}
+                  {% if forum.last_activity %}
+                    <span class="tag is-light is-rounded ml-2">
+                      <span class="icon"><i class="fas fa-clock"></i></span>
+                      <span>{{ forum.last_activity|date_fmt('short', tz_name=tz_name)}}</span>
+                    </span>
+                  {% endif %}
+                </div>
+              </div>
+              <div class="media-right">
+                {% if 'http_url' in forum %}
+                  <span class="icon has-text-info">
+                    <i class="fas fa-chevron-right"></i>
+                  </span>
+                {% endif %}
+              </div>
+            </div>
+          </a>
+        {% endif %}
+      </div>
+    {% endfor %}
+  </div>
+{% endmacro %}
+
+{% block body %}
+  <div class="container forums px-4 py-2">
+    {% if not forums %}
+      <div class="message is-info">
+        <div class="message-body has-text-centered">
+          <span class="icon is-large mb-3">
+            <i class="fas fa-comments fa-2x"></i>
+          </span>
+          <h3 class="title is-4">{% trans %}No forums found{% endtrans %}</h3>
+          <p class="subtitle is-6">{% trans %}You may ask your service administrator to create some.{% endtrans %}</p>
+        </div>
+      </div>
+    {% else %}
+      {{ generate_forums(forums) }}
+    {% endif %}
+  </div>
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/forum/discover_panel.html	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,19 @@
+{% import 'input/form.html' as form with context %}
+{% import 'input/field.html' as field with context %}
+
+{{ icon_defs('magnifying-glass') }}
+
+<aside class="menu mt-4 px-4 has-text-light">
+    <p class="menu-label has-text-grey-light">{% trans %}Open Forum by Address{% endtrans %}</p>
+    {% call form.form() %}
+        {{ field.meta('type', 'forum_open') }}
+        <div class="field">
+            <p class="help">Enter a forum address (JID) to open it.</p>
+            {{ field.search("jid",
+                           icon_left="fa-solid fa-magnifying-glass",
+                           placeholder=_("Enter forum address…"),
+                           delete_button=true)}}
+            {{ field.submit(_("Go to Forum"), icon="magnifying-glass") }}
+        </div>
+    {% endcall %}
+</aside>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/forum/forum.html	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,15 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% set blog_page = True %}{% endif %}
+
+{% block body %}
+
+<div class="columns is-gapless blog-container is-full-height">
+    <!-- Left Panel -->
+    <div class="column is-2 left-sidebar has-background-dark" id="left_panel">
+        {% include 'forum/discover_panel.html' %}
+    </div>
+    <div class="column is-8 main-panel blog-main" id="main_panel">
+        {% include 'forum/categories.html' %}
+    </div>
+</div>
+
+{% endblock body %}
--- a/sat_templates/templates/bulma/forum/overview.html	Sun Aug 31 12:38:49 2025 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-{% extends 'base/base.html' %}
-
-{% macro generate_forums(forums_data, level=0) %}
-    {% if level == 0 %}
-        <section class="section">
-            {% for forum in forums_data %}
-                <div class="box content has-text-centered has-background-info mt-4">
-                    <h4 class="title has-text-white">{{ forum.title }}</h4>
-                    {% if 'short-desc' in forum %}
-                        <p class="subtitle is-size-7 is-italic has-text-white">{{ forum['short-desc'] }}</h5>
-                    {% endif %}
-                </div>
-                {% if 'sub-forums' in forum %}
-                    {{ generate_forums(forum['sub-forums'], level=level+1) }}
-                {% endif %}
-            {% endfor %}
-        </section>
-    {% else %}
-        <div class="forum forum__panel_{{panel_type}}">
-            {% for forum in forums_data %}
-                <div class="my-2 forum forum__cat_{{panel_type}} forum__level_{{level}}">
-                    {% if 'http_url' in forum %}
-                        <a href="{{forum['http_url']}}" class="box content x-is-hoverable">
-                    {% else %}
-                        <a class="box content">
-                    {% endif %}
-                        <h4 class="title">{{ forum.title }}</h4>
-                        {% if 'short-desc' in forum %}
-                            <p class="subtitle is-size-7 is-italic">{{ forum['short-desc'] }}</p>
-                        {% endif %}
-                    </a>
-                    {% if 'sub-forums' in forum %}
-                        {{ generate_forums(forum['sub-forums'], level=level+1) }}
-                    {% endif %}
-                </div>
-            {% endfor %}
-        </div>
-{% endif %}
-{% endmacro %}
-
-{% block body %}
-{% if not forums %}
-    <div class="message">
-        <div class="message-body">{% trans %}No forums found on this server!{% endtrans %}</div>
-    </div>
-{% else %}
-    <div class="container forums">
-        {{ generate_forums(forums) }}
-    </div>
-{% endif %}
-{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/forum/show_messages.html	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,76 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% import 'components/avatar.html' as avatar with context %}
+{% import 'blog/macros.html' as blog with context %}
+
+{% block body %}
+
+    <div class="panel-header px-4 py-2">
+        <div class="level is-mobile">
+            <div class="level-left">
+                <div class="level-item">
+                    <button class="button is-small" id="left_panel-toggle" aria-label="Toggle left panel">
+                        <span class="icon"><i class="fas fa-bars"></i></span>
+                    </button>
+                </div>
+                <div class="level-item">
+                    <h1 class="title is-5 mb-0 pt-1">{{ topic_title }}</h1>
+                </div>
+            </div>
+            <div class="level-right">
+                <div class="level-item">
+                    <div class="field has-addons">
+                        <div class="control">
+                            <input id="search_thread" class="input is-small" type="text" placeholder="{{ _('Search in this forum') }}">
+                        </div>
+                        <div class="control">
+                            <button id="search_thread_button" class="button is-info is-small">
+                                <span class="icon is-small">
+                                    <i class="fas fa-search"></i>
+                                </span>
+                            </button>
+                        </div>
+                    </div>
+                </div>
+                <div class="level-item">
+                    {% include 'forum/subscribe_button.html' %}
+
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <section id="forum_main_body" class="section is-flex is-flex-direction-column">
+        <div class="container is-max-desktop">
+            {# Messages Display #}
+            <section class="section has-background-white">
+                {{ blog.show_items(blog_items["items"], expanded=true) }}
+            </section>
+
+            {# New Message Section #}
+            <section class="section new-message-section">
+                <h2 class="subtitle is-4 mb-3">Add your reply</h2>
+                <div id="message_editor"></div>
+
+                {# Tags #}
+                <div id="tags_container" class="is-hidden">
+                    <label>Add you tags below</label>
+                    <input id="tags" type="text"  />
+                </div>
+                {# Attachments #}
+                <input id="forum-file-input" type="file" multiple="true" style="display: none" />
+
+                <div class="mt-4" id="attachments-area">
+                    <div id="forum-attachments" class="file-attachments is-flex is-flex-wrap-wrap">
+                    {# Attachments will be dynamically added here #}
+                    </div>
+                </div>
+
+                {# Submit Button #}
+                <div class="mt-4">
+                    <button class="button is-primary" id="send-message-button">Send Message</button>
+                </div>
+            </section>
+        </div>
+    </section>
+
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/forum/show_topics.html	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,97 @@
+{% import 'components/avatar.html' as avatar with context %}
+
+{% block body %}
+
+    <div class="panel-header px-4 py-2">
+        <div class="level is-mobile">
+            <div class="level-left">
+                <div class="level-item">
+                    <button class="button is-small" id="left_panel-toggle" aria-label="Toggle left panel">
+                        <span class="icon"><i class="fas fa-bars"></i></span>
+                    </button>
+                </div>
+                <div class="level-item">
+                    <h1 class="title is-5 mb-0 pt-1">{{ target_jid }}</h1>
+                </div>
+            </div>
+            <div class="level-right">
+                <div class="level-item">
+                    <div class="field has-addons">
+                        <div class="control">
+                            <input class="input is-small" type="text" placeholder="{{ _('Search topics…') }}">
+                        </div>
+                        <div class="control">
+                            <button class="button is-info is-small">
+                                <span class="icon is-small">
+                                    <i class="fas fa-search"></i>
+                                </span>
+                            </button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    {% if not blog_items or not blog_items['items'] %}
+        <div class="message">
+            <div class="message-body">
+                {% trans %}There is not message yet in this forum.{% endtrans %}
+                {% if profile %}
+                    {% trans %}You can start a topic of interest by filling the form below.{% endtrans %}
+                {% else %}
+                    {% trans %}You can login to create a new topic.{% endtrans %}
+                {% endif %}
+            </div>
+        </div>
+    {% else %}
+        <div class="topics-container px-3 pb-3">
+            {% for item in blog_items['items'] %}
+                {% if "http_url" in item %}
+                    <a class="topic-item box has-background-white-ter transition-all p-4 mb-3 is-clickable" href="{{ item.http_url }}">
+                {% endif %}
+                    <article class="media">
+                        <figure class="media-left mt-1">
+                            {{ avatar.avatar(item['author_jid']) }}
+                        </figure>
+                        <div class="media-content">
+                            <div class="columns is-mobile is-gapless">
+                                <div class="column">
+                                    <p class="title is-6 mb-1 has-text-black has-text-left">
+                                        {{ item.title | truncate(65) }}
+                                    </p>
+
+                                    <p class="is-size-7 has-text-grey mb-2">
+                                        <span >{{ item.author }}</span>
+                                        <span>—</span>
+                                        <span>{{ item.published | date_fmt('short') }}</span>
+                                        {% if item.nb_items is defined %}
+                                        <span class="icon-text mx-1 is-size-7">
+                                            <span class="icon is-small"><i class="fas fa-comment" aria-hidden="true"></i></span>
+                                            <span>{{ item.nb_items }}</span>
+                                        </span>
+                                        {% endif %}
+                                    </p>
+                                </div>
+
+                                {% if item.published != item.updated %}
+                                <div class="column is-narrow has-text-right">
+                                    <div class="tags has-addons">
+                                        <span class="tag is-light is-size-7">{{ _('Updated') }}</span>
+                                        <span class="tag is-info is-light is-size-7">
+                                            {{ item.updated | date_fmt('relative') }}
+                                        </span>
+                                    </div>
+                                </div>
+                                {% endif %}
+                            </div>
+                        </div>
+                    </article>
+                {% if "http_url" in item %}
+                    </a>
+                {% endif %}
+            {% endfor %}
+        </div>
+    {% endif %}
+
+{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/forum/subscribe_button.html	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,9 @@
+<button class="button is-primary is-small" id="subscribe-button">
+    {% if subscribed %}
+        <span class="icon"><i class="fas fa-bell-slash"></i></span>
+        <span>{% trans %}Unsubscribe{% endtrans %}</span>
+    {% else %}
+        <span class="icon"><i class="fas fa-bell"></i></span>
+        <span>{% trans %}Subscribe{% endtrans %}</span>
+    {% endif %}
+</button>
--- a/sat_templates/templates/bulma/forum/view.html	Sun Aug 31 12:38:49 2025 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
-{% set dates_format='relative' if single else 'short' %}
-{% import 'components/avatar.html' as avatar with context %}
-{% import 'blog/macros.html' as blog with context %}
-{% import 'input/textbox.html' as textbox with context %}
-{% import 'input/navigation.html' as navigation with context %}
-
-{% block body %}
-
-
-<section class="section has-background-white">
-    {{ blog.show_items(blog_items["items"], expanded=true) }}
-</section>
-
-<section class="section">
-    {{- textbox.comment_or_login(service=service, node=node, placeholder=_("Enter your message here")) -}}
-</section>
-
-
-{{ navigation.prev_next(_("older articles"), _("newer articles")) }}
-{% endblock body %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/forum/view_messages.html	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,24 @@
+{% if not embedded %}{% extends 'base/base.html' %}{% endif %}
+{% set dates_format='relative' if single else 'short' %}
+{% import 'components/avatar.html' as avatar with context %}
+{% import 'blog/macros.html' as blog with context %}
+{% import 'input/textbox.html' as textbox with context %}
+{% import 'input/navigation.html' as navigation with context %}
+
+{% block body %}
+
+{{ icon_defs(
+"ellipsis", "regular face-smile", "reply", "share", "star"
+) }}
+
+<div class="columns is-gapless blog-container is-full-height">
+    <!-- Left Panel -->
+    <div class="column is-2 left-sidebar has-background-dark" id="left_panel">
+        {% include 'forum/discover_panel.html' %}
+    </div>
+    <div class="column is-8 main-panel blog-main is-flex is-flex-direction-column" id="main_panel">
+        {% include 'forum/show_messages.html' %}
+    </div>
+</div>
+
+{% endblock body %}
--- a/sat_templates/templates/bulma/forum/view_topics.html	Sun Aug 31 12:38:49 2025 +0200
+++ b/sat_templates/templates/bulma/forum/view_topics.html	Fri Sep 05 21:54:09 2025 +0200
@@ -6,44 +6,13 @@
 
 {% block body %}
 
-    {% if not topics %}
-        <div class="message">
-            <div class="message-body">
-                {% 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>
-        </div>
-    {% endif %}
-
-    <section class="section has-background-white">
-        <nav class="level mb-4">
-            <div class="level-left">
-                <div class="level-item">
-                    {{ component.action_button(url_topic_new, _("New Topic")) }}
-                </div>
-            </div>
-        </nav>
-        <div class="has-background-white px-1 py-1">
-            {% for topic in topics|reverse %}
-                <div class="media has-items-vcentered">
-                    <div class="media-left">
-                        {{ avatar.avatar(topic.author) }}
-                    </div>
-                    <div class="media-content">
-                        <p class="is-size-5-desktop is-size-6-touch x-is-hoverable">
-                        <a href="{{topic.http_uri}}">
-                            {{topic.title}}
-                        </a>
-                        </p>
-                    </div>
-                </div>
-            {% endfor %}
-        </div>
-    </section>
-
-    {{ navigation.prev_next(_("older topics"), _("newer topics")) }}
+<div class="columns is-gapless blog-container is-full-height">
+    <!-- Left Panel -->
+    <div class="column is-2 left-sidebar has-background-dark" id="left_panel">
+        {% include 'forum/discover_panel.html' %}
+    </div>
+    <div class="column is-8 main-panel blog-main" id="main_panel">
+        {% include 'forum/show_topics.html' %}
+    </div>
+</div>
 {% endblock body %}
--- a/sat_templates/templates/bulma/input/field.html	Sun Aug 31 12:38:49 2025 +0200
+++ b/sat_templates/templates/bulma/input/field.html	Fri Sep 05 21:54:09 2025 +0200
@@ -1,6 +1,6 @@
 {# macros to create form fields #}
 
-{% macro field(type, name, label="", value=none, class="", control_class="",  help="", required=false, icon_left=none, icon_right=none, in_group=false, attrs=none) %}
+{% macro field(type, name, label="", value=none, class="", control_class="",  help="", required=false, icon_left=none, icon_right=none, in_group=false, attrs=none, delete_button=false) %}
     {# generic field
        "class" keyword can be used to add classes
        additional kwargs will be passed as attributes #}
@@ -12,17 +12,22 @@
         {% if label %}
             <label for="{{cur_id}}" class="label">{{label}}</label>
         {% endif %}
-        <div class="control{% if icon_left %} has-icons-left{% endif %}{% if icon_right %} has-icons-right{% endif %} {{control_class}}">
+        <div class="control{% if icon_left %} has-icons-left{% endif %}{% if icon_right or delete_button %} has-icons-right{% endif %} {{control_class}}">
             <input id="{{cur_id}}" class="input" type="{{type}}" name="{{name}}" {{"required" if required}}{{{'value': value}|xmlattr}}{{(attrs or {})|xmlattr}}>
             {% if icon_left %}
                 <span class="icon is-left">
                     {# we use <i> with font from CSS instead of SVG, because using directly SVG doesn't play way with Bulma's control #}
-                    <i class="icon-{{icon_left}}"></i>
+                    <i class="{{icon_left}}"></i>
                 </span>
             {% endif %}
             {% if icon_right %}
                 <span class="icon is-right">
-                    <i class="icon-{{icon_right}}"></i>
+                    <i class="{{icon_right}}"></i>
+                </span>
+            {% endif %}
+            {% if delete_button %}
+                <span class="icon is-right action_delete">
+                    <a role="button" class="delete is-small" aria-label="Delete"></a>
                 </span>
             {% endif %}
         </div>
@@ -77,8 +82,8 @@
     {{ field("text", name=name, label=label, value=value, class=class, control_class=control_class, help=help, required=required, icon_left=icon_left, icon_right=icon_right, attrs={'placeholder': placeholder, 'pattern': pattern, 'title': title, 'autocomplete': autocomplete}, in_group=in_group, caller=caller) }}
 {% endmacro %}
 
-{% macro search(name, label="", value=none, class="", control_class="", placeholder=none, help="", required=false, pattern=none, title=none, autocomplete=none, icon_left=none, icon_right=none, in_group=false) %}
-    {{ field("search", name=name, label=label, value=value, class=class, control_class=control_class, help=help, required=required, icon_left=icon_left, icon_right=icon_right, attrs={'placeholder': placeholder, 'pattern': pattern, 'title': title, 'autocomplete': autocomplete}, in_group=in_group, caller=caller) }}
+{% macro search(name, label="", value=none, class="", control_class="", placeholder=none, help="", required=false, pattern=none, title=none, autocomplete=none, icon_left=none, icon_right=none, in_group=false, delete_button=false) %}
+    {{ field("search", name=name, label=label, value=value, class=class, control_class=control_class, help=help, required=required, icon_left=icon_left, icon_right=icon_right, attrs={'placeholder': placeholder, 'pattern': pattern, 'title': title, 'autocomplete': autocomplete}, in_group=in_group, delete_button=delete_button, caller=caller) }}
 {% endmacro %}
 
 {% macro password(name, label="", value=none, required=false, minlength=none, icon_left=none, icon_right=none) %}
--- a/sat_templates/templates/bulma/static/chat.css	Sun Aug 31 12:38:49 2025 +0200
+++ b/sat_templates/templates/bulma/static/chat.css	Fri Sep 05 21:54:09 2025 +0200
@@ -105,11 +105,6 @@
     opacity: 1;
 }
 
-.attachments {
-    overflow-x: auto;
-    white-space: nowrap;
-}
-
 .message-core:hover, .message-core.has-popup-focus {
     background-color: #f5f5f5;
     box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
@@ -141,34 +136,6 @@
     transition: box-shadow 0.3s ease;
 }
 
-/* Attachments */
-
-.attachments {
-    opacity: 1;
-    max-height: 1000px;
-    transition: opacity 0.5s ease-in-out, max-height 0.5s ease-in-out, padding 0.5s ease-in-out, margin 0.5s ease-in-out;
-    overflow-x: auto;
-    overflow-y: hidden;
-    padding: 1.25rem 0;
-    margin: 0;
-}
-
-.attachments.is-contracted {
-    opacity: 0;
-    max-height: 0;
-    padding: 0;
-}
-
-.attachment-preview {
-    position: relative;
-    border: 1px solid #dbdbdb;
-    padding: 0.3rem 0.5rem;
-    border-radius: 4px;
-    background-color: #f5f5f5;
-    width: 9rem;
-    height: 6rem;
-}
-
 
 /* Reply to indicator */
 #reply-to {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_templates/templates/bulma/static/forum.css	Fri Sep 05 21:54:09 2025 +0200
@@ -0,0 +1,133 @@
+#body {
+    display: flex;
+    flex-grow: 1;
+    overflow: auto;
+}
+
+#forum_main_body {
+    overflow-y: auto;
+}
+
+.topics-container {
+    max-height: calc(100vh - 100px);
+    overflow-y: auto;
+}
+
+.topic-item {
+    border-radius: 8px;
+    border: 1px solid #e8e8e8;
+    transition: all 0.2s ease;
+    box-shadow: 0 2px 4px rgba(10, 10, 10, 0.03);
+}
+
+
+.topic-item:hover {
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
+    transform: translateY(-1px);
+    border-color: #3273dc;
+}
+
+.no-topics-message {
+    border-radius: 12px;
+    background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
+    max-width: 500px;
+    margin: 2rem auto 1rem !important;
+}
+
+
+.no-topics-message i.fa-comments {
+    color: #3273dc;
+    opacity: 0.7;
+}
+
+
+@media (max-width: 768px) {
+    .topic-item {
+        padding: 0.75rem !important;
+    }
+
+    .media-left {
+        margin-right: 0.5rem !important;
+    }
+
+    .title.is-6 {
+        font-size: 0.95rem !important;
+    }
+
+    .tags {
+        flex-wrap: wrap;
+    }
+}
+
+.transition-all {
+    transition: all 0.25s ease;
+}
+
+.tags.has-addons .tag:first-child {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+    border-right: none;
+    background: rgba(235, 245, 255, 0.5);
+}
+
+[data-theme="dark"] .tags.has-addons .tag:first-child {
+    background: rgba(0, 50, 100, 0.3);
+}
+
+.tags.has-addons .tag:last-child {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+}
+
+.new-message-section {
+    border-top: 1px solid #dbdbdb;
+    padding-top: 1.5rem;
+    margin-top: 1.5rem;
+}
+
+[data-theme="dark"] .topic-item {
+    background-color: #1a1a1a !important;
+    border-color: #333333;
+}
+
+[data-theme="dark"] .no-topics-message {
+    background: linear-gradient(135deg, #1a1d22 0%, #14171c 100%);
+    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
+}
+
+[data-theme="dark"] .no-topics-message i.fa-comments {
+    color: #48c0ff;
+}
+
+[data-theme="dark"] .new-message-section {
+    border-top: 1px solid #4a4a4a;
+}
+
+.extra-buttons {
+    display: inline-block;
+    vertical-align: middle;
+}
+
+/* Custom Quill toolbar buttons */
+.extra-btn-tag, .extra-btn-attachment {
+    background: none;
+    border: none;
+    cursor: pointer;
+    padding: 4px 8px;
+    border-radius: 3px;
+    display: inline-block;
+}
+
+.extra-btn-tag:hover, .extra-btn-attachment:hover {
+    background-color: #f0f0f0;
+}
+
+[data-theme="dark"] .extra-btn-tag:hover,
+[data-theme="dark"] .extra-btn-attachment:hover {
+    background-color: #4a4a4a;
+}
+
+#message_editor.drag-over, #attachments-area.drag-over {
+    border-color: #3273dc;
+    background-color: rgba(50, 115, 220, 0.05);
+}
--- a/sat_templates/templates/bulma/static/styles.css	Sun Aug 31 12:38:49 2025 +0200
+++ b/sat_templates/templates/bulma/static/styles.css	Fri Sep 05 21:54:09 2025 +0200
@@ -64,6 +64,12 @@
     padding-top: 0.225em;
 }
 
+
+.is-text-content {
+    white-space: break-spaces;
+}
+
+
 .avatar {
     width: 40px;
     height: 40px;
@@ -85,7 +91,6 @@
     background-color: var(--selected-item-bg);
 }
 
-
 .collapsible-content {
     overflow: hidden;
     max-height: 0;
@@ -315,6 +320,49 @@
     transform: translateY(100%) !important;
 }
 
+.thumbnail-image {
+    max-height: 100%;
+}
+
+/* Tags */
+
+.tag_input {
+    display: inline-block;
+    width: 6rem;
+}
+
+/* Attachments */
+
+.attachments {
+    opacity: 1;
+    max-height: 1000px;
+    transition: opacity 0.5s ease-in-out, max-height 0.5s ease-in-out, padding 0.5s ease-in-out, margin 0.5s ease-in-out;
+    overflow-x: auto;
+    overflow-y: hidden;
+    padding: 1.25rem 0;
+    margin: 0;
+    white-space: nowrap;
+}
+
+.attachments.is-contracted {
+    opacity: 0;
+    max-height: 0;
+    padding: 0;
+}
+
+.attachment-preview {
+    position: relative;
+    border: 1px solid #dbdbdb;
+    padding: 0.3rem 0.5rem;
+    border-radius: 4px;
+    background-color: #f5f5f5;
+    margin: 0 1rem 0 0 !important;
+    width: 9rem;
+}
+
+.progress_finished progress {
+    opacity: 0;
+}
 
 /* Responsive */