PHP Classes

File: templates/register.twig

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   templates/register.twig   Download  
File: templates/register.twig
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 12,914 bytes
 

Contents

Class file image Download
{% extends "base.twig" %} {% block title %}{{ t('ui.register.title', {}, 'common') }} - {{ parent() }}{% endblock %} {% block content %} {% if register_splash %} <div class="row justify-content-center mb-3"> <div class="col-md-6 col-lg-6"> <div class="card"> <div class="card-body prose"> {{ register_splash|raw }} </div> </div> </div> </div> {% endif %} <div class="row justify-content-center"> <div class="col-md-6 col-lg-6"> <div class="card"> <div class="card-header"> <h4 class="mb-0"> <i class="fas fa-user-plus"></i> {{ t('ui.register.create_account', {}, 'common') }} </h4> </div> <div class="card-body"> <div class="alert alert-info"> <i class="fas fa-info-circle me-2"></i> <strong>{{ t('ui.register.approval_required', {}, 'common') }}</strong> {{ t('ui.register.approval_required_help', {}, 'common') }} {{ t('ui.register.subject_to_rules', {}, 'common') }} <a href="#" data-bs-toggle="modal" data-bs-target="#houseRulesModal">{{ t('ui.about.house_rules', {}, 'common') }}</a>. <br><small class="mt-1 d-inline-block"><i class="fas fa-envelope me-1"></i>{{ t('ui.register.email_notification_note', {}, 'common') }}</small> </div> <div id="alert-container"></div> <form id="registerForm"> {# Anti-spam honeypot field - hidden from humans, visible to bots #} <div style="position: absolute; left: -9999px; top: -9999px;" aria-hidden="true"> <label for="website">{{ t('ui.register.honeypot_website_leave_blank', {}, 'common') }}</label> <input type="text" id="website" name="website" tabindex="-1" autocomplete="off"> </div> <div class="mb-3"> <label for="username" class="form-label">{{ t('ui.register.username', {}, 'common') }}</label> <input type="text" class="form-control" id="username" name="username" required pattern="[a-zA-Z0-9_]{3,20}" title="{{ t('ui.register.username_help', {}, 'common') }}"> <div class="form-text">{{ t('ui.register.username_help', {}, 'common') }}</div> </div> <div class="mb-3"> <label for="password" class="form-label">{{ t('ui.register.password', {}, 'common') }}</label> <input type="password" class="form-control" id="password" name="password" required minlength="8"> <div class="password-strength-meter mt-2" id="passwordStrengthMeter" style="display: none;"> <div class="progress" style="height: 8px;"> <div class="progress-bar" id="passwordStrengthBar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> </div> <small class="form-text" id="passwordStrengthText"></small> </div> <div class="form-text">{{ t('ui.register.password_help', {}, 'common') }}</div> </div> <div class="mb-3"> <label for="confirmPassword" class="form-label">{{ t('ui.register.confirm_password', {}, 'common') }}</label> <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" required> </div> <div class="mb-3"> <label for="email" class="form-label">{{ t('ui.register.email_address', {}, 'common') }}</label> <input type="email" class="form-control" id="email" name="email"> <div class="form-text">{{ t('ui.register.email_help', {}, 'common') }}</div> </div> <div class="mb-3"> <label for="realName" class="form-label">{{ t('ui.register.real_name', {}, 'common') }}</label> <input type="text" class="form-control" id="realName" name="real_name" required> <div class="form-text">{{ t('ui.register.real_name_help', {}, 'common') }}</div> </div> <div class="mb-3"> <label for="location" class="form-label">{{ t('ui.register.location', {}, 'common') }}</label> <input type="text" class="form-control" id="location" name="location" placeholder="{{ t('ui.register.location_placeholder', {}, 'common') }}"> <div class="form-text">{{ t('ui.register.location_help', {}, 'common') }}</div> </div> <div class="mb-3"> <label for="reason" class="form-label">{{ t('ui.register.reason_for_joining', {}, 'common') }}</label> <textarea class="form-control" id="reason" name="reason" rows="3" placeholder="{{ t('ui.register.reason_placeholder', {}, 'common') }}"></textarea> </div> <div class="d-grid"> <button type="submit" class="btn btn-primary"> <i class="fas fa-user-plus"></i> {{ t('ui.register.submit_registration', {}, 'common') }} </button> </div> </form> </div> </div> <div class="text-center mt-3"> <small class="text-muted"> {{ t('ui.register.already_have_account', {}, 'common') }} <a href="/login" class="text-decoration-none">{{ t('ui.register.sign_in', {}, 'common') }}</a> </small> </div> </div> </div> {% endblock %} {% block scripts %} <script> $(document).ready(function() { function uiT(key, fallback, params = {}) { if (window.t) { return window.t(key, params, fallback); } return fallback; } function apiError(payload, fallback) { if (window.getApiErrorMessage) { return window.getApiErrorMessage(payload, fallback); } return payload && payload.error ? payload.error : fallback; } function showAlert(message, type) { const icon = type === 'success' ? 'check-circle' : type === 'danger' ? 'exclamation-triangle' : 'info-circle'; const alert = $(` <div class="alert alert-${type} alert-dismissible fade show" role="alert"> <i class="fas fa-${icon} me-2"></i> ${message} <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> `); $('#alert-container').append(alert); } // Password strength meter function calculatePasswordStrength(password) { if (!password) return { score: 0, text: '', color: '', width: 0 }; let score = 0; // Length scoring if (password.length >= 8) score += 20; if (password.length >= 12) score += 20; if (password.length >= 16) score += 10; // Character variety if (/[a-z]/.test(password)) score += 10; // lowercase if (/[A-Z]/.test(password)) score += 10; // uppercase if (/[0-9]/.test(password)) score += 10; // numbers if (/[^a-zA-Z0-9]/.test(password)) score += 20; // special characters // Bonus for good combinations const hasLower = /[a-z]/.test(password); const hasUpper = /[A-Z]/.test(password); const hasNumber = /[0-9]/.test(password); const hasSpecial = /[^a-zA-Z0-9]/.test(password); const varietyCount = [hasLower, hasUpper, hasNumber, hasSpecial].filter(Boolean).length; if (varietyCount >= 3) score += 10; // Penalize common patterns if (/^[a-z]+$/.test(password)) score -= 10; // all lowercase if (/^[A-Z]+$/.test(password)) score -= 10; // all uppercase if (/^[0-9]+$/.test(password)) score -= 10; // all numbers if (/(.)\1{2,}/.test(password)) score -= 10; // repeated characters (aaa, 111) if (/^(abc|123|qwerty|password|admin)/i.test(password)) score -= 20; // common sequences // Cap score at 100 score = Math.max(0, Math.min(100, score)); // Determine strength level let text, color; if (score < 30) { text = uiT('ui.register.password_strength.weak', 'Weak - Add more variety'); color = 'bg-danger'; } else if (score < 60) { text = uiT('ui.register.password_strength.fair', 'Fair - Could be stronger'); color = 'bg-warning'; } else if (score < 80) { text = uiT('ui.register.password_strength.good', 'Good - Nice password!'); color = 'bg-info'; } else { text = uiT('ui.register.password_strength.strong', 'Strong - Excellent!'); color = 'bg-success'; } return { score, text, color, width: score }; } function updatePasswordStrength() { const password = $('#password').val(); const meter = $('#passwordStrengthMeter'); const bar = $('#passwordStrengthBar'); const text = $('#passwordStrengthText'); if (!password) { meter.hide(); return; } meter.show(); const strength = calculatePasswordStrength(password); // Update progress bar bar.removeClass('bg-danger bg-warning bg-info bg-success') .addClass(strength.color) .css('width', strength.width + '%') .attr('aria-valuenow', strength.score); // Update text text.text(strength.text); } // Update strength meter on password input $('#password').on('input', updatePasswordStrength); $('#registerForm').on('submit', function(e) { e.preventDefault(); // Clear previous alerts $('#alert-container').empty(); // Anti-spam: Check honeypot field if ($('#website').val() !== '') { // Silent rejection - don't tell bots why they failed return false; } // Validate passwords match if ($('#password').val() !== $('#confirmPassword').val()) { showAlert(uiT('ui.register.passwords_do_not_match', 'Passwords do not match.'), 'danger'); return; } // Disable submit button const submitBtn = $('button[type="submit"]'); const originalText = submitBtn.html(); submitBtn.prop('disabled', true).html(`<i class="fas fa-spinner fa-spin"></i> ${uiT('ui.register.submitting', 'Submitting...')}`); // Submit registration $.ajax({ url: '/api/register', method: 'POST', data: $(this).serialize(), dataType: 'json', success: function(response) { if (response.success) { const successMessage = window.getApiMessage ? window.getApiMessage(response, uiT('ui.register.submitted_success', 'Registration submitted successfully! You will be notified when your account is approved.')) : uiT('ui.register.submitted_success', 'Registration submitted successfully! You will be notified when your account is approved.'); showAlert(successMessage, 'success'); // Hide the form after successful registration $('#registerForm').hide(); $('.card-body .alert-info').hide(); // Show link to login $('#alert-container').append( '<div class="mt-3 text-center">' + `<a href="/login" class="btn btn-primary"><i class="fas fa-sign-in-alt"></i> ${uiT('ui.register.go_to_login', 'Go to Login')}</a>` + '</div>' ); } else { showAlert(apiError(response, uiT('errors.register.failed', 'Registration failed. Please try again.')), 'danger'); } }, error: function(xhr) { const errorMsg = apiError(xhr.responseJSON, uiT('errors.register.failed', 'Registration failed. Please try again.')); showAlert(errorMsg, 'danger'); }, complete: function() { submitBtn.prop('disabled', false).html(originalText); } }); }); }); </script> {% endblock %}