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); } }