Initial production backup
This commit is contained in:
+343
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
$slug = isset($_GET['slug']) ? trim($_GET['slug']) : '';
|
||||
|
||||
if ($slug === '') {
|
||||
header('Location: ' . BLOG_URL);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
|
||||
// Fetch post
|
||||
$stmt = $db->prepare("SELECT p.*, c.name AS category_name, c.slug AS category_slug
|
||||
FROM blog_posts p
|
||||
LEFT JOIN blog_categories c ON p.category_id = c.id
|
||||
WHERE p.slug = :slug AND p.status = 'published'
|
||||
LIMIT 1");
|
||||
$stmt->execute([':slug' => $slug]);
|
||||
$post = $stmt->fetch();
|
||||
|
||||
if (!$post) {
|
||||
http_response_code(404);
|
||||
$pageTitle = 'Artículo no encontrado - Blog TRIMAPE';
|
||||
$notFound = true;
|
||||
} else {
|
||||
$notFound = false;
|
||||
$pageTitle = e($post['title']) . ' - Blog TRIMAPE';
|
||||
$pageDesc = $post['excerpt'] ? e($post['excerpt']) : e(truncate($post['content'], 160));
|
||||
$coverImage = $post['cover_image'] ?: SITE_URL . '/assets/img/trimape-1200.webp';
|
||||
|
||||
// Related posts (same category, excluding current)
|
||||
$relatedStmt = $db->prepare("SELECT p.*, c.name AS category_name, c.slug AS category_slug
|
||||
FROM blog_posts p
|
||||
LEFT JOIN blog_categories c ON p.category_id = c.id
|
||||
WHERE p.status = 'published' AND p.id != :id AND p.category_id = :catid
|
||||
ORDER BY p.published_at DESC LIMIT 3");
|
||||
$relatedStmt->execute([':id' => $post['id'], ':catid' => $post['category_id']]);
|
||||
$related = $relatedStmt->fetchAll();
|
||||
|
||||
// If not enough related from same category, fill with recent
|
||||
if (count($related) < 3) {
|
||||
$excludeIds = array_merge([$post['id']], array_column($related, 'id'));
|
||||
$placeholders = implode(',', array_fill(0, count($excludeIds), '?'));
|
||||
$fillStmt = $db->prepare("SELECT p.*, c.name AS category_name, c.slug AS category_slug
|
||||
FROM blog_posts p
|
||||
LEFT JOIN blog_categories c ON p.category_id = c.id
|
||||
WHERE p.status = 'published' AND p.id NOT IN ($placeholders)
|
||||
ORDER BY p.published_at DESC LIMIT " . (3 - count($related)));
|
||||
$fillStmt->execute($excludeIds);
|
||||
$related = array_merge($related, $fillStmt->fetchAll());
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100..900&display=swap" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<title><?= $pageTitle ?></title>
|
||||
<?php if (!$notFound): ?>
|
||||
<meta name="description" content="<?= $pageDesc ?>">
|
||||
<meta property="og:title" content="<?= e($post['title']) ?>">
|
||||
<meta property="og:description" content="<?= $pageDesc ?>">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="<?= BLOG_URL ?>/<?= e($post['slug']) ?>">
|
||||
<meta property="og:image" content="<?= e($coverImage) ?>">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="<?= e($post['title']) ?>">
|
||||
<meta name="twitter:description" content="<?= $pageDesc ?>">
|
||||
<meta name="twitter:image" content="<?= e($coverImage) ?>">
|
||||
<?php endif; ?>
|
||||
<link rel="icon" href="<?= SITE_URL ?>/assets/img/favico.png">
|
||||
<link rel="icon" href="<?= SITE_URL ?>/assets/img/trimape-logo-512-150x150.png" sizes="32x32">
|
||||
<link rel="icon" href="<?= SITE_URL ?>/assets/img/trimape-logo-512-300x300.png" sizes="192x192">
|
||||
<link rel="apple-touch-icon" href="<?= SITE_URL ?>/assets/img/trimape-logo-512-300x300.png">
|
||||
|
||||
<?php include dirname(__DIR__) . '/css-columna.htm'; ?>
|
||||
<style>
|
||||
.post-hero {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 45vh;
|
||||
min-height: 350px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.post-hero img {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.post-hero .overlay {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: linear-gradient(to top, rgba(10,29,59,0.85) 0%, rgba(10,29,59,0.3) 60%, transparent 100%);
|
||||
z-index: 1;
|
||||
}
|
||||
.post-hero .hero-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.85;
|
||||
color: #333;
|
||||
}
|
||||
.post-content h2, .post-content h3, .post-content h4 {
|
||||
color: #0a1d3b;
|
||||
font-weight: 700;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.post-content p { margin-bottom: 1.25rem; }
|
||||
.post-content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 10px;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
.post-content blockquote {
|
||||
border-left: 4px solid #0e55bb;
|
||||
padding: 1rem 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
background: #f0f7ff;
|
||||
border-radius: 0 8px 8px 0;
|
||||
font-style: italic;
|
||||
color: #444;
|
||||
}
|
||||
.post-content ul, .post-content ol {
|
||||
padding-left: 1.5rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.post-content li { margin-bottom: 0.5rem; }
|
||||
|
||||
.post-meta {
|
||||
font-size: 0.85rem;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.post-meta .badge-cat {
|
||||
background: linear-gradient(135deg, #0e55bb, #1186cc);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
padding: 5px 14px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.author-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 1.25rem;
|
||||
background: #f8fafd;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e8eef5;
|
||||
}
|
||||
.author-avatar {
|
||||
width: 48px; height: 48px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #0e55bb, #1186cc);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: white; font-weight: 700; font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.share-buttons a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px; height: 40px;
|
||||
border-radius: 10px;
|
||||
background: #f0f4f8;
|
||||
color: #555;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
.share-buttons a:hover {
|
||||
background: #0e55bb;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.related-card {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.related-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 30px rgba(10,29,59,0.15);
|
||||
}
|
||||
.related-card img { height: 180px; object-fit: cover; }
|
||||
.related-card .card-title {
|
||||
font-size: 0.95rem; font-weight: 700; color: #0a1d3b;
|
||||
display: -webkit-box; -webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical; overflow: hidden;
|
||||
}
|
||||
a.related-link { text-decoration: none; color: inherit; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php include dirname(__DIR__) . '/menu-general.htm'; ?>
|
||||
|
||||
<?php if ($notFound): ?>
|
||||
<!-- 404 -->
|
||||
<div class="container py-5 text-center" style="min-height: 60vh; display: flex; flex-direction: column; justify-content: center;">
|
||||
<h1 class="display-1 fw-bold" style="color: #0a1d3b;">404</h1>
|
||||
<p class="lead">El artículo que buscas no existe o fue eliminado.</p>
|
||||
<a href="<?= BLOG_URL ?>" class="btn px-4 py-2 mt-3" style="background: linear-gradient(135deg, #0e55bb, #1186cc); color: white; border-radius: 8px;">Volver al blog</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<!-- Post Hero -->
|
||||
<div class="post-hero">
|
||||
<?php if ($post['cover_image']): ?>
|
||||
<img src="<?= e($post['cover_image']) ?>" alt="<?= e($post['title']) ?>">
|
||||
<?php else: ?>
|
||||
<div style="width:100%;height:100%;background: linear-gradient(135deg, #0a1d3b 0%, #0e55bb 50%, #1186cc 100%);"></div>
|
||||
<?php endif; ?>
|
||||
<div class="overlay"></div>
|
||||
<div class="container hero-content">
|
||||
<div class="post-meta mb-2">
|
||||
<?php if ($post['category_name']): ?>
|
||||
<a href="<?= BLOG_URL ?>/categoria/<?= e($post['category_slug']) ?>" class="badge badge-cat text-decoration-none text-white"><?= e($post['category_name']) ?></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<h1 class="text-white fw-bold mb-2" style="font-size: 2.2rem; line-height: 1.3; max-width: 800px;"><?= e($post['title']) ?></h1>
|
||||
<div class="post-meta d-flex align-items-center gap-3 flex-wrap">
|
||||
<span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-person me-1" viewBox="0 0 16 16">
|
||||
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10s-3.516.68-4.168 1.332c-.678.678-.83 1.418-.832 1.664z"/>
|
||||
</svg>
|
||||
<?= e($post['author'] ?: 'TRIMAPE') ?>
|
||||
</span>
|
||||
<span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-calendar3 me-1" viewBox="0 0 16 16">
|
||||
<path d="M14 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M1 3.857C1 3.384 1.448 3 2 3h12c.552 0 1 .384 1 .857v10.286c0 .473-.448.857-1 .857H2c-.552 0-1-.384-1-.857z"/>
|
||||
<path d="M6.5 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2"/>
|
||||
</svg>
|
||||
<?= date('d \d\e F, Y', strtotime($post['published_at'])) ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Post Content -->
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-lg-8">
|
||||
<!-- Share buttons -->
|
||||
<div class="share-buttons d-flex gap-2 mb-4">
|
||||
<a href="https://www.facebook.com/sharer/sharer.php?u=<?= urlencode(BLOG_URL . '/' . $post['slug']) ?>" target="_blank" rel="noopener" title="Compartir en Facebook">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M16 8.049c0-4.446-3.582-8.05-8-8.05C3.58 0-.002 3.603-.002 8.05c0 4.017 2.926 7.347 6.75 7.951v-5.625h-2.03V8.05H6.75V6.275c0-2.017 1.195-3.131 3.022-3.131.876 0 1.791.157 1.791.157v1.98h-1.009c-.993 0-1.303.621-1.303 1.258v1.51h2.218l-.354 2.326H9.25V16c3.824-.604 6.75-3.934 6.75-7.951"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://twitter.com/intent/tweet?url=<?= urlencode(BLOG_URL . '/' . $post['slug']) ?>&text=<?= urlencode($post['title']) ?>" target="_blank" rel="noopener" title="Compartir en X">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/shareArticle?mini=true&url=<?= urlencode(BLOG_URL . '/' . $post['slug']) ?>&title=<?= urlencode($post['title']) ?>" target="_blank" rel="noopener" title="Compartir en LinkedIn">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://wa.me/?text=<?= urlencode($post['title'] . ' ' . BLOG_URL . '/' . $post['slug']) ?>" target="_blank" rel="noopener" title="Compartir en WhatsApp">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M13.601 2.326A7.85 7.85 0 0 0 7.994 0C3.627 0 .068 3.558.064 7.926c0 1.399.366 2.76 1.057 3.965L0 16l4.204-1.102a7.9 7.9 0 0 0 3.79.965h.004c4.368 0 7.926-3.558 7.93-7.93A7.9 7.9 0 0 0 13.6 2.326zM7.994 14.521a6.6 6.6 0 0 1-3.356-.92l-.24-.144-2.494.654.666-2.433-.156-.251a6.56 6.56 0 0 1-1.007-3.505c0-3.626 2.957-6.584 6.591-6.584a6.56 6.56 0 0 1 4.66 1.931 6.56 6.56 0 0 1 1.928 4.66c-.004 3.639-2.961 6.592-6.592 6.592m3.615-4.934c-.197-.099-1.17-.578-1.353-.646-.182-.065-.315-.099-.445.099-.133.197-.513.646-.627.775-.114.133-.232.148-.43.05-.197-.1-.836-.308-1.592-.985-.59-.525-.985-1.175-1.103-1.372-.114-.198-.011-.304.088-.403.087-.088.197-.232.296-.346.1-.114.133-.198.198-.33.065-.134.034-.248-.015-.347-.05-.099-.445-1.076-.612-1.47-.16-.389-.323-.335-.445-.34-.114-.007-.247-.007-.38-.007a.73.73 0 0 0-.529.247c-.182.198-.691.677-.691 1.654s.71 1.916.81 2.049c.098.133 1.394 2.132 3.383 2.992.47.205.84.326 1.129.418.475.152.904.129 1.246.08.38-.058 1.171-.48 1.338-.943.164-.464.164-.86.114-.943-.049-.084-.182-.133-.38-.232"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Article body -->
|
||||
<article class="post-content">
|
||||
<?= $post['content'] ?>
|
||||
</article>
|
||||
|
||||
<!-- Author -->
|
||||
<div class="author-box mt-5">
|
||||
<div class="author-avatar"><?= strtoupper(mb_substr($post['author'] ?: 'T', 0, 1)) ?></div>
|
||||
<div>
|
||||
<div class="fw-bold" style="color: #0a1d3b;"><?= e($post['author'] ?: 'TRIMAPE') ?></div>
|
||||
<div style="font-size: 0.8rem; color: #888;">Publicado el <?= date('d \d\e F, Y', strtotime($post['published_at'])) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Back to blog -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="<?= BLOG_URL ?>" class="btn px-4 py-2 fw-bold" style="background: linear-gradient(135deg, #0e55bb, #1186cc); color: white; border-radius: 8px;">
|
||||
← Volver al blog
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Related Posts -->
|
||||
<?php if (!empty($related)): ?>
|
||||
<div class="mt-5 pt-4 border-top">
|
||||
<h4 class="fw-bold mb-4 text-center" style="color: #0a1d3b;">Artículos relacionados</h4>
|
||||
<div class="row g-4">
|
||||
<?php foreach ($related as $rel): ?>
|
||||
<div class="col-12 col-md-4">
|
||||
<a href="<?= BLOG_URL ?>/<?= e($rel['slug']) ?>" class="related-link">
|
||||
<div class="card related-card h-100">
|
||||
<?php if ($rel['cover_image']): ?>
|
||||
<img src="<?= e($rel['cover_image']) ?>" class="card-img-top" alt="<?= e($rel['title']) ?>">
|
||||
<?php else: ?>
|
||||
<div class="card-img-top d-flex align-items-center justify-content-center" style="background: linear-gradient(135deg, #0a1d3b, #1186cc); height: 180px;">
|
||||
<img src="<?= SITE_URL ?>/assets/img/trimape-logo-512-300x300.png" alt="TRIMAPE" style="width: 60px; opacity: 0.4;">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title"><?= e($rel['title']) ?></h6>
|
||||
<small class="text-muted"><?= date('d M Y', strtotime($rel['published_at'])) ?></small>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php include dirname(__DIR__) . '/contacto-div.htm'; ?>
|
||||
<?php include dirname(__DIR__) . '/footer.htm'; ?>
|
||||
<?php include dirname(__DIR__) . '/footer-scripts.htm'; ?>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user