Problem/Motivation

More than once I've seen custom implementations of warning messages when referenced entities are being edited or deleted. Maybe we could add this option directly in Entity Usage, with a way to opt-out in case sites want to do their own messaging.

Proposed resolution

1) Edit warning messages

- Add a new config setting such as "Show warning when referenced entities are being edited", active by default on new installs (but not changing existing sites)
- When selected, whenever an entity is edited, we would show a warning message alerting users that modifications would affect all usages

2) Delete warning messages

- Add a new config setting such as "Show warning when referenced entities are being deleted", active by default on new installs (but not changing existing sites)
- When selected, the delete form (confirmation step) would show a warning alerting users that there are active references to the entity that is about to be deleted.

The messaging itself may depend on how we want to implement things. For instance, it can be as simple as detecting if there are any usages, and show something like:

There are _recorded usages_ of this entity. (...)

Where the "recorded usages" would be linked to the usage list of that entity.

Ideally though, the message could be smarter and provide more meaningful information. For example:
- On the delete form there is plenty of space, maybe it wouldn't be a problem to show titles (with links) of entities that have references in their default revision? If the list is too big it could be trimmed to the first X results...
- Or we could show just a count of sources?
- Or we could even expose the message to be configurable as well? (with some sort of token to link to the usage page, etc)
- Etc.

Thoughts?

Remaining tasks

User interface changes

API changes

Data model changes

Comments

marcoscano created an issue. See original summary.

miro_dietiker’s picture

Such things would be awesome.

1) The need for warnings vary based on the usage type. Some references are true embeds where awareness is key because the information is embedded in the usage origin. A media item description might be shown immediately. Links in content (linkit) are usually no reason to warn on edit.

2) Agree showing the items individually with label is a great thing as long as the list is not too long.
Also here, some fields need to be excluded. You don't want to warn the user that Paragraphs are deleted when the Host entity is deleted - that's obvious.

berdir’s picture

> Also here, some fields need to be excluded. You don't want to warn the user that Paragraphs are deleted when the Host entity is deleted - that's obvious.

Then don't track them, which they by default aren't :)

Also don't get why it shouldn't warn on links, those kind of embeds/links are just as problematic when being deleted, e.g. they can be embedded download links to files.

miro_dietiker’s picture

Interesting...

So you think that it's fine to treat them all the same?
Just asking - how about term usage or user (author) usage? Just don't track? At least term usage can be of great value because regular term views only show content.

Maybe some type of references should disallow delete?
(Or maybe we should offer a permission to still delete even if it is used.)

And in case we still allow deletion - we maybe should offer a method to fix broken links. From simply filtering them out when rendering to batch updating the content to remove the reference. What do you think? ;-)

berdir’s picture

I'm thinking that this isn't about preventing deletion, not for now anyway. It's just a warning, just like we're doing ourself in paragraps_library edit. We already discussed that preventing deletion of used paragraphs_library_item is flawed and doesn't work and needs to be replaced with a warning message.

And yes, users are by default not tracked and we also don't track terms in our sites.

marcoscano’s picture

Yes, I would definitely want to keep this restricted to only show a warning/error in the edit/delete form.

We can think of the best way to accomplish that so sites also have flexibility to fine-tune it to their needs.

If we really want to go down the deletion prevention rabbit hole I'd imagine we would do that in a separate module/submodule.

marcoscano’s picture

Status: Active » Needs review
Issue tags: +Needs tests
StatusFileSize
new43.26 KB
new50.79 KB
new7.8 KB

First shot at this, if the approach seems reasonable we can proceed fine-tuning it and adding tests.

The optional textarea that allows entering a list of classes for the delete message is aimed at leaving the door open to sites to also show the message in other forms as well. For instance, I have a site where we have built a custom MoveToTrashForm confirmation form, which will as a result unpublish the entity. It will be nice to get this message for free there too.

Edit warning:

Delete warning:

marcoscano’s picture

StatusFileSize
new201.85 KB

And a screenshot of the new configs:

marcoscano’s picture

StatusFileSize
new7.84 KB
new1.14 KB

Small tweak to make it more flexible when using in custom forms.

marcoscano’s picture

StatusFileSize
new7.84 KB
new793 bytes

Oops.

berdir’s picture

  1. +++ b/entity_usage.install
    @@ -111,6 +111,17 @@ function entity_usage_schema() {
    + * Implements hook_install().
    + */
    +function entity_usage_install() {
    +  // On new installs, set the warnings as active by default.
    +  $entity_usage_config = \Drupal::configFactory()->getEditable('entity_usage.settings');
    +  $entity_usage_config->set('show_edit_warning', TRUE);
    +  $entity_usage_config->set('show_delete_warning', TRUE);
    +  $entity_usage_config->save(TRUE);
    +}
    

    Just put that in entity_usage.settings.yml, that is only automatically imported on new installs.

  2. +++ b/entity_usage.module
    @@ -67,3 +70,68 @@ function entity_usage_field_storage_config_delete(FieldStorageConfigInterface $f
    +  if ($form_object->getOperation() === 'edit' && $config->get('show_edit_warning')) {
    +    $form['entity_usage_edit_warning'] = [
    +      '#type' => 'container',
    +      '#markup' => t('Modifications on this form will affect all <a href="@usage_url" target="_blank">existing usages</a> of this entity.', [
    +        '@usage_url' => Url::fromRoute('entity_usage.usage_list', [
    +          'entity_type' => $entity->getEntityTypeId(),
    +          'entity_id' => $entity->id(),
    +        ])->toString(),
    +      ]),
    +      '#attributes' => [
    +        'class' => ['messages', 'messages--warning'],
    +      ],
    +      '#weight' => -201,
    +    ];
    

    In #3002571: Multiple warning messages when having untranslatable fields, we switched to #theme status_messages, a bit simpler and doesn't require us to hardcode classes.

  3. +++ b/src/Form/EntityUsageSettingsForm.php
    @@ -196,6 +196,29 @@ class EntityUsageSettingsForm extends ConfigFormBase {
    +    $form['generic_settings']['delete_warning_form_classes'] = [
    +      '#type' => 'textarea',
    +      '#title' => $this->t('Form classes for the delete warning'),
    +      '#description' => $this->t("A comma or new-line separated list of Drupal classes where the delete warning message should be shown. If you don't know what this means, you should leave this setting unchanged."),
    +      '#default_value' => implode("\r\n", $config->get('delete_warning_form_classes') ?: ['Drupal\Core\Entity\ContentEntityDeleteForm']),
    +      '#states' => [
    +        'visible' => [
    +          ':input[name="show_delete_warning"]' => ['checked' => TRUE],
    +        ],
    +      ],
    +    ];
    

    not very user friendly but I know it's tricky.

    We do have the operation though (\Drupal\Core\Entity\EntityForm::getOperation) and we cna also check whether an entity type has usage tracking enabled, that might be enough?

marcoscano’s picture

StatusFileSize
new7.7 KB
new5.29 KB

@Berdir thanks for reviewing!

#11:

1: Fixed
2: Fixed
3: \Drupal\Core\Entity\EntityForm has a lot bundled... I wanted to leave this flexible enough so it could be used in other forms as well. Maybe I'm making it more complex than it should be, but the idea is that as long as your form has a ::getEntity() method returning an EntityInterface object, it can be used here. I've added some more info in the description, I hope it helps. (I actually have a use case that I want to use this in a form that just extends ConfirmFormBase, not EntityForm... :) )

berdir’s picture

3. I see. Anything that has getEntity() also has an operation, but you can't check for the entity type at the same time. Ok I guess, just seems a bit strange to have that UI :)

marcoscano’s picture

Status: Needs review » Needs work

Marking NW for the missing tests.

arpad.rozsa’s picture

Working on updating the paragraphs_library module to use this and to remove the custom warning messages from there. But if we would enable it automatically, then this would be also enabled on other entities, which users might not want to see everywhere.
So we need to consider to think about adding an option to enable the messages per entity type.
This way other modules would also benefit, if they only want to enable them, for their entity type.

marcoscano’s picture

True, I can see your point.

The first alternative that comes to my mind is to have another set of checkboxes per message type, that shows up only when you opt-in to that message, where you could select entity types where the message should be shown. Leaving them empty would show the warning in all entity types.

Any drawbacks of this approach?

marcoscano’s picture

Status: Needs work » Needs review
StatusFileSize
new12.78 KB
new11.87 KB

Status: Needs review » Needs work

The last submitted patch, 17: 3002316-17.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

arpad.rozsa’s picture

  1. +++ b/config/schema/entity_usage.schema.yml
    @@ -29,3 +29,18 @@ entity_usage.settings:
           type: sequence
           sequence:
             type: string
    +    edit_warning_message_entity_types:
    +      label: Entity types where to show warning on edit form
    +      type: sequence
    +      sequence:
    +        type: string
    +    delete_warning_message_entity_types:
    +      label: Entity types where to show warning on delete form
    +      type: sequence
    +      sequence:
    +        type: string
    +    delete_warning_form_classes:
    +      label: Form classes that should show the delete warning message
    +      type: sequence
    +      sequence:
    +        type: string

    The delete_warning_form_classes setting is not used anymore, so it should be also removed from here.

  2. +++ b/src/Form/EntityUsageSettingsForm.php
    @@ -184,6 +184,54 @@ class EntityUsageSettingsForm extends ConfigFormBase {
           }
         }
     
    +    // Edit warning message.
    +    $form['edit_warning_message_entity_types'] = [
    +      '#type' => 'details',
    +      '#open' => FALSE,
    +      '#title' => $this->t('Warning message on edit form'),
    +      '#description' => $this->t('Check which entity types should show a message when being edited with recorded references to it.'),
    +      '#tree' => TRUE,
    +    ];
    +    $form['edit_warning_message_entity_types']['entity_types'] = [
    +      '#type' => 'checkboxes',
    +      '#title' => $this->t('Entity types to show warning on edit form'),
    +      '#options' => $entity_type_options,
    +      '#default_value' => $config->get('edit_warning_message_entity_types') ?: [],
    +      '#required' => FALSE,
    +    ];
    +    // Only allow to opt-in on target entities being tracked.
    +    foreach (array_keys($entity_type_options) as $entity_type_id) {
    +      $form['edit_warning_message_entity_types']['entity_types'][$entity_type_id]['#states'] = [
    +        'enabled' => [
    +          ':input[name="track_enabled_target_entity_types[entity_types][' . $entity_type_id . ']"]' => ['checked' => TRUE],
    +        ]
    +      ];
    +    }
    +
    +    // Delete warning message.
    +    $form['delete_warning_message_entity_types'] = [
    +      '#type' => 'details',
    +      '#open' => FALSE,
    +      '#title' => $this->t('Warning message on delete form'),
    +      '#description' => $this->t('Check which entity types should show a message when being deleted with recorded references to it.'),
    +      '#tree' => TRUE,
    +    ];
    +    $form['delete_warning_message_entity_types']['entity_types'] = [
    +      '#type' => 'checkboxes',
    +      '#title' => $this->t('Entity types to show warning on delete form'),
    +      '#options' => $entity_type_options,
    +      '#default_value' => $config->get('delete_warning_message_entity_types') ?: [],
    +      '#required' => FALSE,
    +    ];
    +    // Only allow to opt-in on target entities being tracked.
    +    foreach (array_keys($entity_type_options) as $entity_type_id) {
    +      $form['delete_warning_message_entity_types']['entity_types'][$entity_type_id]['#states'] = [
    +        'enabled' => [
    +          ':input[name="track_enabled_target_entity_types[entity_types][' . $entity_type_id . ']"]' => ['checked' => TRUE],
    +        ]
    +      ];
    +    }
    

    Here I think 1 foreach should be enough, since the 2 do the same, except for different warning message types. So move the logic from the first into the second foreach and it should be fine.

  3. +++ b/tests/src/FunctionalJavascript/IntegrationTest.php
    @@ -3,6 +3,7 @@
     namespace Drupal\Tests\entity_usage\FunctionalJavascript;
     
     use Drupal\Core\Field\FieldStorageDefinitionInterface;
    +use Drupal\Core\Url;
     use Drupal\field\Entity\FieldConfig;
     use Drupal\field\Entity\FieldStorageConfig;
     use Drupal\link\LinkItemInterface;
    @@ -78,6 +79,38 @@ class IntegrationTest extends EntityUsageJavascriptTestBase {
         ];
         $this->assertEquals($expected, $usage);
     
    +    // Open Node 1 edit and delete form and verify that no warning is present.
    +    $this->drupalGet("/node/{$node1->id()}/edit");
    +    $assert_session->pageTextNotContains('Modifications on this form will affect all existing usages of this entity');
    +    $this->drupalGet("/node/{$node1->id()}/delete");
    +    $assert_session->pageTextNotContains('There are recorded usages of this entity');
    +    // Configure nodes to have warning messages on both forms and try again.
    +    $this->drupalGet('/admin/config/entity-usage/settings');
    +    $edit_summary = $assert_session->elementExists('css', '#edit-edit-warning-message-entity-types summary');
    +    $edit_summary->click();
    +    $assert_session->pageTextContains('Entity types to show warning on edit form.');
    +    $page->checkField('edit_warning_message_entity_types[entity_types][node]');
    +    $delete_summary = $assert_session->elementExists('css', '#edit-delete-warning-message-entity-types summary');
    +    $delete_summary->click();
    +    $assert_session->pageTextContains('Entity types to show warning on delete form.');

    The dot at the end of the above sentence is not needed, also in 'Entity types to show warning on edit form.'. One reason that the test would fail.
    Also you need to login with a user with the 'administer entity usage' permission to be able to change the settings.

  4. Other test are failing because the test base is changed to the WebDriverTestBase. So revision information is not visible by default, we can't get the status code back now and so on.

Even if those tests are failing, the patch works properly. And those fails are not really connected directly to the new functionality introduced in here.

johnchque’s picture

Status: Needs work » Needs review
StatusFileSize
new11.25 KB
new5.96 KB

Addressing all changes from previous comments. Working on fixing tests now. :)

Status: Needs review » Needs work

The last submitted patch, 20: 3002316-20.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

johnchque’s picture

Status: Needs work » Needs review
StatusFileSize
new25.85 KB
new13.95 KB

This WebDriver Tests are awesome and tricky at the same time. Fixing all tests I think. :)

berdir’s picture

Status: Needs review » Needs work

The form classes stuff is still "used", it was just removed from the UI, so part of the changes in #20 should be reverted.

marcoscano’s picture

Issue tags: -Needs tests

Thanks a lot for working on this! I've been wanting to come back and finish this for several weeks but haven't found the time yet.

This looks great, the only modif necessary is indeed:

+++ b/config/schema/entity_usage.schema.yml
@@ -42,5 +41,0 @@
-    delete_warning_form_classes:
-      label: Form classes that should show the delete warning message
-      type: sequence
-      sequence:
-        type: string

+++ b/entity_usage.module
@@ -115,29 +115,20 @@
-    // Even if this is not on the UI, sites can define additional form classes
-    // where the delete message can be shown.
-    $form_classes = $config->get('delete_warning_form_classes') ?: ['Drupal\Core\Entity\ContentEntityDeleteForm'];
-    $is_delete_form = FALSE;
-    foreach ($form_classes as $class) {
-      if ($form_object instanceof $class) {
-        $is_delete_form = TRUE;
-        break;
-      }
-    }
-    if ($is_delete_form) {
-      $form['entity_usage_delete_warning'] = [
-        '#theme' => 'status_messages',
-        '#message_list' => [
-          'warning' => [t('There are <a href="@usage_url" target="_blank">recorded usages</a> of this entity.', [

+++ b/src/Form/EntityUsageSettingsForm.php
@@ -266,9 +264,6 @@
-    $form_classes = preg_replace('/[\s, ]/', ',', $form_state->getValue('delete_warning_form_classes'));
-    $form_classes = array_values(array_filter(explode(',', $form_classes)));
-

As @Berdir mentioned, we decided some comments above to keep the delete_warning_form_classes config behind the scenes, so sites could still use it (for example by setting its value with drush, CMI or programmatically), and just not expose it on the config form (falling back to the ContentEntityDeleteForm class if it's not defined).

Also, thanks a lot for fixing the unrelated test failures! :)

johnchque’s picture

Status: Needs work » Needs review
StatusFileSize
new26.94 KB
new3.13 KB

Ohh that's true. Adding that logic back. :)

marcoscano’s picture

StatusFileSize
new26.46 KB
new1.02 KB

Wonderful, thanks!

Small tweak to remove some obsolete lines (sorry I may have misled you on this with my previous comment), and I think we should be ready to go.

marcoscano’s picture

Status: Needs review » Fixed

Committed and pushed to dev.

Thanks everyone!

marcoscano’s picture

For the record, I tagged alpha8 to include this.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.