diff --git a/.docker/symfony.conf b/.docker/symfony.conf index 60597dd6..2f8e7f66 100644 --- a/.docker/symfony.conf +++ b/.docker/symfony.conf @@ -26,7 +26,7 @@ # Pass the configuration from the docker env to the PHP environment (here you should list all .env options) PassEnv APP_ENV APP_DEBUG APP_SECRET - PassEnv DATABASE_URL + PassEnv DATABASE_URL ENFORCE_CHANGE_COMMENTS_FOR PassEnv DEFAULT_LANG DEFAULT_TIMEZONE BASE_CURRENCY INSTANCE_NAME ALLOW_ATTACHMENT_DOWNLOADS USE_GRAVATAR MAX_ATTACHMENT_FILE_SIZE DEFAULT_URI PassEnv MAILER_DSN ALLOW_EMAIL_PW_RESET EMAIL_SENDER_EMAIL EMAIL_SENDER_NAME PassEnv HISTORY_SAVE_CHANGED_FIELDS HISTORY_SAVE_CHANGED_DATA HISTORY_SAVE_REMOVED_DATA diff --git a/.env b/.env index 43cffcd1..7db81e46 100644 --- a/.env +++ b/.env @@ -39,6 +39,11 @@ MAX_ATTACHMENT_FILE_SIZE="100M" # This must end with a slash! DEFAULT_URI="https://partdb.changeme.invalid/" +# With this option you can configure, where users are enforced to give a change reason, which will be logged +# This is a comma separated list of values, see documentation for available values +# Leave this empty, to make all change reasons optional +ENFORCE_CHANGE_COMMENTS_FOR="" + ################################################################################### # Email settings ################################################################################### diff --git a/config/parameters.yaml b/config/parameters.yaml index 773b61e8..a7a23db3 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -12,6 +12,7 @@ parameters: partdb.default_currency: '%env(string:BASE_CURRENCY)%' # The currency that is used inside the DB (and is assumed when no currency is set). This can not be changed later, so be sure to set it the currency used in your country partdb.global_theme: '' # The theme to use globally (see public/build/themes/ for choices, use name without .css). Set to '' for default bootstrap theme partdb.locale_menu: ['en', 'de', 'fr', 'ru', 'ja'] # The languages that are shown in user drop down menu + partdb.enforce_change_comments_for: '%env(csv:ENFORCE_CHANGE_COMMENTS_FOR)%' # The actions for which a change comment is required (e.g. "part_edit", "part_create", etc.). If this is empty, change comments are not required at all. partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails @@ -105,6 +106,8 @@ parameters: env(USE_GRAVATAR): '0' env(MAX_ATTACHMENT_FILE_SIZE): '100M' + env(ENFORCE_CHANGE_COMMENTS_FOR): '' + env(ERROR_PAGE_ADMIN_EMAIL): '' env(ERROR_PAGE_SHOW_HELP): 1 diff --git a/config/services.yaml b/config/services.yaml index 961f6258..b075684a 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -102,6 +102,10 @@ services: event: 'Symfony\Component\Security\Http\Event\LogoutEvent' dispatcher: security.event_dispatcher.main + App\Services\LogSystem\EventCommentNeededHelper: + arguments: + $enforce_change_comments_for: '%partdb.enforce_change_comments_for%' + #################################################################################################################### # Attachment system #################################################################################################################### diff --git a/src/Form/AdminPages/AttachmentTypeAdminForm.php b/src/Form/AdminPages/AttachmentTypeAdminForm.php index 75174279..57ba6fed 100644 --- a/src/Form/AdminPages/AttachmentTypeAdminForm.php +++ b/src/Form/AdminPages/AttachmentTypeAdminForm.php @@ -24,6 +24,7 @@ namespace App\Form\AdminPages; use App\Entity\Base\AbstractNamedDBElement; use App\Services\Attachments\FileTypeFilterTools; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -33,10 +34,10 @@ class AttachmentTypeAdminForm extends BaseEntityAdminForm { protected FileTypeFilterTools $filterTools; - public function __construct(Security $security, FileTypeFilterTools $filterTools) + public function __construct(Security $security, FileTypeFilterTools $filterTools, EventCommentNeededHelper $eventCommentNeededHelper) { $this->filterTools = $filterTools; - parent::__construct($security); + parent::__construct($security, $eventCommentNeededHelper); } protected function additionalFormElements(FormBuilderInterface $builder, array $options, AbstractNamedDBElement $entity): void diff --git a/src/Form/AdminPages/BaseEntityAdminForm.php b/src/Form/AdminPages/BaseEntityAdminForm.php index a28e0211..1a95a119 100644 --- a/src/Form/AdminPages/BaseEntityAdminForm.php +++ b/src/Form/AdminPages/BaseEntityAdminForm.php @@ -31,6 +31,7 @@ use App\Form\ParameterType; use App\Form\Type\MasterPictureAttachmentType; use App\Form\Type\RichTextEditorType; use App\Form\Type\StructuralEntityType; +use App\Services\LogSystem\EventCommentNeededHelper; use FOS\CKEditorBundle\Form\Type\CKEditorType; use function get_class; use Symfony\Component\Form\AbstractType; @@ -46,10 +47,12 @@ use Symfony\Component\Security\Core\Security; class BaseEntityAdminForm extends AbstractType { protected Security $security; + protected EventCommentNeededHelper $eventCommentNeededHelper; - public function __construct(Security $security) + public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper) { $this->security = $security; + $this->eventCommentNeededHelper = $eventCommentNeededHelper; } public function configureOptions(OptionsResolver $resolver): void @@ -141,7 +144,7 @@ class BaseEntityAdminForm extends AbstractType $builder->add('log_comment', TextType::class, [ 'label' => 'edit.log_comment', 'mapped' => false, - 'required' => false, + 'required' => $this->eventCommentNeededHelper->isCommentNeeded($is_new ? 'datastructure_create': 'datastructure_edit'), 'empty_data' => null, ]); diff --git a/src/Form/Part/PartBaseType.php b/src/Form/Part/PartBaseType.php index 3951a2ac..ef9bd60c 100644 --- a/src/Form/Part/PartBaseType.php +++ b/src/Form/Part/PartBaseType.php @@ -37,6 +37,7 @@ use App\Form\Type\RichTextEditorType; use App\Form\Type\SIUnitType; use App\Form\Type\StructuralEntityType; use App\Form\WorkaroundCollectionType; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -54,17 +55,20 @@ class PartBaseType extends AbstractType { protected Security $security; protected UrlGeneratorInterface $urlGenerator; + protected EventCommentNeededHelper $event_comment_needed_helper; - public function __construct(Security $security, UrlGeneratorInterface $urlGenerator) + public function __construct(Security $security, UrlGeneratorInterface $urlGenerator, EventCommentNeededHelper $event_comment_needed_helper) { $this->security = $security; $this->urlGenerator = $urlGenerator; + $this->event_comment_needed_helper = $event_comment_needed_helper; } public function buildForm(FormBuilderInterface $builder, array $options): void { /** @var Part $part */ $part = $builder->getData(); + $new_part = null === $part->getID(); $status_choices = [ 'm_status.unknown' => '', @@ -250,7 +254,7 @@ class PartBaseType extends AbstractType $builder->add('log_comment', TextType::class, [ 'label' => 'edit.log_comment', 'mapped' => false, - 'required' => false, + 'required' => $this->event_comment_needed_helper->isCommentNeeded($new_part ? 'part_create' : 'part_edit'), 'empty_data' => null, ]); diff --git a/src/Services/LogSystem/EventCommentNeededHelper.php b/src/Services/LogSystem/EventCommentNeededHelper.php new file mode 100644 index 00000000..7305b304 --- /dev/null +++ b/src/Services/LogSystem/EventCommentNeededHelper.php @@ -0,0 +1,60 @@ +. + */ + +namespace App\Services\LogSystem; + +/** + * This service is used to check if a log change comment is needed for a given operation type. + * It is configured using the "enforce_change_comments_for" config parameter. + */ +class EventCommentNeededHelper +{ + protected array $enforce_change_comments_for; + + public const VALID_OPERATION_TYPES = [ + 'part_edit', + 'part_create', + 'part_delete', + 'part_stock_operation', + 'datastructure_edit', + 'datastructure_create', + 'datastructure_delete', + ]; + + public function __construct(array $enforce_change_comments_for) + { + $this->enforce_change_comments_for = $enforce_change_comments_for; + } + + /** + * Checks if a log change comment is needed for the given operation type + * @param string $comment_type + * @return bool + */ + public function isCommentNeeded(string $comment_type): bool + { + //Check if the comment type is valid + if (! in_array($comment_type, self::VALID_OPERATION_TYPES, true)) { + throw new \InvalidArgumentException('The comment type "'.$comment_type.'" is not valid!'); + } + + return in_array($comment_type, $this->enforce_change_comments_for, true); + } +} \ No newline at end of file diff --git a/src/Twig/MiscExtension.php b/src/Twig/MiscExtension.php new file mode 100644 index 00000000..e154ccf8 --- /dev/null +++ b/src/Twig/MiscExtension.php @@ -0,0 +1,43 @@ +. + */ + +namespace App\Twig; + +use App\Services\LogSystem\EventCommentNeededHelper; +use Twig\Extension\AbstractExtension; + +final class MiscExtension extends AbstractExtension +{ + private EventCommentNeededHelper $eventCommentNeededHelper; + + public function __construct(EventCommentNeededHelper $eventCommentNeededHelper) + { + $this->eventCommentNeededHelper = $eventCommentNeededHelper; + } + + public function getFunctions() + { + return [ + new \Twig\TwigFunction('event_comment_needed', + fn(string $operation_type) => $this->eventCommentNeededHelper->isCommentNeeded($operation_type) + ), + ]; + } +} \ No newline at end of file diff --git a/templates/admin/_delete_form.html.twig b/templates/admin/_delete_form.html.twig index 0423bdca..762b91b6 100644 --- a/templates/admin/_delete_form.html.twig +++ b/templates/admin/_delete_form.html.twig @@ -14,7 +14,8 @@ diff --git a/templates/parts/edit/edit_part_info.html.twig b/templates/parts/edit/edit_part_info.html.twig index 5e5dc243..34ec2d67 100644 --- a/templates/parts/edit/edit_part_info.html.twig +++ b/templates/parts/edit/edit_part_info.html.twig @@ -107,7 +107,6 @@ {{ form_widget(form.save_and_new, {'attr': {'class': 'dropdown-item'}}) }}
- {{ form_row(form.log_comment)}}
diff --git a/templates/parts/info/_tools.html.twig b/templates/parts/info/_tools.html.twig index 3be32f3c..9f60b49f 100644 --- a/templates/parts/info/_tools.html.twig +++ b/templates/parts/info/_tools.html.twig @@ -44,7 +44,9 @@ diff --git a/templates/parts/info/_withdraw_modal.html.twig b/templates/parts/info/_withdraw_modal.html.twig index 79ae2ea2..bb29c8d6 100644 --- a/templates/parts/info/_withdraw_modal.html.twig +++ b/templates/parts/info/_withdraw_modal.html.twig @@ -46,7 +46,7 @@ {% trans %}part.info.withdraw_modal.comment{% endtrans %}
- +
{% trans %}part.info.withdraw_modal.comment.hint{% endtrans %}
diff --git a/tests/Services/LogSystem/EventCommentNeededHelperTest.php b/tests/Services/LogSystem/EventCommentNeededHelperTest.php new file mode 100644 index 00000000..90b9d3ab --- /dev/null +++ b/tests/Services/LogSystem/EventCommentNeededHelperTest.php @@ -0,0 +1,43 @@ +. + */ + +namespace App\Tests\Services\LogSystem; + +use App\Services\LogSystem\EventCommentNeededHelper; +use PHPUnit\Framework\TestCase; + +class EventCommentNeededHelperTest extends TestCase +{ + public function testIsCommentNeeded() + { + $service = new EventCommentNeededHelper(['part_edit', 'part_create']); + $this->assertTrue($service->isCommentNeeded('part_edit')); + $this->assertTrue($service->isCommentNeeded('part_create')); + $this->assertFalse($service->isCommentNeeded('part_delete')); + $this->assertFalse($service->isCommentNeeded('part_lot_operation')); + } + + public function testIsCommentNeededInvalidTypeException() + { + $service = new EventCommentNeededHelper(['part_edit', 'part_create']); + $this->expectException(\InvalidArgumentException::class); + $service->isCommentNeeded('this_is_not_valid'); + } +}