Skip to content

Commit

Permalink
Merge pull request #594 from tighten/mla/monthly-resource-digest
Browse files Browse the repository at this point in the history
Monthly Resource Digest
  • Loading branch information
MarcyLina authored Sep 27, 2024
2 parents 107aeec + 0740f18 commit 69f5b6c
Show file tree
Hide file tree
Showing 18 changed files with 521 additions and 219 deletions.
9 changes: 9 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# Dusting
c380d69ba5f0813410af5eba98c3d3b83e4c288f
b7707e028a9838565a51d617382b0f2690bcd5cb
4d348fe5b2d842f7ec602331da5da10fc926fd61
f726790a2e2c09018532c8e7397359d443b63e70
3d09d3acf18dc8e5cf70cc30a7e42650e98575f9
4bf79783e522ac781fcb7b12cde179c306c85b60
5cf85c749558b2871c1011ef177e37f59f4421be
39ee2d6c68b65918a9104311d4d1681a796c1dc7
0fae228289851aea2d79b09b546dcf5f7f852446
bf119c7c9689569a03c527696008eadd5ad5a29c
9a00c3e9425e213ceca32b57b14dad1f785f99ae
44 changes: 44 additions & 0 deletions app/Console/Commands/SendResourceDigestEmail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace App\Console\Commands;

use App\Mail\ResourceDigestEmail;
use App\Models\Resource;
use App\Models\User;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;

class SendResourceDigestEmail extends Command
{
protected $signature = 'mail:send-resource-digest-email';
protected $description = 'Send the monthly resource digest email';

public function handle()
{
$resources = Resource::where('created_at', '>=', Carbon::now()->subDays(30))->get();

if ($resources->isEmpty()) {
$this->info('No resources created in the last 30 days. Email not sent.');

return;
}

User::where('is_subscriber', true)->chunk(100, function ($subscribedUsers) use ($resources) {
foreach ($subscribedUsers as $user) {
try {
$locale = $user->locale ?? 'en';
$unsubscribeUrl = route('unsubscribe', ['token' => $user->unsubscribe_token, 'locale' => $locale]);

Mail::to($user->email)->queue(new ResourceDigestEmail($resources, $user, $unsubscribeUrl));
} catch (Exception $e) {
Log::error('Failed to send email to ' . $user->email . ': ' . $e->getMessage());
}
}
});

$this->info('Monthly resource digest sent successfully to all subscribed users.');
}
}
5 changes: 4 additions & 1 deletion app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

namespace App\Console;

use App\Console\Commands\SendResourceDigestEmail;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
protected $commands = [
//
SendResourceDigestEmail::class,
];

/**
Expand All @@ -18,6 +19,8 @@ protected function schedule(Schedule $schedule): void
{
$schedule->command('resource:expired -N')
->weeklyOn(Schedule::FRIDAY, '06:00');

$schedule->command('mail:send-resource-digest-email')->monthly();
}

/**
Expand Down
18 changes: 15 additions & 3 deletions app/Http/Controllers/PreferenceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Facades\Preferences;
use App\Preferences\ResourceLanguagePreference;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\View\View;

class PreferenceController extends Controller
Expand All @@ -24,12 +25,23 @@ public function update(Request $request)
collect($request->only(Preferences::getValidKeys()))->filter()
);

if (auth()->user() && $request->filled('track')) {
auth()->user()->track_id = $request->input('track');
$request->validate([
'is_subscriber' => 'nullable|boolean',
'track_id' => 'nullable|integer',
]);

$user = auth()->user();

auth()->user()->save();
if ($request->filled('track')) {
$user->track_id = $request->input('track');
}

$user->is_subscriber = $request->has('digest-subscriber');

$user->unsubscribe_token = $user->is_subscriber ? Str::random(60) : null;

$user->save();

session()->flash('toast', 'Your preferences were saved.');

if ($request->wantsJson()) {
Expand Down
22 changes: 22 additions & 0 deletions app/Http/Controllers/SubscriptionController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Http\Controllers;

use App\Models\User;

class SubscriptionController extends Controller
{
public function destroy($locale, $token)
{
$user = User::where('unsubscribe_token', $token)->first();

$user->update([
'is_subscriber' => false,
'unsubscribe_token' => null,
]);

session()->flash('toast', 'You have been unsubscribed.');

return redirect()->route('welcome', ['locale' => $locale]);
}
}
42 changes: 42 additions & 0 deletions app/Mail/ResourceDigestEmail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class ResourceDigestEmail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;

public function __construct(public $resources, public $user, public $unsubscribeUrl) {}

public function envelope()
{
return new Envelope(
subject: 'New Onramp Resources!',
from: '[email protected]',
);
}

public function content()
{
return new Content(
markdown: 'emails.resource-digest',
with: [
'resources' => $this->resources,
'user' => $this->user,
'unsubscribeUrl' => $this->unsubscribeUrl,
],
);
}

public function attachments(): array
{
return [];
}
}
5 changes: 4 additions & 1 deletion app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ class User extends Authenticatable
];

protected $hidden = [
'password', 'remember_token', 'github_token',
'password',
'remember_token',
'github_token',
];

protected $casts = [
'email_verified_at' => 'datetime',
'preferences' => 'object',
'is_subscriber' => 'boolean',
];

public function track()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_subscriber')->after('email_verified_at')->default(false);
});
}

public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('is_subscriber');
});
}
};
26 changes: 26 additions & 0 deletions database/migrations/2024_08_09_164523_create_jobs_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}

public function down(): void
{
Schema::dropIfExists('jobs');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('unsubscribe_token', 60)->unique()->nullable();
});
}

public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('unsubscribe_token');
});
}
};
8 changes: 7 additions & 1 deletion database/seeders/ContentSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ class ContentSeeder extends Seeder
public function run(): void
{
User::factory()->create([
'email' => 'testuser@tighten.co',
'email' => 'testadmin@tighten.co',
'password' => bcrypt('password'),
'role' => 'admin',
]);

User::factory()->create([
'email' => '[email protected]',
'password' => bcrypt('password'),
'role' => 'user',
]);

$seedsDirectory = config('seeder.directory', 'database/json');

$seeds = $this->getSeedFiles($seedsDirectory);
Expand Down
14 changes: 6 additions & 8 deletions resources/views/components/button/primary.blade.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
@props([
'href' => null,
'href' => null,
])

<x-button.base
:href="$href"
{{ $attributes->merge([
'class' => 'focus:bg-white focus:text-purple bg-purple hover:bg-white hover:text-purple hover:no-underline focus:outline-none border-purple active:bg-white active:text-purple'
]) }}
>
{{ $slot }}
<x-button.base :href="$href" {{ $attributes->merge([
'class' => 'focus:bg-white focus:text-purple bg-purple hover:bg-white hover:text-purple hover:no-underline focus:outline-none hover:border-purple focus:border-purple active:bg-white active:text-purple'
]) }}
>
{{ $slot }}
</x-button.base>
23 changes: 11 additions & 12 deletions resources/views/emails/rejected-resource.blade.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
@component('mail::message')
Hello {{ $user->name }},
<x-mail::message>
Hello {{ $user->name }},

The resource you suggested on [Onramp]({{ route('welcome', ['locale' => 'en']) }}) has been rejected by one of our reviewers:
The resource you suggested on [Onramp]({{ route('welcome', ['locale' => 'en']) }}) has been rejected by one of our reviewers:

@component('mail::panel')
{{ $resource->name }}<br>
[Resource URL]({{ $resource->url }})
###### *Submitted on {{ $resource->created_at->format('d-m-Y') }}*
<x-mail::panel>
{{ $resource->name }}<br>
[Resource URL]({{ $resource->url }})
###### *Submitted on {{ $resource->created_at->format('d-m-Y') }}*

**{{ $resource->rejected_reason }}**
@endcomponent
**{{ $resource->rejected_reason }}**
</x-mail::panel>

We thank you for your contribution and encourage you to submit new resources that may be useful to the community.

@endcomponent
We thank you for your contribution and encourage you to submit new resources that may be useful to the community.
</x-mail::message>
19 changes: 19 additions & 0 deletions resources/views/emails/resource-digest.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<x-mail::message>
# Here are the latest resources:

<x-mail::panel>
@foreach ($resources as $resource)
- [{{ $resource['name'] }}]({{ $resource['url'] }})
Added on {{ \Carbon\Carbon::parse($resource['created_at'])->format('F j, Y') }}

@endforeach
</x-mail::panel>

### Happy Coding!

### Your friends at {{ config('app.name') }}

<x-mail::button :url="$unsubscribeUrl">
Unsubscribe
</x-mail::button>
</x-mail::message>
Loading

0 comments on commit 69f5b6c

Please sign in to comment.