diff --git a/src/Command/ShowEventLogCommand.php b/src/Command/ShowEventLogCommand.php
new file mode 100644
index 00000000..e9cc82f0
--- /dev/null
+++ b/src/Command/ShowEventLogCommand.php
@@ -0,0 +1,160 @@
+entityManager = $entityManager;
+ $this->translator = $translator;
+ $this->elementTypeNameGenerator = $elementTypeNameGenerator;
+ $this->formatter = $formatter;
+
+ $this->repo = $this->entityManager->getRepository(AbstractLogEntry::class);
+ parent::__construct();
+ }
+
+ protected function configure()
+ {
+ $this
+ ->setDescription('List the last event log entries.')
+ ->addOption('count', 'c', InputOption::VALUE_REQUIRED, 'How many log entries should be shown per page.', 50 )
+ ->addOption('oldest_first', null, InputOption::VALUE_NONE,'Show older entries first.')
+ ->addOption('page', 'p', InputOption::VALUE_REQUIRED, 'Which page should be shown?', 1)
+ ->addOption('onePage', null, InputOption::VALUE_NONE, 'Show only one page (dont ask to go to next).')
+ ->addOption('showExtra', 'x', InputOption::VALUE_NONE, 'Show a column with the extra data.');
+ ;
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ $io = new SymfonyStyle($input, $output);
+
+ $onePage = $input->getOption('onePage');
+
+ $desc = $input->getOption('oldest_first');
+ $limit = $input->getOption('count');
+ $page = $input->getOption('page');
+ $showExtra = $input->getOption('showExtra');
+
+ $total_count = $this->repo->count([]);
+ $max_page = ceil($total_count / $limit);
+
+ if ($page > $max_page) {
+ $io->error("There is no page $page! The maximum page is $max_page.");
+ return 1;
+ }
+
+ $io->note("There are a total of $total_count log entries in the DB.");
+
+ $continue = true;
+ while ($continue && $page <= $max_page) {
+ $this->showPage($output, $desc, $limit, $page, $max_page, $showExtra);
+
+ if ($onePage) {
+ return 0;
+ }
+
+ $continue = $io->confirm('Do you want to show the next page?');
+ $page++;
+ }
+
+ return 0;
+ }
+
+ protected function showPage(OutputInterface $output, bool $desc, int $limit, int $page, int $max_page, bool $showExtra): void
+ {
+ $sorting = $desc ? 'ASC' : 'DESC';
+ $offset = ($page - 1) * $limit;
+
+ /** @var AbstractLogEntry[] $entries */
+ $entries = $this->repo->getLogsOrderedByTimestamp($sorting, $limit, $offset);
+
+ $table = new Table($output);
+ $table->setHeaderTitle("Page $page / $max_page");
+ $headers = ['ID', 'Timestamp', 'Type', 'User', 'Target Type', 'Target'];
+ if ($showExtra) {
+ $headers[] = 'Extra data';
+ }
+ $table->setHeaders($headers);
+
+ foreach ($entries as $entry) {
+ $this->addTableRow($table, $entry, $showExtra);
+ }
+
+ $table->render();
+ }
+
+ protected function addTableRow(Table $table, AbstractLogEntry $entry, bool $showExtra): void
+ {
+ $target = $this->repo->getTargetElement($entry);
+ $target_name = "";
+ if ($target instanceof NamedDBElement) {
+ $target_name = $target->getName() . ' (' . $target->getID() . ')';
+ } elseif ($entry->getTargetID()) {
+ $target_name = '(' . $entry->getTargetID() . ')';
+ }
+
+ $target_class = "";
+ if ($entry->getTargetClass() !== null) {
+ $target_class = $this->elementTypeNameGenerator->getLocalizedTypeLabel($entry->getTargetClass());
+ }
+
+ $row = [
+ $entry->getID(),
+ $entry->getTimestamp()->format('Y-m-d H:i:s'),
+ $entry->getType(),
+ $entry->getUser()->getFullName(true),
+ $target_class,
+ $target_name
+ ];
+
+ if ($showExtra) {
+ $row[] = $this->formatter->formatConsole($entry);
+ }
+
+ $table->addRow($row);
+ }
+}
\ No newline at end of file
diff --git a/src/DataTables/Column/LogEntryExtraColumn.php b/src/DataTables/Column/LogEntryExtraColumn.php
index 42cf7d49..e3d9d5c9 100644
--- a/src/DataTables/Column/LogEntryExtraColumn.php
+++ b/src/DataTables/Column/LogEntryExtraColumn.php
@@ -31,16 +31,19 @@ use App\Entity\LogSystem\InstockChangedLogEntry;
use App\Entity\LogSystem\UserLoginLogEntry;
use App\Entity\LogSystem\UserLogoutLogEntry;
use App\Entity\LogSystem\UserNotAllowedLogEntry;
+use App\Services\LogSystem\LogEntryExtraFormatter;
use Omines\DataTablesBundle\Column\AbstractColumn;
use Symfony\Contracts\Translation\TranslatorInterface;
class LogEntryExtraColumn extends AbstractColumn
{
protected $translator;
+ protected $formatter;
- public function __construct(TranslatorInterface $translator)
+ public function __construct(TranslatorInterface $translator, LogEntryExtraFormatter $formatter)
{
$this->translator = $translator;
+ $this->formatter = $formatter;
}
/**
@@ -53,69 +56,6 @@ class LogEntryExtraColumn extends AbstractColumn
public function render($value, $context)
{
- if ($context instanceof UserLoginLogEntry || $context instanceof UserLogoutLogEntry) {
- return sprintf(
- "%s: %s",
- $this->translator->trans('log.user_login.ip'),
- htmlspecialchars($context->getIPAddress())
- );
- }
-
- if ($context instanceof ExceptionLogEntry) {
- return sprintf(
- '%s %s:%d : %s',
- htmlspecialchars($context->getExceptionClass()),
- htmlspecialchars($context->getFile()),
- $context->getLine(),
- htmlspecialchars($context->getMessage())
- );
- }
-
- if ($context instanceof DatabaseUpdatedLogEntry) {
- return sprintf(
- '%s %s %s',
- $this->translator->trans($context->isSuccessful() ? 'log.database_updated.success' : 'log.database_updated.failure'),
- $context->getOldVersion(),
- $context->getNewVersion()
- );
- }
-
- if ($context instanceof ElementCreatedLogEntry && $context->hasCreationInstockValue()) {
- return sprintf(
- '%s: %s',
- $this->translator->trans('log.element_created.original_instock'),
- $context->getCreationInstockValue()
- );
- }
-
- if ($context instanceof ElementDeletedLogEntry) {
- return sprintf(
- '%s: %s',
- $this->translator->trans('log.element_deleted.old_name'),
- $context->getOldName()
- );
- }
-
- if ($context instanceof ElementEditedLogEntry && !empty($context->getMessage())) {
- return htmlspecialchars($context->getMessage());
- }
-
- if ($context instanceof InstockChangedLogEntry) {
- return sprintf(
- '%s; %s %s (%s); %s: %s',
- $this->translator->trans($context->isWithdrawal() ? 'log.instock_changed.withdrawal' : 'log.instock_changed.added'),
- $context->getOldInstock(),
- $context->getNewInstock(),
- (!$context->isWithdrawal() ? '+' : '-') . $context->getDifference(true),
- $this->translator->trans('log.instock_changed.comment'),
- htmlspecialchars($context->getComment())
- );
- }
-
- if ($context instanceof UserNotAllowedLogEntry) {
- return htmlspecialchars($context->getMessage());
- }
-
- return "";
+ return $this->formatter->format($context);
}
}
\ No newline at end of file
diff --git a/src/Repository/LogEntryRepository.php b/src/Repository/LogEntryRepository.php
index e81ae101..e4742131 100644
--- a/src/Repository/LogEntryRepository.php
+++ b/src/Repository/LogEntryRepository.php
@@ -55,6 +55,18 @@ class LogEntryRepository extends EntityRepository
return $this->findBy(['element' => $element], ['timestamp' => $order], $limit, $offset);
}
+ /**
+ * Gets the last log entries ordered by timestamp
+ * @param string $order
+ * @param null $limit
+ * @param null $offset
+ * @return array
+ */
+ public function getLogsOrderedByTimestamp($order = 'DESC', $limit = null, $offset = null)
+ {
+ return $this->findBy([], ['timestamp' => $order], $limit, $offset);
+ }
+
/**
* Gets the target element associated with the logentry.
* @param AbstractLogEntry $logEntry
diff --git a/src/Services/LogSystem/LogEntryExtraFormatter.php b/src/Services/LogSystem/LogEntryExtraFormatter.php
new file mode 100644
index 00000000..5934d9e2
--- /dev/null
+++ b/src/Services/LogSystem/LogEntryExtraFormatter.php
@@ -0,0 +1,138 @@
+translator = $translator;
+ }
+
+ /**
+ * Return an user viewable representation of the extra data in a log entry, styled for console output.
+ * @param AbstractLogEntry $logEntry
+ * @return string
+ */
+ public function formatConsole(AbstractLogEntry $logEntry): string
+ {
+ $tmp = $this->format($logEntry);
+
+ //Just a simple tweak to make the console output more pretty.
+ $search = ['', '', '', '', ' '];
+ $replace = ['', '', '', '', '→'];
+
+ return str_replace($search, $replace, $tmp);
+ }
+
+ /**
+ * Return a HTML formatted string containing a user viewable form of the Extra data
+ * @param AbstractLogEntry $context
+ * @return string
+ */
+ public function format(AbstractLogEntry $context): string
+ {
+ if ($context instanceof UserLoginLogEntry || $context instanceof UserLogoutLogEntry) {
+ return sprintf(
+ "%s: %s",
+ $this->translator->trans('log.user_login.ip'),
+ htmlspecialchars($context->getIPAddress())
+ );
+ }
+
+ if ($context instanceof ExceptionLogEntry) {
+ return sprintf(
+ '%s %s:%d : %s',
+ htmlspecialchars($context->getExceptionClass()),
+ htmlspecialchars($context->getFile()),
+ $context->getLine(),
+ htmlspecialchars($context->getMessage())
+ );
+ }
+
+ if ($context instanceof DatabaseUpdatedLogEntry) {
+ return sprintf(
+ '%s %s %s',
+ $this->translator->trans($context->isSuccessful() ? 'log.database_updated.success' : 'log.database_updated.failure'),
+ $context->getOldVersion(),
+ $context->getNewVersion()
+ );
+ }
+
+ if ($context instanceof ElementCreatedLogEntry && $context->hasCreationInstockValue()) {
+ return sprintf(
+ '%s: %s',
+ $this->translator->trans('log.element_created.original_instock'),
+ $context->getCreationInstockValue()
+ );
+ }
+
+ if ($context instanceof ElementDeletedLogEntry) {
+ return sprintf(
+ '%s: %s',
+ $this->translator->trans('log.element_deleted.old_name'),
+ $context->getOldName()
+ );
+ }
+
+ if ($context instanceof ElementEditedLogEntry && !empty($context->getMessage())) {
+ return htmlspecialchars($context->getMessage());
+ }
+
+ if ($context instanceof InstockChangedLogEntry) {
+ return sprintf(
+ '%s; %s %s (%s); %s: %s',
+ $this->translator->trans($context->isWithdrawal() ? 'log.instock_changed.withdrawal' : 'log.instock_changed.added'),
+ $context->getOldInstock(),
+ $context->getNewInstock(),
+ (!$context->isWithdrawal() ? '+' : '-') . $context->getDifference(true),
+ $this->translator->trans('log.instock_changed.comment'),
+ htmlspecialchars($context->getComment())
+ );
+ }
+
+ if ($context instanceof UserNotAllowedLogEntry) {
+ return htmlspecialchars($context->getMessage());
+ }
+
+ return "";
+ }
+}
\ No newline at end of file