-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Monthly Resource Digest #594
Changes from 25 commits
4ef35eb
b26e83d
5239217
a2d59b9
d834360
c3cf138
3fcb351
4d348fe
762a4f7
90e24da
f726790
c8ba5ea
8b9363f
3d09d3a
94c620e
ea0aa76
4bf7978
5f06e88
4d65a80
5cf85c7
1383464
e802d72
488c87a
47ae386
084a471
7ab5d6e
f2174ac
39ee2d6
4076886
267f972
08ae1ae
23cc791
0fae228
6a2d9f7
431eb19
bf119c7
d1c8139
5a6c1a6
9a00c3e
0740f18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
# Dusting | ||
c380d69ba5f0813410af5eba98c3d3b83e4c288f | ||
b7707e028a9838565a51d617382b0f2690bcd5cb | ||
4d348fe5b2d842f7ec602331da5da10fc926fd61 | ||
f726790a2e2c09018532c8e7397359d443b63e70 | ||
3d09d3acf18dc8e5cf70cc30a7e42650e98575f9 | ||
4bf79783e522ac781fcb7b12cde179c306c85b60 | ||
5cf85c749558b2871c1011ef177e37f59f4421be |
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.'); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -24,10 +25,26 @@ public function update(Request $request) | |
collect($request->only(Preferences::getValidKeys()))->filter() | ||
); | ||
|
||
if (auth()->user() && $request->filled('track')) { | ||
auth()->user()->track_id = $request->input('track'); | ||
$user = auth()->user(); | ||
|
||
auth()->user()->save(); | ||
if ($user) { | ||
if ($request->filled('track')) { | ||
$user->track_id = $request->input('track'); | ||
} | ||
|
||
$request->validate([ | ||
'is_subscriber' => 'nullable|boolean', | ||
]); | ||
|
||
$user->is_subscriber = $request->has('digest-subscriber'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
|
||
if ($user->is_subscriber) { | ||
$user->unsubscribe_token = Str::random(60); | ||
} else { | ||
$user->unsubscribe_token = null; | ||
} | ||
MarcyLina marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
$user->save(); | ||
} | ||
|
||
session()->flash('toast', 'Your preferences were saved.'); | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,21 @@ | ||||||||||||||||
<?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->is_subscriber = false; | ||||||||||||||||
$user->unsubscribe_token = null; | ||||||||||||||||
$user->save(); | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
When I can, I prefer using update because it feels more concise (despite being one more line) and tidy. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd do this in a single |
||||||||||||||||
|
||||||||||||||||
session()->flash('toast', 'You have been unsubscribed.'); | ||||||||||||||||
|
||||||||||||||||
return redirect()->route('welcome', ['locale' => $locale]); | ||||||||||||||||
} | ||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,51 @@ | ||||||||||||||
<?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 $resources; | ||||||||||||||
public $user; | ||||||||||||||
public $unsubscribeUrl; | ||||||||||||||
|
||||||||||||||
public function __construct($resources, $user, $unsubscribeUrl) | ||||||||||||||
{ | ||||||||||||||
$this->resources = $resources; | ||||||||||||||
$this->user = $user; | ||||||||||||||
$this->unsubscribeUrl = $unsubscribeUrl; | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be rewritten like the following and then the properties on lines 16-18 can be removed.
Suggested change
|
||||||||||||||
|
||||||||||||||
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 []; | ||||||||||||||
} | ||||||||||||||
} |
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')->default(false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like to add This option is only available in MySQL and MariaDB, so we couldn't use it for a Postgres project. |
||
}); | ||
} | ||
|
||
public function down(): void | ||
{ | ||
Schema::table('users', function (Blueprint $table) { | ||
$table->dropColumn('is_subscriber'); | ||
}); | ||
} | ||
}; |
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'); | ||
}); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
|
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> |
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> |
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the
user.preferences.update
route be moved up into theauth
middleware group? If so that would avoid the need for this if condition.