First Commit
This commit is contained in:
Executable
+465
@@ -0,0 +1,465 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use Kernel\View;
|
||||
use Modules\Auth\Models\User;
|
||||
use App\Models\Module;
|
||||
use Modules\Company\Models\Company;
|
||||
use Illuminate\Database\Capsule\Manager as DB;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
abstract class BaseAdminController
|
||||
{
|
||||
protected View $view;
|
||||
|
||||
public function __construct(View $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an admin view with common data (sidebar, user, etc).
|
||||
*
|
||||
* @param string $viewName
|
||||
* @param array $data
|
||||
* @return Response
|
||||
*/
|
||||
protected function renderAdminView(string $viewName, array $data = []): Response
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
|
||||
if (!$user) {
|
||||
return new Response('', 302, ['Location' => url('/login')]);
|
||||
}
|
||||
|
||||
// Get flash messages
|
||||
$flashMessages = [];
|
||||
if (isset($_SESSION['flash'])) {
|
||||
$flashMessages = $_SESSION['flash'];
|
||||
unset($_SESSION['flash']);
|
||||
}
|
||||
|
||||
// Get enabled modules for the user's company
|
||||
$enabledModules = $this->getEnabledModules($user);
|
||||
|
||||
// Build sidebar based on user permissions
|
||||
$sidebarItems = $this->buildUserSidebar($enabledModules, $user);
|
||||
|
||||
// Build navbar based on user permissions
|
||||
$navbarItems = $this->buildUserNavbar($enabledModules, $user);
|
||||
|
||||
// Get admin theme slug (default sb-admin)
|
||||
$adminThemeSlug = 'sb-admin';
|
||||
|
||||
// Get config pages visibility
|
||||
$configPagesVisibility = [];
|
||||
if (class_exists('Modules\Configindex\Models\ConfigPage')) {
|
||||
$baseDir = dirname(dirname(__DIR__));
|
||||
$activeTheme = \Kernel\View::getActiveTheme() ?? 'default';
|
||||
$indexViewsDir = $baseDir . '/modules/index/views';
|
||||
$themeViewsDir = $baseDir . '/themes/' . $activeTheme . '/modules/index/views';
|
||||
|
||||
$pages = \Modules\Configindex\Models\ConfigPage::where('config_index_bus_id', $user->users_bus_id)->get();
|
||||
foreach ($pages as $page) {
|
||||
$key = $page->config_index_key;
|
||||
|
||||
// Verifica se o arquivo físico existe
|
||||
$fileExists = false;
|
||||
if ($key === 'index') {
|
||||
$fileExists = true;
|
||||
} else {
|
||||
$possiblePaths = [
|
||||
$indexViewsDir . '/' . $key . '.twig',
|
||||
$indexViewsDir . '/' . $key . '.php',
|
||||
$themeViewsDir . '/' . $key . '.twig',
|
||||
$themeViewsDir . '/' . $key . '.php',
|
||||
$baseDir . '/themes/' . $activeTheme . '/' . $key . '.twig',
|
||||
$baseDir . '/themes/' . $activeTheme . '/' . $key . '.php',
|
||||
$baseDir . '/modules/' . $key . '/views/index.twig',
|
||||
];
|
||||
foreach ($possiblePaths as $path) {
|
||||
if (is_file($path)) {
|
||||
$fileExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$configPagesVisibility[$key] = [
|
||||
'public' => (bool)$page->config_index_show_public_nav && $fileExists,
|
||||
'dashboard' => (bool)$page->config_index_show_dashboard_nav && $fileExists,
|
||||
'title' => $page->config_index_title ?: ucfirst($key)
|
||||
];
|
||||
}
|
||||
// Garante que as páginas padrão tenham uma configuração inicial, mas respeita o banco de dados
|
||||
foreach (['about', 'contact', 'services'] as $key) {
|
||||
if (!isset($configPagesVisibility[$key])) {
|
||||
$configPagesVisibility[$key] = ['public' => false, 'dashboard' => false, 'title' => ucfirst($key)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$commonData = [
|
||||
'auth' => ['user' => $user],
|
||||
'user' => $user,
|
||||
'sidebar_items' => $sidebarItems,
|
||||
'navbar_items' => $navbarItems,
|
||||
'current_company' => $this->getCurrentCompany($user),
|
||||
'admin_theme_slug' => $adminThemeSlug,
|
||||
'config_pages_visibility' => $configPagesVisibility,
|
||||
'flash_messages' => $flashMessages
|
||||
];
|
||||
|
||||
return new Response($this->view->render($viewName, array_merge($commonData, $data)));
|
||||
}
|
||||
|
||||
protected function redirect(string $path, int $status = 302): Response
|
||||
{
|
||||
return new RedirectResponse(url($path), $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current authenticated user.
|
||||
*
|
||||
* @return User|null
|
||||
*/
|
||||
protected function getCurrentUser(): ?User
|
||||
{
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return User::find($_SESSION['user_id']);
|
||||
} catch (\Exception $e) {
|
||||
error_log("Error loading user: " . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user can edit/delete an object.
|
||||
* Only author, admin or super_admin are allowed.
|
||||
*
|
||||
* @param int|string $authorId
|
||||
* @return bool
|
||||
*/
|
||||
protected function canEditObject($authorId): bool
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user->isSuperAdmin() || $user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (int) $user->users_id === (int) $authorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enabled modules for the user.
|
||||
*
|
||||
* @param User $user
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function getEnabledModules(User $user)
|
||||
{
|
||||
if ($user->isSuperAdmin()) {
|
||||
// Super Admin sees all active modules
|
||||
return Module::where('modules_ative', true)
|
||||
->orderBy('modules_order', 'asc')
|
||||
->get();
|
||||
}
|
||||
|
||||
// For other users, get modules enabled for their company
|
||||
$modules = Module::join('bus_modules', 'modules.modules_id', '=', 'bus_modules.bus_modules_module_id')
|
||||
->where('bus_modules.bus_modules_bus_id', $user->users_bus_id)
|
||||
->where('bus_modules.bus_modules_is_enabled', true)
|
||||
->where('modules.modules_ative', true)
|
||||
->orderBy('modules.modules_order', 'asc')
|
||||
->select('modules.*', 'bus_modules.bus_modules_show_public_nav', 'bus_modules.bus_modules_show_dashboard_nav')
|
||||
->get();
|
||||
|
||||
// If user is Admin, they see everything enabled for the company.
|
||||
// If user is Member or User, we MUST ensure they see modules they have explicit permissions for,
|
||||
// even if the module doesn't have bus_modules_show_dashboard_nav = true (if that was the blocker).
|
||||
|
||||
// If we're missing core modules (auth, index) in the company's enabled list,
|
||||
// we should add them manually if they are active system-wide,
|
||||
// because they are essential for the dashboard to function.
|
||||
// We also add 'company' to ensure Admins can always manage their company details.
|
||||
$coreModules = Module::whereIn('modules_name', ['auth', 'index', 'company'])
|
||||
->where('modules_ative', true)
|
||||
->get();
|
||||
|
||||
foreach ($coreModules as $core) {
|
||||
if (!$modules->contains('modules_name', $core->modules_name)) {
|
||||
$modules->push($core);
|
||||
}
|
||||
}
|
||||
|
||||
return $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build user sidebar based on permissions.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Collection $modules
|
||||
* @param User $user
|
||||
* @return array
|
||||
*/
|
||||
protected function buildUserSidebar($modules, User $user): array
|
||||
{
|
||||
$sidebarItems = [];
|
||||
|
||||
// Ensure company settings link is always present for Admins
|
||||
if ($user->isAdmin() || $user->isSuperAdmin()) {
|
||||
$sidebarItems[] = [
|
||||
'icon' => 'bi bi-building',
|
||||
'label' => __('company.menu.dashboard'),
|
||||
'route' => 'admin/company/settings',
|
||||
'order' => 30
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($modules as $module) {
|
||||
// Skip company module if we already added it manually, or auth/index
|
||||
if ($module->modules_name === 'auth' || $module->modules_name === 'index' || $module->modules_name === 'company') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if module is enabled for sidebar
|
||||
if (!$module->menu_side_bar) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$moduleConfig = $module->modules_settings ?: [];
|
||||
|
||||
// If settings are empty, try to load from module.json file as a fallback
|
||||
if (empty($moduleConfig) || !isset($moduleConfig['menu'])) {
|
||||
$moduleJsonPath = dirname(__DIR__, 2) . '/modules/' . $module->modules_name . '/module.json';
|
||||
if (file_exists($moduleJsonPath)) {
|
||||
$jsonContent = json_decode(file_get_contents($moduleJsonPath), true);
|
||||
if ($jsonContent && isset($jsonContent['menu'])) {
|
||||
$moduleConfig['menu'] = $jsonContent['menu'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix for company module if settings are still empty (due to migration issue)
|
||||
if ((empty($moduleConfig) || !isset($moduleConfig['menu'])) && $module->modules_name === 'company') {
|
||||
$moduleConfig['menu'] = [
|
||||
'dashboard' => [
|
||||
'icon' => 'bi bi-building',
|
||||
'label' => 'company.menu.dashboard',
|
||||
'route' => 'admin/company/settings',
|
||||
'order' => 30
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
if (isset($moduleConfig['menu']['dashboard'])) {
|
||||
$menuItem = $moduleConfig['menu']['dashboard'];
|
||||
|
||||
// Check if user can access this menu item
|
||||
// Respect company visibility preference when not super admin or admin
|
||||
// $companyAllows = ($user->isSuperAdmin() || $user->isAdmin()) || (isset($module->bus_modules_show_dashboard_nav) ? (bool) $module->bus_modules_show_dashboard_nav : true);
|
||||
|
||||
// Simplified logic: if it's in the $modules list (which already filters by company enablement)
|
||||
// we only need to check the user's specific permissions.
|
||||
if ($this->userCanAccessMenuItem($user, $menuItem, $module)) {
|
||||
$sidebarItems[] = [
|
||||
'icon' => $menuItem['icon'] ?? $module->modules_icon,
|
||||
'label' => __($menuItem['label'] ?? $module->modules_display_name),
|
||||
'route' => $menuItem['route'] ?? 'admin/' . $module->modules_name,
|
||||
'order' => $menuItem['order'] ?? $module->modules_order
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by order
|
||||
usort($sidebarItems, function ($a, $b) {
|
||||
return $a['order'] <=> $b['order'];
|
||||
});
|
||||
|
||||
return $sidebarItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build user navbar based on permissions.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Collection $modules
|
||||
* @param User $user
|
||||
* @return array
|
||||
*/
|
||||
protected function buildUserNavbar($modules, User $user): array
|
||||
{
|
||||
$navbarItems = [];
|
||||
|
||||
foreach ($modules as $module) {
|
||||
// Check if module is enabled for navbar
|
||||
if (!$module->menu_nav_bar) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$moduleConfig = $module->modules_settings ?: [];
|
||||
|
||||
// If settings are empty, try to load from module.json file as a fallback
|
||||
if (empty($moduleConfig) || !isset($moduleConfig['menu'])) {
|
||||
$moduleJsonPath = dirname(__DIR__, 2) . '/modules/' . $module->modules_name . '/module.json';
|
||||
if (file_exists($moduleJsonPath)) {
|
||||
$jsonContent = json_decode(file_get_contents($moduleJsonPath), true);
|
||||
if ($jsonContent && isset($jsonContent['menu'])) {
|
||||
$moduleConfig['menu'] = $jsonContent['menu'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get specific navbar config, otherwise fallback to public or dashboard/general
|
||||
if (isset($moduleConfig['menu']['navbar'])) {
|
||||
$menuItem = $moduleConfig['menu']['navbar'];
|
||||
} elseif (isset($moduleConfig['menu']['public'])) {
|
||||
$menuItem = $moduleConfig['menu']['public'];
|
||||
} elseif (isset($moduleConfig['menu']['dashboard'])) {
|
||||
$menuItem = $moduleConfig['menu']['dashboard'];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if user can access this menu item
|
||||
// Respect company visibility preference when not super admin or admin
|
||||
// $companyAllows = ($user->isSuperAdmin() || $user->isAdmin()) || (isset($module->bus_modules_show_public_nav) ? (bool) $module->bus_modules_show_public_nav : true);
|
||||
|
||||
if ($this->userCanAccessMenuItem($user, $menuItem, $module)) {
|
||||
$route = $menuItem['route'] ?? $module->modules_name;
|
||||
|
||||
$navbarItems[] = [
|
||||
'icon' => $menuItem['icon'] ?? $module->modules_icon,
|
||||
'label' => __($menuItem['label'] ?? $module->modules_display_name),
|
||||
'route' => $route,
|
||||
'order' => $menuItem['order'] ?? $module->modules_order
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by order
|
||||
usort($navbarItems, function ($a, $b) {
|
||||
return $a['order'] <=> $b['order'];
|
||||
});
|
||||
|
||||
return $navbarItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can access a menu item.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $menuItem
|
||||
* @param Module|null $module
|
||||
* @return bool
|
||||
*/
|
||||
protected function userCanAccessMenuItem(User $user, array $menuItem, ?Module $module = null): bool
|
||||
{
|
||||
// Custom restriction for companies management (Must check before general admin/superadmin bypass)
|
||||
if (isset($menuItem['route']) && $menuItem['route'] === 'admin/companies') {
|
||||
return $user->isSuperAdmin();
|
||||
}
|
||||
|
||||
// Super Admin and Admin have access to everything authorized for the company
|
||||
if ($user->isSuperAdmin() || $user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If specific permission defined for menu item, check it
|
||||
if (!empty($menuItem['permission'])) {
|
||||
return $user->hasPermission($menuItem['permission']);
|
||||
}
|
||||
|
||||
// If no specific permission, but a module is provided
|
||||
if ($module) {
|
||||
// If the module is a core/blocked module, it's generally accessible
|
||||
// (like Auth or Index)
|
||||
if ($module->isBlocked()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For Member role, if no specific permission is required for the menu item,
|
||||
// we check if they have ANY permission for this module.
|
||||
// This ensures that if an Admin gave them "pagebuilder.view", the Page Builder
|
||||
// module link appears in the sidebar.
|
||||
return $this->userHasModulePermission($user, $module);
|
||||
}
|
||||
|
||||
// Default to allowed for items without permissions or module context
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current company for the user.
|
||||
*
|
||||
* @param User $user
|
||||
* @return Company|null
|
||||
*/
|
||||
protected function getCurrentCompany(User $user): ?Company
|
||||
{
|
||||
if ($user->isSuperAdmin()) {
|
||||
return null; // Super Admin can switch companies
|
||||
}
|
||||
|
||||
return Company::find($user->users_bus_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission for module.
|
||||
*
|
||||
* @param User $user
|
||||
* @param Module $module
|
||||
* @return bool
|
||||
*/
|
||||
protected function userHasModulePermission(User $user, Module $module): bool
|
||||
{
|
||||
// Super Admin and Admin have access to all modules enabled for their company
|
||||
if ($user->isSuperAdmin() || $user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Core modules are accessible to all authenticated users
|
||||
if ($module->isBlocked()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$modulePermissions = $module->getPermissionsAttribute();
|
||||
|
||||
// If module has no permissions defined and is public, it might be a general utility
|
||||
if (empty($modulePermissions) && $module->isPublic()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($modulePermissions as $permission) {
|
||||
if ($user->hasPermission($permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a flash message to the session.
|
||||
*
|
||||
* @param string $type success|error|info|warning
|
||||
* @param string $message
|
||||
*/
|
||||
protected function addFlash(string $type, string $message): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
$_SESSION['flash'][$type] = $message;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user