Initial commit: MSPE website - full site with admin panel, API, and public pages
This commit is contained in:
Executable
+273
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
/**
|
||||
* MSPE News/Articles API
|
||||
*/
|
||||
|
||||
require_once 'config.php';
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
if ($id) {
|
||||
getArticle($id);
|
||||
} else {
|
||||
getArticles();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
requireAuth();
|
||||
if (!empty($_POST['id'])) {
|
||||
updateArticle($_POST['id']);
|
||||
} else {
|
||||
createArticle();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
requireAuth();
|
||||
if (!$id) {
|
||||
jsonResponse(['success' => false, 'message' => 'Article ID required'], 400);
|
||||
}
|
||||
updateArticle($id);
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
requireAuth();
|
||||
if (!$id) {
|
||||
jsonResponse(['success' => false, 'message' => 'Article ID required'], 400);
|
||||
}
|
||||
deleteArticle($id);
|
||||
break;
|
||||
|
||||
default:
|
||||
jsonResponse(['success' => false, 'message' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
function getArticles() {
|
||||
global $db;
|
||||
|
||||
$articles = $db->getAll('news');
|
||||
|
||||
// Apply filters
|
||||
$category = $_GET['category'] ?? null;
|
||||
$status = $_GET['status'] ?? null;
|
||||
$limit = (int)($_GET['limit'] ?? 10);
|
||||
$offset = (int)($_GET['offset'] ?? 0);
|
||||
|
||||
if ($category) {
|
||||
$articles = array_filter($articles, function($a) use ($category) {
|
||||
return $a['category'] === $category;
|
||||
});
|
||||
}
|
||||
|
||||
if ($status) {
|
||||
$articles = array_filter($articles, function($a) use ($status) {
|
||||
return $a['status'] === $status;
|
||||
});
|
||||
}
|
||||
|
||||
// Sort by date (newest first)
|
||||
usort($articles, function($a, $b) {
|
||||
return strtotime($b['created_at']) - strtotime($a['created_at']);
|
||||
});
|
||||
|
||||
// For public API, only show published articles
|
||||
if (!checkAuth()) {
|
||||
$articles = array_filter($articles, function($a) {
|
||||
return ($a['status'] ?? 'draft') === 'published';
|
||||
});
|
||||
}
|
||||
|
||||
$total = count($articles);
|
||||
$articles = array_slice(array_values($articles), $offset, $limit);
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'data' => $articles,
|
||||
'total' => $total,
|
||||
'limit' => $limit,
|
||||
'offset' => $offset
|
||||
]);
|
||||
}
|
||||
|
||||
function getArticle($id) {
|
||||
global $db;
|
||||
|
||||
$article = $db->get('news', $id);
|
||||
|
||||
if (!$article) {
|
||||
jsonResponse(['success' => false, 'message' => 'Article not found'], 404);
|
||||
}
|
||||
|
||||
// Check if published or user is authenticated
|
||||
if (($article['status'] ?? 'draft') !== 'published' && !checkAuth()) {
|
||||
jsonResponse(['success' => false, 'message' => 'Article not found'], 404);
|
||||
}
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'data' => $article
|
||||
]);
|
||||
}
|
||||
|
||||
function createArticle() {
|
||||
global $db;
|
||||
|
||||
// Handle multipart form data or JSON
|
||||
if (!empty($_FILES)) {
|
||||
$data = $_POST;
|
||||
} else {
|
||||
$data = getRequestBody();
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (empty($data['title'])) {
|
||||
jsonResponse(['success' => false, 'message' => 'Title is required'], 400);
|
||||
}
|
||||
|
||||
// Sanitize text fields (allow safe HTML in content)
|
||||
$data['title'] = sanitize($data['title']);
|
||||
$data['excerpt'] = sanitize($data['excerpt'] ?? '');
|
||||
$data['category'] = sanitize($data['category'] ?? '');
|
||||
$data['author'] = sanitize($data['author'] ?? 'Admin');
|
||||
// Sanitize rich content: strip dangerous tags/attributes while allowing formatting
|
||||
if (!empty($data['content'])) {
|
||||
$data['content'] = sanitizeRichText($data['content']);
|
||||
}
|
||||
|
||||
// Handle image upload
|
||||
if (!empty($_FILES['featured_image'])) {
|
||||
$upload = handleFileUpload($_FILES['featured_image'], 'news');
|
||||
if ($upload['success']) {
|
||||
$data['featured_image'] = $upload['path'];
|
||||
}
|
||||
}
|
||||
|
||||
// Generate slug if not provided
|
||||
if (empty($data['slug'])) {
|
||||
$data['slug'] = generateSlug($data['title']);
|
||||
} else {
|
||||
$data['slug'] = generateSlug($data['slug']);
|
||||
}
|
||||
|
||||
$data['slug'] = ensureUniqueNewsSlug($data['slug']);
|
||||
|
||||
// Set defaults
|
||||
$data['status'] = $data['status'] ?? 'draft';
|
||||
$data['author'] = $data['author'] ?? 'Admin';
|
||||
$data['views'] = 0;
|
||||
|
||||
$article = $db->insert('news', $data);
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'message' => 'Article created successfully',
|
||||
'data' => $article
|
||||
], 201);
|
||||
}
|
||||
|
||||
function updateArticle($id) {
|
||||
global $db;
|
||||
|
||||
$existing = $db->get('news', $id);
|
||||
if (!$existing) {
|
||||
jsonResponse(['success' => false, 'message' => 'Article not found'], 404);
|
||||
}
|
||||
|
||||
// Handle multipart form data or JSON
|
||||
if (!empty($_FILES)) {
|
||||
$data = $_POST;
|
||||
} else {
|
||||
$data = getRequestBody();
|
||||
}
|
||||
|
||||
// Handle image upload
|
||||
if (!empty($_FILES['featured_image'])) {
|
||||
$upload = handleFileUpload($_FILES['featured_image'], 'news');
|
||||
if ($upload['success']) {
|
||||
$data['featured_image'] = $upload['path'];
|
||||
}
|
||||
}
|
||||
|
||||
// Update slug if title changed
|
||||
if (!empty($data['title']) && empty($data['slug'])) {
|
||||
$data['slug'] = generateSlug($data['title']);
|
||||
} elseif (!empty($data['slug'])) {
|
||||
$data['slug'] = generateSlug($data['slug']);
|
||||
}
|
||||
|
||||
if (!empty($data['slug'])) {
|
||||
$data['slug'] = ensureUniqueNewsSlug($data['slug'], $id);
|
||||
}
|
||||
|
||||
// Sanitize text fields
|
||||
if (!empty($data['title'])) $data['title'] = sanitize($data['title']);
|
||||
if (!empty($data['excerpt'])) $data['excerpt'] = sanitize($data['excerpt']);
|
||||
if (!empty($data['category'])) $data['category'] = sanitize($data['category']);
|
||||
if (!empty($data['author'])) $data['author'] = sanitize($data['author']);
|
||||
// Sanitize rich content: strip dangerous tags/attributes while allowing formatting
|
||||
if (!empty($data['content'])) {
|
||||
$data['content'] = sanitizeRichText($data['content']);
|
||||
}
|
||||
|
||||
$article = $db->update('news', $id, $data);
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'message' => 'Article updated successfully',
|
||||
'data' => $article
|
||||
]);
|
||||
}
|
||||
|
||||
function deleteArticle($id) {
|
||||
global $db;
|
||||
|
||||
$existing = $db->get('news', $id);
|
||||
if (!$existing) {
|
||||
jsonResponse(['success' => false, 'message' => 'Article not found'], 404);
|
||||
}
|
||||
|
||||
$db->delete('news', $id);
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'message' => 'Article deleted successfully'
|
||||
]);
|
||||
}
|
||||
|
||||
function generateSlug($title) {
|
||||
$slug = strtolower($title);
|
||||
$slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
|
||||
$slug = trim($slug, '-');
|
||||
return $slug !== '' ? $slug : 'article';
|
||||
}
|
||||
|
||||
function ensureUniqueNewsSlug($baseSlug, $excludeId = null) {
|
||||
global $db;
|
||||
|
||||
$articles = $db->getAll('news');
|
||||
$used = [];
|
||||
foreach ($articles as $article) {
|
||||
if (!empty($excludeId) && ($article['id'] ?? null) === $excludeId) {
|
||||
continue;
|
||||
}
|
||||
$slug = (string)($article['slug'] ?? '');
|
||||
if ($slug !== '') {
|
||||
$used[$slug] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($used[$baseSlug])) {
|
||||
return $baseSlug;
|
||||
}
|
||||
|
||||
$i = 2;
|
||||
while (isset($used[$baseSlug . '-' . $i])) {
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $baseSlug . '-' . $i;
|
||||
}
|
||||
Reference in New Issue
Block a user