Problem/Motivation

Since EntityOwnerTrait can be used as a default implementation of EntityOwnerInterface the owner can't be NULL anymore for media entities.

Therefore if you cancel a user you can't edit a media entity that was own by this user. The following error is thrown:

Drupal\Core\Entity\EntityStorageException: SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column "uid" violates not-null constraint DETAIL: Failing row contains (188, 241, image, nl, 1, building-buildings-busy-297743_0.jpg, 442, car, null, 1000, 667, null, 1558535344, 1563353147, 1, 1).: INSERT INTO drupal_webshop_nl.media_field_data (mid, vid, bundle, langcode, status, name, thumbnail__target_id, thumbnail__alt, thumbnail__title, thumbnail__width, thumbnail__height, uid, created, changed, default_langcode, revision_translation_affected) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14, :db_insert_placeholder_15); Array ( ) in Drupal\Core\Entity\Sql\SqlContentEntityStorage->save() (line 847 of /var/www/html/src/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).

(Captain Obvious Note: When updating the media entity if you add an owner no errors are thrown.)

I open this issue for people who need a temporary solution.
The issue #3043725: Provide a Entity Handler for user cancelation try to find a generic solution and could take a while.

Proposed resolution

The first proposed solution is a no-brainer: copy/paste what is done for node.

Remaining tasks

Find a quick win better than copy/paste while waiting for #3043725: Provide a Entity Handler for user cancelation.

User interface changes

None

API changes

None

Data model changes

Media entities updated correctly on user cancel.

Issue fork drupal-3069291

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

beram created an issue. See original summary.

beram’s picture

Here's a temporary solution: big copy/paste what is done for node.

(adding the 8.7.x patch because we need it for our sites)

beram’s picture

Title: Media entities support user cancel » Quick win: Media entities support user cancel
chris matthews’s picture

Status: Active » Needs review
ducktape’s picture

Tested this in my project with 8.7.5, works like a charm.
Thanks a lot!

phenaproxima’s picture

Adding #3057004: Not having an author when editing/creating a Media Entity results in an EntityStorageException as a related issue. +1 for this idea, overall; I'm not sure we necessarily want to copy all that code from the Node module, though. I'd prefer a smaller workaround as in the issue I just linked, to be honest.

Thanks for writing the tests, though -- that's really useful!

phenaproxima’s picture

spokje’s picture

I separated the test only from #2 to see if the (much) smaller patch in this issue 3057004#2 already would suffice.

Let's try the test only first.

spokje’s picture

StatusFileSize
new14.49 KB

Excellent test by Beram there!

Let's see if (rather minimalistic) patch from olivier.bouwman in 3057004#2 does the trick

spokje’s picture

And a D8.8 test only patch derived from #2

spokje’s picture

So patch #9 isn't enough to cover the issues in the test.

EDIT: After a nights sleep: Testing #9 like that isn't fair. It solves the problem differently, and never triggers any of the things that test #8 looks for.

#2: Solves the problem at the root: When a user is deleted, its content is either deleted or assigned to uid = 0.
Patch #9 is more like a Just In Time solution: Whenever a Media entity is saved and the owning user was deleted at some point, it sets the owner to uid = 0 in a presave hook, preventing Drupal go CLUNK on the actual save.

Whilst #9 has far less code, it only fixes the problem for Media entities that are being edited. That means it leaves Media entities without an existing owner untouched when they're not edited. In my humble opinion this leaves the database in an undesirable state, where Media entities claim to have an existing owner, but they haven't.

Also for Drupal-wide consistency, #2 seems the way to go.
Luckily I am in no position to make a ruling on this :)

spokje’s picture

Version: 8.8.x-dev » 8.7.x-dev
Assigned: Unassigned » spokje
Status: Needs review » Needs work

So now besides boring everybody with endless messages on this issue, I'm going to up the ante and now annoy everyone by putting it back to Needs Work.

Patch #2 works perfectly IMHO, but only for cases after it has been applied.
Which means that in the time between the launch of D8.7 and the landing of this patch, there will be (quite?) some cases out there in the wild, where a media entity owning user has already been deleted, leaving Media entities with invalid owners.

These Media entities aren't fixed by #2 and will cause fatal errors when trying to edit these entities.

I verified this by deleting a user that owned Media entities before applying patch #2, then applying patch #2, going to one of these Media entities and trying to save it.
That ended in tears and the "nice" combo of:

Drupal\Core\Database\IntegrityConstraintViolationException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'uid' cannot be null: INSERT INTO {media_field_data} (mid, vid, bundle, langcode, status, name, thumbnail__target_id, thumbnail__alt, thumbnail__title, thumbnail__width, thumbnail__height, uid, created, changed, default_langcode, revision_translation_affected, content_translation_source, content_translation_outdated) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14, :db_insert_placeholder_15, :db_insert_placeholder_16, :db_insert_placeholder_17); Array ( [:db_insert_placeholder_0] => 2535 [:db_insert_placeholder_1] => 2535 [:db_insert_placeholder_2] => images [:db_insert_placeholder_3] => en [:db_insert_placeholder_4] => 1 [:db_insert_placeholder_5] => Bea-Orpen [:db_insert_placeholder_6] => 2650 [:db_insert_placeholder_7] => Black and white photo of a woman seated at a table with an open book in front of her. [:db_insert_placeholder_8] => [:db_insert_placeholder_9] => 780 [:db_insert_placeholder_10] => 1056 [:db_insert_placeholder_11] => [:db_insert_placeholder_12] => 1566569018 [:db_insert_placeholder_13] => 1566889711 [:db_insert_placeholder_14] => 1 [:db_insert_placeholder_15] => 1 [:db_insert_placeholder_16] => und [:db_insert_placeholder_17] => 0 ) in Drupal\Core\Database\Connection->handleQueryException() (line 689 of /app/web/core/lib/Drupal/Core/Database/Connection.php).
and
Drupal\Core\Entity\EntityStorageException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'uid' cannot be null: INSERT INTO {media_field_data} (mid, vid, bundle, langcode, status, name, thumbnail__target_id, thumbnail__alt, thumbnail__title, thumbnail__width, thumbnail__height, uid, created, changed, default_langcode, revision_translation_affected, content_translation_source, content_translation_outdated) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14, :db_insert_placeholder_15, :db_insert_placeholder_16, :db_insert_placeholder_17); Array ( [:db_insert_placeholder_0] => 2535 [:db_insert_placeholder_1] => 2535 [:db_insert_placeholder_2] => images [:db_insert_placeholder_3] => en [:db_insert_placeholder_4] => 1 [:db_insert_placeholder_5] => Bea-Orpen [:db_insert_placeholder_6] => 2650 [:db_insert_placeholder_7] => Black and white photo of a woman seated at a table with an open book in front of her. [:db_insert_placeholder_8] => [:db_insert_placeholder_9] => 780 [:db_insert_placeholder_10] => 1056 [:db_insert_placeholder_11] => [:db_insert_placeholder_12] => 1566569018 [:db_insert_placeholder_13] => 1566889711 [:db_insert_placeholder_14] => 1 [:db_insert_placeholder_15] => 1 [:db_insert_placeholder_16] => und [:db_insert_placeholder_17] => 0 ) in Drupal\Core\Entity\Sql\SqlContentEntityStorage->save() (line 847 of /app/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).

I think this patch needs to come with a hook_update_N that identifies and "fixes" these Media entities.
Since we have no (easy) way of knowing what the intentions for the content items owned by the deleted user were at the time of deletion, I think we should take the safest approach and set the owner of these Media entities to Anonymous (uid = 0).

To lower the annoyance level and since I now already have a database with a testcase, I will try to upload such a hook_update_N shortly.

TLDR:
- Patch #2 works perfectly, but only for user-deletions after it has landed.
- Media Entity owning Users which were deleted before landing will lead to editing those Media entities causing a Back-end breaking Error even after the landing of #2.
- hook_update_N is needed to identify and "fix" those Media entities. Safest approach is to set their owner to uid = 0.
- For that setting this issue to "Needs Work" and since the longer we wait. the bigger the amount of user deletions will be. setting the version of this issue to 8.7.x-dev

beram’s picture

Thanks for the tests!

I opened this issue not to merge this dumb solution but just to provide a quick fix, a temporary solution fo people that needs to cancel users.

IMHO we should focus our effort in Some content entities are missing hook_user_cancel implementations:

Maybe we should have a generic implementation for entity types that implement EntityOwnerInterface and EntityPublishedInterface.

Because we cannot copy/paste this and https://www.drupal.org/project/drupal/issues/3057004 for all entities that needs it and because canceling users is not the only point (for instance the owner is mandatory but the interface does not show it for a media).

About the hook_update_N here the one I used on my sites. Should we add it to this patch? My first thought was people should add it to their own custom module to avoid issues with the hook_update_N versions.

/**
 * Make sure media entities with non-existent owner has one (anonymous).
 */
function mymodule_update_8001() {
  $database = \Drupal::database();

  $definitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
  $mediaEntityType = $definitionUpdateManager->getEntityType('media');
  $userEntityType = $definitionUpdateManager->getEntityType('user');

  $userBaseTable = $userEntityType->getBaseTable();

  $updateOwnerForTheGivenTable = function ($table) use ($database, $userBaseTable) {
    if (FALSE === $database->schema()->tableExists($table)) {
      return [];
    }

    $query = $database->select($table, 'm');
    $query->fields('m', ['mid']);
    $query->leftJoin($userBaseTable, 'u', 'm.uid = u.uid');
    $query->isNull('u.uid');
    $mediaIds = $query->execute()
      ->fetchCol('mid');

    if (count($mediaIds) <= 0) {
      return [];
    }

    $database->update($table)
      ->fields(['uid' => 0])
      ->condition('mid', $mediaIds, 'IN')
      ->execute();

    return $mediaIds;
  };

  $dataIds = $updateOwnerForTheGivenTable($mediaEntityType->getDataTable());
  $revisionDataIds = $updateOwnerForTheGivenTable($mediaEntityType->getRevisionDataTable());

  return t(
    'Number of media updated: @countMedia - Number of media revision updated: @countRevision',
    [
      '@countMedia' => count($dataIds),
      '@countRevision' => count($revisionDataIds),
    ]
  );
}

spokje’s picture

Assigned: spokje » Unassigned

Thanks @beram (also for your patch and tests :).

I fully agree we need a generic solution and we need it fast. I've already encounter this problem with Media, File and Taxonomy having put "only" 11 of our sites on D8.7.6 in the last 2 weeks.

However, I also believe that the generic solution will take (some/a lot of?) time to get fully implemented tested and with back-end breaking consequences leading to sad/angry/slightly disappointed users/clients, I don't think we have that time.

Having said that, I'm heading over to the "Generic Solution" issue, change it to priority Major and version 8.7.x-dev and ask the Big Brains around there to come up with a verdict on focussing on the Generic Solution or going with your approach, plugging the current holes in File/Media/Taxonomy first and then refactoring into something nice and generic.

spokje’s picture

@phenaproxima What's your position on Quick Win and Do-It-Generic later vs. Do-It-Generic in the first place?

It doesn't make much sense to put in effort on this issue if you and other Big Brains are not willing to go for the Quick Win first.

beram’s picture

Thinking out loud: a better quick win for the user cancel issue could be to convert node_mass_update() to entity_mass_update()?

spokje’s picture

StatusFileSize
new20.08 KB

Added a test to show the actual errors this issue is causing when trying to save a Media entity that has a deleted owner for #3043725: Provide a Entity Handler for user cancelation, might as well add it into this patch.

D8.7 Test-only first

spokje’s picture

StatusFileSize
new29.53 KB

Combining the excellent work of beram with new test.

spokje’s picture

StatusFileSize
new29.47 KB
new20.03 KB
new819 bytes

That's what you get when combining excellent with crappy work: crappy work... :/

This should be better.

ducktape’s picture

Version: 8.7.x-dev » 8.9.x-dev
Status: Needs work » Needs review
StatusFileSize
new29.57 KB

Updated for 8.8 and 8.9

Status: Needs review » Needs work

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

ducktape’s picture

Status: Needs work » Needs review
StatusFileSize
new32.14 KB
new18.93 KB

Fixed deprecations for 9.x.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

aaronmchale’s picture

Issue summary: View changes

Fixing issue link in IS.

aaronmchale’s picture

#3043725: Provide a Entity Handler for user cancelation is coming along nicely and already has core committer buy-in for the proposed resolution, so at this rate it might make more sense to just postpone this issue and resume once that issue is committed.

aaronmchale’s picture

Issue summary: View changes

Another link.

phenaproxima’s picture

phenaproxima’s picture

Status: Needs review » Closed (duplicate)

I think that this problem would be solved by #3043725: Provide a Entity Handler for user cancelation, so I'm closing this issue as a duplicate.

fernly’s picture

StatusFileSize
new31.7 KB

For the time being I rerolled this patch for 9.1.x based on 9.1.6.

fernly’s picture

StatusFileSize
new32.17 KB

Rerolled patch for 9.1.10.

mpp’s picture

Version: 9.1.x-dev » 9.3.x-dev

mpp’s picture

Version: 9.3.x-dev » 9.2.x-dev
StatusFileSize
new32 KB
fernly’s picture

Version: 9.2.x-dev » 9.3.x-dev
StatusFileSize
new31.77 KB

Rerolled patch for latest 9.3.x

redzeuf’s picture

StatusFileSize
new1.45 KB

Simple hook patch to reassign to the anonymous user whatever we select the unpublish or reassign method.
I used a direct query to change the uid and revision_uid for performance reason (not loading and resaving medias).
I did not integrate the tests.

redzeuf’s picture

StatusFileSize
new1.47 KB
new893 bytes

Just added a clear cache of the media entities, to the previous patch.
Without this clear cache we still have the error untill the media cache or rebuild.

redzeuf’s picture

StatusFileSize
new1.68 KB
new679 bytes

Error in last commit, I removed UserInterface class :(

redzeuf’s picture

After deploying on my dev/staging env. with Varnish I had an issue with the cache because my past patch was deleting directly the cache table. In this patch I with a drupal way function to clear full cache.

fernly’s picture

StatusFileSize
new1.65 KB

Because #3043725: Provide a Entity Handler for user cancelation is not ready yet, I rerolled this quick fix against drupal 11.x-dev.

fernly’s picture

StatusFileSize
new1.68 KB

And another patch specifically for Drupal 11.1.7. Apparently the latest D11 dev version already differs from Drupal 11.1.7.

So when you are higher than Drupal 11.1.7, you might need the patch posted in #39.

fernly’s picture

Version: 9.3.x-dev » 11.3.x-dev
StatusFileSize
new1.68 KB

Issue is closed (duplicate) but since there is no working solution yet, updating this patch.
Attached patch for use with Drupal 11.3.3.