getFromDB($projects_id) || !$project->can($projects_id, UPDATE)) { Html::displayRightError(); } if ($action === 'sync') { // Full metadata sync (partial sync used to wipe trackers/statuses from the cache). $metadata = PluginRedmineRedmineapi::syncMetadata(); if ($metadata !== false) { global $DB; $DB->update('glpi_plugin_redmine_configs', [ 'metadata_cache' => json_encode($metadata) ], [ 'id' => 1 ]); PluginRedmineRedmineapi::syncUsers(); Session::addMessageAfterRedirect("Metadados sincronizados com sucesso!"); } else { Session::addMessageAfterRedirect("Erro ao sincronizar com Redmine.", false, ERROR); } } elseif ($action === 'set_category') { global $DB; $DB->update('glpi_plugin_redmine_projects', [ 'default_category_id' => (int) ($_POST['default_category_id'] ?? 0) ], [ 'projects_id' => $projects_id ]); Session::addMessageAfterRedirect("Categoria padrão salva com sucesso!"); } elseif ($action === 'unlink') { global $DB; $DB->delete('glpi_plugin_redmine_projects', ['projects_id' => $projects_id]); Session::addMessageAfterRedirect("Projeto desvinculado do Redmine."); } elseif ($action === 'process') { $redmine_project_id = $_POST['redmine_project_id'] ?? ''; if (!empty($redmine_project_id)) { // LINK OPERATION global $DB; $DB->insert('glpi_plugin_redmine_projects', [ 'projects_id' => $projects_id, 'redmine_project_id' => $redmine_project_id ]); Session::addMessageAfterRedirect("Projeto vinculado com sucesso!"); } else { // CREATE OPERATION $new_redmine_id = false; // Motor 2.0: template ativo tem precedência; erro de RENDER cai no 1.x. $tpl = PluginRedmineTemplateengine::getActive('create_project'); if ($tpl) { try { $ctx = PluginRedmineTemplateengine::buildFormContext($_POST); $rendered = PluginRedmineTemplateengine::render($tpl['template'], $ctx); if (!empty($rendered['project'])) { $new_redmine_id = PluginRedmineRedmineapi::createProjectFull($rendered['project']); } } catch (\Throwable $e) { Toolbox::logInFile('redmine', "Template create_project falhou no render (" . $e->getMessage() . ") — usando motor 1.x.\n"); $tpl = null; } } if (!$tpl) { $payload = [ 'name' => $_POST['name'], 'identifier' => $_POST['identifier'], 'description' => $_POST['description'], 'is_public' => !empty($_POST['is_public']) ]; if (!empty($_POST['parent_id'])) { $payload['parent_id'] = $_POST['parent_id']; } if (!empty($_POST['enabled_module_names'])) { $payload['enabled_module_names'] = $_POST['enabled_module_names']; } // Trackers do projeto: sem eles o Redmine recusa criar issues. if (!empty($_POST['tracker_ids']) && is_array($_POST['tracker_ids'])) { $tids = array_values(array_filter(array_map('intval', $_POST['tracker_ids']))); if (!empty($tids)) { $payload['tracker_ids'] = $tids; } } if (empty($payload['tracker_ids'])) { $cfg = PluginRedmineConfig::getConfigRow(); if (!empty($cfg['default_tracker_id'])) { $payload['tracker_ids'] = [(int) $cfg['default_tracker_id']]; } } // Custom fields de projeto (obrigatórios em alguns Redmine). // O empty-choice do dropdown envia '0' — descartar junto com vazios. if (!empty($_POST['custom_field_values']) && is_array($_POST['custom_field_values'])) { $cfv = []; foreach ($_POST['custom_field_values'] as $cfid => $cfval) { if ($cfval !== '' && $cfval !== null && $cfval !== '0' && $cfval !== 0) { $cfv[(string) (int) $cfid] = $cfval; } } if (!empty($cfv)) { $payload['custom_field_values'] = $cfv; } } $new_redmine_id = PluginRedmineRedmineapi::createProjectFull($payload); } // fim do motor 1.x if ($new_redmine_id) { global $DB; $DB->insert('glpi_plugin_redmine_projects', [ 'projects_id' => $projects_id, 'redmine_project_id' => $new_redmine_id ]); Session::addMessageAfterRedirect("Projeto criado e vinculado com sucesso!"); } else { Session::addMessageAfterRedirect("Erro ao criar projeto no Redmine.", false, ERROR); } } } Html::redirect($CFG_GLPI["root_doc"] . "/front/project.form.php?id=" . $projects_id); } Html::displayErrorAndDie("Lost");