Skip to content

Commit 47f672f

Browse files
authored
Merge pull request #21 from laravelcm/feature-article
Feature article
2 parents 5c46753 + 7384df4 commit 47f672f

File tree

17 files changed

+528
-24
lines changed

17 files changed

+528
-24
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ GITHUB_REDIRECT=${APP_URL}/auth/github/callback
5555
TWITTER_CLIENT_ID=
5656
TWITTER_CLIENT_SECRET=
5757
TWITTER_REDIRECT=${APP_URL}/auth/twitter/callback
58+
TWITTER_CONSUMER_KEY=
59+
TWITTER_CONSUMER_SECRET=
60+
TWITTER_ACCESS_TOKEN=
61+
TWITTER_ACCESS_SECRET=
5862

5963
SLACK_WEBHOOK_URL=
6064
SLACK_TEAM_NAME="Laravel Cameroun"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use App\Models\Article;
6+
use App\Notifications\PostArticleToTelegram as PostArticleToTelegramNotification;
7+
use Illuminate\Console\Command;
8+
use Illuminate\Notifications\AnonymousNotifiable;
9+
10+
class PostArticleToTelegram extends Command
11+
{
12+
protected $signature = 'lcm:post-article-to-telegram';
13+
14+
protected $description = 'Posts the latest shared article to Telegram';
15+
16+
public function handle(AnonymousNotifiable $notifiable): void
17+
{
18+
if ($article = Article::nexForSharingToTelegram()) {
19+
$notifiable->notify(new PostArticleToTelegramNotification($article));
20+
21+
$article->markAsPublish();
22+
}
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use App\Models\Article;
6+
use App\Notifications\PostArticleToTwitter as PostArticleToTwitterNotification;
7+
use Illuminate\Console\Command;
8+
use Illuminate\Notifications\AnonymousNotifiable;
9+
10+
class PostArticleToTwitter extends Command
11+
{
12+
protected $signature = 'lcm:post-article-to-twitter';
13+
14+
protected $description = 'Posts the latest unshared article to Twitter';
15+
16+
public function handle(AnonymousNotifiable $notifiable): void
17+
{
18+
if ($article = Article::nextForSharing()) {
19+
$notifiable->notify(new PostArticleToTwitterNotification($article));
20+
21+
$article->markAsShared();
22+
}
23+
}
24+
}

app/Console/Kernel.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ class Kernel extends ConsoleKernel
2525
protected function schedule(Schedule $schedule)
2626
{
2727
$schedule->command('media-library:delete-old-temporary-uploads')->daily();
28-
$schedule->command('sitemap:generate')->daily();
2928
$schedule->command('lcm:delete-old-unverified-users')->daily();
29+
$schedule->command('lcm:post-article-to-twitter')->twiceDaily(12, 16);
30+
$schedule->command('lcm:post-article-to-telegram')->twiceDaily(13, 17);
31+
$schedule->command('sitemap:generate')->daily();
3032
}
3133

3234
/**

app/Http/Controllers/ArticlesController.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ public function show(Article $article)
2929
404
3030
);
3131

32+
seo()
33+
->title($article->title)
34+
->description($article->excerpt(100))
35+
->image($article->getFirstMediaUrl('media'))
36+
->twitterTitle($article->title)
37+
->twitterDescription($article->excerpt(100))
38+
->twitterImage($article->getFirstMediaUrl('media'))
39+
->twitterSite('laravelcm')
40+
->withUrl();
41+
3242
return view('articles.show', compact('article'));
3343
}
3444

app/Models/Article.php

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class Article extends Model implements ReactableInterface, HasMedia, Viewable
4747
'tweet_id',
4848
'submitted_at',
4949
'approved_at',
50+
'declined_at',
5051
'shared_at',
5152
'sponsored_at',
5253
];
@@ -59,6 +60,7 @@ class Article extends Model implements ReactableInterface, HasMedia, Viewable
5960
protected $casts = [
6061
'submitted_at' => 'datetime',
6162
'approved_at' => 'datetime',
63+
'declined_at' => 'datetime',
6264
'shared_at' => 'datetime',
6365
'sponsored_at' => 'datetime',
6466
'show_toc' => 'boolean',
@@ -169,6 +171,16 @@ public function isNotApproved(): bool
169171
return $this->approved_at === null;
170172
}
171173

174+
public function isDeclined(): bool
175+
{
176+
return ! $this->isNotDeclined();
177+
}
178+
179+
public function isNotDeclined(): bool
180+
{
181+
return $this->declined_at === null;
182+
}
183+
172184
public function isPublished(): bool
173185
{
174186
return ! $this->isNotPublished();
@@ -201,7 +213,7 @@ public function isShared(): bool
201213

202214
public function isAwaitingApproval(): bool
203215
{
204-
return $this->isSubmitted() && $this->isNotApproved();
216+
return $this->isSubmitted() && $this->isNotApproved() && $this->isNotDeclined();
205217
}
206218

207219
public function isNotAwaitingApproval(): bool
@@ -227,7 +239,8 @@ public function scopeNotApproved(Builder $query): Builder
227239
public function scopeAwaitingApproval(Builder $query): Builder
228240
{
229241
return $query->submitted()
230-
->notApproved();
242+
->notApproved()
243+
->notDeclined();
231244
}
232245

233246
public function scopePublished(Builder $query): Builder
@@ -240,7 +253,8 @@ public function scopeNotPublished(Builder $query): Builder
240253
{
241254
return $query->where(function ($query) {
242255
$query->whereNull('submitted_at')
243-
->orWhereNull('approved_at');
256+
->orWhereNull('approved_at')
257+
->orWhereNotNull('declined_at');
244258
});
245259
}
246260

@@ -264,6 +278,16 @@ public function scopeNotShared(Builder $query): Builder
264278
return $query->whereNull('shared_at');
265279
}
266280

281+
public function scopeDeclined(Builder $query): Builder
282+
{
283+
return $query->whereNotNull('declined_at');
284+
}
285+
286+
public function scopeNotDeclined(Builder $query): Builder
287+
{
288+
return $query->whereNull('declined_at');
289+
}
290+
267291
public function scopeForTag(Builder $query, string $tag): Builder
268292
{
269293
return $query->whereHas('tags', function ($query) use ($tag) {
@@ -305,4 +329,18 @@ public static function nextForSharing(): ?self
305329
->orderBy('submitted_at', 'asc')
306330
->first();
307331
}
332+
333+
public static function nexForSharingToTelegram(): ?self
334+
{
335+
return self::shared()
336+
->published()
337+
->whereNull('tweet_id')
338+
->orderBy('submitted_at', 'asc')
339+
->first();
340+
}
341+
342+
public function markAsPublish()
343+
{
344+
$this->update(['tweet_id' => $this->author->id]);
345+
}
308346
}

app/Models/Tag.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ public function name(): string
4040
return $this->name;
4141
}
4242

43-
public function slug(): string
44-
{
45-
return $this->slug;
46-
}
47-
4843
public function articles(): MorphToMany
4944
{
5045
return $this->morphedByMany(Article::class, 'taggable');
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace App\Notifications;
4+
5+
use App\Models\Article;
6+
use Illuminate\Bus\Queueable;
7+
use Illuminate\Notifications\Notification;
8+
use NotificationChannels\Telegram\TelegramChannel;
9+
use NotificationChannels\Telegram\TelegramMessage;
10+
11+
class PostArticleToTelegram extends Notification
12+
{
13+
use Queueable;
14+
15+
public function __construct(public Article $article)
16+
{
17+
}
18+
19+
public function via($notifiable)
20+
{
21+
return [TelegramChannel::class];
22+
}
23+
24+
public function toTelegram($notifiable)
25+
{
26+
return TelegramMessage::create()
27+
->to('@laravelcm')
28+
->content("{$this->article->title} " . route('articles.show', $this->article->slug()));
29+
}
30+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace App\Notifications;
4+
5+
use App\Models\Article;
6+
use Illuminate\Bus\Queueable;
7+
use Illuminate\Notifications\Notification;
8+
use NotificationChannels\Twitter\TwitterChannel;
9+
use NotificationChannels\Twitter\TwitterStatusUpdate;
10+
11+
class PostArticleToTwitter extends Notification
12+
{
13+
use Queueable;
14+
15+
public function __construct(public Article $article)
16+
{
17+
}
18+
19+
public function via($notifiable)
20+
{
21+
return [TwitterChannel::class];
22+
}
23+
24+
public function toTwitter($notifiable)
25+
{
26+
return new TwitterStatusUpdate($this->generateTweet());
27+
}
28+
29+
public function generateTweet()
30+
{
31+
$title = $this->article->title;
32+
$url = route('articles.show', $this->article->slug());
33+
$author = $this->article->author;
34+
$author = $author->twitter() ? "@{$author->twitter()}" : $author->name;
35+
36+
return "{$title} by {$author}\n\n{$url}";
37+
}
38+
}

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"php": "^8.0",
99
"ext-fileinfo": "*",
1010
"ext-json": "*",
11+
"archtechx/laravel-seo": "^0.2.2",
1112
"blade-ui-kit/blade-heroicons": "^1.2",
1213
"blade-ui-kit/blade-ui-kit": "^0.2.0",
1314
"cyrildewit/eloquent-viewable": "^6.0",
@@ -18,6 +19,7 @@
1819
"guzzlehttp/guzzle": "^7.0.1",
1920
"jenssegers/agent": "^2.6",
2021
"laravel-notification-channels/telegram": "^0.7.0",
22+
"laravel-notification-channels/twitter": "^5.1",
2123
"laravel/fortify": "^1.7",
2224
"laravel/framework": "^8.12",
2325
"laravel/slack-notification-channel": "^2.3",

0 commit comments

Comments
 (0)