Как настроить фирменный стиль GLPI с помощью модуля Branding

Брендинг в GLPI, который переживает обновление: почему правка ядра стирает фирменный стиль при обновлении, реальный DOM страницы входа в GLPI 11 (span.glpi-logo, body.welcome-anonymous), CSS, который модуль внедряет через хук, SQL для аудита и подводные камни на практике.

В этой статьеРазделов: 7

Сменить логотип GLPI кажется делом пяти минут — пока следующее обновление всё не сотрёт. Настройка фирменного стиля через правку CSS ядра или шаблона входа работает в тот же день, но это долг, который созревает сам по себе: он исчезает при обновлении, а при переходе с GLPI 10 на 11 элементы DOM страницы входа получили новые имена, поэтому старый хак перестаёт применяться, не выдавая никакой ошибки. В этом руководстве показано, как сделать брендинг, который переживает обновление, где находится каждый элемент в GLPI 11 и какие подводные камни проявляются только в средах клиентов.

Брендинг в GLPI — это не только эстетика

GLPI с обликом организации снижает трение: конечный пользователь узнаёт, что перед ним Service Desk компании, а не незнакомая система, требующая входа. Но скрытая цена не в выборе правильного цвета — а в том, как применяется настройка. Есть три пути, и два из них потом обходятся дорого.

  • Правка ядра (CSS, изображения или шаблоны внутри glpi/css, glpi/pics или templates/): работает, но следующий git pull или распаковка релиза перезаписывает файлы. Настройка испаряется, и никто не помнит почему.
  • Тема/CSS, внедрённые извне: переживают обновление файлов, но привязаны к селекторам версии. Когда GLPI меняет структуру HTML — как это произошло при переходе с 10 на 11, — селектор перестаёт существовать, и CSS тихо превращается в мёртвую букву.
  • Хук плагина: GLPI предоставляет официальные точки внедрения (display_login, add_css, add_javascript). Модуль применяет фирменный стиль через эти хуки, не трогая ни одного файла ядра. Это единственный путь, который переживает обновление по своей конструкции.

Что можно настроить — и где находится каждый элемент

Каждый визуальный элемент GLPI находится в своём месте DOM, и замена без знания правильной цели порождает большинство заявок вида «я поменял, а ничего не появилось». Матрица ниже сопоставляет каждый элемент с его ключом конфигурации и реальным механизмом в GLPI 11:

ЭлементКлюч конфигурацииМеханизм в GLPI 11Типичная ловушка
Faviconfavicon_picture<link rel="icon">, переписываемый через JSАгрессивный кеш браузера удерживает старый значок
Заголовок вкладкиpage_titledocument.titleСтраницы, задающие собственный заголовок, возвращают его
Логотип входаlogin_picturespan.glpi-logo через content: url(...)Нацеливание на .login-logo, которого в 11 нет
Фон входаlogin_backgroundТолько body.welcome-anonymous (.page-anonymous остаётся прозрачным)Нацеливание на body.login-page, которого в 11 нет
Логотип заголовкаinternal_pictureCSS в <head> через add_cssПутаница с логотипом входа (это разные цели)
Нижний колонтитулfooter_mode / footer_text.copyright (стандартная тема 11)Вставка несанированного HTML в пользовательский текст
Цвета интерфейсаcolor_*CSS-переменные Tabler (--tblr-*)Передача значения не в hex и внедрение сломанного CSS

Ловушка GLPI 11: логотип входа — это не фон

При сопровождении сред клиентов классическая заявка звучит так: «мы обновили GLPI, и страница входа вернулась к стандартному виду». Почти всегда причина — настройка, сделанная прямо в ядре или в CSS, нацеленном на селекторы GLPI 10. При переходе на 11 шаблон страницы входа (page_card_notlogged.html.twig) был переписан: <body> стал body.welcome-anonymous, контейнер — .page-anonymous, а логотип перестал быть фоновым изображением — теперь это <span class="glpi-logo">, отрисовываемый через content: var(--logo). Мы выяснили это на практике 10 июня 2026 г., разбираясь, почему фон страницы входа у клиента просто не применялся после обновления: старого селектора body.login-page в DOM больше не было. В логе не было ошибок — CSS был корректен для версии, которая больше не работала. Поэтому мы стали выдавать правильные селекторы через хук, версионируя их вместе с модулем. Вот реальный CSS, который внедряет модуль, — и мёртвые селекторы, объясняющие исчезновение:

/* GLPI 11 - REAL login screen selectors (page_card_notlogged.html.twig) */
/* The logo is NOT a background: it is a <span class="glpi-logo"> with 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;   /* preserves the image aspect ratio */
}

/* The background goes ONLY on body.welcome-anonymous - NEVER on body.login-page.
   Painting .page-anonymous too (a direct child) duplicated the artwork at the edges. */
body.welcome-anonymous {
  background-image: url("...") !important;
  background-size: cover !important;            /* or contain, or 100% 100% (stretch) */
  background-position: center center !important;
  background-repeat: no-repeat !important;
  background-attachment: fixed !important;      /* sized by the viewport, not the page */
}
.page-anonymous { background-image: none !important; background-color: transparent !important; }

/* GLPI 10 selectors that NO LONGER exist in 11 (why the old hack vanishes with no error):
   .login-logo      -> now span.glpi-logo
   body.login-page  -> now body.welcome-anonymous  */

Как модуль применяет стиль, не трогая ядро

На странице входа сессии нет, поэтому модуль подключается к хуку display_login и внедряет встроенный блок <style>/<script>. Изображения (favicon, логотип, фон) отдаются stateless-эндпоинтом, не требующим входа, с тремя защитами, о которых стоит сказать, потому что именно здесь небрежные плагины допускают утечки: расширение проверяется по белому списку (png, jpg, jpeg, gif, ico, webp; SVG убран в версии 1.4.0), а реальный тип файла проверяется по содержимому; путь разрешается через realpath() и сверяется с префиксом каталога загрузки (блокировка path traversal); файл сохраняется под случайным именем, сгенерированным сервером, без использования присланного имени, чтобы не было коллизий и перезаписи. На внутренних страницах, уже после аутентификации, цвета, логотип заголовка и скрытый нижний колонтитул добавляются как CSS в <head> через хук add_css, а favicon, заголовок и пользовательский нижний колонтитул — через хук add_javascript.

Деталь производительности, которая видна только при замерах: внутренний JS отдаётся с Cache-Control: private, max-age=86400, а изображения — с public, max-age=31536000, immutable (один год: каждая загрузка создаёт файл с новым именем), но без ETag. Причина конкретна: при одновременном использовании ETag и max-age Chrome выполнял повторную проверку (HTTP 304) при каждой загрузке страницы, и каждый 304 всё равно оплачивал полный bootstrap GLPI на сервере. Хеш конфигурации входит в параметр fv= URL, поэтому сохранение настроек меняет URL и сразу инвалидирует кеш, без повторной проверки.

Настройка за пять шагов

  1. Активируйте модуль Branding в каталоге модулей NexTool.
  2. На вкладке Настройки загрузите favicon, логотип входа и логотип заголовка; задайте ширину и высоту каждого логотипа (принимаются px, em, rem, %, vw, vh).
  3. Загрузите фон страницы входа и настройте заголовок вкладки браузера.
  4. Выберите поведение нижнего колонтитула: оставить исходный, скрыть или заменить текстом (HTML ограничен безопасными тегами).
  5. По желанию: задайте цвета интерфейса через CSS-переменные (модуль проверяет каждое значение как hex перед выводом, предотвращая CSS injection). Перезагрузите GLPI, чтобы увидеть результат.

Аудит: кто что изменил

Каждое действие — активация, деактивация, сохранение настроек, загрузка или удаление изображения — записывается в таблицу журнала модуля. В среде с несколькими администраторами этот след отвечает на вопрос «кто сменил логотип в пятницу вечером», не полагаясь на память. Диагностический запрос:

-- Branding module audit trail: who changed the visual identity and when
SELECT l.date_creation  AS when_at,
       u.name           AS user_name,
       l.action         AS action,   -- save_config (image uploads and deletions go in the detail), toggle_enable/disable
       l.detail         AS detail
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;

Подводные камни на практике

  • Favicon не меняется — браузер агрессивно кеширует favicon, по вкладке и по сессии. После загрузки нового значка помогает жёсткое обновление (Ctrl+Shift+R) или закрытие и повторное открытие вкладки; это не ошибка модуля.
  • SVG не принимается — начиная с версии 1.4.0 модуль отклоняет SVG для логотипа и favicon, потому что формат может содержать встроенный скрипт, а изображения модуля отдаются без входа. Используйте PNG с прозрачным фоном (или WEBP) для логотипа и PNG или ICO для favicon; сервер проверяет реальное содержимое файла, поэтому SVG, переименованный в .png, тоже будет отклонён.
  • Нижний колонтитул со вставленным HTML — пользовательский текст колонтитула проходит санацию, которая разрешает лишь несколько тегов (a, b, strong, i, em, span, br). Вставленный целиком блок маркетингового HTML просто не отобразится так, как вы ожидаете.
  • Пропорции логотипа — фиксированные ширина и высота без учёта исходных пропорций искажают изображение. Модуль применяет object-fit: contain именно для того, чтобы не растягивать, но результат стоит проверить на экране.
  • Обновление с 10 на 11 — если у клиента был ручной брендинг в GLPI 10, в 11 он исчезнет из-за сменившихся селекторов. Рассматривайте брендинг как часть плана обновления, а не как сюрприз после миграции.

Хотите унифицировать фирменный стиль GLPI во всех средах, которые вы администрируете, не возвращаясь к этой теме при каждом обновлении? Модуль Branding бесплатен и применяет фирменный стиль через штатные хуки. А если вы предпочитаете передать внедрение и сопровождение GLPI на аутсорс, NexTool поддерживает среды клиентов с версионированным брендингом, устойчивым к обновлениям.


Проверено командой NexTool Solutions.

Часто задаваемые вопросы

Потому что настройка была сделана прямо в ядре (файлы CSS, изображения или шаблоны), а обновление GLPI перезаписывает эти файлы. Чтобы пережить обновление, фирменный стиль нужно применять через хуки плагина (display_login, add_css, add_javascript), не трогая ядро, — именно для этого и существует модуль Branding.

В GLPI 11 шаблон страницы входа был переписан. Логотип перестал быть фоновым изображением и стал элементом <span class="glpi-logo"> с content: var(--logo); body стал body.welcome-anonymous, а контейнер — .page-anonymous. Если CSS нацелен на селекторы GLPI 10 (.login-logo, body.login-page), их больше нет в DOM, и правило не применяется — без какой-либо ошибки.

Нет. Модуль Branding применяет favicon, логотипы, фон страницы входа, нижний колонтитул и цвета через интерфейс, с помощью штатных хуков. Изменения не хранятся в файлах ядра, поэтому переживают обновление GLPI.

На вкладке «Настройки» модуля загрузите изображение favicon и введите нужный заголовок. Модуль переписывает <link rel="icon"> и document.title с помощью JavaScript. Если новый значок не появился, дело в кеше браузера: поможет жёсткое обновление (Ctrl+Shift+R) или повторное открытие вкладки.

Нет. Начиная с версии 1.4.0 модуль отклоняет SVG для логотипа и favicon, потому что этот формат может содержать встроенный скрипт, а изображения модуля отдаются без входа в систему. Используйте PNG с прозрачным фоном (или WEBP) для логотипа и PNG или ICO для favicon. При загрузке проверяется реальное содержимое файла, а сервис защищён от path traversal.

Branding бесплатен и входит в NexTool, но работает только в GLPI 11: версии для GLPI 10 нет. Внимание при обновлении: если в GLPI 10 был ручной брендинг, он не перенесётся в 11 сам по себе (селекторы страницы входа изменились); после миграции примените его заново через модуль.

Нужна помощь?