Hide draft articles from the front page and mark drafts when viewed directly #140

Merged
jonny merged 1 commit from draft_posts_page_tweak into develop 2026-09-26 17:29:30 +02:00
13 changed files with 84 additions and 7 deletions

View file

@ -17,7 +17,7 @@ class ArticlesController extends Controller
*/ */
public function index(?int $year = null, ?int $month = null): View public function index(?int $year = null, ?int $month = null): View
{ {
$articles = Article::where('published', '1') $articles = Article::published()
->date($year, $month) ->date($year, $month)
->orderBy('updated_at', 'desc') ->orderBy('updated_at', 'desc')
->simplePaginate(5); ->simplePaginate(5);

View file

@ -15,7 +15,7 @@ class FeedsController extends Controller
*/ */
public function blogJson(): JsonResponse public function blogJson(): JsonResponse
{ {
$articles = Article::where('published', '1')->latest('updated_at')->take(20)->get(); $articles = Article::published()->latest('updated_at')->take(20)->get();
$data = [ $data = [
'version' => 'https://jsonfeed.org/version/1.1', 'version' => 'https://jsonfeed.org/version/1.1',
'title' => 'The JSON Feed for '.config('user.display_name').'’s blog', 'title' => 'The JSON Feed for '.config('user.display_name').'’s blog',
@ -89,7 +89,7 @@ class FeedsController extends Controller
*/ */
public function blogJf2(): JsonResponse public function blogJf2(): JsonResponse
{ {
$articles = Article::where('published', '1')->latest('updated_at')->take(20)->get(); $articles = Article::published()->latest('updated_at')->take(20)->get();
$items = []; $items = [];
foreach ($articles as $article) { foreach ($articles as $article) {
$items[] = [ $items[] = [

View file

@ -26,7 +26,7 @@ class FrontPageController extends Controller
->withCount(['webmentions AS reposts' => function ($query) { ->withCount(['webmentions AS reposts' => function ($query) {
$query->where('type', 'repost-of'); $query->where('type', 'repost-of');
}])->get(); }])->get();
$articles = Article::latest()->get(); $articles = Article::published()->latest()->get();
$bookmarks = Bookmark::latest()->with('tags')->get(); $bookmarks = Bookmark::latest()->with('tags')->get();
$likes = Like::latest()->get(); $likes = Like::latest()->get();

View file

@ -101,6 +101,14 @@ class Article extends Model
); );
} }
/**
* Scope a query to only include published (non-draft) articles.
*/
public function scopePublished(Builder $query): Builder
{
return $query->where('published', 1);
}
/** /**
* Scope a query to only include articles from a particular year/month. * Scope a query to only include articles from a particular year/month.
*/ */

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -63,3 +63,16 @@
} }
} }
@layer components {
.draft-notice {
margin-block-end: 2ex;
padding: 1ex 1em;
border: 2px dashed var(--clr-border);
border-radius: 8px;
background: light-dark(
oklch(95% 0.08 85deg),
oklch(30% 0.08 85deg)
);
}
}

View file

@ -1,7 +1,18 @@
@extends('master') @extends('master')
@section('title'){{ strip_tags($article->title) }} « @stop @section('title'){{ $article->published ? '' : '[Draft] ' }}{{ strip_tags($article->title) }} « @stop
@if (! $article->published)
@push('head')
<meta name="robots" content="noindex, nofollow">
@endpush
@endif
@section('content') @section('content')
@unless ($article->published)
<aside class="draft-notice" role="note">
<strong>Draft</strong> — this post hasn’t been published yet. It isn’t listed on the site or in feeds, so only people with this link can see it.
</aside>
@endunless
@include('templates.article', ['article' => $article]) @include('templates.article', ['article' => $article])
@stop @stop

View file

@ -22,6 +22,7 @@
@if (File::exists(public_path('gpg.key'))) @if (File::exists(public_path('gpg.key')))
<link rel="pgpkey" href="{{ config('app.url')}}/gpg.key"> <link rel="pgpkey" href="{{ config('app.url')}}/gpg.key">
@endif @endif
@stack('head')
</head> </head>
<body> <body>
<header> <header>

View file

@ -29,6 +29,39 @@ class ArticlesTest extends TestCase
$response->assertViewIs('articles.show'); $response->assertViewIs('articles.show');
} }
#[Test]
public function articles_page_does_not_list_drafts(): void
{
Article::factory()->create(['title' => 'Published Article']);
Article::factory()->create(['title' => 'Draft Article', 'published' => 0]);
$this->get('/blog')
->assertSee('Published Article')
->assertDontSee('Draft Article');
}
#[Test]
public function draft_article_is_viewable_and_marked_as_draft(): void
{
$article = Article::factory()->create(['published' => 0]);
$this->get($article->link)
->assertOk()
->assertSee('draft-notice', false)
->assertSee('<meta name="robots" content="noindex, nofollow">', false)
->assertSee('<title>[Draft] ', false);
}
#[Test]
public function published_article_is_not_marked_as_draft(): void
{
$article = Article::factory()->create();
$this->get($article->link)
->assertDontSee('draft-notice', false)
->assertDontSee('noindex', false);
}
#[Test] #[Test]
public function wrong_date_in_url_redirects_to_correct_date() public function wrong_date_in_url_redirects_to_correct_date()
{ {

View file

@ -31,4 +31,15 @@ class FrontPageTest extends TestCase
->assertSee('https://example.com') ->assertSee('https://example.com')
->assertSee('Like 1'); ->assertSee('Like 1');
} }
#[Test]
public function front_page_does_not_show_draft_articles(): void
{
Article::factory()->create(['title' => 'Published Article']);
Article::factory()->create(['title' => 'Draft Article', 'published' => 0]);
$this->get('/')
->assertSee('Published Article')
->assertDontSee('Draft Article');
}
} }