Added basic contact, host, registrar history pages

This commit is contained in:
Pinga 2025-04-07 22:44:14 +03:00
parent 5e49fbc2bb
commit cae51f7cd0
10 changed files with 549 additions and 2 deletions

View file

@ -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) public function updateContact(Request $request, Response $response, $args)
{ {
if (envi('MINIMUM_DATA') === 'true') { if (envi('MINIMUM_DATA') === 'true') {

View file

@ -298,6 +298,56 @@ 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) public function updateHost(Request $request, Response $response, $args)
{ {
$db = $this->container->get('db'); $db = $this->container->get('db');

View file

@ -408,6 +408,53 @@ 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) public function registrar(Request $request, Response $response)
{ {
$db = $this->container->get('db'); $db = $this->container->get('db');

View file

@ -0,0 +1,125 @@
{% extends "layouts/app.twig" %}
{% block title %}{{ __('Contact History') }}{% endblock %}
{% block content %}
<div class="page-wrapper">
<!-- Page header -->
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<div class="mb-1">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{{route('home')}}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><polyline points="5 12 3 12 12 3 21 12 19 12" /><path d="M5 12v8a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-8" /><rect x="10" y="12" width="4" height="4" /></svg></a>
</li>
<li class="breadcrumb-item">
<a href="{{route('listContacts')}}">{{ __('Contacts') }}</a>
</li>
<li class="breadcrumb-item">
<a href="/contact/view/{{ contact.identifier }}">{{ __('Contact') }} {{ contact.identifier }}</a>
</li>
<li class="breadcrumb-item active">
{{ __('Contact History') }}
</li>
</ol>
</div>
<h2 class="page-title">
{{ __('Contact History') }}
</h2>
</div>
<!-- Page title actions -->
<div class="col-auto ms-auto d-print-none">
<div class="btn-list">
<a href="/contact/view/{{ contact.identifier }}" class="btn d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l-4 4l4 4m-4 -4h11a4 4 0 0 0 0 -8h-1" /></svg>
{{ __('Back to View') }}
</a>
<a href="/contact/view/{{ contact.identifier }}" class="btn d-sm-none btn-icon" aria-label="{{ __('Back to View') }}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l-4 4l4 4m-4 -4h11a4 4 0 0 0 0 -8h-1" /></svg>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Page body -->
<div class="page-body">
<div class="container-xl">
<div class="col-12">
<div class="card mb-3">
<div class="card-header">
<h3 class="card-title">
{{ __('Contact') }} {{ contact.identifier }}
</h3>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-vcenter card-table table-striped">
<thead>
<tr>
<th>{{ __('Timestamp') }}</th>
<th>{{ __('Action') }}</th>
<th>{{ __('User') }}</th>
<th>{{ __('Session') }}</th>
<th>{{ __('Changed Field') }}</th>
<th>{{ __('Old Value') }}</th>
<th>{{ __('New Value') }}</th>
</tr>
</thead>
<tbody>
{% if history|length == 0 %}
<tr>
<td colspan="7" class="text-center text-muted">{{ __('No audit history available for this contact.') }}</td>
</tr>
{% 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'] %}
<tr>
<td>{{ old.audit_timestamp }}</td>
<td>{{ old.audit_statement }}</td>
<td>{{ old.audit_usr_id|default('') }}</td>
<td>{{ old.audit_ses_id|default('') }}</td>
<td><strong>{{ key }}</strong></td>
<td class="text-muted">{{ old[key]|default('') }}</td>
<td class="text-success">{{ new[key]|default('') }}</td>
</tr>
{% endif %}
{% endfor %}
{% elseif entry.audit_statement == 'INSERT' %}
<tr>
<td>{{ entry.audit_timestamp }}</td>
<td>{{ entry.audit_statement }}</td>
<td>{{ entry.audit_usr_id|default('') }}</td>
<td>{{ entry.audit_ses_id|default('') }}</td>
<td colspan="3" class="text-muted">{{ __('New contact inserted.') }}</td>
</tr>
{% elseif entry.audit_statement == 'DELETE' %}
<tr>
<td>{{ entry.audit_timestamp }}</td>
<td>{{ entry.audit_statement }}</td>
<td>{{ entry.audit_usr_id|default('') }}</td>
<td>{{ entry.audit_ses_id|default('') }}</td>
<td colspan="3" class="text-muted">{{ __('Contact was deleted.') }}</td>
</tr>
{% endif %}
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'partials/footer.twig' %}
</div>
{% endblock %}

View file

@ -29,6 +29,13 @@
<!-- Page title actions --> <!-- Page title actions -->
<div class="col-auto ms-auto d-print-none"> <div class="col-auto ms-auto d-print-none">
<div class="btn-list"> <div class="btn-list">
<a href="/contact/history/{{ contact.identifier }}" class="btn btn-outline-purple d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 8l0 4l2 2" /><path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" /></svg>
{{ __('Contact History') }}
</a>
<a href="/contact/history/{{ contact.identifier }}" class="btn btn-outline-purple d-sm-none btn-icon" aria-label="{{ __('Contact History') }}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 8l0 4l2 2" /><path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" /></svg>
</a>
<a href="/contact/update/{{ contact.identifier }}" class="btn btn-primary d-none d-sm-inline-block"> <a href="/contact/update/{{ contact.identifier }}" class="btn btn-primary d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1" /><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z" /><path d="M16 5l3 3" /></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1" /><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z" /><path d="M16 5l3 3" /></svg>
{{ __('Update Contact') }} {{ __('Update Contact') }}

View file

@ -0,0 +1,125 @@
{% extends "layouts/app.twig" %}
{% block title %}{{ __('Host History') }}{% endblock %}
{% block content %}
<div class="page-wrapper">
<!-- Page header -->
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<div class="mb-1">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{{route('home')}}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><polyline points="5 12 3 12 12 3 21 12 19 12" /><path d="M5 12v8a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-8" /><rect x="10" y="12" width="4" height="4" /></svg></a>
</li>
<li class="breadcrumb-item">
<a href="{{route('listHosts')}}">{{ __('Hosts') }}</a>
</li>
<li class="breadcrumb-item">
<a href="/host/view/{{ host.name }}">{{ __('Host') }} {{ host.name }}</a>
</li>
<li class="breadcrumb-item active">
{{ __('Host History') }}
</li>
</ol>
</div>
<h2 class="page-title">
{{ __('Host History') }}
</h2>
</div>
<!-- Page title actions -->
<div class="col-auto ms-auto d-print-none">
<div class="btn-list">
<a href="/host/view/{{ host.name }}" class="btn d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l-4 4l4 4m-4 -4h11a4 4 0 0 0 0 -8h-1" /></svg>
{{ __('Back to View') }}
</a>
<a href="/host/view/{{ host.name }}" class="btn d-sm-none btn-icon" aria-label="{{ __('Back to View') }}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l-4 4l4 4m-4 -4h11a4 4 0 0 0 0 -8h-1" /></svg>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Page body -->
<div class="page-body">
<div class="container-xl">
<div class="col-12">
<div class="card mb-3">
<div class="card-header">
<h3 class="card-title">
{{ __('Host') }} {{ host.name }}
</h3>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-vcenter card-table table-striped">
<thead>
<tr>
<th>{{ __('Timestamp') }}</th>
<th>{{ __('Action') }}</th>
<th>{{ __('User') }}</th>
<th>{{ __('Session') }}</th>
<th>{{ __('Changed Field') }}</th>
<th>{{ __('Old Value') }}</th>
<th>{{ __('New Value') }}</th>
</tr>
</thead>
<tbody>
{% if history|length == 0 %}
<tr>
<td colspan="7" class="text-center text-muted">{{ __('No audit history available for this host.') }}</td>
</tr>
{% 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'] %}
<tr>
<td>{{ old.audit_timestamp }}</td>
<td>{{ old.audit_statement }}</td>
<td>{{ old.audit_usr_id|default('') }}</td>
<td>{{ old.audit_ses_id|default('') }}</td>
<td><strong>{{ key }}</strong></td>
<td class="text-muted">{{ old[key]|default('') }}</td>
<td class="text-success">{{ new[key]|default('') }}</td>
</tr>
{% endif %}
{% endfor %}
{% elseif entry.audit_statement == 'INSERT' %}
<tr>
<td>{{ entry.audit_timestamp }}</td>
<td>{{ entry.audit_statement }}</td>
<td>{{ entry.audit_usr_id|default('') }}</td>
<td>{{ entry.audit_ses_id|default('') }}</td>
<td colspan="3" class="text-muted">{{ __('New host inserted.') }}</td>
</tr>
{% elseif entry.audit_statement == 'DELETE' %}
<tr>
<td>{{ entry.audit_timestamp }}</td>
<td>{{ entry.audit_statement }}</td>
<td>{{ entry.audit_usr_id|default('') }}</td>
<td>{{ entry.audit_ses_id|default('') }}</td>
<td colspan="3" class="text-muted">{{ __('Host was deleted.') }}</td>
</tr>
{% endif %}
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'partials/footer.twig' %}
</div>
{% endblock %}

View file

@ -29,6 +29,13 @@
<!-- Page title actions --> <!-- Page title actions -->
<div class="col-auto ms-auto d-print-none"> <div class="col-auto ms-auto d-print-none">
<div class="btn-list"> <div class="btn-list">
<a href="/host/history/{{ host.name }}" class="btn btn-outline-purple d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 8l0 4l2 2" /><path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" /></svg>
{{ __('Host History') }}
</a>
<a href="/host/history/{{ host.name }}" class="btn btn-outline-purple d-sm-none btn-icon" aria-label="{{ __('Host History') }}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 8l0 4l2 2" /><path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" /></svg>
</a>
<a href="/host/update/{{ host.name }}" class="btn btn-primary d-none d-sm-inline-block"> <a href="/host/update/{{ host.name }}" class="btn btn-primary d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1" /><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z" /><path d="M16 5l3 3" /></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1" /><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z" /><path d="M16 5l3 3" /></svg>
{{ __('Update Host') }} {{ __('Update Host') }}

View file

@ -0,0 +1,125 @@
{% extends "layouts/app.twig" %}
{% block title %}{{ __('Registrar History') }}{% endblock %}
{% block content %}
<div class="page-wrapper">
<!-- Page header -->
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<div class="mb-1">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{{route('home')}}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><polyline points="5 12 3 12 12 3 21 12 19 12" /><path d="M5 12v8a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-8" /><rect x="10" y="12" width="4" height="4" /></svg></a>
</li>
<li class="breadcrumb-item">
<a href="{{route('registrars')}}">{{ __('Registrar') }}</a>
</li>
<li class="breadcrumb-item">
<a href="/registrar/view/{{ registrar.name }}">{{ __('Registrar') }} {{ registrar.name }}</a>
</li>
<li class="breadcrumb-item active">
{{ __('Registrar History') }}
</li>
</ol>
</div>
<h2 class="page-title">
{{ __('Registrar History') }}
</h2>
</div>
<!-- Page title actions -->
<div class="col-auto ms-auto d-print-none">
<div class="btn-list">
<a href="/registrar/view/{{ registrar.name }}" class="btn d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l-4 4l4 4m-4 -4h11a4 4 0 0 0 0 -8h-1" /></svg>
{{ __('Back to View') }}
</a>
<a href="/registrar/view/{{ registrar.name }}" class="btn d-sm-none btn-icon" aria-label="{{ __('Back to View') }}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l-4 4l4 4m-4 -4h11a4 4 0 0 0 0 -8h-1" /></svg>
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Page body -->
<div class="page-body">
<div class="container-xl">
<div class="col-12">
<div class="card mb-3">
<div class="card-header">
<h3 class="card-title">
{{ __('Registrar') }} {{ registrar.name }}
</h3>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-vcenter card-table table-striped">
<thead>
<tr>
<th>{{ __('Timestamp') }}</th>
<th>{{ __('Action') }}</th>
<th>{{ __('User') }}</th>
<th>{{ __('Session') }}</th>
<th>{{ __('Changed Field') }}</th>
<th>{{ __('Old Value') }}</th>
<th>{{ __('New Value') }}</th>
</tr>
</thead>
<tbody>
{% if history|length == 0 %}
<tr>
<td colspan="7" class="text-center text-muted">{{ __('No audit history available for this registrar.') }}</td>
</tr>
{% 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'] %}
<tr>
<td>{{ old.audit_timestamp }}</td>
<td>{{ old.audit_statement }}</td>
<td>{{ old.audit_usr_id|default('') }}</td>
<td>{{ old.audit_ses_id|default('') }}</td>
<td><strong>{{ key }}</strong></td>
<td class="text-muted">{{ old[key]|default('') }}</td>
<td class="text-success">{{ new[key]|default('') }}</td>
</tr>
{% endif %}
{% endfor %}
{% elseif entry.audit_statement == 'INSERT' %}
<tr>
<td>{{ entry.audit_timestamp }}</td>
<td>{{ entry.audit_statement }}</td>
<td>{{ entry.audit_usr_id|default('') }}</td>
<td>{{ entry.audit_ses_id|default('') }}</td>
<td colspan="3" class="text-muted">{{ __('New registrar inserted.') }}</td>
</tr>
{% elseif entry.audit_statement == 'DELETE' %}
<tr>
<td>{{ entry.audit_timestamp }}</td>
<td>{{ entry.audit_statement }}</td>
<td>{{ entry.audit_usr_id|default('') }}</td>
<td>{{ entry.audit_ses_id|default('') }}</td>
<td colspan="3" class="text-muted">{{ __('Registrar was deleted.') }}</td>
</tr>
{% endif %}
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'partials/footer.twig' %}
</div>
{% endblock %}

View file

@ -30,6 +30,13 @@
<!-- Page title actions --> <!-- Page title actions -->
<div class="col-auto ms-auto d-print-none"> <div class="col-auto ms-auto d-print-none">
<div class="btn-list"> <div class="btn-list">
<a href="/registrar/history/{{ registrar.clid }}" class="btn btn-outline-purple d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 8l0 4l2 2" /><path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" /></svg>
{{ __('Registrar History') }}
</a>
<a href="/registrar/history/{{ registrar.clid }}" class="btn btn-outline-purple d-sm-none btn-icon" aria-label="{{ __('Registrar History') }}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 8l0 4l2 2" /><path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" /></svg>
</a>
<a href="/registrar/update/{{ registrar.clid }}" class="btn btn-primary d-none d-sm-inline-block"> <a href="/registrar/update/{{ registrar.clid }}" class="btn btn-primary d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1" /><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z" /><path d="M16 5l3 3" /></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1" /><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z" /><path d="M16 5l3 3" /></svg>
{{ __('Update Registrar') }} {{ __('Update Registrar') }}

View file

@ -78,6 +78,7 @@ $app->group('', function ($route) {
$route->map(['GET', 'POST'], '/contact/create', ContactsController::class . ':createContact')->setName('createContact'); $route->map(['GET', 'POST'], '/contact/create', ContactsController::class . ':createContact')->setName('createContact');
$route->map(['GET', 'POST'], '/contact/create-api', ContactsController::class . ':createContactApi')->setName('createContactApi'); $route->map(['GET', 'POST'], '/contact/create-api', ContactsController::class . ':createContactApi')->setName('createContactApi');
$route->get('/contact/view/{contact}', ContactsController::class . ':viewContact')->setName('viewContact'); $route->get('/contact/view/{contact}', ContactsController::class . ':viewContact')->setName('viewContact');
$route->get('/contact/history/{contact}', ContactsController::class . ':historyContact')->setName('historyContact');
$route->get('/contact/update/{contact}', ContactsController::class . ':updateContact')->setName('updateContact'); $route->get('/contact/update/{contact}', ContactsController::class . ':updateContact')->setName('updateContact');
$route->get('/contact/validate/{contact}', ContactsController::class . ':validateContact')->setName('validateContact'); $route->get('/contact/validate/{contact}', ContactsController::class . ':validateContact')->setName('validateContact');
$route->post('/contact/update', ContactsController::class . ':updateContactProcess')->setName('updateContactProcess'); $route->post('/contact/update', ContactsController::class . ':updateContactProcess')->setName('updateContactProcess');
@ -87,6 +88,7 @@ $app->group('', function ($route) {
$route->get('/hosts', HostsController::class .':listHosts')->setName('listHosts'); $route->get('/hosts', HostsController::class .':listHosts')->setName('listHosts');
$route->map(['GET', 'POST'], '/host/create', HostsController::class . ':createHost')->setName('createHost'); $route->map(['GET', 'POST'], '/host/create', HostsController::class . ':createHost')->setName('createHost');
$route->get('/host/view/{host}', HostsController::class . ':viewHost')->setName('viewHost'); $route->get('/host/view/{host}', HostsController::class . ':viewHost')->setName('viewHost');
$route->get('/host/history/{host}', HostsController::class . ':historyHost')->setName('historyHost');
$route->get('/host/update/{host}', HostsController::class . ':updateHost')->setName('updateHost'); $route->get('/host/update/{host}', HostsController::class . ':updateHost')->setName('updateHost');
$route->post('/host/update', HostsController::class . ':updateHostProcess')->setName('updateHostProcess'); $route->post('/host/update', HostsController::class . ':updateHostProcess')->setName('updateHostProcess');
$route->map(['GET', 'POST'], '/host/delete/{host}', HostsController::class . ':deleteHost')->setName('deleteHost'); $route->map(['GET', 'POST'], '/host/delete/{host}', HostsController::class . ':deleteHost')->setName('deleteHost');
@ -94,6 +96,7 @@ $app->group('', function ($route) {
$route->get('/registrars', RegistrarsController::class .':view')->setName('registrars'); $route->get('/registrars', RegistrarsController::class .':view')->setName('registrars');
$route->map(['GET', 'POST'], '/registrar/create', RegistrarsController::class . ':create')->setName('registrarcreate'); $route->map(['GET', 'POST'], '/registrar/create', RegistrarsController::class . ':create')->setName('registrarcreate');
$route->get('/registrar/view/{registrar}', RegistrarsController::class . ':viewRegistrar')->setName('viewRegistrar'); $route->get('/registrar/view/{registrar}', RegistrarsController::class . ':viewRegistrar')->setName('viewRegistrar');
$route->get('/registrar/history/{registrar}', RegistrarsController::class . ':historyRegistrar')->setName('historyRegistrar');
$route->get('/registrar/update/{registrar}', RegistrarsController::class . ':updateRegistrar')->setName('updateRegistrar'); $route->get('/registrar/update/{registrar}', RegistrarsController::class . ':updateRegistrar')->setName('updateRegistrar');
$route->get('/registrar/pricing/{registrar}', RegistrarsController::class . ':customPricingView')->setName('customPricingView'); $route->get('/registrar/pricing/{registrar}', RegistrarsController::class . ':customPricingView')->setName('customPricingView');
$route->map(['POST', 'DELETE'], '/registrar/updatepricing/{registrar}', RegistrarsController::class . ':updateCustomPricing')->setName('updateCustomPricing'); $route->map(['POST', 'DELETE'], '/registrar/updatepricing/{registrar}', RegistrarsController::class . ':updateCustomPricing')->setName('updateCustomPricing');