mirror of
https://github.com/EDeev/deev.space.git
synced 2026-06-15 11:01:10 +03:00
376 lines
No EOL
16 KiB
HTML
376 lines
No EOL
16 KiB
HTML
{% extends 'wrapper.html' %}
|
|
{% load static %}
|
|
{% load custom_filters %}
|
|
|
|
|
|
{% block og_type %}article{% endblock %}
|
|
|
|
{% block extra_schema %}
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "BlogPosting",
|
|
"headline": "{{ article.title }}",
|
|
"description": "{{ article.excerpt|truncatewords:30 }}",
|
|
"author": {
|
|
"@type": "Person",
|
|
"name": "{{ article.author }}"
|
|
},
|
|
"datePublished": "{{ article.date|date:'c' }}",
|
|
"dateModified": "{{ article.updated_at|date:'c' }}",
|
|
{% if article.img %}
|
|
"image": "https://deev.space{{ article.img.url }}",
|
|
{% endif %}
|
|
"publisher": {
|
|
"@type": "Person",
|
|
"name": "{{ global_settings.owner_name }}"
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container">
|
|
<article class="article-full">
|
|
<!-- Breadcrumb -->
|
|
<nav class="breadcrumb" data-aos="fade-up" aria-label="Навигация">
|
|
<a href="{% url 'index' %}">Главная</a>
|
|
<span class="breadcrumb-separator"><i class="fas fa-chevron-right"></i></span>
|
|
<a href="{% url 'blog' %}">Блог</a>
|
|
{% if article.category %}
|
|
<span class="breadcrumb-separator"><i class="fas fa-chevron-right"></i></span>
|
|
<a href="{{ article.category.get_absolute_url }}">{{ article.category.name }}</a>
|
|
{% endif %}
|
|
<span class="breadcrumb-separator"><i class="fas fa-chevron-right"></i></span>
|
|
<span class="breadcrumb-current">{{ article.title|truncatewords:5 }}</span>
|
|
</nav>
|
|
|
|
<!-- Article Header -->
|
|
<header class="article-header" data-aos="fade-up" data-aos-delay="100">
|
|
{% if article.category %}
|
|
<a href="{{ article.category.get_absolute_url }}" class="article-category-badge">
|
|
<i class="{{ article.category.icon }}"></i>
|
|
{{ article.category.name }}
|
|
</a>
|
|
{% endif %}
|
|
|
|
<h1 class="article-full-title">{{ article.title }}</h1>
|
|
|
|
{% if article.sub_title %}
|
|
<p class="article-full-subtitle">{{ article.sub_title }}</p>
|
|
{% endif %}
|
|
|
|
<div class="article-meta-full">
|
|
<div class="article-author">
|
|
<span class="author-avatar">{{ article.author|first_letter }}</span>
|
|
<span class="author-name">{{ article.author }}</span>
|
|
</div>
|
|
<div class="article-meta-items">
|
|
<span class="meta-item">
|
|
<i class="far fa-calendar-alt"></i>
|
|
{{ article.date|date:"d.m.Y" }}
|
|
</span>
|
|
<span class="meta-item">
|
|
<i class="far fa-clock"></i>
|
|
{{ article.post|reading_time }} мин. чтения
|
|
</span>
|
|
<span class="meta-item">
|
|
<i class="far fa-eye"></i>
|
|
{{ article.views }} просмотров
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Featured Image (если включено показывать в статье) -->
|
|
{% if article.img and article.show_cover_in_article %}
|
|
<div class="article-featured-image" data-aos="fade-up" data-aos-delay="200">
|
|
<img src="{{ article.img.url }}" alt="{{ article.title }}">
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Article Content -->
|
|
<div class="article-body" data-aos="fade-up" data-aos-delay="300">
|
|
{{ article.post|safe }}
|
|
</div>
|
|
|
|
<!-- Image Gallery -->
|
|
{% if gallery_images %}
|
|
<section class="article-gallery" data-aos="fade-up">
|
|
<h3><i class="fas fa-images"></i> Изображения</h3>
|
|
{% if article.gallery_display_mode == 'carousel' %}
|
|
<div class="gallery-carousel" id="articleCarousel">
|
|
<div class="carousel-container">
|
|
{% for image in gallery_images %}
|
|
<div class="carousel-slide {% if forloop.first %}active{% endif %}">
|
|
<img src="{{ image.image.url }}" alt="{{ image.caption|default:article.title }}">
|
|
{% if image.caption %}
|
|
<p class="carousel-caption">{{ image.caption }}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% if gallery_images.count > 1 %}
|
|
<button class="carousel-btn carousel-prev" onclick="carouselPrev()">
|
|
<i class="fas fa-chevron-left"></i>
|
|
</button>
|
|
<button class="carousel-btn carousel-next" onclick="carouselNext()">
|
|
<i class="fas fa-chevron-right"></i>
|
|
</button>
|
|
<div class="carousel-dots">
|
|
{% for image in gallery_images %}
|
|
<span class="carousel-dot {% if forloop.first %}active{% endif %}" onclick="carouselGoTo({{ forloop.counter0 }})"></span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="gallery-collage">
|
|
{% for image in gallery_images %}
|
|
<div class="collage-item" onclick="openLightbox('{{ image.image.url }}', '{{ image.caption|default:'' }}')">
|
|
<img src="{{ image.image.url }}" alt="{{ image.caption|default:article.title }}">
|
|
{% if image.caption %}
|
|
<p class="collage-caption">{{ image.caption }}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- Attached Files -->
|
|
{% if attached_files %}
|
|
<section class="article-files" data-aos="fade-up">
|
|
<h3><i class="fas fa-paperclip"></i> Прикреплённые файлы</h3>
|
|
<div class="files-list">
|
|
{% for file in attached_files %}
|
|
<a href="{{ file.file.url }}" target="_blank" class="file-item" download>
|
|
<div class="file-icon">
|
|
<i class="fas fa-file-{{ file.file_extension|lower }}"></i>
|
|
</div>
|
|
<div class="file-info">
|
|
<span class="file-name">{{ file.title }}</span>
|
|
{% if file.description %}
|
|
<span class="file-desc">{{ file.description }}</span>
|
|
{% endif %}
|
|
<span class="file-meta">{{ file.file_extension }} • {{ file.file_size }}</span>
|
|
</div>
|
|
<div class="file-download">
|
|
<i class="fas fa-download"></i>
|
|
</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- Attached Links -->
|
|
{% if attached_links %}
|
|
<section class="article-links" data-aos="fade-up">
|
|
<h3><i class="fas fa-link"></i> Ссылки</h3>
|
|
<div class="links-grid">
|
|
{% for link in attached_links %}
|
|
<a href="{{ link.url }}" target="_blank" rel="noopener" class="link-card">
|
|
{% if link.preview_image %}
|
|
<div class="link-image">
|
|
<img src="{{ link.preview_image }}" alt="{{ link.title }}">
|
|
</div>
|
|
{% endif %}
|
|
<div class="link-content">
|
|
<h4 class="link-title">{{ link.title|default:link.url }}</h4>
|
|
{% if link.description %}
|
|
<p class="link-description">{{ link.description|truncatewords:20 }}</p>
|
|
{% endif %}
|
|
<span class="link-domain"><i class="fas fa-external-link-alt"></i> {{ link.domain }}</span>
|
|
</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- Article Footer -->
|
|
<footer class="article-footer-full" data-aos="fade-up">
|
|
<!-- Likes -->
|
|
<div class="article-reactions">
|
|
<span class="reactions-label">Оцените статью:</span>
|
|
<div class="reactions-buttons">
|
|
<button type="button"
|
|
class="vote-btn like {% if user_like.is_like %}active{% endif %}"
|
|
id="article-{{ article.id }}-like-btn"
|
|
onclick="toggleArticleLike({{ article.id }}, true)"
|
|
{% if not user.is_authenticated %}disabled title="Войдите, чтобы оценить"{% endif %}>
|
|
<i class="{% if user_like.is_like %}fas{% else %}far{% endif %} fa-thumbs-up"></i>
|
|
<span id="article-{{ article.id }}-likes">{{ article.likes_count }}</span>
|
|
</button>
|
|
<button type="button"
|
|
class="vote-btn dislike {% if user_like and not user_like.is_like %}active{% endif %}"
|
|
id="article-{{ article.id }}-dislike-btn"
|
|
onclick="toggleArticleLike({{ article.id }}, false)"
|
|
{% if not user.is_authenticated %}disabled title="Войдите, чтобы оценить"{% endif %}>
|
|
<i class="{% if user_like and not user_like.is_like %}fas{% else %}far{% endif %} fa-thumbs-down"></i>
|
|
<span id="article-{{ article.id }}-dislikes">{{ article.dislikes_count }}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Share -->
|
|
<div class="article-share">
|
|
<span class="share-label">Поделиться:</span>
|
|
<div class="share-buttons">
|
|
<a href="https://t.me/share/url?url=https://deev.space{{ article.get_absolute_url }}&text={{ article.title }}" target="_blank" rel="noopener" class="share-btn telegram" title="Поделиться в Telegram">
|
|
<i class="fab fa-telegram"></i>
|
|
</a>
|
|
<a href="https://vk.com/share.php?url=https://deev.space{{ article.get_absolute_url }}&title={{ article.title }}" target="_blank" rel="noopener" class="share-btn vk" title="Поделиться в VK">
|
|
<i class="fab fa-vk"></i>
|
|
</a>
|
|
<a href="https://twitter.com/intent/tweet?url=https://deev.space{{ article.get_absolute_url }}&text={{ article.title }}" target="_blank" rel="noopener" class="share-btn twitter" title="Поделиться в Twitter">
|
|
<i class="fab fa-twitter"></i>
|
|
</a>
|
|
<button type="button" class="share-btn copy" onclick="copyArticleLink()" title="Копировать ссылку">
|
|
<i class="fas fa-link"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<!-- Related Articles -->
|
|
{% if related_articles %}
|
|
<section class="related-articles" data-aos="fade-up">
|
|
<h3>Похожие статьи</h3>
|
|
<div class="related-grid">
|
|
{% for related in related_articles %}
|
|
<a href="{{ related.get_absolute_url }}" class="related-card">
|
|
{% if related.img %}
|
|
<img src="{{ related.img.url }}" alt="{{ related.title }}" class="related-image">
|
|
{% endif %}
|
|
<div class="related-content">
|
|
<h4>{{ related.title }}</h4>
|
|
<span class="related-date">{{ related.date|date:"d.m.Y" }}</span>
|
|
</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- Comments Section -->
|
|
{% if article.comments_enabled %}
|
|
<section class="comments-section" data-aos="fade-up" id="comments">
|
|
<h3 class="comments-title">
|
|
<i class="fas fa-comments"></i>
|
|
Комментарии
|
|
<span class="comments-count">({{ article.comments_count }})</span>
|
|
</h3>
|
|
|
|
<!-- Comment Form -->
|
|
{% if user.is_authenticated %}
|
|
<form class="comment-form" id="comment-form" onsubmit="event.preventDefault(); submitComment({{ article.id }});">
|
|
{% csrf_token %}
|
|
<div class="comment-form-header">
|
|
<span class="comment-avatar">{{ user.get_avatar_letter }}</span>
|
|
<span class="comment-author-name">{{ user.username }}</span>
|
|
</div>
|
|
<textarea name="content"
|
|
class="form-control"
|
|
placeholder="Напишите комментарий..."
|
|
rows="3"
|
|
required
|
|
maxlength="2000"></textarea>
|
|
<div class="comment-form-footer">
|
|
<span class="comment-hint">Поддерживается: <code><b></code>, <code><i></code>, <code><code></code></span>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-paper-plane"></i>
|
|
Отправить
|
|
</button>
|
|
</div>
|
|
</form>
|
|
{% else %}
|
|
<div class="comment-auth-prompt">
|
|
<p>Чтобы оставить комментарий, <a href="{% url 'login' %}?next={{ request.path }}#comments">войдите</a> или <a href="{% url 'register' %}?next={{ request.path }}#comments">зарегистрируйтесь</a>.</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Comments List -->
|
|
<div class="comments-list">
|
|
{% include 'includes/comments.html' with comments=comments %}
|
|
</div>
|
|
</section>
|
|
{% else %}
|
|
<div class="comments-disabled" data-aos="fade-up">
|
|
<i class="fas fa-comment-slash"></i>
|
|
<p>Комментарии к этой статье отключены</p>
|
|
</div>
|
|
{% endif %}
|
|
</article>
|
|
</div>
|
|
|
|
<!-- Lightbox -->
|
|
<div id="lightbox" class="lightbox" onclick="closeLightbox()">
|
|
<span class="lightbox-close">×</span>
|
|
<img id="lightbox-img" src="" alt="">
|
|
<p id="lightbox-caption"></p>
|
|
</div>
|
|
|
|
<script>
|
|
function copyArticleLink() {
|
|
navigator.clipboard.writeText(window.location.href).then(() => {
|
|
showNotification('Ссылка скопирована!', 'success');
|
|
});
|
|
}
|
|
|
|
// Carousel functionality
|
|
let currentSlide = 0;
|
|
const slides = document.querySelectorAll('.carousel-slide');
|
|
const dots = document.querySelectorAll('.carousel-dot');
|
|
|
|
function showSlide(index) {
|
|
if (!slides.length) return;
|
|
|
|
if (index >= slides.length) currentSlide = 0;
|
|
if (index < 0) currentSlide = slides.length - 1;
|
|
|
|
slides.forEach((slide, i) => {
|
|
slide.classList.remove('active');
|
|
if (dots[i]) dots[i].classList.remove('active');
|
|
});
|
|
|
|
slides[currentSlide].classList.add('active');
|
|
if (dots[currentSlide]) dots[currentSlide].classList.add('active');
|
|
}
|
|
|
|
function carouselNext() {
|
|
currentSlide++;
|
|
showSlide(currentSlide);
|
|
}
|
|
|
|
function carouselPrev() {
|
|
currentSlide--;
|
|
showSlide(currentSlide);
|
|
}
|
|
|
|
function carouselGoTo(index) {
|
|
currentSlide = index;
|
|
showSlide(currentSlide);
|
|
}
|
|
|
|
// Lightbox functionality
|
|
function openLightbox(src, caption) {
|
|
document.getElementById('lightbox').style.display = 'flex';
|
|
document.getElementById('lightbox-img').src = src;
|
|
document.getElementById('lightbox-caption').textContent = caption;
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeLightbox() {
|
|
document.getElementById('lightbox').style.display = 'none';
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
// Close lightbox on escape
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') closeLightbox();
|
|
});
|
|
</script>
|
|
{% endblock %} |