24 Commits

Author SHA1 Message Date
twotalesanimation 81efab9cb0 Security hardening, DB connection fix, and handover documentation
Prepares the site for handover to the incoming committee. Three groups of
change: security fixes, a production stability fix, and documentation.

SECURITY

- upload_blog_image.php accepted unauthenticated uploads with no extension
  or MIME validation, into a web-served directory. Anyone could upload and
  execute a .php file. Now requires a session, checks blog ownership, and
  runs through validateFileUpload() with a new blog_image type.
- Added uploads/.htaccess and assets/uploads/.htaccess disabling the PHP
  engine, so an upload bypass still cannot execute. Force-added: .gitignore
  contains a bare .htaccess rule.
- blog/autosave.php updated posts by blog_id alone, letting any logged-in
  member overwrite anyone's post. Now scoped to the author, admin override
  retained. It also silently reassigned authorship to the editing admin;
  the existing author is now preserved.
- add_to_cart.php took item_price and user_id from POST, so drinks could be
  charged at any price to any member's tab, unauthenticated. Price and name
  are now looked up server-side from bar_items. Its require paths were also
  relative and broken.
- Session cookies now httponly/secure/samesite with strict mode, set in both
  .htaccess and .user.ini since php_flag and .user.ini apply under different
  SAPIs. The 30 minute idle timeout that login set but never checked is now
  enforced. Google login now regenerates the session id.
- Forced HTTPS. Required, because Secure cookies are never sent over plain
  HTTP and login would fail silently. LetsEncrypt acme-challenge is exempted
  above the redirect so certificate renewal keeps working.
- display_errors off; .htaccess now blocks *.log, error_log, .git and
  dotfiles. Debug logs containing member IDs, raw webhook bodies and payment
  signatures were publicly fetchable.
- Removed the IKHOKHA_BYPASS_SIGNATURE switch and the logging of raw webhook
  bodies, app IDs and signatures. Replaced a hardcoded personal Gmail
  recipient with the optional DEVELOPER_EMAIL variable.
- Added auth to fetch_users, fetch_bar_tabs, fetch_drinks, create_bar_tab and
  submit_order. Converted get_tab_total to a prepared statement.
- Tightened 11 mkdir(0777) calls to 0755.
- Fixed the validate_login rewrite, which pointed at a nonexistent path and
  only worked by falling through to the generic .php rule.

DATABASE STABILITY

openDatabaseConnection() opened a new connection on every call across 73 call
sites, and the server was hitting "Too many connections" in production. It now
returns one shared handle per request, reusing the connection connection.php
already opened. The 40 conn->close() calls in functions.php are removed, since
closing the shared handle breaks every later caller; PHP releases it at end of
request. A liveness check reconnects transparently if anything closes it.

Verified no function was lost (99 before and after), every removed close was
inside a braced block, and no transaction spans a helper that would open a
nested one.

DOCUMENTATION AND TOOLING

- docs/HANDOVER.md: operator guide covering architecture, routing, roles, the
  membership lifecycle, payments, backups, POPIA obligations, environment
  variables, known issues and a handover checklist.
- docs/FUNCTIONS.md: reference for the helpers in functions.php.
- scripts/backup.sh: nightly database and uploads backup, written outside the
  web root, with rotation and verification. Dumps are written to a temp file
  and only promoted after passing gzip, CREATE TABLE and completion-marker
  checks, so a failed run can never overwrite a good backup.
- .deployignore and .gitattributes added; .gitattributes pins LF on shell
  scripts and server config because core.autocrlf is true on Windows.
- Removed leftover debris: src/api/test_log.php and .htaccess1.

All 22 modified PHP files pass php -l. The backup script was tested against a
stub mysqldump for healthy, truncated and failing dumps, and for rotation.
Nothing was run against a live database or web server.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 13:56:32 +02:00
twotalesanimation 32e50ffc39 Commit since isp push 2025-12-13 14:33:23 +02:00
twotalesanimation cce181e2d0 Add interactive Base 4 track map with Leaflet.js
- Created new track-map page with aerial image and SVG overlay
- Implemented custom rotated square markers with obstacle numbers
- Added admin edit mode for placing and repositioning markers
- Database migration for track_obstacles table
- Modal form for adding new obstacles (replaces browser alerts)
- Drag-to-reposition functionality with auto-save
- Color-coded markers (green/red/black/split) for difficulty levels
- Clickable popups showing obstacle details
- Added track-map to navigation menu and sitemap
- URL rewrite rule for clean /track-map URL
2025-12-12 12:00:20 +02:00
twotalesanimation 48ee7592b2 Reorganize event processors and update routing
- Move process_event.php from src/admin to src/processors
- Move toggle_event_published.php from src/admin to src/processors
- Move delete_event.php from src/admin to src/processors
- Update .htaccess rewrite rules to point event processors to correct location
- Keep admin_events.php and manage_events.php in admin (display pages only)
2025-12-11 08:55:24 +02:00
twotalesanimation bbc0aecbcb force update CSS2 2025-12-08 10:55:08 +02:00
twotalesanimation 752ea6e5e9 fix: correct CSS syntax error in .comments rule that was breaking footer and other component styles 2025-12-08 10:37:01 +02:00
twotalesanimation 0af0bd33f9 Blog system enhancements: fix publish/unpublish permissions, add action buttons to blog listings, update gallery to show only published blog images, improve blog card layout and description truncation 2025-12-08 10:20:12 +02:00
twotalesanimation 7dad2a4ce2 chore: add uploads directory to gitignore to prevent tracking user-uploaded files 2025-12-05 10:28:52 +02:00
twotalesanimation 233305cac2 feat: use album cover image as album header background
- Fetch cover_image in album query
- Set album-header background-image with cover image
- Add dark overlay (50% opacity) over cover for text readability
- Increased padding for better header spacing with cover image
- Improved visual design using cover image as backdrop
- Fallback to overlay-only design if no cover image exists
- Enhanced header layout with proper z-index for content layering
2025-12-05 10:18:51 +02:00
twotalesanimation ad460ef85a feat: redesign gallery page with grid layout and enhance ownership checks
- Changed gallery from carousel to responsive grid layout (similar to about page)
- Shows album cover images with titles, creator info, and photo count
- Improved visual design with hover effects and better spacing
- Edit buttons now only visible to album owners (uses current_user_id variable)
- Added proper ownership verification in all album edit/delete operations
- Enhanced styling for mobile/tablet/desktop responsiveness
- Simplified layout makes it easier to browse multiple albums at once
2025-12-05 10:12:08 +02:00
twotalesanimation 9133b7bbc6 feat: improve campsites and events management UX
- Add map-based location picker with centered pin for campsites (two-step process)
- Hide edit buttons for campsites not owned by current user
- Allow numbers in campsite names (fix validateName function)
- Prepopulate edit form with existing campsite data
- Preserve country/province selection when confirming location
- Add real-time filter functionality to campsites table
- Fix events publish button error handling (use output buffering cleanup)
- Improve AJAX response handling with complete callback

Changes:
- src/pages/bookings/campsites.php: Location mode UI, filter, edit form improvements
- src/config/functions.php: Allow numbers in validateName regex
- src/admin/toggle_event_published.php: Clean output buffers before JSON response
- src/admin/admin_events.php: Use complete callback instead of success/error handlers
2025-12-05 09:20:48 +02:00
twotalesanimation cb588d20ee Feature: Campsite management system with map, form, and province/country filtering 2025-12-04 20:15:14 +02:00
twotalesanimation fdeaf85bf0 Update: Add publish/unpublish button to admin trips table and improve table styling 2025-12-04 18:35:36 +02:00
twotalesanimation d81d74a7c7 Fix: Add env.php include to delete_trip and toggle_trip_published processors 2025-12-04 17:31:27 +02:00
twotalesanimation 5f1a6bc441 Fix: Use EFT ID as filename for POP uploads instead of random filename
- Changed from random filename to eft_id.pdf format for proof of payment files
- Updated sendPOP() and auditLog() calls to use new filename variable
2025-12-04 16:11:37 +02:00
twotalesanimation 716de2f0e9 Fix: Clean output buffer in upload_profile_picture.php to prevent HTML in JSON response
- Move header() call to before any includes that might output
- Start output buffering at the beginning
- Clean output buffer before sending JSON response
2025-12-04 16:05:44 +02:00
twotalesanimation 79e292dc7c Fix: Profile picture upload AJAX response handling
- Add dataType: 'json' to AJAX call to properly parse JSON response
- Add Content-Type header to upload_profile_picture.php
- Add error callback with console logging for debugging
- Remove manual JSON parsing since jQuery handles it with dataType
2025-12-04 16:04:22 +02:00
twotalesanimation 59c1e37d5c Fix: Profile picture upload issues and improved error handling
- account_settings.php: Show success message before reloading page (with 1.5s delay)
- upload_profile_picture.php: Reorder require statements for proper initialization, add file error code to error message
2025-12-04 15:59:49 +02:00
twotalesanimation be2b757f4e Code restructure push 2025-12-04 15:09:44 +02:00
twotalesanimation 86faad7a78 image updates 2025-12-04 09:43:15 +02:00
twotalesanimation 062dc46ffd small updates 2025-12-02 18:17:20 +02:00
twotalesanimation b69f8f5f1b local changes. 2025-07-24 07:20:51 +02:00
twotalesanimation ac357402ca homepage update 2025-05-23 10:50:59 +02:00
Local Administrator b83134aca3 Initial commit 2025-04-18 10:32:42 +02:00