diff --git a/front/config.form.php b/front/config.form.php index d5d7494..e98c17e 100644 --- a/front/config.form.php +++ b/front/config.form.php @@ -10,6 +10,30 @@ if (isset($_POST["update"]) || isset($_POST["sync"])) { // Save whatever was typed (URL/key/defaults) before anything else. $config->update($_POST); + // Templates de payload: salvos junto pelo botão Salvar único. + if (!empty($_POST['templates_json']) && is_array($_POST['templates_json'])) { + foreach ($_POST['templates_json'] as $op => $json) { + if (!in_array($op, PluginRedmineTemplateengine::OPERATIONS, true)) { + continue; + } + $json = (string) $json; + // Defensivo contra escaping de aspas na camada de entrada. + if ($json !== '' && json_decode($json, true) === null && json_decode(stripslashes($json), true) !== null) { + $json = stripslashes($json); + } + if ($json !== '' && json_decode($json, true) === null) { + Session::addMessageAfterRedirect( + sprintf(__('Template "%s" não é JSON válido — não foi salvo.', 'redmine'), $op), + false, + ERROR + ); + continue; + } + $active = !empty($_POST['templates_active'][$op]) && $json !== ''; + PluginRedmineTemplateengine::save($op, $json, $active); + } + } + if (isset($_POST["sync"])) { $metadata = PluginRedmineRedmineapi::syncMetadata(); if ($metadata !== false) { @@ -26,43 +50,6 @@ if (isset($_POST["update"]) || isset($_POST["sync"])) { Html::back(); } -// Save a payload template (motor 2.0). -if (isset($_POST['save_template'])) { - $op = (string) ($_POST['template_op'] ?? ''); - if (in_array($op, PluginRedmineTemplateengine::OPERATIONS, true)) { - $json = (string) ($_POST['template_json'] ?? ''); - // Defensivo contra escaping de aspas na camada de entrada. - if ($json !== '' && json_decode($json, true) === null && json_decode(stripslashes($json), true) !== null) { - $json = stripslashes($json); - } - if ($json !== '' && json_decode($json, true) === null) { - Session::addMessageAfterRedirect(__('Template não é JSON válido — nada foi salvo.', 'redmine'), false, ERROR); - } else { - PluginRedmineTemplateengine::save($op, $json, !empty($_POST['template_active']) && $json !== ''); - Session::addMessageAfterRedirect(__('Template salvo.', 'redmine')); - } - } - Html::back(); -} - -// Generate the seed template for an operation (comportamento 1.x + custom fields descobertos). -if (isset($_POST['gen_template'])) { - $op = (string) ($_POST['template_op'] ?? ''); - if (in_array($op, PluginRedmineTemplateengine::OPERATIONS, true)) { - $seed = PluginRedmineTemplateengine::mergeDiscoveredCustomFields( - $op, - PluginRedmineTemplateengine::defaultTemplate($op) - ); - PluginRedmineTemplateengine::save( - $op, - json_encode($seed, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), - false - ); - Session::addMessageAfterRedirect(__('Template gerado (inativo). Revise os campos e ative quando estiver pronto.', 'redmine')); - } - Html::back(); -} - // Add a manual user mapping override (GLPI user -> Redmine user). if (isset($_POST["add_usermap"])) { $uid = (int) ($_POST['override_users_id'] ?? 0); diff --git a/inc/config.class.php b/inc/config.class.php index dc25c9b..c27cf1b 100644 --- a/inc/config.class.php +++ b/inc/config.class.php @@ -206,10 +206,16 @@ class PluginRedmineConfig extends CommonDBTM { $templates = []; foreach (PluginRedmineTemplateengine::OPERATIONS as $op) { $row = PluginRedmineTemplateengine::getRow($op); + $seed = PluginRedmineTemplateengine::mergeDiscoveredCustomFields( + $op, + PluginRedmineTemplateengine::defaultTemplate($op) + ); $templates[$op] = [ 'label' => $tpl_labels[$op] ?? $op, 'template' => (string) ($row['template'] ?? ''), 'is_active' => (int) ($row['is_active'] ?? 0), + // seed pro botão "Gerar template" (ação local no browser) + 'seed' => json_encode($seed, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ]; } diff --git a/templates/config_form.html.twig b/templates/config_form.html.twig index 74ed33c..1352a8d 100644 --- a/templates/config_form.html.twig +++ b/templates/config_form.html.twig @@ -78,6 +78,115 @@ +{# ==================================================================== + Templates de Payload (motor 2.0) — editor linha-a-linha + paleta + ==================================================================== #} +
+
+

{{ __('Templates de Payload (avançado)', 'redmine') }}

+
+
+
+ {{ __('Personalize o JSON enviado ao Redmine em cada operação. Sem template ativo, o plugin usa o comportamento padrão. "Gerar template" cria o ponto de partida equivalente ao padrão + campos obrigatórios descobertos no seu Redmine.', 'redmine') }} +
+ + {# Variáveis do GLPI como AUTOCOMPLETE nos campos de valor #} + + {% for v in palette_vars %} + + {% endfor %} + + + {# Referência opcional (colapsada) — descoberta das tags para remapeamentos #} +
+ + {{ __('Variáveis do GLPI disponíveis — o "DE" (referência; nos campos de valor há autocomplete)', 'redmine') }} + +
+
+ {% set lastgroup = '' %} + {% for v in palette_vars %} + {% if v.group != lastgroup %} + {% set lastgroup = v.group %} +
{{ v.group }}
+ {% endif %} +
+ {{ v.tag }} +
+ {% endfor %} +
+
+
+ + {# ---- Editores por operação (largura total) ---- #} +
+ {% for op, t in templates %} +
+
+ + {{ t.label }} + {{ op }} + {% if t.is_active %} + {{ __('ativo', 'redmine') }} + {% else %} + {{ __('inativo (usa o padrão)', 'redmine') }} + {% endif %} + + + + + +
+ {% if field_dicts[op] is defined and field_dicts[op] is not empty %} +
+ + {{ __('Campos que o Redmine espera — o "PARA" (clique num campo para adicioná-lo)', 'redmine') }} + +
+ + + {% for f in field_dicts[op] %} + + + + + {% endfor %} + +
+ {{ f.path }} + {% if f.required %}{{ __('obrigatório', 'redmine') }}{% endif %} + + {{ f.desc }} + {% if f.values is not empty %} +
{{ f.values|join(' · ') }}
+ {% endif %} +
+
+
+ {% endif %} +
+
+ + +
+ +
+ {% endfor %} +
+
+
+
-{# ==================================================================== - Templates de Payload (motor 2.0) — editor linha-a-linha + paleta - ==================================================================== #} -
-
-

{{ __('Templates de Payload (avançado)', 'redmine') }}

-
-
-
- {{ __('Personalize o JSON enviado ao Redmine em cada operação. Sem template ativo, o plugin usa o comportamento padrão. "Gerar template" cria o ponto de partida equivalente ao padrão + campos obrigatórios descobertos no seu Redmine.', 'redmine') }} -
- - {# Variáveis do GLPI como AUTOCOMPLETE nos campos de valor #} - - {% for v in palette_vars %} - - {% endfor %} - - - {# Referência opcional (colapsada) — descoberta das tags para remapeamentos #} -
- - {{ __('Variáveis do GLPI disponíveis — o "DE" (referência; nos campos de valor há autocomplete)', 'redmine') }} - -
-
- {% set lastgroup = '' %} - {% for v in palette_vars %} - {% if v.group != lastgroup %} - {% set lastgroup = v.group %} -
{{ v.group }}
- {% endif %} -
- {{ v.tag }} -
- {% endfor %} -
-
-
- - {# ---- Editores por operação (largura total) ---- #} -
- {% for op, t in templates %} -
- - -
-
- - {{ t.label }} - {{ op }} - {% if t.is_active %} - {{ __('ativo', 'redmine') }} - {% else %} - {{ __('inativo (usa o padrão)', 'redmine') }} - {% endif %} - - - - - -
- {% if field_dicts[op] is defined and field_dicts[op] is not empty %} -
- - {{ __('Campos que o Redmine espera — o "PARA" (clique num campo para adicioná-lo)', 'redmine') }} - -
- - - {% for f in field_dicts[op] %} - - - - - {% endfor %} - -
- {{ f.path }} - {% if f.required %}{{ __('obrigatório', 'redmine') }}{% endif %} - - {{ f.desc }} - {% if f.values is not empty %} -
{{ f.values|join(' · ') }}
- {% endif %} -
-
-
- {% endif %} -
-
- - -
- -
-
- {% endfor %} -
-
-
- {% verbatim %}