fix: 已解锁后 [paid] 标签裸露——替换为付费内容块
post.rendered 过滤原只在无权限时替换 [paid];有权限时原始标签直接显示在页面。现在: - 有权限:替换为 <div class="paid-content"> 付费内容块(带「付费内容」小标签),三套主题补齐样式 - 先去掉 Markdown 转换产生的 <p>[paid]</p> 包裹,避免替换后残留未闭合标签 - 测试:游客与会员均断言不出现裸 [paid]
This commit is contained in:
@@ -53,15 +53,24 @@ class ServiceProvider extends PluginServiceProvider
|
||||
}
|
||||
}, 10);
|
||||
|
||||
// 付费文章:[paid]...[/paid] 对无权限访客隐藏为 teaser;整篇锁定渲染 paywall
|
||||
// 付费文章:[paid]...[/paid] —— 无权限访客替换为 teaser;有权限替换为付费内容块(避免原始标签裸露在页面)
|
||||
$manager->addFilter('post.rendered', function (string $html, \App\Models\Post $post) {
|
||||
$service = app(Services\MembershipService::class);
|
||||
$user = auth()->user();
|
||||
|
||||
if (preg_match('/\[paid\](.*?)\[\/paid\]/s', $html) && ! $service->canReadPaidContent($user, $post)) {
|
||||
if (preg_match('/\[paid\](.*?)\[\/paid\]/s', $html)) {
|
||||
// Markdown 转换会给短代码包上 <p>,先去掉,避免替换后残留未闭合标签
|
||||
$html = preg_replace('/<p>\s*\[(\/?paid)\]\s*<\/p>/i', '[$1]', $html);
|
||||
|
||||
$html = preg_replace_callback(
|
||||
'/\[paid\](.*?)\[\/paid\]/s',
|
||||
fn () => view('membership.paid-teaser', ['post' => $post])->render(),
|
||||
function (array $m) use ($service, $user, $post) {
|
||||
if ($service->canReadPaidContent($user, $post)) {
|
||||
return '<div class="paid-content"><div class="paid-content-label">付费内容</div>'.trim($m[1]).'</div>';
|
||||
}
|
||||
|
||||
return view('membership.paid-teaser', ['post' => $post])->render();
|
||||
},
|
||||
$html
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,21 +87,24 @@ class MembershipFlowTest extends TestCase
|
||||
'published_at' => now(),
|
||||
]);
|
||||
|
||||
// 游客:付费部分被替换为 teaser
|
||||
// 游客:付费部分被替换为 teaser,且不出现裸 [paid] 标签
|
||||
$this->get('/posts/paid-post.shtml')
|
||||
->assertOk()
|
||||
->assertDontSee('付费隐藏内容')
|
||||
->assertDontSee('[paid]')
|
||||
->assertSee('本文包含付费内容')
|
||||
->assertSee('登录后查看');
|
||||
|
||||
// 会员:可见
|
||||
// 会员:可见,且 [paid] 标签被替换为付费内容块(不裸露)
|
||||
$this->actingAs($this->user)->post('/membership/'.$this->plan->id.'/subscribe');
|
||||
$payment = Payment::query()->where('user_id', $this->user->id)->first();
|
||||
$this->actingAs($this->user)->post('/pay/sandbox/'.$payment->order_no.'/confirm');
|
||||
|
||||
$this->actingAs($this->user)->get('/posts/paid-post.shtml')
|
||||
->assertOk()
|
||||
->assertSee('付费隐藏内容');
|
||||
->assertSee('付费隐藏内容')
|
||||
->assertSee('paid-content')
|
||||
->assertDontSee('[paid]');
|
||||
}
|
||||
|
||||
public function test_unlock_post_creates_payment_with_payable_and_grants_access(): void
|
||||
|
||||
@@ -61,3 +61,7 @@ a:hover { color: var(--d-accent-dark); }
|
||||
.main-layout { grid-template-columns: 1fr; }
|
||||
.d-header .d-container { flex-direction: column; align-items: flex-start; }
|
||||
}
|
||||
|
||||
/* 已解锁的付费内容块 */
|
||||
.paid-content { border: 1px solid var(--d-border); border-left: 4px solid var(--d-accent); background: #fff; border-radius: 8px; padding: 12px 14px; margin: 12px 0; }
|
||||
.paid-content-label { font-size: 12px; color: var(--d-accent); font-weight: 700; margin-bottom: 6px; }
|
||||
|
||||
@@ -186,3 +186,7 @@ a:hover { opacity: 0.8; }
|
||||
.paywall-actions .btn { margin: 0 6px; }
|
||||
.paid-teaser { border: 1px dashed var(--border); background: var(--bg-muted, #f6f6f6); color: var(--muted); border-radius: 10px; padding: 14px 16px; margin: 14px 0; font-size: 14px; }
|
||||
.paid-teaser .btn { margin-left: 8px; }
|
||||
|
||||
/* 已解锁的付费内容块 */
|
||||
.paid-content { border: 1px solid var(--border); border-left: 4px solid var(--accent); background: var(--bg); border-radius: 10px; padding: 14px 16px; margin: 14px 0; }
|
||||
.paid-content-label { font-size: 12px; color: var(--accent); font-weight: 600; margin-bottom: 6px; }
|
||||
|
||||
@@ -163,3 +163,7 @@ a:hover { color: #f60; text-decoration: underline; }
|
||||
/* 社交账号:横向胶囊列表 */
|
||||
.social-links { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; }
|
||||
.social-links li { font-size: 12px; background: #f7f9fb; border: 1px solid #d8dfe6; border-radius: 999px; padding: 3px 10px; }
|
||||
|
||||
/* 已解锁的付费内容块 */
|
||||
.paid-content { border: 1px dashed #f5c6cb; border-left: 4px solid #f60; background: #fff8f0; border-radius: 6px; padding: 12px 14px; margin: 12px 0; }
|
||||
.paid-content-label { font-size: 12px; color: #f60; font-weight: 700; margin-bottom: 6px; }
|
||||
|
||||
Reference in New Issue
Block a user