Add news article comments and reactions

This commit is contained in:
2026-05-01 11:43:49 +02:00
parent 874f8feb9c
commit 28e7e46e13
22 changed files with 20083 additions and 26 deletions

View File

@@ -56,6 +56,9 @@ final class StudioNewsController extends Controller
'tagOptions' => $this->news->tagOptions(),
'relationTypeOptions' => $this->news->relationTypeOptions(),
'storeUrl' => route('studio.news.store'),
'coverUploadUrl' => route('api.studio.news.media.upload'),
'coverDeleteUrl' => route('api.studio.news.media.destroy'),
'coverCdnBaseUrl' => rtrim((string) config('cdn.files_url', 'https://files.skinbase.org'), '/'),
'entitySearchUrl' => route('studio.news.entity-search'),
'categoriesUrl' => route('studio.news.categories'),
'tagsUrl' => route('studio.news.tags'),
@@ -85,7 +88,11 @@ final class StudioNewsController extends Controller
'categoryOptions' => $this->news->categoryOptions(),
'tagOptions' => $this->news->tagOptions(),
'relationTypeOptions' => $this->news->relationTypeOptions(),
'coverUploadUrl' => route('api.studio.news.media.upload'),
'coverDeleteUrl' => route('api.studio.news.media.destroy'),
'coverCdnBaseUrl' => rtrim((string) config('cdn.files_url', 'https://files.skinbase.org'), '/'),
'updateUrl' => route('studio.news.update', ['article' => $article->id]),
'destroyUrl' => route('studio.news.destroy', ['article' => $article->id]),
'previewUrl' => route('studio.news.preview', ['article' => $article->id]),
'publishUrl' => route('studio.news.publish', ['article' => $article->id]),
'archiveUrl' => route('studio.news.archive', ['article' => $article->id]),
@@ -115,6 +122,8 @@ final class StudioNewsController extends Controller
'article' => $article,
'related' => $related,
'relatedEntities' => $this->news->resolveRelatedEntities($article, $request->user()),
'comments' => collect(),
'commentsCount' => 0,
'previewMode' => true,
'previewCanonical' => route('studio.news.preview', ['article' => $article->id]),
'previewBackUrl' => route('studio.news.edit', ['article' => $article->id]),
@@ -127,7 +136,16 @@ final class StudioNewsController extends Controller
$this->news->updateArticle($article, $request->user(), $this->validateArticle($request, $article));
return back()->with('success', 'Article updated.');
return redirect()->route('studio.news.edit', ['article' => $article->id])->with('success', 'Article updated.');
}
public function destroy(Request $request, NewsArticle $article): RedirectResponse
{
$this->authorizeNews($request);
$this->news->deleteArticle($article);
return redirect()->route('studio.news.index')->with('success', 'Article moved to trash.');
}
public function publish(Request $request, NewsArticle $article): RedirectResponse
@@ -331,7 +349,7 @@ final class StudioNewsController extends Controller
'title' => ['required', 'string', 'max:255'],
'slug' => ['nullable', 'string', 'max:255'],
'excerpt' => ['nullable', 'string', 'max:800'],
'content' => ['required', 'string', 'max:50000'],
'content' => ['required', 'string', 'max:500000'],
'cover_image' => ['nullable', 'string', 'max:2048'],
'type' => ['required', Rule::in(array_column($this->news->articleTypeOptions(), 'value'))],
'category_id' => ['nullable', 'integer', 'exists:news_categories,id'],
@@ -340,12 +358,24 @@ final class StudioNewsController extends Controller
'published_at' => ['nullable', 'date'],
'is_featured' => ['nullable', 'boolean'],
'is_pinned' => ['nullable', 'boolean'],
'comments_enabled' => ['nullable', 'boolean'],
'tag_ids' => ['nullable', 'array'],
'tag_ids.*' => ['integer', 'exists:news_tags,id'],
'new_tag_names' => ['nullable', 'array', 'max:12'],
'new_tag_names.*' => ['string', 'max:80'],
'meta_title' => ['nullable', 'string', 'max:255'],
'meta_description' => ['nullable', 'string', 'max:300'],
'meta_keywords' => ['nullable', 'string', 'max:255'],
'canonical_url' => ['nullable', 'url', 'max:2048'],
'canonical_url' => ['nullable', 'string', 'max:2048', function (string $attribute, mixed $value, \Closure $fail): void {
if ($value === '' || $value === null) {
return;
}
$isAbsolute = filter_var($value, FILTER_VALIDATE_URL) !== false;
$isRelative = str_starts_with($value, '/');
if (! $isAbsolute && ! $isRelative) {
$fail('The canonical URL must be a valid URL or a relative path starting with /.');
}
}],
'og_title' => ['nullable', 'string', 'max:255'],
'og_description' => ['nullable', 'string', 'max:300'],
'og_image' => ['nullable', 'string', 'max:2048'],