feat: add content feed with AJAX filtering and load more on front page
This commit is contained in:
@@ -45,6 +45,7 @@ $bitwms_includes = array(
|
|||||||
'inc/enqueue.php',
|
'inc/enqueue.php',
|
||||||
'inc/cpt.php',
|
'inc/cpt.php',
|
||||||
'inc/acf-fields.php',
|
'inc/acf-fields.php',
|
||||||
|
'inc/ajax-filter.php',
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $bitwms_includes as $bitwms_file ) {
|
foreach ( $bitwms_includes as $bitwms_file ) {
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* БИТ.WMS Child Theme — inc/ajax-filter.php
|
||||||
|
*
|
||||||
|
* AJAX handler for the content feed filter bar and "Load more" button.
|
||||||
|
* Registered for both logged-in and non-logged-in users.
|
||||||
|
*
|
||||||
|
* @package bitwms-child
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action( 'wp_ajax_bitwms_filter', 'bitwms_ajax_filter_handler' );
|
||||||
|
add_action( 'wp_ajax_nopriv_bitwms_filter', 'bitwms_ajax_filter_handler' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the AJAX filter/load-more request.
|
||||||
|
*
|
||||||
|
* Expected POST parameters:
|
||||||
|
* - post_type (string) — 'all' or one of: post, case, webinar, course, event, material
|
||||||
|
* - page (int) — page number (1-based)
|
||||||
|
* - nonce (string) — security nonce
|
||||||
|
*
|
||||||
|
* Returns JSON: { success: true, data: { html: "...", has_more: bool } }
|
||||||
|
*/
|
||||||
|
function bitwms_ajax_filter_handler() {
|
||||||
|
// Verify nonce.
|
||||||
|
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'bitwms_filter_nonce' ) ) {
|
||||||
|
wp_send_json_error( array( 'message' => 'Security check failed.' ), 403 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$valid_types = array( 'post', 'case', 'webinar', 'course', 'event', 'material' );
|
||||||
|
$all_post_types = $valid_types;
|
||||||
|
|
||||||
|
$raw_type = isset( $_POST['post_type'] ) ? sanitize_key( $_POST['post_type'] ) : 'all';
|
||||||
|
$page = isset( $_POST['page'] ) ? max( 1, (int) $_POST['page'] ) : 1;
|
||||||
|
|
||||||
|
if ( 'all' === $raw_type ) {
|
||||||
|
$post_type = $all_post_types;
|
||||||
|
} elseif ( in_array( $raw_type, $valid_types, true ) ) {
|
||||||
|
$post_type = array( $raw_type );
|
||||||
|
} else {
|
||||||
|
$post_type = $all_post_types;
|
||||||
|
}
|
||||||
|
|
||||||
|
$posts_per_page = 6;
|
||||||
|
|
||||||
|
$query = new WP_Query( array(
|
||||||
|
'post_type' => $post_type,
|
||||||
|
'posts_per_page' => $posts_per_page,
|
||||||
|
'paged' => $page,
|
||||||
|
'orderby' => 'date',
|
||||||
|
'order' => 'DESC',
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$html = '';
|
||||||
|
$has_more = false;
|
||||||
|
|
||||||
|
if ( $query->have_posts() ) {
|
||||||
|
ob_start();
|
||||||
|
while ( $query->have_posts() ) {
|
||||||
|
$query->the_post();
|
||||||
|
get_template_part( 'templates/content-card' );
|
||||||
|
}
|
||||||
|
$html = ob_get_clean();
|
||||||
|
wp_reset_postdata();
|
||||||
|
|
||||||
|
$has_more = ( $page * $posts_per_page ) < $query->found_posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json_success( array(
|
||||||
|
'html' => $html,
|
||||||
|
'has_more' => $has_more,
|
||||||
|
) );
|
||||||
|
}
|
||||||
@@ -16,13 +16,33 @@ add_action( 'wp_enqueue_scripts', 'bitwms_enqueue_scripts', 20 );
|
|||||||
|
|
||||||
function bitwms_enqueue_scripts() {
|
function bitwms_enqueue_scripts() {
|
||||||
$theme_version = wp_get_theme()->get( 'Version' );
|
$theme_version = wp_get_theme()->get( 'Version' );
|
||||||
|
$theme_uri = get_stylesheet_directory_uri();
|
||||||
|
|
||||||
// Sidebar navigation — loaded in footer (5th arg: true).
|
// Sidebar navigation — loaded in footer (5th arg: true).
|
||||||
wp_enqueue_script(
|
wp_enqueue_script(
|
||||||
'bitwms-sidebar-nav',
|
'bitwms-sidebar-nav',
|
||||||
get_stylesheet_directory_uri() . '/js/sidebar-nav.js',
|
$theme_uri . '/js/sidebar-nav.js',
|
||||||
array(), // no dependencies
|
array(), // no dependencies
|
||||||
$theme_version,
|
$theme_version,
|
||||||
true // load in footer
|
true // load in footer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Content feed AJAX filter — front page only.
|
||||||
|
if ( is_front_page() ) {
|
||||||
|
wp_enqueue_script(
|
||||||
|
'bitwms-ajax-filter',
|
||||||
|
$theme_uri . '/js/ajax-filter.js',
|
||||||
|
array(),
|
||||||
|
$theme_version,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
wp_localize_script(
|
||||||
|
'bitwms-ajax-filter',
|
||||||
|
'bitwmsAjax',
|
||||||
|
array(
|
||||||
|
'url' => admin_url( 'admin-ajax.php' ),
|
||||||
|
'nonce' => wp_create_nonce( 'bitwms_filter_nonce' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* БИТ.WMS Child Theme — js/ajax-filter.js
|
||||||
|
*
|
||||||
|
* Handles filter button clicks and "Load more" for the content feed section.
|
||||||
|
* Communicates with the WordPress AJAX handler (bitwms_ajax_filter_handler).
|
||||||
|
*
|
||||||
|
* Global: bitwmsAjax { url: string, nonce: string }
|
||||||
|
*/
|
||||||
|
|
||||||
|
( function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
document.addEventListener( 'DOMContentLoaded', function () {
|
||||||
|
var filterBar = document.querySelector( '.filter-bar' );
|
||||||
|
var contentGrid = document.getElementById( 'content-grid' );
|
||||||
|
var loadMoreBtn = document.getElementById( 'load-more-btn' );
|
||||||
|
|
||||||
|
if ( ! filterBar || ! contentGrid ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentFilter = 'all';
|
||||||
|
var currentPage = 1;
|
||||||
|
var isLoading = false;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Filter button click
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
filterBar.addEventListener( 'click', function ( e ) {
|
||||||
|
var btn = e.target.closest( '.filter-bar__btn' );
|
||||||
|
if ( ! btn ) return;
|
||||||
|
|
||||||
|
var filter = btn.getAttribute( 'data-filter' );
|
||||||
|
if ( filter === currentFilter ) return;
|
||||||
|
|
||||||
|
// Update active state.
|
||||||
|
filterBar.querySelectorAll( '.filter-bar__btn' ).forEach( function ( b ) {
|
||||||
|
b.classList.remove( 'filter-bar__btn--active' );
|
||||||
|
b.setAttribute( 'aria-selected', 'false' );
|
||||||
|
} );
|
||||||
|
btn.classList.add( 'filter-bar__btn--active' );
|
||||||
|
btn.setAttribute( 'aria-selected', 'true' );
|
||||||
|
|
||||||
|
currentFilter = filter;
|
||||||
|
currentPage = 1;
|
||||||
|
|
||||||
|
loadContent( false );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Load more button click
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
if ( loadMoreBtn ) {
|
||||||
|
loadMoreBtn.addEventListener( 'click', function () {
|
||||||
|
if ( isLoading ) return;
|
||||||
|
currentPage += 1;
|
||||||
|
loadContent( true );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// loadContent( append )
|
||||||
|
// append === false: replace grid content
|
||||||
|
// append === true: append to existing grid content
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
function loadContent( append ) {
|
||||||
|
if ( isLoading ) return;
|
||||||
|
isLoading = true;
|
||||||
|
|
||||||
|
if ( loadMoreBtn ) {
|
||||||
|
loadMoreBtn.disabled = true;
|
||||||
|
loadMoreBtn.textContent = loadMoreBtn.getAttribute( 'data-loading' ) || 'Загрузка…';
|
||||||
|
}
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append( 'action', 'bitwms_filter' );
|
||||||
|
formData.append( 'nonce', bitwmsAjax.nonce );
|
||||||
|
formData.append( 'post_type', currentFilter );
|
||||||
|
formData.append( 'page', currentPage );
|
||||||
|
|
||||||
|
fetch( bitwmsAjax.url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
} )
|
||||||
|
.then( function ( response ) {
|
||||||
|
return response.json();
|
||||||
|
} )
|
||||||
|
.then( function ( data ) {
|
||||||
|
if ( ! data.success ) return;
|
||||||
|
|
||||||
|
var html = data.data.html;
|
||||||
|
var hasMore = data.data.has_more;
|
||||||
|
|
||||||
|
if ( append ) {
|
||||||
|
contentGrid.insertAdjacentHTML( 'beforeend', html );
|
||||||
|
} else {
|
||||||
|
contentGrid.innerHTML = html || '<p>Материалы не найдены.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show/hide load-more button.
|
||||||
|
if ( loadMoreBtn ) {
|
||||||
|
if ( hasMore ) {
|
||||||
|
loadMoreBtn.style.display = '';
|
||||||
|
loadMoreBtn.disabled = false;
|
||||||
|
loadMoreBtn.textContent = 'Загрузить ещё';
|
||||||
|
} else {
|
||||||
|
loadMoreBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.catch( function ( err ) {
|
||||||
|
console.error( 'bitwms filter error:', err );
|
||||||
|
if ( loadMoreBtn ) {
|
||||||
|
loadMoreBtn.disabled = false;
|
||||||
|
loadMoreBtn.textContent = 'Загрузить ещё';
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.finally( function () {
|
||||||
|
isLoading = false;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} )();
|
||||||
@@ -720,3 +720,108 @@ body.sidebar-overlay::after {
|
|||||||
.clients-section { padding: 40px 0 48px; }
|
.clients-section { padding: 40px 0 48px; }
|
||||||
.clients-section__grid { gap: 16px; }
|
.clients-section__grid { gap: 16px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Task 9: Content feed — filter bar + content grid (AJAX)
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* --- Filter bar --- */
|
||||||
|
.filter-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
border-bottom: 1px solid var(--color-gray-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar__btn {
|
||||||
|
padding: 8px 20px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
background: none;
|
||||||
|
border: 1px solid var(--color-gray-light);
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar__btn:hover {
|
||||||
|
border-color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar__btn--active {
|
||||||
|
background: var(--color-black);
|
||||||
|
color: var(--color-white);
|
||||||
|
border-color: var(--color-black);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Content grid --- */
|
||||||
|
.content-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card__link {
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card__link:hover .content-card__title {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card__image {
|
||||||
|
aspect-ratio: 16 / 10;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background: var(--color-gray-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card__image img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card__placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card__title {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card__excerpt {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-gray-text);
|
||||||
|
margin: 0 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card__meta {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-gray-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-feed__more {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Responsive breakpoints --- */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.content-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.content-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* БИТ.WMS Child Theme — templates/content-card.php
|
||||||
|
*
|
||||||
|
* Universal content card used in the content feed section and AJAX responses.
|
||||||
|
* Reads the current post from The Loop (global $post).
|
||||||
|
*
|
||||||
|
* @package bitwms-child
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map post_type to label. For 'post' also check for "news" category.
|
||||||
|
$type_labels = array(
|
||||||
|
'case' => __( 'Кейс', 'bitwms-child' ),
|
||||||
|
'webinar' => __( 'Вебинар', 'bitwms-child' ),
|
||||||
|
'course' => __( 'Курс', 'bitwms-child' ),
|
||||||
|
'event' => __( 'Мероприятие', 'bitwms-child' ),
|
||||||
|
'material' => __( 'Материал', 'bitwms-child' ),
|
||||||
|
);
|
||||||
|
|
||||||
|
$post_type = get_post_type();
|
||||||
|
$badge_text = '';
|
||||||
|
|
||||||
|
if ( 'post' === $post_type ) {
|
||||||
|
if ( has_category( 'news' ) ) {
|
||||||
|
$badge_text = __( 'Новость', 'bitwms-child' );
|
||||||
|
} else {
|
||||||
|
$badge_text = __( 'Статья', 'bitwms-child' );
|
||||||
|
}
|
||||||
|
} elseif ( isset( $type_labels[ $post_type ] ) ) {
|
||||||
|
$badge_text = $type_labels[ $post_type ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim excerpt to 15 words.
|
||||||
|
$excerpt = get_the_excerpt();
|
||||||
|
if ( $excerpt ) {
|
||||||
|
$words = explode( ' ', $excerpt );
|
||||||
|
$excerpt = implode( ' ', array_slice( $words, 0, 15 ) );
|
||||||
|
if ( count( $words ) > 15 ) {
|
||||||
|
$excerpt .= '…';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<article class="content-card" data-post-type="<?php echo esc_attr( $post_type ); ?>">
|
||||||
|
<a href="<?php the_permalink(); ?>" class="content-card__link">
|
||||||
|
|
||||||
|
<div class="content-card__image">
|
||||||
|
<?php if ( has_post_thumbnail() ) : ?>
|
||||||
|
<?php the_post_thumbnail( 'medium_large', array( 'alt' => esc_attr( get_the_title() ) ) ); ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<div class="content-card__placeholder" aria-hidden="true"></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-card__body">
|
||||||
|
<?php if ( $badge_text ) : ?>
|
||||||
|
<span class="badge"><?php echo esc_html( $badge_text ); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<h3 class="content-card__title"><?php the_title(); ?></h3>
|
||||||
|
|
||||||
|
<?php if ( $excerpt ) : ?>
|
||||||
|
<p class="content-card__excerpt"><?php echo esc_html( $excerpt ); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="content-card__meta">
|
||||||
|
<time datetime="<?php echo esc_attr( get_the_date( 'Y-m-d' ) ); ?>">
|
||||||
|
<?php echo esc_html( get_the_date( 'd.m.Y' ) ); ?>
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</article>
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* БИТ.WMS Child Theme — templates/section-filter.php
|
||||||
|
*
|
||||||
|
* Content feed section with tag-based filter bar and "Load more" button.
|
||||||
|
* Initial query fetches 6 posts across all 6 custom post types, ordered by date DESC.
|
||||||
|
*
|
||||||
|
* @package bitwms-child
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter_buttons = array(
|
||||||
|
'all' => __( 'Все', 'bitwms-child' ),
|
||||||
|
'post' => __( 'Статьи', 'bitwms-child' ),
|
||||||
|
'case' => __( 'Кейсы', 'bitwms-child' ),
|
||||||
|
'webinar' => __( 'Вебинары', 'bitwms-child' ),
|
||||||
|
'course' => __( 'Курсы', 'bitwms-child' ),
|
||||||
|
'event' => __( 'Мероприятия', 'bitwms-child' ),
|
||||||
|
'material'=> __( 'Материалы', 'bitwms-child' ),
|
||||||
|
);
|
||||||
|
|
||||||
|
$post_types = array( 'post', 'case', 'webinar', 'course', 'event', 'material' );
|
||||||
|
$posts_per_page = 6;
|
||||||
|
|
||||||
|
$initial_query = new WP_Query( array(
|
||||||
|
'post_type' => $post_types,
|
||||||
|
'posts_per_page' => $posts_per_page,
|
||||||
|
'orderby' => 'date',
|
||||||
|
'order' => 'DESC',
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section class="content-feed">
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div class="filter-bar" role="tablist">
|
||||||
|
<?php foreach ( $filter_buttons as $value => $label ) : ?>
|
||||||
|
<button
|
||||||
|
class="filter-bar__btn<?php echo $value === 'all' ? ' filter-bar__btn--active' : ''; ?>"
|
||||||
|
data-filter="<?php echo esc_attr( $value ); ?>"
|
||||||
|
role="tab"
|
||||||
|
aria-selected="<?php echo $value === 'all' ? 'true' : 'false'; ?>"
|
||||||
|
>
|
||||||
|
<?php echo esc_html( $label ); ?>
|
||||||
|
</button>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-grid" id="content-grid">
|
||||||
|
<?php if ( $initial_query->have_posts() ) : ?>
|
||||||
|
<?php while ( $initial_query->have_posts() ) : $initial_query->the_post(); ?>
|
||||||
|
<?php get_template_part( 'templates/content-card' ); ?>
|
||||||
|
<?php endwhile; ?>
|
||||||
|
<?php wp_reset_postdata(); ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<p><?php esc_html_e( 'Материалы не найдены.', 'bitwms-child' ); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ( $initial_query->found_posts > $posts_per_page ) : ?>
|
||||||
|
<div class="content-feed__more">
|
||||||
|
<button
|
||||||
|
class="btn btn--outline load-more-btn"
|
||||||
|
id="load-more-btn"
|
||||||
|
data-page="1"
|
||||||
|
data-filter="all"
|
||||||
|
>
|
||||||
|
<?php esc_html_e( 'Загрузить ещё', 'bitwms-child' ); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
Reference in New Issue
Block a user