Page MenuHomePhabricator

Refactoring deletion-related code (UI and backend)
Closed, ResolvedPublic

Description

The Community-Tech is currently starting to work on T285514: (Un)delete associated talk page (spike). The first thing we want to do is to add an option to delete the associated talk page when deleting a page. Since the deletion-related code is pretty old and needs to be refactored, we're looking into refactoring it before implementing the new feature (both because it's easier to deal with modern code, and because we need to change the logic behind big deletion checks). Given the expertise of the Platform Engineering team in this area, we'd like to ask for feedback about the refactoring strategy.

Problem: the code for page deletion is currently split into DeleteAction (FormlessAction, just a thin proxy), Article (builds the UI, checks permissions & token, sanity checks, runs some hooks), and WikiPage (some more checks, does the actual deletion at the DB level, logs the deletion). Title::isBigDeletion() is also in the wrong place, since we'd like to sum the revisions count of a page and that of its talk page and compare the result against $wgDeleteRevisionsLimit.

Proposed solution: This is a possible new implementation that I came up with:

  • DeleteAction should be a FormAction, responsible for building the UI, putting the form data together, and performing some higher-level checks (e.g. permissions, token, sanity)
  • We would then introduce a new service, DeletePage (with a PageCommandFactory factory). This would be a command-style object with the actual deletion logic. It would be responsible for running all the hooks, performing the final checks (including $wgDeleteRevisionsLimit), actually deleting the revisions at the DB level, and logging the deletion.

While refactoring this code, the signatures should be updated to remove legacy parameters and replace wide typehints (e.g. Title, User) with the newer interfaces (e.g. UserIdentity, Authority, PageIdentity, LinkTarget). Hook signatures could remain as they are, for now.

Beyond the general "what do you think about the above plan?" and "has there been any recent work in this area?" questions, I have a few specific questions for the team:

  • What interfaces should be used for page parameters? In particular, how to choose between LinkTarget and the many sub-interfaces of PageReference.
  • Title::isBigDeletion is currently called from PermissionManager. As mentioned above, we'd want only DeletePage to be aware of "big deletions", but we can't pass a DeletePage to PermissionManager. What would you suggest for that? I can't see an easy way to use DeletePage logic from PermissionManager (e.g. a public static method wouldn't really work), so the only thing I can think of is moving the whole check to DeletePage, but I'm not sure if that change would be going in the right direction.

Event Timeline

Once a long time ago I started something: https://gerrit.wikimedia.org/r/c/mediawiki/core/+/678709 - it's untested, has a ton of TODOs, but it demonstrates PET thinking on this problem. Feel free to use it as a starting point, or ditch it entirely and start over.

What interfaces should be used for page parameters? In particular, how to choose between LinkTarget and the many sub-interfaces of PageReference.

You want PageIdentity - that represents an editable page on a wiki.

Title::isBigDeletion is currently called from PermissionManager.

Yeah... that's something I've been struggling as well when looking into it. Not sure. Perhaps we could have a separate service 'DeletionInfo' or something, more lightweight. But that will make PermissionManager depend on RevisionStore regardless of which path we'd take. As for moving that code out of PermissionManager into the command - that in general makes sense. Look at MovePage for example - there's a 'move' right, but MovePage has its own authorize methods, which check much more the just 'move' right. This needs some analysis of when is PermissionManager::userCan called, what the purpose is, and whether it would be ok to stop making sure 'bigdelete' is checked if delete is big. Ideally, all code that deletes a page should go via your new command, so PermissionManager should only be used for quick checks to show UI buttons - that would mean the code in question should in theory never be executed.

Once a long time ago I started something: https://gerrit.wikimedia.org/r/c/mediawiki/core/+/678709 - it's untested, has a ton of TODOs, but it demonstrates PET thinking on this problem. Feel free to use it as a starting point, or ditch it entirely and start over.

Thank you, I didn't see that one! I think it might be easier for me to start from scratch (and split the work into multiple patches), but I'll definitely keep your patch at hand and compare it with what I'm doing, or use it to make decisions on unclear points.

Title::isBigDeletion is currently called from PermissionManager.

Yeah... that's something I've been struggling as well when looking into it. Not sure. Perhaps we could have a separate service 'DeletionInfo' or something, more lightweight. But that will make PermissionManager depend on RevisionStore regardless of which path we'd take. As for moving that code out of PermissionManager into the command - that in general makes sense. Look at MovePage for example - there's a 'move' right, but MovePage has its own authorize methods, which check much more the just 'move' right.

Yeah, I think it makes sense to have this logic inside the respective command objects. In my mind, only DeletePage would need to know about specific deletion-related checks (e.g. big deletion), and it doesn't make much sense for PermissionManager to use the internals of other classes (like DeletePage or MovePage).

This needs some analysis of when is PermissionManager::userCan called, what the purpose is, and whether it would be ok to stop making sure 'bigdelete' is checked if delete is big. Ideally, all code that deletes a page should go via your new command, so PermissionManager should only be used for quick checks to show UI buttons - that would mean the code in question should in theory never be executed.

Agreed. I'll create another task for the callers audit and see if we can move the code to PageDelete without problems. I guess if some caller really needs to know something about big deletion we could add some hack, but ideally no caller should need that.