mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 22:01:33 +00:00
284 lines
8.8 KiB
PHP
284 lines
8.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\BasePageController;
|
|
use App\Models\Content;
|
|
use Illuminate\Foundation\Application;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Redirector;
|
|
use Illuminate\View\View;
|
|
|
|
class AdminContentController extends BasePageController
|
|
{
|
|
/**
|
|
* Display list of all content.
|
|
*
|
|
* @throws \Exception
|
|
*/
|
|
public function index(): mixed
|
|
{
|
|
$this->setAdminPrefs();
|
|
|
|
$contentList = Content::query()
|
|
->orderByRaw('contenttype, COALESCE(ordinal, 1000000)')
|
|
->get();
|
|
|
|
$this->viewData = array_merge($this->viewData, [
|
|
'contentlist' => $contentList,
|
|
'meta_title' => 'Content List',
|
|
'title' => 'Content List',
|
|
]);
|
|
|
|
return view('admin.content.index', $this->viewData);
|
|
}
|
|
|
|
/**
|
|
* Show form to create or edit content.
|
|
*
|
|
* @return \Illuminate\Contracts\Foundation\Application|Application|RedirectResponse|Redirector|View
|
|
*
|
|
* @throws \Exception
|
|
*/
|
|
public function create(Request $request)
|
|
{
|
|
$this->setAdminPrefs();
|
|
$meta_title = 'Content Add';
|
|
|
|
// Set the current action.
|
|
$action = $request->input('action') ?? 'view';
|
|
|
|
$content = [
|
|
'id' => '',
|
|
'title' => '',
|
|
'url' => '',
|
|
'body' => '',
|
|
'metadescription' => '',
|
|
'metakeywords' => '',
|
|
'contenttype' => '',
|
|
'status' => '',
|
|
'ordinal' => '',
|
|
'created_at' => '',
|
|
'role' => '',
|
|
];
|
|
|
|
switch ($action) {
|
|
case 'add':
|
|
$meta_title = 'Content Add';
|
|
$content['status'] = Content::STATUS_ENABLED;
|
|
$content['contenttype'] = Content::TYPE_USEFUL;
|
|
break;
|
|
|
|
case 'submit':
|
|
// Validate and add or update content
|
|
if ($request->missing('id') || empty($request->input('id'))) {
|
|
$returnid = $this->addContent($request->all());
|
|
} else {
|
|
$this->updateContent($request->all());
|
|
$returnid = $request->input('id');
|
|
}
|
|
|
|
return redirect('admin/content-add?id='.$returnid);
|
|
|
|
case 'view':
|
|
default:
|
|
if ($request->has('id')) {
|
|
$meta_title = 'Content Edit';
|
|
$id = (int) $request->input('id');
|
|
$content = $this->getContentById($id);
|
|
}
|
|
break;
|
|
}
|
|
|
|
$contenttypelist = [
|
|
Content::TYPE_USEFUL => 'Useful Link',
|
|
Content::TYPE_INDEX => 'Homepage',
|
|
];
|
|
|
|
$rolelist = [
|
|
Content::ROLE_EVERYONE => 'Everyone',
|
|
Content::ROLE_LOGGED_IN => 'Logged in Users',
|
|
Content::ROLE_ADMIN => 'Admins',
|
|
];
|
|
|
|
$this->viewData = array_merge($this->viewData, [
|
|
'status_ids' => [Content::STATUS_ENABLED, Content::STATUS_DISABLED],
|
|
'status_names' => ['Enabled', 'Disabled'],
|
|
'yesno_ids' => [1, 0],
|
|
'yesno_names' => ['Yes', 'No'],
|
|
'contenttypelist' => $contenttypelist,
|
|
'content' => $content,
|
|
'rolelist' => $rolelist,
|
|
'meta_title' => $meta_title,
|
|
'title' => $meta_title,
|
|
]);
|
|
|
|
return view('admin.content.add', $this->viewData);
|
|
}
|
|
|
|
/**
|
|
* Toggle content status (enable/disable).
|
|
*/
|
|
public function toggleStatus(Request $request): mixed
|
|
{
|
|
if ($request->has('id')) {
|
|
$content = Content::query()->find($request->input('id'));
|
|
|
|
if ($content) {
|
|
$newStatus = $content->status === Content::STATUS_ENABLED
|
|
? Content::STATUS_DISABLED
|
|
: Content::STATUS_ENABLED;
|
|
|
|
$content->update(['status' => $newStatus, 'updated_at' => now()]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'status' => $newStatus,
|
|
'message' => $newStatus === Content::STATUS_ENABLED ? 'Content enabled' : 'Content disabled',
|
|
]);
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Content not found',
|
|
], 404);
|
|
}
|
|
|
|
/**
|
|
* Delete content by ID.
|
|
*/
|
|
public function destroy(Request $request): mixed
|
|
{
|
|
if ($request->has('id')) {
|
|
$content = Content::query()->find($request->input('id'));
|
|
|
|
if ($content) {
|
|
$content->delete();
|
|
|
|
// If AJAX request, return JSON
|
|
if ($request->wantsJson() || $request->ajax()) {
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Content deleted successfully',
|
|
]);
|
|
}
|
|
|
|
return redirect()->route('admin.content-list')->with('success', 'Content deleted successfully');
|
|
}
|
|
|
|
// If AJAX request, return error JSON
|
|
if ($request->wantsJson() || $request->ajax()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Content not found',
|
|
], 404);
|
|
}
|
|
}
|
|
|
|
$referrer = $request->server('HTTP_REFERER');
|
|
|
|
return redirect()->to($referrer)->with('error', 'Invalid request');
|
|
}
|
|
|
|
/**
|
|
* Add new content.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
protected function addContent(array $data): int
|
|
{
|
|
// Normalize URL
|
|
$data = $this->normalizeContentUrl($data);
|
|
|
|
// If ordinal is 1, increment all existing ordinals
|
|
if (($data['ordinal'] ?? 0) === 1) {
|
|
Content::query()->where('ordinal', '>', 0)->increment('ordinal');
|
|
}
|
|
|
|
return Content::query()->insertGetId([
|
|
'role' => $data['role'] ?? Content::ROLE_EVERYONE,
|
|
'title' => $data['title'] ?? '',
|
|
'url' => $data['url'] ?? '/',
|
|
'body' => $data['body'] ?? '',
|
|
'metadescription' => $data['metadescription'] ?? '',
|
|
'metakeywords' => $data['metakeywords'] ?? '',
|
|
'contenttype' => $data['contenttype'] ?? Content::TYPE_USEFUL,
|
|
'status' => $data['status'] ?? Content::STATUS_ENABLED,
|
|
'ordinal' => $data['ordinal'] ?? 0,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Update existing content.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
protected function updateContent(array $data): int
|
|
{
|
|
// Normalize URL
|
|
$data = $this->normalizeContentUrl($data);
|
|
|
|
return Content::query()
|
|
->where('id', $data['id'])
|
|
->update([
|
|
'role' => $data['role'] ?? Content::ROLE_EVERYONE,
|
|
'title' => $data['title'] ?? '',
|
|
'url' => $data['url'] ?? '/',
|
|
'body' => $data['body'] ?? '',
|
|
'metadescription' => $data['metadescription'] ?? '',
|
|
'metakeywords' => $data['metakeywords'] ?? '',
|
|
'contenttype' => $data['contenttype'] ?? Content::TYPE_USEFUL,
|
|
'status' => $data['status'] ?? Content::STATUS_ENABLED,
|
|
'ordinal' => $data['ordinal'] ?? 0,
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get content by ID for admin viewing.
|
|
*/
|
|
protected function getContentById(int $id): ?Content
|
|
{
|
|
return Content::query()->find($id);
|
|
}
|
|
|
|
/**
|
|
* Normalize content URL to ensure proper formatting.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function normalizeContentUrl(array $data): array
|
|
{
|
|
if (isset($data['url']) && $data['url'] !== '') {
|
|
$url = $data['url'];
|
|
|
|
// Check if URL is external (has protocol or is a domain pattern)
|
|
$hasProtocol = str_starts_with($url, 'http://') || str_starts_with($url, 'https://');
|
|
$isDomain = ! str_starts_with($url, '/') && preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}/', $url);
|
|
|
|
// Only normalize internal URLs (those starting with / or root)
|
|
if (! $hasProtocol && ! $isDomain) {
|
|
// Ensure internal URL starts with /
|
|
if ($url !== '/' && ! str_starts_with($url, '/')) {
|
|
$data['url'] = '/'.$url;
|
|
}
|
|
|
|
// Ensure internal URL ends with /
|
|
if (! str_ends_with($data['url'], '/')) {
|
|
$data['url'] .= '/';
|
|
}
|
|
}
|
|
// External URLs are left as-is
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
}
|