diff --git a/source/packages/plg_system_mokosuiteclient_dbip/src/Extension/DBIP.php b/source/packages/plg_system_mokosuiteclient_dbip/src/Extension/DBIP.php
index bacfe289..c5d3f6c7 100644
--- a/source/packages/plg_system_mokosuiteclient_dbip/src/Extension/DBIP.php
+++ b/source/packages/plg_system_mokosuiteclient_dbip/src/Extension/DBIP.php
@@ -23,6 +23,7 @@ class DBIP extends CMSPlugin implements SubscriberInterface
{
return [
'onAfterInitialise' => 'onAfterInitialise',
+ 'onAfterRender' => 'onAfterRender',
];
}
@@ -80,4 +81,92 @@ class DBIP extends CMSPlugin implements SubscriberInterface
DBIPHelper::downloadCityDb($url);
}
+
+ /**
+ * Scan rendered admin HTML for IP addresses and enrich with geo flags + tooltips.
+ */
+ public function onAfterRender(): void
+ {
+ $app = $this->getApplication();
+
+ if (!$app->isClient('administrator'))
+ {
+ return;
+ }
+
+ if ($app->getDocument()->getType() !== 'html')
+ {
+ return;
+ }
+
+ $body = $app->getBody();
+
+ if (empty($body))
+ {
+ return;
+ }
+
+ $ipv4 = '(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)';
+ $ipv6 = '(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))?';
+ $pattern = '#]*)?>(' . $ipv4 . '|' . $ipv6 . ')#';
+
+ $cache = [];
+
+ $newBody = preg_replace_callback($pattern, function ($m) use (&$cache) {
+ $fullMatch = $m[0];
+ $ip = $m[1];
+
+ // Skip private/loopback
+ if (filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE) === false)
+ {
+ return $fullMatch;
+ }
+
+ // Already enriched (has a title attribute)
+ if (strpos($fullMatch, 'title=') !== false)
+ {
+ return $fullMatch;
+ }
+
+ if (!isset($cache[$ip]))
+ {
+ $cache[$ip] = DBIPHelper::lookup($ip);
+ }
+
+ $geo = $cache[$ip];
+
+ if ($geo === null || empty($geo['country_code']))
+ {
+ return $fullMatch;
+ }
+
+ $cc = strtoupper($geo['country_code']);
+ $flag = self::countryFlag($cc);
+
+ $parts = array_filter([$geo['city'], $geo['region'], $geo['country_name']]);
+ $tooltip = htmlspecialchars(implode(', ', $parts), \ENT_QUOTES, 'UTF-8');
+ $escaped = htmlspecialchars($ip, \ENT_QUOTES, 'UTF-8');
+
+ return $flag . ' ' . $escaped . '';
+ }, $body);
+
+ if ($newBody !== null && $newBody !== $body)
+ {
+ $app->setBody($newBody);
+ }
+ }
+
+ private static function countryFlag(string $cc): string
+ {
+ if (\strlen($cc) !== 2)
+ {
+ return '';
+ }
+
+ $cc = strtoupper($cc);
+ $first = mb_chr(0x1F1E6 + \ord($cc[0]) - \ord('A'));
+ $second = mb_chr(0x1F1E6 + \ord($cc[1]) - \ord('A'));
+
+ return $first . $second;
+ }
}