232 lines
8.7 KiB
PHP
232 lines
8.7 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Settings;
|
|
|
|
use App\Controllers\BaseController;
|
|
use App\Libraries\AuditLogger;
|
|
use App\Models\SettingsModel;
|
|
use App\Models\WorkspaceMemberModel;
|
|
use App\Models\WorkspaceModel;
|
|
use CodeIgniter\HTTP\ResponseInterface;
|
|
use Config\Email as EmailConfig;
|
|
use Config\Services;
|
|
|
|
class SettingsController extends BaseController
|
|
{
|
|
private function ensureWorkspaceMembership(int $workspaceId): bool
|
|
{
|
|
return (new WorkspaceMemberModel())
|
|
->where('workspace_id', $workspaceId)
|
|
->where('user_id', (int) $this->session->get('user_id'))
|
|
->first() !== null;
|
|
}
|
|
|
|
public function workspace()
|
|
{
|
|
$workspaceId = (int) $this->session->get('active_workspace_id');
|
|
if (! $this->ensureWorkspaceMembership($workspaceId)) {
|
|
return redirect()->to('/workspace')->with('error', 'Access denied.');
|
|
}
|
|
|
|
$workspace = (new WorkspaceModel())->find($workspaceId);
|
|
if (! $workspace) {
|
|
return redirect()->to('/workspace')->with('error', 'Workspace not found.');
|
|
}
|
|
|
|
$defaultTheme = (new SettingsModel())->getValue($workspaceId, 'default_theme', 'system') ?? 'system';
|
|
|
|
return view('settings/workspace', [
|
|
'title' => 'Workspace preferences | Chart-Board',
|
|
'workspace' => $workspace,
|
|
'default_theme' => $defaultTheme,
|
|
]);
|
|
}
|
|
|
|
public function saveWorkspace()
|
|
{
|
|
$workspaceId = (int) $this->session->get('active_workspace_id');
|
|
if (! $this->ensureWorkspaceMembership($workspaceId)) {
|
|
return redirect()->to('/workspace')->with('error', 'Access denied.');
|
|
}
|
|
|
|
$workspaceModel = new WorkspaceModel();
|
|
$workspace = $workspaceModel->find($workspaceId);
|
|
if (! $workspace) {
|
|
return redirect()->to('/workspace')->with('error', 'Workspace not found.');
|
|
}
|
|
|
|
$rules = [
|
|
'name' => 'required|min_length[3]|max_length[150]',
|
|
'description' => 'permit_empty|max_length[1000]',
|
|
'timezone' => 'required|max_length[80]',
|
|
'default_refresh' => 'required|in_list[60,300,900,3600]',
|
|
'is_active' => 'required|in_list[0,1]',
|
|
'default_theme' => 'required|in_list[light,dark,system]',
|
|
'logo' => 'permit_empty|uploaded[logo]|max_size[logo,512]|is_image[logo]|mime_in[logo,image/jpg,image/jpeg,image/png,image/webp,image/svg+xml]',
|
|
];
|
|
|
|
$hasLogo = $this->request->getFile('logo') && $this->request->getFile('logo')->isValid();
|
|
if (! $hasLogo) {
|
|
unset($rules['logo']);
|
|
}
|
|
|
|
if (! $this->validate($rules)) {
|
|
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
|
|
}
|
|
|
|
$update = [
|
|
'name' => strip_tags((string) $this->request->getPost('name')),
|
|
'slug' => (string) $this->request->getPost('slug'),
|
|
'description' => strip_tags((string) $this->request->getPost('description')),
|
|
'timezone' => (string) $this->request->getPost('timezone'),
|
|
'default_refresh' => (int) $this->request->getPost('default_refresh'),
|
|
'is_active' => (int) $this->request->getPost('is_active'),
|
|
];
|
|
|
|
if ($hasLogo) {
|
|
$logo = $this->request->getFile('logo');
|
|
$logoName = $logo->getRandomName();
|
|
$logo->move(WRITEPATH . 'uploads/workspaces', $logoName);
|
|
$update['logo'] = 'writable/uploads/workspaces/' . $logoName;
|
|
}
|
|
|
|
$workspaceModel->update($workspaceId, $update);
|
|
|
|
(new SettingsModel())->setValue(
|
|
$workspaceId,
|
|
'default_theme',
|
|
(string) $this->request->getPost('default_theme'),
|
|
'string'
|
|
);
|
|
|
|
AuditLogger::log(
|
|
'settings.workspace_saved',
|
|
'workspace',
|
|
$workspaceId,
|
|
null,
|
|
['name' => $update['name'], 'default_theme' => (string) $this->request->getPost('default_theme')],
|
|
$workspaceId,
|
|
(int) $this->session->get('user_id')
|
|
);
|
|
|
|
return redirect()->to('/settings/workspace')->with('success', 'Workspace preferences saved.');
|
|
}
|
|
|
|
public function notifications()
|
|
{
|
|
$settings = new SettingsModel();
|
|
$slackUrl = $settings->getValue(null, 'slack_webhook_url', '') ?? '';
|
|
$emailConfig = new EmailConfig();
|
|
|
|
return view('settings/notifications', [
|
|
'title' => 'Notification settings | Chart-Board',
|
|
'smtpHost' => $emailConfig->SMTPHost,
|
|
'smtpPort' => $emailConfig->SMTPPort,
|
|
'smtpUser' => $emailConfig->SMTPUser,
|
|
'smtpCrypto' => $emailConfig->SMTPCrypto,
|
|
'fromEmail' => $emailConfig->fromEmail,
|
|
'slackWebhookUrl' => $slackUrl,
|
|
]);
|
|
}
|
|
|
|
public function saveSlackWebhook(): ResponseInterface
|
|
{
|
|
$rules = ['slack_webhook_url' => 'permit_empty|max_length[2000]'];
|
|
if (! $this->validate($rules)) {
|
|
return redirect()->back()->with('errors', $this->validator->getErrors());
|
|
}
|
|
|
|
$url = trim((string) $this->request->getPost('slack_webhook_url'));
|
|
(new SettingsModel())->setValue(null, 'slack_webhook_url', $url, 'string');
|
|
|
|
AuditLogger::log(
|
|
'settings.slack_webhook_saved',
|
|
'settings',
|
|
null,
|
|
null,
|
|
['slack_webhook_url' => $url !== '' ? '[redacted]' : 'cleared'],
|
|
(int) $this->session->get('active_workspace_id') ?: null,
|
|
(int) $this->session->get('user_id')
|
|
);
|
|
|
|
return redirect()->to('/settings/notifications')->with('success', 'Slack webhook URL saved.');
|
|
}
|
|
|
|
public function testSmtp(): ResponseInterface
|
|
{
|
|
$userId = (int) $this->session->get('user_id');
|
|
$user = (new \App\Models\UserModel())->find($userId);
|
|
if (! $user) {
|
|
return redirect()->back()->with('error', 'User not found.');
|
|
}
|
|
|
|
$to = (string) ($user['email'] ?? '');
|
|
if (! filter_var($to, FILTER_VALIDATE_EMAIL)) {
|
|
return redirect()->back()->with('error', 'Your profile email is invalid.');
|
|
}
|
|
|
|
$config = new EmailConfig();
|
|
$email = Services::email();
|
|
$email->setFrom($config->fromEmail, $config->fromName);
|
|
$email->setTo($to);
|
|
$email->setSubject('Chart-Board SMTP test');
|
|
$email->setMessage('<p>This is a test message from Chart-Board. If you received it, SMTP is configured correctly.</p>');
|
|
|
|
if (! $email->send()) {
|
|
return redirect()->back()->with('error', 'SMTP test failed. Check server mail configuration.');
|
|
}
|
|
|
|
return redirect()->back()->with('success', 'Test email sent to your account email address.');
|
|
}
|
|
|
|
public function testSlack(): ResponseInterface
|
|
{
|
|
$url = trim((string) (new SettingsModel())->getValue(null, 'slack_webhook_url', '') ?? '');
|
|
if ($url === '' || ! filter_var($url, FILTER_VALIDATE_URL)) {
|
|
return redirect()->back()->with('error', 'Save a valid Slack webhook URL first.');
|
|
}
|
|
|
|
$payload = json_encode(['text' => 'Chart-Board: Slack webhook test from ' . date('c')], JSON_UNESCAPED_UNICODE);
|
|
$ch = curl_init($url);
|
|
if ($ch === false) {
|
|
return redirect()->back()->with('error', 'Could not start HTTP request.');
|
|
}
|
|
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
|
CURLOPT_POSTFIELDS => $payload,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 15,
|
|
]);
|
|
$body = curl_exec($ch);
|
|
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($code < 200 || $code >= 300) {
|
|
return redirect()->back()->with('error', 'Slack returned HTTP ' . $code . '.');
|
|
}
|
|
|
|
return redirect()->back()->with('success', 'Slack test message sent.');
|
|
}
|
|
|
|
public function saveTheme()
|
|
{
|
|
$rules = ['theme' => 'required|in_list[light,dark,system]'];
|
|
if (! $this->validate($rules)) {
|
|
return redirect()->back()->with('error', 'Invalid theme.');
|
|
}
|
|
|
|
$theme = (string) $this->request->getPost('theme');
|
|
$userId = (int) $this->session->get('user_id');
|
|
if ($userId <= 0) {
|
|
return redirect()->to('/login');
|
|
}
|
|
|
|
(new \App\Models\UserModel())->skipValidation(true)->update($userId, ['theme_preference' => $theme]);
|
|
$this->session->set('theme_preference', $theme);
|
|
|
|
return redirect()->back()->with('success', 'Theme preference saved.');
|
|
}
|
|
}
|