feat: add invite notification emails, functionality to admin dashboard and sign up page

Signed-off-by: Miguel Nogueira <me@nogueira.codes>
This commit is contained in:
2025-08-07 18:46:34 +01:00
parent 22cffaffca
commit f7c62a4ac2
19 changed files with 1141 additions and 2 deletions

View File

@@ -0,0 +1,147 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\InvitationRequest;
use App\Invitation;
use App\Mail\InviteApprovedMail;
use App\Mail\InvitedToApp;
use App\Mail\InviteRequestReceived;
use App\Response;
use Auth;
use Illuminate\Http\Request;
use Mail;
use Session;
class InvitationController extends Controller
{
public function index()
{
return view('dashboard.administration.invites', [
'invites' => Invitation::all()
]);
}
public function requestInvite(InvitationRequest $request)
{
$guest = Auth::guest();
$invitation = new Invitation();
$invitation->requestor_email = $request->input('email');
$invitation->requestor_ip_address = $request->ip();
$invitation->status = $guest ? 'pending' : 'approved';
$invitation->notified = !$guest; // confirmation msg doesn't count
$invitation->invitation_code = bin2hex(random_bytes(64));
$invitation->expiration = now()->addDays(2);
try {
$invitation->saveOrFail();
$addlMessage = ($guest) ? __('Check your email address for a confirmation email.') : '';
$request->session()->flash('success', __('Invitation request sent. :additionalUnauthenticatedMessage', ['additionalUnauthenticatedMessage' => $addlMessage]));
if ($guest) {
Mail::to($invitation->requestor_email)->send(new InviteRequestReceived());
}
else {
// this is an approved invite
Mail::to($invitation->requestor_email)->send(new InvitedToApp($invitation));
}
} catch (\Exception $exception) {
\Log::debug('[INVITES]: Error saving invite request', ['message' => $exception->getMessage(), 'requestor_ip' => $request->ip()]);
$request->session()->flash('error', __('Sorry, but we were unable to request an invitation for you. If you already requested one, trying to request another will not be possible, nor will it speed up the process.'));
}
return redirect()->back();
}
public function approveInvite(Request $request, Invitation $invitation)
{
$approvableStates = [
'pending'
];
if ($invitation->expiration && now()->lessThanOrEqualTo($invitation->expiration) && in_array($invitation->status, $approvableStates))
{
$invitation->status = 'approved';
$invitation->notified = true;
$invitation->save();
Mail::to($invitation->requestor_email)->send(new InviteApprovedMail($invitation));
return redirect()
->back()
->with('success', __('Invite request approved! This user can now sign up.'));
}
else
{
return redirect()
->back()
->with('error', __('This invitation couldn\'t be approved because either it\'s already approved or it is expired.'));
}
}
public function denyInvite(Request $request, Invitation $invitation)
{
$declinableStates = [
'pending'
];
if ($invitation->expiration && now()->lessThanOrEqualTo($invitation->expiration) && in_array($invitation->status, $declinableStates))
{
$invitation->status = 'denied';
$invitation->save();
return redirect()
->with('success', __('Invitation denied. No notifications were sent. This user cannot be invited again.'))
->back();
}
return redirect()
->with('error', __('This invitation could not be denied because it is either already approved, expired, or in an otherwise invalid state.'));
}
public function redeemInvite(Request $request)
{
return view('auth.redeem-invite', ['validationToken' => $request->route('token')]);
}
public function validateInvite(Request $request)
{
$token = $request->input('validation_token');
$email = $request->input('email');
$invite = Invitation::where('requestor_email', $email)->first();
if (!empty($invite) && $token === $invite->invitation_code && 'approved' === $invite->status && $invite->expiration && now()->lessThanOrEqualTo($invite->expiration))
{
$invite->status = 'completed';
$invite->save();
Session::put('ALLOW_REGISTRATION_OVERRIDE', true);
Session::put('REGISTRATION_OVERRIDE_EMAIL', $email);
return redirect()
->route('register')
->with('success', __('Invitation code validated! You can now sign up with the email address you were invited with.'));
}
else
{
return redirect()
->back()
->with('error', __('Something went wrong while validating your invite. Either it does not exist, is expired, has not been approved yet, or the token is wrong (do not edit it).'));
}
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use App\Facades\Options;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Foundation\Http\FormRequest;
class InvitationRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => ['required', 'email', 'max:254'],
];
}
public function authorize(): bool
{
if (Options::getOption('enable_registrations')) {
return false;
}
return true;
}
protected function failedAuthorization()
{
throw new AuthorizationException(__('You cannot request a new invite for this user/e-mail address right now. Keep in mind that users can only be invited once.'));
}
}