First Commit
This commit is contained in:
Executable
+9
@@ -0,0 +1,9 @@
|
||||
SysCMS Commercial License
|
||||
|
||||
Copyright (c) 2026 Marco Costa. All rights reserved.
|
||||
|
||||
This software is licensed, not sold. Permission is granted to use this software subject to the terms of the SysCMS Commercial License.
|
||||
|
||||
Redistribution, sublicensing, resale, or disclosure of this software, in whole or in part, is strictly prohibited without prior written permission from the author.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
# System Config Module for SysCMS
|
||||
|
||||
System configuration management module.
|
||||
|
||||
## Features
|
||||
- Global system settings
|
||||
- Configuration profiles
|
||||
- Environment-specific settings
|
||||
- Configuration backup and restore
|
||||
- Settings validation
|
||||
|
||||
## Installation
|
||||
1. Upload the `systemconfig.zip` file through the module manager or extract it to the `modules/` directory.
|
||||
2. Run the migrations to register the module.
|
||||
3. Access system configuration in the admin panel.
|
||||
|
||||
## Routes
|
||||
- Web routes: `routes/web.php`
|
||||
|
||||
## License
|
||||
Copyright (c) 2026 Marco Costa. All rights reserved.
|
||||
Licensed under the SysCMS Commercial License.
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemConfig\Controllers;
|
||||
|
||||
use App\Controllers\BaseAdminController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Modules\SystemConfig\Models\SystemConfig;
|
||||
use Kernel\View;
|
||||
|
||||
class CustomizerController extends BaseAdminController
|
||||
{
|
||||
/**
|
||||
* Show customizer for a specific theme
|
||||
*/
|
||||
public function index(Request $request, string $themeSlug): Response
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
if (!$user) {
|
||||
return new RedirectResponse(url('login'));
|
||||
}
|
||||
|
||||
$baseDir = dirname(dirname(dirname(dirname(__DIR__))));
|
||||
$themePath = $baseDir . '/themes/' . $themeSlug;
|
||||
$customizerFile = $themePath . '/customizer.json';
|
||||
|
||||
if (!is_file($customizerFile)) {
|
||||
return new Response("O tema " . ucfirst($themeSlug) . " não possui opções de personalização.", 404);
|
||||
}
|
||||
|
||||
$config = json_decode(file_get_contents($customizerFile), true);
|
||||
|
||||
// Get current values from DB
|
||||
$dbKey = 'theme_settings_' . $themeSlug;
|
||||
$savedSettings = json_decode(SystemConfig::get($dbKey, '{}'), true);
|
||||
|
||||
// Merge defaults with saved values
|
||||
foreach ($config['sections'] as &$section) {
|
||||
foreach ($section['fields'] as &$field) {
|
||||
$field['value'] = $savedSettings[$field['id']] ?? $field['default'];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->renderAdminView('systemconfig/views/admin/customizer/index.twig', [
|
||||
'title' => 'Personalizar Tema: ' . ucfirst($themeSlug),
|
||||
'theme' => $themeSlug,
|
||||
'config' => $config,
|
||||
'saved' => $savedSettings
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save customizer settings
|
||||
*/
|
||||
public function save(Request $request, string $themeSlug): Response
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
if (!$user) {
|
||||
return new RedirectResponse(url('login'));
|
||||
}
|
||||
|
||||
$settings = $request->request->all();
|
||||
// Remove CSRF or other non-setting fields if present
|
||||
unset($settings['_token']);
|
||||
|
||||
$dbKey = 'theme_settings_' . $themeSlug;
|
||||
|
||||
$config = SystemConfig::where('system_config_key', $dbKey)->first();
|
||||
if (!$config) {
|
||||
$config = new SystemConfig();
|
||||
$config->system_config_key = $dbKey;
|
||||
$config->system_config_type = 'json';
|
||||
$config->system_config_is_public = true;
|
||||
}
|
||||
|
||||
$config->system_config_value = json_encode($settings, JSON_UNESCAPED_UNICODE);
|
||||
$config->system_config_updated_by = $user->users_id ?? 1;
|
||||
$config->save();
|
||||
|
||||
return new RedirectResponse(url('admin/themes/' . $themeSlug . '/customize?success=1'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemConfig\Controllers;
|
||||
|
||||
use App\Controllers\BaseAdminController;
|
||||
use Modules\SystemConfig\Models\SystemConfig;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class SystemConfigController extends BaseAdminController
|
||||
{
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
|
||||
if (!$user || !$user->isSuperAdmin()) {
|
||||
return new Response('Access Denied', 403);
|
||||
}
|
||||
|
||||
$configs = SystemConfig::all();
|
||||
|
||||
return $this->renderAdminView('systemconfig/views/admin/index.twig', [
|
||||
'configs' => $configs,
|
||||
'title' => 'Configurações do Sistema'
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
|
||||
if (!$user || !$user->isSuperAdmin()) {
|
||||
return new Response('Access Denied', 403);
|
||||
}
|
||||
|
||||
$data = $request->request->all();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($key === 'csrf_token') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$config = SystemConfig::where('system_config_key', $key)->first();
|
||||
|
||||
if ($config) {
|
||||
$config->system_config_value = $value;
|
||||
$config->system_config_updated_by = $user->users_id;
|
||||
$config->save();
|
||||
} else {
|
||||
SystemConfig::create([
|
||||
'system_config_key' => $key,
|
||||
'system_config_value' => $value,
|
||||
'system_config_updated_by' => $user->users_id
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response('', 302, ['Location' => url('admin/systemconfig')]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemConfig\Controllers;
|
||||
|
||||
use App\Controllers\BaseAdminController;
|
||||
use Modules\SystemConfig\Models\SystemConfig;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
class ThemeController extends BaseAdminController
|
||||
{
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
if (!$user || !$user->isSuperAdmin()) {
|
||||
return new Response('Access Denied', 403);
|
||||
}
|
||||
|
||||
$themesDir = dirname(dirname(dirname(dirname(__DIR__)))) . '/themes';
|
||||
$themes = [];
|
||||
|
||||
if (is_dir($themesDir)) {
|
||||
$folders = array_diff(scandir($themesDir), ['.', '..']);
|
||||
foreach ($folders as $folder) {
|
||||
if (is_dir($themesDir . '/' . $folder)) {
|
||||
$themes[$folder] = $this->getThemeInfo($themesDir . '/' . $folder, $folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$activeTheme = \get_active_theme();
|
||||
|
||||
return $this->renderAdminView('systemconfig/views/admin/themes/index.twig', [
|
||||
'themes' => $themes,
|
||||
'activeTheme' => $activeTheme,
|
||||
'title' => 'Gerenciar Temas'
|
||||
]);
|
||||
}
|
||||
|
||||
public function activate(Request $request, string $theme): Response
|
||||
{
|
||||
$user = $this->getCurrentUser();
|
||||
if (!$user || !$user->isSuperAdmin()) {
|
||||
return new Response('Access Denied', 403);
|
||||
}
|
||||
|
||||
$themesDir = dirname(dirname(dirname(dirname(__DIR__)))) . '/themes';
|
||||
if (!is_dir($themesDir . '/' . $theme)) {
|
||||
return new Response('Tema não encontrado', 404);
|
||||
}
|
||||
|
||||
SystemConfig::updateOrCreate(
|
||||
['system_config_key' => 'active_theme'],
|
||||
[
|
||||
'system_config_value' => $theme,
|
||||
'system_config_updated_by' => $user->users_id,
|
||||
'system_config_type' => 'string'
|
||||
]
|
||||
);
|
||||
|
||||
// Clear Twig cache
|
||||
$cacheDir = dirname(dirname(dirname(dirname(__DIR__)))) . '/cache/twig';
|
||||
if (is_dir($cacheDir)) {
|
||||
$this->rrmdir($cacheDir);
|
||||
}
|
||||
|
||||
return new RedirectResponse(url('admin/themes'));
|
||||
}
|
||||
|
||||
protected function getThemeInfo(string $path, string $slug): array
|
||||
{
|
||||
$info = [
|
||||
'slug' => $slug,
|
||||
'name' => $slug,
|
||||
'version' => '1.0.0',
|
||||
'author' => 'Desconhecido',
|
||||
'description' => '',
|
||||
'screenshot' => null,
|
||||
'is_customizable' => file_exists($path . '/customizer.json')
|
||||
];
|
||||
|
||||
$stylePath = $path . '/style.css';
|
||||
if (file_exists($stylePath)) {
|
||||
$content = file_get_contents($stylePath);
|
||||
if (preg_match('/Theme Name:\s*(.*)$/mi', $content, $matches)) {
|
||||
$info['name'] = trim($matches[1]);
|
||||
}
|
||||
if (preg_match('/Version:\s*(.*)$/mi', $content, $matches)) {
|
||||
$info['version'] = trim($matches[1]);
|
||||
}
|
||||
if (preg_match('/Author:\s*(.*)$/mi', $content, $matches)) {
|
||||
$info['author'] = trim($matches[1]);
|
||||
}
|
||||
if (preg_match('/Description:\s*(.*)$/mi', $content, $matches)) {
|
||||
$info['description'] = trim($matches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($path . '/screenshot.png')) {
|
||||
$info['screenshot'] = '/themes/' . $slug . '/screenshot.png';
|
||||
} elseif (file_exists($path . '/screenshot.jpg')) {
|
||||
$info['screenshot'] = '/themes/' . $slug . '/screenshot.jpg';
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
private function rrmdir($dir)
|
||||
{
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (is_dir($dir . DIRECTORY_SEPARATOR . $object) && !is_link($dir . "/" . $object)) {
|
||||
$this->rrmdir($dir . DIRECTORY_SEPARATOR . $object);
|
||||
} else {
|
||||
unlink($dir . DIRECTORY_SEPARATOR . $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemConfig\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Modules\SystemConfig\Models\SystemConfig;
|
||||
use Kernel\View;
|
||||
|
||||
class MaintenanceMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
// Check if maintenance mode is enabled
|
||||
$maintenanceMode = false;
|
||||
try {
|
||||
if (class_exists(SystemConfig::class)) {
|
||||
$config = SystemConfig::where('system_config_key', 'maintenance_mode')->first();
|
||||
if ($config && $config->system_config_value == '1') {
|
||||
$maintenanceMode = true;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// If DB fails, assume not in maintenance or handle gracefully
|
||||
$maintenanceMode = false;
|
||||
}
|
||||
|
||||
if (!$maintenanceMode) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// Inject maintenance mode variable into view
|
||||
try {
|
||||
$view = new View();
|
||||
$view->getEngine()->addGlobal('is_maintenance_mode', true);
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
// Allow access to login route and assets
|
||||
$path = $request->getPathInfo();
|
||||
$allowedRoutes = ['/login', '/auth/login'];
|
||||
$isAsset = preg_match('/\.(css|js|jpg|jpeg|png|gif|ico|woff|woff2|ttf|svg)$/', $path);
|
||||
|
||||
if (in_array($path, $allowedRoutes) || $isAsset) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// Allow Super Admin to bypass
|
||||
if ($this->isSuperAdmin()) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// Render maintenance page
|
||||
try {
|
||||
$view = new View();
|
||||
return new Response($view->render('errors/maintenance.twig'), 503);
|
||||
} catch (\Throwable $e) {
|
||||
return new Response('Maintenance Mode', 503);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user is Super Admin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSuperAdmin(): bool
|
||||
{
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (class_exists('\Modules\Auth\Models\User')) {
|
||||
$user = \Modules\Auth\Models\User::find($_SESSION['user_id']);
|
||||
return $user && $user->isSuperAdmin();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemConfig\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class SystemConfig extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'system_config';
|
||||
|
||||
/**
|
||||
* The primary key associated with table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'system_config_id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'system_config_key',
|
||||
'system_config_value',
|
||||
'system_config_type',
|
||||
'system_config_description',
|
||||
'system_config_is_public',
|
||||
'system_config_updated_by'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'system_config_is_public' => 'boolean',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = [
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $timestamps = true;
|
||||
|
||||
/**
|
||||
* Get the user who updated the config.
|
||||
*/
|
||||
public function updatedByUser(): BelongsTo
|
||||
{
|
||||
// Check if Auth module exists before relating
|
||||
if (class_exists('\Modules\Auth\Models\User')) {
|
||||
return $this->belongsTo(\Modules\Auth\Models\User::class, 'system_config_updated_by');
|
||||
}
|
||||
// Fallback or return empty relation if Auth module is missing (unlikely but safe)
|
||||
return $this->belongsTo(self::class, 'system_config_updated_by')->whereNull('system_config_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config value by key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get(string $key, $default = null)
|
||||
{
|
||||
$config = self::where('system_config_key', $key)->first();
|
||||
|
||||
if (!$config) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
// Handle type casting if needed based on system_config_type
|
||||
// For now, return value directly
|
||||
return $config->system_config_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config value.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param string $type
|
||||
* @param string|null $description
|
||||
* @param bool $isPublic
|
||||
* @return self
|
||||
*/
|
||||
public static function set(string $key, $value, string $type = 'string', ?string $description = null, bool $isPublic = false): self
|
||||
{
|
||||
$config = self::firstOrNew(['system_config_key' => $key]);
|
||||
|
||||
$config->system_config_value = $value;
|
||||
$config->system_config_type = $type;
|
||||
|
||||
if ($description !== null) {
|
||||
$config->system_config_description = $description;
|
||||
}
|
||||
|
||||
$config->system_config_is_public = $isPublic;
|
||||
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
$config->system_config_updated_by = $_SESSION['user_id'];
|
||||
}
|
||||
|
||||
$config->save();
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemConfig\Providers;
|
||||
|
||||
use Kernel\Application;
|
||||
use Modules\SystemConfig\Middleware\MaintenanceMiddleware;
|
||||
|
||||
class SystemConfigServiceProvider
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$app = Application::getInstance();
|
||||
|
||||
// Register Global Middleware (runs on all requests)
|
||||
if (method_exists($app, 'addGlobalMiddleware')) {
|
||||
$app->addGlobalMiddleware(new MaintenanceMiddleware());
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class CreateSystemConfigTables
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
// System Config
|
||||
if (!Capsule::schema()->hasTable('system_config')) {
|
||||
Capsule::schema()->create('system_config', function (Blueprint $table) {
|
||||
$table->increments('system_config_id');
|
||||
$table->string('system_config_key')->unique();
|
||||
$table->text('system_config_value')->nullable();
|
||||
$table->string('system_config_type')->default('string'); // string, boolean, integer, json
|
||||
$table->text('system_config_description')->nullable();
|
||||
$table->boolean('system_config_is_public')->default(false);
|
||||
$table->integer('system_config_updated_by')->unsigned()->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
// Modules Table (Core table, placed here for system initialization)
|
||||
if (!Capsule::schema()->hasTable('modules')) {
|
||||
Capsule::schema()->create('modules', function (Blueprint $table) {
|
||||
$table->increments('modules_id');
|
||||
$table->string('modules_name')->unique(); // Folder name
|
||||
$table->string('modules_display_name')->nullable();
|
||||
$table->text('modules_description')->nullable();
|
||||
$table->string('modules_version')->default('1.0.0');
|
||||
$table->string('modules_author')->nullable();
|
||||
$table->boolean('modules_ative')->default(false);
|
||||
$table->boolean('modules_public')->default(false);
|
||||
$table->boolean('modules_blocked')->default(false);
|
||||
$table->json('modules_dependencies')->nullable();
|
||||
$table->json('modules_permissions')->nullable();
|
||||
$table->json('modules_settings')->nullable();
|
||||
$table->string('modules_path')->nullable();
|
||||
$table->string('modules_namespace')->nullable();
|
||||
$table->string('modules_icon')->nullable();
|
||||
$table->boolean('menu_side_bar')->default(false);
|
||||
$table->boolean('menu_nav_bar')->default(false);
|
||||
$table->integer('modules_order')->default(0);
|
||||
$table->timestamp('modules_install_date')->nullable();
|
||||
$table->timestamp('modules_update_date')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Capsule::schema()->dropIfExists('modules');
|
||||
Capsule::schema()->dropIfExists('system_config');
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use App\Models\Module;
|
||||
use Modules\Company\Models\Company;
|
||||
|
||||
class InsertSystemConfigModule
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$moduleData = [
|
||||
'modules_name' => 'systemconfig',
|
||||
'modules_display_name' => 'Configurações do Sistema',
|
||||
'modules_description' => 'Módulo de configuração geral do sistema SysCMS',
|
||||
'modules_version' => '1.0.0',
|
||||
'modules_author' => 'SysCMS Team',
|
||||
'modules_ative' => true,
|
||||
'modules_public' => false,
|
||||
'modules_blocked' => false,
|
||||
'modules_dependencies' => [],
|
||||
'modules_permissions' => [
|
||||
"systemconfig.view",
|
||||
"systemconfig.edit"
|
||||
],
|
||||
'modules_settings' => [],
|
||||
'modules_path' => 'modules/systemconfig',
|
||||
'modules_namespace' => 'Modules\\SystemConfig',
|
||||
'modules_icon' => 'bi bi-gear', // Inferred as none in json, but good practice
|
||||
'menu_side_bar' => true,
|
||||
'menu_nav_bar' => false,
|
||||
'modules_order' => 99, // Usually high number for settings
|
||||
'modules_install_date' => date('Y-m-d H:i:s'),
|
||||
'modules_update_date' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
$existingModule = Module::where('modules_name', 'systemconfig')->first();
|
||||
|
||||
if (!$existingModule) {
|
||||
$module = Module::create($moduleData);
|
||||
|
||||
if (Capsule::schema()->hasTable('bus')) {
|
||||
$companies = Company::all();
|
||||
foreach ($companies as $company) {
|
||||
$exists = Capsule::table('bus_modules')
|
||||
->where('bus_modules_bus_id', $company->bus_id)
|
||||
->where('bus_modules_module_id', $module->modules_id)
|
||||
->exists();
|
||||
|
||||
if (!$exists) {
|
||||
Capsule::table('bus_modules')->insert([
|
||||
'bus_modules_bus_id' => $company->bus_id,
|
||||
'bus_modules_module_id' => $module->modules_id,
|
||||
'bus_modules_is_enabled' => true,
|
||||
'bus_modules_settings' => json_encode([]),
|
||||
'bus_modules_enabled_by' => 1,
|
||||
'bus_modules_enabled_at' => date('Y-m-d H:i:s'),
|
||||
'bus_modules_show_public_nav' => true,
|
||||
'bus_modules_show_dashboard_nav' => true,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$module = Module::where('modules_name', 'systemconfig')->first();
|
||||
if ($module) {
|
||||
Capsule::table('bus_modules')->where('bus_modules_module_id', $module->modules_id)->delete();
|
||||
$module->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
|
||||
class AddActiveThemeToConfig
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$exists = Capsule::table('system_config')
|
||||
->where('system_config_key', 'active_theme')
|
||||
->exists();
|
||||
|
||||
if (!$exists) {
|
||||
Capsule::table('system_config')->insert([
|
||||
'system_config_key' => 'active_theme',
|
||||
'system_config_value' => 'default',
|
||||
'system_config_type' => 'string',
|
||||
'system_config_description' => 'O tema atualmente ativo no sistema.',
|
||||
'system_config_is_public' => true,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Capsule::table('system_config')
|
||||
->where('system_config_key', 'active_theme')
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Systemkonfigurationen",
|
||||
"general_settings": "Allgemeine Einstellungen",
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
"key": "Schlüssel",
|
||||
"no_configs": "Keine Konfigurationen im System gefunden.",
|
||||
"save_changes": "Änderungen speichern"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "System Configurations",
|
||||
"general_settings": "General Settings",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"key": "Key",
|
||||
"no_configs": "No configurations found in the system.",
|
||||
"save_changes": "Save Changes"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Configuraciones del Sistema",
|
||||
"general_settings": "Configuraciones Generales",
|
||||
"yes": "Sí",
|
||||
"no": "No",
|
||||
"key": "Clave",
|
||||
"no_configs": "No se encontraron configuraciones en el sistema.",
|
||||
"save_changes": "Guardar Cambios"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Configurations du Système",
|
||||
"general_settings": "Paramètres Généraux",
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
"key": "Clé",
|
||||
"no_configs": "Aucune configuration trouvée dans le système.",
|
||||
"save_changes": "Enregistrer les modifications"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Configurações do Sistema",
|
||||
"general_settings": "Configurações Gerais",
|
||||
"yes": "Sim",
|
||||
"no": "Não",
|
||||
"key": "Chave",
|
||||
"no_configs": "Nenhuma configuração encontrada no sistema.",
|
||||
"save_changes": "Salvar Alterações"
|
||||
}
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "systemconfig",
|
||||
"display_name": "Configurações do Sistema",
|
||||
"description": "Módulo de configuração geral do sistema SysCMS",
|
||||
"version": "1.0.0",
|
||||
"author": "SysCMS Team",
|
||||
"dependencies": [],
|
||||
"permissions": [
|
||||
"systemconfig.view",
|
||||
"systemconfig.edit"
|
||||
],
|
||||
"settings": [],
|
||||
"providers": [
|
||||
"Modules\\SystemConfig\\Providers\\SystemConfigServiceProvider"
|
||||
],
|
||||
"routes": {
|
||||
"web": "routes/web.php"
|
||||
},
|
||||
"views": "views",
|
||||
"locales": "locales",
|
||||
"module_active": true,
|
||||
"module_public": false,
|
||||
"module_blocked": false,
|
||||
"ready": true,
|
||||
"tables": [
|
||||
"system_config"
|
||||
]
|
||||
}
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Kernel\Application;
|
||||
|
||||
return function (Application $app) {
|
||||
$app->addRoute('GET', '/admin/systemconfig', [\Modules\SystemConfig\Controllers\SystemConfigController::class, 'index']);
|
||||
$app->addRoute('POST', '/admin/systemconfig/update', [\Modules\SystemConfig\Controllers\SystemConfigController::class, 'update']);
|
||||
|
||||
// Theme Management
|
||||
$app->addRoute('GET', '/admin/themes', [\Modules\SystemConfig\Controllers\ThemeController::class, 'index']);
|
||||
$app->addRoute('POST', '/admin/themes/activate/{theme}', [\Modules\SystemConfig\Controllers\ThemeController::class, 'activate']);
|
||||
|
||||
// Theme Customizer
|
||||
$app->addRoute('GET', '/admin/themes/{theme}/customize', [\Modules\SystemConfig\Controllers\CustomizerController::class, 'index']);
|
||||
$app->addRoute('POST', '/admin/themes/{theme}/customize/save', [\Modules\SystemConfig\Controllers\CustomizerController::class, 'save']);
|
||||
};
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
{% extends "layouts/dashboard.twig" %}
|
||||
|
||||
{% block title %}{{ title }} - {{ platformName }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid px-4">
|
||||
<h1 class="mt-4">{{ title }}</h1>
|
||||
<ol class="breadcrumb mb-4">
|
||||
<li class="breadcrumb-item"><a href="{{ url('dashboard') }}">{{ __('layout.menu.dashboard') }}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ url('admin/themes') }}">Temas</a></li>
|
||||
<li class="breadcrumb-item active">Personalizar</li>
|
||||
</ol>
|
||||
|
||||
{% if session_has('success') or current_uri matches '/success=1/' %}
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
Configurações salvas com sucesso!
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<form action="{{ url('admin/themes/' ~ theme ~ '/customize/save') }}" method="POST">
|
||||
{% for section in config.sections %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-light">
|
||||
<i class="bi bi-gear-fill me-1"></i>
|
||||
<strong>{{ section.title }}</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% for field in section.fields %}
|
||||
<div class="mb-3 row align-items-center">
|
||||
<label for="{{ field.id }}" class="col-sm-4 col-form-label">{{ field.label }}</label>
|
||||
<div class="col-sm-8">
|
||||
{% if field.type == 'color' %}
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="color" class="form-control form-control-color me-2" id="{{ field.id }}" name="{{ field.id }}" value="{{ field.value }}" title="Escolha a cor">
|
||||
<code>{{ field.value }}</code>
|
||||
</div>
|
||||
{% elseif field.type == 'text' %}
|
||||
<input type="text" class="form-control" id="{{ field.id }}" name="{{ field.id }}" value="{{ field.value }}">
|
||||
{% elseif field.type == 'switch' %}
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="{{ field.id }}" name="{{ field.id }}" value="1" {{ field.value ? 'checked' : '' }}>
|
||||
</div>
|
||||
{% elseif field.type == 'select' %}
|
||||
<select class="form-select" id="{{ field.id }}" name="{{ field.id }}">
|
||||
{% for val, label in field.options %}
|
||||
<option value="{{ val }}" {{ field.value == val ? 'selected' : '' }}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
{% if field.description %}
|
||||
<div class="form-text">{{ field.description }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end mb-5">
|
||||
<a href="{{ url('admin/themes') }}" class="btn btn-secondary">Cancelar</a>
|
||||
<button type="submit" class="btn btn-primary">Salvar Alterações</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card mb-4 shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
Dica de Uso
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Essas configurações são injetadas automaticamente no seu tema.</p>
|
||||
<p>Você pode acessá-las no Twig usando:</p>
|
||||
<pre class="bg-light p-2 rounded"><code>{{ '{{ theme_settings.campo_id }}' }}</code></pre>
|
||||
<hr>
|
||||
<h6>Exemplo de CSS dinâmico:</h6>
|
||||
<pre class="bg-light p-2 rounded small"><code><style>
|
||||
:root {
|
||||
--primary-color: {{ '{{ theme_settings.primary_color }}' }};
|
||||
}
|
||||
</style></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
{% extends "layouts/dashboard.twig" %}
|
||||
|
||||
{% block title %}{{ __('systemconfig.title') }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">{{ __('systemconfig.title') }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">{{ __('systemconfig.general_settings') }}</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url('admin/systemconfig/update') }}">
|
||||
{{ csrf_field()|raw }}
|
||||
|
||||
{% if configs|length > 0 %}
|
||||
{% for config in configs %}
|
||||
<div class="mb-3">
|
||||
<label for="{{ config.system_config_key }}" class="form-label fw-bold">
|
||||
{{ config.system_config_description ? config.system_config_description : config.system_config_key }}
|
||||
</label>
|
||||
|
||||
{% if config.system_config_type == 'boolean' %}
|
||||
<select class="form-select" id="{{ config.system_config_key }}" name="{{ config.system_config_key }}">
|
||||
<option value="1" {% if config.system_config_value == '1' %}selected{% endif %}>{{ __('systemconfig.yes') }}</option>
|
||||
<option value="0" {% if config.system_config_value == '0' %}selected{% endif %}>{{ __('systemconfig.no') }}</option>
|
||||
</select>
|
||||
{% elseif config.system_config_type == 'textarea' %}
|
||||
<textarea class="form-control" id="{{ config.system_config_key }}" name="{{ config.system_config_key }}" rows="3">{{ config.system_config_value }}</textarea>
|
||||
{% else %}
|
||||
<input type="text" class="form-control" id="{{ config.system_config_key }}" name="{{ config.system_config_key }}" value="{{ config.system_config_value }}">
|
||||
{% endif %}
|
||||
|
||||
<div class="form-text text-muted small">{{ __('systemconfig.key') }}: {{ config.system_config_key }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
{{ __('systemconfig.no_configs') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save"></i> {{ __('systemconfig.save_changes') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
{% extends 'layouts/dashboard.twig' %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid px-4">
|
||||
<h1 class="mt-4">Temas</h1>
|
||||
<ol class="breadcrumb mb-4">
|
||||
<li class="breadcrumb-item"><a href="{{ url('admin') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Temas</li>
|
||||
</ol>
|
||||
|
||||
<div class="row">
|
||||
{% for slug, theme in themes %}
|
||||
<div class="col-xl-4 col-md-6 mb-4">
|
||||
<div class="card h-100 theme-card {% if slug == activeTheme %}border-primary border-3{% endif %} shadow-sm">
|
||||
{% if theme.screenshot %}
|
||||
<img src="{{ theme.screenshot }}" class="card-img-top" alt="{{ theme.name }}" style="height: 200px; object-fit: cover;">
|
||||
{% else %}
|
||||
<div class="card-img-top bg-secondary d-flex align-items-center justify-content-center text-white" style="height: 200px;">
|
||||
<i class="bi bi-image" style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card-body">
|
||||
<h5 class="card-title d-flex justify-content-between align-items-center">
|
||||
{{ theme.name }}
|
||||
{% if slug == activeTheme %}
|
||||
<span class="badge bg-primary">Ativo</span>
|
||||
{% endif %}
|
||||
</h5>
|
||||
<p class="card-text text-muted small">{{ theme.description|slice(0, 100) }}{% if theme.description|length > 100 %}...{% endif %}</p>
|
||||
<div class="theme-meta mb-3">
|
||||
<span class="badge bg-light text-dark">v{{ theme.version }}</span>
|
||||
<span class="badge bg-light text-dark">Por: {{ theme.author }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer bg-white border-top-0 pb-3">
|
||||
<div class="d-grid gap-2">
|
||||
{% if slug == activeTheme %}
|
||||
{% if theme.is_customizable %}
|
||||
<a href="{{ url('admin/themes/' ~ slug ~ '/customize') }}" class="btn btn-outline-primary">Personalizar</a>
|
||||
{% else %}
|
||||
<button class="btn btn-outline-secondary" disabled title="Este tema não suporta o Customizer">Personalizar</button>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<form action="{{ url('admin/themes/activate/' ~ slug) }}" method="POST">
|
||||
{{ csrf_field()|raw }}
|
||||
<button type="submit" class="btn btn-primary w-100">Ativar</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{# Card para Adicionar Novo Tema (Simulação) #}
|
||||
<div class="col-xl-4 col-md-6 mb-4">
|
||||
<div class="card h-100 border-dashed d-flex align-items-center justify-content-center bg-light" style="border: 2px dashed #dee2e6; min-height: 350px;">
|
||||
<div class="text-center p-5">
|
||||
<i class="bi bi-plus-circle text-muted" style="font-size: 3rem;"></i>
|
||||
<h5 class="mt-3 text-muted">Adicionar Novo Tema</h5>
|
||||
<p class="text-muted small">Faça upload de um arquivo .zip ou explore o repositório.</p>
|
||||
<button class="btn btn-outline-primary btn-sm mt-2" disabled>Upload (Breve)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.theme-card:hover {
|
||||
transform: translateY(-5px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.border-dashed {
|
||||
border-style: dashed !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user