Files
itflow/includes/inc_alert_feedback.php
johnnyq 3b5c11c552 Add managed mode, so one ITFlow can manage others - Admin > Managed Instances adds an instance and gives a one-time connection code (a week) that the other ITFlow pastes into the new Admin > Managed By, after which it checks in every minute from the new Manager Check-in cron job with a health report (versions, update waiting and whether it has security fixes, cron, last backup, errors today, agents against the seat limit, clients, storage) and takes back what the manager sets; the manager lists each instance's version, health, seats and last check-in with a page per instance showing what needs a look, its requests and everything it reported, can update it by hand or automatically after a set number of days (security releases straight away, not retried for six hours after a failure), set its seat limit and white-label key (locked on the instance while set), make it read-only (look, export and download only, across agents, portal, guest links and the API) or suspended (a notice with the manager's message and contact, 503 for the API) with other cron jobs paused until it is active again, sign in to it as support through a signed one-time two-minute link into a two-hour session that takes no seat, can't open the vault and can't set passwords or make API keys, and remove it, while the new Managed Instances job notifies when one stops checking in and closes requests never answered; instances only call the manager, can disconnect unless config.php sets $config_manager_required, and the Update page and queue_update defer to the manager; database 3.5.8 adds managed_instances, managed_instance_commands, settings.config_manager_keys and settings.config_managed, the logic is in the new functions/managed.php with api/managed/enroll.php, api/managed/checkin.php, login_support.php and includes/managed_blocked.php, cronJobClaim() takes a paused reason that leaves a due job due, getAgentUserLimit() and getActiveAgentUserCount() honour the manager's limit and leave out the support user, the vault helpers read a missing session key as empty, the audit log knows the new types and actions, and the email parser no longer logs an App Logs error every minute while parsing is switched off; changelog updated
2026-11-05 15:22:41 -05:00

121 lines
5.3 KiB
PHP

<?php
/**
* Flash alert rendering.
*
* Renders the session flash message as a toast, replacing toastr and its jQuery
* dependency without adding a library. It borrows Bootstrap's .toast styling
* but NOT its Toast component: the element ships with .show already on it and
* the whole appear/linger/fade cycle is a CSS animation, so the alert is on
* screen at first paint. Driving it with bootstrap.Toast meant waiting for
* DOMContentLoaded, which does not fire until every parser-blocking script has
* run - so it used to arrive well after the page had settled. See the
* .itflow-toast block in css/itflow_custom.css.
*
* SECURITY: the message is rendered into HTML here, not interpolated into a
* JavaScript string literal as it was previously. 429 of the ~653 flashAlert()
* call sites interpolate PHP variables, many of them user-controlled (custom
* link names, ticket status names, saved payment descriptions), and the old
* form allowed a stored value containing a quote or </script> to break out and
* execute. Everything is escaped, then a fixed allowlist of formatting tags is
* restored so the existing <strong> markup in those messages still renders.
*/
if (!empty($_SESSION['alert_message'])) {
$alert_type = $_SESSION['alert_type'] ?? 'success';
// One mapping for both renderers - see alertStyleClass() in
// functions/sanitize.php. flashAlert() is called with more type names than
// Bootstrap has classes ('danger', 'alert' and a typo'd 'errpr' among
// them), and an unmapped value used to resolve to nothing at all.
$alert_style_class = alertStyleClass($alert_type);
$alert_style = 'text-bg-' . $alert_style_class;
// text-bg-info and text-bg-warning are the two Bootstrap pairs with dark text
$alert_dark_text = in_array($alert_style_class, ['warning', 'info'], true);
// Font Awesome 5 names - the vendored build is 5.15.4, so no fa-circle-*
// aliases. Keyed on alertStyleClass()' output rather than the raw type, so
// 'error', 'errpr' and 'danger' all land on the same icon the same way they
// already land on the same colour.
$alert_icons = [
'success' => 'fa-check-circle',
'info' => 'fa-info-circle',
'warning' => 'fa-exclamation-triangle',
'danger' => 'fa-times-circle',
'secondary' => 'fa-bell',
];
$alert_icon = $alert_icons[$alert_style_class] ?? 'fa-bell';
// Escaping lives in one place now - see alertMessageHtml() in functions/sanitize.php
$alert_safe_message = alertMessageHtml($_SESSION['alert_message']);
?>
<?php /* `show` is on the element from the server, and `fade` is gone: the
appearance is a CSS animation now, so the alert is on screen at
first paint instead of waiting for bootstrap.Toast. The old version
called it on DOMContentLoaded, which does not fire until every
parser-blocking script in the document has run - most of a megabyte
on a page with tinymce - so the alert turned up after you had
already started reading. toastr never had that problem because it
loaded in the header. */ ?>
<div class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 1090;">
<div id="itflowFlashToast"
class="toast show itflow-toast align-items-center <?= $alert_style ?> border-0"
role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex align-items-center">
<i class="fas fa-fw <?= $alert_icon ?> fa-lg ms-3"></i>
<div class="toast-body"><?= $alert_safe_message ?></div>
<?php /* No data-bs-dismiss - the click handler below covers the
whole toast, and this button sits inside it. */ ?>
<button type="button"
class="btn-close <?= $alert_dark_text ? '' : 'btn-close-white' ?> me-2 m-auto"
aria-label="Close"></button>
</div>
<div class="itflow-toast-progress"></div>
</div>
</div>
<script>
(function () {
// Deliberately depends on nothing. It runs inline, right after the
// markup above, and does not wait for DOMContentLoaded - showing
// and hiding is CSS, so if this never ran the alert would still
// appear and still go away on its own.
var el = document.getElementById('itflowFlashToast');
if (!el) {
return;
}
// Click anywhere on it to dismiss, the way toastr did. The close
// button is inside the toast, so this covers it too.
el.addEventListener('click', function () {
el.remove();
});
// Remove on the fade rather than a fixed timer, so hovering to
// finish reading a long message keeps the node alive as well as
// visible.
el.addEventListener('animationend', function (event) {
if (event.animationName === 'itflow-toast-out') {
el.remove();
}
});
})();
</script>
<?php
unset($_SESSION['alert_type']);
unset($_SESSION['alert_message']);
}
?>
<?php
// A managed instance its manager has made read-only, or a support session from the manager
// (functions/managed.php)
echo managedBannerHtml();