feat: enhance CreditsPlus and NavbarManager extensions with improved role checks, route handling, and migration logic for better functionality and compatibility across databases
This commit is contained in:
@@ -65,6 +65,22 @@ class CreditsPlus extends Extension
|
|||||||
// Use view composer to add credits data to navigation
|
// Use view composer to add credits data to navigation
|
||||||
View::composer('components.navigation.index', function ($view) use ($extension, $extensionPath) {
|
View::composer('components.navigation.index', function ($view) use ($extension, $extensionPath) {
|
||||||
try {
|
try {
|
||||||
|
// Don't run in admin panel - this is crucial to avoid conflicts
|
||||||
|
if (app()->runningInConsole()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're in admin context
|
||||||
|
$currentPath = request()->path();
|
||||||
|
if (str_starts_with($currentPath, 'admin') || str_contains($currentPath, '/admin/')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't run for API routes
|
||||||
|
if (str_starts_with($currentPath, 'api')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Auth::check()) {
|
if (!Auth::check()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,52 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
return new class extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
// For MySQL, we need to alter the enum column to include new values
|
$driver = Schema::getConnection()->getDriverName();
|
||||||
DB::statement("ALTER TABLE navbar_links MODIFY COLUMN link_type ENUM('route', 'path', 'url', 'product', 'category') DEFAULT 'route'");
|
|
||||||
|
if ($driver === 'mysql') {
|
||||||
|
// For MySQL, use ALTER TABLE to modify enum
|
||||||
|
DB::statement("ALTER TABLE navbar_links MODIFY COLUMN link_type ENUM('route', 'path', 'url', 'product', 'category') DEFAULT 'route'");
|
||||||
|
} else {
|
||||||
|
// For other databases (PostgreSQL, SQLite), drop and recreate column
|
||||||
|
Schema::table('navbar_links', function (Blueprint $table) {
|
||||||
|
$table->string('link_type_temp')->default('route');
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::table('navbar_links')->update(['link_type_temp' => DB::raw('link_type')]);
|
||||||
|
|
||||||
|
Schema::table('navbar_links', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('link_type');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('navbar_links', function (Blueprint $table) {
|
||||||
|
$table->string('link_type')->default('route')->after('type');
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::table('navbar_links')->update(['link_type' => DB::raw('link_type_temp')]);
|
||||||
|
|
||||||
|
Schema::table('navbar_links', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('link_type_temp');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down()
|
public function down()
|
||||||
{
|
{
|
||||||
// Revert back to original enum values
|
$driver = Schema::getConnection()->getDriverName();
|
||||||
DB::statement("ALTER TABLE navbar_links MODIFY COLUMN link_type ENUM('route', 'path', 'url') DEFAULT 'route'");
|
|
||||||
|
if ($driver === 'mysql') {
|
||||||
|
DB::statement("ALTER TABLE navbar_links MODIFY COLUMN link_type ENUM('route', 'path', 'url') DEFAULT 'route'");
|
||||||
|
} else {
|
||||||
|
// For rollback on other databases, just keep as string
|
||||||
|
// Data with 'product' or 'category' will remain but may cause issues
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,61 @@ class NavbarLink extends Model
|
|||||||
'route_params' => 'json',
|
'route_params' => 'json',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set route_params attribute - convert repeater format to associative array
|
||||||
|
*/
|
||||||
|
public function setRouteParamsAttribute($value)
|
||||||
|
{
|
||||||
|
if (is_array($value) && !empty($value)) {
|
||||||
|
// Check if it's in repeater format [['key' => 'foo', 'value' => 'bar']]
|
||||||
|
$firstItem = reset($value);
|
||||||
|
if (is_array($firstItem) && isset($firstItem['key']) && isset($firstItem['value'])) {
|
||||||
|
// Convert to associative array
|
||||||
|
$params = [];
|
||||||
|
foreach ($value as $item) {
|
||||||
|
if (isset($item['key']) && isset($item['value'])) {
|
||||||
|
$params[$item['key']] = $item['value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->attributes['route_params'] = json_encode($params);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise store as-is
|
||||||
|
$this->attributes['route_params'] = is_string($value) ? $value : json_encode($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get route_params attribute - convert to repeater format for Filament
|
||||||
|
*/
|
||||||
|
public function getRouteParamsAttribute($value)
|
||||||
|
{
|
||||||
|
if (empty($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = is_string($value) ? json_decode($value, true) : $value;
|
||||||
|
|
||||||
|
if (!is_array($decoded)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's already in repeater format, return as-is
|
||||||
|
$firstItem = reset($decoded);
|
||||||
|
if (is_array($firstItem) && isset($firstItem['key']) && isset($firstItem['value'])) {
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert associative array to repeater format for Filament
|
||||||
|
$repeaterFormat = [];
|
||||||
|
foreach ($decoded as $key => $val) {
|
||||||
|
$repeaterFormat[] = ['key' => $key, 'value' => $val];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $repeaterFormat;
|
||||||
|
}
|
||||||
|
|
||||||
public function parent()
|
public function parent()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(NavbarLink::class, 'parent_id');
|
return $this->belongsTo(NavbarLink::class, 'parent_id');
|
||||||
|
|||||||
@@ -123,9 +123,9 @@ class NavbarManager extends Extension
|
|||||||
case 'always_hidden':
|
case 'always_hidden':
|
||||||
return false;
|
return false;
|
||||||
case 'clients_only':
|
case 'clients_only':
|
||||||
return auth()->check() && auth()->user()->role_id === null;
|
return auth()->check() && auth()->user()->role === null;
|
||||||
case 'admin_only':
|
case 'admin_only':
|
||||||
return auth()->check() && auth()->user()->role_id !== null;
|
return auth()->check() && auth()->user()->role !== null;
|
||||||
case 'guests_only':
|
case 'guests_only':
|
||||||
return !auth()->check();
|
return !auth()->check();
|
||||||
default:
|
default:
|
||||||
@@ -159,9 +159,11 @@ class NavbarManager extends Extension
|
|||||||
$formatted['route'] = $link->route_name;
|
$formatted['route'] = $link->route_name;
|
||||||
$formatted['spa'] = true; // Enable SPA for route links
|
$formatted['spa'] = true; // Enable SPA for route links
|
||||||
if ($link->route_params) {
|
if ($link->route_params) {
|
||||||
$params = is_string($link->route_params)
|
// Get raw attribute to avoid accessor transformation
|
||||||
? json_decode($link->route_params, true)
|
$rawParams = $link->getAttributes()['route_params'] ?? null;
|
||||||
: $link->route_params;
|
$params = is_string($rawParams)
|
||||||
|
? json_decode($rawParams, true)
|
||||||
|
: $rawParams;
|
||||||
if ($params && is_array($params)) {
|
if ($params && is_array($params)) {
|
||||||
$formatted['params'] = $params;
|
$formatted['params'] = $params;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ class NavigationLinks extends Component
|
|||||||
case 'always_hidden':
|
case 'always_hidden':
|
||||||
return false;
|
return false;
|
||||||
case 'clients_only':
|
case 'clients_only':
|
||||||
return auth()->check() && auth()->user()->role_id === null;
|
return auth()->check() && auth()->user()->role === null;
|
||||||
case 'admin_only':
|
case 'admin_only':
|
||||||
return auth()->check() && auth()->user()->role_id !== null;
|
return auth()->check() && auth()->user()->role !== null;
|
||||||
case 'guests_only':
|
case 'guests_only':
|
||||||
return !auth()->check();
|
return !auth()->check();
|
||||||
default:
|
default:
|
||||||
@@ -81,9 +81,11 @@ class NavigationLinks extends Component
|
|||||||
'priority' => $link->sort_order,
|
'priority' => $link->sort_order,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Add icon
|
// Add icon with validation and strip -line/-fill suffix
|
||||||
if ($link->icon) {
|
if ($link->icon && $this->isValidIcon($link->icon)) {
|
||||||
$formatted['icon'] = $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
|
// Handle different link types
|
||||||
@@ -91,10 +93,13 @@ class NavigationLinks extends Component
|
|||||||
case 'route':
|
case 'route':
|
||||||
if ($link->route_name) {
|
if ($link->route_name) {
|
||||||
$formatted['route'] = $link->route_name;
|
$formatted['route'] = $link->route_name;
|
||||||
|
$formatted['spa'] = true; // Enable SPA for route links
|
||||||
if ($link->route_params) {
|
if ($link->route_params) {
|
||||||
$params = is_string($link->route_params)
|
// Get raw attribute to avoid accessor transformation
|
||||||
? json_decode($link->route_params, true)
|
$rawParams = $link->getAttributes()['route_params'] ?? null;
|
||||||
: $link->route_params;
|
$params = is_string($rawParams)
|
||||||
|
? json_decode($rawParams, true)
|
||||||
|
: $rawParams;
|
||||||
if ($params && is_array($params)) {
|
if ($params && is_array($params)) {
|
||||||
$formatted['params'] = $params;
|
$formatted['params'] = $params;
|
||||||
}
|
}
|
||||||
@@ -104,10 +109,50 @@ class NavigationLinks extends Component
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'path':
|
case 'path':
|
||||||
$formatted['direct_url'] = $link->custom_path;
|
|
||||||
break;
|
|
||||||
case 'url':
|
case 'url':
|
||||||
$formatted['direct_url'] = $link->custom_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) {
|
||||||
|
$formatted['route'] = 'products.show';
|
||||||
|
$formatted['params'] = [
|
||||||
|
'category' => $product->category->slug ?? $product->category->id,
|
||||||
|
'product' => $product->slug ?? $product->id
|
||||||
|
];
|
||||||
|
$formatted['spa'] = true;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
$formatted['route'] = 'category.show';
|
||||||
|
$formatted['params'] = ['category' => $category->slug ?? $category->id];
|
||||||
|
$formatted['spa'] = true;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,4 +181,13 @@ class NavigationLinks extends Component
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function isValidIcon($icon)
|
||||||
|
{
|
||||||
|
// Basic validation for RemixIcon format
|
||||||
|
if (!preg_match('/^ri-[\w-]+$/', $icon)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user