mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-22 09:53:35 +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;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\DataTables\Column\LogEntryTargetColumn;
|
||||||
use App\DataTables\Filters\LogFilter;
|
use App\DataTables\Filters\LogFilter;
|
||||||
use App\DataTables\LogDataTable;
|
use App\DataTables\LogDataTable;
|
||||||
use App\Entity\Base\AbstractDBElement;
|
use App\Entity\Base\AbstractDBElement;
|
||||||
|
@ -33,6 +34,9 @@ use App\Entity\LogSystem\ElementEditedLogEntry;
|
||||||
use App\Form\Filters\LogFilterType;
|
use App\Form\Filters\LogFilterType;
|
||||||
use App\Repository\DBElementRepository;
|
use App\Repository\DBElementRepository;
|
||||||
use App\Services\LogSystem\EventUndoHelper;
|
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 App\Services\LogSystem\TimeTravel;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
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"})
|
* @Route("/undo", name="log_undo", methods={"POST"})
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -36,6 +36,7 @@ use App\Exceptions\EntityNotSupportedException;
|
||||||
use App\Repository\LogEntryRepository;
|
use App\Repository\LogEntryRepository;
|
||||||
use App\Services\ElementTypeNameGenerator;
|
use App\Services\ElementTypeNameGenerator;
|
||||||
use App\Services\EntityURLGenerator;
|
use App\Services\EntityURLGenerator;
|
||||||
|
use App\Services\LogSystem\LogTargetHelper;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Omines\DataTablesBundle\Column\AbstractColumn;
|
use Omines\DataTablesBundle\Column\AbstractColumn;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
@ -43,21 +44,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class LogEntryTargetColumn extends AbstractColumn
|
class LogEntryTargetColumn extends AbstractColumn
|
||||||
{
|
{
|
||||||
protected EntityManagerInterface $em;
|
private LogTargetHelper $logTargetHelper;
|
||||||
protected LogEntryRepository $entryRepository;
|
|
||||||
protected EntityURLGenerator $entityURLGenerator;
|
|
||||||
protected ElementTypeNameGenerator $elementTypeNameGenerator;
|
|
||||||
protected TranslatorInterface $translator;
|
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager, EntityURLGenerator $entityURLGenerator,
|
public function __construct(LogTargetHelper $logTargetHelper)
|
||||||
ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator)
|
|
||||||
{
|
{
|
||||||
$this->em = $entityManager;
|
$this->logTargetHelper = $logTargetHelper;
|
||||||
$this->entryRepository = $entityManager->getRepository(AbstractLogEntry::class);
|
|
||||||
|
|
||||||
$this->entityURLGenerator = $entityURLGenerator;
|
|
||||||
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
|
|
||||||
$this->translator = $translator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,71 +71,9 @@ class LogEntryTargetColumn extends AbstractColumn
|
||||||
|
|
||||||
public function render($value, $context): string
|
public function render($value, $context): string
|
||||||
{
|
{
|
||||||
if ($context instanceof UserNotAllowedLogEntry && $this->options['showAccessDeniedPath']) {
|
return $this->logTargetHelper->formatTarget($context, [
|
||||||
return htmlspecialchars($context->getPath());
|
'showAccessDeniedPath' => $this->options['showAccessDeniedPath'],
|
||||||
}
|
'show_associated' => $this->options['show_associated'],
|
||||||
|
]);
|
||||||
/** @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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ use App\Exceptions\EntityNotSupportedException;
|
||||||
use App\Repository\LogEntryRepository;
|
use App\Repository\LogEntryRepository;
|
||||||
use App\Services\ElementTypeNameGenerator;
|
use App\Services\ElementTypeNameGenerator;
|
||||||
use App\Services\EntityURLGenerator;
|
use App\Services\EntityURLGenerator;
|
||||||
|
use App\Services\LogSystem\LogLevelHelper;
|
||||||
use App\Services\UserSystem\UserAvatarHelper;
|
use App\Services\UserSystem\UserAvatarHelper;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
@ -70,10 +71,11 @@ class LogDataTable implements DataTableTypeInterface
|
||||||
protected LogEntryRepository $logRepo;
|
protected LogEntryRepository $logRepo;
|
||||||
protected Security $security;
|
protected Security $security;
|
||||||
protected UserAvatarHelper $userAvatarHelper;
|
protected UserAvatarHelper $userAvatarHelper;
|
||||||
|
protected LogLevelHelper $logLevelHelper;
|
||||||
|
|
||||||
public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator,
|
public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator,
|
||||||
UrlGeneratorInterface $urlGenerator, EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager,
|
UrlGeneratorInterface $urlGenerator, EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager,
|
||||||
Security $security, UserAvatarHelper $userAvatarHelper)
|
Security $security, UserAvatarHelper $userAvatarHelper, LogLevelHelper $logLevelHelper)
|
||||||
{
|
{
|
||||||
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
|
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
|
@ -82,6 +84,7 @@ class LogDataTable implements DataTableTypeInterface
|
||||||
$this->logRepo = $entityManager->getRepository(AbstractLogEntry::class);
|
$this->logRepo = $entityManager->getRepository(AbstractLogEntry::class);
|
||||||
$this->security = $security;
|
$this->security = $security;
|
||||||
$this->userAvatarHelper = $userAvatarHelper;
|
$this->userAvatarHelper = $userAvatarHelper;
|
||||||
|
$this->logLevelHelper = $logLevelHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $optionsResolver): void
|
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
|
//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, [
|
$dataTable->add('dont_matter', RowClassColumn::class, [
|
||||||
'render' => static function ($value, AbstractLogEntry $context) {
|
'render' => function ($value, AbstractLogEntry $context) {
|
||||||
switch ($context->getLevel()) {
|
return $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString());
|
||||||
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 '';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$dataTable->add('symbol', TextColumn::class, [
|
$dataTable->add('symbol', TextColumn::class, [
|
||||||
'label' => '',
|
'label' => '',
|
||||||
'className' => 'no-colvis',
|
'className' => 'no-colvis',
|
||||||
'render' => static function ($value, AbstractLogEntry $context) {
|
'render' => 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sprintf(
|
return sprintf(
|
||||||
'<i class="fas fa-fw %s" title="%s"></i>',
|
'<i class="fas fa-fw %s" title="%s"></i>',
|
||||||
$symbol,
|
$this->logLevelHelper->logLevelToIconClass($context->getLevelString()),
|
||||||
$context->getLevelString()
|
$context->getLevelString()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -191,6 +143,12 @@ class LogDataTable implements DataTableTypeInterface
|
||||||
$dataTable->add('timestamp', LocaleDateTimeColumn::class, [
|
$dataTable->add('timestamp', LocaleDateTimeColumn::class, [
|
||||||
'label' => 'log.timestamp',
|
'label' => 'log.timestamp',
|
||||||
'timeFormat' => 'medium',
|
'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, [
|
$dataTable->add('type', TextColumn::class, [
|
||||||
|
|
|
@ -53,7 +53,7 @@ class LogEntryVoter extends ExtendedVoter
|
||||||
protected function supports($attribute, $subject): bool
|
protected function supports($attribute, $subject): bool
|
||||||
{
|
{
|
||||||
if ($subject instanceof AbstractLogEntry) {
|
if ($subject instanceof AbstractLogEntry) {
|
||||||
return in_array($subject, static::ALLOWED_OPS, true);
|
return in_array($attribute, static::ALLOWED_OPS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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>
|
<target>Show email on public profile page</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
|
<unit id="4rkjIk2" name="log.details.title">
|
||||||
|
<segment>
|
||||||
|
<source>log.details.title</source>
|
||||||
|
<target>Log details</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue