Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a5be30e84 | |||
| 4791b9ed16 | |||
| 884e00f730 | |||
| 6d1fe65f24 |
@@ -17,9 +17,9 @@ server {
|
|||||||
client_max_body_size 1M;
|
client_max_body_size 1M;
|
||||||
|
|
||||||
# Медиа файлы (загруженные пользователями)
|
# Медиа файлы (загруженные пользователями)
|
||||||
location /media/ {
|
# location /media/ {
|
||||||
alias /home/e-serg/docker-app/etpgrf-site/media/;
|
# alias /home/e-serg/docker-app/etpgrf-site/media/;
|
||||||
}
|
# }
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
# Проксируем на наш контейнер с etpgrf-site
|
# Проксируем на наш контейнер с etpgrf-site
|
||||||
|
|||||||
@@ -69,12 +69,16 @@ http {
|
|||||||
client_max_body_size 1M;
|
client_max_body_size 1M;
|
||||||
|
|
||||||
# --- КАСТОМНЫЕ СТРАНИЦЫ ОШИБОК ---
|
# --- КАСТОМНЫЕ СТРАНИЦЫ ОШИБОК ---
|
||||||
|
error_page 403 /403.html;
|
||||||
|
error_page 404 /404.html;
|
||||||
error_page 500 /500.html;
|
error_page 500 /500.html;
|
||||||
error_page 502 /502.html;
|
error_page 502 /502.html;
|
||||||
error_page 503 /503.html;
|
error_page 503 /503.html;
|
||||||
error_page 504 /504.html;
|
error_page 504 /504.html;
|
||||||
|
|
||||||
location = /500.html { root /app/public/static_collected; internal; } # файл будет сюда скопирован при сборке образа
|
location = /403.html { root /app/public/static_collected; internal; } # файл будет сюда скопирован при сборке образа
|
||||||
|
location = /404.html { root /app/public/static_collected; internal; }
|
||||||
|
location = /500.html { root /app/public/static_collected; internal; }
|
||||||
location = /502.html { root /app/public/static_collected; internal; }
|
location = /502.html { root /app/public/static_collected; internal; }
|
||||||
location = /503.html { root /app/public/static_collected; internal; }
|
location = /503.html { root /app/public/static_collected; internal; }
|
||||||
location = /504.html { root /app/public/static_collected; internal; }
|
location = /504.html { root /app/public/static_collected; internal; }
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ services:
|
|||||||
sh -c "python etpgrf_site/manage.py migrate --noinput &&
|
sh -c "python etpgrf_site/manage.py migrate --noinput &&
|
||||||
python etpgrf_site/manage.py collectstatic --noinput &&
|
python etpgrf_site/manage.py collectstatic --noinput &&
|
||||||
cp /app/etpgrf_site/typograph/templates/500.html /app/public/static_collected/500.html &&
|
cp /app/etpgrf_site/typograph/templates/500.html /app/public/static_collected/500.html &&
|
||||||
|
cp /app/etpgrf_site/typograph/templates/404.html /app/public/static_collected/404.html &&
|
||||||
|
cp /app/etpgrf_site/typograph/templates/typograph/403.html /app/public/static_collected/403.html &&
|
||||||
gunicorn --bind 0.0.0.0:8000 --chdir /app/etpgrf_site etpgrf_site.wsgi"
|
gunicorn --bind 0.0.0.0:8000 --chdir /app/etpgrf_site etpgrf_site.wsgi"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
@@ -59,7 +61,7 @@ services:
|
|||||||
# Статика (общий том)
|
# Статика (общий том)
|
||||||
- static_volume:/app/public/static_collected
|
- static_volume:/app/public/static_collected
|
||||||
# Медиа (папка media должна быть создана на хосте)
|
# Медиа (папка media должна быть создана на хосте)
|
||||||
- ./media:/app/public/media
|
- ./media:/app/media
|
||||||
|
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
@@ -78,7 +80,7 @@ services:
|
|||||||
# Конфиг берем из репозитория
|
# Конфиг берем из репозитория
|
||||||
- ./config/nginx/etpgrf--internal-nginx.conf:/etc/nginx/nginx.conf:ro
|
- ./config/nginx/etpgrf--internal-nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
- static_volume:/app/public/static_collected
|
- static_volume:/app/public/static_collected
|
||||||
- ./media:/app/public/media
|
- ./media:/app/media
|
||||||
|
|
||||||
# Внешний порт. Если у тебя на хосте уже есть Nginx (прокси),
|
# Внешний порт. Если у тебя на хосте уже есть Nginx (прокси),
|
||||||
# то можно пробросить на 127.0.0.1:8000 или использовать внутреннюю сеть.
|
# то можно пробросить на 127.0.0.1:8000 или использовать внутреннюю сеть.
|
||||||
|
|||||||
18
etpgrf_site/blog/migrations/0003_alter_post_excerpt.py
Normal file
18
etpgrf_site/blog/migrations/0003_alter_post_excerpt.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-30 16:47
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('blog', '0002_alter_post_is_published_alter_post_post_type_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='post',
|
||||||
|
name='excerpt',
|
||||||
|
field=models.TextField(help_text='Отображается в списке постов. Если оставить пустым, будет взято начало контента.', verbose_name='Краткое описание (тизер)'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -50,7 +50,6 @@ class Post(models.Model):
|
|||||||
)
|
)
|
||||||
excerpt = models.TextField(
|
excerpt = models.TextField(
|
||||||
verbose_name="Краткое описание (тизер)",
|
verbose_name="Краткое описание (тизер)",
|
||||||
blank=True,
|
|
||||||
help_text="Отображается в списке постов. Если оставить пустым, будет взято начало контента."
|
help_text="Отображается в списке постов. Если оставить пустым, будет взято начало контента."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
{% extends 'typograph/base.html' %}
|
{% extends 'typograph/base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load typograph_extras %}
|
||||||
|
|
||||||
{% block title %}{{ page.seo_title|default:page.title }} — ETPGRF{% endblock %}
|
|
||||||
{% block description %}{{ page.seo_description|default:page.content|striptags|truncatechars:160 }}{% endblock %}
|
|
||||||
{% block keywords %}{{ page.seo_keywords|default:'типограф, типографика, онлайн типограф, подготовка текста для веба, html типограф, неразрывные пробелы, кавычки елочки, длинное тире, очистка текста от мусора, интернет верстка, муравьев' }} seo_keywords {% endblock %}
|
{% block title %}{% if page.seo_title %}{{ page.seo_title }}{% else %}{{ page.title|striptags|unescape|safe }}{% endif %} — ETPGRF{% endblock %}
|
||||||
{% block og_title %}{{ page.seo_title|default:page.title }}{% endblock %}
|
{% block description %}{% if page.seo_description %}{{ page.seo_description }}{% else %}{{ page.excerpt|striptags|unescape|safe|truncatechars:160 }}{% endif %}{% endblock %}
|
||||||
{% block og_description %}{{ page.seo_description|default:page.content|striptags|truncatechars:160 }}{% endblock %}
|
{% block keywords %}{% if page.seo_keywords %}{{ post.seo_keywords }}{% else %}типограф, типографика, блог типограф, онлайн типограф, подготовка текста для веба, html типограф, неразрывные пробелы, кавычки елочки, длинное тире, очистка текста от мусора, интернет верстка, муравьев, лебедев{% endif %}{% endblock %}
|
||||||
|
|
||||||
|
{% block og_title %}{% if page.seo_title %}{{ page.seo_title }}{% else %}{{ page.title|striptags|unescape|safe }}{% endif %}{% endblock %}
|
||||||
|
{% block og_description %}{% if page.seo_description %}{{ page.seo_description }}{% else %}{{ page.excerpt|safe|striptags|unescape|truncatechars:160 }}{% endif %}{% endblock %}
|
||||||
{% block og_image %}{% if page.image %}{{ request.scheme }}://{{ request.get_host }}{{ page.image.url }}{% else %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endif %}{% endblock %}
|
{% block og_image %}{% if page.image %}{{ request.scheme }}://{{ request.get_host }}{{ page.image.url }}{% else %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endif %}{% endblock %}
|
||||||
{% block twitter_title %}{{ page.seo_title|default:page.title }}{% endblock %}
|
{% block twitter_title %}{% if page.seo_title %}{{ page.seo_title }}{% else %}{{ page.title|striptags|unescape|safe }}{% endif %}{% endblock %}
|
||||||
{% block twitter_description %}{{ page.seo_description|default:page.content|striptags|truncatechars:160 }}{% endblock %}
|
{% block twitter_description %}{% if page.seo_description %}{{ page.seo_description }}{% else %}{{ page.excerpt|striptags|unescape|safe|truncatechars:160 }}{% endif %}{% endblock %}
|
||||||
{% block twitter_image %}{% if page.image %}{{ request.scheme }}://{{ request.get_host }}{{ page.image.url }}{% else %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endif %}{% endblock %}
|
{% block twitter_image %}{% if page.image %}{{ request.scheme }}://{{ request.get_host }}{{ page.image.url }}{% else %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endif %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -17,33 +21,28 @@
|
|||||||
{# Левая колонка: Дата и Картинка #}
|
{# Левая колонка: Дата и Картинка #}
|
||||||
<div class="col-lg-2 align-self-start text-end mb-4">
|
<div class="col-lg-2 align-self-start text-end mb-4">
|
||||||
<p class="small align-self-end">
|
<p class="small align-self-end">
|
||||||
<small class="bg-secondary bg-opacity-10 p-2 text-nowrap">
|
<small class="bg-secondary bg-opacity-10 p-2 text-nowrap">{{ page.published_at|date:"d.M.Y"|lower }}</small>
|
||||||
{{ page.published_at|date:"d.M.Y"|lower }}
|
|
||||||
</small>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{% if post.image %}
|
|
||||||
<img src="{{ post.image.url }}" class="w-100" alt="{{ post.title|safe }}" />
|
|
||||||
{% else %}
|
|
||||||
<img src="{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}" class="w-100" alt="{{ post.title|safe }}" />
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
</p>
|
||||||
|
<p>{% if page.image %}
|
||||||
|
<img src="{{ page.image.url }}" class="w-100" alt="{{ page.image|striptags|unescape|safe }}"/>
|
||||||
|
{% else %}<img src="{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}" class="w-100" alt="{{ page.image|striptags|unescape|safe }}"/>
|
||||||
|
{% endif %}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Правая колонка: Контент #}
|
{# Правая колонка: Контент #}
|
||||||
<div class="col-lg-10 border-start ps-lg-4 post-page-content">
|
<div class="col-lg-10 border-start ps-lg-4 post-page-content">
|
||||||
|
|
||||||
<h1 class="display-4 mb-4">{{ page.title|safe }}</h1>
|
<h1 class="display-4 mb-4">{{ page.title|safe }}</h1>
|
||||||
|
|
||||||
{% if page.excerpt %}
|
{% if page.excerpt %}
|
||||||
<div class="lead bg-secondary bg-opacity-10 p-3 rounded">
|
<div class="lead bg-secondary bg-opacity-10 p-3 rounded">
|
||||||
{{ page.excerpt|safe }}
|
{{ page.excerpt|safe }}
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="page-content mt-4">
|
|
||||||
{{ page.content|safe }}
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="page-content mt-4">
|
||||||
|
{{ page.content|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,58 +1,53 @@
|
|||||||
{% extends 'typograph/base.html' %}
|
{% extends 'typograph/base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load typograph_extras %}
|
||||||
|
|
||||||
{% block title %}{{ post.seo_title|default:post.title }} — ETPGRF{% endblock %}
|
{% block title %}{% if post.seo_title %}{{ post.seo_title }}{% else %}{{ post.title|striptags|unescape|safe }}{% endif %} — ETPGRF{% endblock %}
|
||||||
{% block description %}{{ post.seo_description|default:post.excerpt|default:post.content|striptags|truncatechars:160 }}{% endblock %}
|
{% block description %}{% if post.seo_description %}{{ post.seo_description }}{% else %}{{ post.excerpt|striptags|unescape|safe|truncatechars:160 }}{% endif %}{% endblock %}
|
||||||
{% block og_title %}{{ post.seo_title|default:post.title }}{% endblock %}
|
{% block keywords %}{% if post.seo_keywords %}{{ post.seo_keywords }}{% else %}типограф, типографика, блог типограф, онлайн типограф, подготовка текста для веба, html типограф, неразрывные пробелы, кавычки елочки, длинное тире, очистка текста от мусора, интернет верстка, муравьев, лебедев{% endif %}{% endblock %}
|
||||||
{% block og_description %}{{ post.seo_description|default:post.excerpt|default:post.content|striptags|truncatechars:160 }}{% endblock %}
|
|
||||||
|
{% block og_title %}{% if post.seo_title %}{{ post.seo_title }}{% else %}{{ post.title|striptags|unescape|safe }}{% endif %}{% endblock %}
|
||||||
|
{% block og_description %}{% if post.seo_description %}{{ post.seo_description }}{% else %}{{ post.excerpt|striptags|unescape|safe|truncatechars:160 }}{% endif %}{% endblock %}
|
||||||
{% block og_image %}{% if post.image %}{{ request.scheme }}://{{ request.get_host }}{{ post.image.url }}{% else %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endif %}{% endblock %}
|
{% block og_image %}{% if post.image %}{{ request.scheme }}://{{ request.get_host }}{{ post.image.url }}{% else %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endif %}{% endblock %}
|
||||||
{% block twitter_title %}{{ post.seo_title|default:post.title }}{% endblock %}
|
{% block twitter_title %}{% if post.seo_title %}{{ post.seo_title }}{% else %}{{ post.title|striptags|unescape|safe }}{% endif %}{% endblock %}
|
||||||
{% block twitter_description %}{{ post.seo_description|default:post.excerpt|default:post.content|striptags|truncatechars:160 }}{% endblock %}
|
{% block twitter_description %}{% if post.seo_description %}{{ post.seo_description }}{% else %}{{ post.excerpt|striptags|unescape|safe|truncatechars:160 }}{% endif %}{% endblock %}
|
||||||
{% block twitter_image %}{% if post.image %}{{ request.scheme }}://{{ request.get_host }}{{ post.image.url }}{% else %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endif %}{% endblock %}
|
{% block twitter_image %}{% if post.image %}{{ request.scheme }}://{{ request.get_host }}{{ post.image.url }}{% else %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endif %}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
{# Левая колонка: Дата и Картинка #}
|
{# Левая колонка: Дата и Картинка #}
|
||||||
<div class="col-lg-2 align-self-start text-end mb-4">
|
<div class="col-lg-2 align-self-start text-end mb-4">
|
||||||
<p class="small align-self-end">
|
<p class="small align-self-end">
|
||||||
<small class="bg-secondary bg-opacity-10 p-2 text-nowrap">
|
<small class="bg-secondary bg-opacity-10 p-2 text-nowrap">
|
||||||
{{ post.published_at|date:"d.M.Y"|lower }}
|
{{ post.published_at|date:"d.M.Y"|lower }}
|
||||||
</small>
|
</small>
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{% if post.image %}
|
|
||||||
<img src="{{ post.image.url }}" class="w-100" alt="{{ post.title }}" />
|
|
||||||
{% else %}
|
|
||||||
<img src="{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}" class="w-100" alt="{{ post.title }}" />
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
</p>
|
||||||
|
<p>{% if post.image %}
|
||||||
|
<img src="{{ post.image.url }}" class="w-100" alt="{{ post.title|striptags|unescape|safe }}"/>
|
||||||
|
{% else %}<img src="{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}" class="w-100" alt="{{ post.title|striptags|unescape|safe }}"/>
|
||||||
|
{% endif %}</p>
|
||||||
|
|
||||||
<div class="d-none d-lg-block mt-5">
|
<div class="d-none d-lg-block mt-5">
|
||||||
<a href="{% url 'blog:post_list' %}" class="btn btn-sm btn-outline-secondary w-100">← В блог</a>
|
<a href="{% url 'blog:post_list' %}" class="btn btn-sm btn-outline-secondary w-100">← В блог</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Правая колонка: Контент #}
|
{# Правая колонка: Контент #}
|
||||||
<div class="col-lg-10 border-start ps-lg-4 post-page-content">
|
<div class="col-lg-10 border-start ps-lg-4 post-page-content">
|
||||||
|
<h1 class="display-4 mb-4">{{ post.title|safe }}</h1>
|
||||||
|
|
||||||
<h1 class="display-4 mb-4">{{ post.title }}</h1>
|
{% if post.excerpt %}<div class="lead bg-secondary bg-opacity-10 p-3 rounded">
|
||||||
|
{{ post.excerpt|safe }}
|
||||||
|
</div>{% endif %}
|
||||||
|
|
||||||
{% if post.excerpt %}
|
<div class="post-content mt-4">
|
||||||
<p class="lead bg-secondary bg-opacity-10 p-3 rounded">
|
{{ post.content|safe }}
|
||||||
{{ post.excerpt|linebreaksbr }}
|
</div>
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="post-content mt-4">
|
<div class="d-lg-none mt-5 border-top pt-3">
|
||||||
{{ post.content|safe }}
|
<a href="{% url 'blog:post_list' %}" class="btn btn-outline-secondary">← Назад к списку статей</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-lg-none mt-5 border-top pt-3">
|
|
||||||
<a href="{% url 'blog:post_list' %}" class="btn btn-outline-secondary">← Назад к списку статей</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -3,15 +3,11 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
{# --- SEO & Meta Tags --- #}<title>{% block title %}ETPGRF — единая типографика для веба{% endblock %}</title>
|
||||||
{# --- SEO & Meta Tags --- #}
|
|
||||||
<title>{% block title %}ETPGRF — единая типографика для веба{% endblock %}</title>
|
|
||||||
<meta name="description" content="{% block description %}Бесплатный онлайн-типограф для подготовки текстов к публикации в вебе. Расставка неразрывных пробелов, правильных кавычек («ёлочки»), тире, спецсимволы, отбивка, компоновка, висячая пунктуация. Идеально для верстки сайтов, статей и постов.{% endblock %}">
|
<meta name="description" content="{% block description %}Бесплатный онлайн-типограф для подготовки текстов к публикации в вебе. Расставка неразрывных пробелов, правильных кавычек («ёлочки»), тире, спецсимволы, отбивка, компоновка, висячая пунктуация. Идеально для верстки сайтов, статей и постов.{% endblock %}">
|
||||||
<meta name="keywords" content="{% block keywords %}типограф, типографика, онлайн типограф, подготовка текста для веба, html типограф, неразрывные пробелы, кавычки елочки, длинное тире, очистка текста от мусора, интернет верстка, муравьев{% endblock %}">
|
<meta name="keywords" content="{% block keywords %}типограф, типографика, онлайн типограф, подготовка текста для веба, html типограф, неразрывные пробелы, кавычки елочки, длинное тире, очистка текста от мусора, интернет верстка, муравьев{% endblock %}">
|
||||||
<meta name="author" content="Sergei Erjemin">
|
<meta name="author" content="Sergei Erjemin">
|
||||||
|
{# --- Open Graph (Facebook, VK, LinkedIn, Telegram) --- #}<meta property="og:type" content="website" />
|
||||||
{# --- Open Graph (Facebook, VK, LinkedIn, Telegram) --- #}
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:site_name" content="ETPGRF" />
|
<meta property="og:site_name" content="ETPGRF" />
|
||||||
<meta property="og:url" content="{{ request.build_absolute_uri }}" />
|
<meta property="og:url" content="{{ request.build_absolute_uri }}" />
|
||||||
<meta property="og:title" content="{% block og_title %}ETPGRF — единая типографика для веба{% endblock %}" />
|
<meta property="og:title" content="{% block og_title %}ETPGRF — единая типографика для веба{% endblock %}" />
|
||||||
@@ -19,15 +15,11 @@
|
|||||||
<meta property="og:image" content="{% block og_image %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endblock %}" />
|
<meta property="og:image" content="{% block og_image %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endblock %}" />
|
||||||
<meta property="og:image:width" content="1200" />
|
<meta property="og:image:width" content="1200" />
|
||||||
<meta property="og:image:height" content="630" />
|
<meta property="og:image:height" content="630" />
|
||||||
|
{# --- Twitter Cards (X) --- #}<meta name="twitter:card" content="summary_large_image">
|
||||||
{# --- Twitter Cards (X) --- #}
|
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
|
||||||
<meta name="twitter:title" content="{% block twitter_title %}ETPGRF — единая типографика для веба{% endblock %}" />
|
<meta name="twitter:title" content="{% block twitter_title %}ETPGRF — единая типографика для веба{% endblock %}" />
|
||||||
<meta name="twitter:description" content="{% block twitter_description %}Сделайте ваш текст профессиональным и готовым к публикации в интернете за один клик.{% endblock %}" />
|
<meta name="twitter:description" content="{% block twitter_description %}Сделайте ваш текст профессиональным и готовым к публикации в интернете за один клик.{% endblock %}" />
|
||||||
<meta name="twitter:image" content="{% block twitter_image %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endblock %}" />
|
<meta name="twitter:image" content="{% block twitter_image %}{{ request.scheme }}://{{ request.get_host }}{% static 'img/etpgrf-logo-for-fb-vk-x.gif' %}{% endblock %}" />
|
||||||
|
{# --- Favicons --- #}<link rel="icon" href="{{ request.scheme }}://{{ request.get_host }}{% static 'favicon.ico' %}" type="image/x-icon" />
|
||||||
{# --- Favicons --- #}
|
|
||||||
<link rel="icon" href="{{ request.scheme }}://{{ request.get_host }}{% static 'favicon.ico' %}" type="image/x-icon" />
|
|
||||||
<link rel="icon" type="image/png" href="{% static 'favicon-96x96.png' %}" />
|
<link rel="icon" type="image/png" href="{% static 'favicon-96x96.png' %}" />
|
||||||
<link rel="icon" href="{% static 'favicon-light.svg' %}" type="image/svg+xml" media="(prefers-color-scheme: light)" />
|
<link rel="icon" href="{% static 'favicon-light.svg' %}" type="image/svg+xml" media="(prefers-color-scheme: light)" />
|
||||||
<link rel="icon" href="{% static 'favicon-dark.svg' %}" type="image/svg+xml" media="(prefers-color-scheme: dark)" />
|
<link rel="icon" href="{% static 'favicon-dark.svg' %}" type="image/svg+xml" media="(prefers-color-scheme: dark)" />
|
||||||
@@ -87,8 +79,7 @@
|
|||||||
{# Футер #}<footer class="footer mt-auto py-2 mt-4">
|
{# Футер #}<footer class="footer mt-auto py-2 mt-4">
|
||||||
<div class="container d-flex justify-content-between align-items-center">
|
<div class="container d-flex justify-content-between align-items-center">
|
||||||
<span class="text-muted small nowrap me-2">© Sergei Erjemin, 2025–{% now 'Y' %}.</span>
|
<span class="text-muted small nowrap me-2">© Sergei Erjemin, 2025–{% now 'Y' %}.</span>
|
||||||
<nobr class="text-muted small mx-2"><i class="bi bi-tags me-1" title="Версия библиотеки etpgrf / Версия сайта"></i>v0.1.3 / v0.2.0
|
<nobr class="text-muted small mx-2"><i class="bi bi-tags me-1" title="Версия библиотеки etpgrf / Версия сайта"></i><a href="/changelog">v0.1.3 / v0.2.1</a></nobr>
|
||||||
</nobr>
|
|
||||||
{# Сводная статистика (HTMX) #}<span class="text-muted small ms-2" hx-get="{% url 'stats_summary' %}" hx-trigger="load">
|
{# Сводная статистика (HTMX) #}<span class="text-muted small ms-2" hx-get="{% url 'stats_summary' %}" hx-trigger="load">
|
||||||
...
|
...
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from django import template
|
from django import template
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
import html
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@@ -43,3 +44,22 @@ def humanize_num(value):
|
|||||||
|
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name='unescape')
|
||||||
|
def unescape_filter(value):
|
||||||
|
"""
|
||||||
|
Декодирует HTML-сущности ( -> ' ', — -> —)
|
||||||
|
и удаляет лишние пробелы и переводы строк.
|
||||||
|
Полезно для мета-тегов (title, description, og:title).
|
||||||
|
"""
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# 1. Декодируем сущности
|
||||||
|
text = html.unescape(str(value))
|
||||||
|
|
||||||
|
# 2. Удаляем лишние пробелы и переводы строк
|
||||||
|
# split() без аргументов разбивает по любым пробельным символам (\n, \t, space)
|
||||||
|
# " ".join(...) собирает обратно через один пробел
|
||||||
|
return " ".join(text.split())
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Футер */
|
/* Футер */
|
||||||
.footer {
|
footer.footer {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: 1rem 0;
|
padding: 1rem 0;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
@@ -185,6 +185,15 @@ body {
|
|||||||
color: var(--bs-navbar-color);
|
color: var(--bs-navbar-color);
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
footer.footer a {
|
||||||
|
color: var(--bs-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px dotted var(--bs-primary);
|
||||||
|
}
|
||||||
|
footer.footer a:hover {
|
||||||
|
border-bottom-style: solid;
|
||||||
|
color: var(--bs-link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
/* === ПЕРЕОПРЕДЕЛЕНИЕ КОМПОНЕНТОВ BOOTSTRAP === */
|
/* === ПЕРЕОПРЕДЕЛЕНИЕ КОМПОНЕНТОВ BOOTSTRAP === */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user