Модуль ленты (feed): посты, комментарии, TinyMCE, санитизация
- Post (категории, важное/закреплено/опубликовано, автор) + PostComment; тело — HTML из TinyMCE, санитизация nh3 в save() (XSS), загрузка картинок отдельным endpoint с проверкой содержимого (data:-URI вырезаются) - Лента с полным выводом постов, фильтры, сворачивание длинных; детальная страница с комментариями; edit — автор/staff, delete — автор - Публикация постов в каналы Mattermost (mattermost_targets), сигналы - Тесты (40): публикация/черновики, права, санитизация, картинки, выравнивание, комментарии, лента/дашборд Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
{% load common_tags %}
|
||||
{# Карточка поста в ленте: полный вывод. Контекст: post, pinned #}
|
||||
<div style="{% if pinned %}background:linear-gradient(135deg, hsl(0 60% 97%) 0%, hsl(214 60% 97%) 100%);{% else %}background:var(--color-card);{% endif %} border-radius:0.75rem; border:1px solid var(--color-border); box-shadow:0 1px 2px rgb(0 0 0/0.05); padding:1.5rem; margin-bottom:1rem;">
|
||||
|
||||
<!-- Шапка: бейджи + дата -->
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; gap:1rem; margin-bottom:0.625rem;">
|
||||
<div style="display:flex; gap:0.5rem; align-items:center; flex-wrap:wrap;">
|
||||
{% if post.is_pinned %}
|
||||
<span style="font-size:0.6875rem; font-weight:600; text-transform:uppercase; letter-spacing:0.05em; color:var(--color-primary); background:hsl(0 72% 44% / 0.1); border-radius:999px; padding:0.1875rem 0.625rem;">📌 Закріплено</span>
|
||||
{% endif %}
|
||||
{% if post.is_important %}<span class="badge badge-danger">Важливо</span>{% endif %}
|
||||
<span class="badge {{ post.badge_class }}">{{ post.get_category_display }}</span>
|
||||
</div>
|
||||
<span style="font-size:0.75rem; color:var(--color-muted-foreground); white-space:nowrap; flex-shrink:0;">{{ post.created_at|date:"d.m.Y H:i" }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Заголовок -->
|
||||
<a href="/news/{{ post.pk }}/" hx-get="/news/{{ post.pk }}/" hx-target="#main-content" hx-push-url="/news/{{ post.pk }}/" hx-indicator="#loader"
|
||||
style="text-decoration:none;">
|
||||
<h2 style="font-size:1.0625rem; font-weight:600; color:var(--color-foreground); margin-bottom:0.75rem;">{{ post.title }}</h2>
|
||||
</a>
|
||||
|
||||
<!-- Тіло повністю (HTML санітизовано при збереженні); довгі пости згортаються -->
|
||||
<div class="post-collapse">
|
||||
<div class="post-body" style="font-size:0.9075rem; color:var(--color-foreground); line-height:1.65;">{{ post.body|safe }}</div>
|
||||
<div class="post-fade" {% if pinned %}style="background:linear-gradient(transparent, hsl(214 60% 97%));"{% endif %}></div>
|
||||
</div>
|
||||
<button type="button" class="post-toggle" onclick="feedTogglePost(this)"
|
||||
style="display:none; margin-bottom:1rem; background:none; border:none; cursor:pointer; font-size:0.8125rem; font-weight:500; color:var(--color-primary); padding:0;">Показати повністю ↓</button>
|
||||
|
||||
<!-- Футер: автор, коментарі, дії -->
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; gap:1rem; padding-top:0.875rem; border-top:1px solid var(--color-border);">
|
||||
<div style="display:flex; align-items:center; gap:0.5rem; font-size:0.75rem; color:var(--color-muted-foreground);">
|
||||
<div style="width:1.5rem; height:1.5rem; border-radius:50%; background:hsl(270 60% 55% / 0.12); color:hsl(270 60% 42%); display:flex; align-items:center; justify-content:center; font-size:0.5625rem; font-weight:700;">{{ post.author|initials }}</div>
|
||||
{{ post.author.get_full_name|default:"—" }}
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:0.375rem;">
|
||||
<a href="/news/{{ post.pk }}/" hx-get="/news/{{ post.pk }}/" hx-target="#main-content" hx-push-url="/news/{{ post.pk }}/" hx-indicator="#loader"
|
||||
class="btn btn-ghost" style="font-size:0.75rem; padding:0.25rem 0.625rem; text-decoration:none; display:inline-flex; align-items:center; gap:0.375rem;">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/></svg>
|
||||
{{ post.num_comments|default:0 }}
|
||||
</a>
|
||||
{% if post.can_edit %}
|
||||
<a href="/news/{{ post.pk }}/edit/" hx-get="/news/{{ post.pk }}/edit/" hx-target="#main-content" hx-push-url="/news/{{ post.pk }}/edit/" hx-indicator="#loader"
|
||||
class="btn btn-ghost" style="font-size:0.75rem; padding:0.25rem 0.625rem; text-decoration:none;">Редагувати</a>
|
||||
{% endif %}
|
||||
{% if post.can_delete %}
|
||||
<form hx-post="/news/{{ post.pk }}/delete/" hx-target="#main-content" hx-push-url="/news/" hx-indicator="#loader"
|
||||
hx-confirm="Видалити публікацію «{{ post.title }}»? Це незворотньо."
|
||||
style="display:inline; margin:0;">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-ghost" style="font-size:0.75rem; padding:0.25rem 0.625rem; color:var(--color-destructive);">Видалити</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user