You are here

node_access_rebuild_progressive.module in Node Access Rebuild Progressive 8

Provides ability to rebuild node access silently.

File

node_access_rebuild_progressive.module
View source
<?php

/**
 * @file
 * Provides ability to rebuild node access silently.
 */

/**
 * Implements hook_cron().
 */
function node_access_rebuild_progressive_cron() {
  $config = \Drupal::config('node_access_rebuild_progressive.settings');

  // Not enabled on cron, nothing to do.
  if (!$config
    ->get('cron')) {
    return;
  }

  // Trigger the processing.
  if (node_access_needs_rebuild()) {
    node_access_rebuild_progressive_trigger();
  }

  // Process a batch of nodes if needed.
  if (\Drupal::state()
    ->get('node_access_rebuild_progressive.current') > 0) {
    node_access_rebuild_progressive_process_cron();
  }
}

/**
 * Initiate the full rebuild.
 */
function node_access_rebuild_progressive_trigger() {

  // Prevents default message/behaviour.
  node_access_needs_rebuild(FALSE);

  // Add default grants in the unlikely case
  // no modules implement node_grants anymore.
  if (!count(\Drupal::moduleHandler()
    ->getImplementations('node_grants'))) {
    node_access_rebuild_progressive_set_default();
    return node_access_rebuild_progressive_finished();
  }

  // Clean up possible mismatches.
  db_query("DELETE FROM {node_access} WHERE nid NOT IN (SELECT nid FROM {node})");

  // We only need the current one, nodes created afterward would get processed
  // at save time.
  $highest = db_query("SELECT nid FROM {node} ORDER BY nid DESC LIMIT 0,1")
    ->fetchField();
  $count = db_query("SELECT count(nid) FROM {node}")
    ->fetchField();
  \Drupal::state()
    ->set('node_access_rebuild_progressive.current', $highest + 1);
  \Drupal::logger('node_access_rebuild_progressive')
    ->info('%count nodes queued for node access rebuild.', [
    '%count' => $count,
  ]);
}

/**
 * Reset grants to a clean state.
 */
function node_access_rebuild_progressive_set_default() {
  $access_control_handler = \Drupal::entityManager()
    ->getAccessControlHandler('node');
  $access_control_handler
    ->deleteGrants();
  $access_control_handler
    ->writeDefaultGrant();
}

/**
 * Processes a chunk of nodes at cron run.
 */
function node_access_rebuild_progressive_process_cron() {
  $pass = node_access_rebuild_progressive_process_chunk();
  if (empty($pass['total'])) {
    return node_access_rebuild_progressive_finished();
  }
}

/**
 * Processes a chunk of nodes.
 *
 * @return array
 *   An array with the following keys:
 *   - total: the number of nodes retrieved for processing.
 *   - processed: the number of nodes actually processed.
 */
function node_access_rebuild_progressive_process_chunk() {
  $current = \Drupal::state()
    ->get('node_access_rebuild_progressive.current');
  $chunk_size = \Drupal::config('node_access_rebuild_progressive.settings')
    ->get('chunk');
  $nids = db_query("SELECT nid FROM {node} WHERE nid < :current ORDER BY nid DESC LIMIT 0,{$chunk_size}", [
    ':current' => $current,
  ])
    ->fetchCol();
  $total = count($nids);
  $processed = 0;
  if ($total) {
    $access_control_handler = \Drupal::entityManager()
      ->getAccessControlHandler('node');
    $node_storage = \Drupal::entityManager()
      ->getStorage('node');
    $nodes = $node_storage
      ->loadMultiple($nids);
    $node_storage
      ->resetCache();
    foreach ($nodes as $node) {

      // Make sure the node can be loaded properly.
      if (!empty($node
        ->id())) {
        $grants = $access_control_handler
          ->acquireGrants($node);
        \Drupal::service('node.grant_storage')
          ->write($node, $grants);
        $current = $node
          ->id();
        $processed++;
      }
    }
    \Drupal::state()
      ->set('node_access_rebuild_progressive.current', $current);
  }
  return [
    'total' => $total,
    'processed' => $processed,
  ];
}

/**
 * Cleanup after queue completion.
 */
function node_access_rebuild_progressive_finished() {
  \Drupal::state()
    ->set('node_access_rebuild_progressive.current', 0);
  \Drupal::logger('node_access_rebuild_progressive')
    ->notice('Node access rebuild finished.', []);
}

/**
 * Rebuilds the node access grants table.
 */
function _drush_node_access_rebuild_progressive_rebuild() {
  node_access_rebuild_progressive_trigger();
  $finished = FALSE;
  $total = db_query("SELECT count(nid) FROM {node}")
    ->fetchField();
  $processed = 0;
  while (!$finished) {
    $cmd = '_drush_node_access_rebuild_progressive_process(' . $total . ',' . $processed . ');';
    $data = drush_invoke_process('@self', 'php-eval', [
      $cmd,
    ]);
    $processed = $data['context']['_drush_node_access_rebuild_progressive_processed'];
    $finished = drush_get_error() || !$data || isset($data['context']['_drush_node_access_rebuild_progressive_finished']) && $data['context']['_drush_node_access_rebuild_progressive_finished'] === TRUE;
  }
}

/**
 * Processes a pass of nodes.
 *
 * @param int $total
 *   Number of nodes to process.
 * @param int $processed
 *   Number of nodes processed.
 */
function _drush_node_access_rebuild_progressive_process(int $total, int $processed) {
  $pass = node_access_rebuild_progressive_process_chunk();
  $processed += $pass['processed'];
  drush_set_option('_drush_node_access_rebuild_progressive_processed', $processed);
  $figures = [
    '@pass' => $pass['processed'],
    '@nodes' => $pass['total'],
    '@processed' => $processed,
    '@total' => $total,
  ];
  if (empty($pass['total'])) {
    drush_set_option('_drush_node_access_rebuild_progressive_finished', TRUE);
    return;
  }
  drush_print(dt('Processed @pass of @nodes nodes (@processed/@total).', $figures));
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function node_access_rebuild_progressive_form_node_configure_rebuild_confirm_alter(&$form) {
  $config = \Drupal::config('node_access_rebuild_progressive.settings');
  $form['description']['#markup'] = '<p>This form has been disabled by the node_access_rebuild_progressive module.</p>';
  $form['description']['#markup'] .= '<p>You can manually rebuild the permissions using Drush: <strong>drush node-access-rebuild-progressive</strong>.</p>';
  if ($config
    ->get('cron')) {
    $form['description']['#markup'] .= '<p>Else they will be rebuilt incrementally during Drupal cron run.</p>';
  }
  $form['#disabled'] = TRUE;
}