Snapshot of main at 9f39a6e1e (2026-11-02)

This commit is contained in:
Ronald Huynen committed 2026-11-02 10:41:47 +01:00
commit 6ccf1792d7
2580 files changed
+1040202

No files matched your search

@@ -0,0 +1,123 @@
<?php
namespace App\Jobs;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Namu\WireChat\Enums\Actions;
use Namu\WireChat\Models\Message;
class DeleteExpiredWireChatMessagesJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*/
public function __construct()
{
$this->onQueue('low');
}
/**
* Execute the job.
*/
public function handle(): void
{
// Disappearing messages always enabled - this prevents orphaned messages
Log::info('WireChat disappearing messages: Starting cleanup');
$deletedCount = 0;
$deletedKeptCount = 0;
// Get all conversations with disappearing settings
$conversations = \Namu\WireChat\Models\Conversation::whereNotNull('disappearing_started_at')
->whereNotNull('disappearing_duration')
->get();
Log::info("WireChat disappearing messages: Found {$conversations->count()} conversations with disappearing enabled");
foreach ($conversations as $conversation) {
// Get regular messages (not kept) that should be deleted
$messages = $conversation->messages()
->withoutGlobalScopes()
->where(function ($query) {
// Messages that are not kept
$query->whereNull('kept_at')
// Or messages that are kept but have delete actions or are trashed
->orWhere(function ($query) {
$query->whereNotNull('kept_at') // Kept messages
->where(function ($query) {
$query->whereNotNull('deleted_at') // Trashed
->orWhereHas('actions', function ($query) {
$query->where('type', Actions::DELETE);
});
});
});
})
// Only messages created AFTER disappearing was enabled
->where('created_at', '>', $conversation->disappearing_started_at)
->get();
foreach ($messages as $message) {
$createdAt = $message->created_at;
if ($createdAt && $createdAt->isFuture()) {
continue; // Skip future messages
}
// Check if message is older than the conversation's duration
if ($createdAt && (int) ($createdAt->diffInSeconds(now(), true)) > $conversation->disappearing_duration) {
try {
Log::info("WireChat disappearing messages: Deleting message {$message->id} (age: " . (int) $createdAt->diffInSeconds(now(), true) . "s, limit: {$conversation->disappearing_duration}s)");
$message->forceDelete();
$deletedCount++;
} catch (\Exception $e) {
Log::error("WireChat disappearing messages: Failed to delete message {$message->id}: {$e->getMessage()}");
}
}
}
// Get kept messages that should be deleted based on kept_messages_duration
$keptMessagesDurationDays = timebank_config('wirechat.disappearing_messages.kept_messages_duration');
if ($keptMessagesDurationDays !== null && timebank_config('wirechat.disappearing_messages.allow_users_to_keep', true)) {
$keptMessagesDuration = $keptMessagesDurationDays * 86400; // Convert days to seconds
$keptMessages = $conversation->messages()
->withoutGlobalScopes()
->whereNotNull('kept_at')
->whereNull('deleted_at')
->whereDoesntHave('actions', function ($query) {
$query->where('type', Actions::DELETE);
})
->where('kept_at', '>', $conversation->disappearing_started_at)
->get();
foreach ($keptMessages as $keptMessage) {
$keptAt = $keptMessage->kept_at;
if ($keptAt && $keptAt->isFuture()) {
continue; // Skip future kept messages
}
// Check if kept message is older than the kept messages duration
if ($keptAt && (int) ($keptAt->diffInSeconds(now(), true)) > $keptMessagesDuration) {
try {
Log::info("WireChat disappearing messages: Deleting kept message {$keptMessage->id} (kept for: " . (int) $keptAt->diffInSeconds(now(), true) . "s, limit: {$keptMessagesDuration}s)");
$keptMessage->forceDelete();
$deletedKeptCount++;
} catch (\Exception $e) {
Log::error("WireChat disappearing messages: Failed to delete kept message {$keptMessage->id}: {$e->getMessage()}");
}
}
}
}
}
Log::info("WireChat disappearing messages: Deleted {$deletedCount} expired messages and {$deletedKeptCount} expired kept messages");
}
}
@@ -0,0 +1,53 @@
<?php
namespace App\Jobs;
use App\Mail\AccountsEditedByAdminMail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendAccountsEditedByAdminMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
protected $profile;
protected $changedFields;
public function __construct($profile, $changedFields = [])
{
$this->profile = $profile;
$this->changedFields = $changedFields;
}
public function handle()
{
if (!$this->profile->email) {
Log::warning('SendAccountsEditedByAdminMail: Profile has no email address', [
'profile_id' => $this->profile->id,
'profile_type' => get_class($this->profile),
]);
return;
}
Log::info('SendAccountsEditedByAdminMail: Sending email', [
'profile_id' => $this->profile->id,
'profile_type' => get_class($this->profile),
'profile_email' => $this->profile->email,
'changed_fields' => $this->changedFields,
]);
Mail::to($this->profile->email)->send(
new AccountsEditedByAdminMail($this->profile, $this->changedFields)
);
Log::info('SendAccountsEditedByAdminMail: Email sent successfully');
}
}
+419
View File
@@ -0,0 +1,419 @@
<?php
namespace App\Jobs;
use App\Mail\NewsletterMail;
use App\Models\MailingBounce;
use App\Models\Mailing;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendBulkMailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $mailing;
protected $locale;
protected $recipients;
protected $contentBlocks;
public $timeout = 300; // 5 minutes timeout
/**
* Get the number of times the job may be attempted.
*/
public function tries(): int
{
return timebank_config('mailing.max_retries', 3) + 1; // +1 for initial attempt
}
/**
* Determine if the job should be retried based on time limits.
*/
public function retryUntil(): \DateTime
{
$abandonAfterHours = timebank_config('mailing.abandon_after_hours', 72);
return now()->addHours($abandonAfterHours);
}
/**
* Create a new job instance.
*/
public function __construct(Mailing $mailing, string $locale, array $contentBlocks, $recipients)
{
$this->mailing = $mailing;
$this->locale = $locale;
$this->contentBlocks = $contentBlocks;
$this->recipients = $recipients;
}
/**
* Execute the job.
*
* This job processes a batch of recipients for a specific locale.
* Multiple instances of this job may run in parallel for different batches/locales.
*/
public function handle(): void
{
// A long-lived queue worker persists App::getLocale() across jobs; reset it here
// so locale-scoped relation queries (e.g. CityLocale) never see a leftover locale
// from a previously processed batch for a different locale.
App::setLocale($this->locale);
Log::info("MAILING: Starting bulk mail batch job for Mailing ID: {$this->mailing->id}, Recipients in batch: " . count($this->recipients) . ", Locale: {$this->locale}");
$sendDelay = timebank_config('mailing.send_delay_seconds', 5);
$recipients = collect($this->recipients);
$totalInBatch = $recipients->count();
$processedInBatch = 0;
foreach ($recipients as $recipient) {
// Abort if mailing has been stopped — count remaining batch recipients as aborted
$this->mailing->refresh();
if ($this->mailing->status === 'aborted') {
$abortedCount = $totalInBatch - $processedInBatch;
Log::info("MAILING: Batch job aborted for Mailing ID: {$this->mailing->id}. Aborted (not sent): {$abortedCount}");
$this->mailing->increment('aborted_count', $abortedCount);
$this->checkCompletion();
return;
}
$processedInBatch++;
// Skip if email is already suppressed due to bounces
if (MailingBounce::isSuppressed($recipient->email)) {
Log::warning("MAILING: Skipping suppressed email: {$recipient->email} (Mailing ID: {$this->mailing->id})");
$this->mailing->increment('skipped_count', 1);
continue;
}
try {
// Send individual email synchronously - let NewsletterMail generate its own content blocks
$newsletterMail = new NewsletterMail($this->mailing, $recipient, null, $this->locale, true);
// Attempt to send email with comprehensive error handling
$this->sendEmailWithErrorHandling($recipient->email, $newsletterMail);
// Increment immediately so the report reflects live progress
$this->mailing->increment('sent_count', 1);
// Add delay between emails to avoid rate limiting
if ($sendDelay > 0) {
sleep($sendDelay);
}
} catch (\Exception $e) {
$this->mailing->increment('failed_count', 1);
Log::error("Failed to send newsletter to {$recipient->email}: " . $e->getMessage());
// Check if this is a bounce-related error
if ($this->isBounceError($e)) {
$bounceType = $this->determineBounceType($e);
MailingBounce::recordBounce(
$recipient->email,
$bounceType,
$e->getMessage(),
$this->mailing->id
);
Log::warning("Recorded bounce for {$recipient->email}: {$bounceType}");
}
// If it's an SMTP quota/limit error and configured to fail job, trigger retry
if ($this->isSMTPQuotaError($e) && timebank_config('mailing.fail_job_on_quota_error', true)) {
Log::error("SMTP quota/limit error detected for {$recipient->email}. Failing job for retry with extended delay.");
// Set longer delay for quota errors
$this->retryWithQuotaDelay();
throw $e; // This will trigger job retry
}
}
}
Log::info("MAILING: Completed bulk mail job for Mailing ID: {$this->mailing->id}");
$this->checkCompletion();
}
/**
* Check if all recipients have been processed and mark mailing as sent if so.
*/
protected function checkCompletion(): void
{
$this->mailing->refresh();
$totalProcessed = $this->mailing->sent_count + $this->mailing->failed_count
+ $this->mailing->skipped_count + $this->mailing->aborted_count;
if ($totalProcessed >= $this->mailing->recipients_count && $this->mailing->status === 'sending') {
$this->mailing->update([
'status' => 'sent',
'sent_at' => now()
]);
$this->sendSummaryEmail();
}
}
protected function sendSummaryEmail(): void
{
$user = $this->mailing->updatedByUser;
if (!$user || !$user->email) {
Log::warning("MAILING: Cannot send summary email for mailing {$this->mailing->id} — no sender user found.");
return;
}
try {
$this->mailing->refresh();
Mail::to($user->email)->send(new \App\Mail\MailingSummaryMail($this->mailing));
Log::info("MAILING: Summary email sent to {$user->email} for mailing {$this->mailing->id}.");
} catch (\Exception $e) {
Log::error("MAILING: Failed to send summary email for mailing {$this->mailing->id}: " . $e->getMessage());
}
}
protected function sendErrorEmail(\Throwable $exception): void
{
$user = $this->mailing->updatedByUser;
if (!$user || !$user->email) {
Log::warning("MAILING: Cannot send error email for mailing {$this->mailing->id} — no sender user found.");
return;
}
try {
$this->mailing->refresh();
$errorMessage = $exception->getMessage();
Mail::to($user->email)->send(new \App\Mail\MailingErrorMail(
$this->mailing,
$errorMessage,
$this->locale,
count($this->recipients)
));
Log::info("MAILING: Error report email sent to {$user->email} for mailing {$this->mailing->id}.");
} catch (\Exception $e) {
Log::error("MAILING: Failed to send error report email for mailing {$this->mailing->id}: " . $e->getMessage());
}
}
/**
* Handle a job failure.
*/
public function failed(\Throwable $exception): void
{
// Check if this is a quota error and provide specific guidance
if ($this->isSMTPQuotaError($exception)) {
Log::error("Bulk mail job failed due to SMTP quota/limit for mailing {$this->mailing->id}, locale {$this->locale}. This job will be retried automatically when the quota resets.");
// Don't increment failed count for quota errors as they will be retried
return;
}
Log::error("Bulk mail job failed for mailing {$this->mailing->id}, locale {$this->locale}: " . $exception->getMessage());
// Send error report to the user who triggered the mailing
$this->sendErrorEmail($exception);
// Count all unprocessed recipients in this batch as failed
$this->mailing->refresh();
$remaining = $this->mailing->recipients_count
- $this->mailing->sent_count - $this->mailing->failed_count
- $this->mailing->skipped_count - $this->mailing->aborted_count;
$unprocessed = min(count($this->recipients), max(0, $remaining));
if ($unprocessed > 0) {
$this->mailing->increment('failed_count', $unprocessed);
}
$this->checkCompletion();
}
/**
* Send email with comprehensive error handling
*/
protected function sendEmailWithErrorHandling($email, $newsletterMail)
{
try {
// Use a more specific approach to detect SMTP errors
Mail::to($email)->send($newsletterMail);
// Additional verification: Check if we can still send (for services that don't throw immediate exceptions)
// This is a lightweight check that can help detect quota issues
$this->verifyEmailServiceHealth();
} catch (\Symfony\Component\Mailer\Exception\TransportException $e) {
// Specific handling for Symfony mailer transport exceptions
Log::error("Symfony Transport Exception for {$email}: " . $e->getMessage());
throw $e;
} catch (\Swift_TransportException $e) {
// Legacy SwiftMailer transport exceptions (if still in use)
Log::error("Swift Transport Exception for {$email}: " . $e->getMessage());
throw $e;
}
}
/**
* Check if the error is related to SMTP quota/limits
*/
protected function isSMTPQuotaError(\Throwable $e): bool
{
$message = strtolower($e->getMessage());
// Common SMTP quota/limit error patterns
$quotaPatterns = [
'email limit',
'quota exceeded',
'rate limit',
'too many emails',
'limit reached',
'billing',
'upgrade your plan',
'sending limit',
'daily limit',
'monthly limit'
];
foreach ($quotaPatterns as $pattern) {
if (strpos($message, $pattern) !== false) {
return true;
}
}
return false;
}
/**
* Lightweight check to verify email service is still operational
*/
protected function verifyEmailServiceHealth()
{
// This method can be expanded to include specific health checks
// For now, it serves as a placeholder for future enhancements
// Future implementations could include:
// - Test connection to SMTP server
// - Check service status endpoints
// - Validate remaining quota if API available
}
/**
* Set retry delay specifically for quota errors
*/
protected function retryWithQuotaDelay()
{
// Store quota error information for custom backoff
cache()->put("bulk_mail_quota_error_{$this->mailing->id}", now(), now()->addHours(24));
}
/**
* Override backoff to handle quota errors differently
*/
public function backoff(): array
{
// Check if this is a quota error retry
if (cache()->has("bulk_mail_quota_error_{$this->mailing->id}")) {
$quotaDelayHours = timebank_config('mailing.quota_error_retry_delay_hours', 6);
return [$quotaDelayHours * 3600]; // Convert hours to seconds
}
// Use original backoff logic for non-quota errors
$baseDelayMinutes = timebank_config('mailing.retry_delay_minutes', 15);
$multiplier = timebank_config('mailing.retry_multiplier', 2);
$maxDelayHours = timebank_config('mailing.max_retry_delay_hours', 24);
$maxRetries = timebank_config('mailing.max_retries', 3);
$backoffSchedule = [];
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
$delayMinutes = $baseDelayMinutes * pow($multiplier, $attempt - 1);
$delayHours = $delayMinutes / 60;
// Cap the delay at max_retry_delay_hours
if ($delayHours > $maxDelayHours) {
$delayMinutes = $maxDelayHours * 60;
}
$backoffSchedule[] = $delayMinutes * 60; // Convert to seconds
}
return $backoffSchedule;
}
/**
* Check if the error indicates an email bounce
*/
protected function isBounceError(\Exception $e): bool
{
$message = strtolower($e->getMessage());
// Common bounce error patterns
$bouncePatterns = [
'mailbox unavailable',
'user unknown',
'no such user',
'invalid recipient',
'address rejected',
'mailbox full',
'quota exceeded',
'550 ',
'551 ',
'552 ',
'553 ',
'bounce',
'undeliverable',
'does not exist',
'mailbox not found',
];
foreach ($bouncePatterns as $pattern) {
if (strpos($message, $pattern) !== false) {
return true;
}
}
return false;
}
/**
* Determine bounce type based on error message
*/
protected function determineBounceType(\Exception $e): string
{
$message = strtolower($e->getMessage());
// Hard bounce patterns (permanent failures)
$hardBouncePatterns = [
'user unknown',
'no such user',
'invalid recipient',
'address rejected',
'does not exist',
'mailbox not found',
'550 ',
'551 ',
];
foreach ($hardBouncePatterns as $pattern) {
if (strpos($message, $pattern) !== false) {
return 'hard';
}
}
// Soft bounce patterns (temporary failures)
$softBouncePatterns = [
'mailbox full',
'quota exceeded',
'552 ',
'temporarily rejected',
];
foreach ($softBouncePatterns as $pattern) {
if (strpos($message, $pattern) !== false) {
return 'soft';
}
}
return 'unknown';
}
}
+144
View File
@@ -0,0 +1,144 @@
<?php
namespace App\Jobs;
use App\Mail\NewMessageMail;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Namu\WireChat\Events\MessageCreated;
use Namu\WireChat\Models\Message;
class SendDelayedEmail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $messageId;
public $sender;
public $recipient;
public $participant;
/**
* Create a new job instance.
*
* @param MessageCreated $event
* @param mixed $sender
* @param mixed $recipient
* @param mixed $participant
* @return void
*/
public function __construct(MessageCreated $event, $sender, $recipient, $participant)
{
$this->messageId = $event->message->id;
$this->sender = $sender;
$this->recipient = $recipient;
$this->participant = $participant;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
try {
Log::info('SendDelayedEmail job starting', [
'recipient_id' => $this->recipient->id,
'recipient_type' => get_class($this->recipient),
'participant_id' => $this->participant->id ?? 'null',
'message_id' => $this->messageId,
]);
// Re-fetch the message fresh from the DB to avoid stale serialized data
$message = Message::with('conversation.participants')->find($this->messageId);
if (!$message) {
Log::info('Message no longer exists, skipping email', ['message_id' => $this->messageId]);
return;
}
// Refresh the participant to get the latest read state
$this->participant->refresh();
// Check if the message has been read since it was sent
$lastRead = $this->participant->conversation_read_at ? Carbon::parse($this->participant->conversation_read_at) : null;
$messageCreatedAt = Carbon::parse($message->created_at);
Log::info('Read status check', [
'conversation_read_at' => $lastRead ? $lastRead->toDateTimeString() : 'null',
'message_created' => $messageCreatedAt->toDateTimeString(),
]);
if ($lastRead && $lastRead->greaterThanOrEqualTo($messageCreatedAt)) {
Log::info('Recipient has already read the message', [
'recipient_id' => $this->recipient->id,
'message_id' => $this->messageId,
]);
return;
}
// Build a fresh event wrapper with the re-fetched message
$freshEvent = new MessageCreated($message);
// Get the recipient's language preference
$language = $this->recipient->lang_preference ?? config('app.locale', 'en');
Log::info('Language detection', [
'recipient_lang_preference' => $this->recipient->lang_preference ?? 'null',
'selected_language' => $language,
]);
// Send the email with the appropriate language
Mail::to($this->recipient->email)
->locale($language)
->send(new NewMessageMail(
$freshEvent,
$this->sender,
$this->recipient,
$language
));
Log::info('Email sent to recipient', [
'recipient_id' => $this->recipient->id,
'recipient_type' => get_class($this->recipient),
'email' => $this->recipient->email,
'language' => $language,
'message_id' => $this->messageId,
]);
} catch (\Exception $e) {
Log::error('Error sending SendDelayedEmail', [
'recipient_id' => $this->recipient->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
// Re-throw the exception so the job can be retried
throw $e;
}
}
/**
* The job failed to process.
*
* @param \Throwable $exception
* @return void
*/
public function failed(\Throwable $exception)
{
Log::error('SendDelayedEmail job failed', [
'recipient_id' => $this->recipient->id,
'recipient_type' => get_class($this->recipient),
'message_id' => $this->messageId,
'error' => $exception->getMessage(),
]);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Jobs;
use App\Mail\TagAddedMail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Mail;
class SendEmailNewTag implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tagId;
/**
* Create a new job instance.
*
* @param $tagId
*/
public function __construct($tagId)
{
$this->tagId = $tagId;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// Send the email notification
Mail::to(timebank_config('mail.content_admin.email'))->send(new TagAddedMail($this->tagId));
}
}
@@ -0,0 +1,55 @@
<?php
namespace App\Jobs;
use App\Mail\ProfileConvertedByAdminMail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendProfileConvertedByAdminMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
protected $profile;
protected $convertedTo;
protected $manager;
public function __construct($profile, string $convertedTo, $manager = null)
{
$this->profile = $profile;
$this->convertedTo = $convertedTo;
$this->manager = $manager;
}
public function handle()
{
if (!$this->profile->email) {
Log::warning('SendProfileConvertedByAdminMail: Profile has no email address', [
'profile_id' => $this->profile->id,
'profile_type' => get_class($this->profile),
]);
return;
}
Log::info('SendProfileConvertedByAdminMail: Sending email', [
'profile_id' => $this->profile->id,
'profile_type' => get_class($this->profile),
'converted_to' => $this->convertedTo,
'manager_id' => $this->manager?->id,
]);
Mail::to($this->profile->email)->send(
new ProfileConvertedByAdminMail($this->profile, $this->convertedTo, $this->manager)
);
Log::info('SendProfileConvertedByAdminMail: Email sent successfully');
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace App\Jobs;
use App\Mail\ProfileEditedByAdminMail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendProfileEditedByAdminMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
protected $profile;
protected $changedFields;
/**
* Create a new job instance.
*
* @param mixed $profile The profile that was edited
* @param array $changedFields Array of field names that were changed
* @return void
*/
public function __construct($profile, $changedFields = [])
{
$this->profile = $profile;
$this->changedFields = $changedFields;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if (!$this->profile->email) {
Log::warning('SendProfileEditedByAdminMail: Profile has no email address', [
'profile_id' => $this->profile->id,
'profile_type' => get_class($this->profile),
]);
return;
}
Log::info('SendProfileEditedByAdminMail: Sending email', [
'profile_id' => $this->profile->id,
'profile_type' => get_class($this->profile),
'profile_email' => $this->profile->email,
'changed_fields' => $this->changedFields,
]);
Mail::to($this->profile->email)->send(
new ProfileEditedByAdminMail($this->profile, $this->changedFields)
);
Log::info('SendProfileEditedByAdminMail: Email sent successfully');
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
namespace App\Jobs;
use App\Mail\ProfileLinkChangedMail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendProfileLinkChangedMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $tries = 3;
public $backoff = [5, 30, 300]; // wait for 5, 30, or 300 sec before worker tries again
protected $recipient;
protected $linkedProfile;
protected $action;
/**
* Create a new job instance.
*
* @param mixed $recipient The profile receiving the notification
* @param mixed $linkedProfile The profile that was attached/detached
* @param string $action Either 'attached' or 'detached'
* @return void
*/
public function __construct($recipient, $linkedProfile, $action = 'attached')
{
$this->recipient = $recipient;
$this->linkedProfile = $linkedProfile;
$this->action = $action;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Log::info('SendProfileLinkChangedMail: Job starting', [
'recipient_id' => $this->recipient->id,
'recipient_type' => get_class($this->recipient),
'recipient_email' => $this->recipient->email ?? 'NO EMAIL',
'linked_profile_id' => $this->linkedProfile->id,
'linked_profile_type' => get_class($this->linkedProfile),
'action' => $this->action,
]);
// Check if recipient has an email
if (empty($this->recipient->email)) {
Log::warning('SendProfileLinkChangedMail: Recipient has no email address', [
'recipient_id' => $this->recipient->id,
'recipient_type' => get_class($this->recipient),
]);
return;
}
try {
// Send the email to the recipient
Mail::to($this->recipient->email)->send(
new ProfileLinkChangedMail($this->recipient, $this->linkedProfile, $this->action)
);
Log::info('SendProfileLinkChangedMail: Email sent successfully', [
'recipient_email' => $this->recipient->email,
'action' => $this->action,
]);
} catch (\Exception $e) {
Log::error('SendProfileLinkChangedMail: Failed to send email', [
'recipient_email' => $this->recipient->email ?? 'NO EMAIL',
'action' => $this->action,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace App\Jobs;
use App\Mail\ReactionCreatedEmail;
use App\Mail\ReactionCreatedMail;
use Cog\Contracts\Love\Reaction\Models\Reaction;
use Cog\Laravel\Love\Reaction\Events\ReactionHasBeenAdded;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
class SendReactionCreatedMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $tries = 3;
public $backoff = [5,30,300]; // wait for 5, 30, or 300 sec before worker tries again
public $reaction;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Reaction $reaction)
{
$this->reaction = $reaction;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// Send the email to the recipient (person receiving the reaction)
Mail::to($this->reaction->getReactant()->getReactable()->email)->send(new ReactionCreatedMail($this->reaction));
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace App\Jobs;
use App\Mail\ReservationCancelledMail;
use Cog\Contracts\Love\Reaction\Models\Reaction;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
class SendReservationCancelledMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $tries = 3;
public $backoff = [5,30,300]; // wait for 5, 30, or 300 sec before worker tries again
public $data;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
\Log::info('SendReservationCancelledMail: Starting to process job', [
'reacter_email' => $this->data['reacter_email'],
'post_id' => $this->data['post_id']
]);
// Send the email to the reacter (person who cancelled the reservation)
Mail::to($this->data['reacter_email'])->send(new ReservationCancelledMail($this->data));
\Log::info('SendReservationCancelledMail: Email sent successfully', ['to' => $this->data['reacter_email']]);
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace App\Jobs;
use App\Mail\ReservationCreatedMail;
use Cog\Contracts\Love\Reaction\Models\Reaction;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
class SendReservationCreatedMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $tries = 3;
public $backoff = [5,30,300]; // wait for 5, 30, or 300 sec before worker tries again
public $reactionId;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($reactionId)
{
$this->reactionId = $reactionId;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
\Log::info('SendReservationCreatedMail: Starting to process job', ['reaction_id' => $this->reactionId]);
// Fetch the reaction from the database
$reaction = \Cog\Laravel\Love\Reaction\Models\Reaction::findOrFail($this->reactionId);
\Log::info('SendReservationCreatedMail: Reaction loaded', ['reaction_id' => $reaction->getId()]);
$reacter = $reaction->getReacter()->getReacterable();
\Log::info('SendReservationCreatedMail: Reacter loaded', [
'reacter_type' => get_class($reacter),
'reacter_id' => $reacter->id,
'reacter_email' => $reacter->email,
]);
// Send the email to the reacter (person making the reservation)
Mail::to($reacter->email)->send(new ReservationCreatedMail($reaction));
\Log::info('SendReservationCreatedMail: Email sent successfully', ['to' => $reacter->email]);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
namespace App\Jobs;
use App\Mail\ReservationUpdateMail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendReservationUpdateMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $tries = 3;
public $backoff = [5, 30, 300]; // wait for 5, 30, or 300 sec before worker tries again
protected $reacter;
protected $post;
protected $message;
protected $organizer;
/**
* Create a new job instance.
*
* @param mixed $reacter The profile who made the reservation
* @param mixed $post The post/event
* @param string $message The custom message from organizer
* @param mixed $organizer The organizer sending the message
* @return void
*/
public function __construct($reacter, $post, $message, $organizer)
{
$this->reacter = $reacter;
$this->post = $post;
$this->message = $message;
$this->organizer = $organizer;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Log::info('SendReservationUpdateMail: Starting to process job', [
'reacter_type' => get_class($this->reacter),
'reacter_id' => $this->reacter->id,
'post_id' => $this->post->id
]);
// Send the email to the reacter
Mail::to($this->reacter->email)->send(
new ReservationUpdateMail($this->reacter, $this->post, $this->message, $this->organizer)
);
Log::info('SendReservationUpdateMail: Email sent successfully', [
'to' => $this->reacter->email,
'post_id' => $this->post->id
]);
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace App\Jobs;
use App\Mail\ReversalIssued;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendReversalIssuedMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
protected $profile;
protected $bank;
protected $amount;
protected $originalTransactionId;
protected $description;
public function __construct($profile, $bank, int $amount, int $originalTransactionId, string $description)
{
$this->profile = $profile;
$this->bank = $bank;
$this->amount = $amount;
$this->originalTransactionId = $originalTransactionId;
$this->description = $description;
}
public function handle()
{
if (!$this->profile->email) {
Log::warning('SendReversalIssuedMail: Profile has no email address', [
'profile_id' => $this->profile->id,
'profile_type' => get_class($this->profile),
]);
return;
}
Mail::to($this->profile->email)->send(
new ReversalIssued($this->profile, $this->bank, $this->amount, $this->originalTransactionId, $this->description)
);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace App\Jobs;
use App\Mail\UserDeletedMail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
class SendUserDeletedMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $tries = 3;
public $backoff = [5,10,30]; // wait for 5, 10, or 30 sec before worker tries again
public $result;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($result)
{
$this->result = $result;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// Send the email
Mail::to($this->result->email)->send(new UserDeletedMail($this->result));
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class TestDelayJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $delay;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($delayInSeconds)
{
$this->delay = now()->addSeconds($delayInSeconds);
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
\Log::info('Test delay job executed at ' . now());
}
}