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); $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'); $loginPicture = \GlpiPlugin\Butterfly\Config::getConfig('login_picture');
if (!$loginPicture && $themeInfo && !empty($themeInfo['login-logo'])) { if (!$loginPicture && $themeInfo && !empty($themeInfo['login-logo'])) {
$loginPicture = $themeInfo['login-logo'] . '&theme=' . $themeInfo['id']; $loginPicture = $themeInfo['login-logo'] . '&theme=' . $themeInfo['id'];
} }
}
$loginCss = \GlpiPlugin\Butterfly\Scss::getLoginCSS($requestedTheme); $loginCss = \GlpiPlugin\Butterfly\Scss::getLoginCSS($requestedTheme);
if (!empty($loginCss)) { 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 if ($loginPicture) : ?>
<?php <?php
$pictureUrl = \GlpiPlugin\Butterfly\Toolbox::getPictureUrl($loginPicture); $pictureUrl = \GlpiPlugin\Butterfly\Toolbox::getPictureUrl($loginPicture);

Binary file not shown.

View file

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