feat: restore sablog browse modes and clean imported HTML
Keep the public site readable after import (nbsp, highlighter, wrapping code, compact pager) and finish the admin extras already in this tree: sticky levels, preview, page cache, and live llms.txt. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+61
-7
@@ -2,25 +2,25 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Support\MediaDisk;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Xpier\FilamentMediaLibrary\Models\MediaLibrary;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
use Spatie\Tags\HasTags;
|
||||
use Xpier\FilamentMediaLibrary\Models\MediaLibrary;
|
||||
|
||||
class Post extends Model implements HasMedia
|
||||
{
|
||||
use HasFactory;
|
||||
use InteractsWithMedia;
|
||||
use HasTags;
|
||||
use InteractsWithMedia;
|
||||
|
||||
protected $fillable = [
|
||||
'category_id',
|
||||
@@ -34,6 +34,7 @@ class Post extends Model implements HasMedia
|
||||
'cover_media_id',
|
||||
'status',
|
||||
'is_sticky',
|
||||
'sticky_level',
|
||||
'close_comment',
|
||||
'read_password',
|
||||
'views',
|
||||
@@ -45,10 +46,63 @@ class Post extends Model implements HasMedia
|
||||
protected $casts = [
|
||||
'meta' => 'array',
|
||||
'is_sticky' => 'boolean',
|
||||
'sticky_level' => 'integer',
|
||||
'close_comment' => 'boolean',
|
||||
'published_at' => 'datetime',
|
||||
];
|
||||
|
||||
public const STICKY_NONE = 0;
|
||||
|
||||
public const STICKY_LIGHT = 1;
|
||||
|
||||
public const STICKY_STRONG = 2;
|
||||
|
||||
public const STICKY_HEADLINE = 3;
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function stickyLevelOptions(): array
|
||||
{
|
||||
return [
|
||||
self::STICKY_NONE => '不置顶',
|
||||
self::STICKY_LIGHT => '轻置顶',
|
||||
self::STICKY_STRONG => '强置顶',
|
||||
self::STICKY_HEADLINE => '置顶头条',
|
||||
];
|
||||
}
|
||||
|
||||
public function stickyLabel(): ?string
|
||||
{
|
||||
$level = (int) $this->sticky_level;
|
||||
|
||||
return $level > 0 ? (self::stickyLevelOptions()[$level] ?? '置顶') : null;
|
||||
}
|
||||
|
||||
public function isPinned(): bool
|
||||
{
|
||||
return (int) $this->sticky_level > 0 || $this->is_sticky;
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (Post $post): void {
|
||||
$level = (int) ($post->sticky_level ?? 0);
|
||||
$post->sticky_level = max(self::STICKY_NONE, min(self::STICKY_HEADLINE, $level));
|
||||
|
||||
if ($post->is_sticky && $post->sticky_level < self::STICKY_LIGHT) {
|
||||
$post->sticky_level = self::STICKY_LIGHT;
|
||||
}
|
||||
|
||||
$post->is_sticky = $post->sticky_level > self::STICKY_NONE;
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeOrderedForList(Builder $query): Builder
|
||||
{
|
||||
return $query->orderByDesc('sticky_level')->latest('published_at');
|
||||
}
|
||||
|
||||
public function category(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Category::class);
|
||||
@@ -107,8 +161,8 @@ class Post extends Model implements HasMedia
|
||||
|
||||
public function registerMediaCollections(): void
|
||||
{
|
||||
$this->addMediaCollection('attachments')->useDisk(\App\Support\MediaDisk::name());
|
||||
$this->addMediaCollection('cover')->useDisk(\App\Support\MediaDisk::name())
|
||||
$this->addMediaCollection('attachments')->useDisk(MediaDisk::name());
|
||||
$this->addMediaCollection('cover')->useDisk(MediaDisk::name())
|
||||
->singleFile()
|
||||
->registerMediaConversions(function () {
|
||||
$this->addMediaConversion('card')->width(1200)->height(630)->sharpen(1);
|
||||
@@ -153,13 +207,13 @@ class Post extends Model implements HasMedia
|
||||
return '';
|
||||
}
|
||||
|
||||
$text = strip_tags($this->content);
|
||||
$text = (new \App\Blog\Support\SablogHtmlNormalizer)->plainText((string) $this->content);
|
||||
// 付费块不进入摘要,避免付费内容泄漏到列表页/SEO meta
|
||||
$text = preg_replace('/\[paid\].*?\[\/paid\]/s', '', $text) ?? $text;
|
||||
$text = preg_replace('/!\[([^\]]*)\]\((?:media|attach):\d+\)/i', '$1', $text) ?? $text;
|
||||
$text = preg_replace('/\[([^\]]*)\]\((?:media|attach):\d+\)/i', '$1', $text) ?? $text;
|
||||
$text = preg_replace('/\[[^\]]*\]/', '', $text) ?? $text;
|
||||
|
||||
return mb_substr($text, 0, 200);
|
||||
return mb_substr(trim($text), 0, 200);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user