The code that needs to figure out whether a deletion is a revert is flawed; taken from EntityAPIControllerExportable::invoke() :

      // To ease figuring out whether this is a revert, make sure that the
      // entity status is updated in case the providing module has been
      // disabled.
      if (entity_has_status($this->entityType, $entity, ENTITY_IN_CODE) && !module_exists($entity->{$this->moduleKey})) {
        $entity->{$this->statusKey} = ENTITY_CUSTOM;
      }
      $is_revert = entity_has_status($this->entityType, $entity, ENTITY_IN_CODE);

      // ... snip ...

      elseif ($hook == 'delete' && !$is_revert) {
        field_attach_delete_bundle($type, $entity->{$this->bundleKey});
      }

This will not fire if a module (thinking of Features) decides to delete a default entity from its code:

  1. Feature A contains exportable entities X, Y and Z in code
  2. Feature A deletes entity Z from its code
  3. The above code will not flag the entity as ENTITY_CUSTOM because the module still exists
  4. The above code will flag the deletion as a revert, leaving the fields on the entity which no longer exists

In the Group module, I check for a revert like so; taken from GroupTypeController::delete() :

  public function delete($ids, DatabaseTransaction $transaction = NULL) {
    $gids = array();

    foreach (group_types($ids) as $group_type) {
      // If an entity is deleted while it was flagged as ENTITY_IN_CODE, it
      // means the entity was either reverted or really deleted. By checking
      // for the 'is_rebuild' property, we know it was deleted from within
      // _entity_defaults_rebuild() which only deletes the entity if the
      // default it came from is no longer available. In any other case, we
      // are dealing with a revert or a manual deletion which will only result
      // in the entity being rebuilt upon next cache clear.
      $entity_in_code = entity_has_status('group_type', $group_type, ENTITY_IN_CODE);
      $entity_rebuilt = !empty($group_type->is_rebuild);

      // Set this on the group type so other modules can use it.
      $group_type->is_revert = $entity_in_code && !$entity_rebuilt;
    }

    // Delete the group types after setting our flags so those flags are still
    // being passed on to other modules implementing hook_group_delete().
    parent::delete($ids, $transaction);
  }

Perhaps this extra flag should go into EntityAPIControllerExportable::delete()?

Comments

kristiaanvandeneynde’s picture

Issue summary: View changes
kenorb’s picture

Priority: Critical » Major
kristiaanvandeneynde’s picture

Just ran into this one again today :(