feat: show WordPress posts
This commit is contained in:
+48
-5
@@ -469,6 +469,24 @@ img {
|
||||
--mt-color-content-txt: var(--text-color);
|
||||
}
|
||||
|
||||
/* Override couleurs Mastodon - Theme rouge */
|
||||
.mt-container, .mt-container[data-theme=light], .mt-dialog, .mt-dialog[data-theme=light] {
|
||||
--mt-color-btn-bg: var(--primary-red) !important;
|
||||
--mt-color-btn-bg-hover: #cc0000 !important;
|
||||
--mt-color-link: var(--primary-red) !important;
|
||||
}
|
||||
|
||||
.mt-container[data-theme=dark], .mt-dialog[data-theme=dark] {
|
||||
--mt-color-btn-bg: var(--primary-red) !important;
|
||||
--mt-color-btn-bg-hover: #ff6666 !important;
|
||||
--mt-color-link: #ff6666 !important;
|
||||
}
|
||||
|
||||
/* Icône Mastodon en rouge */
|
||||
.mt-title i.fa-mastodon {
|
||||
color: var(--primary-red) !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] img {
|
||||
opacity: 0.9;
|
||||
}
|
||||
@@ -499,15 +517,35 @@ img {
|
||||
|
||||
/* Hero Section */
|
||||
.hero-mastodon-wrapper {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
padding-top: 5px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* Responsive breakpoint intermédiaire pour les 3 éléments */
|
||||
@media (max-width: 1200px) and (min-width: 1025px) {
|
||||
.hero-mastodon-wrapper {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto;
|
||||
}
|
||||
|
||||
.timeline-wordpress-container {
|
||||
grid-column: 1 / -1;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.timeline-wordpress-container .wordpress-section {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.hero {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
background-color: #888;
|
||||
overflow: hidden;
|
||||
@@ -633,7 +671,7 @@ img {
|
||||
}
|
||||
|
||||
#mt-container {
|
||||
width: 50%;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
@@ -1730,7 +1768,7 @@ img {
|
||||
}
|
||||
|
||||
.hero-mastodon-wrapper {
|
||||
flex-direction: column;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.hero, #mt-container {
|
||||
@@ -1738,10 +1776,15 @@ img {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
#mt-container {
|
||||
.hero-mastodon-wrapper #mt-container {
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
/* Pour le nouveau layout timeline-wordpress */
|
||||
.timeline-wordpress-container #mt-container {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
/* Réduire la taille des boutons du footer Mastodon en mobile */
|
||||
.mt-footer .mt-btn-violet {
|
||||
font-size: 12px !important;
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/* Styles pour la liste compacte d'articles WordPress */
|
||||
|
||||
/* Container principal pour timeline + wordpress */
|
||||
.timeline-wordpress-container {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 20px;
|
||||
margin: 0;
|
||||
align-items: start;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wordpress-container {
|
||||
background: var(--card-bg);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
box-shadow: var(--card-shadow);
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wordpress-section {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Assurer que le container Mastodon garde son scroll */
|
||||
.timeline-wordpress-container .mt-container {
|
||||
height: 400px !important;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
.wordpress-header {
|
||||
background: var(--primary-red);
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.wordpress-header a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.wordpress-header a:hover {
|
||||
opacity: 0.8;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.wordpress-posts-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--primary-red) transparent;
|
||||
}
|
||||
|
||||
.wordpress-posts-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.wordpress-posts-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.wordpress-posts-list::-webkit-scrollbar-thumb {
|
||||
background-color: var(--primary-red);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.wordpress-post-item {
|
||||
display: flex;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: background-color 0.2s ease;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.wordpress-post-item:hover {
|
||||
background: var(--hover-bg);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.wordpress-post-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.wordpress-post-thumb {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
background: var(--tag-bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.wordpress-post-thumb img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.wordpress-post-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wordpress-post-title {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 4px 0;
|
||||
line-height: 1.2;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
.wordpress-post-meta {
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-secondary);
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.wordpress-no-posts {
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
padding: 20px;
|
||||
font-style: italic;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Ajustement du container Mastodon */
|
||||
.mt-container {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1024px) {
|
||||
/* Mobile/Tablette: layout vertical */
|
||||
.timeline-wordpress-container {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.wordpress-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Maintenir la hauteur et le scroll du container Mastodon en mobile */
|
||||
.timeline-wordpress-container .mt-container {
|
||||
height: 400px !important;
|
||||
overflow-y: auto !important;
|
||||
max-height: 400px !important;
|
||||
}
|
||||
|
||||
.wordpress-container {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.wordpress-post-thumb {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.wordpress-post-item {
|
||||
padding: 10px 12px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wordpress-post-title {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
|
||||
.wordpress-post-meta {
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
.wordpress-header {
|
||||
padding: 10px 12px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -133,4 +133,17 @@ if (!defined('COUNTDOWN_TIMEZONES')) {
|
||||
'Kanaky' => 'Pacific/Noumea'
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Intégration WordPress par défaut
|
||||
// =========================================
|
||||
|
||||
// URL du site WordPress par défaut
|
||||
if (!defined('WORDPRESS_URL')) define('WORDPRESS_URL', '');
|
||||
|
||||
// Nombre d'articles WordPress à afficher par défaut
|
||||
if (!defined('WORDPRESS_POSTS_COUNT')) define('WORDPRESS_POSTS_COUNT', 6);
|
||||
|
||||
// Activation des articles WordPress par défaut
|
||||
if (!defined('WORDPRESS_ENABLED')) define('WORDPRESS_ENABLED', false);
|
||||
?>
|
||||
|
||||
@@ -252,6 +252,19 @@ define('COUNTDOWN_TIMEZONES', [
|
||||
'Kanaky' => 'Pacific/Noumea'
|
||||
]);
|
||||
|
||||
// =========================================
|
||||
// Intégration WordPress
|
||||
// =========================================
|
||||
|
||||
// URL du site WordPress pour récupérer les articles (sans trailing slash)
|
||||
// define('WORDPRESS_URL', 'https://votre-site-wordpress.com');
|
||||
|
||||
// Nombre d'articles WordPress à afficher
|
||||
// define('WORDPRESS_POSTS_COUNT', 6);
|
||||
|
||||
// Activer/désactiver l'affichage des articles WordPress
|
||||
// define('WORDPRESS_ENABLED', true);
|
||||
|
||||
// =========================================
|
||||
// Texte de présentation du mouvement
|
||||
// =========================================
|
||||
|
||||
@@ -6,6 +6,9 @@ require_once __DIR__ . '/security.php';
|
||||
// Charger le système de cache simple
|
||||
require_once __DIR__ . '/simple-cache.php';
|
||||
|
||||
// Charger les fonctions WordPress
|
||||
require_once __DIR__ . '/wordpress.php';
|
||||
|
||||
// Charger d'abord la configuration locale si elle existe
|
||||
$config_local_file = __DIR__ . '/config.local.php';
|
||||
if (file_exists($config_local_file)) {
|
||||
|
||||
@@ -78,4 +78,5 @@
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* Fonctions pour l'intégration WordPress via l'API REST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Appelle l'API WordPress REST pour récupérer les articles
|
||||
*
|
||||
* @param string $endpoint Point de terminaison de l'API (ex: 'posts')
|
||||
* @param array $params Paramètres optionnels pour la requête
|
||||
* @return array Données retournées par l'API
|
||||
*/
|
||||
function callWordPressApi($endpoint, $params = []) {
|
||||
// Vérifier que WordPress est activé et configuré
|
||||
if (!defined('WORDPRESS_ENABLED') || !WORDPRESS_ENABLED || empty(WORDPRESS_URL)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Validation de l'URL WordPress pour prévenir SSRF
|
||||
if (!isValidWordPressUrl(WORDPRESS_URL)) {
|
||||
error_log('SECURITY: Invalid WordPress URL detected: ' . WORDPRESS_URL);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Construire l'URL de l'API
|
||||
$url = WORDPRESS_URL . '/wp-json/wp/v2/' . ltrim($endpoint, '/');
|
||||
|
||||
// Ajouter les paramètres à l'URL
|
||||
if (!empty($params)) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
// Initialiser cURL avec options de sécurité
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, 0);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'KaubuntuRe-WordPress-Integration/1.0');
|
||||
|
||||
// Exécuter la requête
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
// Traiter la réponse
|
||||
if ($response === false || !empty($error)) {
|
||||
error_log('WordPress API error: ' . $error);
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
error_log('WordPress API HTTP error: ' . $httpCode . ' for URL: ' . $url);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Validation du Content-Type
|
||||
if (!str_contains($contentType, 'application/json')) {
|
||||
error_log('WordPress API error: Invalid content type: ' . $contentType . ' for URL: ' . $url);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Décoder la réponse JSON
|
||||
$data = json_decode($response, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
error_log('WordPress API JSON decode error: ' . json_last_error_msg() . ' for URL: ' . $url);
|
||||
return [];
|
||||
}
|
||||
|
||||
return $data ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide l'URL WordPress pour prévenir les attaques SSRF
|
||||
*
|
||||
* @param string $url URL à valider
|
||||
* @return bool True si l'URL est valide et sûre
|
||||
*/
|
||||
function isValidWordPressUrl($url) {
|
||||
// Vérifier que l'URL est bien formée
|
||||
$parsed = parse_url($url);
|
||||
if (!$parsed || !isset($parsed['scheme']) || !isset($parsed['host'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Autoriser uniquement HTTPS (ou HTTP en développement)
|
||||
if (!in_array($parsed['scheme'], ['https', 'http'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bloquer les adresses IP privées et locales
|
||||
$host = $parsed['host'];
|
||||
if (filter_var($host, FILTER_VALIDATE_IP)) {
|
||||
if (!filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bloquer localhost et autres domaines dangereux
|
||||
$blockedHosts = ['localhost', '127.0.0.1', '::1', '0.0.0.0', 'metadata.google.internal'];
|
||||
if (in_array(strtolower($host), $blockedHosts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les articles WordPress récents
|
||||
*
|
||||
* @param int $count Nombre d'articles à récupérer
|
||||
* @return array Liste des articles formatés
|
||||
*/
|
||||
function getWordPressPosts($count = null) {
|
||||
if ($count === null) {
|
||||
$count = defined('WORDPRESS_POSTS_COUNT') ? WORDPRESS_POSTS_COUNT : 6;
|
||||
}
|
||||
|
||||
// Récupérer les posts via l'API WordPress REST
|
||||
$posts = callWordPressApi('posts', [
|
||||
'per_page' => $count,
|
||||
'_embed' => 'true' // Inclure les médias embarqués (featured image)
|
||||
]);
|
||||
|
||||
return formatWordPressPosts($posts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formate les données des articles WordPress
|
||||
*
|
||||
* @param array $posts Données brutes des articles
|
||||
* @return array Articles formatés
|
||||
*/
|
||||
function formatWordPressPosts($posts) {
|
||||
if (empty($posts)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$formattedPosts = [];
|
||||
|
||||
foreach ($posts as $post) {
|
||||
// Récupérer l'image mise en avant
|
||||
$featuredImage = '';
|
||||
if (isset($post['_embedded']['wp:featuredmedia'][0]['source_url'])) {
|
||||
$featuredImage = $post['_embedded']['wp:featuredmedia'][0]['source_url'];
|
||||
}
|
||||
|
||||
// Nettoyer l'extrait HTML
|
||||
$excerpt = '';
|
||||
if (isset($post['excerpt']['rendered'])) {
|
||||
$excerpt = wp_strip_all_tags($post['excerpt']['rendered']);
|
||||
$excerpt = trim($excerpt);
|
||||
// Limiter à 150 caractères
|
||||
if (strlen($excerpt) > 150) {
|
||||
$excerpt = substr($excerpt, 0, 147) . '...';
|
||||
}
|
||||
}
|
||||
|
||||
// Formater la date
|
||||
$date = '';
|
||||
if (isset($post['date'])) {
|
||||
$dateObj = new DateTime($post['date']);
|
||||
$date = $dateObj->format('d/m/Y');
|
||||
}
|
||||
|
||||
$formattedPosts[] = [
|
||||
'id' => $post['id'],
|
||||
'title' => html_entity_decode($post['title']['rendered'], ENT_QUOTES, 'UTF-8'),
|
||||
'excerpt' => $excerpt,
|
||||
'featured_image' => $featuredImage,
|
||||
'link' => $post['link'],
|
||||
'date' => $date,
|
||||
'author' => isset($post['_embedded']['author'][0]['name']) ? $post['_embedded']['author'][0]['name'] : ''
|
||||
];
|
||||
}
|
||||
|
||||
return $formattedPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction utilitaire pour nettoyer le HTML (similaire à wp_strip_all_tags de WordPress)
|
||||
*
|
||||
* @param string $string Chaîne à nettoyer
|
||||
* @return string Chaîne nettoyée
|
||||
*/
|
||||
function wp_strip_all_tags($string) {
|
||||
$string = preg_replace('@<(script|style)[^>]*?>.*?</\\1>@si', '', $string);
|
||||
$string = strip_tags($string);
|
||||
$string = preg_replace('/\s+/', ' ', $string);
|
||||
return trim($string);
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -24,6 +24,9 @@ setSecurityHeaders();
|
||||
<link rel="stylesheet" href="css/styles.css?v=<?php echo filemtime('css/styles.css'); ?>">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
|
||||
<link rel="stylesheet" href="css/mastodon-timeline.min.css?v=<?php echo filemtime('css/mastodon-timeline.min.css'); ?>">
|
||||
<?php if (defined('WORDPRESS_ENABLED') && WORDPRESS_ENABLED): ?>
|
||||
<link rel="stylesheet" href="css/wordpress-posts.css?v=<?php echo filemtime('css/wordpress-posts.css'); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Favicons -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="img/apple-touch-icon.png">
|
||||
@@ -151,6 +154,8 @@ setSecurityHeaders();
|
||||
?>
|
||||
</section>
|
||||
|
||||
<!-- Container pour Mastodon et WordPress côte à côte -->
|
||||
<div class="timeline-wordpress-container">
|
||||
<div id="mt-container" class="mt-container">
|
||||
<div class="mt-header">
|
||||
<h2 class="mt-title">Fil d'actualités <i class="fab fa-mastodon"></i></h2>
|
||||
@@ -159,6 +164,70 @@ setSecurityHeaders();
|
||||
<div class="mt-loading-spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section Articles WordPress (desktop à droite, mobile en dessous) -->
|
||||
<?php if (defined('WORDPRESS_ENABLED') && WORDPRESS_ENABLED && !empty(WORDPRESS_URL)): ?>
|
||||
<div class="wordpress-section">
|
||||
<div class="wordpress-container">
|
||||
<div class="wordpress-header">
|
||||
<i class="fas fa-newspaper"></i>
|
||||
Articles <?php if (!empty(WORDPRESS_URL)): ?>
|
||||
<a href="<?php echo htmlspecialchars(WORDPRESS_URL); ?>"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="Visiter <?php echo htmlspecialchars(parse_url(WORDPRESS_URL, PHP_URL_HOST)); ?>">
|
||||
<?php echo htmlspecialchars(parse_url(WORDPRESS_URL, PHP_URL_HOST)); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="wordpress-posts-list">
|
||||
<?php
|
||||
$wordpressPosts = getWordPressPosts();
|
||||
|
||||
if (empty($wordpressPosts)) {
|
||||
echo '<div class="wordpress-no-posts">Aucun article disponible</div>';
|
||||
} else {
|
||||
foreach ($wordpressPosts as $post):
|
||||
?>
|
||||
<a href="<?php echo htmlspecialchars($post['link']); ?>"
|
||||
class="wordpress-post-item"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="<?php echo htmlspecialchars($post['title']); ?>">
|
||||
|
||||
<div class="wordpress-post-thumb">
|
||||
<?php if (!empty($post['featured_image'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($post['featured_image']); ?>"
|
||||
alt="<?php echo htmlspecialchars($post['title']); ?>"
|
||||
loading="lazy">
|
||||
<?php else: ?>
|
||||
<i class="fas fa-file-alt"></i>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wordpress-post-info">
|
||||
<h3 class="wordpress-post-title">
|
||||
<?php echo htmlspecialchars($post['title']); ?>
|
||||
</h3>
|
||||
|
||||
<div class="wordpress-post-meta">
|
||||
<span><?php echo htmlspecialchars($post['date']); ?></span>
|
||||
<?php if (!empty($post['author'])): ?>
|
||||
<span>•</span>
|
||||
<span><?php echo htmlspecialchars($post['author']); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<?php
|
||||
endforeach;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -179,6 +248,7 @@ setSecurityHeaders();
|
||||
<!-- Séparateur stylisé -->
|
||||
<hr class="section-divider">
|
||||
|
||||
|
||||
<!-- Section Shorts -->
|
||||
<section class="video-section" aria-labelledby="shorts-heading">
|
||||
<div class="section-header">
|
||||
|
||||
Reference in New Issue
Block a user