diff --git a/cp/app/Controllers/ContactsController.php b/cp/app/Controllers/ContactsController.php
index 82c158a..3973351 100644
--- a/cp/app/Controllers/ContactsController.php
+++ b/cp/app/Controllers/ContactsController.php
@@ -881,6 +881,57 @@ class ContactsController extends Controller
}
+ public function historyContact(Request $request, Response $response, $args)
+ {
+ if (envi('MINIMUM_DATA') === 'true') {
+ return $response->withHeader('Location', '/dashboard')->withStatus(302);
+ }
+
+ $db = $this->container->get('db');
+ $db_audit = $this->container->get('db_audit');
+ // Get the current URI
+ $uri = $request->getUri()->getPath();
+
+ if ($args) {
+ $args = trim($args);
+
+ if (!preg_match('/^[a-zA-Z0-9\-]+$/', $args)) {
+ $this->container->get('flash')->addMessage('error', 'Invalid contact ID format');
+ return $response->withHeader('Location', '/contacts')->withStatus(302);
+ }
+
+ try {
+ $exists = $db_audit->selectValue('SELECT 1 FROM domain LIMIT 1');
+ } catch (\PDOException $e) {
+ throw new \RuntimeException('Audit table is empty or not configured');
+ }
+
+ $contact = $db->selectRow('SELECT id, identifier FROM contact WHERE identifier = ?',
+ [ $args ]);
+
+ if ($contact) {
+ $history = $db_audit->select(
+ 'SELECT * FROM contact WHERE identifier = ? ORDER BY audit_timestamp DESC, audit_rownum ASC',
+ [$args]
+ );
+
+ return view($response,'admin/contacts/historyContact.twig', [
+ 'contact' => $contact,
+ 'history' => $history,
+ 'currentUri' => $uri
+ ]);
+ } else {
+ // Contact does not exist, redirect to the contacts view
+ return $response->withHeader('Location', '/contacts')->withStatus(302);
+ }
+
+ } else {
+ // Redirect to the contacts view
+ return $response->withHeader('Location', '/contacts')->withStatus(302);
+ }
+
+ }
+
public function updateContact(Request $request, Response $response, $args)
{
if (envi('MINIMUM_DATA') === 'true') {
diff --git a/cp/app/Controllers/HostsController.php b/cp/app/Controllers/HostsController.php
index 6111624..224379e 100644
--- a/cp/app/Controllers/HostsController.php
+++ b/cp/app/Controllers/HostsController.php
@@ -297,7 +297,57 @@ class HostsController extends Controller
}
}
-
+
+ public function historyHost(Request $request, Response $response, $args)
+ {
+ $db = $this->container->get('db');
+ $db_audit = $this->container->get('db_audit');
+ // Get the current URI
+ $uri = $request->getUri()->getPath();
+
+ if ($args && isValidHostname($args)) {
+ $args = trim($args);
+
+ if (mb_detect_encoding($args, 'ASCII', true) === false) {
+ $args = idn_to_ascii($args, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($args === false) {
+ // Redirect to the hosts view
+ return $response->withHeader('Location', '/hosts')->withStatus(302);
+ }
+ }
+
+ $host = $db->selectRow('SELECT id, name FROM host WHERE name = ?',
+ [ $args ]);
+
+ if ($host) {
+ try {
+ $exists = $db_audit->selectValue('SELECT 1 FROM domain LIMIT 1');
+ } catch (\PDOException $e) {
+ throw new \RuntimeException('Audit table is empty or not configured');
+ }
+
+ $history = $db_audit->select(
+ 'SELECT * FROM host WHERE name = ? ORDER BY audit_timestamp DESC, audit_rownum ASC',
+ [$args]
+ );
+
+ return view($response,'admin/hosts/historyHost.twig', [
+ 'host' => $host,
+ 'history' => $history,
+ 'currentUri' => $uri
+ ]);
+ } else {
+ // Host does not exist, redirect to the hosts view
+ return $response->withHeader('Location', '/hosts')->withStatus(302);
+ }
+
+ } else {
+ // Redirect to the hosts view
+ return $response->withHeader('Location', '/hosts')->withStatus(302);
+ }
+
+ }
+
public function updateHost(Request $request, Response $response, $args)
{
$db = $this->container->get('db');
diff --git a/cp/app/Controllers/RegistrarsController.php b/cp/app/Controllers/RegistrarsController.php
index 538d6be..4a697e9 100644
--- a/cp/app/Controllers/RegistrarsController.php
+++ b/cp/app/Controllers/RegistrarsController.php
@@ -407,7 +407,54 @@ class RegistrarsController extends Controller
}
}
-
+
+ public function historyRegistrar(Request $request, Response $response, $args)
+ {
+ $db = $this->container->get('db');
+ $db_audit = $this->container->get('db_audit');
+ // Get the current URI
+ $uri = $request->getUri()->getPath();
+
+ if ($args) {
+ $args = trim(preg_replace('/\s+/', ' ', $args));
+
+ if (!preg_match('/^[a-zA-Z0-9\s.\-]+$/', $args)) {
+ $this->container->get('flash')->addMessage('error', 'Invalid registrar');
+ return $response->withHeader('Location', '/registrars')->withStatus(302);
+ }
+
+ $registrar = $db->selectRow('SELECT id,name,clid FROM registrar WHERE clid = ?',
+ [ $args ]);
+
+ if ($registrar) {
+ try {
+ $exists = $db_audit->selectValue('SELECT 1 FROM domain LIMIT 1');
+ } catch (\PDOException $e) {
+ throw new \RuntimeException('Audit table is empty or not configured');
+ }
+
+ $history = $db_audit->select(
+ 'SELECT * FROM registrar WHERE clid = ? ORDER BY audit_timestamp DESC, audit_rownum ASC LIMIT 200',
+ [$args]
+ );
+
+ return view($response,'admin/registrars/historyRegistrar.twig', [
+ 'registrar' => $registrar,
+ 'history' => $history,
+ 'currentUri' => $uri
+ ]);
+ } else {
+ // Registrar does not exist, redirect to the registrars view
+ return $response->withHeader('Location', '/registrars')->withStatus(302);
+ }
+
+ } else {
+ // Redirect to the registrars view
+ return $response->withHeader('Location', '/registrars')->withStatus(302);
+ }
+
+ }
+
public function registrar(Request $request, Response $response)
{
$db = $this->container->get('db');
diff --git a/cp/resources/views/admin/contacts/historyContact.twig b/cp/resources/views/admin/contacts/historyContact.twig
new file mode 100644
index 0000000..53a6c2e
--- /dev/null
+++ b/cp/resources/views/admin/contacts/historyContact.twig
@@ -0,0 +1,125 @@
+{% extends "layouts/app.twig" %}
+
+{% block title %}{{ __('Contact History') }}{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ __('Timestamp') }} |
+ {{ __('Action') }} |
+ {{ __('User') }} |
+ {{ __('Session') }} |
+ {{ __('Changed Field') }} |
+ {{ __('Old Value') }} |
+ {{ __('New Value') }} |
+
+
+
+ {% if history|length == 0 %}
+
+ {{ __('No audit history available for this contact.') }} |
+
+ {% else %}
+ {% set max = history|length %}
+ {% for i in 0..max-1 %}
+ {% set entry = history[i] %}
+ {% if entry.audit_statement == 'UPDATE' and entry.audit_type == 'OLD' %}
+ {% set old = entry %}
+ {% set new = history[i + 1] is defined and history[i + 1].audit_type == 'NEW' ? history[i + 1] : {} %}
+ {% for key in old|keys %}
+ {% if old[key] != new[key] and key not in ['audit_timestamp','audit_statement','audit_type','audit_uuid','audit_rownum','audit_user','audit_ses_id','audit_usr_id'] %}
+
+ {{ old.audit_timestamp }} |
+ {{ old.audit_statement }} |
+ {{ old.audit_usr_id|default('–') }} |
+ {{ old.audit_ses_id|default('–') }} |
+ {{ key }} |
+ {{ old[key]|default('–') }} |
+ {{ new[key]|default('–') }} |
+
+ {% endif %}
+ {% endfor %}
+ {% elseif entry.audit_statement == 'INSERT' %}
+
+ {{ entry.audit_timestamp }} |
+ {{ entry.audit_statement }} |
+ {{ entry.audit_usr_id|default('–') }} |
+ {{ entry.audit_ses_id|default('–') }} |
+ {{ __('New contact inserted.') }} |
+
+ {% elseif entry.audit_statement == 'DELETE' %}
+
+ {{ entry.audit_timestamp }} |
+ {{ entry.audit_statement }} |
+ {{ entry.audit_usr_id|default('–') }} |
+ {{ entry.audit_ses_id|default('–') }} |
+ {{ __('Contact was deleted.') }} |
+
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+
+
+
+
+
+ {% include 'partials/footer.twig' %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/cp/resources/views/admin/contacts/viewContact.twig b/cp/resources/views/admin/contacts/viewContact.twig
index 125015f..5082e3d 100644
--- a/cp/resources/views/admin/contacts/viewContact.twig
+++ b/cp/resources/views/admin/contacts/viewContact.twig
@@ -29,6 +29,13 @@