- seção "Templates de Payload (avançado)" no formconfig: card por operação, editor linha-a-linha (flatten/unflatten em JS puro) com toggle Ver JSON, Gerar template (seed + CFs descobertos), ativar/desativar - paleta de variáveis clicável (copia tag) + referência do Redmine do ambiente (trackers/status/prioridades/atividades/custom fields com valores) - buildFormContext (form.*) e wiring do create_project nos dois controllers, com fallback 1.x em erro de render - validado: render da UI (4 cards + paleta + JS) e E2E create_project via template no Redmine dev (tipos nativos, identifier minúsculo, parent, trackers) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
139 lines
5.5 KiB
PHP
139 lines
5.5 KiB
PHP
<?php
|
|
include ("../../../inc/includes.php");
|
|
|
|
Session::checkLoginUser();
|
|
|
|
if (isset($_POST["action"])) {
|
|
$action = $_POST["action"];
|
|
$projects_id = $_POST["projects_id"];
|
|
|
|
// Permission check
|
|
$project = new Project();
|
|
if (!$project->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");
|