Drupal - How do I prevent a Node from being deleted?
Using hook_node_access()
is one option:
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessResult;
...
function MYMODULE_node_access(EntityInterface $entity, $operation, AccountInterface $account) {
if ($operation == 'delete') {
if (some_access_function($entity, $account)) {
return AccessResult::allowed();
}
else {
return AccessResult::forbidden();
}
}
return AccessResult::neutral();
}
This method has the advantage of preventing deletion through other methods (e.g. REST) at the same time, and will automatically remove the delete link and tab from the node form anyway.
What kind of check?
What's easy is to prevent delete access. Implement the hook hook_node_access(NodeInterface $node, $operation, AccountInterface $account)
and return AccessResult::forbidden() if your condition matches for $operation delete.
That doesn't prevent deletion through the API but it will automatically hide any delete button/link/action in the UI and users won't be able to delete that node.
This does what I want.
The Agenda Node cannot be deleted if there are any Meeting Nodes that refer to it.
Ideally, I’d have preferred that the validation handler would be called when the user clicked the Delete on the primary tasks tabs whereas it is called on the confirmation form. Good enough, though.
use Drupal\Core\Form\FormStateInterface;
function agenda_form_alter(&$form, FormStateInterface $form_state, $form_id) {
switch ($form_id) {
// ...
case "node_agenda_delete_form":
$form['#validate'][] = '_agenda_delete_validation';
break;
// ...
}
}
function _agenda_delete_validation(&$form, FormStateInterface $form_state) {
$agenda_node = $form_state->getFormObject()->getEntity();
$agenda_name = $agenda_node->getTitle();
$agenda_node_id = $agenda_node->id();
$query = \Drupal::entityQuery('node')
->condition('type', 'meeting')
->condition('field_agenda', $agenda_node_id);
$nids = $query->execute();
$count_nids = count($nids);
if ($count_nids > 0) {
$msg_stem = ($count_nids == 1) ? 'There is one Meeting that references "' : 'There are '.$count_nids.' Meetings that reference "';
$form_state->setError($form, $msg_stem.$agenda_name.'". It cannot be deleted yet.' );
}
}
With many thanks to @Clive and @Berdir who gave great answers that I was able to follow up.