debug: add comprehensive logging to membership linking feature

- Added detailed error logging to link_membership_user processor
- Added error handling for database operations in processor
- Added comprehensive logging to linkSecondaryUserToMembership function
- Logs now show: CSRF validation, database operations, link creation, permission grants
- Improved error messages for debugging
This commit is contained in:
twotalesanimation
2025-12-05 11:22:38 +02:00
parent 886bdc5db8
commit 619ad0b320
4 changed files with 414 additions and 237 deletions

107
run_migrations.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
// Migration runner - creates membership linking tables if they don't exist
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/src/config/env.php';
require_once __DIR__ . '/src/config/functions.php';
$conn = openDatabaseConnection();
if (!$conn) {
die("Database connection failed\n");
}
echo "Connected to database successfully.\n\n";
// Check if membership_links table exists
$checkTable = $conn->query("SHOW TABLES LIKE 'membership_links'");
if ($checkTable->num_rows > 0) {
echo "✓ membership_links table already exists\n";
} else {
echo "Creating membership_links table...\n";
$createLink = $conn->query("
CREATE TABLE IF NOT EXISTS `membership_links` (
`link_id` INT AUTO_INCREMENT PRIMARY KEY,
`primary_user_id` INT NOT NULL,
`secondary_user_id` INT NOT NULL,
`relationship` VARCHAR(50) NOT NULL DEFAULT 'spouse',
`linked_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT `fk_membership_links_primary` FOREIGN KEY (`primary_user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_membership_links_secondary` FOREIGN KEY (`secondary_user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `idx_primary_user` (`primary_user_id`),
INDEX `idx_secondary_user` (`secondary_user_id`),
UNIQUE KEY `unique_link` (`primary_user_id`, `secondary_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
if ($createLink) {
echo "✓ membership_links table created successfully\n";
} else {
echo "✗ Error creating membership_links table: " . $conn->error . "\n";
}
}
// Check if membership_permissions table exists
$checkTable = $conn->query("SHOW TABLES LIKE 'membership_permissions'");
if ($checkTable->num_rows > 0) {
echo "✓ membership_permissions table already exists\n";
} else {
echo "Creating membership_permissions table...\n";
$createPerm = $conn->query("
CREATE TABLE IF NOT EXISTS `membership_permissions` (
`permission_id` INT AUTO_INCREMENT PRIMARY KEY,
`link_id` INT NOT NULL,
`permission_name` VARCHAR(100) NOT NULL,
`granted_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT `fk_membership_permissions_link` FOREIGN KEY (`link_id`)
REFERENCES `membership_links`(`link_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `idx_link` (`link_id`),
UNIQUE KEY `unique_permission` (`link_id`, `permission_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
if ($createPerm) {
echo "✓ membership_permissions table created successfully\n";
} else {
echo "✗ Error creating membership_permissions table: " . $conn->error . "\n";
}
}
// Create or replace the view
echo "\nCreating linked_membership_users view...\n";
$createView = $conn->query("
CREATE OR REPLACE VIEW `linked_membership_users` AS
SELECT
primary_user_id,
secondary_user_id,
relationship,
linked_at
FROM membership_links
UNION ALL
SELECT
primary_user_id,
primary_user_id as secondary_user_id,
'primary' as relationship,
linked_at
FROM membership_links
");
if ($createView) {
echo "✓ View created successfully\n";
} else {
echo "✗ Error creating view: " . $conn->error . "\n";
}
$conn->close();
echo "\n✓ Migration completed successfully!\n";
?>

View File

@@ -2924,17 +2924,25 @@ function linkSecondaryUserToMembership($primary_user_id, $secondary_user_id, $re
$conn = openDatabaseConnection(); $conn = openDatabaseConnection();
if ($conn === null) { if ($conn === null) {
error_log("linkSecondaryUserToMembership: Database connection failed");
return ['success' => false, 'message' => 'Database connection failed']; return ['success' => false, 'message' => 'Database connection failed'];
} }
error_log("linkSecondaryUserToMembership: primary=$primary_user_id, secondary=$secondary_user_id, relationship=$relationship");
// Validation: primary and secondary user IDs must be different // Validation: primary and secondary user IDs must be different
if ($primary_user_id === $secondary_user_id) { if ($primary_user_id === $secondary_user_id) {
error_log("linkSecondaryUserToMembership: Cannot link user to themselves");
return ['success' => false, 'message' => 'Cannot link user to themselves']; return ['success' => false, 'message' => 'Cannot link user to themselves'];
} }
// Validation: primary user must have active membership // Validation: primary user must have active membership
if (!getUserMemberStatus($primary_user_id)) { $memberStatus = getUserMemberStatus($primary_user_id);
error_log("linkSecondaryUserToMembership: Primary user member status = " . ($memberStatus ? 'true' : 'false'));
if (!$memberStatus) {
$conn->close(); $conn->close();
error_log("linkSecondaryUserToMembership: Primary user does not have active membership");
return ['success' => false, 'message' => 'Primary user does not have active membership']; return ['success' => false, 'message' => 'Primary user does not have active membership'];
} }
@@ -2947,6 +2955,7 @@ function linkSecondaryUserToMembership($primary_user_id, $secondary_user_id, $re
if ($userResult->num_rows === 0) { if ($userResult->num_rows === 0) {
$conn->close(); $conn->close();
error_log("linkSecondaryUserToMembership: Secondary user does not exist");
return ['success' => false, 'message' => 'Secondary user does not exist']; return ['success' => false, 'message' => 'Secondary user does not exist'];
} }
@@ -2959,12 +2968,14 @@ function linkSecondaryUserToMembership($primary_user_id, $secondary_user_id, $re
if ($existingResult->num_rows > 0) { if ($existingResult->num_rows > 0) {
$conn->close(); $conn->close();
error_log("linkSecondaryUserToMembership: Users are already linked");
return ['success' => false, 'message' => 'Users are already linked']; return ['success' => false, 'message' => 'Users are already linked'];
} }
try { try {
// Start transaction // Start transaction
$conn->begin_transaction(); $conn->begin_transaction();
error_log("linkSecondaryUserToMembership: Starting transaction");
// Insert link // Insert link
$insertLink = $conn->prepare(" $insertLink = $conn->prepare("
@@ -2972,8 +2983,13 @@ function linkSecondaryUserToMembership($primary_user_id, $secondary_user_id, $re
VALUES (?, ?, ?, NOW(), NOW()) VALUES (?, ?, ?, NOW(), NOW())
"); ");
$insertLink->bind_param("iis", $primary_user_id, $secondary_user_id, $relationship); $insertLink->bind_param("iis", $primary_user_id, $secondary_user_id, $relationship);
$insertLink->execute();
if (!$insertLink->execute()) {
throw new Exception("Failed to insert link: " . $insertLink->error);
}
$linkId = $conn->insert_id; $linkId = $conn->insert_id;
error_log("linkSecondaryUserToMembership: Link created with ID = $linkId");
$insertLink->close(); $insertLink->close();
// Grant default permissions to secondary user // Grant default permissions to secondary user
@@ -2991,17 +3007,24 @@ function linkSecondaryUserToMembership($primary_user_id, $secondary_user_id, $re
VALUES (?, ?, NOW()) VALUES (?, ?, NOW())
"); ");
$insertPerm->bind_param("is", $linkId, $permission); $insertPerm->bind_param("is", $linkId, $permission);
$insertPerm->execute();
if (!$insertPerm->execute()) {
throw new Exception("Failed to insert permission: " . $insertPerm->error);
}
error_log("linkSecondaryUserToMembership: Permission '$permission' granted");
$insertPerm->close(); $insertPerm->close();
} }
// Commit transaction // Commit transaction
$conn->commit(); $conn->commit();
error_log("linkSecondaryUserToMembership: Transaction committed successfully");
$conn->close(); $conn->close();
return ['success' => true, 'message' => 'User successfully linked to membership', 'link_id' => $linkId]; return ['success' => true, 'message' => 'User successfully linked to membership', 'link_id' => $linkId];
} catch (Exception $e) { } catch (Exception $e) {
error_log("linkSecondaryUserToMembership: Exception - " . $e->getMessage());
$conn->rollback(); $conn->rollback();
$conn->close(); $conn->close();
return ['success' => false, 'message' => 'Failed to create link: ' . $e->getMessage()]; return ['success' => false, 'message' => 'Failed to create link: ' . $e->getMessage()];
@@ -3157,3 +3180,4 @@ function unlinkSecondaryUser($link_id, $primary_user_id)
return ['success' => false, 'message' => 'Failed to remove link: ' . $e->getMessage()]; return ['success' => false, 'message' => 'Failed to remove link: ' . $e->getMessage()];
} }
} }

View File

@@ -217,73 +217,8 @@ if (empty($application['id_number'])) {
} }
?> ?>
<!-- Linked Accounts Section -->
<div style="margin-top: 40px; padding: 20px; background: white; border-radius: 8px; border: 1px solid #ddd;">
<div class="section-title" style="margin-bottom: 20px;">
<h3>Linked Accounts (Family & Partners)</h3>
<p style="color: #666; font-size: 0.95rem; margin-top: 10px;">Link additional family members or partners to your membership to give them access to member benefits.</p>
</div>
<?php <div style="margin-top: 40px; padding: 20px; border-radius: 8px; border: 1px solid #ddd;">
// Get linked secondary users
$linkedUsers = getLinkedSecondaryUsers($user_id);
?>
<?php if (!empty($linkedUsers)): ?>
<div style="margin-bottom: 30px;">
<h4 style="margin-bottom: 15px;">Currently Linked Accounts</h4>
<div class="linked-users-list">
<?php foreach ($linkedUsers as $linkedUser): ?>
<div style="padding: 15px; background: #f9f9f7; border-radius: 6px; margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
<div>
<p style="margin: 0; font-weight: 600;"><?php echo htmlspecialchars($linkedUser['first_name'] . ' ' . $linkedUser['last_name']); ?></p>
<p style="margin: 5px 0 0 0; font-size: 0.9rem; color: #666;">
<?php echo htmlspecialchars($linkedUser['email']); ?> •
<span style="text-transform: capitalize;"><?php echo htmlspecialchars($linkedUser['relationship']); ?></span>
</p>
</div>
<button type="button" class="unlink-btn" data-link-id="<?php echo $linkedUser['link_id']; ?>" style="background: #f44336; color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; font-size: 0.9rem;">
<i class="fal fa-trash"></i> Remove
</button>
</div>
<?php endforeach; ?>
</div>
</div>
<?php else: ?>
<div style="padding: 20px; text-align: center; background: #f9f9f7; border-radius: 6px; margin-bottom: 20px;">
<p style="color: #999; margin: 0;">No linked accounts yet.</p>
</div>
<?php endif; ?>
<!-- Link New User Form -->
<div style="padding: 20px; background: #f5f5f0; border-radius: 6px; border: 1px solid #e0e0e0;">
<h4 style="margin-top: 0; margin-bottom: 20px;">Add Linked Account</h4>
<form id="linkUserForm" style="display: flex; flex-direction: column; gap: 15px;">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div>
<label style="display: block; margin-bottom: 8px; font-weight: 600;">Email Address *</label>
<input type="email" id="secondary_email" name="secondary_email" placeholder="Enter the email of the person to link" required style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem;">
<p style="font-size: 0.85rem; color: #999; margin: 5px 0 0 0;">They must have an existing 4WDCSA account</p>
</div>
<div>
<label style="display: block; margin-bottom: 8px; font-weight: 600;">Relationship *</label>
<select id="relationship" name="relationship" required style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem;">
<option value="spouse">Spouse/Partner</option>
<option value="family_member">Family Member</option>
<option value="dependent">Dependent</option>
<option value="other">Other</option>
</select>
</div>
<button type="submit" style="background: #667eea; color: white; border: none; padding: 12px 20px; border-radius: 4px; cursor: pointer; font-size: 1rem; font-weight: 600; align-self: flex-start; transition: background 0.3s;">
<i class="fal fa-plus"></i> Link Account
</button>
</form>
<div id="linkMessage" style="margin-top: 15px;"></div>
</div>
</div>
<form id="infoForm" name="registerForm" action="update_application" method="post" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50"> <form id="infoForm" name="registerForm" action="update_application" method="post" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title"> <div class="section-title">
<div id="responseMessage"></div> <!-- Message display area --> <div id="responseMessage"></div> <!-- Message display area -->
@@ -508,9 +443,79 @@ if (empty($application['id_number'])) {
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Linked Accounts Section -->
<div style="margin-top: 40px; padding: 20px; border-radius: 8px; border: 1px solid #ddd;">
<div class="section-title" style="margin-bottom: 20px;">
<h3>Linked Accounts (Family & Partners)</h3>
<p style="color: #666; font-size: 0.95rem; margin-top: 10px;">Link additional family members or partners to your membership to give them access to member benefits.</p>
</div>
<?php
// Get linked secondary users
$linkedUsers = getLinkedSecondaryUsers($user_id);
?>
<?php if (!empty($linkedUsers)): ?>
<div style="margin-bottom: 30px;">
<h4 style="margin-bottom: 15px;">Currently Linked Accounts</h4>
<div class="linked-users-list">
<?php foreach ($linkedUsers as $linkedUser): ?>
<div style="padding: 15px; background: #f9f9f7; border-radius: 6px; margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
<div>
<p style="margin: 0; font-weight: 600;"><?php echo htmlspecialchars($linkedUser['first_name'] . ' ' . $linkedUser['last_name']); ?></p>
<p style="margin: 5px 0 0 0; font-size: 0.9rem; color: #666;">
<?php echo htmlspecialchars($linkedUser['email']); ?> •
<span style="text-transform: capitalize;"><?php echo htmlspecialchars($linkedUser['relationship']); ?></span>
</p>
</div>
<button type="button" class="unlink-btn" data-link-id="<?php echo $linkedUser['link_id']; ?>" style="background: #f44336; color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; font-size: 0.9rem;">
<i class="fal fa-trash"></i> Remove
</button>
</div>
<?php endforeach; ?>
</div>
</div>
<?php else: ?>
<div style="padding: 20px; text-align: center; background: #f9f9f7; border-radius: 6px; margin-bottom: 20px;">
<p style="color: #999; margin: 0;">No linked accounts yet.</p>
</div>
<?php endif; ?>
<!-- Link New User Form -->
<div style="padding: 20px; background: #f5f5f0; border-radius: 6px; border: 1px solid #e0e0e0;">
<h4 style="margin-top: 0; margin-bottom: 20px;">Add Linked Account</h4>
<form id="linkUserForm" style="display: flex; flex-direction: column; gap: 15px;">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div>
<label style="display: block; margin-bottom: 8px; font-weight: 600;">Email Address *</label>
<input type="email" id="secondary_email" name="secondary_email" placeholder="Enter the email of the person to link" required style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem;">
<p style="font-size: 0.85rem; color: #999; margin: 5px 0 0 0;">They must have an existing 4WDCSA account</p>
</div>
<div>
<label style="display: block; margin-bottom: 8px; font-weight: 600;">Relationship *</label>
<select id="relationship" name="relationship" required style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem;">
<option value="spouse">Spouse/Partner</option>
<option value="family_member">Family Member</option>
<option value="dependent">Dependent</option>
<option value="other">Other</option>
</select>
</div>
<button type="submit" class="theme-btn style-two" style="width:100%; margin-top: 10px;">
<span data-hover="LINK ACCOUNT"><i class="fal fa-plus"></i> Link Account</span>
</button>
</form>
<div id="linkMessage" style="margin-top: 15px;"></div>
</div>
</div>
</div>
<!-- Submit Section --> <!-- Submit Section -->
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group mb-0"> <div class="form-group mb-0">

View File

@@ -7,46 +7,86 @@ require_once($rootPath . '/src/config/functions.php');
header('Content-Type: application/json'); header('Content-Type: application/json');
// Log incoming request
error_log("Link membership user request received. Method: " . $_SERVER['REQUEST_METHOD']);
error_log("POST data: " . json_encode($_POST));
error_log("Session user_id: " . ($_SESSION['user_id'] ?? 'NOT SET'));
if (!isset($_SESSION['user_id']) || $_SERVER['REQUEST_METHOD'] !== 'POST') { if (!isset($_SESSION['user_id']) || $_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(403); http_response_code(403);
error_log("Forbidden: No session or wrong method");
exit(json_encode(['success' => false, 'message' => 'Forbidden'])); exit(json_encode(['success' => false, 'message' => 'Forbidden']));
} }
// Validate CSRF token // Validate CSRF token
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) { if (!isset($_POST['csrf_token'])) {
http_response_code(400); http_response_code(400);
exit(json_encode(['success' => false, 'message' => 'Invalid request'])); error_log("No CSRF token provided");
exit(json_encode(['success' => false, 'message' => 'CSRF token missing']));
}
if (!validateCSRFToken($_POST['csrf_token'])) {
http_response_code(400);
error_log("Invalid CSRF token: " . $_POST['csrf_token']);
error_log("Available tokens: " . json_encode($_SESSION['csrf_tokens'] ?? []));
exit(json_encode(['success' => false, 'message' => 'Invalid CSRF token']));
} }
$primary_user_id = intval($_SESSION['user_id']); $primary_user_id = intval($_SESSION['user_id']);
$secondary_email = trim($_POST['secondary_email'] ?? ''); $secondary_email = trim($_POST['secondary_email'] ?? '');
$relationship = trim($_POST['relationship'] ?? 'spouse'); $relationship = trim($_POST['relationship'] ?? 'spouse');
error_log("Processing link: primary=$primary_user_id, secondary_email=$secondary_email, relationship=$relationship");
if (empty($secondary_email)) { if (empty($secondary_email)) {
http_response_code(400); http_response_code(400);
error_log("Secondary email is empty");
exit(json_encode(['success' => false, 'message' => 'Secondary user email is required'])); exit(json_encode(['success' => false, 'message' => 'Secondary user email is required']));
} }
// Get the secondary user by email // Get the secondary user by email
$conn = openDatabaseConnection(); $conn = openDatabaseConnection();
if (!$conn) {
http_response_code(500);
error_log("Failed to open database connection");
exit(json_encode(['success' => false, 'message' => 'Database connection failed']));
}
$userQuery = $conn->prepare("SELECT user_id FROM users WHERE email = ?"); $userQuery = $conn->prepare("SELECT user_id FROM users WHERE email = ?");
if (!$userQuery) {
http_response_code(500);
error_log("Prepare statement failed: " . $conn->error);
$conn->close();
exit(json_encode(['success' => false, 'message' => 'Database error']));
}
$userQuery->bind_param("s", $secondary_email); $userQuery->bind_param("s", $secondary_email);
$userQuery->execute(); if (!$userQuery->execute()) {
http_response_code(500);
error_log("Query execution failed: " . $userQuery->error);
$userQuery->close();
$conn->close();
exit(json_encode(['success' => false, 'message' => 'Database error']));
}
$userResult = $userQuery->get_result(); $userResult = $userQuery->get_result();
$userQuery->close(); $userQuery->close();
if ($userResult->num_rows === 0) { if ($userResult->num_rows === 0) {
$conn->close(); $conn->close();
error_log("User not found with email: $secondary_email");
http_response_code(404); http_response_code(404);
exit(json_encode(['success' => false, 'message' => 'User with that email not found'])); exit(json_encode(['success' => false, 'message' => 'User with that email not found']));
} }
$user = $userResult->fetch_assoc(); $user = $userResult->fetch_assoc();
$secondary_user_id = $user['user_id']; $secondary_user_id = $user['user_id'];
error_log("Found secondary user: $secondary_user_id");
$conn->close(); $conn->close();
// Use the linking function from functions.php // Use the linking function from functions.php
$result = linkSecondaryUserToMembership($primary_user_id, $secondary_user_id, $relationship); $result = linkSecondaryUserToMembership($primary_user_id, $secondary_user_id, $relationship);
error_log("Link result: " . json_encode($result));
http_response_code($result['success'] ? 200 : 400); http_response_code($result['success'] ? 200 : 400);
echo json_encode([ echo json_encode([
@@ -55,3 +95,4 @@ echo json_encode([
'link_id' => $result['link_id'] ?? null 'link_id' => $result['link_id'] ?? null
]); ]);
?> ?>