feat: implement membership linking system for couples and family members

- Created membership_links table to associate secondary users with primary memberships
- Created membership_permissions table for granular permission control
- Added linkSecondaryUserToMembership() function to create links with validation
- Added getUserMembershipLink() to check access via secondary links
- Added getLinkedSecondaryUsers() to list all secondary users for a primary member
- Added unlinkSecondaryUser() to remove links
- Updated getUserMemberStatus() to check both direct and linked memberships
- Created link_membership_user processor to handle linking via API
- Created unlink_membership_user processor to handle unlinking via API
- Added .htaccess routes for linking endpoints
- Grants default permissions: access_member_areas, member_pricing, book_campsites, book_courses, book_trips
- Includes transaction safety with rollback on errors
- Includes comprehensive documentation with usage examples
- Validates primary user has active membership before allowing links
- Prevents duplicate links and self-linking
This commit is contained in:
twotalesanimation
2025-12-05 10:44:52 +02:00
parent 7dad2a4ce2
commit bd20fc0f9b
46 changed files with 718 additions and 2 deletions

View File

@@ -0,0 +1,57 @@
<?php
$rootPath = dirname(dirname(__DIR__));
require_once($rootPath . '/src/config/env.php');
require_once($rootPath . '/src/config/session.php');
require_once($rootPath . '/src/config/connection.php');
require_once($rootPath . '/src/config/functions.php');
header('Content-Type: application/json');
if (!isset($_SESSION['user_id']) || $_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(403);
exit(json_encode(['success' => false, 'message' => 'Forbidden']));
}
// Validate CSRF token
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
http_response_code(400);
exit(json_encode(['success' => false, 'message' => 'Invalid request']));
}
$primary_user_id = intval($_SESSION['user_id']);
$secondary_email = trim($_POST['secondary_email'] ?? '');
$relationship = trim($_POST['relationship'] ?? 'spouse');
if (empty($secondary_email)) {
http_response_code(400);
exit(json_encode(['success' => false, 'message' => 'Secondary user email is required']));
}
// Get the secondary user by email
$conn = openDatabaseConnection();
$userQuery = $conn->prepare("SELECT user_id FROM users WHERE email = ?");
$userQuery->bind_param("s", $secondary_email);
$userQuery->execute();
$userResult = $userQuery->get_result();
$userQuery->close();
if ($userResult->num_rows === 0) {
$conn->close();
http_response_code(404);
exit(json_encode(['success' => false, 'message' => 'User with that email not found']));
}
$user = $userResult->fetch_assoc();
$secondary_user_id = $user['user_id'];
$conn->close();
// Use the linking function from functions.php
$result = linkSecondaryUserToMembership($primary_user_id, $secondary_user_id, $relationship);
http_response_code($result['success'] ? 200 : 400);
echo json_encode([
'success' => $result['success'],
'message' => $result['message'],
'link_id' => $result['link_id'] ?? null
]);
?>