Add useful links support to sidebar

This commit is contained in:
DariusIII
2025-11-27 12:43:15 +01:00
parent ff51661dd5
commit 0cf6ea4e8b
3 changed files with 186 additions and 8 deletions
@@ -193,16 +193,26 @@ class AdminContentController extends BasePageController
*/
protected function normalizeContentUrl(array $data): array
{
if (isset($data['url'])) {
// Ensure URL starts with /
if ($data['url'] !== '/' && !str_starts_with($data['url'], '/')) {
$data['url'] = '/'.$data['url'];
}
if (isset($data['url']) && $data['url'] !== '') {
$url = $data['url'];
// Ensure URL ends with /
if (!str_ends_with($data['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;
@@ -4,6 +4,7 @@ namespace App\View\Composers;
use App\Events\UserLoggedIn;
use App\Models\Category;
use App\Models\Content;
use App\Models\Settings;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
@@ -34,6 +35,13 @@ class GlobalDataComposer
$viewData['site'] = $siteArray; // Now it's a proper array, not a Settings model
}
// Load useful links for sidebar
$usefulLinks = Content::active()
->ofType(Content::TYPE_USEFUL)
->orderBy('ordinal')
->get();
$viewData['usefulLinks'] = $usefulLinks;
if (Auth::check()) {
$userdata = User::find(Auth::id());
$userdata->categoryexclusions = User::getCategoryExclusionById(Auth::id());
+160
View File
@@ -111,6 +111,67 @@
</div>
</div>
<!-- Useful Links Menu -->
@if(isset($usefulLinks) && $usefulLinks->isNotEmpty())
<div class="sidebar-section">
<button class="sidebar-toggle w-full flex items-center justify-between px-4 py-3 text-white hover:bg-gray-800 rounded transition" data-target="submenu-useful-links">
<div class="flex items-center">
<i class="fa fa-link fa-fw mr-3"></i>
<span>Useful Links</span>
</div>
<i class="fas fa-chevron-down text-xs transition-transform"></i>
</button>
<div id="submenu-useful-links" class="sidebar-submenu ml-4 mt-1 space-y-3 hidden">
@foreach($usefulLinks as $link)
<div class="px-2 py-2">
@if($link->url)
@php
$url = $link->url;
// Check if URL has protocol
$hasProtocol = str_starts_with($url, 'http://') || str_starts_with($url, 'https://');
// Check if URL is a domain pattern (e.g., google.com, chatgpt.ai)
// Pattern: contains at least one dot and doesn't start with /
$isDomain = !str_starts_with($url, '/') && preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}/', $url);
$isExternal = $hasProtocol || $isDomain;
// Use external URLs as-is (add https:// if domain without protocol), prepend site URL for internal paths
if ($hasProtocol) {
$linkUrl = $url;
} elseif ($isDomain) {
$linkUrl = 'https://' . $url;
} else {
$linkUrl = url($url);
}
@endphp
<a href="{{ $linkUrl }}"
class="block text-gray-300 hover:text-white transition"
@if($isExternal) target="_blank" rel="noopener noreferrer" @endif>
<div class="font-medium mb-1">{{ $link->title }}</div>
@if($link->body)
<div class="text-sm text-gray-400 useful-link-content">
{!! html_entity_decode(trim($link->body, '\'"')) !!}
</div>
@endif
</a>
@else
<div class="text-gray-300">
<div class="font-medium mb-1">{{ $link->title }}</div>
@if($link->body)
<div class="text-sm text-gray-400 useful-link-content">
{!! html_entity_decode(trim($link->body, '\'"')) !!}
</div>
@endif
</div>
@endif
</div>
@endforeach
</div>
</div>
@endif
<!-- Extend/Upgrade Account -->
@auth
@php
@@ -143,5 +204,104 @@
@endauth
</nav>
@push('styles')
<style>
/* Useful Links Content Styling */
.useful-link-content {
max-width: 100%;
overflow-wrap: break-word;
word-wrap: break-word;
}
/* Scale images to fit sidebar */
.useful-link-content img {
max-width: 100%;
height: auto;
border-radius: 0.375rem;
margin: 0.5rem 0;
display: block;
}
/* Style paragraphs in useful links */
.useful-link-content p {
margin-bottom: 0.5rem;
line-height: 1.5;
}
.useful-link-content p:last-child {
margin-bottom: 0;
}
/* Style links within useful links content */
.useful-link-content a {
color: #60a5fa;
text-decoration: underline;
}
.useful-link-content a:hover {
color: #93c5fd;
}
/* Style lists in useful links */
.useful-link-content ul,
.useful-link-content ol {
margin: 0.5rem 0;
padding-left: 1.5rem;
}
.useful-link-content li {
margin-bottom: 0.25rem;
}
/* Style headings in useful links */
.useful-link-content h1,
.useful-link-content h2,
.useful-link-content h3,
.useful-link-content h4,
.useful-link-content h5,
.useful-link-content h6 {
font-weight: 600;
margin-top: 0.75rem;
margin-bottom: 0.5rem;
color: #e5e7eb;
}
.useful-link-content h1 { font-size: 1.25rem; }
.useful-link-content h2 { font-size: 1.125rem; }
.useful-link-content h3 { font-size: 1rem; }
.useful-link-content h4,
.useful-link-content h5,
.useful-link-content h6 { font-size: 0.875rem; }
/* Style blockquotes */
.useful-link-content blockquote {
border-left: 3px solid #4b5563;
padding-left: 1rem;
margin: 0.5rem 0;
font-style: italic;
color: #9ca3af;
}
/* Style code blocks */
.useful-link-content code {
background-color: #374151;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
font-size: 0.875em;
font-family: monospace;
}
.useful-link-content pre {
background-color: #1f2937;
padding: 0.75rem;
border-radius: 0.375rem;
overflow-x: auto;
margin: 0.5rem 0;
}
.useful-link-content pre code {
background-color: transparent;
padding: 0;
}
</style>
@endpush