Compare commits
3 Commits
be2b757f4e
...
6fd3b8d082
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fd3b8d082 | ||
|
|
902291d8d1 | ||
|
|
ac460ef97f |
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
$rootPath = dirname(__FILE__);
|
||||||
$headerStyle = 'dark';
|
$headerStyle = 'dark';
|
||||||
include_once('header.php');
|
include_once($rootPath . '/header.php');
|
||||||
$indemnityPending = false;
|
$indemnityPending = false;
|
||||||
|
|
||||||
if (isset($_SESSION['user_id']) && isset($conn) && $conn !== null) {
|
if (isset($_SESSION['user_id']) && isset($conn) && $conn !== null) {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
require 'env.php';
|
|
||||||
require 'connection.php';
|
|
||||||
|
|
||||||
$conn = openDatabaseConnection();
|
|
||||||
|
|
||||||
if (!$conn) {
|
|
||||||
die('Database connection failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = file_get_contents('migrations/001_phase1_security_schema.sql');
|
|
||||||
|
|
||||||
if ($conn->multi_query($sql)) {
|
|
||||||
echo "✓ Migration executed successfully\n";
|
|
||||||
} else {
|
|
||||||
echo "✗ Migration error: " . $conn->error . "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->close();
|
|
||||||
?>
|
|
||||||
@@ -1,804 +0,0 @@
|
|||||||
<?php
|
|
||||||
$headerStyle = 'dark';
|
|
||||||
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
|
|
||||||
$indemnityPending = false;
|
|
||||||
|
|
||||||
if (isset($_SESSION['user_id'])) {
|
|
||||||
$userId = $_SESSION['user_id'];
|
|
||||||
$stmt = $conn->prepare("SELECT user_id FROM membership_application WHERE user_id = ? AND accept_indemnity = 0 LIMIT 1");
|
|
||||||
$stmt->bind_param("i", $userId);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->store_result();
|
|
||||||
|
|
||||||
if ($stmt->num_rows > 0) {
|
|
||||||
$indemnityPending = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
<style>
|
|
||||||
.countdown-container {
|
|
||||||
width: 100%;
|
|
||||||
/* background: #111; */
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px 10px;
|
|
||||||
/* font-family: Arial, sans-serif; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.countdown-container h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.countdown-container h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<?php
|
|
||||||
$bannerFolder = 'assets/images/banners/';
|
|
||||||
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
|
|
||||||
|
|
||||||
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
|
|
||||||
if (!empty($bannerImages)) {
|
|
||||||
$randomBanner = $bannerImages[array_rand($bannerImages)];
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<section class="hero-area bgc-black pt-200 rpt-120 rel z-2">
|
|
||||||
<div style="padding-bottom:30px;" class="container-fluid">
|
|
||||||
<div style="text-align: center; position: relative; border-radius: 20px; overflow: hidden; background: linear-gradient(rgba(28, 35, 31, 1), rgba(28, 35, 31, 0.5)), url('<?php echo $randomBanner; ?>'); background-size: cover; background-position: center;">
|
|
||||||
<div style="padding-top: 50px; padding-bottom: 50px;">
|
|
||||||
<img style="width: 250px; margin-bottom: 20px;" src="assets/images/logos/weblogo2.png" alt="Logo">
|
|
||||||
<h1 class="hero-title" data-aos="flip-up" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
Welcome to<br>the Four Wheel Drive Club<br>of Southern Africa
|
|
||||||
</h1>
|
|
||||||
<a href="membership" class="theme-btn style-two bgc-secondary" style="margin-top: 20px; background-color: #e90000; padding: 10px 20px; color: white; text-decoration: none; border-radius: 25px;">
|
|
||||||
<span data-hover="Become a Member">Become a Member</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Hero Area End -->
|
|
||||||
<!-- Destinations Area start -->
|
|
||||||
<?php
|
|
||||||
if (countUpcomingTrips() > 0) { ?>
|
|
||||||
<section class="destinations-area bgc-black pt-100 pb-70 rel z-1">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="section-title text-white text-center counter-text-wrap mb-70" data-aos="fade-up"
|
|
||||||
data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<h2>Discover Africa's Treasures with 4WDCSA</h2>
|
|
||||||
<p>Join us on the following trips:</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<?php
|
|
||||||
// Query to retrieve data from the trips table
|
|
||||||
$sql = "SELECT trip_id, trip_name, location, short_description, start_date, end_date, vehicle_capacity, cost_members, places_booked FROM trips ORDER BY trip_id DESC LIMIT 4";
|
|
||||||
$result = $conn->query($sql);
|
|
||||||
|
|
||||||
if ($result->num_rows > 0) {
|
|
||||||
// Loop through each row
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$trip_id = $row['trip_id'];
|
|
||||||
$trip_name = $row['trip_name'];
|
|
||||||
$location = $row['location'];
|
|
||||||
$short_description = $row['short_description'];
|
|
||||||
$start_date = $row['start_date'];
|
|
||||||
$end_date = $row['end_date'];
|
|
||||||
$capacity = $row['vehicle_capacity'];
|
|
||||||
$cost_members = $row['cost_members'];
|
|
||||||
$places_booked = $row['places_booked'];
|
|
||||||
$remaining_places = $capacity - $places_booked;
|
|
||||||
|
|
||||||
// Determine the badge text based on the status
|
|
||||||
$badge_text = ($remaining_places > 0) ? $remaining_places . ' PLACES LEFT!!' : 'FULLY BOOKED';
|
|
||||||
echo '
|
|
||||||
<div class="col-xxl-3 col-xl-4 col-md-6">
|
|
||||||
<div class="destination-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<img src="assets/images/trips/' . $trip_id . '_01.jpg" alt="' . $trip_name . '">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<span class="location"><i class="fal fa-map-marker-alt"></i> ' . $location . '</span>
|
|
||||||
<h5><a href="trip-details.php?trip_id=' . $trip_id . '">' . $trip_name . '</a></h5>
|
|
||||||
<span class="time">' . convertDate($start_date) . ' - ' . convertDate($end_date) . '</span><br>
|
|
||||||
<span class="time">' . calculateDaysAndNights($start_date, $end_date) . '</span>
|
|
||||||
</div>
|
|
||||||
<div class="destination-footer">
|
|
||||||
<span class="price"><span>R ' . $cost_members . '</span>/per member</span>
|
|
||||||
<a href="trip-details.php?trip_id=' . $trip_id . '" class="read-more">Book Now <i class="fal fa-angle-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo "No trips available.";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<!-- Destinations Area end -->
|
|
||||||
<?php
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- About Us Area start -->
|
|
||||||
<section class="about-us-area py-100 rpb-90 rel z-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row align-items-center">
|
|
||||||
<div class="col-xl-5 col-lg-6">
|
|
||||||
<div class="about-us-content rmb-55" data-aos="fade-left" data-aos-duration="1500"
|
|
||||||
data-aos-offset="50">
|
|
||||||
<div class="section-title mb-25">
|
|
||||||
<h2>Become a member of 4WDCSA</h2>
|
|
||||||
<p>Sign up for an annual membership and receive:</p>
|
|
||||||
<ul class="list-style-two mt-35 mb-30">
|
|
||||||
<li>Year round access to BASE4</li>
|
|
||||||
<li>FREE Camping at BASE4</li>
|
|
||||||
<li>Up to 95% Discount on Training Courses</li>
|
|
||||||
<li>Exclusive Member discounts for all trips and events</li>
|
|
||||||
<li>... and many more!</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p>We go above and beyond to make your travel dreams reality hidden gems and must-see
|
|
||||||
attractions</p>
|
|
||||||
|
|
||||||
<a href="membership" class="theme-btn mt-10 style-two">
|
|
||||||
<span data-hover="Become A Member">Become A Member</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-7 col-lg-6" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="about-us-image">
|
|
||||||
<!-- <div class="shape"><img src="assets/images/about/shape1.png" alt="Shape"></div>
|
|
||||||
<div class="shape"><img src="assets/images/about/shape2.png" alt="Shape"></div>
|
|
||||||
<div class="shape"><img src="assets/images/about/shape3.png" alt="Shape"></div>
|
|
||||||
<div class="shape"><img src="assets/images/about/shape4.png" alt="Shape"></div>
|
|
||||||
<div class="shape"><img src="assets/images/about/shape5.png" alt="Shape"></div>
|
|
||||||
<div class="shape"><img src="assets/images/about/shape6.png" alt="Shape"></div>
|
|
||||||
<div class="shape"><img src="assets/images/about/shape7.png" alt="Shape"></div> -->
|
|
||||||
<img src="assets/images/logos/weblogo.png" alt="About">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<!-- About Us Area end -->
|
|
||||||
|
|
||||||
<section class="hotel-area bgc-black py-100 rel z-1">
|
|
||||||
<div class="countdown-container">
|
|
||||||
<h1 style="color: #e5f5e0;" id="countdown">Loading countdown...</h1>
|
|
||||||
<a href="events" class="theme-btn style-two bgc-secondary" style="margin-top: 20px; background-color: #e90000; padding: 10px 20px; color: white; text-decoration: none; border-radius: 25px;">
|
|
||||||
<span data-hover="Events">Find out more!</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<!-- Popular Destinations Area start -->
|
|
||||||
<!-- <section class="popular-destinations-area rel z-1">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="popular-destinations-wrap br-20 bgc-lighter pt-100 pb-70">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="section-title text-center counter-text-wrap mb-70" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<h2>Explore Popular Destinations</h2>
|
|
||||||
<p>One site <span class="count-text plus" data-speed="3000" data-stop="34500">0</span> most popular experience</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-xl-3 col-md-6">
|
|
||||||
<div class="destination-item style-two" data-aos="flip-up" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
|
|
||||||
<img src="assets/images/destinations/destination1.jpg" alt="Destination">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<h6><a href="destination-details.html">Thailand beach</a></h6>
|
|
||||||
<span class="time">5352+ tours & 856+ Activity</span>
|
|
||||||
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-3 col-md-6">
|
|
||||||
<div class="destination-item style-two" data-aos="flip-up" data-aos-delay="100" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
|
|
||||||
<img src="assets/images/destinations/destination2.jpg" alt="Destination">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<h6><a href="destination-details.html">Parga, Greece</a></h6>
|
|
||||||
<span class="time">5352+ tours & 856+ Activity</span>
|
|
||||||
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="destination-item style-two" data-aos="flip-up" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
|
|
||||||
<img src="assets/images/destinations/destination3.jpg" alt="Destination">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<h6><a href="destination-details.html">Castellammare del Golfo, Italy</a></h6>
|
|
||||||
<span class="time">5352+ tours & 856+ Activity</span>
|
|
||||||
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="destination-item style-two" data-aos="flip-up" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
|
|
||||||
<img src="assets/images/destinations/destination4.jpg" alt="Destination">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<h6><a href="destination-details.html">Reserve of Canada, Canada</a></h6>
|
|
||||||
<span class="time">5352+ tours & 856+ Activity</span>
|
|
||||||
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-3 col-md-6">
|
|
||||||
<div class="destination-item style-two" data-aos="flip-up" data-aos-delay="100" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
|
|
||||||
<img src="assets/images/destinations/destination5.jpg" alt="Destination">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<h6><a href="destination-details.html">Dubai united states</a></h6>
|
|
||||||
<span class="time">5352+ tours & 856+ Activity</span>
|
|
||||||
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-3 col-md-6">
|
|
||||||
<div class="destination-item style-two" data-aos="flip-up" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
|
|
||||||
<img src="assets/images/destinations/destination6.jpg" alt="Destination">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<h6><a href="destination-details.html">Milos, Greece</a></h6>
|
|
||||||
<span class="time">5352+ tours & 856+ Activity</span>
|
|
||||||
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section> -->
|
|
||||||
<!-- Popular Destinations Area end -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Features Area start -->
|
|
||||||
<section class="features-area pt-100 pb-45 rel z-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row align-items-center">
|
|
||||||
<div class="col-xl-6">
|
|
||||||
<div class="features-content-part mb-55" data-aos="fade-left" data-aos-duration="1500"
|
|
||||||
data-aos-offset="50">
|
|
||||||
<div class="section-title mb-20">
|
|
||||||
<h2><b>BASE 4:</b> The home of 4WDCSA.</h2>
|
|
||||||
<p>Situated near the Hennops river, in Doornrandjie, Centurion.</p>
|
|
||||||
<div class="image">
|
|
||||||
<img style="border-radius:10px;" src="assets/images/base4/base4.jpg" alt="Hotel">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="menu-btns py-10">
|
|
||||||
<a href="membership.php" class="theme-btn style-two bgc-secondary">
|
|
||||||
<span data-hover="Become a Member">Become a Member</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="menu-btns py-10">
|
|
||||||
<a href="campsite_booking" class="theme-btn style-two bgc-secondary">
|
|
||||||
<span data-hover="Book a Campsite">Book a Campsite</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <div class="features-customer-box">
|
|
||||||
<div class="image">
|
|
||||||
<img src="assets/images/features/features-box.jpg" alt="Features">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="feature-authors mb-15">
|
|
||||||
<img src="assets/images/features/feature-author1.jpg" alt="Author">
|
|
||||||
<img src="assets/images/features/feature-author2.jpg" alt="Author">
|
|
||||||
<img src="assets/images/features/feature-author3.jpg" alt="Author">
|
|
||||||
<span>4k+</span>
|
|
||||||
</div>
|
|
||||||
<h6>850K+ Happy Customer</h6>
|
|
||||||
<div class="divider style-two counter-text-wrap my-25"><span><span class="count-text plus" data-speed="3000" data-stop="25">0</span> Years</span></div>
|
|
||||||
<p>We pride ourselves offering personalized itineraries</p>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-6" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="row pb-25">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="feature-item">
|
|
||||||
<div class="icon"><i class="flaticon-tent"></i></div>
|
|
||||||
<div class="content">
|
|
||||||
<h5><a href="trip-details.php">Club House</a></h5>
|
|
||||||
<p>We are currently in the process of building a new club house since the previous club house tragically burnt down in November of 2024.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="feature-item">
|
|
||||||
<div class="icon"><i class="flaticon-tent"></i></div>
|
|
||||||
<div class="content">
|
|
||||||
<h5><a href="trip-details.php">4x4 Training Track</a></h5>
|
|
||||||
<p>Test your offroad driving skills on our training track with many obstacles
|
|
||||||
from rocky climbs, daring axle twisters, log bridge, side slopes and more!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="feature-item mt-20">
|
|
||||||
<div class="icon"><i class="flaticon-tent"></i></div>
|
|
||||||
<div class="content">
|
|
||||||
<h5><a href="trip-details.php">24/7 Camping</a></h5>
|
|
||||||
<p>Pristene Camping grounds situated next to a stream, with ablutions, lapa and
|
|
||||||
communal fire pits.</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="feature-item">
|
|
||||||
<div class="icon"><i class="flaticon-tent"></i></div>
|
|
||||||
<div class="content">
|
|
||||||
<h5><a href="trip-details.php">Swimming pool & Braai areas</a></h5>
|
|
||||||
<p>Unwind with a refreshing dip in our crystal-clear swimming pool or gather around the braai area for good food and great company</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<!-- Features Area end -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Hotel Area start -->
|
|
||||||
<section class="hotel-area bgc-black py-100 rel z-1">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="section-title text-white text-center counter-text-wrap mb-70" data-aos="fade-up"
|
|
||||||
data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<h2>Driver Training Courses</h2>
|
|
||||||
<p>Discover the in's and out's of your Four Wheel Drive with one of our dedicated training
|
|
||||||
courses:</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-xxl-6 col-xl-8 col-lg-10">
|
|
||||||
<div class="destination-item style-three" data-aos="fade-up" data-aos-duration="1500"
|
|
||||||
data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<!-- <div class="ratting"><i class="fas fa-star"></i> 4.8</div> -->
|
|
||||||
<!-- <a href="#" class="heart"><i class="fas fa-heart"></i></a> -->
|
|
||||||
<img src="assets/images/courses/driver_training.png" alt="Hotel">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<span class="location"><i class="fal fa-map-marker-alt"></i> BASE4, Hennops</span>
|
|
||||||
<h5><a href="driver_training">Basic 4X4 Driver Training</a></h5>
|
|
||||||
<ul class="list-style-three">
|
|
||||||
<li>Master Off-Road Confidence</li>
|
|
||||||
<li>Hands-On Training</li>
|
|
||||||
<li>Safety First</li>
|
|
||||||
<!-- <li><i class="fal fa-router"></i> Internet</li> -->
|
|
||||||
</ul>
|
|
||||||
<div class="destination-footer">
|
|
||||||
<span class="price"><span>R <?= getPrice('driver_training', 'member'); ?></span>/for members</span>
|
|
||||||
<span class="price"><span>R <?= getPrice('driver_training', 'nonmember'); ?></span>/for non-members</span>
|
|
||||||
<a href="driver_training" class="read-more">Book Now <i class="fal fa-angle-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xxl-6 col-xl-8 col-lg-10">
|
|
||||||
<div class="destination-item style-three" data-aos="fade-up" data-aos-delay="50"
|
|
||||||
data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="image">
|
|
||||||
<!-- <div class="ratting"><i class="fas fa-star"></i> 4.8</div> -->
|
|
||||||
<!-- <a href="#" class="heart"><i class="fas fa-heart"></i></a> -->
|
|
||||||
<img src="assets/images/courses/bush_mechanics.png" alt="Hotel">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<span class="location"><i class="fal fa-map-marker-alt"></i> BASE4, Hennops</span>
|
|
||||||
<h5><a href="bush_mechanics">Bush Mechanics Course</a></h5>
|
|
||||||
<ul class="list-style-three">
|
|
||||||
<li>Fix Your Vehicle in the Wild</li>
|
|
||||||
<li>Survival Skills for Off-Roaders</li>
|
|
||||||
<li>Hands-On Experience</li>
|
|
||||||
<!-- <li><i class="fal fa-router"></i> Internet</li> -->
|
|
||||||
</ul>
|
|
||||||
<div class="destination-footer">
|
|
||||||
<span class="price"><span>R <?= getPrice('bush_mechanics', 'member'); ?></span>/for members</span>
|
|
||||||
<span class="price"><span>R <?= getPrice('bush_mechanics', 'nonmember'); ?></span>/for non-members</span>
|
|
||||||
<a href="bush_mechanics" class="read-more">Book Now <i class="fal fa-angle-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xxl-6 col-xl-8 col-lg-10">
|
|
||||||
<div class="destination-item style-three" data-aos="fade-up" data-aos-duration="1500"
|
|
||||||
data-aos-offset="50">
|
|
||||||
<div class="content">
|
|
||||||
<span class="location"><i class="fal fa-map-marker-alt"></i> BASE4, Hennops</span>
|
|
||||||
<h5><a href="rescue_recovery">Rescue & Recovery Course</a></h5>
|
|
||||||
<ul class="list-style-three">
|
|
||||||
<li>Master Advanced Recovery Techniques</li>
|
|
||||||
<li>Gain Confidence in High-Stress Situations</li>
|
|
||||||
<li>Teamwork and Communication</li>
|
|
||||||
<!-- <li><i class="fal fa-router"></i> Internet</li> -->
|
|
||||||
</ul>
|
|
||||||
<div class="destination-footer">
|
|
||||||
<span class="price"><span>R <?= getPrice('rescue_recovery', 'member'); ?></span>/for members</span>
|
|
||||||
<span class="price"><span>R <?= getPrice('rescue_recovery', 'nonmember'); ?></span>/for non-members</span>
|
|
||||||
<a href="rescue_recovery" class="read-more">Book Now <i class="fal fa-angle-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="image">
|
|
||||||
<!-- <div class="ratting"><i class="fas fa-star"></i> 4.8</div> -->
|
|
||||||
<!-- <a href="#" class="heart"><i class="fas fa-heart"></i></a> -->
|
|
||||||
<img src="assets/images/courses/rescue_recovery.png" alt="Hotel">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="hotel-more-btn text-center mt-40">
|
|
||||||
<a href="destination2.html" class="theme-btn style-four">
|
|
||||||
<span data-hover="Explore More Hotel">Explore More Hotel</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<!-- Hotel Area end -->
|
|
||||||
|
|
||||||
<!-- CTA Area start -->
|
|
||||||
<!-- <section class="cta-area pt-100 rel z-1">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="cta-item" style="background-image: url(assets/images/cta/cta1.jpg);">
|
|
||||||
<span class="category">Tent Camping</span>
|
|
||||||
<h2>Explore the world best tourism</h2>
|
|
||||||
<a href="trip-details.php" class="theme-btn style-two bgc-secondary">
|
|
||||||
<span data-hover="Explore Tours">Explore Tours</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="cta-item" style="background-image: url(assets/images/cta/cta2.jpg);">
|
|
||||||
<span class="category">Sea Beach</span>
|
|
||||||
<h2>World largest Sea Beach in Thailand</h2>
|
|
||||||
<a href="trip-details.php" class="theme-btn style-two">
|
|
||||||
<span data-hover="Explore Tours">Explore Tours</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-delay="100" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="cta-item" style="background-image: url(assets/images/cta/cta3.jpg);">
|
|
||||||
<span class="category">Water Falls</span>
|
|
||||||
<h2>Largest Water falls Bali, Indonesia</h2>
|
|
||||||
<a href="trip-details.php" class="theme-btn style-two bgc-secondary">
|
|
||||||
<span data-hover="Explore Tours">Explore Tours</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section> -->
|
|
||||||
<!-- CTA Area end -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Blog Area start -->
|
|
||||||
<section class="blog-area py-70 rel z-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="section-title text-center counter-text-wrap mb-70" data-aos="fade-up"
|
|
||||||
data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<h2>Read about our past trips and events</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<?php
|
|
||||||
$sql = "SELECT blog_id, title, date, category, image, description, author, link, members_only FROM blogs ORDER BY date DESC LIMIT 3";
|
|
||||||
$result = $conn->query($sql);
|
|
||||||
|
|
||||||
if ($result->num_rows > 0) {
|
|
||||||
// Loop through each row
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$blog_id = $row['blog_id'];
|
|
||||||
$blog_title = $row['title'];
|
|
||||||
$blog_date = $row['date'];
|
|
||||||
$blog_category = $row['category'];
|
|
||||||
$blog_image = $row['image'];
|
|
||||||
$blog_description = $row['description'];
|
|
||||||
$blog_author = $row['author'];
|
|
||||||
$members_only = $row['members_only'];
|
|
||||||
if ($members_only) {
|
|
||||||
if (!isset($_SESSION['user_id'])) {
|
|
||||||
$blog_link = "login.php";
|
|
||||||
$button_hover = "Members Only";
|
|
||||||
$icon = "fa-lock";
|
|
||||||
} else {
|
|
||||||
if (getUserMemberStatus($_SESSION['user_id'])) {
|
|
||||||
$blog_link = $row['link'];
|
|
||||||
$button_hover = "Read More";
|
|
||||||
$icon = "fa-arrow-right";
|
|
||||||
} else {
|
|
||||||
$blog_link = "membership.php";
|
|
||||||
$button_hover = "Members Only";
|
|
||||||
$icon = "fa-lock";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$blog_link = $row['link'];
|
|
||||||
$button_hover = "Read More";
|
|
||||||
$icon = "fa-arrow-right";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
echo '
|
|
||||||
<div class="col-xl-4 col-md-6">
|
|
||||||
<div class="blog-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="content">
|
|
||||||
<a href="#" class="category">' . $blog_category . '</a>
|
|
||||||
<h5><a href="' . $blog_link . '">' . $blog_title . '</a></h5>
|
|
||||||
<ul class="blog-meta">
|
|
||||||
<li><i class="far fa-calendar-alt"></i> <a href="#">' . $blog_date . '</a></li>
|
|
||||||
<li><i class="far fa-user"></i>' . getFullName($blog_author) . '</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="image">
|
|
||||||
<img style="border-radius:20px;" src="assets/images/blog/' . $blog_id . '/' . $blog_image . '" alt="Blog List">
|
|
||||||
</div>
|
|
||||||
<a style="width:100%;" href="' . $blog_link . '" class="theme-btn">
|
|
||||||
<span style="width:100%;" data-hover="' . $button_hover . '">Read More</span>
|
|
||||||
<i class="fal ' . $icon . '"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>';
|
|
||||||
}
|
|
||||||
// Close connection
|
|
||||||
$conn->close();
|
|
||||||
} ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<!-- Blog Area end -->
|
|
||||||
|
|
||||||
<section class="bgc-black py-20 rel z-1">
|
|
||||||
|
|
||||||
<?php include_once('ad_banner.php'); ?>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
<section class="py-20 rel z-1">
|
|
||||||
|
|
||||||
<?php include_once('logos.php'); ?>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- footer area start -->
|
|
||||||
<footer class="main-footer bgs-cover overlay rel z-1 pb-25"
|
|
||||||
style="background-image: url(assets/images/backgrounds/footer.jpg);">
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
<div class="footer-top pt-100 pb-30">
|
|
||||||
<div class="row justify-content-between">
|
|
||||||
<div class="col-xl-5 col-lg-6" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
|
|
||||||
<div class="footer-widget footer-contact">
|
|
||||||
<a href="https://chat.whatsapp.com/JD9xQuJlVX5AAJwcLrpl2B" target="_blank" style="text-decoration: none; color: inherit;">
|
|
||||||
<div style="display: flex; align-items: center; background-color: #e5f5e0; border-radius: 10px; padding: 10px; max-width: 100%; box-shadow: 0 2px 6px rgba(0,0,0,0.1);">
|
|
||||||
<img src="assets/images/icons/whatsapp.png" alt="WhatsApp" style="width: 64px; height: 64px; margin-right: 15px;">
|
|
||||||
<h1 style="margin: 0; font-size: 24px;">Join our WhatsApp Group</h1>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="footer-widget footer-contact">
|
|
||||||
<div class="footer-title">
|
|
||||||
<h5>Get In Touch</h5>
|
|
||||||
</div>
|
|
||||||
<ul class="list-style-one">
|
|
||||||
<li><i class="fal fa-map-marked-alt"></i> Plot 50 Gemstone Rd, Doornrandje, Centurion, 0157</li>
|
|
||||||
<li><i class="fal fa-envelope"></i> <a
|
|
||||||
href="mailto:info@4wdcsa.co.za">info@4wdcsa.co.za</a></li>
|
|
||||||
<li><i class="fal fa-clock"></i> Mon - Fri, 09:00 - 17:00</li>
|
|
||||||
<li><i class="fal fa-phone-volume"></i> <a href="callto:+2779 065 2795">079 065 2795</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-5 col-lg-6" data-aos="fade-up" data-aos-delay="50" data-aos-duration="1500"
|
|
||||||
data-aos-offset="50">
|
|
||||||
<div class="section-title counter-text-wrap mb-35">
|
|
||||||
<h2>Subscribe to our Mailing List</h2>
|
|
||||||
<p>Receive news and updates about upcoming trips and events.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="mc_embed_shell">
|
|
||||||
|
|
||||||
<div id="mc_embed_signup">
|
|
||||||
<form class="newsletter-form mb-50" action="https://fwdcsa.us17.list-manage.com/subscribe/post?u=3c26590bcc200ef52edc0bec2&id=3c370893eb&f_id=0099ebe3f0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_self" novalidate="">
|
|
||||||
<div id="mc_embed_signup_scroll" style="width:100%;">
|
|
||||||
<div class="mc-field-group"></label><input type="email" name="EMAIL" class="required email" id="mce-EMAIL" required="" value="" placeholder="Email"></div>
|
|
||||||
<div class="mc-field-group"><input type="text" name="FNAME" class=" text" id="mce-FNAME" value="" placeholder="First Name"></div>
|
|
||||||
<div class="mc-field-group"><input type="text" name="LNAME" class=" text" id="mce-LNAME" value="" placeholder="Last Name"></div>
|
|
||||||
<div class="mc-field-group"><input type="text" name="PHONE" class="REQ_CSS" id="mce-PHONE" value="" placeholder="Phone Number"></div>
|
|
||||||
<div hidden=""><input type="hidden" name="tags" value="8324220"></div>
|
|
||||||
<div id="mce-responses" class="clear">
|
|
||||||
<div class="response" id="mce-error-response" style="display: none;"></div>
|
|
||||||
<div class="response" id="mce-success-response" style="display: none;"></div>
|
|
||||||
</div>
|
|
||||||
<div aria-hidden="true" style="position: absolute; left: -5000px;"><input type="text" name="b_3c26590bcc200ef52edc0bec2_3c370893eb" tabindex="-1" value=""></div>
|
|
||||||
<div class="clear"><input style="width:100%;" type="submit" name="subscribe" id="mc-embedded-subscribe" class="theme-btn bgc-secondary style-two" value="Subscribe"></div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<form class="newsletter-form mb-50" action="#">
|
|
||||||
<input id="news-email" type="email" placeholder="Email Address" required>
|
|
||||||
<button type="submit" class="theme-btn bgc-secondary style-two">
|
|
||||||
<span data-hover="Subscribe">Subscribe</span>
|
|
||||||
<i class="fal fa-arrow-right"></i>
|
|
||||||
</button>
|
|
||||||
</form> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-bottom pt-20 pb-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-5">
|
|
||||||
<div class="copyright-text text-center text-lg-start">
|
|
||||||
<p>Copyright © <?php echo date("Y"); ?> <a href="index.html">4WDCSA</a> | All rights reserved.</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-7 text-center text-lg-end">
|
|
||||||
<ul class="footer-bottom-nav">
|
|
||||||
<li><a href="privacy_policy">Privacy Policy</a></li>
|
|
||||||
<!-- <li><a href="about.html">Terms</a></li> -->
|
|
||||||
<!-- <li><a href="about.html">Privacy Policy</a></li> -->
|
|
||||||
<!-- <li><a href="about.html">Legal notice</a></li> -->
|
|
||||||
<!-- <li><a href="about.html">Accessibility</a></li> -->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Scroll Top Button -->
|
|
||||||
<button class="scroll-top scroll-to-target" data-target="html"><img
|
|
||||||
src="assets/images/icons/scroll-up.png" alt="Scroll Up"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
||||||
<!-- footer area end -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!--End pagewrapper-->
|
|
||||||
<?php if ($indemnityPending): ?>
|
|
||||||
<!-- Bootstrap Modal -->
|
|
||||||
<div class="modal fade" id="indemnityModal" tabindex="-1" aria-labelledby="indemnityModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
|
||||||
<div class="modal-content border-secondary">
|
|
||||||
<div class="modal-header bg-secondary text-white">
|
|
||||||
<h5 class="modal-title" id="indemnityModalLabel">Membership Application Incomplete</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
To link your existing FWDCSA membership, you need to sign and accept the indemnity aggreement before proceeding.<br>
|
|
||||||
<a style="width:100%; border-radius:20px;" href="indemnity" class="btn btn-danger mt-3">Review and Accept</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Show modal when page loads
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
var indemnityModal = new bootstrap.Modal(document.getElementById('indemnityModal'));
|
|
||||||
indemnityModal.show();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Jquery -->
|
|
||||||
<script src="assets/js/jquery-3.6.0.min.js"></script>
|
|
||||||
<!-- Bootstrap -->
|
|
||||||
<script src="assets/js/bootstrap.min.js"></script>
|
|
||||||
<!-- Appear Js -->
|
|
||||||
<script src="assets/js/appear.min.js"></script>
|
|
||||||
<!-- Slick -->
|
|
||||||
<script src="assets/js/slick.min.js"></script>
|
|
||||||
<!-- Magnific Popup -->
|
|
||||||
<script src="assets/js/jquery.magnific-popup.min.js"></script>
|
|
||||||
<!-- Nice Select -->
|
|
||||||
<script src="assets/js/jquery.nice-select.min.js"></script>
|
|
||||||
<!-- Image Loader -->
|
|
||||||
<script src="assets/js/imagesloaded.pkgd.min.js"></script>
|
|
||||||
<!-- Skillbar -->
|
|
||||||
<script src="assets/js/skill.bars.jquery.min.js"></script>
|
|
||||||
<!-- Isotope -->
|
|
||||||
<script src="assets/js/isotope.pkgd.min.js"></script>
|
|
||||||
<!-- AOS Animation -->
|
|
||||||
<script src="assets/js/aos.js"></script>
|
|
||||||
<!-- Custom script -->
|
|
||||||
<script src="assets/js/script.js"></script>
|
|
||||||
<script>
|
|
||||||
// Set your target date and time
|
|
||||||
const targetDate = new Date("<?php echo getNextOpenDayDate(); ?>T08:00:00"); // yyyy-mm-ddThh:mm:ss
|
|
||||||
|
|
||||||
function updateCountdown() {
|
|
||||||
const now = new Date();
|
|
||||||
const diff = targetDate - now;
|
|
||||||
|
|
||||||
if (diff <= 0) {
|
|
||||||
document.getElementById("countdown").innerHTML = "We're open now!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
||||||
const hours = Math.floor((diff / (1000 * 60 * 60)) % 24);
|
|
||||||
const minutes = Math.floor((diff / (1000 * 60)) % 60);
|
|
||||||
const seconds = Math.floor((diff / 1000) % 60);
|
|
||||||
|
|
||||||
document.getElementById("countdown").innerHTML =
|
|
||||||
`${String(days).padStart(2, '0')} days ` +
|
|
||||||
`${String(hours).padStart(2, '0')} hours ` +
|
|
||||||
`${String(minutes).padStart(2, '0')} minutes ` +
|
|
||||||
`${String(seconds).padStart(2, '0')} seconds<br>` +
|
|
||||||
`till our next BASE4 Open Day!`;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCountdown(); // initial call
|
|
||||||
setInterval(updateCountdown, 1000);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
54
src/processors/create_bar_tab.php
Normal file
54
src/processors/create_bar_tab.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/session.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Security token validation failed.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user_id is set in the POST request
|
||||||
|
if (isset($_POST['user_id']) && !empty($_POST['user_id'])) {
|
||||||
|
// Validate user_id as integer
|
||||||
|
$user_id = intval($_POST['user_id']);
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid user ID.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$created_at = date('Y-m-d H:i:s'); // Use current date and time for created_at
|
||||||
|
|
||||||
|
// First, check if a bar tab already exists for this user_id
|
||||||
|
$stmt = $conn->prepare("SELECT * FROM bar_tabs WHERE user_id = ? LIMIT 1");
|
||||||
|
$stmt->bind_param("i", $user_id);
|
||||||
|
$stmt->execute();
|
||||||
|
$checkResult = $stmt->get_result();
|
||||||
|
|
||||||
|
if ($checkResult->num_rows > 0) {
|
||||||
|
// If a bar tab already exists for this user_id, return an error message
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'A bar tab already exists for this user.']);
|
||||||
|
} else {
|
||||||
|
// Prepare the SQL query to insert a new record into the bar_tabs table
|
||||||
|
$stmt = $conn->prepare("INSERT INTO bar_tabs (user_id) VALUES (?)");
|
||||||
|
$stmt->bind_param("i", $user_id);
|
||||||
|
|
||||||
|
// Execute the query
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
// If the insertion is successful, return a success message
|
||||||
|
echo json_encode(['status' => 'success', 'message' => 'Bar tab created successfully.']);
|
||||||
|
} else {
|
||||||
|
// If there's an error, return an error message
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Error: ' . $conn->error]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If user_id is not provided, return an error message
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'User ID is required.']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
|
||||||
14
src/processors/logout.php
Normal file
14
src/processors/logout.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Destroy the session
|
||||||
|
session_destroy();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Redirect to login page
|
||||||
|
header("Location: index"); // Replace with your actual login page URL
|
||||||
|
exit();
|
||||||
|
?>
|
||||||
202
src/processors/process_application.php
Normal file
202
src/processors/process_application.php
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/session.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
||||||
|
$eft_id = strtoupper($user_id." SUBS ".date("Y")." ".getInitialSurname($user_id));
|
||||||
|
$status = 'AWAITING PAYMENT';
|
||||||
|
$description = 'Membership Fees '.date("Y")." ".getInitialSurname($user_id);
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
auditLog($user_id, 'CSRF_VALIDATION_FAILED', 'membership_application', null, ['endpoint' => 'process_application.php']);
|
||||||
|
http_response_code(403);
|
||||||
|
die('Security token validation failed. Please try again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the form fields with validation
|
||||||
|
$first_name = validateName($_POST['first_name'] ?? '');
|
||||||
|
if ($first_name === false) {
|
||||||
|
die('Invalid first name format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$last_name = validateName($_POST['last_name'] ?? '');
|
||||||
|
if ($last_name === false) {
|
||||||
|
die('Invalid last name format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$id_number = validateSAIDNumber($_POST['id_number'] ?? '');
|
||||||
|
if ($id_number === false) {
|
||||||
|
die('Invalid ID number format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$dob = validateDate($_POST['dob'] ?? '');
|
||||||
|
if ($dob === false) {
|
||||||
|
die('Invalid date of birth format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$occupation = sanitizeTextInput($_POST['occupation'] ?? '', 100);
|
||||||
|
$tel_cell = validatePhoneNumber($_POST['tel_cell'] ?? '');
|
||||||
|
if ($tel_cell === false) {
|
||||||
|
die('Invalid phone number format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = validateEmail($_POST['email'] ?? '');
|
||||||
|
if ($email === false) {
|
||||||
|
die('Invalid email format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spouse or Partner details (optional)
|
||||||
|
$spouse_first_name = !empty($_POST['spouse_first_name']) ? validateName($_POST['spouse_first_name']) : null;
|
||||||
|
$spouse_last_name = !empty($_POST['spouse_last_name']) ? validateName($_POST['spouse_last_name']) : null;
|
||||||
|
$spouse_id_number = !empty($_POST['spouse_id_number']) ? validateSAIDNumber($_POST['spouse_id_number']) : null;
|
||||||
|
$spouse_dob = !empty($_POST['spouse_dob']) ? validateDate($_POST['spouse_dob']) : NULL;
|
||||||
|
$spouse_occupation = !empty($_POST['spouse_occupation']) ? sanitizeTextInput($_POST['spouse_occupation'], 100) : null;
|
||||||
|
$spouse_tel_cell = !empty($_POST['spouse_tel_cell']) ? validatePhoneNumber($_POST['spouse_tel_cell']) : null;
|
||||||
|
$spouse_email = !empty($_POST['spouse_email']) ? validateEmail($_POST['spouse_email']) : null;
|
||||||
|
|
||||||
|
// Children details (optional)
|
||||||
|
$child_name1 = !empty($_POST['child_name1']) ? $_POST['child_name1'] : null;
|
||||||
|
$child_dob1 = !empty($_POST['child_dob1']) ? $_POST['child_dob1'] : null;
|
||||||
|
$child_name2 = !empty($_POST['child_name2']) ? $_POST['child_name2'] : null;
|
||||||
|
$child_dob2 = !empty($_POST['child_dob2']) ? $_POST['child_dob2'] : null;
|
||||||
|
$child_name3 = !empty($_POST['child_name3']) ? $_POST['child_name3'] : null;
|
||||||
|
$child_dob3 = !empty($_POST['child_dob3']) ? $_POST['child_dob3'] : null;
|
||||||
|
|
||||||
|
// Address and other details
|
||||||
|
$physical_address = $_POST['physical_address'];
|
||||||
|
$postal_address = $_POST['postal_address'];
|
||||||
|
$interests_hobbies = $_POST['interests_hobbies'];
|
||||||
|
|
||||||
|
// Primary vehicle details
|
||||||
|
$vehicle_make = $_POST['vehicle_make'];
|
||||||
|
$vehicle_model = $_POST['vehicle_model'];
|
||||||
|
$vehicle_year = $_POST['vehicle_year'];
|
||||||
|
$vehicle_registration = $_POST['vehicle_registration'];
|
||||||
|
|
||||||
|
// Secondary vehicle details (optional)
|
||||||
|
$secondary_vehicle_make = !empty($_POST['secondary_vehicle_make']) ? $_POST['secondary_vehicle_make'] : null;
|
||||||
|
$secondary_vehicle_model = !empty($_POST['secondary_vehicle_model']) ? $_POST['secondary_vehicle_model'] : null;
|
||||||
|
$secondary_vehicle_year = !empty($_POST['secondary_vehicle_year']) ? $_POST['secondary_vehicle_year'] : null;
|
||||||
|
$secondary_vehicle_registration = !empty($_POST['secondary_vehicle_registration']) ? $_POST['secondary_vehicle_registration'] : null;
|
||||||
|
|
||||||
|
// Start a transaction to ensure data consistency
|
||||||
|
$conn->begin_transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Insert into the member application table
|
||||||
|
$stmt = $conn->prepare("INSERT INTO membership_application (
|
||||||
|
user_id, first_name, last_name, id_number, dob, occupation, tel_cell, email,
|
||||||
|
spouse_first_name, spouse_last_name, spouse_id_number, spouse_dob, spouse_occupation, spouse_tel_cell, spouse_email,
|
||||||
|
child_name1, child_dob1, child_name2, child_dob2, child_name3, child_dob3,
|
||||||
|
physical_address, postal_address, interests_hobbies, vehicle_make, vehicle_model, vehicle_year, vehicle_registration,
|
||||||
|
secondary_vehicle_make, secondary_vehicle_model, secondary_vehicle_year, secondary_vehicle_registration
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
|
// Check if preparation was successful
|
||||||
|
if (!$stmt) {
|
||||||
|
die("SQL error: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param(
|
||||||
|
"isssssssssssssssssssssssssssssss",
|
||||||
|
$user_id,
|
||||||
|
$first_name,
|
||||||
|
$last_name,
|
||||||
|
$id_number,
|
||||||
|
$dob,
|
||||||
|
$occupation,
|
||||||
|
$tel_cell,
|
||||||
|
$email,
|
||||||
|
$spouse_first_name,
|
||||||
|
$spouse_last_name,
|
||||||
|
$spouse_id_number,
|
||||||
|
$spouse_dob,
|
||||||
|
$spouse_occupation,
|
||||||
|
$spouse_tel_cell,
|
||||||
|
$spouse_email,
|
||||||
|
$child_name1,
|
||||||
|
$child_dob1,
|
||||||
|
$child_name2,
|
||||||
|
$child_dob2,
|
||||||
|
$child_name3,
|
||||||
|
$child_dob3,
|
||||||
|
$physical_address,
|
||||||
|
$postal_address,
|
||||||
|
$interests_hobbies,
|
||||||
|
$vehicle_make,
|
||||||
|
$vehicle_model,
|
||||||
|
$vehicle_year,
|
||||||
|
$vehicle_registration,
|
||||||
|
$secondary_vehicle_make,
|
||||||
|
$secondary_vehicle_model,
|
||||||
|
$secondary_vehicle_year,
|
||||||
|
$secondary_vehicle_registration
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
// Insert into the membership fees table
|
||||||
|
$payment_amount = calculateProrata(210); // Assuming a fixed membership fee, adjust as needed
|
||||||
|
$payment_date = date('Y-m-d');
|
||||||
|
$membership_start_date = $payment_date;
|
||||||
|
// $membership_end_date = date('Y-12-31');
|
||||||
|
|
||||||
|
// Get today's date
|
||||||
|
$today = new DateTime();
|
||||||
|
|
||||||
|
// Determine the target February
|
||||||
|
if ($today->format('n') > 2) {
|
||||||
|
// If we're past February, target is next year's Feb 28/29
|
||||||
|
$year = $today->format('Y') + 1;
|
||||||
|
} else {
|
||||||
|
// Otherwise, this year's February
|
||||||
|
$year = $today->format('Y');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle leap year (Feb 29) automatically
|
||||||
|
$membership_end_date = (new DateTime("$year-02-01"))
|
||||||
|
->modify('last day of this month')
|
||||||
|
->format('Y-m-d');
|
||||||
|
|
||||||
|
$stmt = $conn->prepare("INSERT INTO membership_fees (user_id, payment_amount, payment_date, membership_start_date, membership_end_date, payment_status, payment_id)
|
||||||
|
VALUES (?, ?, ?, ?, ?, 'PENDING', ?)");
|
||||||
|
$stmt->bind_param("idssss", $user_id, $payment_amount, $payment_date, $membership_start_date, $membership_end_date, $eft_id);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
// Commit the transaction
|
||||||
|
$conn->commit();
|
||||||
|
addSubsEFT($eft_id, $user_id, $status, $payment_amount, $description);
|
||||||
|
sendInvoice(getEmail($user_id), getFullName($user_id), $eft_id, formatCurrency($payment_amount), $description);
|
||||||
|
sendAdminNotification('4WDCSA.co.za - New Membership Application - '.$last_name , 'A new member has signed up, '.$first_name.' '.$last_name);
|
||||||
|
header("Location: indemnity");
|
||||||
|
// Success message
|
||||||
|
$response = [
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Your membership application has been submitted successfully!'
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
throw new Exception("Failed to insert membership fee. SQL error: " . $conn->error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception("Failed to insert member application.SQL error: " . $conn->error);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Rollback the transaction in case of error
|
||||||
|
$conn->rollback();
|
||||||
|
|
||||||
|
// Error response
|
||||||
|
$response = [
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Error: ' . $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the response in JSON format
|
||||||
|
echo json_encode($response);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
95
src/processors/process_booking.php
Normal file
95
src/processors/process_booking.php
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
// Start session to retrieve the logged-in user's ID
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Get user ID from session (assuming user is logged in)
|
||||||
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
||||||
|
|
||||||
|
// Check if the form has been submitted
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
auditLog($user_id, 'CSRF_VALIDATION_FAILED', 'bookings', null, ['endpoint' => 'process_booking.php']);
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Security token validation failed. Please try again.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate dates and integers
|
||||||
|
$from_date = validateDate($_POST['from_date'] ?? '');
|
||||||
|
if ($from_date === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid from date format.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$to_date = validateDate($_POST['to_date'] ?? '');
|
||||||
|
if ($to_date === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid to date format.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$num_vehicles = validateInteger($_POST['vehicles'] ?? 0, 1, 10);
|
||||||
|
if ($num_vehicles === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid number of vehicles.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$num_adults = validateInteger($_POST['adults'] ?? 0, 0, 20);
|
||||||
|
if ($num_adults === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid number of adults.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$num_children = validateInteger($_POST['children'] ?? 0, 0, 20);
|
||||||
|
if ($num_children === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid number of children.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get values from the form
|
||||||
|
$add_firewood = isset($_POST['AddExtra']) ? 1 : 0; // Checkbox for extras
|
||||||
|
$is_member = isset($_POST['is_member']) ? (int)$_POST['is_member'] : 0; // Hidden member status
|
||||||
|
$type = "camping";
|
||||||
|
|
||||||
|
// Calculate the total number of nights
|
||||||
|
$date1 = new DateTime($from_date);
|
||||||
|
$date2 = new DateTime($to_date);
|
||||||
|
$nights = $date2->diff($date1)->days;
|
||||||
|
|
||||||
|
// Determine rate per night
|
||||||
|
$rate_per_night = ($is_member) ? 0 : 200; // Free for members, R200 for non-members
|
||||||
|
|
||||||
|
// Calculate the total cost
|
||||||
|
$vehicle_cost = $rate_per_night * $num_vehicles * $nights;
|
||||||
|
$firewood_cost = $add_firewood ? 50 : 0;
|
||||||
|
$total_amount = $vehicle_cost + $firewood_cost;
|
||||||
|
|
||||||
|
// Calculate discount if the user is a member
|
||||||
|
$discount_amount = ($is_member) ? $vehicle_cost : 0;
|
||||||
|
|
||||||
|
// Insert booking into the database
|
||||||
|
$sql = "INSERT INTO bookings (booking_type, user_id, from_date, to_date, num_vehicles, num_adults, num_children, add_firewood, total_amount, discount_amount)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bind_param('sissiiiidd', $type, $user_id, $from_date, $to_date, $num_vehicles, $num_adults, $num_children, $add_firewood, $total_amount, $discount_amount);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
// Redirect to success page or display success message
|
||||||
|
echo "<script>alert('Booking successfully created!'); window.location.href = 'booking.php';</script>";
|
||||||
|
} else {
|
||||||
|
// Handle error if insert fails
|
||||||
|
echo "<script>alert('Error processing booking. Please try again later.');</script>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
} else {
|
||||||
|
echo "Invalid request.";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
146
src/processors/process_camp_booking.php
Normal file
146
src/processors/process_camp_booking.php
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
// Start session to retrieve the logged-in user's ID
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Get user ID from session (assuming user is logged in)
|
||||||
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
||||||
|
|
||||||
|
// Validate user session
|
||||||
|
if (!$user_id) {
|
||||||
|
echo "<script>alert('User is not logged in. Please log in to make a booking.'); window.location.href = 'login.php';</script>";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$is_member = getUserMemberStatus($user_id);
|
||||||
|
|
||||||
|
// Check if the form has been submitted
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
auditLog($user_id, 'CSRF_VALIDATION_FAILED', 'bookings', null, ['endpoint' => 'process_camp_booking.php']);
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Security token validation failed. Please try again.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate dates and integers
|
||||||
|
$from_date = validateDate($_POST['from_date'] ?? '');
|
||||||
|
if ($from_date === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid from date format.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$to_date = validateDate($_POST['to_date'] ?? '');
|
||||||
|
if ($to_date === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid to date format.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$num_vehicles = validateInteger($_POST['vehicles'] ?? 1, 1, 10);
|
||||||
|
if ($num_vehicles === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid number of vehicles.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$num_adults = validateInteger($_POST['adults'] ?? 0, 0, 20);
|
||||||
|
if ($num_adults === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid number of adults.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$num_children = validateInteger($_POST['children'] ?? 0, 0, 20);
|
||||||
|
if ($num_children === false) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid number of children.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get values from the form
|
||||||
|
$add_firewood = isset($_POST['AddExtra']) ? 1 : 0; // Checkbox for extras
|
||||||
|
// $is_member = isset($_POST['is_member']) ? (int)$_POST['is_member'] : 0; // Hidden member status
|
||||||
|
$type = "camping";
|
||||||
|
|
||||||
|
// Calculate the total number of nights
|
||||||
|
$date1 = new DateTime($from_date);
|
||||||
|
$date2 = new DateTime($to_date);
|
||||||
|
$nights = $date2->diff($date1)->days;
|
||||||
|
|
||||||
|
// Validate date range
|
||||||
|
if ($nights <= 0) {
|
||||||
|
echo "<script>alert('Invalid date range. Please select valid dates.'); window.history.back();</script>";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine rate per night
|
||||||
|
$rate_per_night = 200; // Free for members, R200 for non-members
|
||||||
|
|
||||||
|
// Calculate the total cost
|
||||||
|
$vehicle_cost = $rate_per_night * $num_vehicles * $nights;
|
||||||
|
$total_discount = $is_member ? $vehicle_cost : 0;
|
||||||
|
$firewood_cost = $add_firewood ? 50 : 0;
|
||||||
|
$total_amount = $vehicle_cost + $firewood_cost;
|
||||||
|
$payment_amount = $total_amount - $total_discount;
|
||||||
|
$status = "AWAITING PAYMENT";
|
||||||
|
$description = "BASE4 Camping";
|
||||||
|
|
||||||
|
$payment_id = uniqid();
|
||||||
|
$eft_id = strtoupper($trip_code." ".getLastName($user_id));
|
||||||
|
|
||||||
|
// Insert booking into the database
|
||||||
|
$sql = "INSERT INTO bookings (booking_type, user_id, from_date, to_date, num_vehicles, num_adults, num_children, add_firewood, total_amount, discount_amount, status, payment_id)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bind_param('sissiiiiddss', $type, $user_id, $from_date, $to_date, $num_vehicles, $num_adults, $num_children, $add_firewood, $total_amount, $total_discount, $status, $payment_id);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$booking_id = $conn->insert_id;
|
||||||
|
|
||||||
|
if ($payment_amount < 1) {
|
||||||
|
if (processZeroPayment($payment_id, $payment_amount, $description)) {
|
||||||
|
echo "<script>alert('Booking successfully created!'); window.location.href = 'bookings.php';</script>";
|
||||||
|
} else {
|
||||||
|
$error_message = $stmt->error;
|
||||||
|
echo "Error processing booking: $error_message";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addEFT($eft_id, $booking_id, $user_id, $status, $payment_amount, $description);
|
||||||
|
header("Location: payment_confirmation?booking_id=".$booking_id);
|
||||||
|
exit(); // Ensure no further code is executed after the redirect
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle error if insert fails and echo the MySQL error
|
||||||
|
$error_message = $stmt->error;
|
||||||
|
echo "Error processing booking: $error_message";
|
||||||
|
}
|
||||||
|
|
||||||
|
// if ($stmt->execute()) {
|
||||||
|
// if ($payment_amount < 1) {
|
||||||
|
// if (processZeroPayment($payment_id, $payment_amount, $description)) {
|
||||||
|
// echo "<script>alert('Booking successfully created!'); window.location.href = 'bookings.php';</script>";
|
||||||
|
// } else {
|
||||||
|
// $error_message = $stmt->error;
|
||||||
|
// echo "Error processing booking: $error_message";
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// if (processPayment($payment_id, $payment_amount, $description)) {
|
||||||
|
// echo "<script>alert('Booking successfully created!'); window.location.href = 'bookings.php';</script>";
|
||||||
|
// } else {
|
||||||
|
// $error_message = $stmt->error;
|
||||||
|
// echo "Error processing booking: $error_message";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // Handle error if insert fails and echo the MySQL error
|
||||||
|
// $error_message = $stmt->error;
|
||||||
|
// echo "Error processing booking: $error_message";
|
||||||
|
// }
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
} else {
|
||||||
|
echo "Invalid request.";
|
||||||
|
}
|
||||||
|
|
||||||
145
src/processors/process_course_booking.php
Normal file
145
src/processors/process_course_booking.php
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
|
||||||
|
// Get user ID from session (assuming user is logged in)
|
||||||
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
||||||
|
|
||||||
|
// Validate user session
|
||||||
|
if (!$user_id) {
|
||||||
|
echo "<script>alert('User is not logged in. Please log in to make a booking.'); window.location.href = 'login.php';</script>";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$is_member = getUserMemberStatus($user_id);
|
||||||
|
$pending_member = getUserMemberStatusPending($user_id);
|
||||||
|
|
||||||
|
// Check if the form has been submitted
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
auditLog($user_id, 'CSRF_VALIDATION_FAILED', 'bookings', null, ['endpoint' => 'process_course_booking.php']);
|
||||||
|
http_response_code(403);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['error' => 'Security token validation failed.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input variables from the form (use default values if not provided)
|
||||||
|
$additional_members = validateInteger($_POST['members'] ?? 0, 0, 20);
|
||||||
|
if ($additional_members === false) $additional_members = 0;
|
||||||
|
|
||||||
|
$num_adults = validateInteger($_POST['non-members'] ?? 0, 0, 20);
|
||||||
|
if ($num_adults === false) $num_adults = 0;
|
||||||
|
|
||||||
|
$course_id = validateInteger($_POST['course_id'] ?? 0, 1, 999999);
|
||||||
|
if ($course_id === false) {
|
||||||
|
http_response_code(400);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['error' => 'Invalid course ID.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAndRedirectCourseBooking($course_id);
|
||||||
|
// Fetch trip costs from the database
|
||||||
|
$query = "SELECT date, cost_members, cost_nonmembers, course_type FROM courses WHERE course_id = ?";
|
||||||
|
$stmt = $conn->prepare($query);
|
||||||
|
$stmt->bind_param('i', $course_id);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
|
||||||
|
// Check if trip exists
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$response = ['error' => 'Trip not found.'];
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($response);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch trip details
|
||||||
|
$course = $result->fetch_assoc();
|
||||||
|
$type = $course['course_type'];
|
||||||
|
$date = $course['date'];
|
||||||
|
$cost_members = intval($course['cost_members']);
|
||||||
|
$cost_nonmembers = intval($course['cost_nonmembers']);
|
||||||
|
|
||||||
|
if ($type === "driver_training") {
|
||||||
|
$description = "Basic 4X4 Driver Training Course " . $date;
|
||||||
|
} elseif ($type === "bush_mechanics") {
|
||||||
|
$description = "Bush Mechanics Course " . $date;
|
||||||
|
} elseif ($type === "rescue_recovery") {
|
||||||
|
$description = "Rescue & Recovery Training Course " . $date;
|
||||||
|
} else {
|
||||||
|
$description = "General Course " . $date; // Default fallback description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize total and discount amount
|
||||||
|
$total = 0;
|
||||||
|
|
||||||
|
// Calculate total based on membership
|
||||||
|
if ($is_member || $pending_member) {
|
||||||
|
$num_members = 1 + $additional_members;
|
||||||
|
$total = ($num_members * $cost_members) + ($num_adults * $cost_nonmembers);
|
||||||
|
$payment_amount = $total;
|
||||||
|
} else {
|
||||||
|
$num_members = 0;
|
||||||
|
$total = (($cost_nonmembers) + ($num_adults * $cost_nonmembers));
|
||||||
|
$payment_amount = $total;
|
||||||
|
$num_adults = $num_adults + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = "AWAITING PAYMENT";
|
||||||
|
$type = 'course';
|
||||||
|
$payment_id = uniqid();
|
||||||
|
$num_vehicles = 1;
|
||||||
|
$discountAmount = 0;
|
||||||
|
$eft_id = strtoupper("COURSE ".date("m-d", strtotime($date))." ".getInitialSurname($user_id));
|
||||||
|
$notes = "";
|
||||||
|
if ($pending_member){
|
||||||
|
$notes = "Membership Payment pending at time of booking. Please confirm payment has been received.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Insert booking into the database
|
||||||
|
$sql = "INSERT INTO bookings (booking_type, user_id, from_date, to_date, num_vehicles, num_adults, total_amount, discount_amount, status, payment_id, course_id, course_non_members, eft_id, notes)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Preparation failed: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param('sissiiddssiiss', $type, $user_id, $date, $date, $num_vehicles, $num_members, $total, $discountAmount, $status, $payment_id, $course_id, $num_adults, $eft_id, $notes);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$booking_id = $conn->insert_id;
|
||||||
|
|
||||||
|
if ($payment_amount < 1) {
|
||||||
|
if (processZeroPayment($payment_id, $payment_amount, $description)) {
|
||||||
|
echo "<script>alert('Booking successfully created!'); window.location.href = 'bookings.php';</script>";
|
||||||
|
} else {
|
||||||
|
$error_message = $stmt->error;
|
||||||
|
echo "Error processing booking: $error_message";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addEFT($eft_id, $booking_id, $user_id, $status, $payment_amount, $description);
|
||||||
|
sendInvoice(getEmail($user_id), getFullName($user_id), $eft_id, formatCurrency($payment_amount), $description);
|
||||||
|
sendAdminNotification('New Course Booking - '.getFullName($user_id), getFullName($user_id).' has booked for '.$description);
|
||||||
|
header("Location: payment_confirmation?token=".encryptData($booking_id, $salt));
|
||||||
|
exit(); // Ensure no further code is executed after the redirect
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle error if insert fails and echo the MySQL error
|
||||||
|
$error_message = $stmt->error;
|
||||||
|
echo "Error processing booking: $error_message";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
} else {
|
||||||
|
echo "Invalid request.";
|
||||||
|
}
|
||||||
|
|
||||||
99
src/processors/process_eft.php
Normal file
99
src/processors/process_eft.php
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/session.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
checkAdmin();
|
||||||
|
|
||||||
|
// CSRF Token Validation for POST requests
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
auditLog($_SESSION['user_id'] ?? null, 'CSRF_VALIDATION_FAILED', 'efts', null, ['endpoint' => 'process_eft.php']);
|
||||||
|
http_response_code(403);
|
||||||
|
die('Security token validation failed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_GET['token']) || empty($_GET['token'])) {
|
||||||
|
die("Invalid request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $_GET['token'];
|
||||||
|
// echo $token;
|
||||||
|
$eft_id = decryptData($token, $salt);
|
||||||
|
$user = getUserIdFromEFT($eft_id);
|
||||||
|
|
||||||
|
// echo $eft_id;
|
||||||
|
// Start transaction for atomicity
|
||||||
|
$conn->begin_transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Update the efts table to set status = 'PAID'
|
||||||
|
$updateEFT = "UPDATE efts SET status = 'PAID' WHERE eft_id = ?";
|
||||||
|
$stmt = $conn->prepare($updateEFT);
|
||||||
|
if (!$stmt) {
|
||||||
|
throw new Exception("Prepare failed: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param("s", $eft_id);
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
throw new Exception("EFT update failed: " . $stmt->error);
|
||||||
|
}
|
||||||
|
$stmt->close();
|
||||||
|
|
||||||
|
// Retrieve the booking_id from efts table
|
||||||
|
$getBooking = "SELECT booking_id FROM efts WHERE eft_id = ?";
|
||||||
|
$stmt = $conn->prepare($getBooking);
|
||||||
|
if (!$stmt) {
|
||||||
|
throw new Exception("Prepare failed: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param("s", $eft_id);
|
||||||
|
$stmt->execute();
|
||||||
|
$stmt->bind_result($booking_id);
|
||||||
|
$stmt->fetch();
|
||||||
|
$stmt->close();
|
||||||
|
|
||||||
|
if (!empty($booking_id)) {
|
||||||
|
// Update the bookings table if booking_id exists
|
||||||
|
$updateBooking = "UPDATE bookings SET status = 'PAID' WHERE booking_id = ?";
|
||||||
|
$stmt = $conn->prepare($updateBooking);
|
||||||
|
if (!$stmt) {
|
||||||
|
throw new Exception("Prepare failed: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param("i", $booking_id);
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
throw new Exception("Booking update failed: " . $stmt->error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If no booking_id is found, update membership_fees instead
|
||||||
|
$updateMembership = "UPDATE membership_fees SET payment_status = 'PAID' WHERE payment_id = ?";
|
||||||
|
$stmt = $conn->prepare($updateMembership);
|
||||||
|
if (!$stmt) {
|
||||||
|
throw new Exception("Prepare failed: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param("s", $eft_id);
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
throw new Exception("Membership fee update failed: " . $stmt->error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$stmt->close();
|
||||||
|
|
||||||
|
// Commit transaction if everything was successful
|
||||||
|
$conn->commit();
|
||||||
|
sendPaymentConfirmation(getEmail($user), getFullName($user), getEftDescription($eft_id));
|
||||||
|
header("Location: admin_efts");
|
||||||
|
exit(); // Ensure no further code is executed after the redirect
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Rollback transaction if an error occurs
|
||||||
|
$conn->rollback();
|
||||||
|
echo "Error: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
$conn->close();
|
||||||
|
|
||||||
78
src/processors/process_membership_payment.php
Normal file
78
src/processors/process_membership_payment.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
// Start session to retrieve the logged-in user's ID
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Get user ID from session (assuming user is logged in)
|
||||||
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
||||||
|
|
||||||
|
// Validate user session
|
||||||
|
if (!$user_id) {
|
||||||
|
echo "<script>alert('User is not logged in. Please log in to make a booking.'); window.location.href = 'login.php';</script>";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$is_member = getUserMemberStatus($user_id);
|
||||||
|
|
||||||
|
$query = "SELECT payment_amount, payment_status, membership_end_date FROM membership_fees WHERE user_id = ?";
|
||||||
|
$stmt = $conn->prepare($query);
|
||||||
|
$stmt->bind_param('i', $user_id);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
|
||||||
|
// Check if trip exists
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$response = ['error' => 'Application Fee not found.'];
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($response);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch trip details
|
||||||
|
$fee = $result->fetch_assoc();
|
||||||
|
$payment_status = $fee['payment_status'];
|
||||||
|
$membership_end_date = $fee['membership_end_date'];
|
||||||
|
$payment_amount = intval($fee['payment_amount']);
|
||||||
|
|
||||||
|
$description = "4WDCSA: Membership Fee " . getFullName($user_id) . " " . date("Y");
|
||||||
|
$payment_id = uniqid();
|
||||||
|
$eft_id = "SUBS 2025 ".getLastName($user_id);
|
||||||
|
|
||||||
|
// Update the membership_fees table to set payment_id
|
||||||
|
$stmt = $conn->prepare("UPDATE membership_fees SET payment_id = ? WHERE user_id = ?");
|
||||||
|
if ($stmt) {
|
||||||
|
$stmt->bind_param("ss", $payment_id, $user_id);
|
||||||
|
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
throw new Exception("Failed to update membership_fees table.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
} else {
|
||||||
|
throw new Exception("Failed to prepare statement for membership_fees table: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current date
|
||||||
|
$current_date = new DateTime();
|
||||||
|
|
||||||
|
// Convert $membership_end_date to a DateTime object
|
||||||
|
$membership_end_date_obj = DateTime::createFromFormat('Y-m-d', $membership_end_date);
|
||||||
|
|
||||||
|
// Check if the current date is after membership_end_date
|
||||||
|
// OR if the current date is before or on membership_end_date AND payment_status is "PENDING"
|
||||||
|
if (
|
||||||
|
$current_date > $membership_end_date_obj ||
|
||||||
|
($current_date <= $membership_end_date_obj && $payment_status === "PENDING")
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Call the processMembershipPayment function
|
||||||
|
// processMembershipPayment($payment_id, $payment_amount, $description);
|
||||||
|
addMembershipEFT($eft_id, $user_id, $status, $amount, $description, $membershipfee_id);
|
||||||
|
header("Location: payment_confirmation?booking_id=" . $booking_id);
|
||||||
|
exit(); // Ensure no further code is executed after the redirect
|
||||||
|
}
|
||||||
|
|
||||||
152
src/processors/process_payments.php
Normal file
152
src/processors/process_payments.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
$headerStyle = 'light';
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
include_once($rootPath . '/header.php');
|
||||||
|
checkAdmin();
|
||||||
|
checkUserSession();
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
.image {
|
||||||
|
width: 400px;
|
||||||
|
/* Set your desired width */
|
||||||
|
height: 350px;
|
||||||
|
/* Set your desired height */
|
||||||
|
overflow: hidden;
|
||||||
|
/* Hide any overflow */
|
||||||
|
display: block;
|
||||||
|
/* Ensure proper block behavior */
|
||||||
|
}
|
||||||
|
|
||||||
|
.image img {
|
||||||
|
width: 100%;
|
||||||
|
/* Image scales to fill the container */
|
||||||
|
height: 100%;
|
||||||
|
/* Image scales to fill the container */
|
||||||
|
object-fit: cover;
|
||||||
|
/* Fills the container while maintaining aspect ratio */
|
||||||
|
object-position: top;
|
||||||
|
/* Aligns the top of the image with the top of the container */
|
||||||
|
display: block;
|
||||||
|
/* Prevents inline whitespace issues */
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-box {
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
padding: 10px;
|
||||||
|
padding-right: 35px;
|
||||||
|
/* Ensures text doesn't overlap with the close button */
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
/* Centers vertically */
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</style>
|
||||||
|
<?php
|
||||||
|
$status = "PROCESSING";
|
||||||
|
$bannerFolder = 'assets/images/banners/';
|
||||||
|
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
|
||||||
|
|
||||||
|
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
|
||||||
|
if (!empty($bannerImages)) {
|
||||||
|
$randomBanner = $bannerImages[array_rand($bannerImages)];
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
|
||||||
|
<div class="banner-overlay"></div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="banner-inner text-white mb-50">
|
||||||
|
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Process Payments</h2>
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
|
||||||
|
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
|
||||||
|
<li class="breadcrumb-item active">Process Payments</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Tour List Area start -->
|
||||||
|
<section class="tour-list-page py-100 rel z-1">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<?php if (isset($_SESSION['message'])): ?>
|
||||||
|
<div class="alert alert-warning message-box">
|
||||||
|
<?php echo $_SESSION['message']; ?>
|
||||||
|
<span class="close-btn" onclick="this.parentElement.style.display='none'">×</span>
|
||||||
|
</div>
|
||||||
|
<?php unset($_SESSION['message']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
// Query to retrieve data from the bookings table
|
||||||
|
$sql = "SELECT * FROM efts WHERE status = ? ORDER BY timestamp DESC";
|
||||||
|
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bind_param("s", $status);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
|
||||||
|
if ($result->num_rows > 0) {
|
||||||
|
// Loop through each row
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
$eft_id = $row['eft_id'];
|
||||||
|
$file_name = str_replace(' ', '_', $eft_id);
|
||||||
|
$eft_user = $row['user_id'];
|
||||||
|
$eft_amount = $row['amount'];
|
||||||
|
$eft_description = $row['description'];
|
||||||
|
|
||||||
|
// Output the HTML structure with dynamic data
|
||||||
|
echo '
|
||||||
|
<div class="destination-item style-three bgc-lighter booking " data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
|
||||||
|
<div class="p-4" >
|
||||||
|
<iframe src="uploads/pop/'.$file_name.'.pdf#toolbar=0" width="400px" height="200px"></iframe>
|
||||||
|
<p><a href="uploads/pop/'.$file_name.'.pdf" target="_new" class="theme-btn style-three" style="width:100%;">View Full PDF</a></p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div style="width:100%;" class="content">
|
||||||
|
<h5>' . htmlspecialchars($eft_description) . '</a></h5>
|
||||||
|
<h5>' . getFullName($eft_user) . '</a></h5>
|
||||||
|
<div class="destination-footer">
|
||||||
|
<span class="price"><span>Booking Total: R ' . number_format($eft_amount, 2) . '</span></span>
|
||||||
|
<a href="process_eft.php?token=' . encryptData($eft_id, $salt) . '" class="theme-btn style-three"><span data-hover="POP RECEIVED">PROCESS</span></a>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo '<p>There are no pending payments for processing.</p>';
|
||||||
|
}
|
||||||
|
// Close connection
|
||||||
|
$conn->close();
|
||||||
|
?>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- Tour List Area end -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||||
70
src/processors/process_signature.php
Normal file
70
src/processors/process_signature.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/session.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
die(json_encode(['status' => 'error', 'message' => 'User not logged in']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['signature'])) {
|
||||||
|
// CSRF Token Validation
|
||||||
|
// if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
// auditLog($_SESSION['user_id'], 'CSRF_VALIDATION_FAILED', 'membership_application', null, ['endpoint' => 'process_signature.php']);
|
||||||
|
// die(json_encode(['status' => 'error', 'message' => 'Security token validation failed']));
|
||||||
|
// }
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id']; // Get the user ID from the session
|
||||||
|
$signature = $_POST['signature']; // Base64 image data
|
||||||
|
|
||||||
|
// Decode the base64 image
|
||||||
|
$signature = str_replace('data:image/png;base64,', '', $signature);
|
||||||
|
$signature = str_replace(' ', '+', $signature);
|
||||||
|
$signatureData = base64_decode($signature);
|
||||||
|
|
||||||
|
// Create a file path for the signature image
|
||||||
|
$fileName = 'signature_' . $user_id . '.png';
|
||||||
|
$filePath = 'uploads/signatures/' . $fileName;
|
||||||
|
|
||||||
|
// Ensure the directory exists
|
||||||
|
if (!is_dir('uploads/signatures')) {
|
||||||
|
mkdir('uploads/signatures', 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the image file
|
||||||
|
if (file_put_contents($filePath, $signatureData)) {
|
||||||
|
// Update the database
|
||||||
|
|
||||||
|
if ($conn->connect_error) {
|
||||||
|
die(json_encode(['status' => 'error', 'message' => 'Database connection failed']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the signature and indemnity acceptance in the membership application table
|
||||||
|
$stmt = $conn->prepare("UPDATE membership_application SET sig = ?, accept_indemnity = 1 WHERE user_id = ?");
|
||||||
|
$stmt->bind_param('si', $filePath, $user_id);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
// Check the payment status
|
||||||
|
$paymentStatus = checkMembershipPaymentStatus($user_id) ? 'PAID' : 'NOT_PAID';
|
||||||
|
|
||||||
|
// Respond with the appropriate redirect URL based on the payment status
|
||||||
|
echo json_encode([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Signature saved successfully!',
|
||||||
|
'paymentStatus' => $paymentStatus // Send payment status
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Database update failed']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
} else {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Failed to save signature']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Signature not provided']);
|
||||||
|
}
|
||||||
|
|
||||||
172
src/processors/process_trip_booking.php
Normal file
172
src/processors/process_trip_booking.php
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Get the trip_id from the request (ensure it's sanitized)
|
||||||
|
$trip_id = isset($_POST['trip_id']) ? intval($_POST['trip_id']) : 0;
|
||||||
|
|
||||||
|
checkAndRedirectBooking($trip_id);
|
||||||
|
|
||||||
|
// Check available spaces
|
||||||
|
$available_spaces = getAvailableSpaces($trip_id); // Assuming you're using MySQLi and the function is updated for it
|
||||||
|
|
||||||
|
if ($available_spaces < 1) {
|
||||||
|
// Redirect back to trips.php with an error message
|
||||||
|
header("Location: trips?error=fully_booked");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user ID from session (assuming user is logged in)
|
||||||
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
||||||
|
|
||||||
|
// Validate user session
|
||||||
|
if (!$user_id) {
|
||||||
|
echo "<script>alert('User is not logged in. Please log in to make a booking.'); window.location.href = 'login.php';</script>";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$is_member = getUserMemberStatus($user_id);
|
||||||
|
|
||||||
|
// Check if the form has been submitted
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
auditLog($user_id, 'CSRF_VALIDATION_FAILED', 'bookings', null, ['endpoint' => 'process_trip_booking.php']);
|
||||||
|
http_response_code(403);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['error' => 'Security token validation failed.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input variables from the form (use default values if not provided)
|
||||||
|
$num_vehicles = validateInteger($_POST['vehicles'] ?? 1, 1, 10);
|
||||||
|
if ($num_vehicles === false) $num_vehicles = 1;
|
||||||
|
|
||||||
|
$num_adults = validateInteger($_POST['adults'] ?? 1, 1, 20);
|
||||||
|
if ($num_adults === false) $num_adults = 1;
|
||||||
|
|
||||||
|
$num_children = validateInteger($_POST['children'] ?? 0, 0, 20);
|
||||||
|
if ($num_children === false) $num_children = 0;
|
||||||
|
|
||||||
|
$num_pensioners = validateInteger($_POST['pensioners'] ?? 0, 0, 20);
|
||||||
|
if ($num_pensioners === false) $num_pensioners = 0;
|
||||||
|
// Fetch trip costs from the database
|
||||||
|
$query = "SELECT trip_name, cost_members, cost_nonmembers, cost_pensioner_member, cost_pensioner, booking_fee, start_date, end_date, trip_code FROM trips WHERE trip_id = ?";
|
||||||
|
$stmt = $conn->prepare($query);
|
||||||
|
$stmt->bind_param('i', $trip_id);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
|
||||||
|
// Check if trip exists
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$response = ['error' => 'Trip not found.'];
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($response);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch trip details
|
||||||
|
$trip = $result->fetch_assoc();
|
||||||
|
$trip_code = $trip['trip_code'];
|
||||||
|
$trip_name = $trip['trip_name'];
|
||||||
|
$cost_members = intval($trip['cost_members']);
|
||||||
|
$cost_nonmembers = intval($trip['cost_nonmembers']);
|
||||||
|
$cost_pensioner_member = intval($trip['cost_pensioner_member']);
|
||||||
|
$cost_pensioner = intval($trip['cost_pensioner']);
|
||||||
|
$member_discount = $cost_nonmembers - $cost_members;
|
||||||
|
$member_discount_pensioner = $cost_pensioner - $cost_pensioner_member;
|
||||||
|
$booking_fee = $trip['booking_fee'];
|
||||||
|
$radioCost = $radio ? 50 : 0;
|
||||||
|
$start_date = $trip['start_date']; // Start date of the trip
|
||||||
|
$end_date = $trip['end_date']; // End date of the trip
|
||||||
|
|
||||||
|
|
||||||
|
// Assume the membership status is determined elsewhere
|
||||||
|
$is_member = getUserMemberStatus($user_id);
|
||||||
|
|
||||||
|
// Initialize total and discount amount
|
||||||
|
$total = 0;
|
||||||
|
$discountAmount = 0;
|
||||||
|
|
||||||
|
// Calculate total based on membership
|
||||||
|
if ($is_member) {
|
||||||
|
$total = (($num_adults + $num_children) * $cost_nonmembers) + ($num_pensioners * $cost_pensioner) + $radioCost + ($num_vehicles * $booking_fee);
|
||||||
|
$discountAmount = (($num_adults + $num_children) * $member_discount) + ($num_pensioners * $member_discount_pensioner );
|
||||||
|
$payment_amount = $total - $discountAmount;
|
||||||
|
} else {
|
||||||
|
$total = (($num_adults + $num_children) * $cost_nonmembers) + ($num_pensioners * $cost_pensioner) + $radioCost + ($num_vehicles * $booking_fee);
|
||||||
|
$payment_amount = $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = "AWAITING PAYMENT";
|
||||||
|
$description = $trip_name;
|
||||||
|
$type = 'trip';
|
||||||
|
$payment_id = uniqid();
|
||||||
|
// $eft_id = strtoupper(base_convert(time(), 10, 36)); // Convert timestamp to base36
|
||||||
|
$eft_id = strtoupper($trip_code." ".getInitialSurname($user_id));
|
||||||
|
|
||||||
|
|
||||||
|
// Insert booking into the database
|
||||||
|
$sql = "INSERT INTO bookings (booking_type, user_id, from_date, to_date, num_vehicles, num_adults, num_children, total_amount, discount_amount, status, payment_id, trip_id, radio, eft_id, num_pensioners)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Preparation failed: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param('sissiiiddssiisi', $type, $user_id, $start_date, $end_date, $num_vehicles, $num_adults, $num_children, $total, $discountAmount, $status, $payment_id, $trip_id, $radio, $eft_id, $num_pensioners);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
// Get the generated booking_id
|
||||||
|
$booking_id = $conn->insert_id;
|
||||||
|
|
||||||
|
if ($payment_amount < 1) {
|
||||||
|
if (processZeroPayment($payment_id, $payment_amount, $description)) {
|
||||||
|
echo "<script>alert('Booking successfully created!'); window.location.href = 'bookings.php';</script>";
|
||||||
|
} else {
|
||||||
|
$error_message = $stmt->error;
|
||||||
|
echo "Error processing booking: $error_message";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addEFT($eft_id, $booking_id, $user_id, $status, $payment_amount, $description);
|
||||||
|
sendAdminNotification('New Trip Booking - '.getFullName($user_id), getFullName($user_id).' has booked for '.$description);
|
||||||
|
header("Location: payment_confirmation?token=".encryptData($booking_id, $salt));
|
||||||
|
exit(); // Ensure no further code is executed after the redirect
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle error if insert fails and echo the MySQL error
|
||||||
|
$error_message = $stmt->error;
|
||||||
|
echo "Error processing booking: $error_message";
|
||||||
|
}
|
||||||
|
|
||||||
|
// if ($stmt->execute()) {
|
||||||
|
// if ($payment_amount < 1) {
|
||||||
|
// if (processZeroPayment($payment_id, $payment_amount, $description)) {
|
||||||
|
// echo "<script>alert('Booking successfully created!'); window.location.href = 'bookings.php';</script>";
|
||||||
|
// } else {
|
||||||
|
// $error_message = $stmt->error;
|
||||||
|
// echo "Error processing booking: $error_message";
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// if (processPayment($payment_id, $payment_amount, $description)) {
|
||||||
|
// echo "<script>alert('Booking successfully created!'); window.location.href = 'bookings.php';</script>";
|
||||||
|
// } else {
|
||||||
|
// $error_message = $stmt->error;
|
||||||
|
// echo "Error processing booking: $error_message";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // Handle error if insert fails and echo the MySQL error
|
||||||
|
// $error_message = $stmt->error;
|
||||||
|
// echo "Error processing booking: $error_message";
|
||||||
|
// }
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
} else {
|
||||||
|
echo "Invalid request.";
|
||||||
|
}
|
||||||
|
|
||||||
147
src/processors/register_user.php
Normal file
147
src/processors/register_user.php
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/session.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
require_once($rootPath . "/vendor/autoload.php");
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
// Create connection
|
||||||
|
$conn = openDatabaseConnection();
|
||||||
|
|
||||||
|
// Check connection
|
||||||
|
if ($conn->connect_error) {
|
||||||
|
die("Connection failed: " . $conn->connect_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Form processing
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
auditLog(null, 'CSRF_VALIDATION_FAILED', 'users', null, ['endpoint' => 'register_user.php']);
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Security token validation failed. Please try again.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check rate limiting on registration endpoint (by IP)
|
||||||
|
$ip = getClientIPAddress();
|
||||||
|
$cutoffTime = date('Y-m-d H:i:s', time() - (3600)); // Last hour
|
||||||
|
|
||||||
|
$stmt = $conn->prepare("SELECT COUNT(*) as count FROM audit_log WHERE action = 'REGISTRATION_ATTEMPT' AND ip_address = ? AND created_at > ?");
|
||||||
|
$stmt->bind_param('ss', $ip, $cutoffTime);
|
||||||
|
$stmt->execute();
|
||||||
|
$stmt->bind_result($regAttempts);
|
||||||
|
$stmt->fetch();
|
||||||
|
$stmt->close();
|
||||||
|
|
||||||
|
// Allow max 5 registration attempts per IP per hour
|
||||||
|
if ($regAttempts >= 5) {
|
||||||
|
auditLog(null, 'REGISTRATION_RATE_LIMIT_EXCEEDED', 'users', null, ['ip' => $ip, 'attempts' => $regAttempts]);
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Too many registration attempts. Please try again later.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and sanitize first name
|
||||||
|
$first_name = validateName($_POST['first_name'] ?? '');
|
||||||
|
if ($first_name === false) {
|
||||||
|
auditLog(null, 'REGISTRATION_INVALID_FIRST_NAME', 'users');
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid first name. Only letters, spaces, hyphens, and apostrophes allowed (2-100 characters).']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and sanitize last name
|
||||||
|
$last_name = validateName($_POST['last_name'] ?? '');
|
||||||
|
if ($last_name === false) {
|
||||||
|
auditLog(null, 'REGISTRATION_INVALID_LAST_NAME', 'users');
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid last name. Only letters, spaces, hyphens, and apostrophes allowed (2-100 characters).']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and sanitize phone number
|
||||||
|
$phone_number = validatePhoneNumber($_POST['phone_number'] ?? '');
|
||||||
|
if ($phone_number === false) {
|
||||||
|
auditLog(null, 'REGISTRATION_INVALID_PHONE', 'users');
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid phone number format.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate email
|
||||||
|
$email = validateEmail($_POST['email'] ?? '');
|
||||||
|
if ($email === false) {
|
||||||
|
auditLog(null, 'REGISTRATION_INVALID_EMAIL', 'users');
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Invalid email format.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$password_confirm = $_POST['password_confirm'] ?? '';
|
||||||
|
|
||||||
|
// Validate password strength (minimum 8 characters, must contain uppercase, lowercase, number, special char)
|
||||||
|
if (strlen($password) < 8) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Password must be at least 8 characters long.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/[A-Z]/', $password) || !preg_match('/[a-z]/', $password) ||
|
||||||
|
!preg_match('/[0-9]/', $password) || !preg_match('/[!@#$%^&*]/', $password)) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Password must contain uppercase, lowercase, number, and special character (!@#$%^&*).']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($password !== $password_confirm) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Passwords do not match.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the email is already registered
|
||||||
|
$stmt = $conn->prepare('SELECT user_id FROM users WHERE email = ?');
|
||||||
|
$stmt->bind_param('s', $email);
|
||||||
|
$stmt->execute();
|
||||||
|
$stmt->store_result();
|
||||||
|
|
||||||
|
if ($stmt->num_rows > 0) {
|
||||||
|
auditLog(null, 'REGISTRATION_EMAIL_EXISTS', 'users', null, ['email' => $email]);
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Email is already registered.']);
|
||||||
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
|
||||||
|
// Hash password
|
||||||
|
$hashed_password = password_hash($password, PASSWORD_BCRYPT);
|
||||||
|
|
||||||
|
// Generate email verification token
|
||||||
|
$token = bin2hex(random_bytes(50));
|
||||||
|
|
||||||
|
// Prepare and execute query
|
||||||
|
$stmt = $conn->prepare('INSERT INTO users (first_name, last_name, phone_number, email, password, token, is_verified, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
|
||||||
|
$is_verified = 0; // Not verified
|
||||||
|
$type = 'credentials';
|
||||||
|
$stmt->bind_param('ssssssis', $first_name, $last_name, $phone_number, $email, $hashed_password, $token, $is_verified, $type);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$newUser_id = $conn->insert_id;
|
||||||
|
processLegacyMembership($newUser_id);
|
||||||
|
auditLog($newUser_id, 'USER_REGISTRATION', 'users', $newUser_id, ['email' => $email]);
|
||||||
|
|
||||||
|
if (sendVerificationEmail($email, $first_name . ' ' . $last_name, $token)) {
|
||||||
|
sendEmail($_ENV['ADMIN_EMAIL'], '4WDCSA: New User Registration', $first_name . ' ' . $last_name . ' (' . $email . ') has just created an account using Credentials.');
|
||||||
|
echo json_encode(['status' => 'success', 'message' => 'Registration successful. Please check your email to verify your account.']);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Failed to send verification email.']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auditLog(null, 'REGISTRATION_DATABASE_ERROR', 'users', null, ['email' => $email]);
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Failed to register user.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->close();
|
||||||
|
|
||||||
49
src/processors/send_reset_link.php
Normal file
49
src/processors/send_reset_link.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
$response = array('status' => 'error', 'message' => 'Something went wrong');
|
||||||
|
|
||||||
|
if (isset($_POST['email'])) {
|
||||||
|
$email = $_POST['email'];
|
||||||
|
|
||||||
|
// Check if the email exists
|
||||||
|
$sql = "SELECT user_id FROM users WHERE email = ?";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bind_param("s", $email);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
|
||||||
|
if ($result->num_rows > 0) {
|
||||||
|
$user = $result->fetch_assoc();
|
||||||
|
$user_id = $user['user_id'];
|
||||||
|
|
||||||
|
// Generate a unique token
|
||||||
|
$token = bin2hex(random_bytes(50));
|
||||||
|
|
||||||
|
// Store the token and expiration time in the database
|
||||||
|
$expiry = date("Y-m-d H:i:s", strtotime('+3 hour')); // Token expires in 1 hour
|
||||||
|
$sql = "INSERT INTO password_resets (user_id, token, expires_at) VALUES (?, ?, ?)
|
||||||
|
ON DUPLICATE KEY UPDATE token = VALUES(token), expires_at = VALUES(expires_at)";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bind_param("iss", $user_id, $token, $expiry);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
// Send the reset link to the user
|
||||||
|
$reset_link = "https://www.4wdcsa.co.za/reset_password.php?token=$token";
|
||||||
|
$subject = "Password Reset Request";
|
||||||
|
$message = "Click the following link to reset your password: $reset_link";
|
||||||
|
sendEmail($email, $subject, $message);
|
||||||
|
|
||||||
|
$response['status'] = 'success';
|
||||||
|
$response['message'] = 'Password reset link has been sent to your email.';
|
||||||
|
} else {
|
||||||
|
$response['message'] = 'Email not found.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
?>
|
||||||
|
|
||||||
48
src/processors/submit_order.php
Normal file
48
src/processors/submit_order.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Security token validation failed.']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['tab_id']) && isset($_SESSION['cart'][$_POST['tab_id']])) {
|
||||||
|
$tab_id = (int) $_POST['tab_id']; // Ensure it's an integer
|
||||||
|
$drinks = $_SESSION['cart'][$tab_id];
|
||||||
|
$created_at = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
$errors = []; // Array to store SQL errors
|
||||||
|
|
||||||
|
foreach ($drinks as $drink) {
|
||||||
|
$drink_id = (int) $drink['item_id']; // Ensure drink ID is an integer
|
||||||
|
$drink_name = $drink['item_name']; // No escaping needed with prepared statements
|
||||||
|
$drink_price = (float) $drink['item_price']; // Ensure price is a float
|
||||||
|
$user_id = (int) $drink['user_id']; // Convert to integer
|
||||||
|
|
||||||
|
// Insert each drink into the bar_transactions table using prepared statement
|
||||||
|
$stmt = $conn->prepare("INSERT INTO bar_transactions (user_id, tab_id, item_id, item_name, item_price) VALUES (?, ?, ?, ?, ?)");
|
||||||
|
$stmt->bind_param("iiisi", $user_id, $tab_id, $drink_id, $drink_name, $drink_price);
|
||||||
|
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
$errors[] = "Error inserting drink ID $drink_id: " . $conn->error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
// Clear the cart for this tab after successful submission
|
||||||
|
unset($_SESSION['cart'][$tab_id]);
|
||||||
|
echo json_encode(['status' => 'success', 'message' => 'Order submitted successfully!']);
|
||||||
|
} else {
|
||||||
|
// Log all errors and return failure message
|
||||||
|
error_log(implode("\n", $errors)); // Log errors to the server
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Some items failed to be added.', 'errors' => $errors]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => 'Cart is empty or tab ID is invalid.']);
|
||||||
|
}
|
||||||
|
|
||||||
215
src/processors/submit_pop.php
Normal file
215
src/processors/submit_pop.php
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<?php
|
||||||
|
$headerStyle = 'light';
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
include_once($rootPath . '/header.php');
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
checkUserSession();
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'] ?? null;
|
||||||
|
|
||||||
|
if (!$user_id) {
|
||||||
|
die("Not logged in.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle POST submission
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// CSRF Token Validation
|
||||||
|
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
http_response_code(403);
|
||||||
|
die('Security token validation failed. Please try again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$eft_id = $_POST['eft_id'] ?? null;
|
||||||
|
|
||||||
|
if (!$eft_id || !isset($_FILES['pop_file'])) {
|
||||||
|
echo "<div class='alert alert-danger'>Invalid submission: missing eft_id or file.</div>";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate file using hardened validation function
|
||||||
|
$validationResult = validateFileUpload($_FILES['pop_file'], 'proof_of_payment');
|
||||||
|
|
||||||
|
if ($validationResult === false) {
|
||||||
|
echo "<div class='alert alert-danger'>Invalid file. Only PDF files under 10MB are allowed.</div>";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$target_dir = "uploads/pop/";
|
||||||
|
$randomFilename = $validationResult['filename'];
|
||||||
|
$target_file = $target_dir . $randomFilename;
|
||||||
|
|
||||||
|
// Make sure target directory exists and writable
|
||||||
|
if (!is_dir($target_dir)) {
|
||||||
|
mkdir($target_dir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_writable($target_dir)) {
|
||||||
|
echo "<div class='alert alert-danger'>Upload directory is not writable: $target_dir</div>";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (move_uploaded_file($_FILES['pop_file']['tmp_name'], $target_file)) {
|
||||||
|
chmod($target_file, 0644);
|
||||||
|
|
||||||
|
// Update EFT and booking status
|
||||||
|
$payment_type = $_POST['payment_type'] ?? 'booking';
|
||||||
|
|
||||||
|
if ($payment_type === 'membership') {
|
||||||
|
// Update EFT and booking status
|
||||||
|
$stmt1 = $conn->prepare("UPDATE efts SET status = 'PROCESSING' WHERE eft_id = ?");
|
||||||
|
$stmt1->bind_param("s", $eft_id);
|
||||||
|
$stmt1->execute();
|
||||||
|
$stmt1->close();
|
||||||
|
|
||||||
|
// Update membership fee status
|
||||||
|
$stmt = $conn->prepare("UPDATE membership_fees SET payment_status = 'PROCESSING' WHERE payment_id = ?");
|
||||||
|
$stmt->bind_param("s", $eft_id);
|
||||||
|
$stmt->execute();
|
||||||
|
$stmt->close();
|
||||||
|
} else {
|
||||||
|
// Update EFT and booking status
|
||||||
|
$stmt1 = $conn->prepare("UPDATE efts SET status = 'PROCESSING' WHERE eft_id = ?");
|
||||||
|
$stmt1->bind_param("s", $eft_id);
|
||||||
|
$stmt1->execute();
|
||||||
|
$stmt1->close();
|
||||||
|
|
||||||
|
$stmt2 = $conn->prepare("UPDATE bookings SET status = 'PROCESSING' WHERE eft_id = ?");
|
||||||
|
$stmt2->bind_param("s", $eft_id);
|
||||||
|
$stmt2->execute();
|
||||||
|
$stmt2->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send notification email using sendPOP()
|
||||||
|
$fullname = getFullName($user_id);
|
||||||
|
$eftDetails = getEFTDetails($eft_id);
|
||||||
|
|
||||||
|
if ($eftDetails) {
|
||||||
|
$amount = "R" . number_format($eftDetails['amount'], 2);
|
||||||
|
$description = $eftDetails['description'];
|
||||||
|
} else {
|
||||||
|
$amount = "R0.00";
|
||||||
|
$description = "Payment";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendPOP($fullname, $randomFilename, $amount, $description)) {
|
||||||
|
$_SESSION['message'] = "Thank you! Your payment proof has been uploaded and notification sent.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['message'] = "Payment uploaded, but notification email could not be sent.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the action
|
||||||
|
auditLog($user_id, 'POP_UPLOAD', 'efts', $eft_id, ['filename' => $randomFilename, 'payment_type' => $payment_type]);
|
||||||
|
|
||||||
|
header("Location: bookings");
|
||||||
|
exit;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
echo "<div class='alert alert-danger'>Unable to move uploaded file.</div>";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch bookings for dropdown
|
||||||
|
$stmt = $conn->prepare("
|
||||||
|
SELECT eft_id AS id, 'booking' AS type FROM bookings WHERE user_id = ? AND status = 'AWAITING PAYMENT'
|
||||||
|
UNION
|
||||||
|
SELECT payment_id AS id, 'membership' AS type FROM membership_fees WHERE user_id = ? AND payment_status = 'PENDING'
|
||||||
|
");
|
||||||
|
$stmt->bind_param("ii", $user_id, $user_id);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
$items = $result->fetch_all(MYSQLI_ASSOC);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$bannerFolder = 'assets/images/banners/';
|
||||||
|
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
|
||||||
|
|
||||||
|
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
|
||||||
|
if (!empty($bannerImages)) {
|
||||||
|
$randomBanner = $bannerImages[array_rand($bannerImages)];
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
|
||||||
|
<div class="banner-overlay"></div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="banner-inner text-white mb-50">
|
||||||
|
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Submit Proof of Payment</h2>
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
|
||||||
|
<li class="breadcrumb-item"><a href="index">Home</a></li>
|
||||||
|
<li class="breadcrumb-item active">Submit Proof of Payment</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Tour List Area start -->
|
||||||
|
<section class="tour-list-page py-100 rel z-1">
|
||||||
|
<div class="container" style="max-width:600px;">
|
||||||
|
<div class="row">
|
||||||
|
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
|
||||||
|
<div class="widget widget-booking" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
|
||||||
|
<div class="section-title">
|
||||||
|
<h3>Submit Proof of Payment</h3>
|
||||||
|
<div style="text-align: center;" id="responseMessage"></div>
|
||||||
|
<p>To finalise your booking/membership, select the payment reference below, and then upload your PDF proof of payment.</p> <!-- Message display area -->
|
||||||
|
</div>
|
||||||
|
<?php if (count($items) > 0) {?>
|
||||||
|
|
||||||
|
<form enctype="multipart/form-data" method="POST">
|
||||||
|
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
|
||||||
|
<div class="row mt-35">
|
||||||
|
<ul class="tickets clearfix">
|
||||||
|
<li>
|
||||||
|
Select Payment Reference:
|
||||||
|
<select name="eft_id" id="eft_id" required onchange="updatePaymentType(this)">
|
||||||
|
<?php
|
||||||
|
if (count($items) > 0) {
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$label = strtoupper($item['type']) . ' - ' . htmlspecialchars($item['id']);
|
||||||
|
echo '<option value="' . htmlspecialchars($item['id']) . '" data-type="' . $item['type'] . '">' . $label . '</option>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo '<option value="" disabled selected>No payments available</option>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
<input type="hidden" name="payment_type" id="payment_type">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<li>
|
||||||
|
<input style="border-radius:30px;" type="file" name="pop_file" id="pop_file" accept="application/pdf" class="form-control" required>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
<div class="mt-10 mb-0">
|
||||||
|
<button type="submit" class="theme-btn style-two" style="width:100%;">Submit POP</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
}else{
|
||||||
|
echo 'No unpaid bookings';
|
||||||
|
}?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function updatePaymentType(selectEl) {
|
||||||
|
const selectedOption = selectEl.options[selectEl.selectedIndex];
|
||||||
|
const type = selectedOption.getAttribute('data-type');
|
||||||
|
document.getElementById('payment_type').value = type;
|
||||||
|
}
|
||||||
|
window.onload = function() {
|
||||||
|
const dropdown = document.getElementById('eft_id');
|
||||||
|
updatePaymentType(dropdown); // set default value on page load
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||||
129
src/processors/update_application.php
Normal file
129
src/processors/update_application.php
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/session.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
|
||||||
|
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
// Get all the form fields
|
||||||
|
$first_name = $_POST['first_name'];
|
||||||
|
$last_name = $_POST['last_name'];
|
||||||
|
$id_number = $_POST['id_number'];
|
||||||
|
$dob = $_POST['dob'];
|
||||||
|
$occupation = $_POST['occupation'];
|
||||||
|
$tel_cell = $_POST['tel_cell'];
|
||||||
|
$email = $_POST['email'];
|
||||||
|
|
||||||
|
// Spouse or Partner details (optional)
|
||||||
|
$spouse_first_name = !empty($_POST['spouse_first_name']) ? $_POST['spouse_first_name'] : null;
|
||||||
|
$spouse_last_name = !empty($_POST['spouse_last_name']) ? $_POST['spouse_last_name'] : null;
|
||||||
|
$spouse_id_number = !empty($_POST['spouse_id_number']) ? $_POST['spouse_id_number'] : null;
|
||||||
|
$spouse_dob = !empty($_POST['spouse_dob']) ? $_POST['spouse_dob'] : NULL; // if empty, set to NULL
|
||||||
|
$spouse_occupation = !empty($_POST['spouse_occupation']) ? $_POST['spouse_occupation'] : null;
|
||||||
|
$spouse_tel_cell = !empty($_POST['spouse_tel_cell']) ? $_POST['spouse_tel_cell'] : null;
|
||||||
|
$spouse_email = !empty($_POST['spouse_email']) ? $_POST['spouse_email'] : null;
|
||||||
|
|
||||||
|
// Children details (optional)
|
||||||
|
$child_name1 = !empty($_POST['child_name1']) ? $_POST['child_name1'] : null;
|
||||||
|
$child_dob1 = !empty($_POST['child_dob1']) ? $_POST['child_dob1'] : null;
|
||||||
|
$child_name2 = !empty($_POST['child_name2']) ? $_POST['child_name2'] : null;
|
||||||
|
$child_dob2 = !empty($_POST['child_dob2']) ? $_POST['child_dob2'] : null;
|
||||||
|
$child_name3 = !empty($_POST['child_name3']) ? $_POST['child_name3'] : null;
|
||||||
|
$child_dob3 = !empty($_POST['child_dob3']) ? $_POST['child_dob3'] : null;
|
||||||
|
|
||||||
|
// Address and other details
|
||||||
|
$physical_address = $_POST['physical_address'];
|
||||||
|
$postal_address = $_POST['postal_address'];
|
||||||
|
$interests_hobbies = $_POST['interests_hobbies'];
|
||||||
|
|
||||||
|
// Primary vehicle details
|
||||||
|
$vehicle_make = $_POST['vehicle_make'];
|
||||||
|
$vehicle_model = $_POST['vehicle_model'];
|
||||||
|
$vehicle_year = $_POST['vehicle_year'];
|
||||||
|
$vehicle_registration = $_POST['vehicle_registration'];
|
||||||
|
|
||||||
|
// Secondary vehicle details (optional)
|
||||||
|
$secondary_vehicle_make = !empty($_POST['secondary_vehicle_make']) ? $_POST['secondary_vehicle_make'] : null;
|
||||||
|
$secondary_vehicle_model = !empty($_POST['secondary_vehicle_model']) ? $_POST['secondary_vehicle_model'] : null;
|
||||||
|
$secondary_vehicle_year = !empty($_POST['secondary_vehicle_year']) ? $_POST['secondary_vehicle_year'] : null;
|
||||||
|
$secondary_vehicle_registration = !empty($_POST['secondary_vehicle_registration']) ? $_POST['secondary_vehicle_registration'] : null;
|
||||||
|
|
||||||
|
// Start a transaction to ensure data consistency
|
||||||
|
$conn->begin_transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Prepare the SQL update statement
|
||||||
|
$stmt = $conn->prepare("UPDATE membership_application SET
|
||||||
|
first_name = ?, last_name = ?, id_number = ?, dob = ?, occupation = ?, tel_cell = ?, email = ?,
|
||||||
|
spouse_first_name = ?, spouse_last_name = ?, spouse_id_number = ?, spouse_dob = ?, spouse_occupation = ?, spouse_tel_cell = ?, spouse_email = ?,
|
||||||
|
child_name1 = ?, child_dob1 = ?, child_name2 = ?, child_dob2 = ?, child_name3 = ?, child_dob3 = ?,
|
||||||
|
physical_address = ?, postal_address = ?, interests_hobbies = ?, vehicle_make = ?, vehicle_model = ?, vehicle_year = ?, vehicle_registration = ?,
|
||||||
|
secondary_vehicle_make = ?, secondary_vehicle_model = ?, secondary_vehicle_year = ?, secondary_vehicle_registration = ?
|
||||||
|
WHERE user_id = ?");
|
||||||
|
|
||||||
|
// Check if preparation was successful
|
||||||
|
if (!$stmt) {
|
||||||
|
die("SQL error: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param(
|
||||||
|
"sssssssssssssssssssssssssssssssi",
|
||||||
|
$first_name,
|
||||||
|
$last_name,
|
||||||
|
$id_number,
|
||||||
|
$dob,
|
||||||
|
$occupation,
|
||||||
|
$tel_cell,
|
||||||
|
$email,
|
||||||
|
$spouse_first_name,
|
||||||
|
$spouse_last_name,
|
||||||
|
$spouse_id_number,
|
||||||
|
$spouse_dob,
|
||||||
|
$spouse_occupation,
|
||||||
|
$spouse_tel_cell,
|
||||||
|
$spouse_email,
|
||||||
|
$child_name1,
|
||||||
|
$child_dob1,
|
||||||
|
$child_name2,
|
||||||
|
$child_dob2,
|
||||||
|
$child_name3,
|
||||||
|
$child_dob3,
|
||||||
|
$physical_address,
|
||||||
|
$postal_address,
|
||||||
|
$interests_hobbies,
|
||||||
|
$vehicle_make,
|
||||||
|
$vehicle_model,
|
||||||
|
$vehicle_year,
|
||||||
|
$vehicle_registration,
|
||||||
|
$secondary_vehicle_make,
|
||||||
|
$secondary_vehicle_model,
|
||||||
|
$secondary_vehicle_year,
|
||||||
|
$secondary_vehicle_registration,
|
||||||
|
$user_id // User ID for WHERE condition
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$conn->commit();
|
||||||
|
header("Location: membership_details");
|
||||||
|
exit(); // Ensure no further code is executed after the redirect
|
||||||
|
} else {
|
||||||
|
throw new Exception("Failed to update member application. SQL error: " . $conn->error);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Rollback the transaction in case of error
|
||||||
|
$conn->rollback();
|
||||||
|
|
||||||
|
// Error response
|
||||||
|
$response = [
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Error: ' . $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
42
src/processors/update_user.php
Normal file
42
src/processors/update_user.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
require_once($rootPath . "/src/config/session.php");
|
||||||
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
|
||||||
|
$response = array('status' => 'error', 'message' => 'Something went wrong');
|
||||||
|
|
||||||
|
// Check if the user is logged in
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
$response['message'] = 'You are not logged in.';
|
||||||
|
echo json_encode($response);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
// Handle updating user details (excluding profile picture)
|
||||||
|
if (isset($_POST['first_name'], $_POST['last_name'], $_POST['phone_number'], $_POST['email'])) {
|
||||||
|
$first_name = ucwords(strtolower($_POST['first_name']));
|
||||||
|
$last_name = ucwords(strtolower($_POST['last_name']));
|
||||||
|
$phone_number = $_POST['phone_number'];
|
||||||
|
$email = $_POST['email'];
|
||||||
|
|
||||||
|
// Update user details in the database
|
||||||
|
$sql = "UPDATE users SET first_name = ?, last_name = ?, phone_number = ?, email = ? WHERE user_id = ?";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bind_param("ssssi", $first_name, $last_name, $phone_number, $email, $user_id);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$response['status'] = 'success';
|
||||||
|
$response['message'] = 'User details updated successfully';
|
||||||
|
} else {
|
||||||
|
$response['message'] = 'Failed to update user details';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$response['message'] = 'Invalid form submission';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
|
||||||
81
src/processors/upload_profile_picture.php
Normal file
81
src/processors/upload_profile_picture.php
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
$rootPath = dirname(dirname(__DIR__));
|
||||||
|
include_once($rootPath . '/src/config/connection.php');
|
||||||
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
require_once($rootPath . "/src/config/env.php");
|
||||||
|
|
||||||
|
$response = array('status' => 'error', 'message' => 'Something went wrong');
|
||||||
|
|
||||||
|
// Check if the user is logged in
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
$response['message'] = 'You are not logged in.';
|
||||||
|
echo json_encode($response);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
// Handle profile picture upload
|
||||||
|
if (isset($_FILES['profile_picture']) && $_FILES['profile_picture']['error'] != UPLOAD_ERR_NO_FILE) {
|
||||||
|
// Validate file using hardened validation function
|
||||||
|
$validationResult = validateFileUpload($_FILES['profile_picture'], 'profile_picture');
|
||||||
|
|
||||||
|
if ($validationResult === false) {
|
||||||
|
$response['message'] = 'Invalid file. Only JPG, JPEG, PNG, GIF, and WEBP images under 5MB are allowed.';
|
||||||
|
echo json_encode($response);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract validated filename
|
||||||
|
$randomFilename = $validationResult['filename'];
|
||||||
|
$target_dir = "assets/images/pp/";
|
||||||
|
$target_file = $target_dir . $randomFilename;
|
||||||
|
|
||||||
|
// Ensure upload directory exists and is writable
|
||||||
|
if (!is_dir($target_dir)) {
|
||||||
|
mkdir($target_dir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_writable($target_dir)) {
|
||||||
|
$response['message'] = 'Upload directory is not writable.';
|
||||||
|
echo json_encode($response);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the uploaded file
|
||||||
|
if (move_uploaded_file($_FILES['profile_picture']['tmp_name'], $target_file)) {
|
||||||
|
// Set secure file permissions (readable but not executable)
|
||||||
|
chmod($target_file, 0644);
|
||||||
|
|
||||||
|
// Update the profile picture path in the database
|
||||||
|
$sql = "UPDATE users SET profile_pic = ? WHERE user_id = ?";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
if (!$stmt) {
|
||||||
|
$response['message'] = 'Database error.';
|
||||||
|
echo json_encode($response);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param("si", $target_file, $user_id);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$_SESSION['profile_pic'] = $target_file;
|
||||||
|
$response['status'] = 'success';
|
||||||
|
$response['message'] = 'Profile picture updated successfully';
|
||||||
|
|
||||||
|
// Log the action
|
||||||
|
auditLog($user_id, 'PROFILE_PIC_UPLOAD', 'users', $user_id, ['filename' => $randomFilename]);
|
||||||
|
} else {
|
||||||
|
$response['message'] = 'Failed to update profile picture in the database';
|
||||||
|
}
|
||||||
|
$stmt->close();
|
||||||
|
} else {
|
||||||
|
$response['message'] = 'Failed to move uploaded file.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$response['message'] = 'No file uploaded or file error.';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
?>
|
||||||
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once("env.php");
|
$rootPath = dirname(__FILE__);
|
||||||
require_once("session.php");
|
require_once($rootPath . "/src/config/env.php");
|
||||||
require_once("connection.php");
|
require_once($rootPath . "/src/config/session.php");
|
||||||
require_once("functions.php");
|
require_once($rootPath . "/src/config/connection.php");
|
||||||
require_once 'google-client/vendor/autoload.php'; // Add this line for Google Client
|
require_once($rootPath . "/src/config/functions.php");
|
||||||
|
require_once($rootPath . '/google-client/vendor/autoload.php'); // Add this line for Google Client
|
||||||
|
|
||||||
// Check if connection is established
|
// Check if connection is established
|
||||||
if (!$conn) {
|
if (!$conn) {
|
||||||
|
|||||||
Reference in New Issue
Block a user