Allow to add a comment when editing/creating/deleting an element.

This commit is contained in:
Jan Böhmer 2020-02-23 00:44:52 +01:00
parent c14d6d91ff
commit b6f95ebe48
19 changed files with 421 additions and 47 deletions

View file

@ -52,6 +52,7 @@ use App\Services\Attachments\AttachmentManager;
use App\Services\Attachments\AttachmentSubmitHandler; use App\Services\Attachments\AttachmentSubmitHandler;
use App\Services\EntityExporter; use App\Services\EntityExporter;
use App\Services\EntityImporter; use App\Services\EntityImporter;
use App\Services\LogSystem\EventCommentHelper;
use App\Services\StructuralElementRecursionHelper; use App\Services\StructuralElementRecursionHelper;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException; use InvalidArgumentException;
@ -77,9 +78,11 @@ abstract class BaseAdminController extends AbstractController
protected $translator; protected $translator;
protected $attachmentHelper; protected $attachmentHelper;
protected $attachmentSubmitHandler; protected $attachmentSubmitHandler;
protected $commentHelper;
public function __construct(TranslatorInterface $translator, UserPasswordEncoderInterface $passwordEncoder, public function __construct(TranslatorInterface $translator, UserPasswordEncoderInterface $passwordEncoder,
AttachmentManager $attachmentHelper, AttachmentSubmitHandler $attachmentSubmitHandler) AttachmentManager $attachmentHelper, AttachmentSubmitHandler $attachmentSubmitHandler,
EventCommentHelper $commentHelper)
{ {
if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) { if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) {
throw new InvalidArgumentException('You have to override the $entity_class, $form_class, $route_base and $twig_template value in your subclasss!'); throw new InvalidArgumentException('You have to override the $entity_class, $form_class, $route_base and $twig_template value in your subclasss!');
@ -93,6 +96,7 @@ abstract class BaseAdminController extends AbstractController
$this->passwordEncoder = $passwordEncoder; $this->passwordEncoder = $passwordEncoder;
$this->attachmentHelper = $attachmentHelper; $this->attachmentHelper = $attachmentHelper;
$this->attachmentSubmitHandler = $attachmentSubmitHandler; $this->attachmentSubmitHandler = $attachmentSubmitHandler;
$this->commentHelper = $commentHelper;
} }
@ -131,6 +135,8 @@ abstract class BaseAdminController extends AbstractController
} }
} }
$this->commentHelper->setMessage($form['log_comment']->getData());
$em->persist($entity); $em->persist($entity);
$em->flush(); $em->flush();
$this->addFlash('success', 'entity.edit_flash'); $this->addFlash('success', 'entity.edit_flash');
@ -188,6 +194,8 @@ abstract class BaseAdminController extends AbstractController
} }
} }
$this->commentHelper->setMessage($form['log_comment']->getData());
$em->persist($new_entity); $em->persist($new_entity);
$em->flush(); $em->flush();
$this->addFlash('success', 'entity.created_flash'); $this->addFlash('success', 'entity.created_flash');
@ -215,6 +223,10 @@ abstract class BaseAdminController extends AbstractController
'csv_separator' => $data['csv_separator'], 'csv_separator' => $data['csv_separator'],
]; ];
$this->commentHelper->setMessage('Import ' . $file->getClientOriginalName());
$errors = $importer->fileToDBEntities($file, $this->entity_class, $options); $errors = $importer->fileToDBEntities($file, $this->entity_class, $options);
foreach ($errors as $name => $error) { foreach ($errors as $name => $error) {
@ -280,6 +292,8 @@ abstract class BaseAdminController extends AbstractController
$entityManager->remove($entity); $entityManager->remove($entity);
} }
$this->commentHelper->setMessage($request->request->get('log_comment', null));
//Flush changes //Flush changes
$entityManager->flush(); $entityManager->flush();

View file

@ -50,6 +50,7 @@ use App\Form\Part\PartBaseType;
use App\Services\Attachments\AttachmentManager; use App\Services\Attachments\AttachmentManager;
use App\Services\Attachments\AttachmentSubmitHandler; use App\Services\Attachments\AttachmentSubmitHandler;
use App\Services\Attachments\PartPreviewGenerator; use App\Services\Attachments\PartPreviewGenerator;
use App\Services\LogSystem\EventCommentHelper;
use App\Services\LogSystem\HistoryHelper; use App\Services\LogSystem\HistoryHelper;
use App\Services\LogSystem\TimeTravel; use App\Services\LogSystem\TimeTravel;
use App\Services\PricedetailHelper; use App\Services\PricedetailHelper;
@ -71,12 +72,15 @@ class PartController extends AbstractController
protected $attachmentManager; protected $attachmentManager;
protected $pricedetailHelper; protected $pricedetailHelper;
protected $partPreviewGenerator; protected $partPreviewGenerator;
protected $commentHelper;
public function __construct(AttachmentManager $attachmentManager, PricedetailHelper $pricedetailHelper, PartPreviewGenerator $partPreviewGenerator) public function __construct(AttachmentManager $attachmentManager, PricedetailHelper $pricedetailHelper,
PartPreviewGenerator $partPreviewGenerator, EventCommentHelper $commentHelper)
{ {
$this->attachmentManager = $attachmentManager; $this->attachmentManager = $attachmentManager;
$this->pricedetailHelper = $pricedetailHelper; $this->pricedetailHelper = $pricedetailHelper;
$this->partPreviewGenerator = $partPreviewGenerator; $this->partPreviewGenerator = $partPreviewGenerator;
$this->commentHelper = $commentHelper;
} }
/** /**
@ -166,6 +170,8 @@ class PartController extends AbstractController
} }
} }
$this->commentHelper->setMessage($form['log_comment']->getData());
$em->persist($part); $em->persist($part);
$em->flush(); $em->flush();
$this->addFlash('info', 'part.edited_flash'); $this->addFlash('info', 'part.edited_flash');
@ -197,6 +203,8 @@ class PartController extends AbstractController
if ($this->isCsrfTokenValid('delete'.$part->getId(), $request->request->get('_token'))) { if ($this->isCsrfTokenValid('delete'.$part->getId(), $request->request->get('_token'))) {
$entityManager = $this->getDoctrine()->getManager(); $entityManager = $this->getDoctrine()->getManager();
$this->commentHelper->setMessage($request->request->get('log_comment', null));
//Remove part //Remove part
$entityManager->remove($part); $entityManager->remove($part);
@ -257,6 +265,8 @@ class PartController extends AbstractController
} }
} }
$this->commentHelper->setMessage($form['log_comment']->getData());
$em->persist($new_part); $em->persist($new_part);
$em->flush(); $em->flush();
$this->addFlash('success', 'part.created_flash'); $this->addFlash('success', 'part.created_flash');

View file

@ -0,0 +1,45 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2020 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\Entity\Contracts;
interface LogWithCommentInterface
{
/**
* Checks if this log entry has a user provided comment.
* @return bool
*/
public function hasComment(): bool;
/**
* Gets the user provided comment associated with this log entry.
* Returns null if not comment was set.
* @return string|null
*/
public function getComment(): ?string;
/**
* Sets the user provided comment associated with this log entry.
* @param string|null $new_comment
* @return $this
*/
public function setComment(?string $new_comment): self;
}

View file

@ -43,12 +43,13 @@ declare(strict_types=1);
namespace App\Entity\LogSystem; namespace App\Entity\LogSystem;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Contracts\LogWithCommentInterface;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity() * @ORM\Entity()
*/ */
class ElementCreatedLogEntry extends AbstractLogEntry class ElementCreatedLogEntry extends AbstractLogEntry implements LogWithCommentInterface
{ {
protected $typeString = 'element_created'; protected $typeString = 'element_created';
@ -78,4 +79,29 @@ class ElementCreatedLogEntry extends AbstractLogEntry
{ {
return null !== $this->getCreationInstockValue(); return null !== $this->getCreationInstockValue();
} }
/**
* @inheritDoc
*/
public function hasComment(): bool
{
return isset($this->extra['m']);
}
/**
* @inheritDoc
*/
public function getComment(): ?string
{
return $this->extra['m'] ?? null;
}
/**
* @inheritDoc
*/
public function setComment(?string $new_comment): LogWithCommentInterface
{
$this->extra['m'] = $new_comment;
return $this;
}
} }

View file

@ -43,6 +43,7 @@ declare(strict_types=1);
namespace App\Entity\LogSystem; namespace App\Entity\LogSystem;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Contracts\LogWithCommentInterface;
use App\Entity\Contracts\NamedElementInterface; use App\Entity\Contracts\NamedElementInterface;
use App\Entity\Contracts\TimeTravelInterface; use App\Entity\Contracts\TimeTravelInterface;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
@ -50,7 +51,7 @@ use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity() * @ORM\Entity()
*/ */
class ElementDeletedLogEntry extends AbstractLogEntry implements TimeTravelInterface class ElementDeletedLogEntry extends AbstractLogEntry implements TimeTravelInterface, LogWithCommentInterface
{ {
protected $typeString = 'element_deleted'; protected $typeString = 'element_deleted';
@ -111,4 +112,29 @@ class ElementDeletedLogEntry extends AbstractLogEntry implements TimeTravelInter
{ {
return $this->extra['d'] ?? []; return $this->extra['d'] ?? [];
} }
/**
* @inheritDoc
*/
public function hasComment(): bool
{
return isset($this->extra['m']);
}
/**
* @inheritDoc
*/
public function getComment(): ?string
{
return $this->extra['m'] ?? null;
}
/**
* @inheritDoc
*/
public function setComment(?string $new_comment): LogWithCommentInterface
{
$this->extra['m'] = $new_comment;
return $this;
}
} }

View file

@ -43,13 +43,14 @@ declare(strict_types=1);
namespace App\Entity\LogSystem; namespace App\Entity\LogSystem;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Contracts\LogWithCommentInterface;
use App\Entity\Contracts\TimeTravelInterface; use App\Entity\Contracts\TimeTravelInterface;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity() * @ORM\Entity()
*/ */
class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterface class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterface, LogWithCommentInterface
{ {
protected $typeString = 'element_edited'; protected $typeString = 'element_edited';
@ -72,16 +73,6 @@ class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterf
return $this; return $this;
} }
/**
* Returns the message associated with this edit change.
*
* @return string
*/
public function getMessage(): string
{
return $this->extra['m'] ?? '';
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@ -97,4 +88,29 @@ class ElementEditedLogEntry extends AbstractLogEntry implements TimeTravelInterf
{ {
return $this->extra['d'] ?? []; return $this->extra['d'] ?? [];
} }
/**
* @inheritDoc
*/
public function hasComment(): bool
{
return isset($this->extra['m']);
}
/**
* @inheritDoc
*/
public function getComment(): ?string
{
return $this->extra['m'] ?? null;
}
/**
* @inheritDoc
*/
public function setComment(?string $new_comment): LogWithCommentInterface
{
$this->extra['m'] = $new_comment;
return $this;
}
} }

View file

@ -25,6 +25,7 @@ use App\Entity\LogSystem\AbstractLogEntry;
use App\Entity\LogSystem\ElementCreatedLogEntry; use App\Entity\LogSystem\ElementCreatedLogEntry;
use App\Entity\LogSystem\ElementDeletedLogEntry; use App\Entity\LogSystem\ElementDeletedLogEntry;
use App\Entity\LogSystem\ElementEditedLogEntry; use App\Entity\LogSystem\ElementEditedLogEntry;
use App\Services\LogSystem\EventCommentHelper;
use App\Services\LogSystem\EventLogger; use App\Services\LogSystem\EventLogger;
use Doctrine\Common\EventSubscriber; use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs; use Doctrine\ORM\Event\OnFlushEventArgs;
@ -38,11 +39,13 @@ class EventLoggerSubscriber implements EventSubscriber
{ {
protected $logger; protected $logger;
protected $serializer; protected $serializer;
protected $eventCommentHelper;
public function __construct(EventLogger $logger, SerializerInterface $serializer) public function __construct(EventLogger $logger, SerializerInterface $serializer, EventCommentHelper $commentHelper)
{ {
$this->logger = $logger; $this->logger = $logger;
$this->serializer = $serializer; $this->serializer = $serializer;
$this->eventCommentHelper = $commentHelper;
} }
public function onFlush(OnFlushEventArgs $eventArgs) public function onFlush(OnFlushEventArgs $eventArgs)
@ -59,6 +62,10 @@ class EventLoggerSubscriber implements EventSubscriber
if ($this->validEntity($entity)) { if ($this->validEntity($entity)) {
$log = new ElementEditedLogEntry($entity); $log = new ElementEditedLogEntry($entity);
$this->saveChangeSet($entity, $log, $uow); $this->saveChangeSet($entity, $log, $uow);
//Add user comment to log entry
if ($this->eventCommentHelper->isMessageSet()) {
$log->setComment($this->eventCommentHelper->getMessage());
}
$this->logger->log($log); $this->logger->log($log);
} }
} }
@ -66,6 +73,10 @@ class EventLoggerSubscriber implements EventSubscriber
foreach ($uow->getScheduledEntityDeletions() as $entity) { foreach ($uow->getScheduledEntityDeletions() as $entity) {
if ($this->validEntity($entity)) { if ($this->validEntity($entity)) {
$log = new ElementDeletedLogEntry($entity); $log = new ElementDeletedLogEntry($entity);
//Add user comment to log entry
if ($this->eventCommentHelper->isMessageSet()) {
$log->setComment($this->eventCommentHelper->getMessage());
}
$this->saveChangeSet($entity, $log, $uow); $this->saveChangeSet($entity, $log, $uow);
$this->logger->log($log); $this->logger->log($log);
} }
@ -82,7 +93,6 @@ class EventLoggerSubscriber implements EventSubscriber
} }
$changeSet = $uow->getEntityChangeSet($entity); $changeSet = $uow->getEntityChangeSet($entity);
dump($changeSet);
$old_data = array_diff(array_combine(array_keys($changeSet), array_column($changeSet, 0)), [null]); $old_data = array_diff(array_combine(array_keys($changeSet), array_column($changeSet, 0)), [null]);
$logEntry->setOldData($old_data); $logEntry->setOldData($old_data);
} }
@ -95,6 +105,10 @@ class EventLoggerSubscriber implements EventSubscriber
$entity = $args->getObject(); $entity = $args->getObject();
if ($this->validEntity($entity)) { if ($this->validEntity($entity)) {
$log = new ElementCreatedLogEntry($entity); $log = new ElementCreatedLogEntry($entity);
//Add user comment to log entry
if ($this->eventCommentHelper->isMessageSet()) {
$log->setComment($this->eventCommentHelper->getMessage());
}
$this->logger->log($log); $this->logger->log($log);
} }
} }
@ -107,6 +121,9 @@ class EventLoggerSubscriber implements EventSubscriber
if ($uow->hasPendingInsertions()) { if ($uow->hasPendingInsertions()) {
$em->flush(); $em->flush();
} }
//Clear the message provided by user.
$this->eventCommentHelper->clearMessage();
} }
/** /**

View file

@ -144,6 +144,13 @@ class BaseEntityAdminForm extends AbstractType
'entity' => $entity, 'entity' => $entity,
]); ]);
$builder->add('log_comment', TextType::class, [
'label' => 'edit.log_comment',
'mapped' => false,
'required' => false,
'empty_data' => null,
]);
//Buttons //Buttons
$builder->add('save', SubmitType::class, [ $builder->add('save', SubmitType::class, [
'label' => $is_new ? 'entity.create' : 'entity.edit.save', 'label' => $is_new ? 'entity.create' : 'entity.edit.save',

View file

@ -264,6 +264,13 @@ class PartBaseType extends AbstractType
], ],
]); ]);
$builder->add('log_comment', TextType::class, [
'label' => 'edit.log_comment',
'mapped' => false,
'required' => false,
'empty_data' => null,
]);
$builder $builder
//Buttons //Buttons
->add('save', SubmitType::class, ['label' => 'part.edit.save']) ->add('save', SubmitType::class, ['label' => 'part.edit.save'])

View file

@ -90,7 +90,7 @@ class LogEntryRepository extends EntityRepository
->where('log INSTANCE OF ' . ElementEditedLogEntry::class) ->where('log INSTANCE OF ' . ElementEditedLogEntry::class)
->andWhere('log.target_type = :target_type') ->andWhere('log.target_type = :target_type')
->andWhere('log.target_id = :target_id') ->andWhere('log.target_id = :target_id')
->andWhere('log.timestamp > :until') ->andWhere('log.timestamp >= :until')
->orderBy('log.timestamp', 'DESC'); ->orderBy('log.timestamp', 'DESC');
$qb->setParameters([ $qb->setParameters([

View file

@ -151,7 +151,8 @@ class EntityExporter
// Create the disposition of the file // Create the disposition of the file
$disposition = $response->headers->makeDisposition( $disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT, ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$filename $filename,
$string = preg_replace('![^'.preg_quote('-').'a-z0-_9\s]+!', '', strtolower($filename))
); );
// Set the content disposition // Set the content disposition
$response->headers->set('Content-Disposition', $disposition); $response->headers->set('Content-Disposition', $disposition);

View file

@ -0,0 +1,72 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2020 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;
class EventCommentHelper
{
protected const MAX_MESSAGE_LENGTH = 255;
protected $message;
public function __construct()
{
$message = null;
}
/**
* Set the message that will be saved for all ElementEdited/Created/Deleted messages during the next flush.
* Set to null if no message should be shown.
* After the flush this message is cleared.
* @param string|null $message
*/
public function setMessage(?string $message): void
{
//Restrict the length of the string
$this->message = mb_strimwidth($message, 0, self::MAX_MESSAGE_LENGTH, '...');
}
/**
* Returns the currently set message, or null if no message is set yet.
* @return string|null
*/
public function getMessage(): ?string
{
return $this->message;
}
/**
* Clear the currently set message.
*/
public function clearMessage(): void
{
$this->message = null;
}
/**
* Check if a message is currently set.
* @return bool
*/
public function isMessageSet(): bool
{
return is_string($this->message);
}
}

View file

@ -116,24 +116,38 @@ class LogEntryExtraFormatter
); );
} }
if ($context instanceof ElementCreatedLogEntry && $context->hasCreationInstockValue()) { if ($context instanceof ElementCreatedLogEntry ) {
return sprintf( $comment = '';
'<i>%s</i>: %s', if ($context->hasComment()) {
$this->translator->trans('log.element_created.original_instock'), $comment = htmlspecialchars($context->getComment()) . '; ';
$context->getCreationInstockValue() }
); if($context->hasCreationInstockValue()) {
return $comment . sprintf(
'<i>%s</i>: %s',
$this->translator->trans('log.element_created.original_instock'),
$context->getCreationInstockValue()
);
}
return $comment;
} }
if ($context instanceof ElementDeletedLogEntry) { if ($context instanceof ElementDeletedLogEntry) {
return sprintf( $comment = '';
'<i>%s</i>: %s', if ($context->hasComment()) {
$this->translator->trans('log.element_deleted.old_name'), $comment = htmlspecialchars($context->getComment()) . '; ';
$context->getOldName() ?? $this->translator->trans('log.element_deleted.old_name.unknown') }
); return $comment . sprintf(
'<i>%s</i>: %s',
$this->translator->trans('log.element_deleted.old_name'),
$context->getOldName() ?? $this->translator->trans('log.element_deleted.old_name.unknown')
);
} }
if ($context instanceof ElementEditedLogEntry && ! empty($context->getMessage())) { if ($context instanceof ElementEditedLogEntry) {
return htmlspecialchars($context->getMessage()); if ($context->hasComment()) {
return htmlspecialchars($context->getComment());
}
} }
if ($context instanceof InstockChangedLogEntry) { if ($context instanceof InstockChangedLogEntry) {

View file

@ -1,5 +1,7 @@
{% extends "main_card.html.twig" %} {% extends "main_card.html.twig" %}
{% form_theme form.log_comment 'bootstrap_4_layout.html.twig' %}
{% block card_content %} {% block card_content %}
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-4">
@ -96,7 +98,21 @@
</div> </div>
</div> </div>
{{ form_row(form.save) }} <div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<div class="btn-group">
{{ form_widget(form.save) }}
<button type="button" class="btn {% if entity.id is not null %}btn-primary{% else %}btn-success{% endif %} dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu p-2">
{{ form_row(form.log_comment)}}
</div>
</div>
</div>
</div>
{{ form_row(form.reset) }} {{ form_row(form.reset) }}
{{ form_end(form) }} {{ form_end(form) }}

View file

@ -7,12 +7,22 @@
<div class=""></div> <div class=""></div>
<div class="col-sm offset-sm-3 pl-2"> <div class="col-sm offset-sm-3 pl-2">
{% set delete_disabled = (not is_granted("delete", entity)) or (entity.group is defined and entity.id == 1) %} {% set delete_disabled = (not is_granted("delete", entity)) or (entity.group is defined and entity.id == 1) %}
<button class="btn btn-danger" {% if delete_disabled %}disabled{% endif %}>{% trans %}entity.delete{% endtrans %}</button> <div class="btn-group">
{% if entity.parent is defined %} <button class="btn btn-danger" {% if delete_disabled %}disabled{% endif %}>{% trans %}entity.delete{% endtrans %}</button>
<div class="ml-2 custom-control custom-checkbox custom-control-inline"> <button type="button" class="btn btn-danger dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<input type="checkbox" class="custom-control-input" id="recursive" name="delete_recursive"> <span class="sr-only">Toggle Dropdown</span>
<label class="custom-control-label" for="recursive">{% trans %}entity.delete.recursive{% endtrans %}</label> </button>
<div class="dropdown-menu p-2">
<div class="form-group"><label for="delete_log_comment">{% trans %}edit.log_comment{% endtrans %}</label>
<input type="text" id="delete_log_comment" name="log_comment" class="form-control">
</div>
</div>
</div> </div>
{% if entity.parent is defined %}
<div class="ml-2 custom-control custom-checkbox custom-control-inline">
<input type="checkbox" class="custom-control-input" id="recursive" name="delete_recursive">
<label class="custom-control-label" for="recursive">{% trans %}entity.delete.recursive{% endtrans %}</label>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View file

@ -84,11 +84,23 @@
<div class="tab-pane fade p-2" id="comment" role="tabpanel"> <div class="tab-pane fade p-2" id="comment" role="tabpanel">
{{ form_widget(form.comment)}} {{ form_widget(form.comment)}}
</div> </div>
</div> </div>
{{ form_row(form.save) }}
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<div class="btn-group">
{{ form_widget(form.save) }}
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu p-2">
{{ form_row(form.log_comment)}}
</div>
</div>
</div>
</div>
{{ form_row(form.reset) }} {{ form_row(form.reset) }}
{{ form_errors(form) }} {{ form_errors(form) }}
{{ form_end(form) }} {{ form_end(form) }}

View file

@ -31,9 +31,19 @@
<input type="hidden" name="_method" value="DELETE"> <input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ part.id) }}"> <input type="hidden" name="_token" value="{{ csrf_token('delete' ~ part.id) }}">
<div class="form-group"> <div class="form-group">
<button class="btn btn-danger" {% if not is_granted("delete", part) %}disabled{% endif %}> <div class="btn-group">
<i class="fa fa-trash fa-fw"></i> <button class="btn btn-danger" {% if not is_granted("delete", part) %}disabled{% endif %}>
{% trans %}part.delete{% endtrans %} <i class="fa fa-trash fa-fw"></i>
</button> {% trans %}part.delete{% endtrans %}
</button>
<button type="button" class="btn btn-danger dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu p-2">
<div class="form-group"><label for="delete_log_comment">{% trans %}edit.log_comment{% endtrans %}</label>
<input type="text" id="delete_log_comment" name="log_comment" class="form-control">
</div>
</div>
</div>
</div> </div>
</form> </form>

View file

@ -0,0 +1,71 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2020 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\Tests\Services\LogSystem;
use App\Services\LogSystem\EventCommentHelper;
use App\Services\LogSystem\EventLogger;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class EventCommentHelperTest extends WebTestCase
{
/**
* @var EventCommentHelper
*/
protected $service;
protected function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
//Get an service instance.
self::bootKernel();
$this->service = self::$container->get(EventCommentHelper::class);
}
public function testInitialState()
{
$this->assertNull($this->service->getMessage());
$this->assertFalse($this->service->isMessageSet());
}
public function testClearMessage()
{
$this->service->setMessage('Test');
$this->assertTrue($this->service->isMessageSet());
$this->service->clearMessage();
$this->assertFalse($this->service->isMessageSet());
}
public function testGetSetMessage()
{
$this->service->setMessage('Test');
$this->assertSame('Test', $this->service->getMessage());
}
public function testIsMessageSet()
{
$this->service->setMessage('Test');
$this->assertTrue($this->service->isMessageSet());
$this->service->clearMessage();
$this->assertFalse($this->service->isMessageSet());
}
}

View file

@ -6376,7 +6376,7 @@ Element 3</target>
<unit id="tagdXMa" name="part.info.timetravel_hint"> <unit id="tagdXMa" name="part.info.timetravel_hint">
<segment> <segment>
<source>part.info.timetravel_hint</source> <source>part.info.timetravel_hint</source>
<target><![CDATA[This is how the part appeared on %timestamp%. <i>Please note that this feature is experimental, so the infos are maybe not correct.</i>]]></target> <target><![CDATA[This is how the part appeared before %timestamp%. <i>Please note that this feature is experimental, so the infos are maybe not correct.</i>]]></target>
</segment> </segment>
</unit> </unit>
</file> </file>