Trocar o logótipo do GLPI parece um ajuste de cinco minutos - até a próxima atualização apagar tudo. Personalizar a identidade visual editando o CSS do core ou o template de início de sessão funciona no dia, mas é uma dívida que vence sozinha: desaparece na atualização e, no salto do GLPI 10 para o 11, o DOM do ecrã de início de sessão mudou de nome, por isso o hack antigo deixa de aplicar sem lançar qualquer erro. Este guia mostra como fazer branding que sobrevive às atualizações, onde vive cada peça no GLPI 11 e as armadilhas que só aparecem em ambiente de cliente.
Branding no GLPI não é só estética
Um GLPI com a cara da organização reduz o atrito: o utilizador final reconhece que aquilo é o Service Desk da empresa, e não um sistema estranho a pedir credenciais. Mas o custo escondido não está em escolher a cor certa - está na forma como a personalização é aplicada. Há três caminhos, e dois deles cobram caro depois.
- Editar o core (CSS, imagens ou templates dentro de
glpi/css,glpi/picsoutemplates/): funciona, mas o próximogit pullou a extração do release sobrescreve os ficheiros. A personalização evapora-se e ninguém se lembra porquê. - Tema/CSS injetado por fora: sobrevive à atualização dos ficheiros, mas fica preso aos seletores dessa versão. Quando o GLPI muda a estrutura HTML - como fez do 10 para o 11 - o seletor deixa de existir e o CSS torna-se letra morta, silenciosamente.
- Hook de plugin: o GLPI expõe pontos de injeção oficiais (
display_login,add_css,add_javascript). O módulo aplica a marca por esses hooks, sem tocar num único ficheiro do core. É o único caminho que sobrevive às atualizações por conceção.
O que se pode personalizar - e onde vive cada peça
Cada elemento visual do GLPI vive num sítio diferente do DOM, e trocá-lo sem saber o alvo certo é o que gera a maioria dos pedidos de "mudei e não apareceu". A matriz abaixo mapeia cada peça à sua chave de configuração e ao mecanismo real no GLPI 11:
| Peça | Chave de config | Mecanismo no GLPI 11 | Armadilha comum |
|---|---|---|---|
| Favicon | favicon_picture | <link rel="icon"> reescrito via JS | A cache agressiva do navegador retém o ícone antigo |
| Título do separador | page_title | document.title | Ecrãs que definem o próprio título revertem-no |
| Logótipo do login | login_picture | span.glpi-logo via content: url(...) | Apontar a .login-logo, que não existe no 11 |
| Fundo do login | login_background | Só body.welcome-anonymous (o .page-anonymous fica transparente) | Apontar a body.login-page, que não existe no 11 |
| Logótipo do cabeçalho | internal_picture | CSS no <head> via add_css | Confundi-lo com o logótipo de login (alvos distintos) |
| Rodapé | footer_mode / footer_text | .copyright (tema predefinido do 11) | Colar HTML não sanitizado no texto personalizado |
| Cores da interface | color_* | Variáveis CSS Tabler (--tblr-*) | Passar um valor que não é hex e injetar CSS partido |
A armadilha do GLPI 11: o logótipo de login não é fundo
Na sustentação de ambientes de clientes, o pedido clássico é: "atualizámos o GLPI e o ecrã de início de sessão voltou ao predefinido". A causa é quase sempre uma personalização feita diretamente no core ou num CSS que apontava aos seletores do GLPI 10. No salto para o 11, o template do ecrã de início de sessão (page_card_notlogged.html.twig) foi reescrito: o <body> passou a body.welcome-anonymous, o contentor passou a .page-anonymous e o logótipo deixou de ser uma imagem de fundo - agora é um <span class="glpi-logo"> renderizado por content: var(--logo). Descobrimos isto na prática a 2026-06-10, a depurar porque o fundo de login de um cliente simplesmente não aplicava após o upgrade: o seletor antigo body.login-page já não existia no DOM. Não havia erro no log - o CSS estava correto para uma versão que já não corria. Por isso passámos a emitir os seletores certos por hook, versionados junto do módulo. Este é o CSS real que o módulo injeta - e os seletores mortos que explicam o desaparecimento:
/* GLPI 11 - seletores REAIS do ecra de inicio de sessao (page_card_notlogged.html.twig) */
/* O logotipo NAO e fundo: e um <span class="glpi-logo"> com content: var(--logo) */
.page-anonymous .glpi-logo {
content: url("/plugins/nextool/ajax/module_ajax.php?module=branding&file=branding.picture.send.php&path=...") !important;
width: 240px !important;
height: 130px !important;
object-fit: contain !important; /* preserva a proporcao da imagem */
}
/* O fundo vai SO no body.welcome-anonymous - NUNCA em body.login-page.
Pintar tambem o .page-anonymous (filho direto) duplicava a arte nas margens. */
body.welcome-anonymous {
background-image: url("...") !important;
background-size: cover !important; /* ou contain, ou 100% 100% (esticar) */
background-position: center center !important;
background-repeat: no-repeat !important;
background-attachment: fixed !important; /* mede pela janela, nao pela pagina */
}
.page-anonymous { background-image: none !important; background-color: transparent !important; }
/* Seletores do GLPI 10 que JA NAO existem no 11 (por isso o hack antigo desaparece sem erro):
.login-logo -> agora e span.glpi-logo
body.login-page -> agora e body.welcome-anonymous */
Como o módulo aplica sem tocar no core
No ecrã de início de sessão não há sessão, por isso o módulo pendura-se no hook display_login e injeta um bloco <style>/<script> em linha. As imagens (favicon, logótipo, fundo) são servidas por um endpoint stateless que não exige início de sessão, com três proteções que vale a pena nomear porque são precisamente onde plugins descuidados deixam fugir dados: a extensão passa por uma whitelist (png, jpg, jpeg, gif, ico, webp; o SVG saiu na versão 1.4.0) e o tipo real do ficheiro é verificado pelo conteúdo, o caminho é resolvido com realpath() e verificado contra o prefixo da pasta de carregamento (bloqueio de path traversal), e o ficheiro é gravado com um nome aleatório gerado pelo servidor, sem aproveitar o nome enviado, para não colidir nem sobrescrever. Nas páginas internas, já autenticadas, as cores, o logótipo do cabeçalho e o rodapé oculto entram como CSS no <head> pelo hook add_css, e o favicon, o título e o rodapé personalizado entram pelo hook add_javascript.
Um detalhe de desempenho que só aparece a medir: o JS interno vai com Cache-Control: private, max-age=86400 e as imagens com public, max-age=31536000, immutable (um ano: cada carregamento gera um ficheiro com nome novo), mas sem ETag. A razão é concreta - com ETag e max-age juntos, o Chrome revalidava (HTTP 304) em cada carregamento de página, e cada 304 ainda pagava o bootstrap completo do GLPI no servidor. Um hash da configuração entra no parâmetro fv= do URL, por isso guardar a config muda o URL e invalida a cache de imediato, sem revalidação.
Configuração em cinco passos
- Ative o módulo Branding no catálogo de módulos do NexTool.
- No separador Configurações, carregue o favicon, o logótipo de login e o logótipo do cabeçalho; defina a largura e a altura de cada logótipo (aceita
px,em,rem,%,vw,vh). - Carregue o fundo do ecrã de início de sessão e ajuste o título do separador do navegador.
- Escolha o comportamento do rodapé: manter o original, ocultar ou substituir por texto (HTML limitado a etiquetas seguras).
- Opcional: defina as cores da interface por variáveis CSS (o módulo valida cada valor como hex antes de emitir, evitando CSS injection). Recarregue o GLPI para ver o resultado.
Auditoria: quem mudou o quê
Toda a ação - ativar, desativar, guardar config, carregar ou apagar imagem - é registada numa tabela de log do módulo. Num ambiente com mais do que um administrador, esse rasto responde a "quem trocou o logótipo na sexta à noite" sem depender da memória. A consulta de diagnóstico:
-- Rasto de auditoria do modulo Branding: quem mudou a identidade visual e quando
SELECT l.date_creation AS quando,
u.name AS utilizador,
l.action AS acao, -- save_config (carregamento e eliminacao de imagem vao no detalhe), toggle_enable/disable
l.detail AS detalhe
FROM glpi_plugin_nextool_branding_log l
LEFT JOIN glpi_users u ON u.id = l.users_id
ORDER BY l.date_creation DESC
LIMIT 20;
Armadilhas de campo
- Favicon que não muda - o navegador guarda o favicon em cache de forma agressiva, por separador e por sessão. Depois de carregar o novo ícone, um hard refresh (Ctrl+Shift+R) ou fechar e reabrir o separador resolve; não é bug do módulo.
- O SVG não é aceite - desde a versão 1.4.0 o módulo recusa SVG no logótipo e no favicon, porque o formato pode transportar script embutido e as imagens do módulo são servidas sem início de sessão. Use PNG com fundo transparente (ou WEBP) no logótipo e PNG ou ICO no favicon; o servidor verifica o conteúdo real do ficheiro, por isso um SVG renomeado para .png também é recusado.
- Rodapé com HTML colado - o texto personalizado do rodapé passa por sanitização que só permite um punhado de etiquetas (
a, b, strong, i, em, span, br). Colar um bloco inteiro de HTML de marketing simplesmente não renderiza o que espera. - Proporção do logótipo - definir largura e altura fixas sem respeitar a proporção original distorce a imagem. O módulo aplica
object-fit: containjustamente para não esticar, mas convém confirmar no ecrã. - Upgrade do 10 para o 11 - se o cliente já tinha branding manual no GLPI 10, ele vai desaparecer no 11 pelos seletores trocados. Trate o branding como parte do plano de upgrade, não como surpresa pós-migração.
Quer padronizar a identidade visual do GLPI em todos os ambientes que administra, sem reabrir o assunto a cada atualização? O módulo Branding é gratuito e aplica a marca por hooks nativos. E, se preferir externalizar a implementação e a sustentação do GLPI, a NexTool mantém ambientes de clientes com branding versionado e à prova de atualização.
Revisto pela equipa NexTool Solutions.