411 lines
18 KiB
PHP
Executable File
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']);
|
|
}
|