Files

411 lines
18 KiB
PHP
Executable File

<?php
/**
* MSPE Pages API
* Stores metadata, SEO fields, and section content for static pages.
* Also manages global sections (header, footer, CTA, cookie banner).
*/
require_once 'config.php';
$method = $_SERVER['REQUEST_METHOD'];
$id = $_GET['id'] ?? null;
$type = $_GET['type'] ?? null; // 'global' for global sections
// CSRF protection for write operations
if ($method !== 'GET') {
requireSameOriginRequest();
}
switch ($method) {
case 'GET':
if ($type === 'global') {
getGlobalSections();
} elseif ($id) {
getPage($id);
} else {
getPages();
}
break;
case 'POST':
requireAuth();
if ($type === 'global') {
saveGlobalSection();
} else {
savePage();
}
break;
default:
jsonResponse(['success' => false, 'message' => 'Method not allowed'], 405);
}
// ── Page defaults ────────────────────────────────────────────
function getDefaultPages() {
return [
[
'slug' => 'home',
'title' => 'Home Page',
'icon' => 'fa-home',
'url' => '/index.html',
'description' => 'Main landing page with hero, services, and CTA sections',
'status' => 'published',
'meta_title' => 'MSPE | Architects of Digital Resilience',
'meta_description' => 'Next-generation cybersecurity, cloud innovation, and technology consulting.',
'sections' => [
['key' => 'hero', 'label' => 'Hero', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'subheading', 'type' => 'textarea', 'label' => 'Subheading', 'value' => ''],
['name' => 'cta_text', 'type' => 'text', 'label' => 'CTA Button Text', 'value' => ''],
['name' => 'cta_link', 'type' => 'text', 'label' => 'CTA Button Link', 'value' => ''],
]],
['key' => 'services', 'label' => 'Services', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Section Heading', 'value' => ''],
['name' => 'subheading', 'type' => 'textarea', 'label' => 'Section Subheading', 'value' => ''],
]],
['key' => 'stats', 'label' => 'Stats', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Section Heading', 'value' => ''],
]],
['key' => 'cta', 'label' => 'CTA', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'CTA Heading', 'value' => ''],
['name' => 'text', 'type' => 'textarea', 'label' => 'CTA Text', 'value' => ''],
['name' => 'button_text', 'type' => 'text', 'label' => 'Button Text', 'value' => ''],
['name' => 'button_link', 'type' => 'text', 'label' => 'Button Link', 'value' => ''],
]],
],
],
[
'slug' => 'about',
'title' => 'About Us',
'icon' => 'fa-building',
'url' => '/about.html',
'description' => 'Company story, mission, values, and team information',
'status' => 'published',
'meta_title' => 'About Us - MSPE',
'meta_description' => 'Learn about MSPE — our story, mission, values, and the team behind your digital resilience.',
'sections' => [
['key' => 'story', 'label' => 'Story', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'content', 'type' => 'textarea', 'label' => 'Content', 'value' => ''],
]],
['key' => 'mission', 'label' => 'Mission', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'content', 'type' => 'textarea', 'label' => 'Content', 'value' => ''],
]],
['key' => 'values', 'label' => 'Values', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
]],
['key' => 'team', 'label' => 'Team', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'subheading', 'type' => 'textarea', 'label' => 'Subheading', 'value' => ''],
]],
],
],
[
'slug' => 'services',
'title' => 'Services',
'icon' => 'fa-concierge-bell',
'url' => '/services.html',
'description' => 'Detailed service offerings and pricing plans',
'status' => 'published',
'meta_title' => 'Services - MSPE',
'meta_description' => 'Explore our cybersecurity, cloud, and IT support services.',
'sections' => [
['key' => 'it_support', 'label' => 'IT Support', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'content', 'type' => 'textarea', 'label' => 'Content', 'value' => ''],
]],
['key' => 'security', 'label' => 'Security', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'content', 'type' => 'textarea', 'label' => 'Content', 'value' => ''],
]],
['key' => 'cloud', 'label' => 'Cloud', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'content', 'type' => 'textarea', 'label' => 'Content', 'value' => ''],
]],
['key' => 'pricing', 'label' => 'Pricing', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
]],
],
],
[
'slug' => 'portfolio',
'title' => 'Portfolio',
'icon' => 'fa-briefcase',
'url' => '/portfolio.html',
'description' => 'Showcase of completed projects and case studies',
'status' => 'published',
'meta_title' => 'Portfolio - MSPE',
'meta_description' => 'See our completed projects, case studies, and client success stories.',
'sections' => [
['key' => 'projects', 'label' => 'Projects', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'subheading', 'type' => 'textarea', 'label' => 'Subheading', 'value' => ''],
]],
['key' => 'case_studies', 'label' => 'Case Studies', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
]],
['key' => 'clients', 'label' => 'Clients', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
]],
],
],
[
'slug' => 'news',
'title' => 'News & Events',
'icon' => 'fa-newspaper',
'url' => '/news.html',
'description' => 'Company news, blog posts, and upcoming events',
'status' => 'published',
'meta_title' => 'News & Events - MSPE',
'meta_description' => 'Stay updated with the latest MSPE news, tech insights, and events.',
'sections' => [
['key' => 'news_list', 'label' => 'News List', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
]],
['key' => 'newsletter', 'label' => 'Newsletter', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'subheading', 'type' => 'textarea', 'label' => 'Subheading', 'value' => ''],
]],
],
],
[
'slug' => 'contact',
'title' => 'Contact',
'icon' => 'fa-envelope',
'url' => '/contact.html',
'description' => 'Contact form, location, and FAQ section',
'status' => 'published',
'meta_title' => 'Contact Us - MSPE',
'meta_description' => 'Get in touch with MSPE for cybersecurity, cloud, and IT services.',
'sections' => [
['key' => 'form', 'label' => 'Form', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'subheading', 'type' => 'textarea', 'label' => 'Subheading', 'value' => ''],
]],
['key' => 'info_cards', 'label' => 'Info Cards', 'fields' => [
['name' => 'address', 'type' => 'textarea', 'label' => 'Address', 'value' => ''],
['name' => 'phone', 'type' => 'text', 'label' => 'Phone', 'value' => ''],
['name' => 'email', 'type' => 'text', 'label' => 'Email', 'value' => ''],
['name' => 'hours', 'type' => 'text', 'label' => 'Business Hours', 'value' => ''],
]],
['key' => 'faq', 'label' => 'FAQ', 'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
]],
],
],
];
}
function getDefaultGlobalSections() {
return [
[
'slug' => 'header',
'label' => 'Header / Navigation',
'icon' => 'fa-bars',
'description' => 'Logo, menu items, CTA button',
'fields' => [
['name' => 'cta_text', 'type' => 'text', 'label' => 'CTA Button Text', 'value' => ''],
['name' => 'cta_link', 'type' => 'text', 'label' => 'CTA Button Link', 'value' => ''],
],
],
[
'slug' => 'footer',
'label' => 'Footer',
'icon' => 'fa-shoe-prints',
'description' => 'Links, contact info, social media',
'fields' => [
['name' => 'tagline', 'type' => 'textarea', 'label' => 'Footer Tagline', 'value' => ''],
['name' => 'copyright', 'type' => 'text', 'label' => 'Copyright Text', 'value' => ''],
],
],
[
'slug' => 'cta',
'label' => 'CTA Section',
'icon' => 'fa-bullhorn',
'description' => 'Call-to-action banner',
'fields' => [
['name' => 'heading', 'type' => 'text', 'label' => 'Heading', 'value' => ''],
['name' => 'text', 'type' => 'textarea', 'label' => 'Text', 'value' => ''],
['name' => 'button_text', 'type' => 'text', 'label' => 'Button Text', 'value' => ''],
['name' => 'button_link', 'type' => 'text', 'label' => 'Button Link', 'value' => ''],
],
],
[
'slug' => 'cookie',
'label' => 'Cookie Banner',
'icon' => 'fa-cookie',
'description' => 'GDPR compliance notice',
'fields' => [
['name' => 'message', 'type' => 'textarea', 'label' => 'Banner Message', 'value' => ''],
['name' => 'button_text', 'type' => 'text', 'label' => 'Accept Button Text', 'value' => ''],
['name' => 'link_text', 'type' => 'text', 'label' => 'Policy Link Text', 'value' => ''],
['name' => 'link_url', 'type' => 'text', 'label' => 'Policy URL', 'value' => ''],
],
],
];
}
// ── Handlers ─────────────────────────────────────────────────
function getPages() {
global $db;
$storedPages = $db->getAll('pages');
$storedMap = [];
foreach ($storedPages as $p) {
$slug = $p['slug'] ?? '';
if ($slug) $storedMap[$slug] = $p;
}
$pages = getDefaultPages();
foreach ($pages as &$page) {
if (isset($storedMap[$page['slug']])) {
$stored = $storedMap[$page['slug']];
// Merge saved values into section fields
if (!empty($stored['meta_title'])) $page['meta_title'] = $stored['meta_title'];
if (!empty($stored['meta_description'])) $page['meta_description'] = $stored['meta_description'];
if (isset($stored['status'])) $page['status'] = $stored['status'];
if (!empty($stored['sections_data'])) {
$savedData = $stored['sections_data']; // key => { fieldName => value }
foreach ($page['sections'] as &$section) {
if (isset($savedData[$section['key']])) {
foreach ($section['fields'] as &$field) {
if (isset($savedData[$section['key']][$field['name']])) {
$field['value'] = $savedData[$section['key']][$field['name']];
}
}
}
}
}
if (isset($stored['updated_at'])) $page['updated_at'] = $stored['updated_at'];
}
}
jsonResponse(['success' => true, 'data' => $pages]);
}
function getPage($id) {
global $db;
$pages = getDefaultPages();
$default = null;
foreach ($pages as $p) {
if ($p['slug'] === $id) { $default = $p; break; }
}
if (!$default) {
jsonResponse(['success' => false, 'message' => 'Page not found'], 404);
}
$stored = $db->query('pages', ['slug' => $id]);
if (!empty($stored)) {
$stored = array_values($stored)[0];
if (!empty($stored['meta_title'])) $default['meta_title'] = $stored['meta_title'];
if (!empty($stored['meta_description'])) $default['meta_description'] = $stored['meta_description'];
if (isset($stored['status'])) $default['status'] = $stored['status'];
if (!empty($stored['sections_data'])) {
$savedData = $stored['sections_data'];
foreach ($default['sections'] as &$section) {
if (isset($savedData[$section['key']])) {
foreach ($section['fields'] as &$field) {
if (isset($savedData[$section['key']][$field['name']])) {
$field['value'] = $savedData[$section['key']][$field['name']];
}
}
}
}
}
if (isset($stored['updated_at'])) $default['updated_at'] = $stored['updated_at'];
}
jsonResponse(['success' => true, 'data' => $default]);
}
function savePage() {
global $db;
$data = getRequestBody();
if (empty($data['slug'])) {
jsonResponse(['success' => false, 'message' => 'Page slug required'], 400);
}
$slug = sanitize($data['slug']);
$record = [
'slug' => $slug,
'meta_title' => sanitize($data['meta_title'] ?? ''),
'meta_description' => sanitize($data['meta_description'] ?? ''),
'status' => in_array($data['status'] ?? '', ['published', 'draft']) ? $data['status'] : 'published',
'sections_data' => $data['sections_data'] ?? [],
'updated_at' => date('Y-m-d H:i:s'),
];
$existing = $db->query('pages', ['slug' => $slug]);
if (!empty($existing)) {
$id = array_values($existing)[0]['id'];
$db->update('pages', $id, $record);
} else {
$db->insert('pages', $record);
}
jsonResponse(['success' => true, 'message' => 'Page saved successfully']);
}
// ── Global sections ──────────────────────────────────────────
function getGlobalSections() {
global $db;
$stored = $db->query('pages', ['slug' => '__global__']);
$savedData = [];
if (!empty($stored)) {
$savedData = array_values($stored)[0]['sections_data'] ?? [];
}
$globals = getDefaultGlobalSections();
foreach ($globals as &$section) {
if (isset($savedData[$section['slug']])) {
foreach ($section['fields'] as &$field) {
if (isset($savedData[$section['slug']][$field['name']])) {
$field['value'] = $savedData[$section['slug']][$field['name']];
}
}
}
}
jsonResponse(['success' => true, 'data' => $globals]);
}
function saveGlobalSection() {
global $db;
$data = getRequestBody();
if (empty($data['section_slug'])) {
jsonResponse(['success' => false, 'message' => 'Section slug required'], 400);
}
$sectionSlug = sanitize($data['section_slug']);
$fieldValues = $data['fields'] ?? [];
// Load existing global record
$existing = $db->query('pages', ['slug' => '__global__']);
$record = [];
if (!empty($existing)) {
$record = array_values($existing)[0];
}
$sectionsData = $record['sections_data'] ?? [];
$sectionsData[$sectionSlug] = $fieldValues;
$saveData = [
'slug' => '__global__',
'sections_data' => $sectionsData,
'updated_at' => date('Y-m-d H:i:s'),
];
if (!empty($existing)) {
$id = array_values($existing)[0]['id'];
$db->update('pages', $id, $saveData);
} else {
$db->insert('pages', $saveData);
}
jsonResponse(['success' => true, 'message' => 'Global section saved']);
}