Novo recurso: controle da logo do login (custom/original/ocultar) + caixa de CSS grande

- Dropdown 'Logo' na seção Login Page: custom (imagem enviada ou logo do
  tema — comportamento legado, default), original (força logo GLPI) e
  hide (oculta via CSS injetado no display_login).
- Campos de CSS custom agora são textarea de 14 linhas sempre visível
  (monospace, 100%); o Monaco só assume se window.GLPI.Monaco existir
  (melhoria progressiva — antes o fallback dependia de JS e o campo
  podia ficar escondido/curto).
- Traduções pt_BR novas + pt_BR.mo recompilado (msgfmt indisponível;
  compilado com script Python puro).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Claude 2026-07-03 18:25:28 -03:00
parent c6f6c405f6
commit 020558f14f
4 changed files with 59 additions and 21 deletions

View file

@ -17,11 +17,17 @@ function plugin_butterfly_display_login() {
$themeInfo = \GlpiPlugin\Butterfly\Theme::getThemeInfo($theme);
}
// Logo do login: custom (imagem enviada ou do tema) / original / hide
$logoDisplay = \GlpiPlugin\Butterfly\Config::getConfig('login_logo_display', 'custom');
$loginPicture = null;
if ($logoDisplay === 'custom') {
$loginPicture = \GlpiPlugin\Butterfly\Config::getConfig('login_picture');
if (!$loginPicture && $themeInfo && !empty($themeInfo['login-logo'])) {
$loginPicture = $themeInfo['login-logo'] . '&theme=' . $themeInfo['id'];
}
}
$loginCss = \GlpiPlugin\Butterfly\Scss::getLoginCSS($requestedTheme);
if (!empty($loginCss)) {
@ -29,6 +35,14 @@ function plugin_butterfly_display_login() {
}
?>
<?php if ($logoDisplay === 'hide') : ?>
<style>
.page-anonymous .glpi-logo,
.login-header img, .glpi-login__logo img, .login-logo img {
display: none !important;
}
</style>
<?php endif; ?>
<?php if ($loginPicture) : ?>
<?php
$pictureUrl = \GlpiPlugin\Butterfly\Toolbox::getPictureUrl($loginPicture);

Binary file not shown.

View file

@ -80,3 +80,11 @@ msgstr "Tema"
#: inc/config.class.php:540
msgid "Background picture"
msgstr "Imagem de fundo"
#: src/Config.php
msgid "Logo"
msgstr "Logo"
#: src/Config.php
msgid "Custom (uploaded picture or theme logo)"
msgstr "Personalizada (imagem enviada ou logo do tema)"

View file

@ -161,6 +161,7 @@ class Config extends \CommonDBTM {
'page_title' => '',
'page_footer_display' => 'original',
'page_footer_text' => '',
'login_logo_display' => 'custom',
'login_picture' => '',
'login_picture_max_width' => '240px',
'login_picture_max_height' => '130px',
@ -237,45 +238,49 @@ class Config extends \CommonDBTM {
echo "<td colspan='4' style='max-width: 1000px'>";
$rand = mt_rand();
$textareaId = $fullName . '_' . $rand;
$containerId = $fullName . '_editor_' . $rand;
$value = rtrim($this->fields[$fullName]);
// Editor Monaco do core (GLPI 11) com fallback: o textarea escondido
// mantém o name/valor e é sincronizado no submit via evento formdata
// Caixa de texto longa sempre visível; o Monaco do core (GLPI 11) só
// assume se a lib estiver carregada — aí esconde o textarea e passa a
// sincronizar o valor no submit via evento formdata
echo sprintf(
'<textarea %1$s>',
\Html::parseAttributes([
'id' => $fullName . '_' . $rand,
'id' => $textareaId,
'name' => $fullName,
'class' => 'butterfly-css-editor d-none',
'class' => 'butterfly-css-editor',
'rows' => 14,
'spellcheck' => 'false',
])
);
echo $value;
echo '</textarea>';
echo '<div id="' . $containerId . '" style="height: 300px"></div>';
echo '<div id="' . $containerId . '" class="d-none" style="height: 300px; border: 1px solid #dcdcdc"></div>';
echo \Html::scriptBlock('
$(function() {
if (window.GLPI === undefined || window.GLPI.Monaco === undefined) {
$("#' . $fullName . '_' . $rand . '").removeClass("d-none").attr({rows: 15, spellcheck: false});
$("#' . $containerId . '").remove();
return;
return; // sem Monaco nesta página — fica o textarea
}
window.GLPI.Monaco.createEditor(
' . json_encode($containerId, JSON_HEX_TAG | JSON_HEX_AMP) . ',
"scss",
' . json_encode($value, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP) . ',
[]
).then(() => {
$("#' . $containerId . '").closest("form").on("formdata", (e) => {
var taId = ' . json_encode($textareaId, JSON_HEX_TAG | JSON_HEX_AMP) . ';
var containerId = ' . json_encode($containerId, JSON_HEX_TAG | JSON_HEX_AMP) . ';
var fieldName = ' . json_encode($fullName, JSON_HEX_TAG | JSON_HEX_AMP) . ';
window.GLPI.Monaco.createEditor(containerId, "scss", $("#" + taId).val(), []).then(() => {
$("#" + containerId).removeClass("d-none");
$("#" + taId).addClass("d-none").prop("disabled", true);
$("#" + containerId).closest("form").on("formdata", (e) => {
const editors = window.monaco.editor.getEditors().filter(
(editor) => editor._domElement.id === ' . json_encode($containerId, JSON_HEX_TAG | JSON_HEX_AMP) . '
(editor) => editor._domElement.id === containerId
);
if (editors.length) {
e.originalEvent.formData.delete(' . json_encode($fullName, JSON_HEX_TAG | JSON_HEX_AMP) . ');
e.originalEvent.formData.append(' . json_encode($fullName, JSON_HEX_TAG | JSON_HEX_AMP) . ', editors[0].getValue());
e.originalEvent.formData.delete(fieldName);
e.originalEvent.formData.append(fieldName, editors[0].getValue());
}
});
}).catch(() => {
// editor falhou — mantém o textarea funcional
});
});
');
@ -534,6 +539,17 @@ class Config extends \CommonDBTM {
}
}
echo "<tr class='tab_bg_1'>";
echo "<td>" . t_butterfly('Logo') . "</td>";
echo "<td colspan='3'>";
\Dropdown::showFromArray('login_logo_display', [
'custom' => t_butterfly('Custom (uploaded picture or theme logo)'),
'original' => __('Original'),
'hide' => t_butterfly('Hide'),
], ['value' => $this->fields['login_logo_display']]);
echo "</td>";
echo "</tr>\n";
echo "<tr class='tab_bg_1'>";
echo "<td>" . __('Picture') . "</td>";
$this->buildPictureLine('login_picture', '145px x 80px');