1 Commits

Author SHA1 Message Date
7d078cb954 WIP: Blogposts 2025-12-02 17:41:24 +02:00
331 changed files with 19290 additions and 47019 deletions

5
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.env .env
/vendor/ /vendor/
.htaccess .htaccess
/assets/uploads/gallery/ /uploads/
/assets/uploads/
/uploads/pop/

165
.htaccess
View File

@@ -1,168 +1,3 @@
# URL Rewrite Rules - Maps old URLs to new directory structure during migration
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Don't rewrite existing files or directories
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# === STRIP .PHP EXTENSION ===
# Redirect /page.php to /page (301 permanent redirect)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)\.php$ /$1 [R=301,L]
# Internally rewrite /page to /page.php if page.php exists
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.+)$ $1.php [L]
# === AUTH PAGES ===
RewriteRule ^login$ src/pages/auth/login.php [L]
RewriteRule ^register$ src/pages/auth/register.php [L]
RewriteRule ^forgot_password$ src/pages/auth/forgot_password.php [L]
RewriteRule ^reset_password$ src/pages/auth/reset_password.php [L]
RewriteRule ^verify$ src/pages/auth/verify.php [L]
RewriteRule ^resend_verification$ src/pages/auth/resend_verification.php [L]
RewriteRule ^change_password$ src/pages/auth/change_password.php [L]
RewriteRule ^update_password$ src/pages/auth/update_password.php [L]
# === MEMBERSHIP PAGES ===
RewriteRule ^membership$ src/pages/memberships/membership.php [L]
RewriteRule ^membership_details$ src/pages/memberships/membership_details.php [L]
RewriteRule ^membership_application$ src/pages/memberships/membership_application.php [L]
RewriteRule ^membership_payment$ src/pages/memberships/membership_payment.php [L]
RewriteRule ^renew_membership$ src/pages/memberships/renew_membership.php [L]
RewriteRule ^member_info$ src/pages/memberships/member_info.php [L]
# === BOOKING PAGES ===
RewriteRule ^bookings$ src/pages/bookings/bookings.php [L]
RewriteRule ^campsites$ src/pages/bookings/campsites.php [L]
RewriteRule ^campsite_booking$ src/pages/bookings/campsite_booking.php [L]
RewriteRule ^add_campsite$ src/pages/add_campsite.php [L]
RewriteRule ^trips$ src/pages/bookings/trips.php [L]
RewriteRule ^trip-details$ src/pages/bookings/trip-details.php [L]
RewriteRule ^course_details$ src/pages/bookings/course_details.php [L]
RewriteRule ^driver_training$ src/pages/bookings/driver_training.php [L]
# === SHOP PAGES ===
RewriteRule ^view_cart$ src/pages/shop/view_cart.php [L]
RewriteRule ^add_to_cart$ src/pages/shop/add_to_cart.php [L]
RewriteRule ^bar_tabs$ src/pages/shop/bar_tabs.php [L]
RewriteRule ^payment_confirmation$ src/pages/shop/payment_confirmation.php [L]
RewriteRule ^confirm$ src/pages/shop/confirm.php [L]
RewriteRule ^confirm2$ src/pages/shop/confirm2.php [L]
# === GALLERY PAGES ===
RewriteRule ^gallery$ src/pages/gallery/gallery.php [L]
RewriteRule ^create_album$ src/pages/gallery/create_album.php [L]
RewriteRule ^edit_album$ src/pages/gallery/create_album.php [L]
RewriteRule ^view_album$ src/pages/gallery/view_album.php [L]
# === EVENTS & BLOG PAGES ===
RewriteRule ^events$ src/pages/events/events.php [L]
RewriteRule ^blog$ src/pages/blog/blog.php [L]
RewriteRule ^blog_details$ src/pages/blog/blog_details.php [L]
RewriteRule ^best_of_the_eastern_cape_2024$ src/pages/events/best_of_the_eastern_cape_2024.php [L]
RewriteRule ^2025_agm_minutes$ src/pages/events/2025_agm_minutes.php [L]
RewriteRule ^agm_content$ src/pages/events/agm_content.php [L]
RewriteRule ^instapage$ src/pages/events/instapage.php [L]
# === OTHER PAGES ===
RewriteRule ^about$ src/pages/other/about.php [L]
RewriteRule ^contact$ src/pages/other/contact.php [L]
RewriteRule ^privacy_policy$ src/pages/other/privacy_policy.php [L]
RewriteRule ^track-map$ src/pages/track-map.php [L]
RewriteRule ^404$ src/pages/other/404.php [L]
RewriteRule ^account_settings$ src/pages/other/account_settings.php [L]
RewriteRule ^rescue_recovery$ src/pages/other/rescue_recovery.php [L]
RewriteRule ^bush_mechanics$ src/pages/other/bush_mechanics.php [L]
RewriteRule ^indemnity$ src/pages/other/indemnity.php [L]
RewriteRule ^indemnity_waiver$ src/pages/other/indemnity_waiver.php [L]
RewriteRule ^basic_indemnity$ src/pages/other/basic_indemnity.php [L]
RewriteRule ^view_indemnity$ src/pages/other/view_indemnity.php [L]
# === PAYMENT RETURN PAGES ===
RewriteRule ^success$ src/pages/payment/success.php [L]
RewriteRule ^failure$ src/pages/payment/failure.php [L]
RewriteRule ^cancel$ src/pages/payment/cancel.php [L]
# === ADMIN PAGES ===
RewriteRule ^admin_trips_events_courses$ src/admin/admin_trips_events_courses.php [L]
RewriteRule ^admin_bookings$ src/admin/admin_bookings.php [L]
RewriteRule ^admin_members$ src/admin/admin_members.php [L]
RewriteRule ^admin_payments$ src/admin/admin_payments.php [L]
RewriteRule ^admin_web_users$ src/admin/admin_web_users.php [L]
RewriteRule ^admin_events$ src/admin/admin_events.php [L]
RewriteRule ^admin_course_bookings$ src/admin/admin_course_bookings.php [L]
RewriteRule ^admin_camp_bookings$ src/admin/admin_camp_bookings.php [L]
RewriteRule ^admin_trip_bookings$ src/admin/admin_trip_bookings.php [L]
RewriteRule ^admin_visitors$ src/admin/admin_visitors.php [L]
RewriteRule ^admin_transactions$ src/admin/admin_transactions.php [L]
RewriteRule ^admin_trips$ src/admin/admin_trips.php [L]
RewriteRule ^manage_events$ src/admin/manage_events.php [L]
RewriteRule ^manage_trips$ src/admin/manage_trips.php [L]
RewriteRule ^admin_courses$ /src/admin/admin_courses.php [L,QSA]
RewriteRule ^manage_courses$ /src/admin/manage_courses.php [L,QSA]
# === API/AJAX ENDPOINTS ===
RewriteRule ^fetch_users$ src/api/fetch_users.php [L]
RewriteRule ^fetch_drinks$ src/api/fetch_drinks.php [L]
RewriteRule ^fetch_bar_tabs$ src/api/fetch_bar_tabs.php [L]
RewriteRule ^get_campsites$ src/api/get_campsites.php [L]
RewriteRule ^get_tab_total$ src/api/get_tab_total.php [L]
RewriteRule ^google_validate_login$ src/api/google_validate_login.php [L]
# === PROCESSORS ===
RewriteRule ^process_course$ /src/processors/process_course.php [L,QSA]
RewriteRule ^delete_course$ /src/processors/delete_course.php [L,QSA]
RewriteRule ^validate_login$ src/processors/validate_login.php [L]
RewriteRule ^register_user$ src/processors/register_user.php [L]
RewriteRule ^process_application$ src/processors/process_application.php [L]
RewriteRule ^process_booking$ src/processors/process_booking.php [L]
RewriteRule ^process_camp_booking$ src/processors/process_camp_booking.php [L]
RewriteRule ^process_course_booking$ src/processors/process_course_booking.php [L]
RewriteRule ^process_trip_booking$ src/processors/process_trip_booking.php [L]
RewriteRule ^process_membership_payment$ src/processors/process_membership_payment.php [L]
RewriteRule ^process_payments$ src/processors/process_payments.php [L]
RewriteRule ^process_eft$ src/processors/process_eft.php [L]
RewriteRule ^submit_order$ src/processors/submit_order.php [L]
RewriteRule ^submit_pop$ src/processors/submit_pop.php [L]
RewriteRule ^process_signature$ src/processors/process_signature.php [L]
RewriteRule ^create_bar_tab$ src/processors/create_bar_tab.php [L]
RewriteRule ^update_application$ src/processors/update_application.php [L]
RewriteRule ^update_user$ src/processors/update_user.php [L]
RewriteRule ^upload_profile_picture$ src/processors/upload_profile_picture.php [L]
RewriteRule ^send_reset_link$ src/processors/send_reset_link.php [L]
RewriteRule ^logout$ src/processors/logout.php [L]
RewriteRule ^process_trip$ src/processors/process_trip.php [L]
RewriteRule ^process_event$ src/processors/process_event.php [L]
RewriteRule ^toggle_trip_published$ src/processors/toggle_trip_published.php [L]
RewriteRule ^toggle_event_published$ src/processors/toggle_event_published.php [L]
RewriteRule ^delete_trip$ src/processors/delete_trip.php [L]
RewriteRule ^delete_event$ src/processors/delete_event.php [L]
RewriteRule ^save_album$ src/processors/save_album.php [L]
RewriteRule ^update_album$ src/processors/update_album.php [L]
RewriteRule ^delete_album$ src/processors/delete_album.php [L]
RewriteRule ^delete_photo$ src/processors/delete_photo.php [L]
RewriteRule ^get_album_photos$ src/processors/get_album_photos.php [L]
RewriteRule ^link_membership_user$ src/processors/link_membership_user.php [L]
RewriteRule ^unlink_membership_user$ src/processors/unlink_membership_user.php [L]
# Blog routes
RewriteRule ^admin_blogs$ src/admin/admin_blogs.php [L]
RewriteRule ^user_blogs$ src/pages/blog/user_blogs.php [L]
RewriteRule ^blog_read$ src/pages/blog/blog_read.php [L]
RewriteRule ^blog_edit$ src/pages/blog/blog_edit.php [L]
RewriteRule ^blog_create$ src/processors/blog/blog_create.php [L]
RewriteRule ^blog_delete$ src/processors/blog/blog_delete.php [L]
RewriteRule ^publish_blog$ src/processors/blog/publish_blog.php [L]
RewriteRule ^blog_unpublish$ src/processors/blog/blog_unpublish.php [L]
RewriteRule ^submit_blog$ src/processors/blog/submit_blog.php [L]
RewriteRule ^upload_blog_image$ src/processors/blog/upload_blog_image.php [L]
RewriteRule ^autosave$ src/processors/blog/autosave.php [L]
</IfModule>
php_flag display_errors On php_flag display_errors On
# php_value error_reporting -1 # php_value error_reporting -1
RedirectMatch 403 ^/\.well-known RedirectMatch 403 ^/\.well-known

View File

@@ -1,4 +0,0 @@
; memory_limit = 512M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120

View File

@@ -1,7 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
$page_id = 'agm_minutes'; $page_id = 'agm_minutes';
?> ?>
@@ -68,15 +65,28 @@ $page_id = 'agm_minutes';
float: right; float: right;
} }
.clearfix {
clear: both;
}
</style> </style>
<?php <section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('assets/images/blog/2/agm.jpg');">
$pageTitle = '2025 AGM Minutes'; <div class="banner-overlay"></div>
$breadcrumbs = [['Home' => 'index.php']]; <div class="container">
require_once($rootPath . '/components/banner.php'); <div class="banner-inner text-white">
?> <h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">2025 AGM Minutes & Chairman's Report</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">2025 AGM Minutes & Chairman's Report</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<!-- Blog Detaisl Area start --> <!-- Blog Detaisl Area start -->
<section class="blog-detaisl-page py-100 rel z-1"> <section class="blog-detaisl-page py-100 rel z-1">
@@ -102,7 +112,7 @@ $page_id = 'agm_minutes';
<div class="item" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50"> <div class="item" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<h6>Tags </h6> <h6>Tags </h6>
<div class="tag-coulds"> <div class="tag-coulds">
<a href="blog">Reports</a> <a href="blog.php">Reports</a>
</div> </div>
</div> </div>
@@ -255,4 +265,4 @@ $page_id = 'agm_minutes';
<!-- Blog Detaisl Area end --> <!-- Blog Detaisl Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,6 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
?> ?>
@@ -43,4 +41,4 @@ include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
</section> </section>
<!-- 404 Error Area end --> <!-- 404 Error Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,8 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
// Determine the correct path to header.php based on file location
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?> ?>
<style> <style>
@@ -41,11 +37,31 @@ include_once($rootPath . '/header.php');
} }
</style> </style>
<!-- Page Banner Start -->
<?php <?php
$pageTitle = 'About'; $bannerFolder = 'assets/images/banners/';
$breadcrumbs = [['Home' => 'index.php']]; $bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
require_once($rootPath . '/components/banner.php');
$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; ?>');">
<!-- Overlay PNG -->
<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">About</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">About</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Benefit Area start --> <!-- Benefit Area start -->
<section class="benefit-area mt-100 rel z-1"> <section class="benefit-area mt-100 rel z-1">
<div class="container"> <div class="container">
@@ -103,12 +119,8 @@ require_once($rootPath . '/components/banner.php');
</div> </div>
<div class="gallery-slider-active"> <div class="gallery-slider-active">
<?php <?php
$folder = $rootPath . '/assets/images/opendays/'; $folder = 'assets/images/opendays/';
$images = glob($folder . '*.{jpg,jpeg,png,gif}', GLOB_BRACE); $images = glob($folder . '*.{jpg,jpeg,png,gif}', GLOB_BRACE);
// Convert absolute paths to web-relative paths
$images = array_map(function($path) use ($rootPath) {
return str_replace($rootPath, '', $path);
}, $images);
// Shuffle and pick first 5 // Shuffle and pick first 5
shuffle($images); shuffle($images);
@@ -198,12 +210,8 @@ require_once($rootPath . '/components/banner.php');
</div> </div>
</div> </div>
<div class="gallery-slider-active"><?php <div class="gallery-slider-active"><?php
$folder = $rootPath . '/assets/images/memories/'; $folder = 'assets/images/memories/';
$images = glob($folder . '*.{jpg,jpeg,png,gif}', GLOB_BRACE); $images = glob($folder . '*.{jpg,jpeg,png,gif}', GLOB_BRACE);
// Convert absolute paths to web-relative paths
$images = array_map(function($path) use ($rootPath) {
return str_replace($rootPath, '', $path);
}, $images);
// Shuffle and pick first 5 // Shuffle and pick first 5
shuffle($images); shuffle($images);
@@ -238,7 +246,7 @@ require_once($rootPath . '/components/banner.php');
<div class="cta-item" style="background-image: url(assets/images/trips/1_01.jpg);"> <div class="cta-item" style="background-image: url(assets/images/trips/1_01.jpg);">
<span class="category">Extended Trips</span> <span class="category">Extended Trips</span>
<h2>Come and Explore Africa and beyond</h2> <h2>Come and Explore Africa and beyond</h2>
<a href="<?= url('trips') ?>" class="theme-btn style-two bgc-secondary"> <a href="trips.php" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Tours">Explore Trips</span> <span data-hover="Explore Tours">Explore Trips</span>
<i class="fal fa-arrow-right"></i> <i class="fal fa-arrow-right"></i>
</a> </a>
@@ -248,7 +256,7 @@ require_once($rootPath . '/components/banner.php');
<div class="cta-item" style="background-image: url(assets/images/courses/driver_training.png);"> <div class="cta-item" style="background-image: url(assets/images/courses/driver_training.png);">
<span class="category">Driver Training</span> <span class="category">Driver Training</span>
<h2>Level up your 4x4 Driving Skills</h2> <h2>Level up your 4x4 Driving Skills</h2>
<a href="<?= url('driver_training') ?>" class="theme-btn style-two"> <a href="driver_training.php" class="theme-btn style-two">
<span data-hover="Explore Tours">Explore Training</span> <span data-hover="Explore Tours">Explore Training</span>
<i class="fal fa-arrow-right"></i> <i class="fal fa-arrow-right"></i>
</a> </a>
@@ -258,7 +266,7 @@ require_once($rootPath . '/components/banner.php');
<div class="cta-item" style="background-image: url(assets/images/base4/camping.jpg);"> <div class="cta-item" style="background-image: url(assets/images/base4/camping.jpg);">
<span class="category">Events</span> <span class="category">Events</span>
<h2>See whats cooking at BASE4!</h2> <h2>See whats cooking at BASE4!</h2>
<a href="<?= url('events') ?>" class="theme-btn style-two bgc-secondary"> <a href="events.php" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Tours">Explore Events</span> <span data-hover="Explore Tours">Explore Events</span>
<i class="fal fa-arrow-right"></i> <i class="fal fa-arrow-right"></i>
</a> </a>
@@ -281,4 +289,4 @@ require_once($rootPath . '/components/banner.php');
<!-- Blog Area end --> <!-- Blog Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,6 +1,5 @@
<?php <?php
$headerStyle = 'light'; include_once('header02.php');
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
// Assuming you have the user ID stored in the session // Assuming you have the user ID stored in the session
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
@@ -60,7 +59,7 @@ $user = $result->fetch_assoc();
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-lg-12"> <div class="col-lg-12">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55"> <div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<form id="accountForm" name="accountForm" method="post" action="update_user"> <form id="accountForm" name="accountForm" method="post" action="update_user.php">
<div class="section-title py-20"> <div class="section-title py-20">
<h2>Account Settings</h2> <h2>Account Settings</h2>
<div id="responseMessage"></div> <!-- Message display area --> <div id="responseMessage"></div> <!-- Message display area -->
@@ -102,7 +101,6 @@ $user = $result->fetch_assoc();
<input type="email" id="email" name="email" class="form-control" value="<?php echo $user['email']; ?>" required> <input type="email" id="email" name="email" class="form-control" value="<?php echo $user['email']; ?>" required>
</div> </div>
</div> </div>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group mb-0"> <div class="form-group mb-0">
<button type="submit" class="theme-btn style-two" style="width:100%;">Update Info</button> <button type="submit" class="theme-btn style-two" style="width:100%;">Update Info</button>
@@ -114,8 +112,7 @@ $user = $result->fetch_assoc();
<!-- Change Password Form --> <!-- Change Password Form -->
<form id="changePasswordForm" name="changePasswordForm" action="change_password" method="post"> <form id="changePasswordForm" name="changePasswordForm" action="change_password.php" method="post">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="col-md-12 mt-20"> <div class="col-md-12 mt-20">
<h4>Change Password</h4> <h4>Change Password</h4>
<div id="responseMessage2"></div> <!-- Message display area --> <div id="responseMessage2"></div> <!-- Message display area -->
@@ -163,27 +160,29 @@ $user = $result->fetch_assoc();
formData.append('profile_picture', $('#profile_picture')[0].files[0]); formData.append('profile_picture', $('#profile_picture')[0].files[0]);
$.ajax({ $.ajax({
url: 'upload_profile_picture', url: 'upload_profile_picture.php',
type: 'POST', type: 'POST',
data: formData, data: formData,
contentType: false, contentType: false,
processData: false, processData: false,
dataType: 'json',
success: function(response) { success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') { if (response.status === 'success') {
// Update the profile picture source with cache-busting query string
// Reload the current page
window.location.reload();
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>'); $('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
// Reload the current page after a short delay
setTimeout(function() {
window.location.reload();
}, 1500);
} else { } else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>'); $('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
} }
}, },
error: function(xhr, status, error) { error: function() {
console.log('AJAX Error:', status, error); $('#responseMessage').html('<div class="alert alert-danger">Error uploading profile picture.</div>');
console.log('Response Text:', xhr.responseText);
$('#responseMessage').html('<div class="alert alert-danger">Error uploading profile picture: ' + error + '</div>');
} }
}); });
}); });
@@ -194,7 +193,7 @@ $user = $result->fetch_assoc();
event.preventDefault(); // Prevent default form submission event.preventDefault(); // Prevent default form submission
$.ajax({ $.ajax({
url: 'update_user', url: 'update_user.php',
type: 'POST', type: 'POST',
data: $(this).serialize(), data: $(this).serialize(),
success: function(response) { success: function(response) {
@@ -221,7 +220,7 @@ $user = $result->fetch_assoc();
event.preventDefault(); // Prevent default form submission event.preventDefault(); // Prevent default form submission
$.ajax({ $.ajax({
url: 'change_password', url: 'change_password.php',
type: 'POST', type: 'POST',
data: $(this).serialize(), data: $(this).serialize(),
success: function(response) { success: function(response) {
@@ -244,4 +243,4 @@ $user = $result->fetch_assoc();
}); });
</script> </script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

55
add_campsite.php Normal file
View File

@@ -0,0 +1,55 @@
<?php include_once('connection.php');
include_once('functions.php');
require_once("env.php");
session_start();
$user_id = $_SESSION['user_id']; // assuming you're storing it like this
// campsites.php
$conn = openDatabaseConnection();
// Get text inputs
$name = $_POST['name'];
$desc = $_POST['description'];
$lat = $_POST['latitude'];
$lng = $_POST['longitude'];
$website = $_POST['website'];
$telephone = $_POST['telephone'];
// Handle file upload
$thumbnailPath = null;
if (isset($_FILES['thumbnail']) && $_FILES['thumbnail']['error'] == 0) {
$uploadDir = "assets/uploads/campsites/";
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$filename = time() . "_" . basename($_FILES["thumbnail"]["name"]);
$targetFile = $uploadDir . $filename;
if (move_uploaded_file($_FILES["thumbnail"]["tmp_name"], $targetFile)) {
$thumbnailPath = $targetFile;
}
}
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
if ($id > 0) {
// UPDATE
if ($thumbnailPath) {
$stmt = $conn->prepare("UPDATE campsites SET name=?, description=?, latitude=?, longitude=?, website=?, telephone=?, thumbnail=? WHERE id=?");
$stmt->bind_param("ssddsssi", $name, $desc, $lat, $lng, $website, $telephone, $thumbnailPath, $id);
} else {
$stmt = $conn->prepare("UPDATE campsites SET name=?, description=?, latitude=?, longitude=?, website=?, telephone=? WHERE id=?");
$stmt->bind_param("ssddssi", $name, $desc, $lat, $lng, $website, $telephone, $id);
}
} else {
// INSERT
$stmt = $conn->prepare("INSERT INTO campsites (name, description, latitude, longitude, website, telephone, thumbnail, user_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("ssddsssi", $name, $desc, $lat, $lng, $website, $telephone, $thumbnailPath, $user_id);
}
$stmt->execute();
header("Location: campsites.php");
?>

144
admin_blogs.php Normal file
View File

@@ -0,0 +1,144 @@
<?php include_once('header02.php');
checkAdmin();
checkUserSession();
$result = $conn->prepare("
SELECT
b.blog_id,
b.title,
b.description,
b.status,
b.date,
b.image,
CONCAT(u.first_name, ' ', u.last_name) AS author_name,
u.email AS author_email,
u.profile_pic
FROM blogs b
JOIN users u ON b.author = u.user_id
WHERE b.status != 'deleted'
ORDER BY b.date DESC
");
$result->execute();
$posts = $result->get_result();
?>
<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 */
}
</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="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<!-- Overlay PNG -->
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Admin Blogs</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">Admin Blogs</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<!-- Blog List Area start -->
<section class="blog-list-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<h2>My Posts</h2>
<?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'">&times;</span>
</div>
<?php unset($_SESSION['message']); ?>
<?php endif; ?>
<a href="blog_create.php">+ New Post</a>
<?php while ($post = $posts->fetch_assoc()):
// 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="image" style="width:200px;height:200px;">
<img src="' . $post["image"] . '" alt="' . $post["title"] . '">
</div>
<div class="content" style="width:100%;">
<div class="destination-header d-flex align-items-start gap-3">
<img src="' . $post["profile_pic"] . '" alt="Author" class="rounded-circle border" width="80" height="80">
<div>
<span class="badge bg-dark mb-1">' . strtoupper($post["status"]) . '</span>
<h5 class="mb-0">' . $post["title"] . '</h5>
<small class="text-muted">' . $post["author_name"] . '</small>
</div>
</div>
<p>' . $post["description"] . '</p>
<div class="destination-footer">
<div class="btn-group" style="display:flex; justify-content:flex-end; gap:10px; margin-top:10px;">
<a href="blog_edit.php?token='.encryptData($post["blog_id"], $salt).'" class="btn btn-sm" data-bs-toggle="tooltip" data-bs-placement="top" title="Edit"><i class="bi bi-pencil"></i></a>
<a href="blog_read.php?token='.encryptData($post["blog_id"], $salt).'" class="btn btn-sm" data-bs-toggle="tooltip" data-bs-placement="top" title="Preview"><i class="bi bi-eye"></i></a>
<a href="blog_delete.php?token='.encryptData($post["blog_id"], $salt).'" class="btn btn-sm" data-bs-toggle="tooltip" data-bs-placement="top" title="Delete"><i class="bi bi-trash"></i></a>
</div>
</div>
</div>
</div>
';
endwhile; ?>
</div>
</div>
</div>
</section>
<!-- Blog List Area end -->
<script>
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
</script>
<?php include_once("insta_footer.php"); ?>

224
admin_camp_bookings.php Normal file
View File

@@ -0,0 +1,224 @@
<?php include_once('header02.php');
checkAdmin();
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.trip-booking {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const tables = document.querySelectorAll("table");
tables.forEach((table) => {
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = table.previousElementSibling;
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
if (rows.length === 0) {
filterInput.style.display = "none";
} else {
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
}
});
});
</script>
<?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="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">4WDCSA Camping Bookings</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">Camping Bookings</li>
</ol>
</nav>
</div>
</div>
</section>
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<?php
echo "<div class='trip-booking' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>";
echo "<div style='padding:10px;'>";
echo "<h4>BASE4 Camping</h4>";
// Fetch bookings for the current trip
$bookingsSql = "SELECT b.user_id, b.from_date, b.to_date, b.num_vehicles, b.num_adults, b.num_children, b.add_firewood, b.status,
u.first_name, u.last_name,
(b.total_amount - b.discount_amount) AS paid
FROM bookings b
INNER JOIN users u ON b.user_id = u.user_id
WHERE b.booking_type = 'camping'";
$stmt = $conn->prepare($bookingsSql);
$stmt->execute();
$bookingsResult = $stmt->get_result();
if ($bookingsResult->num_rows > 0) {
echo '<input type="text" class="filter-input" placeholder="Filter results...">';
echo '<table>
<thead>
<tr>
<th>Name</th>
<th>From</th>
<th>To</th>
<th>Vehicles</th>
<th>Adults</th>
<th>Children</th>
<th>Add Firewood</th>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody>';
while ($booking = $bookingsResult->fetch_assoc()) {
$userName = htmlspecialchars($booking['first_name'] . ' ' . $booking['last_name']);
$numVehicles = htmlspecialchars($booking['num_vehicles']);
$from = htmlspecialchars($booking['from_date']);
$to = htmlspecialchars($booking['to_date']);
$numAdults = htmlspecialchars($booking['num_adults']);
$numChildren = htmlspecialchars($booking['num_children']);
$radio = $booking['add_firewood'] == 1 ? "YES" : "NO";
$status = htmlspecialchars($booking['status']);
$paid = "R " . number_format($booking['paid'], 2);
echo "<tr>
<td>{$userName}</td>
<td>{$from}</td>
<td>{$to}</td>
<td>{$numVehicles}</td>
<td>{$numAdults}</td>
<td>{$numChildren}</td>
<td>{$radio}</td>
<td>{$status}</td>
<td>{$paid}</td>
</tr>";
}
echo '</tbody></table>';
} else {
echo '<p>No bookings found for this trip.</p>';
}
echo "</div>";
echo "</div>";
?>
</div>
</section>
<?php include_once("insta_footer.php"); ?>

244
admin_course_bookings.php Normal file
View File

@@ -0,0 +1,244 @@
<?php include_once('header02.php');
checkAdmin();
// Fetch all trips
$courseSql = "SELECT date, course_id, course_type FROM courses WHERE DATE(date) >= CURDATE()";
$courseResult = $conn->query($courseSql);
if (!$courseResult) {
echo "Error in SQL query: " . $conn->error;
}
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.trip-booking {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const tables = document.querySelectorAll("table");
tables.forEach((table) => {
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = table.previousElementSibling;
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
if (rows.length === 0) {
filterInput.style.display = "none";
} else {
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
}
});
});
</script>
<?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="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">4WDCSA Course Bookings</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">Course Bookings</li>
</ol>
</nav>
</div>
</div>
</section>
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<?php
if ($courseResult->num_rows > 0) {
while ($course = $courseResult->fetch_assoc()) {
$course_id = $course['course_id'];
$date = $course['date'];
$type = htmlspecialchars($course['course_type']);
if ($type === "driver_training") {
$course_name = "Basic 4X4 Driver Training Course ".$date;
} elseif ($type === "bush_mechanics") {
$course_name = "Bush Mechanics Course ".$date;
} elseif ($type === "rescue_recovery") {
$course_name = "Rescue & Recovery Training Course ".$date;
} else {
$course_name = "General Course ".$date; // Default fallback description
}
echo "<div class='trip-booking' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>";
echo "<div style='padding:10px;'>";
echo "<h4>{$course_name}</h4>";
// Fetch bookings for the current trip
$bookingsSql = "SELECT b.user_id, b.num_adults, b.total_amount, b.status, b.course_non_members,
u.first_name, u.last_name, u.profile_pic
FROM bookings b
INNER JOIN users u ON b.user_id = u.user_id
WHERE b.course_id = ?";
if ($stmt = $conn->prepare($bookingsSql)) {
$stmt->bind_param('i', $course_id);
$stmt->execute();
$bookingsResult = $stmt->get_result();
} else {
echo "Error in prepared statement: " . $conn->error;
}
if ($bookingsResult->num_rows > 0) {
echo '<input type="text" class="filter-input" placeholder="Filter results...">';
echo '<table>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Members</th>
<th>Non-Members</th>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody>';
while ($booking = $bookingsResult->fetch_assoc()) {
$userName = htmlspecialchars($booking['first_name'] . ' ' . $booking['last_name']);
$members = htmlspecialchars($booking['num_adults']);
$non_members = htmlspecialchars($booking['course_non_members']);
$status = htmlspecialchars($booking['status']);
$paid = "R " . number_format($booking['total_amount'], 2);
echo "<tr>
<td><img src=".$booking['profile_pic']." alt='Profile Picture' class='profile-pic'></td>
<td>{$userName}</td>
<td>{$members}</td>
<td>{$non_members}</td>
<td>{$status}</td>
<td>{$paid}</td>
</tr>";
}
echo '</tbody></table>';
} else {
echo '<p>No bookings found for this trip.</p>';
}
echo "</div>";
echo "</div>";
}
} else {
echo '<p>No courses found.</p>';
}
?>
</div>
</section>
<?php include_once("insta_footer.php"); ?>

View File

@@ -1,7 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin(); checkAdmin();
?> ?>
@@ -224,4 +221,4 @@ if (!empty($bannerImages)) {
<!-- Tour List Area end --> <!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,7 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin(); checkAdmin();
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['accept_indemnity'])) { if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['accept_indemnity'])) {
@@ -14,10 +11,10 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['accept_indemnity']))
} }
} }
// SQL query to fetch membership applications // SQL query to fetch data
$stmt = $conn->prepare("SELECT user_id, first_name, last_name, tel_cell, email, dob, accept_indemnity FROM membership_application"); $sql = "SELECT user_id, first_name, last_name, tel_cell, email, dob, accept_indemnity FROM membership_application";
$stmt->execute();
$result = $stmt->get_result(); $result = $conn->query($sql);
?> ?>
<style> <style>
table { table {
@@ -235,4 +232,4 @@ if (!empty($bannerImages)) {
<!-- Tour List Area end --> <!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,7 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin(); checkAdmin();
?> ?>
@@ -208,4 +205,4 @@ if (!empty($bannerImages)) {
<!-- Tour List Area end --> <!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

237
admin_trip_bookings.php Normal file
View File

@@ -0,0 +1,237 @@
<?php include_once('header02.php');
checkAdmin();
// Fetch all trips
$tripsSql = "SELECT trip_id, trip_name FROM trips";
$tripsResult = $conn->query($tripsSql);
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.trip-booking {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const tables = document.querySelectorAll("table");
tables.forEach((table) => {
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = table.previousElementSibling;
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
if (rows.length === 0) {
filterInput.style.display = "none";
} else {
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
}
});
});
</script>
<?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="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">4WDCSA Trip Bookings</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">Trip Bookings</li>
</ol>
</nav>
</div>
</div>
</section>
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<?php
if ($tripsResult->num_rows > 0) {
while ($trip = $tripsResult->fetch_assoc()) {
$tripId = $trip['trip_id'];
$tripName = htmlspecialchars($trip['trip_name']);
echo "<div class='trip-booking' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>";
echo "<div style='padding:10px;'>";
echo "<h4>{$tripName}</h4>";
// Fetch bookings for the current trip
$bookingsSql = "SELECT b.user_id, b.num_vehicles, b.num_adults, b.num_children, b.num_pensioners, b.radio, b.status,
u.first_name, u.last_name,
(b.total_amount - b.discount_amount) AS paid
FROM bookings b
INNER JOIN users u ON b.user_id = u.user_id
WHERE b.trip_id = ?";
$stmt = $conn->prepare($bookingsSql);
$stmt->bind_param('i', $tripId);
$stmt->execute();
$bookingsResult = $stmt->get_result();
if ($bookingsResult->num_rows > 0) {
echo '<input type="text" class="filter-input" placeholder="Filter results...">';
echo '<table>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Vehicles</th>
<th>Adults</th>
<th>Children</th>
<th>Pensioners</th>
<th>Radio</th>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody>';
while ($booking = $bookingsResult->fetch_assoc()) {
$userName = htmlspecialchars($booking['first_name'] . ' ' . $booking['last_name']);
$numVehicles = htmlspecialchars($booking['num_vehicles']);
$numAdults = htmlspecialchars($booking['num_adults']);
$numPensioners = htmlspecialchars($booking['num_pensioners']);
$numChildren = htmlspecialchars($booking['num_children']);
$radio = $booking['radio'] == 1 ? "YES" : "NO";
$status = htmlspecialchars($booking['status']);
$paid = "R " . number_format($booking['paid'], 2);
echo "<tr>
<td><img src=".$booking['profile_pic']." alt='Profile Picture' class='profile-pic'></td>
<td>{$userName}</td>
<td>{$numVehicles}</td>
<td>{$numAdults}</td>
<td>{$numChildren}</td>
<td>{$numPensioners}</td>
<td>{$radio}</td>
<td>{$status}</td>
<td>{$paid}</td>
</tr>";
}
echo '</tbody></table>';
} else {
echo '<p>No bookings found for this trip.</p>';
}
echo "</div>";
echo "</div>";
}
} else {
echo '<p>No trips found.</p>';
}
?>
</div>
</section>
<?php include_once("insta_footer.php"); ?>

View File

@@ -1,7 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin(); checkAdmin();
// SQL query to fetch data // SQL query to fetch data
$sql = "SELECT ip_address, user_id, page_url, referrer_url, visit_time, country FROM visitor_logs WHERE NOT (ip_address = '185.203.122.69' OR ip_address = '156.155.29.213') ORDER BY visit_time DESC"; $sql = "SELECT ip_address, user_id, page_url, referrer_url, visit_time, country FROM visitor_logs WHERE NOT (ip_address = '185.203.122.69' OR ip_address = '156.155.29.213') ORDER BY visit_time DESC";
@@ -201,4 +198,4 @@ if (!empty($bannerImages)) {
<!-- Tour List Area end --> <!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,8 +1,5 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light'; checkSuperAdmin();
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
// SQL query to fetch data // SQL query to fetch data
$sql = "SELECT user_id, first_name, last_name, email, member, date_joined, token, is_verified, profile_pic FROM users"; $sql = "SELECT user_id, first_name, last_name, email, member, date_joined, token, is_verified, profile_pic FROM users";
$result = $conn->query($sql); $result = $conn->query($sql);
@@ -256,7 +253,7 @@ if (!empty($bannerImages)) {
const name = this.dataset.name; const name = this.dataset.name;
const token = this.dataset.token; const token = this.dataset.token;
fetch('resend_verification', { fetch('resend_verification.php', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@@ -281,4 +278,4 @@ if (!empty($bannerImages)) {
</script> </script>
<?php include_once($rootPath . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,73 +0,0 @@
.notif-avatar-container {
position: relative;
display: inline-block;
}
.notif-badge {
position: absolute;
top: -6px;
right: -6px;
background: #e74c3c;
color: #fff;
border-radius: 50%;
min-width: 20px;
height: 20px;
padding: 0 6px;
font-size: 12px;
display: none;
line-height: 20px;
text-align: center;
box-sizing: border-box;
font-weight: 600;
}
.notif-panel {
position: absolute;
right: 10px;
top: 44px;
width: 320px;
background: #fff;
border: 1px solid #ddd;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
z-index: 9999;
padding: 8px;
border-radius: 6px;
}
.notif-panel .notif-empty {
padding: 12px;
text-align: center;
color: #666;
}
.notif-item {
display: flex;
align-items: center;
padding: 8px;
border-bottom: 1px solid #f1f1f1;
}
.notif-item:last-child {
border-bottom: none;
}
.notif-item-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
object-fit: cover;
margin-right: 8px;
}
.notif-item-body {
flex: 1;
}
.notif-item-title {
font-weight: 600;
font-size: 13px;
}
.notif-item-meta {
font-size: 11px;
color: #888;
}
.notif-close {
background: transparent;
border: 0;
color: #999;
font-weight: 700;
padding: 6px;
cursor: pointer;
}

View File

@@ -1,6 +1,12 @@
@charset "UTF-8"; @charset "UTF-8";
/*---------------------------------------------------------------------- /*----------------------------------------------------------------------
4WDCSA.co.za CSS Stylesheet Template Name: Ravelo - Travel & Tour Booking HTML Template
Template URI: https://webtend.net/demo/html/ravelo/
Author: WebTend
Author URI: https://webtend.net/
Version: 1.0
Note: This is Main Style CSS File. */
/*---------------------------------------------------------------------- /*----------------------------------------------------------------------
CSS INDEX CSS INDEX
---------------------- ----------------------
@@ -7118,8 +7124,7 @@ blockquote {
/* Comments */ /* Comments */
.comments { .comments {
border-radius: 10px; border-radius: 10px;
/* border: 1px solid var(--border-color); */ border: 1px solid var(--border-color); }
}
.comment-body { .comment-body {
padding: 50px; } padding: 50px; }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 2.6 MiB

View File

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -1,66 +0,0 @@
/**
* TRACK MAP WITH LEAFLET.JS
*
* Basic Leaflet map test
*/
console.log('Track map script loaded2');
// Check if Leaflet is available
if (typeof L === 'undefined') {
console.error('Leaflet library not loaded!');
} else {
console.log('Leaflet library is available, version:', L.version);
}
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM loaded, initializing map...');
const mapElement = document.getElementById('map');
console.log('Map element:', mapElement);
if (!mapElement) {
console.error('Map element not found!');
return;
}
console.log('Map element dimensions:', mapElement.offsetWidth, 'x', mapElement.offsetHeight);
try {
// Image dimensions: 2876 x 2035 pixels
const imageWidth = 2876;
const imageHeight = 2035;
// Create map with simple CRS (pixel coordinates)
// Note: Leaflet uses [y, x] format, so bounds are [[0, 0], [height, width]]
const bounds = [[0, 0], [imageHeight, imageWidth]];
const map = L.map('map', {
crs: L.CRS.Simple,
minZoom: -2,
maxZoom: 2,
center: [imageHeight / 2, imageWidth / 2],
zoom: -1
});
console.log('Map object created with CRS.Simple:', map);
// Add aerial image overlay
const imageUrl = '/assets/images/track-aerial.jpg';
L.imageOverlay(imageUrl, bounds).addTo(map);
console.log('Aerial image overlay added');
// Add SVG overlay
const svgUrl = '/assets/images/track-route.svg';
L.imageOverlay(svgUrl, bounds, {
opacity: 0.8,
interactive: false
}).addTo(map);
console.log('SVG route overlay added');
// Fit map to image bounds
map.fitBounds(bounds);
console.log('Map initialized successfully');
} catch (error) {
console.error('Error initializing map:', error);
}
});

View File

@@ -1,103 +0,0 @@
/* notifications.js - small admin notification panel
Requires jQuery. */
(function($){
function timeAgo(ts){
var seconds = Math.floor((Date.now() - (new Date(ts)).getTime())/1000);
if (seconds < 60) return seconds + 's ago';
var minutes = Math.floor(seconds/60);
if (minutes < 60) return minutes + 'm ago';
var hours = Math.floor(minutes/60);
if (hours < 24) return hours + 'h ago';
var days = Math.floor(hours/24);
return days + 'd ago';
}
function renderNotifications(list){
var $panel = $('#notif-panel');
$panel.empty();
if (!list || list.length === 0) {
$panel.append('<div class="notif-empty">No notifications</div>');
return;
}
list.forEach(function(n){
var actorAvatar = (n.data && n.data.actor_avatar) ? n.data.actor_avatar : 'assets/images/icons/user.png';
// Prefer the notification payload title (n.data.title). Do NOT fall back to the event string.
var title = (n.data && n.data.title) ? n.data.title : 'Notification';
var time = n.time_created || new Date().toISOString();
var read = (n.read_by && Array.isArray(n.read_by) && n.read_by.length>0);
var $item = $('<div class="notif-item" data-id="'+n.id+'">');
$item.append('<img class="notif-item-avatar" src="'+actorAvatar+'" alt="avatar">');
var $body = $('<div class="notif-item-body">');
$body.append('<div class="notif-item-title">'+escapeHtml(title)+'</div>');
$body.append('<div class="notif-item-meta">'+timeAgo(time)+'</div>');
$item.append($body);
$item.append('<button class="notif-close" title="Mark read">×</button>');
$panel.append($item);
});
}
function escapeHtml(str) {
if (!str) return '';
return String(str).replace(/[&<>"'`]/g, function(s){ return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":"&#39;",'`':'&#96;'}[s]; });
}
function fetchAndRender(adminId){
$.getJSON('/src/api/notifications.php', { action: 'fetch' }, function(resp){
if (resp && resp.success) {
renderNotifications(resp.notifications);
if (resp.unread_count && resp.unread_count > 0) {
$('#notif-badge').text(resp.unread_count).show();
} else {
$('#notif-badge').hide();
}
}
});
}
// Fetch only unread count (used on page load so badge shows without opening panel)
function fetchUnreadCount(){
$.getJSON('/src/api/notifications.php', { action: 'fetch' }, function(resp){
if (resp && resp.success) {
if (resp.unread_count && resp.unread_count > 0) {
$('#notif-badge').text(resp.unread_count).show();
} else {
$('#notif-badge').hide();
}
}
});
}
$(function(){
var $container = $('.notif-avatar-container');
if (!$container.length) return;
var adminId = $container.data('admin-id');
// ensure badge is populated on page load
fetchUnreadCount();
$container.on('click', function(e){
e.preventDefault();
$('#notif-panel').toggle();
if ($('#notif-panel').is(':visible')) fetchAndRender(adminId);
});
$(document).on('click', '.notif-close', function(e){
e.stopPropagation();
var $it = $(this).closest('.notif-item');
var id = $it.data('id');
if (!id) return;
$.post('/src/api/notifications.php', { action: 'mark_read', id: id }, function(resp){
if (resp && resp.success) {
$it.remove();
// refresh count
fetchAndRender(adminId);
}
}, 'json');
});
// click outside to close
$(document).on('click', function(e){
if (!$(e.target).closest('#notif-panel, .notif-avatar-container').length) {
$('#notif-panel').hide();
}
});
});
})(jQuery);

View File

@@ -46,7 +46,7 @@
<div class="header-inner rel d-flex align-items-center"> <div class="header-inner rel d-flex align-items-center">
<div class="logo-outer"> <div class="logo-outer">
<div class="logo"><a href="index"><img src="assets/images/logos/logo-two.png" alt="Logo" title="Logo"></a></div> <div class="logo"><a href="index.php"><img src="assets/images/logos/logo-two.png" alt="Logo" title="Logo"></a></div>
</div> </div>
<div class="nav-outer mx-lg-auto ps-xxl-5 clearfix"> <div class="nav-outer mx-lg-auto ps-xxl-5 clearfix">
@@ -71,7 +71,7 @@
<ul class="navigation clearfix"> <ul class="navigation clearfix">
<li class="dropdown current"><a href="#">Home</a> <li class="dropdown current"><a href="#">Home</a>
<ul> <ul>
<li><a href="index">Travel Agency</a></li> <li><a href="index.php">Travel Agency</a></li>
<li><a href="index2.html">City Tou</a></li> <li><a href="index2.html">City Tou</a></li>
<li><a href="index3.html">Tour Package</a></li> <li><a href="index3.html">Tour Package</a></li>
</ul> </ul>
@@ -161,7 +161,7 @@
<!--Appointment Form--> <!--Appointment Form-->
<div class="appointment-form"> <div class="appointment-form">
<form method="post" action="contact"> <form method="post" action="contact.php">
<div class="form-group"> <div class="form-group">
<input type="text" name="text" value="" placeholder="Name" required> <input type="text" name="text" value="" placeholder="Name" required>
</div> </div>
@@ -182,9 +182,9 @@
<!--Social Icons--> <!--Social Icons-->
<div class="social-style-one"> <div class="social-style-one">
<a href="contact"><i class="fab fa-twitter"></i></a> <a href="contact.php"><i class="fab fa-twitter"></i></a>
<a href="contact"><i class="fab fa-facebook-f"></i></a> <a href="contact.php"><i class="fab fa-facebook-f"></i></a>
<a href="contact"><i class="fab fa-instagram"></i></a> <a href="contact.php"><i class="fab fa-instagram"></i></a>
<a href="#"><i class="fab fa-pinterest-p"></i></a> <a href="#"><i class="fab fa-pinterest-p"></i></a>
</div> </div>
</div> </div>
@@ -201,7 +201,7 @@
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Bali, Indonesia</h2> <h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Bali, Indonesia</h2>
<nav aria-label="breadcrumb"> <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"> <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"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">Tour Details</li> <li class="breadcrumb-item active">Tour Details</li>
</ol> </ol>
</nav> </nav>
@@ -795,7 +795,7 @@
<i class="fal fa-arrow-right"></i> <i class="fal fa-arrow-right"></i>
</button> </button>
<div class="text-center"> <div class="text-center">
<a href="contact">Need some help?</a> <a href="contact.php">Need some help?</a>
</div> </div>
</form> </form>
</div> </div>
@@ -871,7 +871,7 @@
<div class="col col-small" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50"> <div class="col col-small" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="footer-widget footer-text"> <div class="footer-widget footer-text">
<div class="footer-logo mb-40"> <div class="footer-logo mb-40">
<a href="index"><img src="assets/images/logos/logo.png" alt="Logo"></a> <a href="index.php"><img src="assets/images/logos/logo.png" alt="Logo"></a>
</div> </div>
<div class="footer-map"> <div class="footer-map">
<iframe src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d96777.16150026117!2d-74.00840582560909!3d40.71171357405996!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2sbd!4v1706508986625!5m2!1sen!2sbd" style="border:0; width: 100%;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe> <iframe src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d96777.16150026117!2d-74.00840582560909!3d40.71171357405996!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2sbd!4v1706508986625!5m2!1sen!2sbd" style="border:0; width: 100%;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
@@ -899,7 +899,7 @@
<ul class="list-style-three"> <ul class="list-style-three">
<li><a href="about.html">About Company</a></li> <li><a href="about.html">About Company</a></li>
<li><a href="blog.html">Community Blog</a></li> <li><a href="blog.html">Community Blog</a></li>
<li><a href="contact">Jobs and Careers</a></li> <li><a href="contact.php">Jobs and Careers</a></li>
<li><a href="blog.html">latest News Blog</a></li> <li><a href="blog.html">latest News Blog</a></li>
</ul> </ul>
</div> </div>
@@ -937,7 +937,7 @@
<div class="row"> <div class="row">
<div class="col-lg-5"> <div class="col-lg-5">
<div class="copyright-text text-center text-lg-start"> <div class="copyright-text text-center text-lg-start">
<p>@Copy 2024 <a href="index">Ravelo</a>, All rights reserved</p> <p>@Copy 2024 <a href="index.php">Ravelo</a>, All rights reserved</p>
</div> </div>
</div> </div>
<div class="col-lg-7 text-center text-lg-end"> <div class="col-lg-7 text-center text-lg-end">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

101
autosave.php Normal file
View File

@@ -0,0 +1,101 @@
<?php
require_once("env.php");
require_once("session.php");
require_once("connection.php");
require_once("functions.php");
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
echo "Not authorized";
exit;
}
$article_id = (int)($_POST['id'] ?? 0);
$title = $_POST['title'] ?? '';
$content = $_POST['content'] ?? '';
$description = $_POST['subtitle'] ?? '';
$category = $_POST['category'] ?? '';
$user_id = $_SESSION['user_id'];
// Default to current user
$author_id = $_SESSION['user_id'];
// Allow override if admin
$role = getUserRole();
if (($role === 'admin' || $role === 'superadmin') && isset($_POST['author'])) {
$author_id = (int)$_POST['author'];
}
echo $author_id;
$cover_image_path = null;
// Only attempt upload if a file was submitted
if (!empty($_FILES['cover_image']['name'])) {
$uploadDir = __DIR__ . "/uploads/blogs/".$article_id."/images/";
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
// Sanitize and rename file
$originalName = basename($_FILES['cover_image']['name']);
$originalName = preg_replace("/[^a-zA-Z0-9\._-]/", "_", $originalName); // remove unsafe characters
$targetPath = $uploadDir . $originalName;
$publicPath = "/uploads/blogs/".$article_id."/images/" . $originalName;
// Error detection before upload
$fileError = $_FILES['cover_image']['error'];
if ($fileError !== UPLOAD_ERR_OK) {
$errorMessages = [
UPLOAD_ERR_INI_SIZE => 'The uploaded file exceeds the upload_max_filesize directive in php.ini.',
UPLOAD_ERR_FORM_SIZE => 'The uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form.',
UPLOAD_ERR_PARTIAL => 'The uploaded file was only partially uploaded.',
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder.',
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk.',
UPLOAD_ERR_EXTENSION => 'A PHP extension stopped the upload.',
];
$errorMessage = $errorMessages[$fileError] ?? 'Unknown upload error.';
http_response_code(500);
echo "Upload error: $errorMessage";
exit;
}
// Skip upload if identical file already exists
if (file_exists($targetPath)) {
$cover_image_path = $publicPath;
} else {
if (move_uploaded_file($_FILES['cover_image']['tmp_name'], $targetPath)) {
$cover_image_path = $publicPath;
} else {
http_response_code(500);
echo "Failed to move uploaded file.";
exit;
}
}
}
// Prepare SQL with/without image update
if ($cover_image_path) {
$stmt = $conn->prepare("
UPDATE blogs
SET title = ?, content = ?, description = ?, category = ?, image = ?, author = ?
WHERE blog_id = ?
");
$stmt->bind_param("ssssssi", $title, $content, $description, $category, $cover_image_path, $author_id, $article_id);
} else {
$stmt = $conn->prepare("
UPDATE blogs
SET title = ?, content = ?, description = ?, category = ?, author = ?
WHERE blog_id = ?
");
$stmt->bind_param("ssssii", $title, $content, $description, $category, $author_id, $article_id);
}
if ($stmt->execute()) {
echo "Saved";
} else {
http_response_code(500);
echo "Database update failed: " . $stmt->error;
}

View File

@@ -1,6 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
checkUserSession(); checkUserSession();
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
unset($_SESSION['cart']); unset($_SESSION['cart']);
@@ -157,7 +155,6 @@ unset($_SESSION['cart']);
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form id="barTabForm"> <form id="barTabForm">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="form-group"> <div class="form-group">
<label for="userSelect">Select User</label> <label for="userSelect">Select User</label>
<input type="text" id="userSelect" class="form-control" placeholder="Search User" required> <input type="text" id="userSelect" class="form-control" placeholder="Search User" required>
@@ -176,7 +173,7 @@ unset($_SESSION['cart']);
$('#userSelect').autocomplete({ $('#userSelect').autocomplete({
source: function(request, response) { source: function(request, response) {
$.ajax({ $.ajax({
url: 'fetch_users', url: 'fetch_users.php',
method: 'GET', method: 'GET',
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
@@ -215,7 +212,7 @@ unset($_SESSION['cart']);
$('#barTabForm').submit(function(e) { $('#barTabForm').submit(function(e) {
e.preventDefault(); // Prevent default form submission e.preventDefault(); // Prevent default form submission
$.ajax({ $.ajax({
url: 'create_bar_tab', url: 'create_bar_tab.php',
method: 'POST', method: 'POST',
data: $(this).serialize(), // Send form data, including user_id data: $(this).serialize(), // Send form data, including user_id
dataType: 'json', dataType: 'json',
@@ -239,7 +236,7 @@ unset($_SESSION['cart']);
// Fetch and render bar tabs // Fetch and render bar tabs
function loadBarTabs() { function loadBarTabs() {
$.ajax({ $.ajax({
url: 'fetch_bar_tabs', url: 'fetch_bar_tabs.php',
method: 'GET', method: 'GET',
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
@@ -289,7 +286,7 @@ unset($_SESSION['cart']);
// Fetch available drinks for the selected tab // Fetch available drinks for the selected tab
$.ajax({ $.ajax({
url: 'fetch_drinks', url: 'fetch_drinks.php',
method: 'GET', method: 'GET',
data: { data: {
tab_id: tabId tab_id: tabId
@@ -346,7 +343,7 @@ unset($_SESSION['cart']);
// Add the drink to the cart (session) // Add the drink to the cart (session)
$.ajax({ $.ajax({
url: 'add_to_cart', url: 'add_to_cart.php',
method: 'POST', method: 'POST',
data: { data: {
tab_id: tabId, tab_id: tabId,
@@ -414,7 +411,7 @@ unset($_SESSION['cart']);
// Submit the order // Submit the order
$.ajax({ $.ajax({
url: 'submit_order', url: 'submit_order.php',
method: 'POST', method: 'POST',
data: { data: {
tab_id: tabId tab_id: tabId
@@ -455,7 +452,7 @@ unset($_SESSION['cart']);
function fetchTabTotal(tabId) { function fetchTabTotal(tabId) {
console.log("fetching tab total...") console.log("fetching tab total...")
$.ajax({ $.ajax({
url: 'get_tab_total', url: 'get_tab_total.php',
method: 'POST', method: 'POST',
data: { data: {
tab_id: tabId tab_id: tabId
@@ -482,4 +479,4 @@ unset($_SESSION['cart']);
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,7 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
// Assuming you have the user ID stored in the session // Assuming you have the user ID stored in the session
if (isset($_SESSION['user_id'])) { if (isset($_SESSION['user_id'])) {
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
@@ -39,11 +36,33 @@ if (isset($_SESSION['user_id'])) {
} }
</style> </style>
<!-- Page Banner Start -->
<?php <?php
$pageTitle = 'Indemnity'; $bannerFolder = 'assets/images/banners/';
$breadcrumbs = [['Home' => 'index.php']]; $bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
require_once($rootPath . '/components/banner.php');
$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">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Indemnity</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 ">Membership</li>
<li class="breadcrumb-item ">Application</li>
<li class="breadcrumb-item active">Indemnity</li>
<li class="breadcrumb-item ">Payment</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End --> <!-- Page Banner End -->
@@ -94,7 +113,7 @@ if (isset($_SESSION['user_id'])) {
var dataUrl = signaturePad.toDataURL(); // Get signature as base64 image var dataUrl = signaturePad.toDataURL(); // Get signature as base64 image
$.ajax({ $.ajax({
url: 'process_signature', url: 'process_signature.php',
type: 'POST', type: 'POST',
data: { data: {
signature: dataUrl // Send the base64 signature image signature: dataUrl // Send the base64 signature image
@@ -130,4 +149,4 @@ if (isset($_SESSION['user_id'])) {
</script> </script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?> <?php include_once('insta_footer.php') ?>

View File

@@ -1,7 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
$page_id = 'best_0f_ec'; $page_id = 'best_0f_ec';
?> ?>
@@ -68,15 +65,31 @@ $page_id = 'best_0f_ec';
float: right; float: right;
} }
.clearfix {
clear: both;
}
</style> </style>
<?php
$pageTitle = 'Best of the Eastern Cape 2024';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php'); <section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('assets/images/blog/1/cover.jpg');">
?> <div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Best of the Eastern Cape 2024</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">Best of the Eastern Cape 2024</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<!-- Blog Detaisl Area start --> <!-- Blog Detaisl Area start -->
<section class="blog-detaisl-page py-100 rel z-1"> <section class="blog-detaisl-page py-100 rel z-1">
@@ -337,10 +350,10 @@ $page_id = 'best_0f_ec';
<h4>Richard M. Fudge</h4> <h4>Richard M. Fudge</h4>
<p>The world is a book, and those who do not travel read only one page. Every journey we undertake is a chapter filled with lessons, experiences, and stories.</p> <p>The world is a book, and those who do not travel read only one page. Every journey we undertake is a chapter filled with lessons, experiences, and stories.</p>
<div class="social-icons"> <div class="social-icons">
<a href="contact"><i class="fab fa-facebook-f"></i></a> <a href="contact.php"><i class="fab fa-facebook-f"></i></a>
<a href="contact"><i class="fab fa-twitter"></i></a> <a href="contact.php"><i class="fab fa-twitter"></i></a>
<a href="contact"><i class="fab fa-linkedin-in"></i></a> <a href="contact.php"><i class="fab fa-linkedin-in"></i></a>
<a href="contact"><i class="fab fa-instagram"></i></a> <a href="contact.php"><i class="fab fa-instagram"></i></a>
</div> </div>
</div> </div>
</div> </div>
@@ -432,4 +445,4 @@ $page_id = 'best_0f_ec';
<!-- Blog Detaisl Area end --> <!-- Blog Detaisl Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,8 +1,4 @@
<?php <?php include_once('header02.php') ?>
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style> <style>
.image { .image {
@@ -30,11 +26,35 @@ include_once($rootPath . '/header.php');
} }
</style><?php </style>
$pageTitle = 'Blogs';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
<?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="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<!-- Overlay PNG -->
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Blogs</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">Blogs</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<!-- Blog List Area start --> <!-- Blog List Area start -->
@@ -43,12 +63,9 @@ include_once($rootPath . '/header.php');
<div class="row"> <div class="row">
<div class="col-lg-8"> <div class="col-lg-8">
<?php <?php
// Query to retrieve data from blogs table // Query to retrieve data from the trips table
$status = 'published'; $sql = "SELECT blog_id, title, date, category, image, description, author, members_only, link FROM blogs WHERE status = 'published' ORDER BY date DESC";
$stmt = $conn->prepare("SELECT blog_id, title, date, category, image, description, author, members_only, link FROM blogs WHERE status = ? ORDER BY date DESC"); $result = $conn->query($sql);
$stmt->bind_param("s", $status);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) { if ($result->num_rows > 0) {
// Loop through each row // Loop through each row
@@ -69,7 +86,7 @@ include_once($rootPath . '/header.php');
$icon = "fa-lock"; $icon = "fa-lock";
} else { } else {
if (getUserMemberStatus($_SESSION['user_id'])) { if (getUserMemberStatus($_SESSION['user_id'])) {
$blog_link = $row['link']; $blog_link = "blog_read.php?token=".encryptData($blog_id, $salt);
$button_hover = "Read More"; $button_hover = "Read More";
$icon = "fa-arrow-right"; $icon = "fa-arrow-right";
} else { } else {
@@ -79,7 +96,7 @@ include_once($rootPath . '/header.php');
} }
} }
} else { } else {
$blog_link = $row['link']; $blog_link = "blog_read.php?token=".encryptData($blog_id, $salt);
$button_hover = "Read More"; $button_hover = "Read More";
$icon = "fa-arrow-right"; $icon = "fa-arrow-right";
} }
@@ -88,10 +105,10 @@ include_once($rootPath . '/header.php');
echo ' echo '
<div class="blog-item style-three" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50"> <div class="blog-item style-three" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image"> <div class="image">
<img style="border-radius:20px;" src="assets/images/blog/' . $blog_id . '/' . $image . '" alt="Blog List"> <img style="border-radius:20px;" src="' . $image . '" alt="Blog List">
</div> </div>
<div class="content"> <div class="content">
<a href="' . url('blog') . '" class="category">' . $category . '</a> <a href="blog.php" class="category">' . $category . '</a>
<h5><a href="' . $blog_link . '">' . $title . '</a></h5> <h5><a href="' . $blog_link . '">' . $title . '</a></h5>
<ul class="blog-meta"> <ul class="blog-meta">
<li><i class="far fa-calendar-alt"></i> <a href="#">' . $date . '</a></li> <li><i class="far fa-calendar-alt"></i> <a href="#">' . $date . '</a></li>
@@ -205,7 +222,7 @@ include_once($rootPath . '/header.php');
<div class="content text-white"> <div class="content text-white">
<span class="h6">Explore The World</span> <span class="h6">Explore The World</span>
<h3>Become a Member</h3> <h3>Become a Member</h3>
<a href="<?= url('membership') ?>" class="theme-btn style-two bgc-secondary"> <a href="membership.php" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Now">Join Now</span> <span data-hover="Explore Now">Join Now</span>
<i class="fal fa-arrow-right"></i> <i class="fal fa-arrow-right"></i>
</a> </a>
@@ -225,4 +242,4 @@ include_once($rootPath . '/header.php');
<!-- Blog List Area end --> <!-- Blog List Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,9 +1,9 @@
<?php <?php
$rootPath = dirname(dirname(dirname(__DIR__))); // session_start();
require_once($rootPath . "/src/config/env.php"); require_once("env.php");
require_once($rootPath . "/src/config/connection.php"); require_once("session.php");
require_once($rootPath . "/src/config/functions.php"); require_once("connection.php");
session_start(); require_once("functions.php");
if (!isset($_SESSION['user_id'])) { if (!isset($_SESSION['user_id'])) {
die("Not logged in"); die("Not logged in");

View File

@@ -1,9 +1,8 @@
<?php <?php
$rootPath = dirname(dirname(dirname(__DIR__))); require_once("env.php");
require_once($rootPath . "/src/config/env.php"); require_once("session.php");
require_once($rootPath . "/src/config/connection.php"); require_once("connection.php");
require_once($rootPath . "/src/config/functions.php"); require_once("functions.php");
session_start();
if (!isset($_SESSION['user_id'])) { if (!isset($_SESSION['user_id'])) {
$_SESSION['message'] = "Not authorized."; $_SESSION['message'] = "Not authorized.";

View File

@@ -1,8 +1,4 @@
<?php <?php include_once('header02.php') ?>
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style> <style>
.image { .image {
@@ -33,11 +29,20 @@ include_once($rootPath . '/header.php');
<?php <section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('assets/images/blog/1/cover.jpg');">
$pageTitle = 'Blog Details'; <div class="container">
$breadcrumbs = [['Home' => 'index.php'], ['Blogs' => 'blog.php']]; <div class="banner-inner text-white">
require_once($rootPath . '/components/banner.php'); <h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Best of the Eastern Cape 2024</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">Best of the Eastern Cape 2024</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<!-- Blog Detaisl Area start --> <!-- Blog Detaisl Area start -->
@@ -193,10 +198,10 @@ include_once($rootPath . '/header.php');
<h4>Richard M. Fudge</h4> <h4>Richard M. Fudge</h4>
<p>The world is a book, and those who do not travel read only one page. Every journey we undertake is a chapter filled with lessons, experiences, and stories.</p> <p>The world is a book, and those who do not travel read only one page. Every journey we undertake is a chapter filled with lessons, experiences, and stories.</p>
<div class="social-icons"> <div class="social-icons">
<a href="contact"><i class="fab fa-facebook-f"></i></a> <a href="contact.php"><i class="fab fa-facebook-f"></i></a>
<a href="contact"><i class="fab fa-twitter"></i></a> <a href="contact.php"><i class="fab fa-twitter"></i></a>
<a href="contact"><i class="fab fa-linkedin-in"></i></a> <a href="contact.php"><i class="fab fa-linkedin-in"></i></a>
<a href="contact"><i class="fab fa-instagram"></i></a> <a href="contact.php"><i class="fab fa-instagram"></i></a>
</div> </div>
</div> </div>
</div> </div>
@@ -444,7 +449,7 @@ include_once($rootPath . '/header.php');
<ul class="list-style-three"> <ul class="list-style-three">
<li><a href="about.html">About Company</a></li> <li><a href="about.html">About Company</a></li>
<li><a href="blog.html">Community Blog</a></li> <li><a href="blog.html">Community Blog</a></li>
<li><a href="contact">Jobs and Careers</a></li> <li><a href="contact.php">Jobs and Careers</a></li>
<li><a href="blog.html">latest News Blog</a></li> <li><a href="blog.html">latest News Blog</a></li>
</ul> </ul>
</div> </div>

View File

@@ -1,19 +1,11 @@
<?php <?php
$rootPath = dirname(dirname(dirname(__DIR__))); include_once('header02.php');
require_once($rootPath . "/src/config/env.php");
require_once($rootPath . "/src/config/connection.php");
require_once($rootPath . "/src/config/functions.php");
require_once($rootPath . "/header.php");
// Ensure the user is logged in // Ensure the user is logged in
if (!isset($_SESSION['user_id'])) { if (!isset($_SESSION['user_id'])) {
die("User not logged in."); die("User not logged in.");
} }
$pageTitle = 'Edit Blog Post';
$breadcrumbs = [['Home' => 'index'], ['My Blog Posts' => 'user_blogs']];
require_once($rootPath . '/components/banner.php');
$token = $_GET['token']; $token = $_GET['token'];
// Sanitize the trip_id to prevent SQL injection // Sanitize the trip_id to prevent SQL injection
$blog_id = intval(decryptData($token, $salt)); // Ensures $trip_id is treated as an integer $blog_id = intval(decryptData($token, $salt)); // Ensures $trip_id is treated as an integer
@@ -39,7 +31,7 @@ $stmt->close();
selector: '#content', selector: '#content',
plugins: 'image code link', plugins: 'image code link',
toolbar: 'undo redo | blocks | bold italic | alignleft aligncenter alignright | code | image | link', toolbar: 'undo redo | blocks | bold italic | alignleft aligncenter alignright | code | image | link',
images_upload_url: 'upload_blog_image?blog_id=<?= $blog_id ?>', images_upload_url: 'upload.php?blog_id=<?= $blog_id ?>',
image_class_list: [ image_class_list: [
{ title: 'None', value: '' }, { title: 'None', value: '' },
{ title: 'Left Align', value: 'img-left' }, { title: 'Left Align', value: 'img-left' },
@@ -184,7 +176,7 @@ $stmt->close();
formData.append("cover_image", coverImageInput.files[0]); formData.append("cover_image", coverImageInput.files[0]);
} }
return fetch("autosave", { return fetch("autosave.php", {
method: "POST", method: "POST",
body: formData body: formData
}).then(response => { }).then(response => {
@@ -192,15 +184,12 @@ $stmt->close();
document.getElementById("autosave-status").innerText = "Draft autosaved at " + new Date().toLocaleTimeString(); document.getElementById("autosave-status").innerText = "Draft autosaved at " + new Date().toLocaleTimeString();
return true; return true;
} else { } else {
return response.text().then(errorText => { document.getElementById("autosave-status").innerText = "Autosave failed";
document.getElementById("autosave-status").innerText = "Autosave failed: " + errorText; console.error("Autosave failed", response.statusText);
console.error("Autosave failed", response.status, errorText); return false;
return false;
});
} }
}).catch(err => { }).catch(err => {
console.error("Autosave error:", err); console.error("Autosave error:", err);
document.getElementById("autosave-status").innerText = "Autosave error: " + err.message;
return false; return false;
}); });
} }
@@ -209,74 +198,68 @@ $stmt->close();
setInterval(autosavePost, 15000); setInterval(autosavePost, 15000);
// Manual autosave button // Manual autosave button
const manualSaveBtn = document.getElementById("manualSaveBtn"); document.getElementById("manualSaveBtn").addEventListener("click", autosavePost);
if (manualSaveBtn) {
manualSaveBtn.addEventListener("click", autosavePost);
}
// Manual publish button // Manual publish button
const manualPostBtn = document.getElementById("manualPostBtn"); document.getElementById("manualPostBtn").addEventListener("click", function () {
if (manualPostBtn) { autosavePost().then(success => {
manualPostBtn.addEventListener("click", function () { if (!success) return;
autosavePost().then(success => {
if (!success) return;
const articleId = document.querySelector('[name="article_id"]').value; const articleId = document.querySelector('[name="article_id"]').value;
const publishData = new FormData(); const publishData = new FormData();
publishData.append("id", articleId); publishData.append("id", articleId);
fetch("publish_blog", { fetch("publish_blog.php", {
method: "POST", method: "POST",
body: publishData body: publishData
}).then(response => { }).then(response => {
if (response.ok) { if (response.ok) {
alert("Post published successfully!"); alert("Post published successfully!");
// Optional: redirect to the live post // Optional: redirect to the live post
window.location.href = "blog_read.php?token=<?php echo encryptData($blog_id, $salt);?>"; window.location.href = "blog_read.php?token=<?php echo encryptData($blog_id, $salt);?>";
} else { } else {
alert("Publish failed."); alert("Publish failed.");
console.error("Publish error:", response.statusText); console.error("Publish error:", response.statusText);
} }
}).catch(err => { }).catch(err => {
console.error("Publish error:", err); console.error("Publish error:", err);
alert("Publish failed due to network error."); alert("Publish failed due to network error.");
});
}); });
}); });
} });
// Manual unpublish button // Manual unpublish button
const manualDraftBtn = document.getElementById("manualDraftBtn"); document.getElementById("manualDraftBtn").addEventListener("click", function () {
if (manualDraftBtn) { autosavePost().then(success => {
manualDraftBtn.addEventListener("click", function () { if (!success) return;
autosavePost().then(success => {
if (!success) return;
const articleId = document.querySelector('[name="article_id"]').value; const articleId = document.querySelector('[name="article_id"]').value;
const publishData = new FormData(); const publishData = new FormData();
publishData.append("id", articleId); publishData.append("id", articleId);
fetch("blog_unpublish", { fetch("blog_unpublish.php", {
method: "POST", method: "POST",
body: publishData body: publishData
}).then(response => { }).then(response => {
if (response.ok) { if (response.ok) {
alert("Post unpublished successfully!"); alert("Post unpublished successfully!");
// Optional: redirect to the live post // Optional: redirect to the live post
window.location.href = "blog_read.php?token=<?php echo encryptData($blog_id, $salt);?>"; window.location.href = "blog_read.php?token=<?php echo encryptData($blog_id, $salt);?>";
} else { } else {
alert("unPublish failed."); alert("unPublish failed.");
console.error("Publish error:", response.statusText); console.error("Publish error:", response.statusText);
} }
}).catch(err => { }).catch(err => {
console.error("Publish error:", err); console.error("Publish error:", err);
alert("Publish failed due to network error."); alert("Publish failed due to network error.");
});
}); });
}); });
} });
</script> </script>
</script> </script>
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
<?php include_once("insta_footer.php"); ?>

View File

@@ -1,23 +1,17 @@
<?php <?php include_once('header02.php');
$rootPath = dirname(dirname(dirname(__DIR__)));
require_once($rootPath . "/src/config/env.php");
require_once($rootPath . "/src/config/connection.php");
require_once($rootPath . "/src/config/functions.php");
require_once($rootPath . "/header.php");
$token = $_GET['token']; $token = $_GET['token'];
// Sanitize the trip_id to prevent SQL injection // Sanitize the trip_id to prevent SQL injection
$blog_id = intval(decryptData($token, $salt)); // Ensures $trip_id is treated as an integer $blog_id = intval(decryptData($token, $salt)); // Ensures $trip_id is treated as an integer
$pageTitle = 'Blog Post';
$breadcrumbs = [['Home' => 'index'], ['Blog' => 'blog']];
require_once($rootPath . '/components/banner.php');
$page_id = 'blog_'.$blog_id; $page_id = 'blog_'.$blog_id;
echo getCommentCount($page_id);
$stmt = $conn->prepare(" $stmt = $conn->prepare("
SELECT a.blog_id, a.title, a.category, a.description, a.content, a.date, a.author, SELECT a.title, a.category, a.description, a.content, a.date,
u.first_name, u.last_name, u.user_id u.first_name, u.last_name
FROM blogs a FROM blogs a
JOIN users u ON a.author = u.user_id JOIN users u ON a.author = u.user_id
WHERE a.blog_id = ? WHERE a.blog_id = ?
@@ -32,8 +26,6 @@ if ($result->num_rows === 0) {
$row = $result->fetch_assoc(); $row = $result->fetch_assoc();
$author = htmlspecialchars($row['first_name'] . ' ' . $row['last_name']); $author = htmlspecialchars($row['first_name'] . ' ' . $row['last_name']);
$author_id = $row['author'];
$is_author = (isset($_SESSION['user_id']) && $_SESSION['user_id'] == $author_id);
?> ?>
@@ -107,14 +99,30 @@ $is_author = (isset($_SESSION['user_id']) && $_SESSION['user_id'] == $author_id)
</style> </style>
<?php <?php
// Dynamically set page title to blog title $bannerFolder = 'assets/images/banners/';
if (isset($row) && !empty($row['title'])) { $bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$pageTitle = htmlspecialchars($row['title']);
} else {
$pageTitle = 'Blog Post';
}
$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">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50"><?= htmlspecialchars($row['title']) ?></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"><?= htmlspecialchars($row['title']) ?></li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<!-- Blog Detaisl Area start --> <!-- Blog Detaisl Area start -->
<section class="blog-detaisl-page py-100 rel z-1"> <section class="blog-detaisl-page py-100 rel z-1">
@@ -127,45 +135,12 @@ if (isset($row) && !empty($row['title'])) {
<li><img src="assets/images/pp/default.png" alt="Admin"> <a href="#"><?= $author?></a></li> <li><img src="assets/images/pp/default.png" alt="Admin"> <a href="#"><?= $author?></a></li>
<li><i class="far fa-calendar-alt"></i> <a href="#"><?= htmlspecialchars($row['date']) ?></a></li> <li><i class="far fa-calendar-alt"></i> <a href="#"><?= htmlspecialchars($row['date']) ?></a></li>
<li><i class="far fa-comments"></i> <a href="#">Comments (<?= getCommentCount($page_id);?>)</a></li> <li><i class="far fa-comments"></i> <a href="#">Comments (<?= getCommentCount($page_id);?>)</a></li>
<?php if ($is_author): ?>
<li><a href="blog_edit.php?token=<?php echo encryptData($blog_id, $salt); ?>">Edit Post</a></li>
<?php endif; ?>
</ul> </ul>
<?= $row['content'] ?> <?= $row['content'] ?>
</div> </div>
</div>
<div class="col-lg-4 col-md-8 col-sm-10 rmt-75">
<div class="blog-sidebar">
<div class="widget widget-gallery" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Gallery</h5>
<div class="gallery">
<?php
$folder = $rootPath . '/uploads/blogs/' . $blog_id . '/';
$files = glob($folder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
if ($files && count($files) > 0) {
shuffle($files); // Randomize the order
foreach ($files as $file) {
$relativePath = '/uploads/blogs/' . $blog_id . '/' . basename($file);
echo '<a href="' . $relativePath . '" style="width: 110px; height: 110px; overflow: hidden; display: inline-block; margin: 2px;">';
echo '<img src="' . $relativePath . '" alt="Gallery" style="width: 100%; height: 100%; object-fit: cover; display: block;">';
echo '</a>';
}
} else {
echo '<p style="font-size: 0.9em; color: #999;">No images available</p>';
}
?>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-8">
<hr class="mb-45"> <hr class="mb-45">
<div class="tag-share"> <div class="tag-share mb-50">
<div class="item" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50"> <div class="item" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<h6>Tags </h6> <h6>Tags </h6>
<div class="tag-coulds"> <div class="tag-coulds">
@@ -173,9 +148,29 @@ if (isset($row) && !empty($row['title'])) {
</div> </div>
</div> </div>
</div> </div>
<?php include_once($rootPath . '/src/pages/other/comment_box.php'); ?> <?php include_once('comment_box.php'); ?>
</div>
<div class="col-lg-4 col-md-8 col-sm-10 rmt-75">
<div class="blog-sidebar">
<div class="widget widget-gallery" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Gallery</h5>
<div class="gallery">
<?php
$folder = 'uploads/blogs/'.$blog_id.'/images/';
$files = glob($folder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
shuffle($files); // Randomize the order
foreach ($files as $file) {
echo '<a href="' . $file . '" style="width: 110px; height: 110px; overflow: hidden; display: inline-block; margin: 2px;">';
echo '<img src="' . $file . '" alt="Gallery" style="width: 100%; height: 100%; object-fit: cover; display: block;">';
echo '</a>';
}
?>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<?php include_once($rootPath . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

31
blog_unpublish.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
require_once("env.php");
require_once("session.php");
require_once("connection.php");
require_once("functions.php");
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
echo "Not authorized";
exit;
}
$article_id = (int)($_POST['id'] ?? 0);
$user_id = $_SESSION['user_id'];
if ($article_id <= 0) {
http_response_code(400);
echo "Invalid blog ID";
exit;
}
$stmt = $conn->prepare("UPDATE blogs SET status = 'draft' WHERE blog_id = ? AND author = ?");
$stmt->bind_param("ii", $article_id, $user_id);
if ($stmt->execute()) {
echo "Published";
} else {
http_response_code(500);
echo "Failed to publish: " . $stmt->error;
}
?>

View File

@@ -1,7 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
checkUserSession(); checkUserSession();
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
@@ -60,10 +57,28 @@ $user_id = $_SESSION['user_id'];
</style> </style>
</style> </style>
<?php <?php
$pageTitle = 'My Bookings'; $bannerFolder = 'assets/images/banners/';
$breadcrumbs = [['Home' => 'index.php']]; $bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
require_once($rootPath . '/components/banner.php');
$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">My Bookings</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">My bookings</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Tour List Area start --> <!-- Tour List Area start -->
<section class="tour-list-page py-100 rel z-1"> <section class="tour-list-page py-100 rel z-1">
@@ -114,7 +129,6 @@ $user_id = $_SESSION['user_id'];
// Loop through each row // Loop through each row
while ($row = $result->fetch_assoc()) { while ($row = $result->fetch_assoc()) {
$booking_id = $row['booking_id']; $booking_id = $row['booking_id'];
$payment_id = $row['payment_id'];
$booking_type = $row['booking_type']; $booking_type = $row['booking_type'];
$from_date = $row['from_date']; $from_date = $row['from_date'];
$to_date = $row['to_date']; $to_date = $row['to_date'];
@@ -268,8 +282,8 @@ $user_id = $_SESSION['user_id'];
<div class="destination-footer"> <div class="destination-footer">
<span class="price"><span>Booking Total: R ' . number_format($amount, 2) . '</span></span>'; <span class="price"><span>Booking Total: R ' . number_format($amount, 2) . '</span></span>';
if ($status == "AWAITING PAYMENT") { if ($status == "AWAITING PAYMENT") {
echo '<a href="' . getPaymentLinkByPaymentId($payment_id) . '" class="theme-btn style-two style-three"> echo '<a href="payment_confirmation.php?token=' . encryptData($booking_id, $salt) . '" class="theme-btn style-two style-three">
<span data-hover="PAY NOW">' . $status . '</span> <span data-hover="PAYMENT INFO">' . $status . '</span>
</a>'; </a>';
} else { } else {
echo '<a href="" class="theme-btn style-two style-three"> echo '<a href="" class="theme-btn style-two style-three">
@@ -323,4 +337,4 @@ $user_id = $_SESSION['user_id'];
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,15 +1,9 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
checkUserSession(); checkUserSession();
// SQL query to fetch dates for bush mechanics // SQL query to fetch dates for driver training
$stmt = $conn->prepare("SELECT course_id, date FROM courses WHERE course_type = ? AND date >= CURDATE()"); $sql = "SELECT course_id, date FROM courses WHERE course_type = 'bush_mechanics'";
$course_type = 'bush_mechanics'; $result = $conn->query($sql);
$stmt->bind_param("s", $course_type);
$stmt->execute();
$result = $stmt->get_result();
$page_id = 'bush_mechanics'; $page_id = 'bush_mechanics';
?> ?>
@@ -24,11 +18,32 @@ $page_id = 'bush_mechanics';
font-size: 16px; font-size: 16px;
} }
</style><?php </style>
$pageTitle = 'Bush Mechanics';
$breadcrumbs = [['Home' => 'index.php']]; <?php
require_once($rootPath . '/components/banner.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="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">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Bush Mechanics</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">Bush Mechanics</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<!-- Product Details Start --> <!-- Product Details Start -->
<section class="product-details pt-100"> <section class="product-details pt-100">
@@ -79,7 +94,7 @@ $page_id = 'bush_mechanics';
<hr class="mt-40"> <hr class="mt-40">
<div class="blog-sidebar tour-sidebar"> <div class="blog-sidebar tour-sidebar">
<div class="widget widget-booking" 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">
<form action="process_course_booking" method="POST"> <form action="process_course_booking.php" method="POST">
<ul class="tickets clearfix"> <ul class="tickets clearfix">
<li> <li>
Select Date Select Date
@@ -99,7 +114,7 @@ $page_id = 'bush_mechanics';
</select> </select>
</li> </li>
<?php <?php
if ($is_member || $pending_member) { if ($is_member) {
echo ' echo '
<li> <li>
Additional Members <span class="price"></span> Additional Members <span class="price"></span>
@@ -154,23 +169,13 @@ $page_id = 'bush_mechanics';
<label for="agreeCheckbox" id="agreeLabel" style="color: #888;">I have read and agree to the indemnity terms</label> <label for="agreeCheckbox" id="agreeLabel" style="color: #888;">I have read and agree to the indemnity terms</label>
</div> </div>
</div> </div>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>"> <button type="submit" class="theme-btn style-two w-100 mt-15 mb-5">
<?php <span data-hover="Book Now">Book Now</span>
$button_text = "PROCEED TO PAYMENT";
$button_disabled = "";
if (!$result || $result->num_rows == 0) {
$button_text = "No booking dates available";
$button_disabled = "disabled";
}
?>
<button type="submit" class="theme-btn style-two w-100 mt-15 mb-5" <?php echo $button_disabled; ?>>
<span data-hover="<?php echo $button_text; ?>"><?php echo $button_text; ?></span>
<i class="fal fa-arrow-right"></i> <i class="fal fa-arrow-right"></i>
</button> </button>
<div class="text-center"> <div class="text-center">
<a href="contact">You will be redirected to iKhokha's Secure payment gateway.</a> <a href="contact.php">Need some help?</a>
</div> </div>
<img src="assets/images/logos/ikhokha.png"alt="Secure Payment Badges" style="max-width: 200px; display: block; margin: 10px auto 0;">
</form> </form>
</div> </div>
@@ -347,12 +352,11 @@ $page_id = 'bush_mechanics';
// Function to calculate booking total // Function to calculate booking total
function calculateTotal() { function calculateTotal() {
// Get selected values from the form // Get selected values from the form
var additional_members = parseInt($('#members').val()) || 0; // Default to 0 if not selected var members = parseInt($('#members').val()) || 0; // Default to 1 vehicle if not selected
var additional_nonmembers = parseInt($('#non-members').val()) || 0; // Default to 0 if not selected var nonmembers = parseInt($('#non-members').val()) || 0; // Default to 1 adult if not selected
// Fetch PHP variables // Fetch PHP variables
var isMember = <?php echo $is_member ? 'true' : 'false'; ?>; var isMember = <?php echo $is_member ? 'true' : 'false'; ?>;
var pendingMember = <?php echo $pending_member ? 'true' : 'false'; ?>;
var cost_members = <?= getPrice('bush_mechanics', 'member');?>; var cost_members = <?= getPrice('bush_mechanics', 'member');?>;
var cost_nonmembers = <?= getPrice('bush_mechanics', 'nonmember');?>; var cost_nonmembers = <?= getPrice('bush_mechanics', 'nonmember');?>;
@@ -360,11 +364,11 @@ $page_id = 'bush_mechanics';
var total = 0; var total = 0;
// Calculate cost for members // Calculate cost for members
if (isMember || pendingMember) { if (isMember) {
total = (cost_members) + (additional_members * cost_members) + (additional_nonmembers * cost_nonmembers); total = (cost_members) + (members * cost_members) + (nonmembers * cost_nonmembers);
} else { } else {
// Calculate cost for non-members // Calculate cost for non-members
total = (cost_nonmembers) + (additional_nonmembers * cost_nonmembers); total = (cost_nonmembers) + (members * cost_members) + (nonmembers * cost_nonmembers);
} }
// Update total price in the DOM // Update total price in the DOM
@@ -383,4 +387,4 @@ $page_id = 'bush_mechanics';
</script> </script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?> <?php include_once('insta_footer.php') ?>

View File

@@ -1,6 +1,4 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
checkUserSession(); checkUserSession();
?> ?>
@@ -78,7 +76,7 @@ checkUserSession();
</div> </div>
<div class="widget widget-booking" 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">
<h5 class="widget-title">Book your Campsite</h5> <h5 class="widget-title">Book your Campsite</h5>
<form action="process_camp_booking" method="POST"> <form action="process_camp_booking.php" method="POST">
<div class="date mb-25"> <div class="date mb-25">
<b>From Date</b> <b>From Date</b>
<input type="date" id="from_date" name="from_date"> <input type="date" id="from_date" name="from_date">
@@ -125,7 +123,6 @@ checkUserSession();
<?php endif ?> <?php endif ?>
<h6>Total: <span id="booking_total" class="price">-</span></h6> <h6>Total: <span id="booking_total" class="price">-</span></h6>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<button type="submit" class="theme-btn style-two w-100 mt-15 mb-5"> <button type="submit" class="theme-btn style-two w-100 mt-15 mb-5">
<span data-hover="Book Now">Book Now</span> <span data-hover="Book Now">Book Now</span>
<i class="fal fa-arrow-right"></i> <i class="fal fa-arrow-right"></i>
@@ -214,4 +211,4 @@ checkUserSession();
}); });
</script> </script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?> <?php include_once('insta_footer.php') ?>

208
campsites.php Normal file
View File

@@ -0,0 +1,208 @@
<?php include_once('header02.php');
$conn = openDatabaseConnection();
$result = $conn->query("SELECT * FROM campsites");
$campsites = [];
while ($row = $result->fetch_assoc()) {
$campsites[] = $row;
}
?>
<style>
#map {
height: 600px;
width: 100%;
}
.gm-style .info-box {
max-width: 250px;
}
.info-box img {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
</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="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">Campsites</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">Campsites</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">
<div id="map" style="width: 100%; height: 500px;"></div>
<!-- Add Campsite Modal -->
</div>
</div>
</div>
</section>
<div class="modal fade" id="addCampsiteModal" tabindex="-1">
<div class="modal-dialog">
<form id="addCampsiteForm" method="POST" action="add_campsite.php" enctype="multipart/form-data">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Campsite</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" name="latitude" id="latitude">
<input type="hidden" name="longitude" id="longitude">
<div class="mb-3">
<label class="form-label">Campsite Name</label>
<input type="text" class="form-control" name="name" required>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea class="form-control" name="description" rows="3"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Booking URL</label>
<input type="url" class="form-control" name="website">
</div>
<div class="mb-3">
<label class="form-label">Phone Number</label>
<input type="text" class="form-control" name="telephone">
</div>
<div class="mb-3">
<label class="form-label">Thumbnail Image</label>
<input type="file" class="form-control" name="thumbnail" accept="image/*">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="submit">Save Campsite</button>
<button class="btn btn-secondary" type="button" data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</form>
</div>
</div>
<script>
let map;
const campsites = <?php echo json_encode($campsites); ?>;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
center: {
lat: -28.0,
lng: 24.0
}, // SA center
zoom: 6,
});
map.addListener("click", function(e) {
const lat = e.latLng.lat();
const lng = e.latLng.lng();
document.getElementById("latitude").value = lat;
document.getElementById("longitude").value = lng;
const addModal = new bootstrap.Modal(document.getElementById("addCampsiteModal"));
addModal.show();
});
// Load existing campsites from PHP
fetch("get_campsites.php")
.then(response => response.json())
.then(data => {
data.forEach(site => {
const marker = new google.maps.Marker({
position: {
lat: parseFloat(site.latitude),
lng: parseFloat(site.longitude)
},
map,
title: site.name,
});
const content = `
<div class="info-box">
<strong>${site.name}</strong><br>
${site.description ? site.description + "<br>" : ""}
${site.website ? `<a href="${site.website}" target="_blank">Visit Website</a><br>` : ""}
${site.telephone ? `Phone: ${site.telephone}<br>` : ""}
${site.thumbnail ? `<img src="${site.thumbnail}" style="width: 100%; max-width: 200px; border-radius: 8px; margin-top: 5px;">` : ""}
${site.user && site.user.first_name ? `
<div class="user-info mt-2 d-flex align-items-center">
<img src="${site.user.profile_pic}" style="width: 40px; height: 40px; border-radius: 50%; object-fit: cover; margin-right: 10px;">
<div>
<small>Added by:</small><br>
<strong>${site.user.first_name} ${site.user.last_name}</strong>
</div>
</div>` : ""}
<br>
<button class="btn btn-sm btn-warning mt-2" onclick='editCampsite(${JSON.stringify(site)})'>Edit</button>
<a href="https://www.google.com/maps/dir/?api=1&destination=${site.latitude},${site.longitude}" target="_blank" class="btn btn-sm btn-outline-primary mt-2 ms-2">Get Directions</a>
</div>
`;
const infowindow = new google.maps.InfoWindow({
content: content
});
marker.addListener("click", () => {
infowindow.open(map, marker);
});
});
})
.catch(err => console.error("Failed to load campsites:", err));
}
function editCampsite(site) {
// Pre-fill form
document.querySelector("#addCampsiteForm input[name='name']").value = site.name;
document.querySelector("#addCampsiteForm textarea[name='description']").value = site.description || "";
document.querySelector("#addCampsiteForm input[name='website']").value = site.website || "";
document.querySelector("#addCampsiteForm input[name='telephone']").value = site.telephone || "";
document.querySelector("#addCampsiteForm input[name='latitude']").value = site.latitude;
document.querySelector("#addCampsiteForm input[name='longitude']").value = site.longitude;
// Add hidden ID input
let idInput = document.querySelector("#addCampsiteForm input[name='id']");
if (!idInput) {
idInput = document.createElement("input");
idInput.type = "hidden";
idInput.name = "id";
document.querySelector("#addCampsiteForm").appendChild(idInput);
}
idInput.value = site.id;
// Show the modal
const addModal = new bootstrap.Modal(document.getElementById("addCampsiteModal"));
addModal.show();
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-JuvnbUYc8WGjQBFFVZtKiv5_bFJoWLU&callback=initMap" async defer></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<?php include_once("insta_footer.php"); ?>

View File

@@ -1,9 +1,8 @@
<?php <?php
$rootPath = dirname(dirname(dirname(__DIR__))); require_once("env.php");
require_once($rootPath . '/src/config/env.php'); require_once("session.php");
require_once($rootPath . '/src/config/session.php'); require_once("connection.php");
require_once($rootPath . '/src/config/connection.php'); require_once("functions.php");
require_once($rootPath . '/src/config/functions.php');
$response = array('status' => 'error', 'message' => 'Something went wrong'); $response = array('status' => 'error', 'message' => 'Something went wrong');

View File

@@ -1,320 +0,0 @@
<?php
/**
* DatabaseService Class
*
* Provides a centralized database abstraction layer for all database operations.
* Enforces prepared statements, proper error handling, and type safety.
*
* @package 4WDCSA
* @version 1.0
*/
class DatabaseService {
private $conn;
private $lastError = null;
private $lastQuery = null;
/**
* Constructor - Initialize database connection
*
* @param mysqli $connection The MySQLi connection object
*/
public function __construct($connection) {
if (!$connection) {
throw new Exception("Database connection failed");
}
$this->conn = $connection;
}
/**
* Get the last error message
*
* @return string|null The last error or null if no error
*/
public function getLastError() {
return $this->lastError;
}
/**
* Get the last executed query
*
* @return string|null The last query or null
*/
public function getLastQuery() {
return $this->lastQuery;
}
/**
* Execute a SELECT query with parameter binding
*
* @param string $query SQL query with ? placeholders
* @param array $params Parameters to bind
* @param string $types Type specification string (e.g., "isi" for int, string, int)
* @return array|false Array of results or false on error
*/
public function select($query, $params = [], $types = "") {
try {
$this->lastQuery = $query;
$stmt = $this->conn->prepare($query);
if (!$stmt) {
$this->lastError = "Prepare failed: " . $this->conn->error;
return false;
}
if (!empty($params) && !empty($types)) {
if (!$stmt->bind_param($types, ...$params)) {
$this->lastError = "Bind failed: " . $stmt->error;
return false;
}
}
if (!$stmt->execute()) {
$this->lastError = "Execute failed: " . $stmt->error;
return false;
}
$result = $stmt->get_result();
$data = [];
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
$stmt->close();
return $data;
} catch (Exception $e) {
$this->lastError = $e->getMessage();
return false;
}
}
/**
* Execute a SELECT query returning a single row
*
* @param string $query SQL query with ? placeholders
* @param array $params Parameters to bind
* @param string $types Type specification string
* @return array|false Single row as associative array or false
*/
public function selectOne($query, $params = [], $types = "") {
$results = $this->select($query, $params, $types);
return ($results && count($results) > 0) ? $results[0] : false;
}
/**
* Execute an INSERT query
*
* @param string $query SQL query with ? placeholders
* @param array $params Parameters to bind
* @param string $types Type specification string
* @return int|false Last insert ID or false on error
*/
public function insert($query, $params = [], $types = "") {
try {
$this->lastQuery = $query;
$stmt = $this->conn->prepare($query);
if (!$stmt) {
$this->lastError = "Prepare failed: " . $this->conn->error;
return false;
}
if (!empty($params) && !empty($types)) {
if (!$stmt->bind_param($types, ...$params)) {
$this->lastError = "Bind failed: " . $stmt->error;
return false;
}
}
if (!$stmt->execute()) {
$this->lastError = "Execute failed: " . $stmt->error;
return false;
}
$insertId = $stmt->insert_id;
$stmt->close();
return $insertId;
} catch (Exception $e) {
$this->lastError = $e->getMessage();
return false;
}
}
/**
* Execute an UPDATE query
*
* @param string $query SQL query with ? placeholders
* @param array $params Parameters to bind
* @param string $types Type specification string
* @return int|false Number of affected rows or false on error
*/
public function update($query, $params = [], $types = "") {
try {
$this->lastQuery = $query;
$stmt = $this->conn->prepare($query);
if (!$stmt) {
$this->lastError = "Prepare failed: " . $this->conn->error;
return false;
}
if (!empty($params) && !empty($types)) {
if (!$stmt->bind_param($types, ...$params)) {
$this->lastError = "Bind failed: " . $stmt->error;
return false;
}
}
if (!$stmt->execute()) {
$this->lastError = "Execute failed: " . $stmt->error;
return false;
}
$affectedRows = $stmt->affected_rows;
$stmt->close();
return $affectedRows;
} catch (Exception $e) {
$this->lastError = $e->getMessage();
return false;
}
}
/**
* Execute a DELETE query
*
* @param string $query SQL query with ? placeholders
* @param array $params Parameters to bind
* @param string $types Type specification string
* @return int|false Number of affected rows or false on error
*/
public function delete($query, $params = [], $types = "") {
return $this->update($query, $params, $types);
}
/**
* Execute an arbitrary query (for complex queries)
*
* @param string $query SQL query with ? placeholders
* @param array $params Parameters to bind
* @param string $types Type specification string
* @return mixed Query result or false on error
*/
public function execute($query, $params = [], $types = "") {
try {
$this->lastQuery = $query;
$stmt = $this->conn->prepare($query);
if (!$stmt) {
$this->lastError = "Prepare failed: " . $this->conn->error;
return false;
}
if (!empty($params) && !empty($types)) {
if (!$stmt->bind_param($types, ...$params)) {
$this->lastError = "Bind failed: " . $stmt->error;
return false;
}
}
if (!$stmt->execute()) {
$this->lastError = "Execute failed: " . $stmt->error;
return false;
}
$stmt->close();
return true;
} catch (Exception $e) {
$this->lastError = $e->getMessage();
return false;
}
}
/**
* Count rows matching a condition
*
* @param string $table Table name
* @param string $where WHERE clause (without WHERE keyword)
* @param array $params Parameters to bind
* @param string $types Type specification string
* @return int|false Row count or false on error
*/
public function count($table, $where = "1=1", $params = [], $types = "") {
$query = "SELECT COUNT(*) as count FROM {$table} WHERE {$where}";
$result = $this->selectOne($query, $params, $types);
return ($result) ? (int)$result['count'] : false;
}
/**
* Check if a record exists
*
* @param string $table Table name
* @param string $where WHERE clause (without WHERE keyword)
* @param array $params Parameters to bind
* @param string $types Type specification string
* @return bool True if record exists, false otherwise
*/
public function exists($table, $where, $params = [], $types = "") {
$count = $this->count($table, $where, $params, $types);
return ($count !== false && $count > 0);
}
/**
* Get the MySQLi connection object for advanced operations
*
* @return mysqli The MySQLi connection
*/
public function getConnection() {
return $this->conn;
}
/**
* Start a transaction
*
* @return bool Success status
*/
public function beginTransaction() {
try {
$this->conn->begin_transaction();
return true;
} catch (Exception $e) {
$this->lastError = $e->getMessage();
return false;
}
}
/**
* Commit a transaction
*
* @return bool Success status
*/
public function commit() {
try {
$this->conn->commit();
return true;
} catch (Exception $e) {
$this->lastError = $e->getMessage();
return false;
}
}
/**
* Rollback a transaction
*
* @return bool Success status
*/
public function rollback() {
try {
$this->conn->rollback();
return true;
} catch (Exception $e) {
$this->lastError = $e->getMessage();
return false;
}
}
}
?>

View File

@@ -1,120 +0,0 @@
<?php
class IkhokhaClient {
private string $appId;
private string $appSecret;
private string $apiUrl;
public function __construct() {
// Try getenv first, then fallback to $_ENV if available
$this->appId = getenv('IKHOKHA_APP_ID') ?: ($_ENV['IKHOKHA_APP_ID'] ?? '');
$this->appSecret = getenv('IKHOKHA_APP_SECRET') ?: ($_ENV['IKHOKHA_APP_SECRET'] ?? '');
$this->apiUrl = getenv('IKHOKHA_API_URL') ?: ($_ENV['IKHOKHA_API_URL'] ?? '');
}
/**
* Make a request to the iKhokha API. Signs the payload per API docs.
* $endpoint should be the path portion starting with '/public-api/...'
*/
private function request(string $endpoint, array $data, string $method = 'POST') {
// Validate apiUrl
if (empty($this->apiUrl)) {
return ['error' => true, 'errno' => 3, 'message' => 'IKHOKHA_API_URL is not configured in environment'];
}
// If the configured API URL already contains the endpoint path, use it as-is.
if ((function_exists('str_ends_with') && str_ends_with($this->apiUrl, $endpoint)) ||
(substr_compare($this->apiUrl, $endpoint, -strlen($endpoint)) === 0)) {
$url = $this->apiUrl;
} else {
$url = rtrim($this->apiUrl, '/') . $endpoint;
}
$body = json_encode($data);
// Build payload to sign: path + body and apply escape rules per iKhokha docs
$parsed = parse_url($url);
$path = $parsed['path'] ?? $endpoint;
$payloadToSign = $path . $body;
// Escape function from iKhokha example
$escapeString = function ($str) {
$escaped = preg_replace(['/[\\\"\'\"]/u', '/\x00/'], ['\\\\$0', '\\0'], (string)$str);
$cleaned = str_replace('\/', '/', $escaped);
return $cleaned;
};
$escapedPayload = $escapeString($payloadToSign);
$signature = hash_hmac('sha256', $escapedPayload, $this->appSecret);
$ch = curl_init($url);
$headers = [
'Content-Type: application/json',
"IK-APPID: {$this->appId}",
"IK-SIGN: {$signature}"
];
// Optional debug logging to logs/ikhokha.log when IKHOKHA_DEBUG_LOG is true
$debugLog = getenv('IKHOKHA_DEBUG_LOG') ?: ($_ENV['IKHOKHA_DEBUG_LOG'] ?? null);
if ($debugLog) {
$logPath = dirname(__DIR__) . '/logs/ikhokha.log';
$logEntry = [
'time' => date('c'),
'url' => $url,
'headers' => $headers,
'body' => $data,
'signature' => $signature
];
@file_put_contents($logPath, json_encode(['request' => $logEntry]) . PHP_EOL, FILE_APPEND | LOCK_EX);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if (strtoupper($method) === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
} else {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
}
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Log response if debug enabled
if (!empty($debugLog)) {
$logPath = dirname(__DIR__) . '/logs/ikhokha.log';
$respEntry = [
'time' => date('c'),
'http_code' => $httpCode,
'errno' => $errno,
'error' => $error,
'response' => $response
];
@file_put_contents($logPath, json_encode(['response' => $respEntry]) . PHP_EOL, FILE_APPEND | LOCK_EX);
}
if ($response === false) {
return ['error' => true, 'message' => $error, 'errno' => $errno];
}
return json_decode($response, true);
}
/**
* Create a payment link using the iKhokha create payment endpoint.
* $body must match iKhokha request schema (amount in smallest unit, urls, externalTransactionID, etc.)
*/
public function createPaymentLink(array $body) {
return $this->request('/public-api/v1/api/payment', $body, 'POST');
}
public function getPaymentStatus($paymentId) {
// Use the GET status endpoint
$endpoint = '/public-api/v1/api/getStatus/' . urlencode($paymentId);
return $this->request($endpoint, [], 'GET');
}
}

152
comment_box.php Normal file
View File

@@ -0,0 +1,152 @@
<?php
if (!isset($page_id)) {
die("Page ID not set for comment system.");
}
$conn = openDatabaseConnection();
// Handle comment post
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_comment'])) {
$comment = $conn->real_escape_string(trim($_POST['comment']));
if (!empty($comment)) {
$stmt = $conn->prepare("INSERT INTO comments (page_id, user_id, comment) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $page_id, $user_id, $comment);
if ($stmt->execute()) {
header("Location: " . $_SERVER['REQUEST_URI']);
exit;
}
}
}
// Fetch comments
$stmt = $conn->prepare("SELECT user_id, comment, created_at FROM comments WHERE page_id = ? ORDER BY created_at DESC");
$stmt->bind_param("s", $page_id);
$stmt->execute();
$result = $stmt->get_result();
?>
<div>
<h5>Comments</h5>
<div class="comments">
<?php while ($row = $result->fetch_assoc()): ?>
<div class="comment-body" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div>
<img class="profile-pic" src="<?= getProfilePic($user_id); ?>" alt="Author">
</div>
<div class="">
<h6><?= getFullName($row['user_id']); ?></h6>
<?php
if (getUserMemberStatus($row['user_id'])){
echo '<div class="badge badge-primary badge-pill">MEMBER</div>';
}
?>
<em><?= $row['created_at'] ?></em>
<!-- <div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div> -->
<p><?= nl2br(htmlspecialchars($row['comment'])) ?></p>
<!-- <a class="read-more" href="#">Reply <i class="far fa-angle-right"></i></a> -->
</div>
</div>
<?php endwhile; ?>
</div>
<!-- <h5>Add A Comment</h5> -->
<form method="POST" id="comment-form" class="comment-form bgc-lighter z-1 rel mt-30" name="review-form" action="" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="row gap-20">
<div class="col-md-12">
<div class="form-group">
<textarea name="comment" id="comment" class="form-control" rows="5" placeholder="Add comment..." required></textarea>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<button type="submit" name="submit_comment" class="theme-btn bgc-secondary style-two">
<span data-hover="Submit reviews">Add comment</span>
<i class="fal fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</form>
</div>
<style>
.comment-box {
border: 1px solid #ccc;
padding: 10px;
max-width: 600px;
}
.comment-box form input,
.comment-box form textarea {
width: 100%;
margin-bottom: 8px;
}
.comments-list {
margin-top: 20px;
}
.comment {
border-top: 1px solid #eee;
padding-top: 10px;
margin-top: 10px;
}
.profile-pic {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
object-fit: cover;
/* Ensures the image fits without distortion */
}
.badge {
display: inline-block;
padding: 0.4em 0.8em;
font-size: 0.875rem;
font-weight: 600;
color: white;
border-radius: 0.375em;
margin-right: 0.5em;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge-primary {
background-color: #e90000;
}
.badge-success {
background-color: #28a745;
}
.badge-warning {
background-color: #ffc107;
color: #212529;
}
.badge-danger {
background-color: #dc3545;
}
.badge-info {
background-color: #17a2b8;
}
.badge-pill {
border-radius: 999px;
}
</style>

View File

@@ -1,80 +0,0 @@
<?php
/**
* REUSABLE PAGE BANNER COMPONENT
*
* Displays a page banner with background image, title, and breadcrumb navigation.
*
* Usage in your page:
*
* <?php
* $pageTitle = 'About';
* $bannerImage = 'assets/images/blog/cover.jpg'; // optional
* require_once('components/banner.php');
* ?>
*
* Parameters:
* $pageTitle (required) - Page title to display
* $bannerImage (optional) - URL to banner background image. If not set, uses random banner
* $breadcrumbs (optional) - Array of breadcrumb items. Default: [['Home' => 'index.php']]
* $classes (optional) - Additional CSS classes for banner section
*/
// Default values
$pageTitle = $pageTitle ?? 'Page';
$bannerImage = $bannerImage ?? '';
$breadcrumbs = $breadcrumbs ?? [['Home' => 'index.php']];
$classes = $classes ?? '';
// If no banner image provided, try to use random banner
if (empty($bannerImage)) {
// Try to determine root path if not already set
if (!isset($rootPath)) {
$rootPath = $_SERVER['DOCUMENT_ROOT'] ?? dirname(__DIR__);
}
$bannerFolder = $rootPath . '/assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
// Convert absolute paths back to web-relative paths
$bannerImages = array_map(function($path) use ($rootPath) {
return str_replace($rootPath, '', $path);
}, $bannerImages);
$bannerImage = !empty($bannerImages) ? $bannerImages[array_rand($bannerImages)] : '/assets/images/base4/camping.jpg';
}
// Add the page title to breadcrumbs as last item (not a link)
$breadcrumbItems = [];
foreach ($breadcrumbs as $item) {
foreach ($item as $label => $url) {
$breadcrumbItems[] = ['label' => $label, 'url' => $url];
}
}
$breadcrumbItems[] = ['label' => $pageTitle, 'url' => null];
?>
<!-- Page Banner Start -->
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover <?php echo $classes; ?>" style="background-image: url('<?php echo $bannerImage; ?>');">
<!-- Overlay PNG -->
<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">
<?php echo htmlspecialchars($pageTitle); ?>
</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">
<?php foreach ($breadcrumbItems as $item): ?>
<li class="breadcrumb-item <?php echo $item['url'] === null ? 'active' : ''; ?>">
<?php if ($item['url']): ?>
<a href="<?php echo htmlspecialchars($item['url']); ?>">
<?php echo htmlspecialchars($item['label']); ?>
</a>
<?php else: ?>
<?php echo htmlspecialchars($item['label']); ?>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->

15
connection.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
$dbhost = $_ENV['DB_HOST'];
$dbuser = $_ENV['DB_USER'];
$dbpass = $_ENV['DB_PASS'];
$dbname = $_ENV['DB_NAME'];
$salt = $_ENV['SALT'];
if(!$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname)){
die("Failed to connect: " . mysqli_connect_error());
}
date_default_timezone_set('Africa/Johannesburg');

View File

@@ -1,8 +1,4 @@
<?php <?php include_once('header02.php') ?>
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style> <style>
.image { .image {
@@ -28,11 +24,32 @@ include_once($rootPath . '/header.php');
display: block; display: block;
/* Prevents inline whitespace issues */ /* Prevents inline whitespace issues */
} }
</style><?php </style>
$pageTitle = 'Contact Us';
$breadcrumbs = [['Home' => 'index.php']]; <?php
require_once($rootPath . '/components/banner.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="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">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Contact Us</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.html">Home</a></li>
<li class="breadcrumb-item active">Contact Us</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<!-- Contact Info Area start --> <!-- Contact Info Area start -->
@@ -110,4 +127,4 @@ include_once($rootPath . '/header.php');
<!-- Contact Map End --> <!-- Contact Map End -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?> <?php include_once("insta_footer.php"); ?>

View File

@@ -1,23 +1,26 @@
<?php <?php include_once('header02.php');
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
// SQL query to fetch dates for driver training // SQL query to fetch dates for driver training
$stmt = $conn->prepare("SELECT course_id, date FROM courses WHERE course_type = ?"); $sql = "SELECT course_id, date FROM courses WHERE course_type = 'driver_training'";
$course_type = 'driver_training'; $result = $conn->query($sql);
$stmt->bind_param("s", $course_type);
$stmt->execute();
$result = $stmt->get_result();
?> ?>
<?php <!-- Page Banner Start -->
$pageTitle = 'Course Details'; <section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url(assets/images/banner/banner.jpg);">
$breadcrumbs = [['Home' => 'index.php']]; <div class="container">
require_once($rootPath . '/components/banner.php'); <div class="banner-inner text-white">
?> <h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4X4 Driver Training</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">4X4 Driver Training</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End --> <!-- Page Banner End -->
<!-- Product Details Start --> <!-- Product Details Start -->
@@ -68,7 +71,6 @@ $result = $stmt->get_result();
<p>Our 4x4 Basic Training Course equips you with the essential skills and knowledge to confidently tackle off-road terrains. Learn vehicle mechanics, driving techniques, obstacle navigation, and recovery methods while promoting safe and responsible off-road practices. Perfect for beginners and new 4x4 owners!</p> <p>Our 4x4 Basic Training Course equips you with the essential skills and knowledge to confidently tackle off-road terrains. Learn vehicle mechanics, driving techniques, obstacle navigation, and recovery methods while promoting safe and responsible off-road practices. Perfect for beginners and new 4x4 owners!</p>
<hr class="mt-40"> <hr class="mt-40">
<form action="#" class="add-to-cart pt-15 pb-30"> <form action="#" class="add-to-cart pt-15 pb-30">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<label for="course_date">Select a Date:</label> <label for="course_date">Select a Date:</label>
<select name="course_date" id="course_date" required> <select name="course_date" id="course_date" required>
<!-- <option value="" disabled selected>-- Select a Date --</option> --> <!-- <option value="" disabled selected>-- Select a Date --</option> -->
@@ -300,4 +302,4 @@ $result = $stmt->get_result();
<!-- Shop Details Area end --> <!-- Shop Details Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?> <?php include_once('insta_footer.php') ?>

View File

@@ -1,54 +1,39 @@
<?php <?php
$rootPath = dirname(dirname(__DIR__)); require_once("session.php");
require_once($rootPath . "/src/config/session.php"); require_once("connection.php");
require_once($rootPath . "/src/config/connection.php"); require_once("functions.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 // Check if user_id is set in the POST request
if (isset($_POST['user_id']) && !empty($_POST['user_id'])) { if (isset($_POST['user_id']) && !empty($_POST['user_id'])) {
// Validate user_id as integer // Sanitize the input to prevent SQL injection
$user_id = intval($_POST['user_id']); $user_id = mysqli_real_escape_string($conn, $_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 $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 // First, check if a bar tab already exists for this user_id
$stmt = $conn->prepare("SELECT * FROM bar_tabs WHERE user_id = ? LIMIT 1"); $checkSql = "SELECT * FROM bar_tabs WHERE user_id = '$user_id' LIMIT 1";
$stmt->bind_param("i", $user_id); $checkResult = mysqli_query($conn, $checkSql);
$stmt->execute();
$checkResult = $stmt->get_result();
if ($checkResult->num_rows > 0) { if (mysqli_num_rows($checkResult) > 0) {
// If a bar tab already exists for this user_id, return an error message // 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.']); echo json_encode(['status' => 'error', 'message' => 'A bar tab already exists for this user.']);
} else { } else {
// Prepare the SQL query to insert a new record into the bar_tabs table // Prepare the SQL query to insert a new record into the bar_tabs table
$stmt = $conn->prepare("INSERT INTO bar_tabs (user_id) VALUES (?)"); $sql = "INSERT INTO bar_tabs (user_id) VALUES ('$user_id')";
$stmt->bind_param("i", $user_id);
// Execute the query // Execute the query
if ($stmt->execute()) { if (mysqli_query($conn, $sql)) {
// If the insertion is successful, return a success message // If the insertion is successful, return a success message
echo json_encode(['status' => 'success', 'message' => 'Bar tab created successfully.']); echo json_encode(['status' => 'success', 'message' => 'Bar tab created successfully.']);
} else { } else {
// If there's an error, return an error message // If there's an error, return an error message
echo json_encode(['status' => 'error', 'message' => 'Error: ' . $conn->error]); echo json_encode(['status' => 'error', 'message' => 'Error: ' . mysqli_error($conn)]);
} }
} }
} else { } else {
// If user_id is not provided, return an error message // If user_id is not provided, return an error message
echo json_encode(['status' => 'error', 'message' => 'User ID is required.']); echo json_encode(['status' => 'error', 'message' => 'User ID is required.']);
} }
// Close the database connection
mysqli_close($conn);
?> ?>

View File

@@ -1,368 +0,0 @@
# DatabaseService Usage Examples
This document shows how to refactor existing code to use the new `DatabaseService` class for cleaner, more maintainable database operations.
## Current State
Files are using the procedural MySQLi pattern:
```php
$stmt = $conn->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
$stmt->close();
```
## Example 1: Simple SELECT (admin_members.php)
### Current Code
```php
$stmt = $conn->prepare("SELECT user_id, first_name, last_name, tel_cell, email, dob, accept_indemnity FROM membership_application");
$stmt->execute();
$result = $stmt->get_result();
// Then in HTML/JS loop:
while ($row = $result->fetch_assoc()) {
// display row
}
```
### Using DatabaseService
```php
// Simple - get all records
$members = $db->select("SELECT user_id, first_name, last_name, tel_cell, email, dob, accept_indemnity FROM membership_application");
// In HTML/JS loop:
foreach ($members as $row) {
// display row
}
```
**Benefits:**
- No manual `bind_param()`, `execute()`, `close()` needed
- Returns array directly
- Automatic error tracking via `$db->getLastError()`
---
## Example 2: SELECT with Parameters (validate_login.php)
### Current Code
```php
$query = "SELECT * FROM users WHERE email = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 1) {
$row = $result->fetch_assoc();
// use $row
}
$stmt->close();
```
### Using DatabaseService
```php
$user = $db->selectOne(
"SELECT * FROM users WHERE email = ?",
[$email],
"s" // s = string type
);
if ($user) {
// use $user - returns false if no row found
}
```
**Benefits:**
- One-liner for single row
- Handles null checks automatically
- Type specification clear in parameters
---
## Example 3: INSERT (validate_login.php)
### Current Code
```php
$query = "INSERT INTO users (email, first_name, last_name, profile_pic, password, is_verified) VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($query);
$is_verified = 1;
$stmt->bind_param("sssssi", $email, $first_name, $last_name, $picture, $password, $is_verified);
if ($stmt->execute()) {
$user_id = $conn->insert_id; // ❌ Bug: insert_id from $conn, not $stmt
// use $user_id
}
$stmt->close();
```
### Using DatabaseService
```php
$user_id = $db->insert(
"INSERT INTO users (email, first_name, last_name, profile_pic, password, is_verified) VALUES (?, ?, ?, ?, ?, ?)",
[$email, $first_name, $last_name, $picture, $password, 1],
"sssssi"
);
if ($user_id) {
// $user_id contains the auto-increment ID
} else {
$error = $db->getLastError();
}
```
**Benefits:**
- Returns insert ID directly
- Automatic error handling
- Cleaner parameter list
---
## Example 4: UPDATE (admin_members.php)
### Current Code
```php
$user_id = intval($_POST['user_id']);
$stmt = $conn->prepare("UPDATE membership_application SET accept_indemnity = 1 WHERE user_id = ?");
if ($stmt) {
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->close();
}
```
### Using DatabaseService
```php
$user_id = intval($_POST['user_id']);
$affectedRows = $db->update(
"UPDATE membership_application SET accept_indemnity = 1 WHERE user_id = ?",
[$user_id],
"i"
);
if ($affectedRows !== false) {
// Updated successfully, $affectedRows = number of rows changed
}
```
**Benefits:**
- Returns affected row count
- No manual statement closing
- Error available via `$db->getLastError()`
---
## Example 5: COUNT / EXISTS
### Current Pattern (Need 3 lines)
```php
$stmt = $conn->prepare("SELECT COUNT(*) as count FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row['count'] > 0) { /* exists */ }
$stmt->close();
```
### Using DatabaseService (One line)
```php
$exists = $db->exists("users", "email = ?", [$email], "s");
if ($exists) {
// User exists
}
```
**Benefits:**
- Boolean result
- Intent is clear
- One-liner
---
## Example 6: Multiple Rows with Filtering
### Current Code
```php
$status = 'active';
$stmt = $conn->prepare("SELECT * FROM members WHERE status = ? ORDER BY last_name ASC");
$stmt->bind_param("s", $status);
$stmt->execute();
$result = $stmt->get_result();
$members = [];
while ($row = $result->fetch_assoc()) {
$members[] = $row;
}
$stmt->close();
```
### Using DatabaseService
```php
$members = $db->select(
"SELECT * FROM members WHERE status = ? ORDER BY last_name ASC",
['active'],
"s"
);
```
**Benefits:**
- Returns array directly
- No loop needed
- 2 lines vs 8 lines
---
## Example 7: Error Handling
### Current Pattern
```php
$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
if (!$stmt) {
echo "Prepare failed: " . $conn->error;
exit();
}
$stmt->bind_param("i", $id);
if (!$stmt->execute()) {
echo "Execute failed: " . $stmt->error;
exit();
}
```
### Using DatabaseService
```php
$user = $db->selectOne("SELECT * FROM users WHERE id = ?", [$id], "i");
if ($user === false) {
$error = $db->getLastError();
error_log("Database error: " . $error);
// handle error
}
```
**Benefits:**
- Error handling centralized
- No null checks for each step
- Debug via `$db->getLastQuery()`
---
## Example 8: Transactions
### Current Pattern
```php
$conn->begin_transaction();
try {
$stmt = $conn->prepare("INSERT INTO orders ...");
$stmt->execute();
$stmt = $conn->prepare("UPDATE inventory ...");
$stmt->execute();
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
}
```
### Using DatabaseService
```php
$db->beginTransaction();
$order_id = $db->insert("INSERT INTO orders ...", [...], "...");
if ($order_id === false) {
$db->rollback();
exit("Order creation failed");
}
$updated = $db->update("UPDATE inventory ...", [...], "...");
if ($updated === false) {
$db->rollback();
exit("Inventory update failed");
}
$db->commit();
```
**Benefits:**
- Unified transaction API
- Built-in error checking
- Clean rollback on failure
---
## Type Specification Reference
When using DatabaseService methods, specify parameter types:
| Type | Meaning | Example |
|------|---------|---------|
| `"i"` | Integer | `user_id = 5` |
| `"d"` | Double/Float | `price = 19.99` |
| `"s"` | String | `email = 'test@example.com'` |
| `"b"` | Blob | Binary data |
Examples:
```php
// Single parameter
$db->select("SELECT * FROM users WHERE id = ?", [123], "i");
// Multiple parameters
$db->select(
"SELECT * FROM users WHERE email = ? AND status = ?",
["test@example.com", "active"],
"ss"
);
// Mixed types
$db->select(
"SELECT * FROM orders WHERE user_id = ? AND total > ? AND date = ?",
[5, 100.50, "2025-01-01"],
"ids" // integer, double, string
);
```
---
## Migration Path
### Phase 1: New Code
Start using `$db` for all new features and AJAX endpoints.
### Phase 2: High-Traffic Files
Refactor popular files:
1. `validate_login.php` - Login is critical
2. `functions.php` - Helper functions
3. `admin_members.php`, `admin_payments.php` - Admin pages
### Phase 3: Gradual Rollout
As each file is refactored, commit and test thoroughly before moving to next.
### Phase 4: Full Migration
Eventually all procedural `$conn->prepare()` patterns replaced.
---
## Benefits Summary
| Aspect | Before | After |
|--------|--------|-------|
| Lines per query | 5-8 | 1-3 |
| Error handling | Manual checks | Automatic |
| Type safety | bind_param() | Parameter array |
| Statement closing | Manual | Automatic |
| Insert ID handling | `$conn->insert_id` (buggy) | Direct return |
| Debugging | Check multiple vars | `getLastError()`, `getLastQuery()` |
| Consistency | Varies | Unified API |
---
## Next Steps
1. Start with one file (e.g., `admin_members.php`)
2. Convert simple queries first
3. Test thoroughly
4. Commit and move to next file
5. Keep `$conn` available for complex queries that don't fit the standard patterns
The `$db` service makes your code **cleaner, safer, and easier to maintain**.

View File

@@ -1,680 +0,0 @@
-- phpMyAdmin SQL Dump
-- version 5.2.2
-- https://www.phpmyadmin.net/
--
-- Host: db
-- Generation Time: Dec 02, 2025 at 07:32 PM
-- Server version: 8.0.41
-- PHP Version: 8.2.27
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `4wdcsa`
--
-- --------------------------------------------------------
--
-- Table structure for table `bar_items`
--
DROP TABLE IF EXISTS `bar_items`;
CREATE TABLE `bar_items` (
`item_id` int NOT NULL,
`price` decimal(10,2) DEFAULT NULL,
`description` varchar(64) DEFAULT NULL,
`image` varchar(255) DEFAULT NULL,
`qty` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `bar_tabs`
--
DROP TABLE IF EXISTS `bar_tabs`;
CREATE TABLE `bar_tabs` (
`tab_id` int NOT NULL,
`user_id` int DEFAULT NULL,
`image` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `bar_transactions`
--
DROP TABLE IF EXISTS `bar_transactions`;
CREATE TABLE `bar_transactions` (
`transaction_id` int NOT NULL,
`user_id` int DEFAULT NULL,
`item_price` decimal(10,2) DEFAULT NULL,
`item_name` varchar(64) DEFAULT NULL,
`eft_id` varchar(255) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`item_id` int DEFAULT NULL,
`tab_id` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `blacklist`
--
DROP TABLE IF EXISTS `blacklist`;
CREATE TABLE `blacklist` (
`blacklist_id` int NOT NULL,
`ip` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Table structure for table `blogs`
--
DROP TABLE IF EXISTS `blogs`;
CREATE TABLE `blogs` (
`blog_id` int NOT NULL,
`title` varchar(255) DEFAULT NULL,
`date` date DEFAULT NULL,
`category` varchar(255) DEFAULT NULL,
`description` text,
`image` varchar(255) DEFAULT NULL,
`author` int DEFAULT NULL,
`link` varchar(255) DEFAULT NULL,
`members_only` tinyint(1) NOT NULL DEFAULT '1',
`content` text,
`status` enum('draft','published','deleted') CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT 'draft'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `bookings`
--
DROP TABLE IF EXISTS `bookings`;
CREATE TABLE `bookings` (
`booking_id` int NOT NULL,
`booking_type` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`user_id` int NOT NULL,
`from_date` date DEFAULT NULL,
`to_date` date DEFAULT NULL,
`num_vehicles` int NOT NULL DEFAULT '1',
`num_adults` int NOT NULL DEFAULT '0',
`num_children` int NOT NULL DEFAULT '0',
`add_firewood` tinyint(1) DEFAULT '0',
`total_amount` decimal(10,2) DEFAULT NULL,
`discount_amount` decimal(10,2) NOT NULL DEFAULT '0.00',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
`payment_id` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`trip_id` int DEFAULT NULL,
`radio` tinyint(1) DEFAULT '0',
`course_id` int DEFAULT NULL,
`course_non_members` int DEFAULT '0',
`eft_id` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL,
`accept_indemnity` tinyint(1) DEFAULT '0',
`num_pensioners` int DEFAULT '0',
`notes` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `campsites`
--
DROP TABLE IF EXISTS `campsites`;
CREATE TABLE `campsites` (
`id` int NOT NULL,
`name` varchar(255) NOT NULL,
`description` text,
`latitude` float(10,6) NOT NULL,
`longitude` float(10,6) NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`website` varchar(255) DEFAULT NULL,
`telephone` varchar(50) DEFAULT NULL,
`thumbnail` varchar(255) DEFAULT NULL,
`user_id` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Table structure for table `comments`
--
DROP TABLE IF EXISTS `comments`;
CREATE TABLE `comments` (
`comment_id` int NOT NULL,
`page_id` varchar(255) NOT NULL,
`user_id` varchar(100) NOT NULL,
`comment` text NOT NULL,
`created_at` datetime DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Table structure for table `courses`
--
DROP TABLE IF EXISTS `courses`;
CREATE TABLE `courses` (
`course_id` int NOT NULL,
`course_type` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`date` date NOT NULL,
`capacity` int NOT NULL,
`booked` int NOT NULL,
`cost_members` decimal(10,2) NOT NULL,
`cost_nonmembers` decimal(10,2) NOT NULL,
`instructor` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`instructor_email` varchar(255) COLLATE utf8mb4_general_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `efts`
--
DROP TABLE IF EXISTS `efts`;
CREATE TABLE `efts` (
`eft_id` varchar(255) NOT NULL,
`booking_id` int DEFAULT NULL,
`user_id` int NOT NULL,
`status` varchar(64) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`amount` decimal(10,2) NOT NULL,
`description` varchar(255) DEFAULT NULL,
`membershipfee_id` int DEFAULT NULL,
`proof_of_payment` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `events`
--
DROP TABLE IF EXISTS `events`;
CREATE TABLE `events` (
`event_id` int NOT NULL,
`date` date DEFAULT NULL,
`time` time DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`image` varchar(255) DEFAULT NULL,
`description` text,
`feature` varchar(255) DEFAULT NULL,
`location` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`promo` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `legacy_members`
--
DROP TABLE IF EXISTS `legacy_members`;
CREATE TABLE `legacy_members` (
`legacy_id` varchar(12) NOT NULL,
`last_name` varchar(255) DEFAULT NULL,
`first_name` varchar(255) DEFAULT NULL,
`amount` varchar(12) DEFAULT NULL,
`phone_number` varchar(16) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Table structure for table `membership_application`
--
DROP TABLE IF EXISTS `membership_application`;
CREATE TABLE `membership_application` (
`application_id` int NOT NULL,
`user_id` int NOT NULL,
`first_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`last_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`id_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`dob` date DEFAULT NULL,
`occupation` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`tel_cell` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`spouse_first_name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`spouse_last_name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`spouse_id_number` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
`spouse_dob` date DEFAULT NULL,
`spouse_occupation` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`spouse_tel_cell` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL,
`spouse_email` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`child_name1` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`child_dob1` date DEFAULT NULL,
`child_name2` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`child_dob2` date DEFAULT NULL,
`child_name3` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`child_dob3` date DEFAULT NULL,
`physical_address` text COLLATE utf8mb4_general_ci,
`postal_address` text COLLATE utf8mb4_general_ci,
`interests_hobbies` text COLLATE utf8mb4_general_ci,
`vehicle_make` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`vehicle_model` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`vehicle_year` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL,
`vehicle_registration` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL,
`secondary_vehicle_make` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`secondary_vehicle_model` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
`secondary_vehicle_year` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL,
`secondary_vehicle_registration` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`accept_indemnity` tinyint(1) NOT NULL DEFAULT '0',
`sig` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
`code` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `membership_fees`
--
DROP TABLE IF EXISTS `membership_fees`;
CREATE TABLE `membership_fees` (
`fee_id` int NOT NULL,
`user_id` int NOT NULL,
`payment_amount` decimal(10,2) NOT NULL,
`payment_date` date DEFAULT NULL,
`payment_status` varchar(255) COLLATE utf8mb4_general_ci DEFAULT 'PENDING',
`membership_start_date` date NOT NULL,
`membership_end_date` date NOT NULL,
`due_date` date DEFAULT NULL,
`renewal_reminder_sent` tinyint(1) DEFAULT '0',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`payment_id` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `password_resets`
--
DROP TABLE IF EXISTS `password_resets`;
CREATE TABLE `password_resets` (
`id` int NOT NULL,
`user_id` int NOT NULL,
`token` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`expires_at` datetime NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `payments`
--
DROP TABLE IF EXISTS `payments`;
CREATE TABLE `payments` (
`payment_id` varchar(255) NOT NULL,
`user_id` int NOT NULL,
`amount` decimal(10,2) NOT NULL,
`status` varchar(255) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`description` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `prices`
--
DROP TABLE IF EXISTS `prices`;
CREATE TABLE `prices` (
`price_id` int NOT NULL,
`description` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`amount` decimal(10,2) DEFAULT NULL,
`amount_nonmembers` decimal(10,2) DEFAULT NULL,
`detail` text
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Table structure for table `trips`
--
DROP TABLE IF EXISTS `trips`;
CREATE TABLE `trips` (
`trip_id` int NOT NULL,
`trip_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`start_date` date NOT NULL,
`end_date` date NOT NULL,
`short_description` text COLLATE utf8mb4_general_ci NOT NULL,
`long_description` text COLLATE utf8mb4_general_ci NOT NULL,
`vehicle_capacity` int NOT NULL,
`cost_members` decimal(10,2) NOT NULL,
`cost_nonmembers` decimal(10,2) NOT NULL,
`location` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`places_booked` int DEFAULT NULL,
`booking_fee` decimal(10,2) NOT NULL,
`trip_code` varchar(12) COLLATE utf8mb4_general_ci DEFAULT NULL,
`published` tinyint(1) NOT NULL DEFAULT '0',
`cost_pensioner_member` decimal(10,2) NOT NULL,
`cost_pensioner` decimal(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`user_id` int NOT NULL,
`first_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`last_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`email` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`member` tinyint(1) NOT NULL DEFAULT '0',
`date_joined` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`is_verified` tinyint(1) NOT NULL DEFAULT '0',
`token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`phone_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`profile_pic` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'assets/images/pp/default.png',
`role` enum('user','admin','superadmin','') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'user',
`type` enum('google','credentials') COLLATE utf8mb4_general_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- --------------------------------------------------------
--
-- Table structure for table `visitor_logs`
--
DROP TABLE IF EXISTS `visitor_logs`;
CREATE TABLE `visitor_logs` (
`id` int NOT NULL,
`ip_address` varchar(45) NOT NULL,
`page_url` text NOT NULL,
`referrer_url` text,
`visit_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`user_id` int DEFAULT NULL,
`country` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `bar_items`
--
ALTER TABLE `bar_items`
ADD PRIMARY KEY (`item_id`);
--
-- Indexes for table `bar_tabs`
--
ALTER TABLE `bar_tabs`
ADD PRIMARY KEY (`tab_id`);
--
-- Indexes for table `bar_transactions`
--
ALTER TABLE `bar_transactions`
ADD PRIMARY KEY (`transaction_id`);
--
-- Indexes for table `blacklist`
--
ALTER TABLE `blacklist`
ADD PRIMARY KEY (`blacklist_id`);
--
-- Indexes for table `blogs`
--
ALTER TABLE `blogs`
ADD PRIMARY KEY (`blog_id`);
--
-- Indexes for table `bookings`
--
ALTER TABLE `bookings`
ADD PRIMARY KEY (`booking_id`),
ADD KEY `user_id` (`user_id`);
--
-- Indexes for table `campsites`
--
ALTER TABLE `campsites`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `comments`
--
ALTER TABLE `comments`
ADD PRIMARY KEY (`comment_id`);
--
-- Indexes for table `courses`
--
ALTER TABLE `courses`
ADD PRIMARY KEY (`course_id`);
--
-- Indexes for table `efts`
--
ALTER TABLE `efts`
ADD PRIMARY KEY (`eft_id`);
--
-- Indexes for table `events`
--
ALTER TABLE `events`
ADD PRIMARY KEY (`event_id`);
--
-- Indexes for table `legacy_members`
--
ALTER TABLE `legacy_members`
ADD PRIMARY KEY (`legacy_id`);
--
-- Indexes for table `membership_application`
--
ALTER TABLE `membership_application`
ADD PRIMARY KEY (`application_id`);
--
-- Indexes for table `membership_fees`
--
ALTER TABLE `membership_fees`
ADD PRIMARY KEY (`fee_id`);
--
-- Indexes for table `password_resets`
--
ALTER TABLE `password_resets`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `token` (`token`),
ADD KEY `user_id` (`user_id`);
--
-- Indexes for table `payments`
--
ALTER TABLE `payments`
ADD PRIMARY KEY (`payment_id`);
--
-- Indexes for table `prices`
--
ALTER TABLE `prices`
ADD PRIMARY KEY (`price_id`);
--
-- Indexes for table `trips`
--
ALTER TABLE `trips`
ADD PRIMARY KEY (`trip_id`);
--
-- Indexes for table `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`user_id`);
--
-- Indexes for table `visitor_logs`
--
ALTER TABLE `visitor_logs`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `bar_items`
--
ALTER TABLE `bar_items`
MODIFY `item_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `bar_tabs`
--
ALTER TABLE `bar_tabs`
MODIFY `tab_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `bar_transactions`
--
ALTER TABLE `bar_transactions`
MODIFY `transaction_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `blacklist`
--
ALTER TABLE `blacklist`
MODIFY `blacklist_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `blogs`
--
ALTER TABLE `blogs`
MODIFY `blog_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `bookings`
--
ALTER TABLE `bookings`
MODIFY `booking_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `campsites`
--
ALTER TABLE `campsites`
MODIFY `id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `comments`
--
ALTER TABLE `comments`
MODIFY `comment_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `courses`
--
ALTER TABLE `courses`
MODIFY `course_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `events`
--
ALTER TABLE `events`
MODIFY `event_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `membership_application`
--
ALTER TABLE `membership_application`
MODIFY `application_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `membership_fees`
--
ALTER TABLE `membership_fees`
MODIFY `fee_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `password_resets`
--
ALTER TABLE `password_resets`
MODIFY `id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `prices`
--
ALTER TABLE `prices`
MODIFY `price_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `trips`
--
ALTER TABLE `trips`
MODIFY `trip_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
MODIFY `user_id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `visitor_logs`
--
ALTER TABLE `visitor_logs`
MODIFY `id` int NOT NULL AUTO_INCREMENT;
--
-- Constraints for dumped tables
--
--
-- Constraints for table `bookings`
--
ALTER TABLE `bookings`
ADD CONSTRAINT `bookings_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE;
--
-- Constraints for table `password_resets`
--
ALTER TABLE `password_resets`
ADD CONSTRAINT `password_resets_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Some files were not shown because too many files have changed in this diff Show More