SEO Improvements: - Add Twitter/OG meta tags to gdpr-compliance.php and terms-of-service.php - Add author bios with E-E-A-T signals to all blog articles - Add breadcrumb schema markup to key pages - Create new service pages: price-monitoring.php, competitive-intelligence.php - Create location pages: london.php, manchester.php, birmingham.php - Update sitemap.xml with new pages UI/CSS Improvements: - Move testimonials section from inline styles to CSS classes - Standardize hero gradients to #144784 → #179e83 across all pages - Unify card border styles to border-left: 4px solid #179e83 - Fix CTA gradient direction consistency on location pages - Standardize breadcrumb colors to #144784 - Replace all purple accent colors (#667eea, #764ba2) with brand colors - Add CSS variables for brand consistency in main.css Code Cleanup: - Delete 6 emergency CSS fix files (button-emergency-fix.css, etc.) - Remove related-articles-fix.css references from blog articles - Consolidate styles into main.css 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
720 lines
24 KiB
PHP
720 lines
24 KiB
PHP
<?php
|
|
// Quote Form Handler with Enhanced Security
|
|
// Ensure session cookie is available for AJAX requests
|
|
ini_set('session.cookie_samesite', 'Lax');
|
|
ini_set('session.cookie_httponly', '1');
|
|
ini_set('session.cookie_secure', '0'); // Set to '1' if using HTTPS
|
|
session_start();
|
|
|
|
// Security headers
|
|
header('X-Content-Type-Options: nosniff');
|
|
header('X-Frame-Options: DENY');
|
|
header('X-XSS-Protection: 1; mode=block');
|
|
|
|
// CSRF Protection
|
|
function generateCSRFToken() {
|
|
if (!isset($_SESSION['csrf_token'])) {
|
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
}
|
|
return $_SESSION['csrf_token'];
|
|
}
|
|
|
|
function validateCSRFToken($token) {
|
|
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
|
|
}
|
|
|
|
// Rate limiting
|
|
function checkRateLimit() {
|
|
$ip = $_SERVER['REMOTE_ADDR'];
|
|
$key = 'quote_' . md5($ip);
|
|
|
|
if (!isset($_SESSION[$key])) {
|
|
$_SESSION[$key] = ['count' => 0, 'time' => time()];
|
|
}
|
|
|
|
$data = $_SESSION[$key];
|
|
|
|
if (time() - $data['time'] > 3600) {
|
|
$_SESSION[$key] = ['count' => 0, 'time' => time()];
|
|
$data = $_SESSION[$key];
|
|
}
|
|
|
|
if ($data['count'] >= 3) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Input validation
|
|
function validateInput($data, $type = 'text') {
|
|
$data = trim($data);
|
|
$data = stripslashes($data);
|
|
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
|
|
|
|
switch ($type) {
|
|
case 'email':
|
|
return filter_var($data, FILTER_VALIDATE_EMAIL) ? $data : false;
|
|
case 'phone':
|
|
return preg_match('/^[\+]?[0-9\s\-\(\)]+$/', $data) ? $data : false;
|
|
case 'text':
|
|
return strlen($data) > 0 ? $data : false;
|
|
case 'long_text':
|
|
return strlen($data) >= 20 ? $data : false;
|
|
default:
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
// Check if request is AJAX
|
|
function isAjaxRequest() {
|
|
return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
|
|
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
|
|
}
|
|
|
|
// Response function
|
|
function sendResponse($success, $message, $data = null) {
|
|
// If AJAX request, send JSON
|
|
if (isAjaxRequest()) {
|
|
$response = [
|
|
'success' => $success,
|
|
'message' => $message
|
|
];
|
|
|
|
if ($data !== null) {
|
|
$response['data'] = $data;
|
|
}
|
|
|
|
header('Content-Type: application/json');
|
|
echo json_encode($response);
|
|
exit;
|
|
}
|
|
|
|
// Otherwise, display HTML response page
|
|
displayHTMLResponse($success, $message);
|
|
}
|
|
|
|
// Display HTML response page
|
|
function displayHTMLResponse($success, $message) {
|
|
$pageTitle = $success ? 'Quote Request Sent Successfully' : 'Quote Submission Error';
|
|
$messageClass = $success ? 'success' : 'error';
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title><?php echo htmlspecialchars($pageTitle); ?> - UK Data Services</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
line-height: 1.6;
|
|
color: #333;
|
|
background: linear-gradient(135deg, #144784 0%, #179e83 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
}
|
|
|
|
.response-container {
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
|
|
max-width: 600px;
|
|
width: 100%;
|
|
padding: 40px;
|
|
text-align: center;
|
|
animation: slideUp 0.4s ease;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.logo {
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.logo img {
|
|
height: 50px;
|
|
width: auto;
|
|
}
|
|
|
|
.icon {
|
|
width: 80px;
|
|
height: 80px;
|
|
margin: 0 auto 20px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 40px;
|
|
}
|
|
|
|
.icon.success {
|
|
background: rgba(23, 158, 131, 0.1);
|
|
color: #179e83;
|
|
}
|
|
|
|
.icon.error {
|
|
background: rgba(231, 76, 60, 0.1);
|
|
color: #e74c3c;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 28px;
|
|
margin-bottom: 15px;
|
|
color: #1a1a1a;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.message {
|
|
font-size: 16px;
|
|
color: #666;
|
|
margin-bottom: 30px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.message.error {
|
|
color: #721c24;
|
|
background: #fee;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border: 1px solid #fcc;
|
|
}
|
|
|
|
.message.success {
|
|
color: #155724;
|
|
background: #efe;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border: 1px solid #cfc;
|
|
}
|
|
|
|
.buttons {
|
|
display: flex;
|
|
gap: 15px;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.btn {
|
|
display: inline-block;
|
|
padding: 14px 32px;
|
|
border-radius: 50px;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
border: none;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: #179e83;
|
|
color: white;
|
|
box-shadow: 0 4px 14px rgba(23, 158, 131, 0.25);
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 20px rgba(23, 158, 131, 0.35);
|
|
background: #15896f;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: transparent;
|
|
color: #144784;
|
|
border: 2px solid #144784;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #144784;
|
|
color: white;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 20px rgba(20, 71, 132, 0.25);
|
|
}
|
|
|
|
.contact-info {
|
|
margin-top: 30px;
|
|
padding-top: 30px;
|
|
border-top: 1px solid #e1e5e9;
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
|
|
.contact-info a {
|
|
color: #179e83;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.contact-info a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="response-container">
|
|
<div class="logo">
|
|
<img src="/assets/images/ukds-main-logo.webp" alt="UK Data Services" onerror="this.onerror=null; this.src='/assets/images/ukds-main-logo.png';">
|
|
</div>
|
|
|
|
<div class="icon <?php echo $messageClass; ?>">
|
|
<?php echo $success ? '✓' : '✕'; ?>
|
|
</div>
|
|
|
|
<h1><?php echo $success ? 'Thank You!' : 'Oops! Something Went Wrong'; ?></h1>
|
|
|
|
<div class="message <?php echo $messageClass; ?>">
|
|
<?php
|
|
if ($success) {
|
|
echo htmlspecialchars($message);
|
|
} else {
|
|
// Parse multiple error messages if present
|
|
if (strpos($message, '. ') !== false) {
|
|
$errors = explode('. ', $message);
|
|
echo '<ul style="text-align: left; padding-left: 20px; list-style: disc;">';
|
|
foreach ($errors as $error) {
|
|
if (trim($error)) {
|
|
echo '<li>' . htmlspecialchars(trim($error)) . '</li>';
|
|
}
|
|
}
|
|
echo '</ul>';
|
|
} else {
|
|
echo htmlspecialchars($message);
|
|
}
|
|
}
|
|
?>
|
|
</div>
|
|
|
|
<div class="buttons">
|
|
<?php if ($success): ?>
|
|
<a href="/" class="btn btn-primary">Return to Home</a>
|
|
<a href="/blog/" class="btn btn-secondary">Read Our Blog</a>
|
|
<?php else: ?>
|
|
<button onclick="history.back()" class="btn btn-primary">Go Back & Try Again</button>
|
|
<a href="/" class="btn btn-secondary">Return to Home</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="contact-info">
|
|
<?php if ($success): ?>
|
|
<p>We'll be in touch within 24 hours with a detailed proposal.</p>
|
|
<p>Have questions? Email us at <a href="mailto:info@ukdataservices.co.uk">info@ukdataservices.co.uk</a></p>
|
|
<?php else: ?>
|
|
<p>If you continue to experience issues, please contact us directly:</p>
|
|
<p>Email: <a href="mailto:info@ukdataservices.co.uk">info@ukdataservices.co.uk</a></p>
|
|
<p>Phone: +44 1692 689150</p>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
<?php
|
|
exit;
|
|
}
|
|
|
|
// Handle POST requests only
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
sendResponse(false, 'Invalid request method');
|
|
}
|
|
|
|
// Validate CSRF token
|
|
if (!isset($_POST['csrf_token'])) {
|
|
sendResponse(false, 'Security validation failed. Please refresh the page and try again.');
|
|
}
|
|
|
|
if (!validateCSRFToken($_POST['csrf_token'])) {
|
|
sendResponse(false, 'Security validation failed. Please refresh the page and try again.');
|
|
}
|
|
|
|
// Check for blocked IPs
|
|
function checkBlockedIP() {
|
|
$ip = $_SERVER['REMOTE_ADDR'];
|
|
$blockFile = 'logs/blocked-ips.txt';
|
|
|
|
if (file_exists($blockFile)) {
|
|
$blockedIPs = file($blockFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
foreach ($blockedIPs as $blockedEntry) {
|
|
$parts = explode('|', $blockedEntry);
|
|
if (isset($parts[0]) && $parts[0] === $ip) {
|
|
$blockTime = isset($parts[1]) ? (int)$parts[1] : 0;
|
|
// Block for 24 hours
|
|
if (time() - $blockTime < 86400) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Check for blocked IPs first
|
|
if (!checkBlockedIP()) {
|
|
sendResponse(false, 'Access temporarily restricted');
|
|
}
|
|
|
|
// Check rate limiting
|
|
if (!checkRateLimit()) {
|
|
sendResponse(false, 'Too many requests. Please try again later.');
|
|
}
|
|
|
|
// reCAPTCHA Verification
|
|
require_once '.recaptcha-config.php';
|
|
|
|
function validateRecaptcha($token) {
|
|
if (!RECAPTCHA_ENABLED) {
|
|
// Skip validation if reCAPTCHA is disabled (test keys)
|
|
error_log("reCAPTCHA validation skipped - test keys in use");
|
|
return true;
|
|
}
|
|
|
|
if (empty($token)) {
|
|
return false;
|
|
}
|
|
|
|
$secretKey = RECAPTCHA_SECRET_KEY;
|
|
$verifyURL = 'https://www.google.com/recaptcha/api/siteverify';
|
|
|
|
$data = [
|
|
'secret' => $secretKey,
|
|
'response' => $token,
|
|
'remoteip' => $_SERVER['REMOTE_ADDR']
|
|
];
|
|
|
|
$options = [
|
|
'http' => [
|
|
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
|
'method' => 'POST',
|
|
'content' => http_build_query($data)
|
|
]
|
|
];
|
|
|
|
$context = stream_context_create($options);
|
|
$result = file_get_contents($verifyURL, false, $context);
|
|
|
|
if ($result === false) {
|
|
error_log('reCAPTCHA verification request failed');
|
|
return false;
|
|
}
|
|
|
|
$resultJson = json_decode($result, true);
|
|
|
|
if ($resultJson['success'] && isset($resultJson['score'])) {
|
|
return $resultJson['score'] >= RECAPTCHA_THRESHOLD;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Verify reCAPTCHA
|
|
$recaptchaResponse = $_POST['recaptcha_response'] ?? '';
|
|
if (!validateRecaptcha($recaptchaResponse)) {
|
|
sendResponse(false, 'Security verification failed. Please try again.');
|
|
}
|
|
|
|
// Spam protection - honeypot field
|
|
if (isset($_POST['website']) && !empty($_POST['website'])) {
|
|
sendResponse(false, 'Spam detected');
|
|
}
|
|
|
|
// Validate and sanitize inputs
|
|
$services = $_POST['services'] ?? [];
|
|
$project_scale = validateInput($_POST['project_scale'] ?? '', 'text');
|
|
$timeline = validateInput($_POST['timeline'] ?? '', 'text');
|
|
$name = validateInput($_POST['name'] ?? '', 'text');
|
|
$email = validateInput($_POST['email'] ?? '', 'email');
|
|
$company = validateInput($_POST['company'] ?? '', 'text');
|
|
$phone = validateInput($_POST['phone'] ?? '', 'phone');
|
|
$data_sources = validateInput($_POST['data_sources'] ?? '', 'text');
|
|
$requirements = validateInput($_POST['requirements'] ?? '', 'long_text');
|
|
$budget = validateInput($_POST['budget'] ?? '', 'text');
|
|
|
|
// Validation
|
|
$errors = [];
|
|
|
|
if (empty($services) || !is_array($services)) {
|
|
$errors[] = 'Please select at least one service';
|
|
}
|
|
|
|
if (!$project_scale) {
|
|
$errors[] = 'Please select a project scale';
|
|
}
|
|
|
|
if (!$timeline) {
|
|
$errors[] = 'Please select a timeline';
|
|
}
|
|
|
|
if (!$name || strlen($name) < 2) {
|
|
$errors[] = 'Please enter a valid name';
|
|
}
|
|
|
|
if (!$email) {
|
|
$errors[] = 'Please enter a valid email address';
|
|
}
|
|
|
|
if (!$requirements) {
|
|
$errors[] = 'Please provide detailed project requirements';
|
|
}
|
|
|
|
if (!empty($errors)) {
|
|
sendResponse(false, implode('. ', $errors));
|
|
}
|
|
|
|
// Enhanced spam protection - content filtering
|
|
$spamKeywords = [
|
|
'viagra', 'cialis', 'casino', 'lottery', 'bitcoin', 'forex', 'loan', 'debt',
|
|
'pharmacy', 'click here', 'act now', 'limited time', 'risk free', 'guarantee',
|
|
'no obligation', 'free money', 'make money fast', 'work from home', 'get rich',
|
|
'investment opportunity', 'credit repair', 'refinance', 'consolidate debt',
|
|
'weight loss', 'miracle cure', 'lose weight', 'adult content', 'porn',
|
|
'sex', 'dating', 'singles', 'webcam', 'escort', 'massage'
|
|
];
|
|
|
|
$contentToCheck = strtolower($requirements . ' ' . $name . ' ' . $company . ' ' . $data_sources);
|
|
foreach ($spamKeywords as $keyword) {
|
|
if (strpos($contentToCheck, $keyword) !== false) {
|
|
sendResponse(false, 'Invalid content detected');
|
|
}
|
|
}
|
|
|
|
// Bot detection - check for suspicious patterns
|
|
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
|
$suspiciousAgents = ['curl', 'wget', 'python', 'bot', 'crawler', 'spider', 'scraper'];
|
|
foreach ($suspiciousAgents as $agent) {
|
|
if (stripos($userAgent, $agent) !== false) {
|
|
sendResponse(false, 'Automated submissions not allowed');
|
|
}
|
|
}
|
|
|
|
// Check submission speed (too fast = likely bot) - More lenient timing
|
|
if (isset($_SESSION['form_start_time'])) {
|
|
$submissionTime = time() - $_SESSION['form_start_time'];
|
|
if ($submissionTime < 3) { // Only block if under 3 seconds (very aggressive bots)
|
|
sendResponse(false, 'Form submitted too quickly');
|
|
}
|
|
}
|
|
|
|
// Sanitize services array
|
|
$services = array_map(function($service) {
|
|
return htmlspecialchars(trim($service), ENT_QUOTES, 'UTF-8');
|
|
}, $services);
|
|
|
|
// Update rate limit counter
|
|
$ip = $_SERVER['REMOTE_ADDR'];
|
|
$key = 'quote_' . md5($ip);
|
|
$_SESSION[$key]['count']++;
|
|
|
|
// Create friendly service names
|
|
$service_names = [
|
|
'web-scraping' => 'Web Scraping & Data Extraction',
|
|
'business-intelligence' => 'Business Intelligence & Analytics',
|
|
'data-processing' => 'Data Processing & Cleaning',
|
|
'automation' => 'Automation & APIs',
|
|
'consulting' => 'Custom Development',
|
|
'other' => 'Other Services'
|
|
];
|
|
|
|
$selected_services = array_map(function($service) use ($service_names) {
|
|
return $service_names[$service] ?? $service;
|
|
}, $services);
|
|
|
|
// Create friendly scale names
|
|
$scale_names = [
|
|
'small' => 'Small Project (One-time extraction, < 10k records)',
|
|
'medium' => 'Medium Project (Regular updates, 10k-100k records)',
|
|
'large' => 'Large Project (Ongoing service, 100k+ records)',
|
|
'enterprise' => 'Enterprise (Complex multi-source solution)'
|
|
];
|
|
|
|
$friendly_scale = $scale_names[$project_scale] ?? $project_scale;
|
|
|
|
// Create friendly timeline names
|
|
$timeline_names = [
|
|
'asap' => 'ASAP (Rush job)',
|
|
'1-week' => 'Within 1 week',
|
|
'2-4-weeks' => '2-4 weeks',
|
|
'flexible' => 'Flexible timeline'
|
|
];
|
|
|
|
$friendly_timeline = $timeline_names[$timeline] ?? $timeline;
|
|
|
|
// Prepare email content
|
|
$to = 'info@ukdataservices.co.uk';
|
|
$subject = 'New Quote Request - UK Data Services';
|
|
|
|
// Create detailed HTML email
|
|
$emailHTML = '
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>New Quote Request</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
|
.container { max-width: 700px; margin: 0 auto; padding: 20px; }
|
|
.header { background: linear-gradient(135deg, #144784 0%, #179e83 100%); color: white; padding: 30px; text-align: center; border-radius: 8px 8px 0 0; }
|
|
.content { background: #f9f9f9; padding: 30px; border-radius: 0 0 8px 8px; }
|
|
.section { margin-bottom: 30px; padding: 20px; background: white; border-radius: 8px; border-left: 4px solid #179e83; }
|
|
.section-title { font-size: 18px; font-weight: bold; color: #144784; margin-bottom: 15px; }
|
|
.field { margin-bottom: 12px; }
|
|
.field-label { font-weight: bold; color: #555; }
|
|
.field-value { margin-top: 5px; padding: 8px; background: #f8f9fa; border-radius: 4px; }
|
|
.services-list { list-style: none; padding: 0; }
|
|
.services-list li { padding: 8px; background: #e3f2fd; margin: 5px 0; border-radius: 4px; }
|
|
.priority { background: #fff3cd; border-left-color: #ffc107; }
|
|
.contact-info { background: #d4edda; border-left-color: #28a745; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>🚀 New Quote Request</h1>
|
|
<p>UK Data Services</p>
|
|
<p style="font-size: 14px; opacity: 0.9;">Received: ' . date('Y-m-d H:i:s') . ' UTC</p>
|
|
</div>
|
|
|
|
<div class="content">
|
|
<div class="section contact-info">
|
|
<div class="section-title">👤 Contact Information</div>
|
|
<div class="field">
|
|
<div class="field-label">Name:</div>
|
|
<div class="field-value">' . htmlspecialchars($name) . '</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="field-label">Email:</div>
|
|
<div class="field-value">' . htmlspecialchars($email) . '</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="field-label">Company:</div>
|
|
<div class="field-value">' . htmlspecialchars($company ?: 'Not provided') . '</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="field-label">Phone:</div>
|
|
<div class="field-value">' . htmlspecialchars($phone ?: 'Not provided') . '</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<div class="section-title">🎯 Services Required</div>
|
|
<ul class="services-list">';
|
|
|
|
foreach ($selected_services as $service) {
|
|
$emailHTML .= '<li>✓ ' . htmlspecialchars($service) . '</li>';
|
|
}
|
|
|
|
$emailHTML .= '</ul>
|
|
</div>
|
|
|
|
<div class="section ' . ($timeline === 'asap' ? 'priority' : '') . '">
|
|
<div class="section-title">📊 Project Details</div>
|
|
<div class="field">
|
|
<div class="field-label">Project Scale:</div>
|
|
<div class="field-value">' . htmlspecialchars($friendly_scale) . '</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="field-label">Timeline:</div>
|
|
<div class="field-value">' . htmlspecialchars($friendly_timeline) . '</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="field-label">Budget Range:</div>
|
|
<div class="field-value">' . htmlspecialchars($budget ?: 'Not specified') . '</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<div class="section-title">🌐 Data Sources</div>
|
|
<div class="field-value">' . nl2br(htmlspecialchars($data_sources ?: 'Not specified')) . '</div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<div class="section-title">📝 Detailed Requirements</div>
|
|
<div class="field-value">' . nl2br(htmlspecialchars($requirements)) . '</div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<div class="section-title">🔍 Submission Details</div>
|
|
<div class="field">
|
|
<div class="field-label">IP Address:</div>
|
|
<div class="field-value">' . htmlspecialchars($_SERVER['REMOTE_ADDR']) . '</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="field-label">User Agent:</div>
|
|
<div class="field-value">' . htmlspecialchars($_SERVER['HTTP_USER_AGENT']) . '</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="field-label">Referrer:</div>
|
|
<div class="field-value">' . htmlspecialchars($_SERVER['HTTP_REFERER'] ?? 'Direct') . '</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>';
|
|
|
|
// Email headers
|
|
$headers = "MIME-Version: 1.0\r\n";
|
|
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";
|
|
$headers .= "From: \"UK Data Services Quote System\" <noreply@ukdataservices.co.uk>\r\n";
|
|
$headers .= "Reply-To: " . $email . "\r\n";
|
|
$headers .= "X-Mailer: PHP/" . phpversion() . "\r\n";
|
|
$headers .= "X-Priority: " . ($timeline === 'asap' ? '1' : '3') . "\r\n";
|
|
|
|
// Create logs directory if it doesn't exist
|
|
if (!file_exists('logs')) {
|
|
mkdir('logs', 0755, true);
|
|
}
|
|
|
|
// Send email
|
|
try {
|
|
// Clear any previous errors
|
|
error_clear_last();
|
|
|
|
$emailSent = mail($to, $subject, $emailHTML, $headers);
|
|
|
|
if ($emailSent) {
|
|
// Log successful submission
|
|
$logEntry = date('Y-m-d H:i:s') . " - Quote request from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - Services: " . implode(', ', $services) . "\n";
|
|
file_put_contents('logs/quote-requests.log', $logEntry, FILE_APPEND | LOCK_EX);
|
|
|
|
sendResponse(true, 'Thank you for your quote request! We will send you a detailed proposal within 24 hours.');
|
|
} else {
|
|
// Get detailed error information
|
|
$lastError = error_get_last();
|
|
$errorMsg = $lastError ? $lastError['message'] : 'Unknown mail error';
|
|
|
|
// Log failed email with detailed error
|
|
$logEntry = date('Y-m-d H:i:s') . " - FAILED quote request from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - Error: " . $errorMsg . "\n";
|
|
file_put_contents('logs/quote-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
|
|
|
|
// Check common issues
|
|
if (strpos($errorMsg, 'sendmail') !== false) {
|
|
error_log("Mail server configuration issue: " . $errorMsg);
|
|
}
|
|
|
|
sendResponse(false, 'There was an error sending your quote request. Please try again or contact us directly at info@ukdataservices.co.uk');
|
|
}
|
|
} catch (Exception $e) {
|
|
// Log exception with full details
|
|
$logEntry = date('Y-m-d H:i:s') . " - EXCEPTION: " . $e->getMessage() . " from " . $email . " (" . $_SERVER['REMOTE_ADDR'] . ") - File: " . $e->getFile() . " Line: " . $e->getLine() . "\n";
|
|
file_put_contents('logs/quote-errors.log', $logEntry, FILE_APPEND | LOCK_EX);
|
|
|
|
sendResponse(false, 'There was an error processing your quote request. Please contact us directly at info@ukdataservices.co.uk');
|
|
}
|
|
?>
|