Compare commits
1 Commits
feature/ik
...
feature/bl
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d078cb954 |
5
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.env
|
||||
/vendor/
|
||||
.htaccess
|
||||
/assets/uploads/gallery/
|
||||
/assets/uploads/
|
||||
/uploads/
|
||||
|
||||
/uploads/pop/
|
||||
158
.htaccess
@@ -1,161 +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_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_efts$ src/admin/admin_efts.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]
|
||||
|
||||
# === 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 ^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/pages/blog/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_value error_reporting -1
|
||||
RedirectMatch 403 ^/\.well-known
|
||||
|
||||
215
.htaccess copy
@@ -1,215 +0,0 @@
|
||||
# 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]
|
||||
|
||||
# === ADMIN PAGES ===
|
||||
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_efts$ src/admin/admin_efts.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]
|
||||
|
||||
# === 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 ^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/pages/blog/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_value error_reporting -1
|
||||
RedirectMatch 403 ^/\.well-known
|
||||
Options -Indexes
|
||||
|
||||
<FilesMatch "\.(env|sql|bak|zip|tar|gz|ini)$">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
|
||||
ErrorDocument 404 /404.php
|
||||
|
||||
<RequireAll>
|
||||
Require all granted
|
||||
Require not ip 4.222.252.98
|
||||
Require not ip 4.222.252.97
|
||||
</RequireAll>
|
||||
|
||||
<Files .env>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</Files>
|
||||
|
||||
|
||||
# ALL CUSTOM ENTRIES SHOULD GO ABOVE THIS LINE
|
||||
# BEGIN IWORX header
|
||||
# This file was created by InterWorx-CP
|
||||
# You may modify this file, but any changes made between
|
||||
# BEGIN IWORX and END IWORX tags may be lost on future
|
||||
# updates. Additionally, changes NOT made between these
|
||||
# tags will not be recognized in the SiteWorx interface.
|
||||
# END IWORX header
|
||||
|
||||
# BEGIN IWORX accesscontrol
|
||||
# END IWORX accesscontrol
|
||||
|
||||
# BEGIN IWORX errordocs
|
||||
# END IWORX errordocs
|
||||
|
||||
# BEGIN IWORX mimetypes
|
||||
# END IWORX mimetypes
|
||||
|
||||
# BEGIN IWORX handlers
|
||||
# END IWORX handlers
|
||||
|
||||
# BEGIN IWORX charset
|
||||
# END IWORX charset
|
||||
|
||||
# BEGIN IWORX redirects
|
||||
# END IWORX redirects
|
||||
|
||||
# BEGIN IWORX phpvars
|
||||
# END IWORX phpvars
|
||||
|
||||
# BEGIN IWORX dirindex
|
||||
# END IWORX dirindex
|
||||
|
||||
# BEGIN IWORX hotlink
|
||||
# END IWORX hotlink
|
||||
|
||||
# BEGIN IWORX passwordprotection
|
||||
# END IWORX passwordprotection
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
; memory_limit = 512M
|
||||
upload_max_filesize = 64M
|
||||
post_max_size = 64M
|
||||
max_execution_time = 120
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
$page_id = 'agm_minutes';
|
||||
?>
|
||||
|
||||
@@ -68,15 +65,28 @@ $page_id = 'agm_minutes';
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
.clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<?php
|
||||
$pageTitle = '2025 AGM Minutes';
|
||||
$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/2/agm.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">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 -->
|
||||
<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">
|
||||
<h6>Tags </h6>
|
||||
<div class="tag-coulds">
|
||||
<a href="blog">Reports</a>
|
||||
<a href="blog.php">Reports</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -255,4 +265,4 @@ $page_id = 'agm_minutes';
|
||||
<!-- Blog Detaisl Area end -->
|
||||
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,6 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
?>
|
||||
|
||||
|
||||
@@ -43,4 +41,4 @@ include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
|
||||
</section>
|
||||
<!-- 404 Error Area end -->
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,8 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
// Determine the correct path to header.php based on file location
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
?>
|
||||
|
||||
<style>
|
||||
@@ -41,11 +37,31 @@ include_once($rootPath . '/header.php');
|
||||
}
|
||||
|
||||
</style>
|
||||
<!-- Page Banner Start -->
|
||||
<?php
|
||||
$pageTitle = 'About';
|
||||
$breadcrumbs = [['Home' => 'index.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; ?>');">
|
||||
<!-- 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 -->
|
||||
<section class="benefit-area mt-100 rel z-1">
|
||||
<div class="container">
|
||||
@@ -103,12 +119,8 @@ require_once($rootPath . '/components/banner.php');
|
||||
</div>
|
||||
<div class="gallery-slider-active">
|
||||
<?php
|
||||
$folder = $rootPath . '/assets/images/opendays/';
|
||||
$folder = 'assets/images/opendays/';
|
||||
$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($images);
|
||||
@@ -198,12 +210,8 @@ require_once($rootPath . '/components/banner.php');
|
||||
</div>
|
||||
</div>
|
||||
<div class="gallery-slider-active"><?php
|
||||
$folder = $rootPath . '/assets/images/memories/';
|
||||
$folder = 'assets/images/memories/';
|
||||
$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($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);">
|
||||
<span class="category">Extended Trips</span>
|
||||
<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>
|
||||
<i class="fal fa-arrow-right"></i>
|
||||
</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);">
|
||||
<span class="category">Driver Training</span>
|
||||
<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>
|
||||
<i class="fal fa-arrow-right"></i>
|
||||
</a>
|
||||
@@ -258,7 +266,7 @@ require_once($rootPath . '/components/banner.php');
|
||||
<div class="cta-item" style="background-image: url(assets/images/base4/camping.jpg);">
|
||||
<span class="category">Events</span>
|
||||
<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>
|
||||
<i class="fal fa-arrow-right"></i>
|
||||
</a>
|
||||
@@ -281,4 +289,4 @@ require_once($rootPath . '/components/banner.php');
|
||||
<!-- Blog Area end -->
|
||||
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
|
||||
include_once('header02.php');
|
||||
|
||||
// Assuming you have the user ID stored in the session
|
||||
$user_id = $_SESSION['user_id'];
|
||||
@@ -60,7 +59,7 @@ $user = $result->fetch_assoc();
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-12">
|
||||
<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">
|
||||
<h2>Account Settings</h2>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<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 -->
|
||||
<form id="changePasswordForm" name="changePasswordForm" action="change_password" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
|
||||
<form id="changePasswordForm" name="changePasswordForm" action="change_password.php" method="post">
|
||||
<div class="col-md-12 mt-20">
|
||||
<h4>Change Password</h4>
|
||||
<div id="responseMessage2"></div> <!-- Message display area -->
|
||||
@@ -163,27 +160,29 @@ $user = $result->fetch_assoc();
|
||||
formData.append('profile_picture', $('#profile_picture')[0].files[0]);
|
||||
|
||||
$.ajax({
|
||||
url: 'upload_profile_picture',
|
||||
url: 'upload_profile_picture.php',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
// Parse response if needed
|
||||
if (typeof response === "string") {
|
||||
response = JSON.parse(response);
|
||||
}
|
||||
|
||||
if (response.status === 'success') {
|
||||
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
|
||||
// Reload the current page after a short delay
|
||||
setTimeout(function() {
|
||||
// Update the profile picture source with cache-busting query string
|
||||
// Reload the current page
|
||||
window.location.reload();
|
||||
}, 1500);
|
||||
|
||||
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
|
||||
} else {
|
||||
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log('AJAX Error:', status, error);
|
||||
console.log('Response Text:', xhr.responseText);
|
||||
$('#responseMessage').html('<div class="alert alert-danger">Error uploading profile picture: ' + error + '</div>');
|
||||
error: function() {
|
||||
$('#responseMessage').html('<div class="alert alert-danger">Error uploading profile picture.</div>');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -194,7 +193,7 @@ $user = $result->fetch_assoc();
|
||||
event.preventDefault(); // Prevent default form submission
|
||||
|
||||
$.ajax({
|
||||
url: 'update_user',
|
||||
url: 'update_user.php',
|
||||
type: 'POST',
|
||||
data: $(this).serialize(),
|
||||
success: function(response) {
|
||||
@@ -221,7 +220,7 @@ $user = $result->fetch_assoc();
|
||||
event.preventDefault(); // Prevent default form submission
|
||||
|
||||
$.ajax({
|
||||
url: 'change_password',
|
||||
url: 'change_password.php',
|
||||
type: 'POST',
|
||||
data: $(this).serialize(),
|
||||
success: function(response) {
|
||||
@@ -244,4 +243,4 @@ $user = $result->fetch_assoc();
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
55
add_campsite.php
Normal 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");
|
||||
?>
|
||||
@@ -1,16 +1,7 @@
|
||||
<?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");
|
||||
<?php include_once('header02.php');
|
||||
checkAdmin();
|
||||
checkUserSession();
|
||||
|
||||
$pageTitle = 'Manage Blog Posts';
|
||||
$breadcrumbs = [['Home' => 'index']];
|
||||
require_once($rootPath . '/components/banner.php');
|
||||
|
||||
$result = $conn->prepare("
|
||||
SELECT
|
||||
b.blog_id,
|
||||
@@ -24,7 +15,7 @@ $result = $conn->prepare("
|
||||
u.profile_pic
|
||||
FROM blogs b
|
||||
JOIN users u ON b.author = u.user_id
|
||||
WHERE b.status = 'published'
|
||||
WHERE b.status != 'deleted'
|
||||
ORDER BY b.date DESC
|
||||
");
|
||||
|
||||
@@ -68,7 +59,29 @@ $posts = $result->get_result();
|
||||
$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">
|
||||
@@ -76,7 +89,7 @@ $bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
||||
<h2>Manage Blog Posts</h2>
|
||||
<h2>My Posts</h2>
|
||||
<?php if (isset($_SESSION['message'])): ?>
|
||||
<div class="alert alert-warning message-box">
|
||||
<?php echo $_SESSION['message']; ?>
|
||||
@@ -87,13 +100,11 @@ $bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
|
||||
<a href="blog_create.php">+ New Post</a>
|
||||
|
||||
<?php while ($post = $posts->fetch_assoc()):
|
||||
// Determine cover image - use provided image or fallback placeholder
|
||||
$coverImage = $post["image"] ? $post["image"] : 'assets/images/placeholder.jpg';
|
||||
// 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="' . htmlspecialchars($coverImage) . '" alt="' . htmlspecialchars($post["title"]) . '">
|
||||
<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">
|
||||
@@ -106,11 +117,10 @@ $bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
|
||||
</div>
|
||||
<p>' . $post["description"] . '</p>
|
||||
<div class="destination-footer">
|
||||
<div class="btn-group" style="display:flex; justify-content:flex-end; gap:10px;">
|
||||
<a href="blog_edit.php?token=' . encryptData($post["blog_id"], $salt) . '" data-bs-toggle="tooltip" data-bs-placement="top" title="Edit"><span class="material-icons">edit</span></a>
|
||||
<a href="blog_read.php?token=' . encryptData($post["blog_id"], $salt) . '" data-bs-toggle="tooltip" data-bs-placement="top" title="Preview"><span class="material-icons">visibility</span></a>
|
||||
<button type="button" class="publish-btn" data-blog-id="' . $post["blog_id"] . '" data-status="' . $post["status"] . '" data-bs-toggle="tooltip" data-bs-placement="top" title="' . ($post["status"] == "published" ? "Unpublish" : "Publish") . '" style="background:none; border:none; cursor:pointer; color:inherit;"><span class="material-icons">' . ($post["status"] == "published" ? "cloud_off" : "cloud_upload") . '</span></button>
|
||||
<a href="blog_delete.php?token=' . encryptData($post["blog_id"], $salt) . '" data-bs-toggle="tooltip" data-bs-placement="top" title="Delete"><span class="material-icons">delete</span></a>
|
||||
<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>
|
||||
@@ -128,38 +138,7 @@ $bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
|
||||
<script>
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
tooltipTriggerList.forEach(el => new bootstrap.Tooltip(el));
|
||||
|
||||
// Handle publish/unpublish button clicks
|
||||
document.querySelectorAll('.publish-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const blogId = this.dataset.blogId;
|
||||
const status = this.dataset.status;
|
||||
const action = status === 'published' ? 'unpublish' : 'publish';
|
||||
const endpoint = status === 'published' ? 'blog_unpublish' : 'publish_blog';
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('id', blogId);
|
||||
|
||||
fetch(endpoint, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
alert(action.charAt(0).toUpperCase() + action.slice(1) + ' successful!');
|
||||
location.reload();
|
||||
} else {
|
||||
alert(action + ' failed.');
|
||||
console.error('Error:', response.statusText);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error:', err);
|
||||
alert(action + ' failed due to network error.');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkAdmin();
|
||||
|
||||
?>
|
||||
@@ -224,4 +221,4 @@ if (!empty($bannerImages)) {
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,11 +1,8 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkAdmin();
|
||||
|
||||
// Fetch all trips
|
||||
$courseSql = "SELECT date, course_id, course_type FROM courses";
|
||||
$courseSql = "SELECT date, course_id, course_type FROM courses WHERE DATE(date) >= CURDATE()";
|
||||
|
||||
$courseResult = $conn->query($courseSql);
|
||||
if (!$courseResult) {
|
||||
@@ -244,4 +241,4 @@ if (!empty($bannerImages)) {
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkAdmin();
|
||||
|
||||
?>
|
||||
@@ -224,4 +221,4 @@ if (!empty($bannerImages)) {
|
||||
<!-- Tour List Area end -->
|
||||
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkAdmin();
|
||||
|
||||
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
|
||||
$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();
|
||||
// SQL query to fetch data
|
||||
$sql = "SELECT user_id, first_name, last_name, tel_cell, email, dob, accept_indemnity FROM membership_application";
|
||||
|
||||
$result = $conn->query($sql);
|
||||
?>
|
||||
<style>
|
||||
table {
|
||||
@@ -235,4 +232,4 @@ if (!empty($bannerImages)) {
|
||||
<!-- Tour List Area end -->
|
||||
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkAdmin();
|
||||
|
||||
?>
|
||||
@@ -208,4 +205,4 @@ if (!empty($bannerImages)) {
|
||||
<!-- Tour List Area end -->
|
||||
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkAdmin();
|
||||
|
||||
// Fetch all trips
|
||||
@@ -174,7 +171,7 @@ if (!empty($bannerImages)) {
|
||||
|
||||
// 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, u.profile_pic,
|
||||
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
|
||||
@@ -237,4 +234,4 @@ if (!empty($bannerImages)) {
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkAdmin();
|
||||
// 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";
|
||||
@@ -201,4 +198,4 @@ if (!empty($bannerImages)) {
|
||||
<!-- Tour List Area end -->
|
||||
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,8 +1,5 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
include_once($rootPath . '/header.php');
|
||||
checkAdmin();
|
||||
<?php include_once('header02.php');
|
||||
checkSuperAdmin();
|
||||
// SQL query to fetch data
|
||||
$sql = "SELECT user_id, first_name, last_name, email, member, date_joined, token, is_verified, profile_pic FROM users";
|
||||
$result = $conn->query($sql);
|
||||
@@ -256,7 +253,7 @@ if (!empty($bannerImages)) {
|
||||
const name = this.dataset.name;
|
||||
const token = this.dataset.token;
|
||||
|
||||
fetch('resend_verification', {
|
||||
fetch('resend_verification.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -281,4 +278,4 @@ if (!empty($bannerImages)) {
|
||||
</script>
|
||||
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,6 +1,12 @@
|
||||
@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
|
||||
----------------------
|
||||
@@ -7118,8 +7124,7 @@ blockquote {
|
||||
/* Comments */
|
||||
.comments {
|
||||
border-radius: 10px;
|
||||
/* border: 1px solid var(--border-color); */
|
||||
}
|
||||
border: 1px solid var(--border-color); }
|
||||
|
||||
.comment-body {
|
||||
padding: 50px; }
|
||||
|
||||
|
Before Width: | Height: | Size: 494 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 6.6 MiB |
|
Before Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 340 KiB |
|
Before Width: | Height: | Size: 352 KiB |
|
Before Width: | Height: | Size: 374 KiB |
|
Before Width: | Height: | Size: 482 KiB |
|
Before Width: | Height: | Size: 4.2 MiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 47 KiB |
@@ -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);
|
||||
}
|
||||
});
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
<div class="header-inner rel d-flex align-items-center">
|
||||
<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 class="nav-outer mx-lg-auto ps-xxl-5 clearfix">
|
||||
@@ -71,7 +71,7 @@
|
||||
<ul class="navigation clearfix">
|
||||
<li class="dropdown current"><a href="#">Home</a>
|
||||
<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="index3.html">Tour Package</a></li>
|
||||
</ul>
|
||||
@@ -161,7 +161,7 @@
|
||||
|
||||
<!--Appointment Form-->
|
||||
<div class="appointment-form">
|
||||
<form method="post" action="contact">
|
||||
<form method="post" action="contact.php">
|
||||
<div class="form-group">
|
||||
<input type="text" name="text" value="" placeholder="Name" required>
|
||||
</div>
|
||||
@@ -182,9 +182,9 @@
|
||||
|
||||
<!--Social Icons-->
|
||||
<div class="social-style-one">
|
||||
<a href="contact"><i class="fab fa-twitter"></i></a>
|
||||
<a href="contact"><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-twitter"></i></a>
|
||||
<a href="contact.php"><i class="fab fa-facebook-f"></i></a>
|
||||
<a href="contact.php"><i class="fab fa-instagram"></i></a>
|
||||
<a href="#"><i class="fab fa-pinterest-p"></i></a>
|
||||
</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>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
|
||||
<li class="breadcrumb-item"><a href="index">Home</a></li>
|
||||
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
|
||||
<li class="breadcrumb-item active">Tour Details</li>
|
||||
</ol>
|
||||
</nav>
|
||||
@@ -795,7 +795,7 @@
|
||||
<i class="fal fa-arrow-right"></i>
|
||||
</button>
|
||||
<div class="text-center">
|
||||
<a href="contact">Need some help?</a>
|
||||
<a href="contact.php">Need some help?</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -871,7 +871,7 @@
|
||||
<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-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 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>
|
||||
@@ -899,7 +899,7 @@
|
||||
<ul class="list-style-three">
|
||||
<li><a href="about.html">About Company</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>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -937,7 +937,7 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<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 class="col-lg-7 text-center text-lg-end">
|
||||
|
||||
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.7 MiB |
101
autosave.php
Normal 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;
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkUserSession();
|
||||
$user_id = $_SESSION['user_id'];
|
||||
unset($_SESSION['cart']);
|
||||
@@ -157,7 +155,6 @@ unset($_SESSION['cart']);
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="barTabForm">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
|
||||
<div class="form-group">
|
||||
<label for="userSelect">Select User</label>
|
||||
<input type="text" id="userSelect" class="form-control" placeholder="Search User" required>
|
||||
@@ -176,7 +173,7 @@ unset($_SESSION['cart']);
|
||||
$('#userSelect').autocomplete({
|
||||
source: function(request, response) {
|
||||
$.ajax({
|
||||
url: 'fetch_users',
|
||||
url: 'fetch_users.php',
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
@@ -215,7 +212,7 @@ unset($_SESSION['cart']);
|
||||
$('#barTabForm').submit(function(e) {
|
||||
e.preventDefault(); // Prevent default form submission
|
||||
$.ajax({
|
||||
url: 'create_bar_tab',
|
||||
url: 'create_bar_tab.php',
|
||||
method: 'POST',
|
||||
data: $(this).serialize(), // Send form data, including user_id
|
||||
dataType: 'json',
|
||||
@@ -239,7 +236,7 @@ unset($_SESSION['cart']);
|
||||
// Fetch and render bar tabs
|
||||
function loadBarTabs() {
|
||||
$.ajax({
|
||||
url: 'fetch_bar_tabs',
|
||||
url: 'fetch_bar_tabs.php',
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
@@ -289,7 +286,7 @@ unset($_SESSION['cart']);
|
||||
|
||||
// Fetch available drinks for the selected tab
|
||||
$.ajax({
|
||||
url: 'fetch_drinks',
|
||||
url: 'fetch_drinks.php',
|
||||
method: 'GET',
|
||||
data: {
|
||||
tab_id: tabId
|
||||
@@ -346,7 +343,7 @@ unset($_SESSION['cart']);
|
||||
|
||||
// Add the drink to the cart (session)
|
||||
$.ajax({
|
||||
url: 'add_to_cart',
|
||||
url: 'add_to_cart.php',
|
||||
method: 'POST',
|
||||
data: {
|
||||
tab_id: tabId,
|
||||
@@ -414,7 +411,7 @@ unset($_SESSION['cart']);
|
||||
|
||||
// Submit the order
|
||||
$.ajax({
|
||||
url: 'submit_order',
|
||||
url: 'submit_order.php',
|
||||
method: 'POST',
|
||||
data: {
|
||||
tab_id: tabId
|
||||
@@ -455,7 +452,7 @@ unset($_SESSION['cart']);
|
||||
function fetchTabTotal(tabId) {
|
||||
console.log("fetching tab total...")
|
||||
$.ajax({
|
||||
url: 'get_tab_total',
|
||||
url: 'get_tab_total.php',
|
||||
method: 'POST',
|
||||
data: {
|
||||
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"); ?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
// Assuming you have the user ID stored in the session
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
$user_id = $_SESSION['user_id'];
|
||||
@@ -39,11 +36,33 @@ if (isset($_SESSION['user_id'])) {
|
||||
|
||||
}
|
||||
</style>
|
||||
<!-- Page Banner Start -->
|
||||
<?php
|
||||
$pageTitle = 'Indemnity';
|
||||
$breadcrumbs = [['Home' => 'index.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">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 -->
|
||||
|
||||
|
||||
@@ -94,7 +113,7 @@ if (isset($_SESSION['user_id'])) {
|
||||
var dataUrl = signaturePad.toDataURL(); // Get signature as base64 image
|
||||
|
||||
$.ajax({
|
||||
url: 'process_signature',
|
||||
url: 'process_signature.php',
|
||||
type: 'POST',
|
||||
data: {
|
||||
signature: dataUrl // Send the base64 signature image
|
||||
@@ -130,4 +149,4 @@ if (isset($_SESSION['user_id'])) {
|
||||
</script>
|
||||
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>
|
||||
<?php include_once('insta_footer.php') ?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
$page_id = 'best_0f_ec';
|
||||
?>
|
||||
|
||||
@@ -68,15 +65,31 @@ $page_id = 'best_0f_ec';
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
.clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
</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 -->
|
||||
<section class="blog-detaisl-page py-100 rel z-1">
|
||||
@@ -337,10 +350,10 @@ $page_id = 'best_0f_ec';
|
||||
<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>
|
||||
<div class="social-icons">
|
||||
<a href="contact"><i class="fab fa-facebook-f"></i></a>
|
||||
<a href="contact"><i class="fab fa-twitter"></i></a>
|
||||
<a href="contact"><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-facebook-f"></i></a>
|
||||
<a href="contact.php"><i class="fab fa-twitter"></i></a>
|
||||
<a href="contact.php"><i class="fab fa-linkedin-in"></i></a>
|
||||
<a href="contact.php"><i class="fab fa-instagram"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -432,4 +445,4 @@ $page_id = 'best_0f_ec';
|
||||
<!-- Blog Detaisl Area end -->
|
||||
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,8 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
?>
|
||||
<?php include_once('header02.php') ?>
|
||||
|
||||
<style>
|
||||
.image {
|
||||
@@ -30,11 +26,35 @@ include_once($rootPath . '/header.php');
|
||||
|
||||
|
||||
}
|
||||
</style><?php
|
||||
$pageTitle = 'Blogs';
|
||||
$breadcrumbs = [['Home' => 'index.php']];
|
||||
require_once($rootPath . '/components/banner.php');
|
||||
</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">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 -->
|
||||
@@ -43,12 +63,9 @@ include_once($rootPath . '/header.php');
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<?php
|
||||
// Query to retrieve data from blogs table
|
||||
$status = 'published';
|
||||
$stmt = $conn->prepare("SELECT blog_id, title, date, category, image, description, author, members_only, link FROM blogs WHERE status = ? ORDER BY date DESC");
|
||||
$stmt->bind_param("s", $status);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
// Query to retrieve data from the trips table
|
||||
$sql = "SELECT blog_id, title, date, category, image, description, author, members_only, link FROM blogs WHERE status = 'published' ORDER BY date DESC";
|
||||
$result = $conn->query($sql);
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
// Loop through each row
|
||||
@@ -69,7 +86,7 @@ include_once($rootPath . '/header.php');
|
||||
$icon = "fa-lock";
|
||||
} else {
|
||||
if (getUserMemberStatus($_SESSION['user_id'])) {
|
||||
$blog_link = $row['link'];
|
||||
$blog_link = "blog_read.php?token=".encryptData($blog_id, $salt);
|
||||
$button_hover = "Read More";
|
||||
$icon = "fa-arrow-right";
|
||||
} else {
|
||||
@@ -79,7 +96,7 @@ include_once($rootPath . '/header.php');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$blog_link = $row['link'];
|
||||
$blog_link = "blog_read.php?token=".encryptData($blog_id, $salt);
|
||||
$button_hover = "Read More";
|
||||
$icon = "fa-arrow-right";
|
||||
}
|
||||
@@ -88,10 +105,10 @@ include_once($rootPath . '/header.php');
|
||||
echo '
|
||||
<div class="blog-item style-three" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
|
||||
<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 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>
|
||||
<ul class="blog-meta">
|
||||
<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">
|
||||
<span class="h6">Explore The World</span>
|
||||
<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>
|
||||
<i class="fal fa-arrow-right"></i>
|
||||
</a>
|
||||
@@ -225,4 +242,4 @@ include_once($rootPath . '/header.php');
|
||||
<!-- Blog List Area end -->
|
||||
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,9 +1,9 @@
|
||||
<?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");
|
||||
session_start();
|
||||
// session_start();
|
||||
require_once("env.php");
|
||||
require_once("session.php");
|
||||
require_once("connection.php");
|
||||
require_once("functions.php");
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
die("Not logged in");
|
||||
@@ -1,9 +1,8 @@
|
||||
<?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");
|
||||
session_start();
|
||||
require_once("env.php");
|
||||
require_once("session.php");
|
||||
require_once("connection.php");
|
||||
require_once("functions.php");
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
$_SESSION['message'] = "Not authorized.";
|
||||
@@ -1,8 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
?>
|
||||
<?php include_once('header02.php') ?>
|
||||
|
||||
<style>
|
||||
.image {
|
||||
@@ -33,11 +29,20 @@ include_once($rootPath . '/header.php');
|
||||
|
||||
|
||||
|
||||
<?php
|
||||
$pageTitle = 'Blog Details';
|
||||
$breadcrumbs = [['Home' => 'index.php'], ['Blogs' => 'blog.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="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 -->
|
||||
@@ -193,10 +198,10 @@ include_once($rootPath . '/header.php');
|
||||
<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>
|
||||
<div class="social-icons">
|
||||
<a href="contact"><i class="fab fa-facebook-f"></i></a>
|
||||
<a href="contact"><i class="fab fa-twitter"></i></a>
|
||||
<a href="contact"><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-facebook-f"></i></a>
|
||||
<a href="contact.php"><i class="fab fa-twitter"></i></a>
|
||||
<a href="contact.php"><i class="fab fa-linkedin-in"></i></a>
|
||||
<a href="contact.php"><i class="fab fa-instagram"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -444,7 +449,7 @@ include_once($rootPath . '/header.php');
|
||||
<ul class="list-style-three">
|
||||
<li><a href="about.html">About Company</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>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -1,19 +1,11 @@
|
||||
<?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");
|
||||
include_once('header02.php');
|
||||
|
||||
// Ensure the user is logged in
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
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'];
|
||||
// Sanitize the trip_id to prevent SQL injection
|
||||
$blog_id = intval(decryptData($token, $salt)); // Ensures $trip_id is treated as an integer
|
||||
@@ -39,7 +31,7 @@ $stmt->close();
|
||||
selector: '#content',
|
||||
plugins: 'image code 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: [
|
||||
{ title: 'None', value: '' },
|
||||
{ title: 'Left Align', value: 'img-left' },
|
||||
@@ -184,7 +176,7 @@ $stmt->close();
|
||||
formData.append("cover_image", coverImageInput.files[0]);
|
||||
}
|
||||
|
||||
return fetch("autosave", {
|
||||
return fetch("autosave.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
}).then(response => {
|
||||
@@ -192,15 +184,12 @@ $stmt->close();
|
||||
document.getElementById("autosave-status").innerText = "Draft autosaved at " + new Date().toLocaleTimeString();
|
||||
return true;
|
||||
} else {
|
||||
return response.text().then(errorText => {
|
||||
document.getElementById("autosave-status").innerText = "Autosave failed: " + errorText;
|
||||
console.error("Autosave failed", response.status, errorText);
|
||||
document.getElementById("autosave-status").innerText = "Autosave failed";
|
||||
console.error("Autosave failed", response.statusText);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error("Autosave error:", err);
|
||||
document.getElementById("autosave-status").innerText = "Autosave error: " + err.message;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -209,15 +198,10 @@ $stmt->close();
|
||||
setInterval(autosavePost, 15000);
|
||||
|
||||
// Manual autosave button
|
||||
const manualSaveBtn = document.getElementById("manualSaveBtn");
|
||||
if (manualSaveBtn) {
|
||||
manualSaveBtn.addEventListener("click", autosavePost);
|
||||
}
|
||||
document.getElementById("manualSaveBtn").addEventListener("click", autosavePost);
|
||||
|
||||
// Manual publish button
|
||||
const manualPostBtn = document.getElementById("manualPostBtn");
|
||||
if (manualPostBtn) {
|
||||
manualPostBtn.addEventListener("click", function () {
|
||||
document.getElementById("manualPostBtn").addEventListener("click", function () {
|
||||
autosavePost().then(success => {
|
||||
if (!success) return;
|
||||
|
||||
@@ -225,7 +209,7 @@ $stmt->close();
|
||||
const publishData = new FormData();
|
||||
publishData.append("id", articleId);
|
||||
|
||||
fetch("publish_blog", {
|
||||
fetch("publish_blog.php", {
|
||||
method: "POST",
|
||||
body: publishData
|
||||
}).then(response => {
|
||||
@@ -243,12 +227,9 @@ $stmt->close();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Manual unpublish button
|
||||
const manualDraftBtn = document.getElementById("manualDraftBtn");
|
||||
if (manualDraftBtn) {
|
||||
manualDraftBtn.addEventListener("click", function () {
|
||||
document.getElementById("manualDraftBtn").addEventListener("click", function () {
|
||||
autosavePost().then(success => {
|
||||
if (!success) return;
|
||||
|
||||
@@ -256,7 +237,7 @@ $stmt->close();
|
||||
const publishData = new FormData();
|
||||
publishData.append("id", articleId);
|
||||
|
||||
fetch("blog_unpublish", {
|
||||
fetch("blog_unpublish.php", {
|
||||
method: "POST",
|
||||
body: publishData
|
||||
}).then(response => {
|
||||
@@ -274,9 +255,11 @@ $stmt->close();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</script>
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
|
||||
|
||||
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,23 +1,17 @@
|
||||
<?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");
|
||||
<?php include_once('header02.php');
|
||||
|
||||
$token = $_GET['token'];
|
||||
// Sanitize the trip_id to prevent SQL injection
|
||||
$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;
|
||||
echo getCommentCount($page_id);
|
||||
|
||||
|
||||
$stmt = $conn->prepare("
|
||||
SELECT a.blog_id, a.title, a.category, a.description, a.content, a.date, a.author,
|
||||
u.first_name, u.last_name, u.user_id
|
||||
SELECT a.title, a.category, a.description, a.content, a.date,
|
||||
u.first_name, u.last_name
|
||||
FROM blogs a
|
||||
JOIN users u ON a.author = u.user_id
|
||||
WHERE a.blog_id = ?
|
||||
@@ -32,8 +26,6 @@ if ($result->num_rows === 0) {
|
||||
|
||||
$row = $result->fetch_assoc();
|
||||
$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>
|
||||
|
||||
<?php
|
||||
// Dynamically set page title to blog title
|
||||
if (isset($row) && !empty($row['title'])) {
|
||||
$pageTitle = htmlspecialchars($row['title']);
|
||||
} else {
|
||||
$pageTitle = 'Blog Post';
|
||||
}
|
||||
$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"><?= 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 -->
|
||||
<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><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>
|
||||
<?php if ($is_author): ?>
|
||||
<li><a href="blog_edit.php?token=<?php echo encryptData($blog_id, $salt); ?>">Edit Post</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
<?= $row['content'] ?>
|
||||
</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">
|
||||
<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">
|
||||
<h6>Tags </h6>
|
||||
<div class="tag-coulds">
|
||||
@@ -173,9 +148,29 @@ if (isset($row) && !empty($row['title'])) {
|
||||
</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>
|
||||
</section>
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
31
blog_unpublish.php
Normal 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;
|
||||
}
|
||||
?>
|
||||
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkUserSession();
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
@@ -60,10 +57,28 @@ $user_id = $_SESSION['user_id'];
|
||||
</style>
|
||||
</style>
|
||||
<?php
|
||||
$pageTitle = 'My Bookings';
|
||||
$breadcrumbs = [['Home' => 'index.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 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 -->
|
||||
<section class="tour-list-page py-100 rel z-1">
|
||||
@@ -114,7 +129,6 @@ $user_id = $_SESSION['user_id'];
|
||||
// Loop through each row
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$booking_id = $row['booking_id'];
|
||||
$payment_id = $row['payment_id'];
|
||||
$booking_type = $row['booking_type'];
|
||||
$from_date = $row['from_date'];
|
||||
$to_date = $row['to_date'];
|
||||
@@ -268,8 +282,8 @@ $user_id = $_SESSION['user_id'];
|
||||
<div class="destination-footer">
|
||||
<span class="price"><span>Booking Total: R ' . number_format($amount, 2) . '</span></span>';
|
||||
if ($status == "AWAITING PAYMENT") {
|
||||
echo '<a href="' . getPaymentLinkByPaymentId($payment_id) . '" class="theme-btn style-two style-three">
|
||||
<span data-hover="PAY NOW">' . $status . '</span>
|
||||
echo '<a href="payment_confirmation.php?token=' . encryptData($booking_id, $salt) . '" class="theme-btn style-two style-three">
|
||||
<span data-hover="PAYMENT INFO">' . $status . '</span>
|
||||
</a>';
|
||||
} else {
|
||||
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"); ?>
|
||||
@@ -1,15 +1,9 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkUserSession();
|
||||
|
||||
// SQL query to fetch dates for bush mechanics
|
||||
$stmt = $conn->prepare("SELECT course_id, date FROM courses WHERE course_type = ? AND date >= CURDATE()");
|
||||
$course_type = 'bush_mechanics';
|
||||
$stmt->bind_param("s", $course_type);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
// SQL query to fetch dates for driver training
|
||||
$sql = "SELECT course_id, date FROM courses WHERE course_type = 'bush_mechanics'";
|
||||
$result = $conn->query($sql);
|
||||
$page_id = 'bush_mechanics';
|
||||
?>
|
||||
|
||||
@@ -24,11 +18,32 @@ $page_id = 'bush_mechanics';
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
</style><?php
|
||||
$pageTitle = 'Bush Mechanics';
|
||||
$breadcrumbs = [['Home' => 'index.php']];
|
||||
require_once($rootPath . '/components/banner.php');
|
||||
</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">
|
||||
<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 -->
|
||||
<section class="product-details pt-100">
|
||||
@@ -79,7 +94,7 @@ $page_id = 'bush_mechanics';
|
||||
<hr class="mt-40">
|
||||
<div class="blog-sidebar tour-sidebar">
|
||||
<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">
|
||||
<li>
|
||||
Select Date
|
||||
@@ -99,7 +114,7 @@ $page_id = 'bush_mechanics';
|
||||
</select>
|
||||
</li>
|
||||
<?php
|
||||
if ($is_member || $pending_member) {
|
||||
if ($is_member) {
|
||||
echo '
|
||||
<li>
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
|
||||
<?php
|
||||
$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>
|
||||
<button type="submit" class="theme-btn style-two w-100 mt-15 mb-5">
|
||||
<span data-hover="Book Now">Book Now</span>
|
||||
<i class="fal fa-arrow-right"></i>
|
||||
</button>
|
||||
<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>
|
||||
<img src="assets/images/logos/ikhokha.png"alt="Secure Payment Badges" style="max-width: 200px; display: block; margin: 10px auto 0;">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -347,12 +352,11 @@ $page_id = 'bush_mechanics';
|
||||
// Function to calculate booking total
|
||||
function calculateTotal() {
|
||||
// Get selected values from the form
|
||||
var additional_members = parseInt($('#members').val()) || 0; // Default to 0 if not selected
|
||||
var additional_nonmembers = parseInt($('#non-members').val()) || 0; // Default to 0 if not selected
|
||||
var members = parseInt($('#members').val()) || 0; // Default to 1 vehicle if not selected
|
||||
var nonmembers = parseInt($('#non-members').val()) || 0; // Default to 1 adult if not selected
|
||||
|
||||
// Fetch PHP variables
|
||||
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_nonmembers = <?= getPrice('bush_mechanics', 'nonmember');?>;
|
||||
|
||||
@@ -360,11 +364,11 @@ $page_id = 'bush_mechanics';
|
||||
var total = 0;
|
||||
|
||||
// Calculate cost for members
|
||||
if (isMember || pendingMember) {
|
||||
total = (cost_members) + (additional_members * cost_members) + (additional_nonmembers * cost_nonmembers);
|
||||
if (isMember) {
|
||||
total = (cost_members) + (members * cost_members) + (nonmembers * cost_nonmembers);
|
||||
} else {
|
||||
// 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
|
||||
@@ -383,4 +387,4 @@ $page_id = 'bush_mechanics';
|
||||
</script>
|
||||
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>
|
||||
<?php include_once('insta_footer.php') ?>
|
||||
@@ -1,6 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
checkUserSession();
|
||||
?>
|
||||
|
||||
@@ -78,7 +76,7 @@ checkUserSession();
|
||||
</div>
|
||||
<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>
|
||||
<form action="process_camp_booking" method="POST">
|
||||
<form action="process_camp_booking.php" method="POST">
|
||||
<div class="date mb-25">
|
||||
<b>From Date</b>
|
||||
<input type="date" id="from_date" name="from_date">
|
||||
@@ -125,7 +123,6 @@ checkUserSession();
|
||||
<?php endif ?>
|
||||
|
||||
<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">
|
||||
<span data-hover="Book Now">Book Now</span>
|
||||
<i class="fal fa-arrow-right"></i>
|
||||
@@ -214,4 +211,4 @@ checkUserSession();
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>
|
||||
<?php include_once('insta_footer.php') ?>
|
||||
208
campsites.php
Normal 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"); ?>
|
||||
@@ -1,9 +1,8 @@
|
||||
<?php
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
require_once($rootPath . '/src/config/env.php');
|
||||
require_once($rootPath . '/src/config/session.php');
|
||||
require_once($rootPath . '/src/config/connection.php');
|
||||
require_once($rootPath . '/src/config/functions.php');
|
||||
require_once("env.php");
|
||||
require_once("session.php");
|
||||
require_once("connection.php");
|
||||
require_once("functions.php");
|
||||
|
||||
$response = array('status' => 'error', 'message' => 'Something went wrong');
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -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
@@ -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>
|
||||
@@ -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
@@ -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');
|
||||
@@ -1,8 +1,4 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
?>
|
||||
<?php include_once('header02.php') ?>
|
||||
|
||||
<style>
|
||||
.image {
|
||||
@@ -28,11 +24,32 @@ include_once($rootPath . '/header.php');
|
||||
display: block;
|
||||
/* Prevents inline whitespace issues */
|
||||
}
|
||||
</style><?php
|
||||
$pageTitle = 'Contact Us';
|
||||
$breadcrumbs = [['Home' => 'index.php']];
|
||||
require_once($rootPath . '/components/banner.php');
|
||||
</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">
|
||||
<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 -->
|
||||
@@ -110,4 +127,4 @@ include_once($rootPath . '/header.php');
|
||||
<!-- Contact Map End -->
|
||||
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>
|
||||
<?php include_once("insta_footer.php"); ?>
|
||||
@@ -1,23 +1,26 @@
|
||||
<?php
|
||||
$headerStyle = 'light';
|
||||
$rootPath = dirname(dirname(dirname(__DIR__)));
|
||||
include_once($rootPath . '/header.php');
|
||||
<?php include_once('header02.php');
|
||||
|
||||
// SQL query to fetch dates for driver training
|
||||
$stmt = $conn->prepare("SELECT course_id, date FROM courses WHERE course_type = ?");
|
||||
$course_type = 'driver_training';
|
||||
$stmt->bind_param("s", $course_type);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$sql = "SELECT course_id, date FROM courses WHERE course_type = 'driver_training'";
|
||||
$result = $conn->query($sql);
|
||||
?>
|
||||
|
||||
|
||||
|
||||
<?php
|
||||
$pageTitle = 'Course Details';
|
||||
$breadcrumbs = [['Home' => 'index.php']];
|
||||
require_once($rootPath . '/components/banner.php');
|
||||
?>
|
||||
<!-- Page Banner Start -->
|
||||
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url(assets/images/banner/banner.jpg);">
|
||||
<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">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 -->
|
||||
|
||||
<!-- 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>
|
||||
<hr class="mt-40">
|
||||
<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>
|
||||
<select name="course_date" id="course_date" required>
|
||||
<!-- <option value="" disabled selected>-- Select a Date --</option> -->
|
||||
@@ -300,4 +302,4 @@ $result = $stmt->get_result();
|
||||
<!-- Shop Details Area end -->
|
||||
|
||||
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>
|
||||
<?php include_once('insta_footer.php') ?>
|
||||
@@ -1,54 +1,39 @@
|
||||
<?php
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
require_once($rootPath . "/src/config/session.php");
|
||||
require_once($rootPath . "/src/config/connection.php");
|
||||
require_once($rootPath . "/src/config/functions.php");
|
||||
|
||||
// CSRF Token Validation
|
||||
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['status' => 'error', 'message' => 'Security token validation failed.']);
|
||||
exit();
|
||||
}
|
||||
require_once("session.php");
|
||||
require_once("connection.php");
|
||||
require_once("functions.php");
|
||||
|
||||
// Check if user_id is set in the POST request
|
||||
if (isset($_POST['user_id']) && !empty($_POST['user_id'])) {
|
||||
// Validate user_id as integer
|
||||
$user_id = intval($_POST['user_id']);
|
||||
if ($user_id <= 0) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Invalid user ID.']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Sanitize the input to prevent SQL injection
|
||||
$user_id = mysqli_real_escape_string($conn, $_POST['user_id']);
|
||||
$created_at = date('Y-m-d H:i:s'); // Use current date and time for created_at
|
||||
|
||||
// First, check if a bar tab already exists for this user_id
|
||||
$stmt = $conn->prepare("SELECT * FROM bar_tabs WHERE user_id = ? LIMIT 1");
|
||||
$stmt->bind_param("i", $user_id);
|
||||
$stmt->execute();
|
||||
$checkResult = $stmt->get_result();
|
||||
$checkSql = "SELECT * FROM bar_tabs WHERE user_id = '$user_id' LIMIT 1";
|
||||
$checkResult = mysqli_query($conn, $checkSql);
|
||||
|
||||
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
|
||||
echo json_encode(['status' => 'error', 'message' => 'A bar tab already exists for this user.']);
|
||||
} else {
|
||||
// Prepare the SQL query to insert a new record into the bar_tabs table
|
||||
$stmt = $conn->prepare("INSERT INTO bar_tabs (user_id) VALUES (?)");
|
||||
$stmt->bind_param("i", $user_id);
|
||||
$sql = "INSERT INTO bar_tabs (user_id) VALUES ('$user_id')";
|
||||
|
||||
// Execute the query
|
||||
if ($stmt->execute()) {
|
||||
if (mysqli_query($conn, $sql)) {
|
||||
// If the insertion is successful, return a success message
|
||||
echo json_encode(['status' => 'success', 'message' => 'Bar tab created successfully.']);
|
||||
} else {
|
||||
// If there's an error, return an error message
|
||||
echo json_encode(['status' => 'error', 'message' => 'Error: ' . $conn->error]);
|
||||
echo json_encode(['status' => 'error', 'message' => 'Error: ' . mysqli_error($conn)]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If user_id is not provided, return an error message
|
||||
echo json_encode(['status' => 'error', 'message' => 'User ID is required.']);
|
||||
}
|
||||
|
||||
// Close the database connection
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
|
||||
|
||||
@@ -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**.
|
||||
@@ -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 */;
|
||||
@@ -1,176 +0,0 @@
|
||||
# Events Management Admin System
|
||||
|
||||
## Overview
|
||||
A complete admin system for managing events on the 4WDCSA website, following the same patterns as the trip management system.
|
||||
|
||||
## Files Created
|
||||
|
||||
### 1. `/src/admin/manage_events.php`
|
||||
**Purpose**: Form for creating and editing events
|
||||
|
||||
**Features**:
|
||||
- Create new events form
|
||||
- Edit existing events form
|
||||
- Fields:
|
||||
- Event Name (required)
|
||||
- Event Type (required) - e.g., Workshop, Training, Rally
|
||||
- Location (required)
|
||||
- Date (required)
|
||||
- Time (required)
|
||||
- Feature/Category (required) - e.g., Off-Road Training, Social Event
|
||||
- Description (required) - Full text description
|
||||
- Event Image (required for new, optional for updates)
|
||||
- Promotional Image (optional) - Displayed when users click "View Promo"
|
||||
- Published Status (checkbox) - Controls visibility on website
|
||||
|
||||
**Technical Details**:
|
||||
- AJAX form submission to `process_event` endpoint
|
||||
- Image upload with validation
|
||||
- CSRF token protection
|
||||
- Responsive Bootstrap grid layout (col-md-6 fields)
|
||||
- Success/error message display with auto-redirect
|
||||
|
||||
### 2. `/src/admin/process_event.php`
|
||||
**Purpose**: Backend endpoint for handling event CRUD operations
|
||||
|
||||
**Endpoints**:
|
||||
- `POST /process_event` - Create/Update event
|
||||
- `GET /process_event?action=delete&event_id={id}` - Delete event
|
||||
|
||||
**Features**:
|
||||
- Create new events with image uploads
|
||||
- Update existing events with optional image replacement
|
||||
- Delete events and associated image files
|
||||
- CSRF token validation
|
||||
- Image type validation (JPEG, PNG, GIF, WebP)
|
||||
- File organization in `/assets/images/events/`
|
||||
- Automatic timestamp management (created_at, updated_at)
|
||||
- User tracking (created_by stores admin user_id)
|
||||
|
||||
**Image Handling**:
|
||||
- Main event image: Stored with unique ID prefix
|
||||
- Promo image: Stored with `_promo_` prefix
|
||||
- Both uploaded to `/assets/images/events/`
|
||||
|
||||
### 3. `/src/admin/admin_events.php`
|
||||
**Purpose**: Admin dashboard for managing all events
|
||||
|
||||
**Features**:
|
||||
- List all events with sortable columns
|
||||
- Real-time search/filter across all columns
|
||||
- Create new event button
|
||||
- Edit event link for each row
|
||||
- Delete event with confirmation dialog
|
||||
- Status badges (Published/Draft)
|
||||
- Responsive table with alternating row colors
|
||||
- Rounded corners on even rows
|
||||
|
||||
**Sortable Columns**:
|
||||
- Event Name
|
||||
- Type
|
||||
- Location
|
||||
- Date
|
||||
- Status
|
||||
|
||||
**Actions**:
|
||||
- Edit - Redirects to manage_events.php with event_id
|
||||
- Delete - Removes event and associated files
|
||||
|
||||
## Database Schema Changes
|
||||
|
||||
### Migration File: `/docs/migrations/001_add_events_tracking_columns.sql`
|
||||
|
||||
**Columns Added to events table**:
|
||||
- `created_by` (int) - References user who created the event
|
||||
- `published` (tinyint(1)) - Boolean flag for publication status (default 0/false)
|
||||
- `created_at` (timestamp) - Automatic timestamp when event is created
|
||||
- `updated_at` (timestamp) - Automatic timestamp updated on modification
|
||||
|
||||
**Indexes Added**:
|
||||
- `idx_date` - For sorting and filtering by date
|
||||
- `idx_published` - For filtering published/draft events
|
||||
- `idx_created_by` - For tracking who created events
|
||||
|
||||
## Design Patterns
|
||||
|
||||
### Follows Trip Management System Architecture
|
||||
- Same form layout and styling (`.comment-form.bgc-lighter`)
|
||||
- Same table styling with sortable headers and filters
|
||||
- Same image upload and validation patterns
|
||||
- AJAX submission with success/error messaging
|
||||
- Auto-redirect on successful operation
|
||||
|
||||
### Image Organization
|
||||
```
|
||||
/assets/images/events/
|
||||
├── {unique_id}_{original_filename}.jpg (event images)
|
||||
└── {unique_id}_promo_{original_filename}.jpg (promo images)
|
||||
```
|
||||
|
||||
### Front-end Integration
|
||||
The existing `/src/pages/events/events.php` displays published events:
|
||||
- Shows event image, name, location, date, time
|
||||
- Feature description and full description
|
||||
- "View Promo" button displays promotional image in modal
|
||||
|
||||
## Usage Workflow
|
||||
|
||||
### Creating an Event
|
||||
1. Navigate to `/src/admin/manage_events.php`
|
||||
2. Fill in all required fields
|
||||
3. Upload event image
|
||||
4. Optionally upload promotional image
|
||||
5. Check "Publish Event" if ready to display
|
||||
6. Submit form via AJAX
|
||||
7. Redirected to admin_events.php list view
|
||||
|
||||
### Editing an Event
|
||||
1. Click "Edit" button on admin_events.php
|
||||
2. Modify any fields
|
||||
3. Image upload is optional - existing image retained if not changed
|
||||
4. Update timestamps and user tracking automatic
|
||||
5. Submit form
|
||||
6. Redirected back to list view
|
||||
|
||||
### Deleting an Event
|
||||
1. Click "Delete" button on admin_events.php
|
||||
2. Confirm deletion in dialog
|
||||
3. Event and associated image files removed from server
|
||||
4. Page automatically refreshes
|
||||
|
||||
### Publishing/Unpublishing
|
||||
- Toggle "Publish Event" checkbox before saving
|
||||
- Only published events appear on `/src/pages/events/events.php`
|
||||
- Draft events hidden from public view
|
||||
|
||||
## Security Features
|
||||
|
||||
1. **CSRF Token Protection**: All forms include CSRF token validation
|
||||
2. **Admin-only Access**: `checkAdmin()` function validates user permissions
|
||||
3. **File Validation**: Image type checking (JPEG, PNG, GIF, WebP)
|
||||
4. **SQL Injection Prevention**: Prepared statements with parameter binding
|
||||
5. **XSS Prevention**: `htmlspecialchars()` used for output escaping
|
||||
|
||||
## Styling Classes
|
||||
|
||||
**Form Container**: `.comment-form.bgc-lighter.z-1.rel.mb-30.rmb-55`
|
||||
**Action Buttons**: `.btn-edit`, `.btn-delete`
|
||||
**Status Badges**: `.badge.badge-published`, `.badge.badge-draft`
|
||||
**Tables**: Uses sortable header styling with visual sort indicators
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
- Modern browsers with AJAX/Fetch API support
|
||||
- JavaScript enabled required for filtering and sorting
|
||||
- File input accepts image MIME types
|
||||
|
||||
## Future Enhancement Opportunities
|
||||
|
||||
1. Bulk event operations (bulk delete, publish multiple)
|
||||
2. Event categories/tags system
|
||||
3. Event capacity limits with registrations
|
||||
4. Email notifications for published events
|
||||
5. Event calendar view
|
||||
6. Event image gallery (multiple images per event)
|
||||
7. Recurring events support
|
||||
8. Event attendee tracking
|
||||