Fix consultation form CSRF validation and improve site optimization
- Fix CSRF token validation for quote form by adding proper session handling and credentials to AJAX requests - Replace alert boxes with styled error/success messages and HTML response pages for better UX - Add visual indicators (*) to required form fields for better accessibility - Standardize navigation links to use absolute paths for consistency - Update JavaScript references to use minified version (main.min.js) for better performance - Archive large error log file and clean up sitemap by removing broken image references - Add comprehensive documentation to reCAPTCHA configuration about test vs production keys - Update sitemap lastmod dates to current timestamp 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
// Google reCAPTCHA v3 Configuration
|
// Google reCAPTCHA v3 Configuration
|
||||||
// IMPORTANT: Replace these with your actual keys from https://www.google.com/recaptcha/admin
|
// IMPORTANT: These are test keys. For production, replace with actual keys from https://www.google.com/recaptcha/admin
|
||||||
define('RECAPTCHA_SITE_KEY', '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'); // Google test site key
|
// To get production keys:
|
||||||
define('RECAPTCHA_SECRET_KEY', '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe'); // Google test secret key
|
// 1. Go to https://www.google.com/recaptcha/admin
|
||||||
|
// 2. Register your site domain
|
||||||
|
// 3. Choose reCAPTCHA v3
|
||||||
|
// 4. Replace the keys below with your actual keys
|
||||||
|
|
||||||
|
// WARNING: Currently using Google's test keys - forms are NOT protected!
|
||||||
|
define('RECAPTCHA_SITE_KEY', '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'); // TODO: Replace with production site key
|
||||||
|
define('RECAPTCHA_SECRET_KEY', '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe'); // TODO: Replace with production secret key
|
||||||
define('RECAPTCHA_THRESHOLD', 0.5); // Score threshold (0.0 - 1.0), higher is more strict
|
define('RECAPTCHA_THRESHOLD', 0.5); // Score threshold (0.0 - 1.0), higher is more strict
|
||||||
|
|
||||||
|
// Set to true when production keys are configured
|
||||||
|
define('RECAPTCHA_ENABLED', false); // Currently disabled - using test keys
|
||||||
?>
|
?>
|
||||||
|
|||||||
38
index.php
38
index.php
@@ -46,11 +46,11 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
<meta name="twitter:image" content="<?php echo htmlspecialchars($twitter_card_image); ?>">
|
<meta name="twitter:image" content="<?php echo htmlspecialchars($twitter_card_image); ?>">
|
||||||
|
|
||||||
<!-- Favicon and App Icons -->
|
<!-- Favicon and App Icons -->
|
||||||
<link rel="icon" type="image/svg+xml" href="assets/images/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/assets/images/favicon.svg">
|
||||||
<link rel="icon" type="image/svg+xml" sizes="16x16" href="assets/images/favicon-16x16.svg">
|
<link rel="icon" type="image/svg+xml" sizes="16x16" href="/assets/images/favicon-16x16.svg">
|
||||||
<link rel="icon" type="image/svg+xml" sizes="32x32" href="assets/images/favicon-32x32.svg">
|
<link rel="icon" type="image/svg+xml" sizes="32x32" href="/assets/images/favicon-32x32.svg">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="assets/images/apple-touch-icon.svg">
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/images/apple-touch-icon.svg">
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
<meta name="theme-color" content="#764ba2">
|
<meta name="theme-color" content="#764ba2">
|
||||||
<meta name="msapplication-TileColor" content="#667eea">
|
<meta name="msapplication-TileColor" content="#667eea">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
@@ -102,9 +102,9 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Lato:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Lato:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Resource Preloading for Performance -->
|
<!-- Resource Preloading for Performance -->
|
||||||
<link rel="preload" href="assets/css/main.min.css" as="style">
|
<link rel="preload" href="/assets/css/main.min.css" as="style">
|
||||||
<link rel="preload" href="assets/images/ukds-main-logo.webp" as="image">
|
<link rel="preload" href="/assets/images/ukds-main-logo.webp" as="image">
|
||||||
<link rel="preload" href="assets/js/main.js" as="script">
|
<link rel="preload" href="/assets/js/main.min.js" as="script">
|
||||||
|
|
||||||
<!-- Critical CSS for Above-the-Fold -->
|
<!-- Critical CSS for Above-the-Fold -->
|
||||||
<style>
|
<style>
|
||||||
@@ -128,7 +128,7 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Styles -->
|
<!-- Styles -->
|
||||||
<link rel="stylesheet" href="assets/css/main.min.css" media="print" onload="this.media='all'">
|
<link rel="stylesheet" href="/assets/css/main.min.css" media="print" onload="this.media='all'">
|
||||||
<noscript><link rel="stylesheet" href="assets/css/main.min.css"></noscript>
|
<noscript><link rel="stylesheet" href="assets/css/main.min.css"></noscript>
|
||||||
|
|
||||||
<!-- Enhanced Local SEO Schema -->
|
<!-- Enhanced Local SEO Schema -->
|
||||||
@@ -435,11 +435,11 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
<div class="nav-menu" id="nav-menu">
|
<div class="nav-menu" id="nav-menu">
|
||||||
<a href="#home" class="nav-link">Home</a>
|
<a href="#home" class="nav-link">Home</a>
|
||||||
<a href="#services" class="nav-link">Capabilities</a>
|
<a href="#services" class="nav-link">Capabilities</a>
|
||||||
<a href="project-types" class="nav-link">Project Types</a>
|
<a href="/project-types" class="nav-link">Project Types</a>
|
||||||
<a href="about" class="nav-link">About</a>
|
<a href="/about" class="nav-link">About</a>
|
||||||
<a href="blog/" class="nav-link">Blog</a>
|
<a href="blog/" class="nav-link">Blog</a>
|
||||||
<a href="#contact" class="nav-link">Contact</a>
|
<a href="#contact" class="nav-link">Contact</a>
|
||||||
<a href="quote" class="nav-link cta-button">Request Consultation</a>
|
<a href="/quote" class="nav-link cta-button">Request Consultation</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-toggle" id="nav-toggle">
|
<div class="nav-toggle" id="nav-toggle">
|
||||||
<span class="bar"></span>
|
<span class="bar"></span>
|
||||||
@@ -457,7 +457,7 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
<h1 class="hero-title"><span class="highlight rotating-text" id="rotating-text">UK's Leading Web Scraping & Data Analytics Services</span></h1>
|
<h1 class="hero-title"><span class="highlight rotating-text" id="rotating-text">UK's Leading Web Scraping & Data Analytics Services</span></h1>
|
||||||
<p class="hero-subtitle" id="hero-subtitle">Premier web scraping services UK specialists delivering data analytics London expertise. Professional data extraction, competitive intelligence, and business automation solutions.</p>
|
<p class="hero-subtitle" id="hero-subtitle">Premier web scraping services UK specialists delivering data analytics London expertise. Professional data extraction, competitive intelligence, and business automation solutions.</p>
|
||||||
<div class="hero-buttons">
|
<div class="hero-buttons">
|
||||||
<a href="quote" class="btn btn-primary">Request Consultation</a>
|
<a href="/quote" class="btn btn-primary">Request Consultation</a>
|
||||||
<a href="#services" class="btn btn-secondary">Our Capabilities</a>
|
<a href="#services" class="btn btn-secondary">Our Capabilities</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-stats">
|
<div class="hero-stats">
|
||||||
@@ -575,7 +575,7 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h2>Enterprise Data Solutions Portfolio</h2>
|
<h2>Enterprise Data Solutions Portfolio</h2>
|
||||||
<p>Comprehensive data intelligence services designed for mission-critical business operations and strategic decision-making across British industry sectors. Our <a href="about">experienced team</a> delivers solutions for <a href="project-types">diverse project types</a> while maintaining full <a href="blog/articles/web-scraping-compliance-uk-guide.php">legal compliance</a> with UK data protection regulations.</p>
|
<p>Comprehensive data intelligence services designed for mission-critical business operations and strategic decision-making across British industry sectors. Our <a href="/about">experienced team</a> delivers solutions for <a href="/project-types">diverse project types</a> while maintaining full <a href="blog/articles/web-scraping-compliance-uk-guide.php">legal compliance</a> with UK data protection regulations.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="services-grid">
|
<div class="services-grid">
|
||||||
<div class="service-card animate-on-scroll" style="--animation-delay: 0.1s;">
|
<div class="service-card animate-on-scroll" style="--animation-delay: 0.1s;">
|
||||||
@@ -831,7 +831,7 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
<img src="assets/images/icon-support.svg" alt="Expert support icon showing customer service representatives, technical assistance, and 24/7 help desk operations" loading="lazy">
|
<img src="assets/images/icon-support.svg" alt="Expert support icon showing customer service representatives, technical assistance, and 24/7 help desk operations" loading="lazy">
|
||||||
</div>
|
</div>
|
||||||
<h3>Dedicated Expert Consultancy</h3>
|
<h3>Dedicated Expert Consultancy</h3>
|
||||||
<p>Continuous support from chartered data professionals and certified engineers, providing strategic guidance and technical expertise. Meet our <a href="about">experienced team</a> of data specialists.</p>
|
<p>Continuous support from chartered data professionals and certified engineers, providing strategic guidance and technical expertise. Meet our <a href="/about">experienced team</a> of data specialists.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="feature">
|
<div class="feature">
|
||||||
@@ -957,10 +957,10 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="blog/">Data Intelligence Blog</a></li>
|
<li><a href="blog/">Data Intelligence Blog</a></li>
|
||||||
<li><a href="case-studies/">Case Studies</a></li>
|
<li><a href="case-studies/">Case Studies</a></li>
|
||||||
<li><a href="about">About UK Data Services</a></li>
|
<li><a href="/about">About UK Data Services</a></li>
|
||||||
<li><a href="project-types">Project Types</a></li>
|
<li><a href="/project-types">Project Types</a></li>
|
||||||
<li><a href="faq">FAQ</a></li>
|
<li><a href="faq">FAQ</a></li>
|
||||||
<li><a href="quote">Request Consultation</a></li>
|
<li><a href="/quote">Request Consultation</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -986,7 +986,7 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="/assets/js/main.min.js"></script>
|
||||||
|
|
||||||
<!-- Service Worker Registration -->
|
<!-- Service Worker Registration -->
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
// Quote Form Handler with Enhanced Security
|
// 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();
|
session_start();
|
||||||
|
|
||||||
// Security headers
|
// Security headers
|
||||||
header('X-Content-Type-Options: nosniff');
|
header('X-Content-Type-Options: nosniff');
|
||||||
header('X-Frame-Options: DENY');
|
header('X-Frame-Options: DENY');
|
||||||
header('X-XSS-Protection: 1; mode=block');
|
header('X-XSS-Protection: 1; mode=block');
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
// CSRF Protection
|
// CSRF Protection
|
||||||
function generateCSRFToken() {
|
function generateCSRFToken() {
|
||||||
@@ -63,8 +66,16 @@ function validateInput($data, $type = 'text') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if request is AJAX
|
||||||
|
function isAjaxRequest() {
|
||||||
|
return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
|
||||||
|
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
|
||||||
|
}
|
||||||
|
|
||||||
// Response function
|
// Response function
|
||||||
function sendResponse($success, $message, $data = null) {
|
function sendResponse($success, $message, $data = null) {
|
||||||
|
// If AJAX request, send JSON
|
||||||
|
if (isAjaxRequest()) {
|
||||||
$response = [
|
$response = [
|
||||||
'success' => $success,
|
'success' => $success,
|
||||||
'message' => $message
|
'message' => $message
|
||||||
@@ -74,17 +85,262 @@ function sendResponse($success, $message, $data = null) {
|
|||||||
$response['data'] = $data;
|
$response['data'] = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
echo json_encode($response);
|
echo json_encode($response);
|
||||||
exit;
|
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, #667eea 0%, #764ba2 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: #764ba2;
|
||||||
|
border: 2px solid #764ba2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: #764ba2;
|
||||||
|
color: white;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(118, 75, 162, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 30px;
|
||||||
|
border-top: 1px solid #e1e5e9;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info a {
|
||||||
|
color: #667eea;
|
||||||
|
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
|
// Handle POST requests only
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
sendResponse(false, 'Invalid request method');
|
sendResponse(false, 'Invalid request method');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate CSRF token (if present)
|
// Validate CSRF token
|
||||||
if (isset($_POST['csrf_token']) && !validateCSRFToken($_POST['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.');
|
sendResponse(false, 'Security validation failed. Please refresh the page and try again.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
149
quote.php
149
quote.php
@@ -1,4 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
// Start session before any output
|
||||||
|
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();
|
||||||
|
|
||||||
// Enhanced security headers
|
// Enhanced security headers
|
||||||
header('X-Content-Type-Options: nosniff');
|
header('X-Content-Type-Options: nosniff');
|
||||||
header('X-Frame-Options: DENY');
|
header('X-Frame-Options: DENY');
|
||||||
@@ -214,6 +220,73 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Required field indicators */
|
||||||
|
.required-indicator {
|
||||||
|
color: #e74c3c;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error and success messages */
|
||||||
|
.message-container {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: none;
|
||||||
|
animation: slideDown 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-container.error {
|
||||||
|
background: #fee;
|
||||||
|
border: 1px solid #fcc;
|
||||||
|
color: #c00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-container.success {
|
||||||
|
background: #efe;
|
||||||
|
border: 1px solid #cfc;
|
||||||
|
color: #060;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-container.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-list li {
|
||||||
|
padding: 5px 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-list li:before {
|
||||||
|
content: '\2022';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.step-title {
|
.step-title {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -414,12 +487,20 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
<section class="quote-form-section">
|
<section class="quote-form-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="quote-form-container">
|
<div class="quote-form-container">
|
||||||
|
<!-- Message Container -->
|
||||||
|
<div id="message-container" class="message-container">
|
||||||
|
<div class="message-title"></div>
|
||||||
|
<ul class="message-list"></ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form id="quote-form" action="quote-handler.php" method="POST">
|
<form id="quote-form" action="quote-handler.php" method="POST">
|
||||||
<?php
|
<?php
|
||||||
session_start();
|
// Session already started at top of file
|
||||||
if (!isset($_SESSION['csrf_token'])) {
|
if (!isset($_SESSION['csrf_token'])) {
|
||||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||||
}
|
}
|
||||||
|
// Set form start time for bot detection
|
||||||
|
$_SESSION['form_start_time'] = time();
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
||||||
<!-- Honeypot field for spam protection -->
|
<!-- Honeypot field for spam protection -->
|
||||||
@@ -428,7 +509,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
<div class="form-step">
|
<div class="form-step">
|
||||||
<div class="step-title">
|
<div class="step-title">
|
||||||
<span class="step-number">1</span>
|
<span class="step-number">1</span>
|
||||||
What type of data services do you need?
|
What type of data services do you need? <span class="required-indicator">*</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox-group">
|
<div class="checkbox-group">
|
||||||
<label class="checkbox-item">
|
<label class="checkbox-item">
|
||||||
@@ -462,7 +543,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
<div class="form-step">
|
<div class="form-step">
|
||||||
<div class="step-title">
|
<div class="step-title">
|
||||||
<span class="step-number">2</span>
|
<span class="step-number">2</span>
|
||||||
What's the scale of your project?
|
What's the scale of your project? <span class="required-indicator">*</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="radio-group">
|
<div class="radio-group">
|
||||||
<label class="radio-item">
|
<label class="radio-item">
|
||||||
@@ -500,7 +581,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
<div class="form-step">
|
<div class="form-step">
|
||||||
<div class="step-title">
|
<div class="step-title">
|
||||||
<span class="step-number">3</span>
|
<span class="step-number">3</span>
|
||||||
When do you need this completed?
|
When do you need this completed? <span class="required-indicator">*</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="radio-group">
|
<div class="radio-group">
|
||||||
<label class="radio-item">
|
<label class="radio-item">
|
||||||
@@ -671,6 +752,44 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Message display functions
|
||||||
|
function showMessage(type, title, messages) {
|
||||||
|
const container = document.getElementById('message-container');
|
||||||
|
const titleEl = container.querySelector('.message-title');
|
||||||
|
const listEl = container.querySelector('.message-list');
|
||||||
|
|
||||||
|
// Reset classes
|
||||||
|
container.className = 'message-container';
|
||||||
|
container.classList.add(type);
|
||||||
|
|
||||||
|
// Set content
|
||||||
|
titleEl.textContent = title;
|
||||||
|
listEl.innerHTML = '';
|
||||||
|
|
||||||
|
if (Array.isArray(messages)) {
|
||||||
|
messages.forEach(msg => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = msg;
|
||||||
|
listEl.appendChild(li);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = messages;
|
||||||
|
listEl.appendChild(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show with animation
|
||||||
|
container.classList.add('show');
|
||||||
|
|
||||||
|
// Scroll to message
|
||||||
|
container.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideMessage() {
|
||||||
|
const container = document.getElementById('message-container');
|
||||||
|
container.classList.remove('show');
|
||||||
|
}
|
||||||
|
|
||||||
// Enhanced form submission with reCAPTCHA
|
// Enhanced form submission with reCAPTCHA
|
||||||
const quoteForm = document.getElementById('quote-form');
|
const quoteForm = document.getElementById('quote-form');
|
||||||
|
|
||||||
@@ -767,10 +886,13 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
alert('Please fix the following errors:\n\n' + errors.join('\n'));
|
showMessage('error', 'Please complete the following required fields:', errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide any existing error messages
|
||||||
|
hideMessage();
|
||||||
|
|
||||||
// Execute reCAPTCHA
|
// Execute reCAPTCHA
|
||||||
const self = this;
|
const self = this;
|
||||||
grecaptcha.ready(function() {
|
grecaptcha.ready(function() {
|
||||||
@@ -788,6 +910,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
fetch('quote-handler.php', {
|
fetch('quote-handler.php', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData,
|
body: formData,
|
||||||
|
credentials: 'same-origin',
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
}
|
}
|
||||||
@@ -795,7 +918,7 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
alert('Thank you! Your quote request has been sent. We will get back to you within 24 hours with a detailed proposal.');
|
showMessage('success', 'Thank you!', 'Your quote request has been sent successfully. We will get back to you within 24 hours with a detailed proposal.');
|
||||||
self.reset();
|
self.reset();
|
||||||
// Reset interaction tracking
|
// Reset interaction tracking
|
||||||
formInteractions = {
|
formInteractions = {
|
||||||
@@ -808,13 +931,23 @@ $canonical_url = "https://ukdataservices.co.uk/quote";
|
|||||||
// Reset styling
|
// Reset styling
|
||||||
checkboxItems.forEach(item => item.classList.remove('checked'));
|
checkboxItems.forEach(item => item.classList.remove('checked'));
|
||||||
radioItems.forEach(item => item.classList.remove('checked'));
|
radioItems.forEach(item => item.classList.remove('checked'));
|
||||||
|
|
||||||
|
// Scroll to top after success
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
} else {
|
} else {
|
||||||
alert('Error: ' + data.message);
|
// Parse error message if it contains multiple validation errors
|
||||||
|
let errorMessages = [];
|
||||||
|
if (data.message.includes('. ')) {
|
||||||
|
errorMessages = data.message.split('. ').filter(msg => msg.trim());
|
||||||
|
} else {
|
||||||
|
errorMessages = [data.message];
|
||||||
|
}
|
||||||
|
showMessage('error', 'There was a problem with your submission:', errorMessages);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert('There was an error sending your quote request. Please try again or contact us directly.');
|
showMessage('error', 'Connection Error', 'There was an error sending your quote request. Please check your internet connection and try again, or contact us directly at info@ukdataservices.co.uk');
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
submitButton.textContent = originalText;
|
submitButton.textContent = originalText;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<!-- Homepage -->
|
<!-- Homepage -->
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ukdataservices.co.uk/</loc>
|
<loc>https://ukdataservices.co.uk/</loc>
|
||||||
<lastmod>2025-06-08T20:30:00+00:00</lastmod>
|
<lastmod>2025-08-09T06:06:11+00:00</lastmod>
|
||||||
<changefreq>weekly</changefreq>
|
<changefreq>weekly</changefreq>
|
||||||
<priority>1.0</priority>
|
<priority>1.0</priority>
|
||||||
<image:image>
|
<image:image>
|
||||||
@@ -100,11 +100,6 @@
|
|||||||
<lastmod>2025-06-08T14:30:00+00:00</lastmod>
|
<lastmod>2025-06-08T14:30:00+00:00</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.9</priority>
|
<priority>0.9</priority>
|
||||||
<image:image>
|
|
||||||
<image:loc>https://ukdataservices.co.uk/assets/images/blog/web-scraping-compliance-hero.webp</image:loc>
|
|
||||||
<image:title>UK Web Scraping Compliance Guide - Legal Framework and Best Practices</image:title>
|
|
||||||
<image:caption>Comprehensive guide to legal web scraping compliance in the UK, covering GDPR, data protection laws, and industry best practices</image:caption>
|
|
||||||
</image:image>
|
|
||||||
</url>
|
</url>
|
||||||
|
|
||||||
<url>
|
<url>
|
||||||
|
|||||||
Reference in New Issue
Block a user