Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Right now each entity type needs to reinvent a delete action AND a confirmation form for it. That's a lot of work just to be able to use the mini-VBO we have in core.
Instead we should provide a generic DeleteAction with a deriver that provides a definition for each content entity type.
Plus, a confirmation form.
We already did it in the contrib entity api module:
https://github.com/fago/entity/pull/29/files
Comment | File | Size | Author |
---|---|---|---|
#89 | 2670730-89.patch | 672 bytes | chr.fritsch |
#81 | interdiff-2670730-79-81.txt | 1.76 KB | chr.fritsch |
#81 | 2670730-81.patch | 75.09 KB | chr.fritsch |
#79 | interdiff-2670730-78-79.txt | 6.04 KB | chr.fritsch |
#79 | 2670730-79.patch | 75.03 KB | chr.fritsch |
Comments
Comment #6
chr.fritschPostpone this on #2916740: Add generic entity actions, because this should be built on top of EntityActionDeriverBase
Comment #7
chr.fritschComment #8
chr.fritschFirst patch based on #2916740: Add generic entity actions
Comment #9
chr.fritschUpdated patch a bit. 2670730-9-full.patch also contains changes of #2916740: Add generic entity actions
Comment #11
chr.fritschI introduced a new content type key, to get the multiple delete form route for existing content types.
Comment #13
chr.fritschI applied #2879435: Make DeleteAction compatible with ConfigEntities on top of this patch and improved the messages.
Comment #14
chr.fritschHopefully i fixed all the tests.
Comment #16
chr.fritsch#2916740: Add generic entity actions is RTBC, so let's move on here.
I updated the patch accordingly to all the adjustments that happen in #2916740: Add generic entity actions.
Comment #18
chr.fritschComment #21
chr.fritschAdded DeleteMultipleFormTest from contrib entity module.
Comment #22
chr.fritsch#2916740: Add generic entity actions lands. Wohoo 🎉
So unpostponing this.
Comment #23
chr.fritschAdded "Media Initiative" tag because it's important for us in order to fix #2877383: Add action support to Media module
Comment #25
BerdirI find it strange to have form classes without form suffix.
this doesn't work. Not just that not all entity types have an admin permission, it is also wrong.
See #2779389: Node bulk delete confirmation from requires 'administer nodes' permission.
What we likely need is a custom access check that ensures that the user can delete the entities in the tempstore.
Comment #26
chr.fritschI tried to address @Berdir's feedback.
There is currently one big issue with this patch.
EntityDeleteMultipleAccessCheck
andDeleteMultipleForm
are sitting in the core space but they are depending on the user module because of the tempstore.But moving them into the user modules feels wrong to me.
Comment #28
BerdirI'm wondering if we can limit it to having at least one entity that we can delete.
We could then also check on the form itself before deleting if the user is allowed to, but we already check before adding them to the temp store, so the only scenario that I can think of right now is that you remove delete permission *between* adding them to tempstore and actually deleting them. Which usually is just a few seconds.
I'm not sure about the module, but I know @plach recently basically blocked an issue because it added a new \Drupal\user dependency in \Drupal\Core\Entity so I don't think keeping it there is an option.
So afaik, our only options are:
* user.module: Because we depend on it, it is required so everyone has it
* another module that depends on user, not sure which that should be.
* system.module: both system and user are required and depend on each other a lot. The action config entity also lives there.
Comment #29
phenaproximaSystem has my vote. Every site has it, so it might as well be part of the \Drupal\Core namespace.
Comment #30
chr.fritschAlso, the DeleteAction requires the temp store and it would be a shame if DeleteAction lives in another place then PublishAction/UnpublishAction and SaveAction.
Comment #31
alexpottEvery site has both the system and user module. Either module is fine. It could be argued that we should be looking to move them totally into the core namespace at some point - but is the disruption worth it? I vote for system too as it is the more generic of the two modules. The other option is to move the
user.shared_tempstore
anduser.private_tempstore
into core. These services have no dependencies on user code apart from themselves. However moving this will be disruptive and whilst BC is possible for most things the \Drupal\user\TempStoreException will be tricky. We could make it extend from the new core TempStoreException but core class can't throw \Drupal\user\TempStoreException. I think if we want Delete actions to be in core we need to do the service move. The path of least resistance is to put the new actions in system.Comment #32
catchI'm wondering if we could do the following:
- move tempstore to core
- subclass with deprecations in user module
- for the exception in core, use class_alias() to simulate \Drupal\user\TempStoreException one. That way it could throw the same exception without an actual dependency on user module or an API change.
No way to trigger_error() if we do that, but obviously we can document it with a change record and anything catching the core exception will be forwards compatible with 9.x still.
Comment #33
alexpott@catch trying to do all of that in #2935617: Move User module's temp stores to core
Comment #34
chr.fritschSo this patch is now based on top of #2935617: Move User module's temp stores to core.
I also tried to address #28
Comment #36
alexpottI think it is worth explaining that as long as you can delete one it will proceed but access is checked again in the ::submitForm() and only entities you are allowed to delete are deleted.
It's great that we check access again - just incase it changed after adding to the temp store but I also wonder if we should notify the user in some way about what has not been deleted. Yes this is new functionality but I think I it has been brought about by the change @Berdir requested in #28.
Is there test coverage of all the counting logic? I don't think non translatable entities are counted correctly. Not sure though - it is complex.
Nice - consistency!
as EntityDeleteMultipleForm - for consistency - there a re multiple instances of this.
Not used
Yeah we have to add these here. Because update path tests are not yet in the legacy group and they are not all PHPUnit based tests.
Comment #37
chr.fritschThanks for reviewing @alexpott
#36.1: Added some notes
#36.2: Injected the messenger and inform user if some entities are not possible to delete
#36.3: Extended the test coverage in DeleteMultipleFormTest. Now we also add translations to the tempstore. For me it seems that the calculation is correct.
#36.5: Fixed that in node and comment
#36.6: I reverted all the changes in MediaDeleteMultipleConfirmForm because we are going to deprecate this class in #2935610: Remove or deprecate MediaDeleteMultipleConfirmForm and it's route definition
Comment #39
alexpottI think the logic here could benefit from commentary. Something like:
The code above also fixes the count for entities that don't implement TranslatableInterface.
I also wonder if a user can have access to delete translations but not the entity. Looking at the code in content_translation it looks possible. But the existing code does not cope with this so I guess we could open a follow-up.
Comment #40
alexpottAlso
The mixing of entity counts with translation counts in the message the user gets as opposed to the messages left in the log are weird. This happens in HEAD too but is perhaps worth a follow-up.
Comment #41
tstoecklerIs checking
EntityInterface
really necessary? Seems a lot of things that are much worse than the multiple delete will be broken if an entity type does not implement this.I think similarly to #39 we should iterate over all translations and check access for each.
id -> ID
So the selection is not entity-type-specific, it seems. What if I delete both multiple nodes and multiple comments simultaneously, i.e. in separate browser tabs?
I think it would make sense to use the same counting here that we later use for the status message below, i.e.
$total_count
instead ofcount($this->selection)
. Perhaps we can put the splitting up$this->selection
in a helper method?We should check whether a collection link template exists and fall back to the front page otherwise (or is there anything more sensible to fall back to?). That will also avoid having to add a collection in the test coverage.
To avoid confusion we could rename this to
$selected_langcodes
and$entity_languages
.Also
$(entity_)languages
could be outside of the innerforeach
, as well.This could be outside of the inner
foreach
which would actually make the code more readable in my opinion.I think "inaccessible" is better than "not accessible".
Why should these be content entities?
EntityDeleteActionDeriver
actually doesn't check forContentEntityInterface
and we explicitly checkTranslatableInterface
below.I get why we are building up
$total_count
differently for entity deletes vs. translation deletes but it could use a code comment in my opinion.I recently learned that
count($foo, COUNT_RECURSIVE)
is a thing, that could be used here.Let's use double quotes for the string so we can avoid the escaping.
Is the reason we are not putting this into
DefaultHtmlRouteProvider
the fact that that is not @internal anymore? That would be unfortunate in my opinion, if adding that would be considered a BC break.I think to be more consistent with other routes we should use a form operation for this, i.e. do
$entity_type->getFormClass('delete-multiple-confirm')
here (and add a respective->hasFormClass()
to the if check). That would also allow Node and Comment to cleanly override the form class.Missing "e" in "delte". I guess that means we are missing test coverage?
I'm really not excited about adding another top-level entity type key, especially when it's really just for BC as new entity types will just use the link template and auto-generated route.
I would rather have Node and Comment use the link template as well and then have them declare duplicated routes i.e. provide a "entity.comment.delete_multiple_confirm" route which just duplicates "comment.multiple_delete_confirm". Then we can deprecate the latter once [#928750] lands.
Let's add it then?
Comment #42
tstoecklerAlso re #39: You are absolutely right, but I don't think we should handle that in a follow-up. This might be a pre-existing bug with nodes and comments, but I think we should avoid introducing bugs in new code. Just my 2 cents.
Comment #43
alexpottre
COUNT_RECURSIVE
- I'm not sure it'll be that useful here - see https://3v4l.org/cjDeh. I think for it to be useful we'd need it to return 5 and not 7.Comment #44
alexpottAs as far as I can the whole translation deletion as provided by content_translation is encapsulated in that module so putting something into core for it would wrong. As this is not introducing a new bug doing things in smaller steps especially steps that might not even be simple is preferable - thats why I recommended a follow-up.
Comment #45
tstoecklerRe #43: Oops I totally thought that would yield 5, yes. Sorry about that and thanks for setting me straight
Re #44: Hmm, I guess I am missing something, but doesn't #39 mean that potentially translations are left around that should have been deleted? That does seem like a bug and one that we are introducing here. Maybe you can elaborate...
Comment #46
chr.fritschI addressed #39 and then started with #41.
1. Removed that check
2. Still open
3. Fixed
4. Fixed, added the entity type ID to the key
5. Still open
6. Fixed
7. Fixed
8. Moved things up, as far as possible
9. Fixed
10. Changed to TranslatableInterface
11. Still open
12. Still open
13. Fixed
14. Removed the new router and added it to DefaultHtmlRouteProvider. I think API additions should be ok.
15. Fixed
16. Fixed, test coverage might still be needed
17. Removed the entire delete_multiple_form_route key handling
18. Still open
Comment #48
chr.fritschTry to fix some tests
Comment #50
tstoecklerSome more comments:
Genuine question (i.e. not sure, what the answer is): Should this also check
hasFormClass('delete-multiple-confirm
now?This is now no longer true, I think, since it now includes the entity type ID, as well.
I think using
$entityType
would be more consistent with the rest of core.I think instead of checking the router explicitly, it should be sufficient to check
$this->entityDefinition->hasLinkTemplate('collection')
Hmm... so I'm still not sure the new way of storing all entities prefixed by entity type ID is correct. Again, if I simultaneously delete nodes and comments
$this->selection
will contain both, so this will still end up deleting both nodes and comments. I think what would make more sense is to do the prefixing at the collection level, i.e.$this->selection = $this->tempStore->get($this->currentUser->id() . ':' . $entity_type_id)
(or similar...)This should be "comment", not "node"
Comment #51
chr.fritschRegarding #41
2. Will leave that open because of #44
5. Not sure whats the best way to spilt that up
11. Fixed
12. Will leave that open because of #43
Regarding #50
1. I don't think so. The form class is only required for the route provider. For example, Node is not using the route provider, so there is no need to implement the form handler.
2. I changed the logic back as suggested in 5., so this should be valid again
3. Done
4. Done
5. Followed your suggestion
6. Done
Comment #53
robpowelllooks like it needs a re-roll. Will attempt and post results.
Comment #54
robpowellWhen I run patch -p1 < ~/Desktop/2670730-51.patch this file is not created. If I preemptively create the file and run the patch again, the changes are not applied.
Since this is inherited from DeleteAction.php we don't have to define the params for the annotation?
Also, we dropped the type hint of array from the first parameter.
Locally, I was not able to create DeleteAction so I am pushing my patch without testing it locally.
Comment #55
robpowellLooks like I overcommitted (as both the patch and interdiff files are rather large). Here is the latest patch.
Comment #56
robpowellComment #58
chr.fritsch@robpowell use
git apply my.patch
then it works.So hopefully I fixed the last failing test now
Comment #59
robpowellthanks for the heads up @chr.fritsch. can you weigh in on #54.2?
Comment #60
chr.fritschRegarding #54.2: The definitions will be first inherited from the base class and some keys will be added also from the deriver.
Correct, the array type hint should be added again.
Comment #61
tstoecklerThis is really looking pretty nice now, thanks for all the effort! Some more comments while some of the comments above are still unresolved, as far as I can tell.
This is potentially problematic for translators, because translators cannot change the order of the words which might make sense in some languages. So while in general re-using the base label is a good thing, in this particular case I would propose the following:
t('Delete @entity_type', ...)
. (I just checked and that's in fact the exact string that Entity API in contrib uses...)Wow, that's pretty bizarre, but unfortunately it's absolutely correct to include this check here.
BaseFormIdInterface
to provide the generic string as the base form ID.Comment #62
chr.fritschRe #41.16: I couldn't find delete actions in d6 and d7. So, no need to migrate them 😃
Also fixed #60 and #61
So I think I covered everything now.
Comment #64
chr.fritschSeems like we missed the 8.5 window 😢 So I changed the CR and the deprecation messages.
Also made some minor adjustments.
Comment #65
alexpottt()? I think \Drupal\Core\Action\Plugin\Action\Derivative\EntityActionDeriverBase should have string translation injected. Also we missed on the generic action issue:
This is in \Drupal\Core\Action\Plugin\Action\Derivative\EntityActionDeriverBase. I'm not sure it is correct. It is assuming word order.
The access result. Since neutral is also returned.
Unnecessary use of he. Could rephrase as
It's best to say what you are doing and then why. Something like:
Build a nested list of translations that will be deleted if the entity has multiple translations.
I think we should update the text to
@count @item has not been deleted because you don't have the necessary permissions.
Also as far as I can see this is not tested yet.Comment #66
chr.fritschI adjusted all the things from #65
About 1: These generic translations are really hard. I talked to @Berdir and we agreed that changing the label in EntityActionDeriverBase should be done in a follow-up.
Comment #68
chr.fritschDamn it, forgot to fix the tests 😞
Comment #69
tstoecklerOK, read through the patch again and it looks fine to me.
I opened #2938844: Provide a more meaningful count for entity delete multiple confirm forms for #41.5 and #2938841: Avoid string concatenation of translatable string in EntityActionDeriverBaser for #66. Didn't mark those posponed as we could just as well use those to fix the current node and comment-specific implementations, but don't feel strongly about that.
So, nothing left to complain -> RTBC
Thanks for all the hard work!!
Comment #70
alexpottWe need to also use the trait
\Drupal\Core\StringTranslation\StringTranslationTrait
because the l.d.o can't find$this->stringTranslation->translate()
. Once you use the trait you can use $this->t() and the translation can be found.I think this needs testing.
Comment #71
chr.fritschGood that you asked for tests. The logic was still broken 😔
So I fixed both.
Comment #72
tstoecklerMissed this earlier, but the plural string is actually wrong: It uses "... @item has ..." but that should be "... @items have ..." unless I'm mistaken.
I guess since we're now actually testing the singular case, might be reasonable to test the plural form?
Comment #74
alexpottUpdating issue credit for reviews and providing the contrib code (@bojanz and @dawehner according to @chr.fritsch).
Comment #75
chr.fritschNice catch @tstoeckler.
I added another test for untranslatable entities and cover #72 in that.
Comment #76
BerdirAlso, that's another translation problem. As discussed in the issue that added singular/plural labels, those labels must not be used inside a text with a specifc quantity, it only works as a standalone label.
The probem is languages like russian which have 6 or so different plural versions.. it's impossible to correctly translate it for those cases (e.g. 3 needs a different term than 0 or 10).
Besides, lower/uppercase is also again a problem, in german you need "Inhalte", uppercase and in english you'd want "content" lowercase.
IMHO, I would just use "items" literally, like "@count items have been deleted" and forget about per-entity-type specific labels. If we want to keep the labels of the existing forms, then move those messages in helper functions and add subclasses for Node/Comment that keep the existing naming (for strings that actually
About the test: Interesting I was confused why you can test so easily (because in a real scenario, the action plugin will never add those entities to the tempstore in the first place, the only way to test it there would be to make a permission/access change between executing the operation and submitting the confirm form.
Comment #77
tstoecklerRe #76: I don't mind using more generic messages in this case, not a bad idea. Just to make sure, though: We do have
$entity_type->getCountLabel($count)
for language-specific counts. It does a format-plural internally, so I always thought that shouldn't be used as part of larger translated strings, but it seems you have more experience in this area, so wanted to bring it up at least.Two more nitpicks:
"don't" -> "do not"
We don't use contractions in user-facing code (no pun intended). That will also get rid of the ugly escaping of the strings in the test.
In the plural form:
"has" -> "have"
Comment #78
chr.fritschBased on what @Berdir suggested in #76, I updated the patch.
Comment #79
chr.fritsch@Berdir pointed out on Slack that it is not allowed to pass translated strings into the logger. So we decided instead of passing a non-helpful message about how much items have been deleted, better pass a logger notice for every deleted item. Then we also don't have problems with plural messages there.
Comment #80
tstoecklerIn both cases the logging should happen after the
->delete()
/->save()
call.Comment #81
chr.fritschSure
Comment #82
tstoecklerAs far as I can tell @alexpott's and @Berdir's points have been addressed.
Read through the patch again and my complain-o-meter didn't go off.
Back to RTBC.
Comment #83
alexpottI reviewed all the new code since my last review. Looks great. The only thing holding this back from a commit is a slightly incomplete change record. I think the change record should detail what a contrib entity type would need to do to benefit from this work.
Comment #84
chr.fritschCR adjusted: https://www.drupal.org/node/2934349
Comment #85
alexpottThanks for updating the change record @chr.fritsch.
Committed 3d55922 and pushed to 8.6.x. Thanks!
Removed unused use on commit.
Comment #87
tacituseu CreditAttribution: tacituseu commentedThis is failing hard on PHP 5 environments:
https://www.drupal.org/pift-ci-job/870739
https://www.drupal.org/pift-ci-job/871059
Comment #88
TR CreditAttribution: TR commentedThese test failures are also reported in #2939903: Duplicate definition of StringTranslationTrait
Comment #89
chr.fritschOh damn it. Let's see if this would fix it.
Comment #90
dawehnerNote: I think its always better to open up a new issue and link it from the old one.
Comment #91
BerdirYeah, also saw this with contrib module test fails that still use 5.5 for daily tests. +1 on a new issue.
Comment #92
chr.fritschHere is the new issue: https://www.drupal.org/project/drupal/issues/2939991
I'm setting this back to fixed.
Comment #94
psf_ CreditAttribution: psf_ at SDOS commentedHi @chr.fritsch, I found unwanted effects of this path in configuration entity system while importing configurations, and I write a patch in the issue https://www.drupal.org/project/drupal/issues/2999737 about it.
The problem is that "delete node" and "delete comment" plugins don't set a type, because was removed in your patch. Was a mistake or was a requirement?
Thanks :)
Comment #95
LOBsTerr CreditAttribution: LOBsTerr commented@psf_ I have faced similar problem, what I did in my case I have manually edited configuration file system.action.node_delete_action.yml
and replaced
with
I think we are missing an update hook which will reset it
Comment #96
iyyappan.govindHi I executed the below on devel
I hope this helps
Comment #97
awm CreditAttribution: awm at Johnson & Johnson commentedthank you @LOBsTerr. your suggestion fixed the delete action for me. I believe after an update the delete action plugin id changed but I had it already exported in the system.action.node_delete_action.yml under config/sync. Changing it to plugin: 'entity:delete_action:node' did the trick.
Comment #98
alejomc CreditAttribution: alejomc commentedThanks, @LOBsTerr, indeed that was the issue I was facing, for some reason got config validation import errors on
drush cim -y
, I've had to create an update.Comment #99
psf_ CreditAttribution: psf_ at SDOS commentedComment #100
mpastas CreditAttribution: mpastas at Globant commentedHi guys, this could be causing what I stated in this thread
https://www.drupal.org/project/drupal/issues/3045570#comment-13088526
Caused when upgrading Drupal in following function:
system_post_update_change_delete_action_plugins