feat(knowledge): add KnowledgeResource with plugin hooks

- Add KnowledgeResource with user.knowledge.resource hook
- Unify processKnowledgeContent for both single and list items
- Remove isListItem parameter for cleaner architecture
This commit is contained in:
xboard
2025-09-20 13:36:10 +08:00
parent 8ae3de511b
commit 61a44483d4
2 changed files with 143 additions and 35 deletions
@@ -4,50 +4,58 @@ namespace App\Http\Controllers\V1\User;
use App\Exceptions\ApiException; use App\Exceptions\ApiException;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Resources\KnowledgeResource;
use App\Models\Knowledge; use App\Models\Knowledge;
use App\Models\User; use App\Models\User;
use App\Services\Plugin\HookManager;
use App\Services\UserService; use App\Services\UserService;
use App\Utils\Helper; use App\Utils\Helper;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class KnowledgeController extends Controller class KnowledgeController extends Controller
{ {
private UserService $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function fetch(Request $request) public function fetch(Request $request)
{ {
if ($request->input('id')) { $request->validate([
$knowledge = Knowledge::where('id', $request->input('id')) 'id' => 'nullable|sometimes|integer|min:1',
->where('show', 1) 'language' => 'nullable|sometimes|string|max:10',
->first(); 'keyword' => 'nullable|sometimes|string|max:255',
]);
if (!$knowledge) {
return $this->fail([500, __('Article does not exist')]); return $request->input('id')
} ? $this->fetchSingle($request)
: $this->fetchList($request);
$knowledge = $knowledge->toArray(); }
$user = User::find($request->user()->id);
$userService = new UserService(); private function fetchSingle(Request $request)
if (!$userService->isAvailable($user)) { {
$this->formatAccessData($knowledge['body']); $knowledge = $this->buildKnowledgeQuery()
} ->where('id', $request->input('id'))
$subscribeUrl = Helper::getSubscribeUrl($user['token']); ->first();
$knowledge['body'] = str_replace('{{siteName}}', admin_setting('app_name', 'XBoard'), $knowledge['body']);
$knowledge['body'] = str_replace('{{subscribeUrl}}', $subscribeUrl, $knowledge['body']); if (!$knowledge) {
$knowledge['body'] = str_replace('{{urlEncodeSubscribeUrl}}', urlencode($subscribeUrl), $knowledge['body']); return $this->fail([500, __('Article does not exist')]);
$knowledge['body'] = str_replace(
'{{safeBase64SubscribeUrl}}',
str_replace(
array('+', '/', '='),
array('-', '_', ''),
base64_encode($subscribeUrl)
),
$knowledge['body']
);
return $this->success($knowledge);
} }
$builder = Knowledge::select(['id', 'category', 'title', 'updated_at'])
$knowledge = $knowledge->toArray();
$knowledge = $this->processKnowledgeContent($knowledge, $request->user());
return $this->success(KnowledgeResource::make($knowledge));
}
private function fetchList(Request $request)
{
$builder = $this->buildKnowledgeQuery(['id', 'category', 'title', 'updated_at', 'body'])
->where('language', $request->input('language')) ->where('language', $request->input('language'))
->where('show', 1)
->orderBy('sort', 'ASC'); ->orderBy('sort', 'ASC');
$keyword = $request->input('keyword'); $keyword = $request->input('keyword');
if ($keyword) { if ($keyword) {
$builder = $builder->where(function ($query) use ($keyword) { $builder = $builder->where(function ($query) use ($keyword) {
@@ -57,14 +65,86 @@ class KnowledgeController extends Controller
} }
$knowledges = $builder->get() $knowledges = $builder->get()
->map(function ($knowledge) use ($request) {
$knowledge = $knowledge->toArray();
$knowledge = $this->processKnowledgeContent($knowledge, $request->user());
return KnowledgeResource::make($knowledge);
})
->groupBy('category'); ->groupBy('category');
return $this->success($knowledges); return $this->success($knowledges);
} }
private function formatAccessData(&$body) private function buildKnowledgeQuery(array $select = ['*'])
{ {
$pattern = '/<!--access start-->(.*?)<!--access end-->/s'; return Knowledge::select($select)->where('show', 1);
$replacement = '<div class="v2board-no-access">' . __('You must have a valid subscription to view content in this area') . '</div>'; }
$body = preg_replace($pattern, $replacement, $body);
private function processKnowledgeContent(array $knowledge, User $user): array
{
if (!isset($knowledge['body'])) {
return $knowledge;
}
if (!$this->userService->isAvailable($user)) {
$this->formatAccessData($knowledge['body']);
}
$subscribeUrl = Helper::getSubscribeUrl($user['token']);
$knowledge['body'] = $this->replacePlaceholders($knowledge['body'], $subscribeUrl);
return $knowledge;
}
private function formatAccessData(&$body): void
{
$rules = [
[
'type' => 'regex',
'pattern' => '/<!--access start-->(.*?)<!--access end-->/s',
'replacement' => '<div class="v2board-no-access">' . __('You must have a valid subscription to view content in this area') . '</div>'
]
];
$this->applyReplacementRules($body, $rules);
}
private function replacePlaceholders(string $body, string $subscribeUrl): string
{
$rules = [
[
'type' => 'string',
'search' => '{{siteName}}',
'replacement' => admin_setting('app_name', 'XBoard')
],
[
'type' => 'string',
'search' => '{{subscribeUrl}}',
'replacement' => $subscribeUrl
],
[
'type' => 'string',
'search' => '{{urlEncodeSubscribeUrl}}',
'replacement' => urlencode($subscribeUrl)
],
[
'type' => 'string',
'search' => '{{safeBase64SubscribeUrl}}',
'replacement' => str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($subscribeUrl))
]
];
$this->applyReplacementRules($body, $rules);
return $body;
}
private function applyReplacementRules(string &$body, array $rules): void
{
foreach ($rules as $rule) {
if ($rule['type'] === 'regex') {
$body = preg_replace($rule['pattern'], $rule['replacement'], $body);
} else {
$body = str_replace($rule['search'], $rule['replacement'], $body);
}
}
} }
} }
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Services\Plugin\HookManager;
class KnowledgeResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
$data = [
'id' => $this['id'],
'category' => $this['category'],
'title' => $this['title'],
'body' => $this->when(isset($this['body']), $this['body']),
'updated_at' => $this['updated_at'],
];
return HookManager::filter('user.knowledge.resource', $data, $request, $this);
}
}