295 lines
12 KiB
PHP
295 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Paymenter\Extensions\Others\NavbarManager;
|
|
|
|
use App\Classes\Extension\Extension;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\Event;
|
|
use Illuminate\Support\HtmlString;
|
|
use Paymenter\Extensions\Others\NavbarManager\Admin\Resources\NavbarLinkResource;
|
|
use Paymenter\Extensions\Others\NavbarManager\Models\NavbarLink;
|
|
|
|
class NavbarManager extends Extension
|
|
{
|
|
private static bool $migrationAttempted = false;
|
|
public function getConfig($values = [])
|
|
{
|
|
try {
|
|
return [
|
|
[
|
|
'name' => 'Notice',
|
|
'type' => 'placeholder',
|
|
'label' => new HtmlString('You can use this extension to manage custom navigation links. To manage navbar links, go to <a class="text-primary-600" href="' . NavbarLinkResource::getUrl() . '">Navbar Manager</a>.'),
|
|
],
|
|
];
|
|
} catch (\Exception $e) {
|
|
return [
|
|
[
|
|
'name' => 'Notice',
|
|
'type' => 'placeholder',
|
|
'label' => new HtmlString('You can use this extension to manage custom navigation links. You\'ll need to enable this extension above to get started.'),
|
|
],
|
|
];
|
|
}
|
|
}
|
|
|
|
public function enabled()
|
|
{
|
|
// This method is called only when the extension is being enabled
|
|
// Run migrations or initial setup here
|
|
try {
|
|
Artisan::call('migrate', [
|
|
'--path' => 'extensions/Others/NavbarManager/database/migrations',
|
|
'--force' => true
|
|
]);
|
|
} catch (\Exception $e) {
|
|
\Log::error('NavbarManager: Migration failed', ['error' => $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
public function boot()
|
|
{
|
|
// Register view service provider
|
|
app()->register(\Paymenter\Extensions\Others\NavbarManager\Providers\ViewServiceProvider::class);
|
|
|
|
// Register custom routes for navbar links
|
|
require __DIR__ . '/routes/web.php';
|
|
|
|
// Register event listeners for navigation - dodaće naše linkove uz postojeće
|
|
Event::listen('navigation', function () {
|
|
return $this->getNavigationLinks('primary');
|
|
});
|
|
|
|
Event::listen('navigation.account-dropdown', function () {
|
|
return $this->getNavigationLinks('account');
|
|
});
|
|
|
|
Event::listen('navigation.dashboard', function () {
|
|
return $this->getNavigationLinks('dashboard');
|
|
});
|
|
|
|
// Note: Admin resources are automatically discovered by Paymenter's AdminPanelProvider
|
|
// from the Admin/Resources folder - no manual registration needed
|
|
}
|
|
|
|
private function getNavigationLinks($type)
|
|
{
|
|
try {
|
|
if (!\Schema::hasTable('navbar_links')) {
|
|
if (!self::$migrationAttempted) {
|
|
self::$migrationAttempted = true;
|
|
try {
|
|
Artisan::call('migrate', [
|
|
'--path' => 'extensions/Others/NavbarManager/database/migrations',
|
|
'--force' => true,
|
|
]);
|
|
} catch (\Exception $migrationException) {
|
|
\Log::error('NavbarManager: Failed to run migrations', [
|
|
'error' => $migrationException->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Table still missing, skip navigation injection to prevent 500s
|
|
if (!\Schema::hasTable('navbar_links')) {
|
|
\Log::warning('NavbarManager: navbar_links table missing. Skipping navigation injection.', [
|
|
'type' => $type,
|
|
]);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
$links = NavbarLink::where('type', $type)
|
|
->where('is_active', true)
|
|
->whereNull('parent_id') // Only get top-level links
|
|
->orderBy('sort_order')
|
|
->with('children')
|
|
->get();
|
|
|
|
$navLinks = [];
|
|
foreach ($links as $link) {
|
|
if ($this->shouldShowLink($link)) {
|
|
$formattedLink = $this->formatLink($link);
|
|
if ($formattedLink) {
|
|
$navLinks[] = $formattedLink;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $navLinks;
|
|
} catch (\Exception $e) {
|
|
// Log error but don't break navigation
|
|
\Log::error('NavbarManager: Error loading navigation links', [
|
|
'type' => $type,
|
|
'error' => $e->getMessage(),
|
|
'trace' => $e->getTraceAsString()
|
|
]);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
private function shouldShowLink($link)
|
|
{
|
|
try {
|
|
switch ($link->condition) {
|
|
case 'always_visible':
|
|
return true;
|
|
case 'always_hidden':
|
|
return false;
|
|
case 'clients_only':
|
|
return auth()->check() && auth()->user()->role === null;
|
|
case 'admin_only':
|
|
return auth()->check() && auth()->user()->role !== null;
|
|
case 'guests_only':
|
|
return !auth()->check();
|
|
default:
|
|
return true;
|
|
}
|
|
} catch (\Exception $e) {
|
|
// If there's an error checking conditions, default to visible
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private function formatLink($link)
|
|
{
|
|
try {
|
|
$formatted = [
|
|
'name' => $link->name,
|
|
'priority' => $link->sort_order,
|
|
];
|
|
|
|
// Add icon with validation and strip -line/-fill suffix
|
|
if ($link->icon && $this->isValidIcon($link->icon)) {
|
|
// Remove -line or -fill suffix since Navigation::markActiveRoute will add it
|
|
$baseIcon = preg_replace('/-(?:line|fill)$/', '', $link->icon);
|
|
$formatted['icon'] = $baseIcon;
|
|
}
|
|
|
|
// Handle different link types
|
|
switch ($link->link_type) {
|
|
case 'route':
|
|
if ($link->route_name) {
|
|
$formatted['route'] = $link->route_name;
|
|
$formatted['spa'] = true; // Enable SPA for route links
|
|
if ($link->route_params) {
|
|
// Get raw attribute to avoid accessor transformation
|
|
$rawParams = $link->getAttributes()['route_params'] ?? null;
|
|
$params = is_string($rawParams)
|
|
? json_decode($rawParams, true)
|
|
: $rawParams;
|
|
if ($params && is_array($params)) {
|
|
$formatted['params'] = $params;
|
|
}
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
break;
|
|
case 'path':
|
|
case 'url':
|
|
// Use navbar redirect route
|
|
$formatted['route'] = 'navbar.redirect';
|
|
$formatted['params'] = ['linkId' => $link->id];
|
|
$formatted['spa'] = false; // Disable SPA for external links
|
|
break;
|
|
case 'product':
|
|
if ($link->product_id) {
|
|
try {
|
|
$product = \App\Models\Product::with('category')->find($link->product_id);
|
|
if ($product && $product->category) {
|
|
// Use products show route (requires both category and product slugs)
|
|
$formatted['route'] = 'products.show';
|
|
$formatted['params'] = [
|
|
'category' => $product->category->slug ?? $product->category->id,
|
|
'product' => $product->slug ?? $product->id
|
|
];
|
|
$formatted['spa'] = true;
|
|
} else {
|
|
return null; // Product or category not found
|
|
}
|
|
} catch (\Exception $e) {
|
|
return null;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
break;
|
|
case 'category':
|
|
if ($link->category_id) {
|
|
try {
|
|
$category = \App\Models\Category::find($link->category_id);
|
|
if ($category) {
|
|
// Use category show route
|
|
$formatted['route'] = 'category.show';
|
|
$formatted['params'] = ['category' => $category->slug ?? $category->id];
|
|
$formatted['spa'] = true;
|
|
} else {
|
|
return null; // Category not found
|
|
}
|
|
} catch (\Exception $e) {
|
|
return null;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Handle children (dropdown items)
|
|
if ($link->children && $link->children->count() > 0) {
|
|
$children = [];
|
|
foreach ($link->children->where('is_active', true) as $child) {
|
|
if ($this->shouldShowLink($child)) {
|
|
$childFormatted = $this->formatLink($child);
|
|
if ($childFormatted) {
|
|
$children[] = $childFormatted;
|
|
}
|
|
}
|
|
}
|
|
if (!empty($children)) {
|
|
$formatted['children'] = $children;
|
|
}
|
|
}
|
|
|
|
return $formatted;
|
|
} catch (\Exception $e) {
|
|
\Log::error('NavbarManager: Error formatting link', [
|
|
'link_id' => $link->id ?? 'unknown',
|
|
'error' => $e->getMessage()
|
|
]);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private function isValidIcon($icon)
|
|
{
|
|
// Basic validation for RemixIcon format
|
|
if (!preg_match('/^ri-[\w-]+$/', $icon)) {
|
|
return false;
|
|
}
|
|
|
|
// List of common valid RemixIcon names to avoid errors (without -line/-fill suffix)
|
|
$commonIcons = [
|
|
'ri-home', 'ri-user', 'ri-settings', 'ri-dashboard',
|
|
'ri-menu', 'ri-link', 'ri-external-link', 'ri-folder',
|
|
'ri-file', 'ri-edit', 'ri-delete-bin', 'ri-add',
|
|
'ri-navigation', 'ri-admin', 'ri-function', 'ri-archive-stack',
|
|
'ri-receipt', 'ri-customer-service', 'ri-settings-3', 'ri-megaphone',
|
|
'ri-shopping-bag', 'ri-check', 'ri-close', 'ri-arrow-down-s'
|
|
];
|
|
|
|
// Remove -line or -fill suffix if present to get base icon name
|
|
$baseIcon = preg_replace('/-(?:line|fill)$/', '', $icon);
|
|
|
|
// If it's in our common list, it's definitely valid
|
|
if (in_array($baseIcon, $commonIcons)) {
|
|
return true;
|
|
}
|
|
|
|
// For other icons, just check the format (basic validation)
|
|
// In a production environment, you might want to check if the SVG file actually exists
|
|
return true;
|
|
}
|
|
}
|