mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-22 01:49:05 +02:00
Added basic log entry info page
This commit is contained in:
parent
e0e5fb3d5a
commit
4107535b19
8 changed files with 339 additions and 136 deletions
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\DataTables\Column\LogEntryTargetColumn;
|
||||
use App\DataTables\Filters\LogFilter;
|
||||
use App\DataTables\LogDataTable;
|
||||
use App\Entity\Base\AbstractDBElement;
|
||||
|
@ -33,6 +34,9 @@ use App\Entity\LogSystem\ElementEditedLogEntry;
|
|||
use App\Form\Filters\LogFilterType;
|
||||
use App\Repository\DBElementRepository;
|
||||
use App\Services\LogSystem\EventUndoHelper;
|
||||
use App\Services\LogSystem\LogEntryExtraFormatter;
|
||||
use App\Services\LogSystem\LogLevelHelper;
|
||||
use App\Services\LogSystem\LogTargetHelper;
|
||||
use App\Services\LogSystem\TimeTravel;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
@ -93,6 +97,28 @@ class LogController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}/details", name="log_details")
|
||||
* @param Request $request
|
||||
* @param AbstractLogEntry $logEntry
|
||||
* @return Response
|
||||
*/
|
||||
public function logDetails(Request $request, AbstractLogEntry $logEntry, LogEntryExtraFormatter $logEntryExtraFormatter,
|
||||
LogLevelHelper $logLevelHelper, LogTargetHelper $logTargetHelper): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('read', $logEntry);
|
||||
|
||||
$extra_html = $logEntryExtraFormatter->format($logEntry);
|
||||
$target_html = $logTargetHelper->formatTarget($logEntry);
|
||||
|
||||
return $this->render('log_system/details/log_details.html.twig', [
|
||||
'log_entry' => $logEntry,
|
||||
'extra_html' => $extra_html,
|
||||
'target_html' => $target_html,
|
||||
'log_level_helper' => $logLevelHelper,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/undo", name="log_undo", methods={"POST"})
|
||||
*/
|
||||
|
|
|
@ -36,6 +36,7 @@ use App\Exceptions\EntityNotSupportedException;
|
|||
use App\Repository\LogEntryRepository;
|
||||
use App\Services\ElementTypeNameGenerator;
|
||||
use App\Services\EntityURLGenerator;
|
||||
use App\Services\LogSystem\LogTargetHelper;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Omines\DataTablesBundle\Column\AbstractColumn;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
@ -43,21 +44,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
|||
|
||||
class LogEntryTargetColumn extends AbstractColumn
|
||||
{
|
||||
protected EntityManagerInterface $em;
|
||||
protected LogEntryRepository $entryRepository;
|
||||
protected EntityURLGenerator $entityURLGenerator;
|
||||
protected ElementTypeNameGenerator $elementTypeNameGenerator;
|
||||
protected TranslatorInterface $translator;
|
||||
private LogTargetHelper $logTargetHelper;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, EntityURLGenerator $entityURLGenerator,
|
||||
ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator)
|
||||
public function __construct(LogTargetHelper $logTargetHelper)
|
||||
{
|
||||
$this->em = $entityManager;
|
||||
$this->entryRepository = $entityManager->getRepository(AbstractLogEntry::class);
|
||||
|
||||
$this->entityURLGenerator = $entityURLGenerator;
|
||||
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
|
||||
$this->translator = $translator;
|
||||
$this->logTargetHelper = $logTargetHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,71 +71,9 @@ class LogEntryTargetColumn extends AbstractColumn
|
|||
|
||||
public function render($value, $context): string
|
||||
{
|
||||
if ($context instanceof UserNotAllowedLogEntry && $this->options['showAccessDeniedPath']) {
|
||||
return htmlspecialchars($context->getPath());
|
||||
}
|
||||
|
||||
/** @var AbstractLogEntry $context */
|
||||
$target = $this->entryRepository->getTargetElement($context);
|
||||
|
||||
$tmp = '';
|
||||
|
||||
//The element is existing
|
||||
if ($target instanceof NamedElementInterface && !empty($target->getName())) {
|
||||
try {
|
||||
$tmp = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
$this->entityURLGenerator->infoURL($target),
|
||||
$this->elementTypeNameGenerator->getTypeNameCombination($target, true)
|
||||
);
|
||||
} catch (EntityNotSupportedException $exception) {
|
||||
$tmp = $this->elementTypeNameGenerator->getTypeNameCombination($target, true);
|
||||
}
|
||||
} elseif ($target instanceof AbstractDBElement) { //Target does not have a name
|
||||
$tmp = sprintf(
|
||||
'<i>%s</i>: %s',
|
||||
$this->elementTypeNameGenerator->getLocalizedTypeLabel($target),
|
||||
$target->getID()
|
||||
);
|
||||
} elseif (null === $target && $context->hasTarget()) { //Element was deleted
|
||||
$tmp = sprintf(
|
||||
'<i>%s</i>: %s [%s]',
|
||||
$this->elementTypeNameGenerator->getLocalizedTypeLabel($context->getTargetClass()),
|
||||
$context->getTargetID(),
|
||||
$this->translator->trans('log.target_deleted')
|
||||
);
|
||||
}
|
||||
|
||||
//Add a hint to the associated element if possible
|
||||
if (null !== $target && $this->options['show_associated']) {
|
||||
if ($target instanceof Attachment && null !== $target->getElement()) {
|
||||
$on = $target->getElement();
|
||||
} elseif ($target instanceof AbstractParameter && null !== $target->getElement()) {
|
||||
$on = $target->getElement();
|
||||
} elseif ($target instanceof PartLot && null !== $target->getPart()) {
|
||||
$on = $target->getPart();
|
||||
} elseif ($target instanceof Orderdetail && null !== $target->getPart()) {
|
||||
$on = $target->getPart();
|
||||
} elseif ($target instanceof Pricedetail && null !== $target->getOrderdetail() && null !== $target->getOrderdetail()->getPart()) {
|
||||
$on = $target->getOrderdetail()->getPart();
|
||||
} elseif ($target instanceof ProjectBOMEntry && null !== $target->getProject()) {
|
||||
$on = $target->getProject();
|
||||
}
|
||||
|
||||
if (isset($on) && is_object($on)) {
|
||||
try {
|
||||
$tmp .= sprintf(
|
||||
' (<a href="%s">%s</a>)',
|
||||
$this->entityURLGenerator->infoURL($on),
|
||||
$this->elementTypeNameGenerator->getTypeNameCombination($on, true)
|
||||
);
|
||||
} catch (EntityNotSupportedException $exception) {
|
||||
$tmp .= ' ('.$this->elementTypeNameGenerator->getTypeNameCombination($target, true).')';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Log is not associated with an element
|
||||
return $tmp;
|
||||
return $this->logTargetHelper->formatTarget($context, [
|
||||
'showAccessDeniedPath' => $this->options['showAccessDeniedPath'],
|
||||
'show_associated' => $this->options['show_associated'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ use App\Exceptions\EntityNotSupportedException;
|
|||
use App\Repository\LogEntryRepository;
|
||||
use App\Services\ElementTypeNameGenerator;
|
||||
use App\Services\EntityURLGenerator;
|
||||
use App\Services\LogSystem\LogLevelHelper;
|
||||
use App\Services\UserSystem\UserAvatarHelper;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
@ -70,10 +71,11 @@ class LogDataTable implements DataTableTypeInterface
|
|||
protected LogEntryRepository $logRepo;
|
||||
protected Security $security;
|
||||
protected UserAvatarHelper $userAvatarHelper;
|
||||
protected LogLevelHelper $logLevelHelper;
|
||||
|
||||
public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator,
|
||||
UrlGeneratorInterface $urlGenerator, EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager,
|
||||
Security $security, UserAvatarHelper $userAvatarHelper)
|
||||
Security $security, UserAvatarHelper $userAvatarHelper, LogLevelHelper $logLevelHelper)
|
||||
{
|
||||
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
|
||||
$this->translator = $translator;
|
||||
|
@ -82,6 +84,7 @@ class LogDataTable implements DataTableTypeInterface
|
|||
$this->logRepo = $entityManager->getRepository(AbstractLogEntry::class);
|
||||
$this->security = $security;
|
||||
$this->userAvatarHelper = $userAvatarHelper;
|
||||
$this->logLevelHelper = $logLevelHelper;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $optionsResolver): void
|
||||
|
@ -115,69 +118,18 @@ class LogDataTable implements DataTableTypeInterface
|
|||
|
||||
//This special $$rowClass column is used to set the row class depending on the log level. The class gets set by the frontend controller
|
||||
$dataTable->add('dont_matter', RowClassColumn::class, [
|
||||
'render' => static function ($value, AbstractLogEntry $context) {
|
||||
switch ($context->getLevel()) {
|
||||
case AbstractLogEntry::LEVEL_EMERGENCY:
|
||||
case AbstractLogEntry::LEVEL_ALERT:
|
||||
case AbstractLogEntry::LEVEL_CRITICAL:
|
||||
case AbstractLogEntry::LEVEL_ERROR:
|
||||
return 'table-danger';
|
||||
case AbstractLogEntry::LEVEL_WARNING:
|
||||
return 'table-warning';
|
||||
case AbstractLogEntry::LEVEL_NOTICE:
|
||||
return 'table-info';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
'render' => function ($value, AbstractLogEntry $context) {
|
||||
return $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString());
|
||||
},
|
||||
]);
|
||||
|
||||
$dataTable->add('symbol', TextColumn::class, [
|
||||
'label' => '',
|
||||
'className' => 'no-colvis',
|
||||
'render' => static function ($value, AbstractLogEntry $context) {
|
||||
switch ($context->getLevelString()) {
|
||||
case LogLevel::DEBUG:
|
||||
$symbol = 'fa-bug';
|
||||
|
||||
break;
|
||||
case LogLevel::INFO:
|
||||
$symbol = 'fa-info';
|
||||
|
||||
break;
|
||||
case LogLevel::NOTICE:
|
||||
$symbol = 'fa-flag';
|
||||
|
||||
break;
|
||||
case LogLevel::WARNING:
|
||||
$symbol = 'fa-exclamation-circle';
|
||||
|
||||
break;
|
||||
case LogLevel::ERROR:
|
||||
$symbol = 'fa-exclamation-triangle';
|
||||
|
||||
break;
|
||||
case LogLevel::CRITICAL:
|
||||
$symbol = 'fa-bolt';
|
||||
|
||||
break;
|
||||
case LogLevel::ALERT:
|
||||
$symbol = 'fa-radiation';
|
||||
|
||||
break;
|
||||
case LogLevel::EMERGENCY:
|
||||
$symbol = 'fa-skull-crossbones';
|
||||
|
||||
break;
|
||||
default:
|
||||
$symbol = 'fa-question-circle';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
'render' => function ($value, AbstractLogEntry $context) {
|
||||
return sprintf(
|
||||
'<i class="fas fa-fw %s" title="%s"></i>',
|
||||
$symbol,
|
||||
$this->logLevelHelper->logLevelToIconClass($context->getLevelString()),
|
||||
$context->getLevelString()
|
||||
);
|
||||
},
|
||||
|
@ -191,6 +143,12 @@ class LogDataTable implements DataTableTypeInterface
|
|||
$dataTable->add('timestamp', LocaleDateTimeColumn::class, [
|
||||
'label' => 'log.timestamp',
|
||||
'timeFormat' => 'medium',
|
||||
'render' => function (string $value, AbstractLogEntry $context) {
|
||||
return sprintf('<a href="%s">%s</a>',
|
||||
$this->urlGenerator->generate('log_details', ['id' => $context->getId()]),
|
||||
$value
|
||||
);
|
||||
}
|
||||
]);
|
||||
|
||||
$dataTable->add('type', TextColumn::class, [
|
||||
|
|
|
@ -53,7 +53,7 @@ class LogEntryVoter extends ExtendedVoter
|
|||
protected function supports($attribute, $subject): bool
|
||||
{
|
||||
if ($subject instanceof AbstractLogEntry) {
|
||||
return in_array($subject, static::ALLOWED_OPS, true);
|
||||
return in_array($attribute, static::ALLOWED_OPS, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
80
src/Services/LogSystem/LogLevelHelper.php
Normal file
80
src/Services/LogSystem/LogLevelHelper.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\LogSystem;
|
||||
|
||||
use App\Entity\LogSystem\AbstractLogEntry;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
class LogLevelHelper
|
||||
{
|
||||
/**
|
||||
* Returns the FontAwesome icon class for the given log level.
|
||||
* This returns just the specific icon class (so 'fa-info' for example).
|
||||
* @param string $logLevel The string representation of the log level (one of the LogLevel::* constants)
|
||||
* @return string
|
||||
*/
|
||||
public function logLevelToIconClass(string $logLevel): string
|
||||
{
|
||||
switch ($logLevel) {
|
||||
case LogLevel::DEBUG:
|
||||
return 'fa-bug';
|
||||
case LogLevel::INFO:
|
||||
return 'fa-info';
|
||||
case LogLevel::NOTICE:
|
||||
return 'fa-flag';
|
||||
case LogLevel::WARNING:
|
||||
return 'fa-exclamation-circle';
|
||||
case LogLevel::ERROR:
|
||||
return 'fa-exclamation-triangle';
|
||||
case LogLevel::CRITICAL:
|
||||
return 'fa-bolt';
|
||||
case LogLevel::ALERT:
|
||||
return 'fa-radiation';
|
||||
case LogLevel::EMERGENCY:
|
||||
return 'fa-skull-crossbones';
|
||||
default:
|
||||
return 'fa-question-circle';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Bootstrap table color class for the given log level.
|
||||
* @param string $logLevel The string representation of the log level (one of the LogLevel::* constants)
|
||||
* @return string The table color class (one of the 'table-*' classes)
|
||||
*/
|
||||
public function logLevelToTableColorClass(string $logLevel): string
|
||||
{
|
||||
|
||||
switch ($logLevel) {
|
||||
case LogLevel::EMERGENCY:
|
||||
case LogLevel::ALERT:
|
||||
case LogLevel::CRITICAL:
|
||||
case LogLevel::ERROR:
|
||||
return 'table-danger';
|
||||
case LogLevel::WARNING:
|
||||
return 'table-warning';
|
||||
case LogLevel::NOTICE:
|
||||
return 'table-info';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
141
src/Services/LogSystem/LogTargetHelper.php
Normal file
141
src/Services/LogSystem/LogTargetHelper.php
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\LogSystem;
|
||||
|
||||
use App\Entity\Attachments\Attachment;
|
||||
use App\Entity\Base\AbstractDBElement;
|
||||
use App\Entity\Contracts\NamedElementInterface;
|
||||
use App\Entity\LogSystem\AbstractLogEntry;
|
||||
use App\Entity\LogSystem\UserNotAllowedLogEntry;
|
||||
use App\Entity\Parameters\AbstractParameter;
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Entity\PriceInformations\Orderdetail;
|
||||
use App\Entity\PriceInformations\Pricedetail;
|
||||
use App\Entity\ProjectSystem\ProjectBOMEntry;
|
||||
use App\Exceptions\EntityNotSupportedException;
|
||||
use App\Repository\LogEntryRepository;
|
||||
use App\Services\ElementTypeNameGenerator;
|
||||
use App\Services\EntityURLGenerator;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class LogTargetHelper
|
||||
{
|
||||
protected EntityManagerInterface $em;
|
||||
protected LogEntryRepository $entryRepository;
|
||||
protected EntityURLGenerator $entityURLGenerator;
|
||||
protected ElementTypeNameGenerator $elementTypeNameGenerator;
|
||||
protected TranslatorInterface $translator;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, EntityURLGenerator $entityURLGenerator,
|
||||
ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator)
|
||||
{
|
||||
$this->em = $entityManager;
|
||||
$this->entryRepository = $entityManager->getRepository(AbstractLogEntry::class);
|
||||
|
||||
$this->entityURLGenerator = $entityURLGenerator;
|
||||
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
private function configureOptions(OptionsResolver $resolver): self
|
||||
{
|
||||
$resolver->setDefault('show_associated', true);
|
||||
$resolver->setDefault('showAccessDeniedPath', true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function formatTarget(AbstractLogEntry $context, array $options = []): string
|
||||
{
|
||||
$optionsResolver = new OptionsResolver();
|
||||
$this->configureOptions($optionsResolver);
|
||||
$options = $optionsResolver->resolve($options);
|
||||
|
||||
if ($context instanceof UserNotAllowedLogEntry && $options['showAccessDeniedPath']) {
|
||||
return htmlspecialchars($context->getPath());
|
||||
}
|
||||
|
||||
/** @var AbstractLogEntry $context */
|
||||
$target = $this->entryRepository->getTargetElement($context);
|
||||
|
||||
$tmp = '';
|
||||
|
||||
//The element is existing
|
||||
if ($target instanceof NamedElementInterface && !empty($target->getName())) {
|
||||
try {
|
||||
$tmp = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
$this->entityURLGenerator->infoURL($target),
|
||||
$this->elementTypeNameGenerator->getTypeNameCombination($target, true)
|
||||
);
|
||||
} catch (EntityNotSupportedException $exception) {
|
||||
$tmp = $this->elementTypeNameGenerator->getTypeNameCombination($target, true);
|
||||
}
|
||||
} elseif ($target instanceof AbstractDBElement) { //Target does not have a name
|
||||
$tmp = sprintf(
|
||||
'<i>%s</i>: %s',
|
||||
$this->elementTypeNameGenerator->getLocalizedTypeLabel($target),
|
||||
$target->getID()
|
||||
);
|
||||
} elseif (null === $target && $context->hasTarget()) { //Element was deleted
|
||||
$tmp = sprintf(
|
||||
'<i>%s</i>: %s [%s]',
|
||||
$this->elementTypeNameGenerator->getLocalizedTypeLabel($context->getTargetClass()),
|
||||
$context->getTargetID(),
|
||||
$this->translator->trans('log.target_deleted')
|
||||
);
|
||||
}
|
||||
|
||||
//Add a hint to the associated element if possible
|
||||
if (null !== $target && $options['show_associated']) {
|
||||
if ($target instanceof Attachment && null !== $target->getElement()) {
|
||||
$on = $target->getElement();
|
||||
} elseif ($target instanceof AbstractParameter && null !== $target->getElement()) {
|
||||
$on = $target->getElement();
|
||||
} elseif ($target instanceof PartLot && null !== $target->getPart()) {
|
||||
$on = $target->getPart();
|
||||
} elseif ($target instanceof Orderdetail && null !== $target->getPart()) {
|
||||
$on = $target->getPart();
|
||||
} elseif ($target instanceof Pricedetail && null !== $target->getOrderdetail() && null !== $target->getOrderdetail()->getPart()) {
|
||||
$on = $target->getOrderdetail()->getPart();
|
||||
} elseif ($target instanceof ProjectBOMEntry && null !== $target->getProject()) {
|
||||
$on = $target->getProject();
|
||||
}
|
||||
|
||||
if (isset($on) && is_object($on)) {
|
||||
try {
|
||||
$tmp .= sprintf(
|
||||
' (<a href="%s">%s</a>)',
|
||||
$this->entityURLGenerator->infoURL($on),
|
||||
$this->elementTypeNameGenerator->getTypeNameCombination($on, true)
|
||||
);
|
||||
} catch (EntityNotSupportedException $exception) {
|
||||
$tmp .= ' ('.$this->elementTypeNameGenerator->getTypeNameCombination($target, true).')';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Log is not associated with an element
|
||||
return $tmp;
|
||||
}
|
||||
}
|
63
templates/log_system/details/log_details.html.twig
Normal file
63
templates/log_system/details/log_details.html.twig
Normal file
|
@ -0,0 +1,63 @@
|
|||
{% extends "main_card.html.twig" %}
|
||||
|
||||
{% import "helper.twig" as helper %}
|
||||
|
||||
{% block title %}
|
||||
{% trans %}log.details.title{% endtrans %}:
|
||||
{{ ('log.type.' ~ log_entry.type) | trans }} ({{ log_entry.timestamp | format_datetime('short') }})
|
||||
{% endblock %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fas fa-binoculars"></i>
|
||||
{% trans %}log.details.title{% endtrans %}:
|
||||
<i>{{ ('log.type.' ~ log_entry.type) | trans }}</i> ({{ log_entry.timestamp | format_datetime('short') }})
|
||||
<span class="float-end">ID: {{ log_entry.iD }}</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block card_body %}
|
||||
<table class="table table-striped table-hover mb-0 {{ log_level_helper.logLevelToTableColorClass(log_entry.levelString) }}">
|
||||
<tr>
|
||||
<td>{% trans %}log.timestamp{% endtrans %}</td>
|
||||
<td>{{ log_entry.timestamp | format_datetime('full') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans %}log.type{% endtrans %}</td>
|
||||
<td>
|
||||
{{ ('log.type.' ~ log_entry.type) | trans }}
|
||||
{% if log_entry.type == 'part_stock_changed' %}
|
||||
({{ ('log.part_stock_changed.' ~ log_entry.instockChangeType)|trans }})
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans %}log.level{% endtrans %}</td>
|
||||
<td>
|
||||
<i class="fa-solid {{ log_level_helper.logLevelToIconClass(log_entry.levelString) }} fa-fw"></i>
|
||||
{{ ('log.level.'~ log_entry.levelString)|trans }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans %}log.user{% endtrans %}
|
||||
<td>
|
||||
{% if log_entry.cLIEntry %}
|
||||
<i class="fa-solid fa-terminal"></i>
|
||||
{{ log_entry.cLIUsername }} ({% trans %}log.cli_user{% endtrans %})
|
||||
{% else %}
|
||||
{% if log_entry.user %}
|
||||
{{ helper.user_icon_link(log_entry.user) }} (@{{ log_entry.user.username }})
|
||||
{% else %}
|
||||
@{{ log_entry.username }} ({% trans %}log.target_deleted{% endtrans %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans %}log.target{% endtrans %}</td>
|
||||
<td>{{ target_html|raw }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="card-body">
|
||||
{{ extra_html | raw }}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -11289,5 +11289,11 @@ Element 3</target>
|
|||
<target>Show email on public profile page</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="4rkjIk2" name="log.details.title">
|
||||
<segment>
|
||||
<source>log.details.title</source>
|
||||
<target>Log details</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue