You are here

nodeorder.module in Node Order 8

Same filename and directory in other branches
  1. 5 nodeorder.module
  2. 6 nodeorder.module
  3. 7 nodeorder.module

Nodeorder module.

File

nodeorder.module
View source
<?php

/**
 * @file
 * Nodeorder module.
 */
use Drupal\Core\Cache\Cache;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\taxonomy\Entity\Term;

/**
 * @defgroup nodeorder Module functions
 * @{
 * Additional functions and hooks for Node Order module.
 */

/**
 * Implements hook_module_implements_alter().
 */
function nodeorder_module_implements_alter(&$implementations, $hook) {
  if ($hook === 'node_update') {
    $group = $implementations['nodeorder'];
    unset($implementations['nodeorder']);
    $implementations['nodeorder'] = $group;
  }
}

/**
 * Implements hook_entity_operation().
 */
function nodeorder_entity_operation(EntityInterface $entity) {
  $operations = [];
  if ($entity instanceof Term) {

    /** @var \Drupal\nodeorder\NodeOrderManagerInterface $nodeorder_manager */
    $nodeorder_manager = \Drupal::service('nodeorder.manager');
    if ($nodeorder_manager
      ->vocabularyIsOrderable($entity
      ->getVocabularyId())) {
      $operations['order'] = [
        'title' => t('Order'),
        'query' => \Drupal::destination()
          ->getAsArray(),
        'url' => Url::fromRoute('nodeorder.admin_order', [
          'taxonomy_term' => $entity
            ->id(),
        ]),
        'weight' => 20,
      ];
    }
  }
  return $operations;
}

/**
 * Implements hook_form_FORM_ID_alter() for taxonomy_form_vocabulary().
 */
function nodeorder_form_taxonomy_vocabulary_form_alter(&$form, FormStateInterface $form_state) {
  if ($form_state
    ->get('confirm_delete')) {
    return;
  }

  /** @var \Drupal\nodeorder\NodeOrderManagerInterface $nodeorder_manager */
  $nodeorder_manager = \Drupal::service('nodeorder.manager');
  $vocabulary = $form_state
    ->getFormObject()
    ->getEntity();
  $form['nodeorder'] = [
    '#type' => 'fieldset',
    '#title' => t('Node Order'),
    '#weight' => 1,
  ];
  $form['nodeorder']['orderable'] = [
    '#type' => 'checkbox',
    '#title' => t('Orderable'),
    '#description' => t('If enabled, nodes may be ordered within this vocabulary.'),
    '#default_value' => $nodeorder_manager
      ->vocabularyIsOrderable($vocabulary
      ->id()),
  ];

  // Add a submit handler for saving the orderable settings.
  $form['actions']['submit']['#submit'][] = 'nodeorder_taxonomy_vocabulary_form_submit';
}

/**
 * Submit handler for nodeorder_form_taxonomy_vocabulary_form_alter().
 */
function nodeorder_taxonomy_vocabulary_form_submit($form, FormStateInterface $form_state) {

  /** @var \Drupal\nodeorder\NodeOrderManagerInterface $nodeorder_manager */
  $nodeorder_manager = \Drupal::service('nodeorder.manager');
  $vocabulary = $form_state
    ->getFormObject()
    ->getEntity();
  $config = \Drupal::configFactory()
    ->getEditable('nodeorder.settings');
  $orderable = $form_state
    ->getValue('orderable');
  $vocabularies = $config
    ->get('vocabularies');
  if ($orderable && empty($vocabularies[$vocabulary
    ->id()])) {

    // Switching from non-orderable to orderable.
    Cache::invalidateTags([
      'nodeorder',
    ]);

    // Set weight to nid for all rows in term_node where the tid is in this
    // vocabulary.
    $tree = \Drupal::service('entity_type.manager')
      ->getStorage("taxonomy_term")
      ->loadTree($vocabulary
      ->id());
    $tids = [];
    foreach ($tree as $term) {
      $tids[] = $term->tid;
    }
    if (count($tids) > 0) {
      $order = 'n.sticky DESC, tn0.weight';
      $tid = 0;
      $query_max = \Drupal::database()
        ->select('taxonomy_index', 'ti')
        ->condition('tid', $tid);
      $query_max
        ->addExpression('MAX(weight)', 'mweight');
      foreach ($tids as $tid) {

        // Select *current* nodes for the current term.
        // @todo: Replace this hacked function call.
        $result = $nodeorder_manager
          ->selectNodes([
          $tid,
        ], 'and', 0, FALSE, $order, 0);
        foreach ($result as $node) {
          $max = $query_max
            ->execute()
            ->fetchField();
          $max = (int) $max + 1;
          \Drupal::database()
            ->update('taxonomy_index')
            ->condition('nid', $node->nid)
            ->condition('tid', $tid)
            ->fields([
            'weight' => $max,
          ])
            ->execute();
        }
      }
    }
    \Drupal::messenger()
      ->addStatus(t('You may now order nodes within this vocabulary.'));
  }
  elseif (!$orderable && !empty($vocabularies[$vocabulary
    ->id()])) {

    // Switching from orderable to non-orderable.
    Cache::invalidateTags([
      'nodeorder',
    ]);

    // Set weight to 0 for all rows in term_node where the tid is in this
    // vocabulary.
    $tree = \Drupal::service('entity_type.manager')
      ->getStorage("taxonomy_term")
      ->loadTree($vocabulary
      ->id());
    $tids = [];
    foreach ($tree as $term) {
      $tids[] = $term->tid;
    }
    if (count($tids) > 0) {
      \Drupal::database()
        ->update('taxonomy_index')
        ->fields([
        'weight' => 0,
      ])
        ->condition('tid', $tids, 'IN')
        ->execute();
    }
    \Drupal::messenger()
      ->addStatus(t('You may no longer order nodes within this vocabulary.'));
  }

  // Update config.
  $vocabularies[$vocabulary
    ->id()] = $orderable;
  $config
    ->set('vocabularies', $vocabularies);
  $config
    ->save();
}

/**
 * Implements hook_node_presave().
 */
function nodeorder_node_presave(NodeInterface $node) {

  /** @var \Drupal\nodeorder\NodeOrderManagerInterface $nodeorder_manager */
  $nodeorder_manager = \Drupal::service('nodeorder.manager');
  if ($nodeorder_manager
    ->canBeOrdered($node)) {

    // Store the old node orders for use in nodeorder_node_update().
    $node->nodeorder = [];

    // When a node is created, store an element called 'nodeorder' that
    // contains an associative array of tid to weight.
    $query = \Drupal::database()
      ->select('taxonomy_index', 'ti')
      ->fields('ti', [
      'tid',
      'weight',
    ])
      ->condition('nid', $node
      ->id());
    $result = $query
      ->execute();
    foreach ($result as $term_node) {
      $node->nodeorder[$term_node->tid] = $term_node->weight;
    }
  }
}

/**
 * Implements hook_node_delete().
 *
 * Handle lists in which the node is removed.
 */
function nodeorder_node_delete(NodeInterface $node) {

  /** @var \Drupal\nodeorder\NodeOrderManagerInterface $nodeorder_manager */
  $nodeorder_manager = \Drupal::service('nodeorder.manager');

  // Get tids from node var; in the database they're already removed.
  $tids = $nodeorder_manager
    ->getOrderableTidsFromNode($node);
  foreach ($tids as $tid) {
    $nodeorder_manager
      ->handleListsDecrease($tid);
  }
}

/**
 * Implements hook_node_insert().
 *
 * Handle the weights of the node in the taxonomy orderable lists it id added.
 */
function nodeorder_node_insert(NodeInterface $node) {

  /** @var \Drupal\nodeorder\NodeOrderManagerInterface $nodeorder_manager */
  $nodeorder_manager = \Drupal::service('nodeorder.manager');
  $tids = $nodeorder_manager
    ->getOrderableTids($node, TRUE);
  foreach ($tids as $tid) {
    $nodeorder_manager
      ->addToList($node, $tid);
  }
}

/**
 * Implements hook_node_update().
 *
 * Handle the weights, which were reset on rebuild of the taxonomy.
 */
function nodeorder_node_update(NodeInterface $node) {

  /** @var \Drupal\nodeorder\NodeOrderManagerInterface $nodeorder_manager */
  $nodeorder_manager = \Drupal::service('nodeorder.manager');
  if (!$nodeorder_manager
    ->canBeOrdered($node)) {
    return;
  }
  $tids = $nodeorder_manager
    ->getOrderableTids($node, TRUE);
  $old_tids = $node->nodeorder;
  foreach ($tids as $tid) {

    // Restore weight of unchanged terms, or leave as is if zero.
    if (isset($old_tids[$tid])) {
      $old_weight = $old_tids[$tid];
      unset($old_tids[$tid]);
      if (!$old_weight) {
        continue;
      }
      \Drupal::database()
        ->update('taxonomy_index')
        ->fields([
        'weight' => $old_weight,
      ])
        ->condition('nid', $node
        ->id())
        ->condition('tid', $tid)
        ->execute();
    }
    else {
      $nodeorder_manager
        ->addToList($node, $tid);
    }
  }

  // Handle lists in which the node is removed.
  // Note that the old tids are at this point only the ones that were not
  // updated, the others were dropped when restoring above.
  foreach ($old_tids as $tid => $weight) {
    $nodeorder_manager
      ->handleListsDecrease($tid);
  }
}

/**
 * Implements hook_help().
 */
function nodeorder_help($route_name, RouteMatchInterface $route_match) {
  $output = '';
  switch ($route_name) {
    case 'entity.taxonomy_vocabulary.overview_form':

      /** @var \Drupal\nodeorder\NodeOrderManagerInterface $nodeorder_manager */
      $nodeorder_manager = \Drupal::service('nodeorder.manager');
      $vocabulary = $route_match
        ->getParameter('taxonomy_vocabulary');
      if ($nodeorder_manager
        ->vocabularyIsOrderable($vocabulary
        ->id())) {
        $output = '<p>' . t('%capital_name is an orderable vocabulary. You may order the nodes associated with a term within this vocabulary by clicking the down arrow in the <em class="placeholder">Edit</em> Button and selecting <em class="placeholder">Order</em>.', [
          '%capital_name' => ucfirst($vocabulary
            ->label()),
        ]);
      }
      break;
    case 'nodeorder.admin_order':
      $output = '<p>' . t('This page provides a drag-and-drop interface for ordering nodes. To change the order of nodes, grab a drag-and-drop handle next to the <em class="placeholder">Title</em> column and drag the node to a new location in the list. Your changes will be saved only when you click the <em class="placeholder">Save</em> button at the bottom of the page.') . '</p>';
      break;
  }
  return $output;
}

/**
 * @} End of "defgroup nodeorder".
 */

Functions

Namesort descending Description
nodeorder_entity_operation Implements hook_entity_operation().
nodeorder_form_taxonomy_vocabulary_form_alter Implements hook_form_FORM_ID_alter() for taxonomy_form_vocabulary().
nodeorder_help Implements hook_help().
nodeorder_module_implements_alter Implements hook_module_implements_alter().
nodeorder_node_delete Implements hook_node_delete().
nodeorder_node_insert Implements hook_node_insert().
nodeorder_node_presave Implements hook_node_presave().
nodeorder_node_update Implements hook_node_update().
nodeorder_taxonomy_vocabulary_form_submit Submit handler for nodeorder_form_taxonomy_vocabulary_form_alter().