Files
Leantime/app/Domain/Projects/Controllers/ShowProject.php

311 lines
12 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Leantime\Domain\Projects\Controllers;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Auth\Models\Roles;
use Leantime\Domain\Auth\Services\Auth;
use Leantime\Domain\Bom\Services\Bom as BomService;
use Leantime\Domain\Bom\Services\MasterRef as MasterRefService;
use Leantime\Domain\Clients\Services\Clients as ClientService;
use Leantime\Domain\Projects\Services\Projects as ProjectService;
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
use Symfony\Component\HttpFoundation\Response;
class ShowProject extends Controller
{
private ProjectService $projectService;
private TicketService $ticketService;
private ClientService $clientService;
private BomService $bomService;
private MasterRefService $masterRefService;
/**
* Initializes dependencies.
*/
public function init(
ProjectService $projectService,
TicketService $ticketService,
ClientService $clientService,
BomService $bomService,
MasterRefService $masterRefService
): void {
Auth::authOrRedirect([Roles::$owner, Roles::$admin, Roles::$manager]);
$this->projectService = $projectService;
$this->ticketService = $ticketService;
$this->clientService = $clientService;
$this->bomService = $bomService;
$this->masterRefService = $masterRefService;
if (! session()->exists('lastPage')) {
session(['lastPage' => CURRENT_URL]);
}
}
/**
* Displays the project settings page.
*
* @param array $params Request parameters
*/
public function get(array $params): Response
{
if (! isset($params['id'])) {
return $this->tpl->display('errors.error404', responseCode: 404);
}
$id = (int) $params['id'];
if (Auth::userHasRole(Roles::$manager)) {
if ($this->projectService->isUserAssignedToProject(session('userdata.id'), $id) === false) {
return Frontcontroller::redirect(BASE_URL.'/errors/error403');
}
}
$project = $this->projectService->getProject($id);
if (! isset($project['id'])) {
return Frontcontroller::redirect(BASE_URL.'/errors/error404');
}
if (session('currentProject') != $project['id']) {
$this->projectService->changeCurrentSessionProject($project['id']);
}
session(['lastPage' => BASE_URL.'/projects/showProject/'.$id]);
$project['assignedUsers'] = $this->projectService->getUsersAssignedToProject($id, true);
$this->assignIntegrationSettings($id);
$this->assignTemplateVars($id, $project);
$this->assignMasterData($id);
return $this->tpl->display('projects.showProject');
}
/**
* Handles project settings form submissions.
*
* @param array $params Request parameters
*/
public function post(array $params): Response
{
if (! isset($params['id'])) {
return $this->tpl->display('errors.error404', responseCode: 404);
}
$id = (int) $params['id'];
if (Auth::userHasRole(Roles::$manager)) {
if ($this->projectService->isUserAssignedToProject(session('userdata.id'), $id) === false) {
return Frontcontroller::redirect(BASE_URL.'/errors/error403');
}
}
$project = $this->projectService->getProject($id);
if (! isset($project['id'])) {
return Frontcontroller::redirect(BASE_URL.'/errors/error404');
}
if (session('currentProject') != $project['id']) {
$this->projectService->changeCurrentSessionProject($project['id']);
}
// Handle Mattermost integration
if (isset($_POST['mattermostSave'])) {
$this->projectService->saveMattermostWebhook($id, $_POST['mattermostWebhookURL']);
$this->tpl->setNotification($this->language->__('notification.saved_mattermost_webhook'), 'success');
}
// Handle Slack integration
if (isset($_POST['slackSave'])) {
$this->projectService->saveSlackWebhook($id, $_POST['slackWebhookURL']);
$this->tpl->setNotification($this->language->__('notification.saved_slack_webhook'), 'success');
}
// Handle Zulip integration
if (isset($_POST['zulipSave'])) {
$zulipResult = $this->projectService->saveZulipWebhook($id, $_POST);
if ($zulipResult['saved']) {
$this->tpl->setNotification($this->language->__('notification.saved_zulip_webhook'), 'success');
} else {
$this->tpl->setNotification($this->language->__('notification.error_zulip_webhook_fill_out_fields'), 'error');
}
$this->tpl->assign('zulipHook', $zulipResult['hook']);
}
// Handle Telegram integration
if (isset($_POST['telegramSave'])) {
$telegramResult = $this->projectService->saveTelegramWebhook($id, $_POST);
if ($telegramResult['saved']) {
$this->tpl->setNotification($this->language->__('notification.saved_telegram_webhook'), 'success');
} else {
$errorKey = $telegramResult['error'] === 'missing_token'
? 'notification.error_telegram_missing_token'
: 'notification.error_telegram_chat_not_found';
$this->tpl->setNotification($this->language->__($errorKey), 'error');
}
$this->tpl->assign('telegramHook', $telegramResult['hook']);
}
// Handle Discord integration
if (isset($_POST['discordSave'])) {
$this->projectService->saveDiscordWebhooks($id, $_POST);
$this->tpl->setNotification($this->language->__('notification.saved_discord_webhook'), 'success');
}
// Handle status label settings
if (isset($_POST['submitSettings'])) {
if (isset($_POST['labelKeys']) && is_array($_POST['labelKeys']) && count($_POST['labelKeys']) > 0) {
if ($this->ticketService->saveStatusLabels($_POST)) {
$this->tpl->setNotification($this->language->__('notification.new_status_saved'), 'success');
} else {
$this->tpl->setNotification($this->language->__('notification.error_saving_status'), 'error');
}
} else {
$this->tpl->setNotification($this->language->__('notification.at_least_one_status'), 'error');
}
}
// Handle user assignment
if (isset($_POST['saveUsers'])) {
$assignedUsers = (isset($_POST['editorId']) && count($_POST['editorId']))
? $_POST['editorId']
: [];
$this->projectService->updateProjectUsers($id, $assignedUsers, $_POST);
$this->tpl->setNotification($this->language->__('notifications.user_was_added_to_project'), 'success');
}
// Handle project save
if (isset($_POST['save'])) {
$values = [
'name' => $_POST['name'],
'details' => $_POST['details'],
'clientId' => $_POST['clientId'],
'state' => $_POST['projectState'],
'hourBudget' => $_POST['hourBudget'],
'dollarBudget' => $_POST['dollarBudget'],
'psettings' => $_POST['globalProjectUserAccess'],
'menuType' => $_POST['menuType'],
'type' => $_POST['type'] ?? $project['type'],
'parent' => $_POST['parent'] ?? '',
'start' => isset($_POST['start']) && dtHelper()->isValidDateString($_POST['start']) ? dtHelper()->parseUserDateTime($_POST['start'])->formatDateTimeForDb() : '',
'end' => isset($_POST['end']) && dtHelper()->isValidDateString($_POST['end']) ? dtHelper()->parseUserDateTime($_POST['end'])->formatDateTimeForDb() : '',
];
if ($values['name'] !== '') {
if ($this->projectService->hasTickets($id) && $values['state'] == 1) {
$this->tpl->setNotification($this->language->__('notification.project_has_tickets'), 'error');
} else {
$this->projectService->editProjectAndNotify(
$values,
$id,
$project,
CURRENT_URL,
session('userdata.id'),
session('userdata.name')
);
$this->tpl->setNotification($this->language->__('notification.project_saved'), 'success');
return Frontcontroller::redirect(BASE_URL.'/projects/showProject/'.$id);
}
} else {
$this->tpl->setNotification($this->language->__('notification.no_project_name'), 'error');
}
}
// Handle master data linking (关联全局主数据到项目)
if (isset($_POST['linkMaster']) && ! empty($_POST['masterId'])) {
$masterId = (int) $_POST['masterId'];
if ($this->masterRefService->link($id, $masterId) > 0) {
$this->tpl->setNotification($this->language->__('notification.master_linked', '主数据已关联到项目'), 'success');
} else {
$this->tpl->setNotification($this->language->__('notification.master_link_failed', '关联失败'), 'error');
}
}
session(['lastPage' => BASE_URL.'/projects/showProject/'.$id]);
$project['assignedUsers'] = $this->projectService->getUsersAssignedToProject($id, true);
$this->assignIntegrationSettings($id);
$this->assignTemplateVars($id, $project);
$this->assignMasterData($id);
return $this->tpl->display('projects.showProject');
}
/**
* Loads and assigns integration settings to the template.
*/
private function assignIntegrationSettings(int $projectId): void
{
$settings = $this->projectService->getProjectIntegrationSettings($projectId);
array_map([$this->tpl, 'assign'], array_keys($settings), array_values($settings));
}
/**
* Assigns common template variables.
*/
private function assignTemplateVars(int $projectId, array $project): void
{
$this->tpl->assign('availableUsers', $this->projectService->getAllUsers());
$this->tpl->assign('clients', $this->clientService->getAll());
$this->tpl->assign('todoStatus', $this->ticketService->getStatusLabels());
$this->tpl->assign('employees', $this->projectService->getEmployees());
$this->tpl->assign('project', $project);
$this->tpl->assign('menuTypes', $this->projectService->getMenuTypes());
$this->tpl->assign('projectTypes', $this->projectService->getProjectTypes());
$this->tpl->assign('state', ['open', 'closed']);
$this->tpl->assign('role', session('userdata.role'));
$this->tpl->assign('projectMuteCount', $this->projectService->getMuteCountForProject($projectId));
}
/**
* 加载项目已引用的主数据 + 可关联的全局主数据列表供「主数据」tab 使用。
*/
private function assignMasterData(int $projectId): void
{
$typeNames = ['bom' => 'BOM', 'process' => '工艺文件', 'tooling' => '工具清单'];
// 已引用
$refs = $this->masterRefService->getRefs($projectId);
$linked = [];
foreach ($refs as $ref) {
$master = $this->bomService->getBom((int) $ref['masterId']);
if ($master !== false) {
$ref['master'] = $master;
$linked[] = $ref;
}
}
// 可关联的全局主数据(排除已关联)
$linkedMasterIds = array_map(fn ($r) => (int) $r['masterId'], $refs);
$available = [];
foreach (['bom', 'process', 'tooling'] as $type) {
foreach ($this->bomService->getMasters($type, 0) as $m) {
if (in_array((int) $m['id'], $linkedMasterIds, true)) {
continue;
}
$m['typeName'] = $typeNames[$m['type'] ?? 'bom'] ?? 'BOM';
$available[] = $m;
}
}
$this->tpl->assign('linkedMasters', $linked);
$this->tpl->assign('availableMasters', $available);
}
}