Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Fixed

- Faster display of pages showing alerts (ticket form, central page, ticket list) when many alerts exist
- Faster display of the alert targets tab
- Closing an alert sent one request per tab previously opened on the page

## [1.14.3] - 2026-11-05

### Fixed
Expand Down
3 changes: 3 additions & 0 deletions hook.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ function plugin_news_install()
$migration->addKey($alert_table, 'is_active');
}

// index used to find the alerts to display, which would otherwise scan every past alert
$migration->addKey($alert_table, ['is_active', 'is_deleted', 'date_end'], 'is_active_is_deleted_date_end');

// fix is_default default value
$alert_fields = $DB->listFields($alert_table);
if ($alert_fields['is_deleted']['Default'] !== '0') {
Expand Down
18 changes: 6 additions & 12 deletions inc/alert.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,24 +227,18 @@ public function rawSearchOptions()

public function post_updateItem($history = true)
{
/** @var DBmysql $DB */
global $DB;

// force display of alerts after update regardless of their closable state
$alert_user = new PluginNewsAlert_User();
//get all Alert_User for this alert where state is hidden
$all_alert = $alert_user->find(
$DB->update(
PluginNewsAlert_User::getTable(),
['state' => PluginNewsAlert_User::VISIBLE],
[
'plugin_news_alerts_id' => $this->getID(),
'state' => PluginNewsAlert_User::HIDDEN,
],
);
foreach ($all_alert as $alert) {
//update state to force display
$alert_user->update(
[
'id' => $alert['id'],
'state' => PluginNewsAlert_User::VISIBLE,
],
);
}
}

public static function findAllToNotify($params = [])
Expand Down
23 changes: 20 additions & 3 deletions inc/alert_target.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,11 @@ public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $

public static function showForAlert(PluginNewsAlert $alert)
{
/** @var array $CFG_GLPI */
global $CFG_GLPI;
/**
* @var array $CFG_GLPI
* @var DBmysql $DB
*/
global $CFG_GLPI, $DB;
$rand = mt_rand();

echo "<form method='post' action='" . Toolbox::getItemTypeFormURL('PluginNewsAlert') . "'>";
Expand Down Expand Up @@ -171,10 +174,24 @@ public static function showForAlert(PluginNewsAlert $alert)
echo '<th>' . __s('Recipient', 'news') . '</th>';
echo '</tr>';

// Load the targeted items with one query per itemtype instead of one per target
$ids_by_itemtype = [];
foreach ($found_target as $current_target) {
if (is_a($current_target['itemtype'], CommonDBTM::class, true) && $current_target['items_id'] > 0) {
$ids_by_itemtype[$current_target['itemtype']][] = $current_target['items_id'];
}
}
$rows_by_itemtype = [];
foreach ($ids_by_itemtype as $itemtype => $ids) {
foreach ($DB->request(['FROM' => getTableForItemType($itemtype),'WHERE' => ['id' => $ids]]) as $row) {
$rows_by_itemtype[$itemtype][$row['id']] = $row;
}
}

foreach ($found_target as $current_target) {
if (is_a($current_target['itemtype'], CommonDBTM::class, true)) {
$item = new $current_target['itemtype']();
$item->getFromDB($current_target['items_id']);
$item->getFromResultSet($rows_by_itemtype[$current_target['itemtype']][$current_target['items_id']] ?? []);
$name = ($current_target['all_items'] == 1
&& $current_target['itemtype'] == 'Profile')
? __s('All', 'news')
Expand Down
14 changes: 14 additions & 0 deletions public/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,24 @@
* -------------------------------------------------------------------------
*/

/* Missing tabler variable */
:root {
--tblr-lime: #74b816;
}

.plugin_news_alert-container {
margin-bottom: 1rem;
}

/*
* ITIL form has a specified layout, we must always display the full size alert.
* The "col-12" class can't be directly used for this purpose because a GLPI's core
* script will automatically replace it by "col-6" when expanding the sidebar.
*/
#itil-form .plugin_news_alert {
width: 100% !important;
}

/* Needed to have similar height for alerts on the same row */
.plugin_news_alert-container .alert {
height: 100%;
Expand Down
10 changes: 3 additions & 7 deletions public/js/news.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
*/

pluginNewsCloseAlerts = function() {
$(document).on("mousedown", ".plugin_news_alert .alert a[data-bs-dismiss=alert]", function(event) {
$(document).off("mousedown.pluginnews").on("mousedown.pluginnews", ".plugin_news_alert .alert a[data-bs-dismiss=alert]", function(event) {
var alert = $(this).closest(".plugin_news_alert");
var id = alert.attr('data-id');
var a_url = CFG_GLPI.root_doc+"/plugins/news/ajax";
Expand All @@ -36,18 +36,14 @@ pluginNewsCloseAlerts = function() {
};

pluginNewsToggleAlerts = function() {
$(document).on("click", ".plugin_news_alert-toggle",function() {
$(document).off("click.pluginnews").on("click.pluginnews", ".plugin_news_alert-toggle",function() {
var alert = $(this).closest(".plugin_news_alert");
alert.toggleClass('expanded');
});
}

// Handlers are delegated on document: bound once, they also cover alerts loaded later in tabs
$(function() {
pluginNewsCloseAlerts();
pluginNewsToggleAlerts();

$(document).on('glpi.tab.loaded', function() {
pluginNewsCloseAlerts();
pluginNewsToggleAlerts();
});
});
48 changes: 21 additions & 27 deletions setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,37 +47,31 @@ function plugin_init_news()

$PLUGIN_HOOKS['csrf_compliant']['news'] = true;

$plugin = new Plugin();
if (
$plugin->isInstalled('news')
&& $plugin->isActivated('news')
) {
Plugin::registerClass('PluginNewsProfile', ['addtabon' => 'Profile']);
Plugin::registerClass('PluginNewsProfile', ['addtabon' => 'Profile']);

$PLUGIN_HOOKS['add_css']['news'] = 'css/styles.css';
$PLUGIN_HOOKS['add_javascript']['news'][] = 'js/news.js';
$PLUGIN_HOOKS['display_login']['news'] = [
'PluginNewsAlert', 'displayOnLogin',
];
$PLUGIN_HOOKS['display_central']['news'] = [
'PluginNewsAlert', 'displayOnCentral',
];
$PLUGIN_HOOKS['display_service_catalog']['news'] = [
'PluginNewsAlert', 'displayOnServiceCatalog',
];
$PLUGIN_HOOKS['pre_item_list']['news'] = ['PluginNewsAlert', 'preItemList'];
$PLUGIN_HOOKS['add_css']['news'] = 'css/styles.css';
$PLUGIN_HOOKS['add_javascript']['news'][] = 'js/news.js';
$PLUGIN_HOOKS['display_login']['news'] = [
'PluginNewsAlert', 'displayOnLogin',
];
$PLUGIN_HOOKS['display_central']['news'] = [
'PluginNewsAlert', 'displayOnCentral',
];
$PLUGIN_HOOKS['display_service_catalog']['news'] = [
'PluginNewsAlert', 'displayOnServiceCatalog',
];
$PLUGIN_HOOKS['pre_item_list']['news'] = ['PluginNewsAlert', 'preItemList'];

$PLUGIN_HOOKS['pre_item_form']['news'] = ['PluginNewsAlert', 'preItemForm'];
$PLUGIN_HOOKS['pre_item_form']['news'] = ['PluginNewsAlert', 'preItemForm'];

if (Session::haveRight(PluginNewsAlert::$rightname, READ)) {
$PLUGIN_HOOKS['menu_toadd']['news'] = [
'tools' => 'PluginNewsAlert',
];
$PLUGIN_HOOKS['config_page']['news'] = 'front/alert.php';
if (Session::haveRight(PluginNewsAlert::$rightname, READ)) {
$PLUGIN_HOOKS['menu_toadd']['news'] = [
'tools' => 'PluginNewsAlert',
];
$PLUGIN_HOOKS['config_page']['news'] = 'front/alert.php';

// require tinymce (for glpi >= 9.2)
$CFG_GLPI['javascript']['tools']['pluginnewsalert'] = ['tinymce'];
}
// require tinymce (for glpi >= 9.2)
$CFG_GLPI['javascript']['tools']['pluginnewsalert'] = ['tinymce'];
}
}

Expand Down
44 changes: 6 additions & 38 deletions templates/display_alert.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,20 @@
# -------------------------------------------------------------------------
#}

<style>
/* Mising tabler variable */
:root{
--tblr-lime: #74b816;
}
</style>

{% set js_rand = random() %}
{# Accentuation color has a lower opacity, unless it is the background color (= no accent) #}
{% set accent = 'var(--tblr-' ~ alert_fields.emphasis_color ~ ')' %}
{% if alert_fields.emphasis_color != alert_fields.background_color %}
{% set accent = 'color-mix(in srgb, ' ~ accent ~ ' 60%, transparent)' %}
{% endif %}
<div class="plugin_news_alert {{ size }}" data-id="{{ alert_fields.id|default(0) }}">
<div
class="
alert-{{ js_rand }}
alert bg-{{ alert_fields.background_color }}
{{ can_close ? "alert-dismissible" : "" }}
"
style="
color: var(--tblr-{{ alert_fields.text_color }}) !important;
border-left-color: var(--tblr-{{ alert_fields.emphasis_color }}) !important;
border-left-color: {{ accent }} !important;
"
>
<div class="d-flex">
Expand Down Expand Up @@ -82,31 +78,3 @@
</div>
</div>

<script>
(function () {
// Accentuation color must have a lower opacity
// Must be done in JS, we can't do a rgba on hex variables directly from css
const background = $('.alert-{{ js_rand }}').css('background-color');
const accent = $('.alert-{{ js_rand }}').css('border-left-color');

// Do not add opacity if background and accent are identical (= no accent)
if (background !== accent) {
// Rewrite "rgb(r,g,b)" into "rgba(r,g,b,0.6)"
$('.alert-{{ js_rand }}').css(
'border-left-color',
$('.alert-{{ js_rand }}').css('border-left-color')
.replace('rgb', 'rgba')
.replace(')', ', 0.6)')
)
}

// ITIL form has a specified layout, we must always display the full size
// alert.
// The "col-12" class can't be directly used for this purpose because
// a GLPI's core script will automatically replace it by "col-6" when
// expending the sidebar.
$('form[id="itil-form"]')
.find('.plugin_news_alert')
.addClass('w-100');
})();
</script>
Loading