M2: 主题系统(sablog经典+modern简约)+ 前台全部页面 + 老 URL 301 兼容 + markdown 双份导入
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Post;
|
||||
use Illuminate\Console\Command;
|
||||
use League\HTMLToMarkdown\HtmlConverter;
|
||||
|
||||
class ConvertContent extends Command
|
||||
{
|
||||
protected $signature = 'content:convert
|
||||
{--all : 转换全部 html 文章}
|
||||
{id? : 单篇文章 ID}
|
||||
{--dry-run : 只预览不写库}';
|
||||
|
||||
protected $description = '把文章内容从 HTML 转换为 Markdown(老 sablog 数据迁移用)';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$query = Post::query()->where('content_format', 'html');
|
||||
|
||||
if ($id = $this->argument('id')) {
|
||||
$query->where('id', $id);
|
||||
} elseif (! $this->option('all')) {
|
||||
$this->error('请指定文章 ID 或使用 --all');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$converter = new HtmlConverter([
|
||||
'strip_tags' => false,
|
||||
'hard_break' => true,
|
||||
]);
|
||||
|
||||
$renderer = app(\App\Blog\Services\PostContentRenderer::class);
|
||||
|
||||
$posts = $query->get();
|
||||
if ($posts->isEmpty()) {
|
||||
$this->info('没有需要转换的 HTML 文章');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$bar = $this->output->createProgressBar($posts->count());
|
||||
$bar->start();
|
||||
|
||||
foreach ($posts as $post) {
|
||||
// [attach=xx] 是 markdown 保留字符,先换成令牌,渲染时再解析
|
||||
$markdown = $converter->convert($renderer->toMarkdownSafeTokens($post->content));
|
||||
|
||||
if ($this->option('dry-run')) {
|
||||
$this->newLine();
|
||||
$this->line("[{$post->id}] {$post->title}");
|
||||
$this->line(mb_substr($markdown, 0, 200));
|
||||
$bar->advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
$post->content = $markdown;
|
||||
$post->content_format = 'markdown';
|
||||
$post->save();
|
||||
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info('转换完成:'.($this->option('dry-run') ? '预览模式,未写库' : $posts->count().' 篇已转换为 Markdown'));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ class SablogImport extends Command
|
||||
{--prefix=sablog_ : 老表前缀}
|
||||
{--attachments-dir= : 老 attachments 目录(用于同步文件,可选)}
|
||||
{--sync-attachments : 同步附件文件到新存储}
|
||||
{--convert-markdown : 导入后把 HTML 内容转为 Markdown([attach=xx] 转成 {{attach:xx}} 令牌,渲染时解析)}
|
||||
{--fresh : 清空目标表后重新导入}';
|
||||
|
||||
protected $description = '从 SaBlog-X 老库脚本迁移数据到 laralog';
|
||||
@@ -63,6 +64,10 @@ class SablogImport extends Command
|
||||
$this->importLinks();
|
||||
$this->importAttachments();
|
||||
|
||||
if ($this->option('convert-markdown')) {
|
||||
$this->convertToMarkdown();
|
||||
}
|
||||
|
||||
$this->syncCounters();
|
||||
|
||||
$this->newLine();
|
||||
@@ -157,7 +162,7 @@ class SablogImport extends Command
|
||||
$count = 0;
|
||||
foreach ($this->rows('categories') as $row) {
|
||||
$slug = SlugGenerator::make($row['name'], 'categories', 'slug', ignoreId: (int) $row['cid'], fallback: 'category-'.$row['cid']);
|
||||
Category::query()->updateOrCreate(['id' => $row['cid']], [
|
||||
DB::table('categories')->updateOrInsert(['id' => (int) $row['cid']], [
|
||||
'name' => $row['name'],
|
||||
'slug' => $slug,
|
||||
'display_order' => (int) $row['displayorder'],
|
||||
@@ -182,7 +187,7 @@ class SablogImport extends Command
|
||||
$count = 0;
|
||||
foreach ($this->rows('users') as $row) {
|
||||
$email = $this->legacyEmail($row['username'], $count);
|
||||
$user = User::query()->updateOrCreate(['id' => $row['userid']], [
|
||||
DB::table('users')->updateOrInsert(['id' => (int) $row['userid']], [
|
||||
'name' => $row['username'],
|
||||
'email' => $email,
|
||||
'password' => null,
|
||||
@@ -197,8 +202,9 @@ class SablogImport extends Command
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$user = User::find((int) $row['userid']);
|
||||
$role = $groupMap[(int) $row['groupid']] ?? 'member';
|
||||
if (! $user->hasRole($role)) {
|
||||
if ($user && ! $user->hasRole($role)) {
|
||||
$user->assignRole($role);
|
||||
}
|
||||
$count++;
|
||||
@@ -222,13 +228,14 @@ class SablogImport extends Command
|
||||
$status = (int) $row['visible'] === 1 ? 'published' : 'draft';
|
||||
$dateline = date('Y-m-d H:i:s', (int) $row['dateline']);
|
||||
|
||||
Post::query()->updateOrCreate(['id' => $row['articleid']], [
|
||||
DB::table('posts')->updateOrInsert(['id' => (int) $row['articleid']], [
|
||||
'category_id' => (int) $row['cid'] ?: null,
|
||||
'user_id' => (int) $row['uid'] ?: null,
|
||||
'title' => $row['title'],
|
||||
'slug' => $slug,
|
||||
'excerpt' => $row['description'] ?: null,
|
||||
'content' => $row['content'],
|
||||
'content_format' => 'html',
|
||||
'keywords' => $row['keywords'] ?: null,
|
||||
'status' => $status,
|
||||
'is_sticky' => (bool) $row['stick'],
|
||||
@@ -248,6 +255,7 @@ class SablogImport extends Command
|
||||
|
||||
private function importTags(): void
|
||||
{
|
||||
$legacyMap = [];
|
||||
$count = 0;
|
||||
foreach ($this->rows('tags') as $row) {
|
||||
$tagName = trim($row['tag']);
|
||||
@@ -262,6 +270,8 @@ class SablogImport extends Command
|
||||
$tag->save();
|
||||
}
|
||||
|
||||
$legacyMap[(int) $row['tagid']] = $tag->id;
|
||||
|
||||
foreach (explode(',', (string) $row['aids']) as $aid) {
|
||||
$post = Post::find((int) trim($aid));
|
||||
if ($post && ! $post->tags()->where('tags.id', $tag->id)->exists()) {
|
||||
@@ -271,6 +281,10 @@ class SablogImport extends Command
|
||||
$count++;
|
||||
}
|
||||
|
||||
if ($legacyMap) {
|
||||
Setting::set('legacy_tags', $legacyMap);
|
||||
}
|
||||
|
||||
$this->report['tags'] = $count;
|
||||
}
|
||||
|
||||
@@ -278,7 +292,7 @@ class SablogImport extends Command
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->rows('comments') as $row) {
|
||||
Comment::query()->updateOrCreate(['id' => $row['commentid']], [
|
||||
DB::table('comments')->updateOrInsert(['id' => (int) $row['commentid']], [
|
||||
'post_id' => (int) $row['articleid'],
|
||||
'author_name' => $row['author'] ?: '匿名',
|
||||
'author_url' => $row['url'] ?: null,
|
||||
@@ -297,7 +311,7 @@ class SablogImport extends Command
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->rows('links') as $row) {
|
||||
Link::query()->updateOrCreate(['id' => $row['linkid']], [
|
||||
DB::table('links')->updateOrInsert(['id' => (int) $row['linkid']], [
|
||||
'name' => $row['name'],
|
||||
'url' => $row['url'],
|
||||
'note' => $row['note'] ?: null,
|
||||
@@ -312,6 +326,27 @@ class SablogImport extends Command
|
||||
$this->report['links'] = $count;
|
||||
}
|
||||
|
||||
private function convertToMarkdown(): void
|
||||
{
|
||||
$this->info('转换文章内容为 Markdown ...');
|
||||
|
||||
$converter = new \League\HTMLToMarkdown\HtmlConverter([
|
||||
'strip_tags' => false,
|
||||
'hard_break' => true,
|
||||
]);
|
||||
$renderer = app(\App\Blog\Services\PostContentRenderer::class);
|
||||
|
||||
$count = 0;
|
||||
foreach (Post::query()->where('content_format', 'html')->cursor() as $post) {
|
||||
$post->content = $converter->convert($renderer->toMarkdownSafeTokens($post->content));
|
||||
$post->content_format = 'markdown';
|
||||
$post->save();
|
||||
$count++;
|
||||
}
|
||||
|
||||
$this->report['markdown_converted'] = $count;
|
||||
}
|
||||
|
||||
private function importAttachments(): void
|
||||
{
|
||||
if (! $this->db->query('SHOW TABLES LIKE "'.$this->prefix.'attachments"')->fetchColumn()) {
|
||||
|
||||
Reference in New Issue
Block a user