Skip to content
node.module 64.8 KiB
Newer Older
Dries Buytaert's avatar
Dries Buytaert committed
<?php

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * @file
 * The core module that allows content to be submitted to the site.
 *
 * Modules and scripts may programmatically submit nodes using the usual form
 * API pattern.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */

use Drupal\Core\Language\Language;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\node\NodeTypeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 * Denotes that the node is not published.
 * Denotes that the node is published.
 * Denotes that the node is not promoted to the front page.
 * Denotes that the node is promoted to the front page.
 * Denotes that the node is not sticky at the top of the page.
 * Denotes that the node is sticky at the top of the page.
 * Denotes that access is allowed for a node.
 *
 * Modules should return this value from hook_node_access() to allow access to a
 * node.
 * Denotes that access is denied for a node.
 *
 * Modules should return this value from hook_node_access() to deny access to a
 * node.
 * Denotes that access is unaffected for a node.
 *
 * Modules should return this value from hook_node_access() to indicate no
 * effect on node access.
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
  // Remind site administrators about the {node_access} table being flagged
  // for rebuild. We don't need to issue the message on the confirm form, or
  // while the rebuild is being processed.
  if ($path != 'admin/reports/status/rebuild' && $path != 'batch' && strpos($path, '#') === FALSE
      && user_access('access administration pages') && node_access_needs_rebuild()) {
    if ($path == 'admin/reports/status') {
      $message = t('The content access permissions need to be rebuilt.');
    }
    else {
      $message = t('The content access permissions need to be rebuilt. <a href="!node_access_rebuild">Rebuild permissions</a>.', array('!node_access_rebuild' => \Drupal::url('node.configure_rebuild_confirm')));
    }
    drupal_set_message($message, 'error');
  }

Dries Buytaert's avatar
 
Dries Buytaert committed
    case 'admin/help#node':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('The Node module manages the creation, editing, deletion, settings, and display of the main site content. Content items managed by the Node module are typically displayed as pages on your site, and include a title, some meta-data (author, creation time, content type, etc.), and optional fields containing text or other data (fields are managed by the <a href="!field">Field module</a>). For more information, see <a href="!node">the online documentation for the Node module</a>.', array('!node' => 'https://drupal.org/documentation/modules/node', '!field' => \Drupal::url('help.page', array('name' => 'field')))) . '</p>';
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Creating content') . '</dt>';
      $output .= '<dd>' . t('When new content is created, the Node module records basic information about the content, including the author, date of creation, and the <a href="!content-type">Content type</a>. It also manages the <em>publishing options</em>, which define whether or not the content is published, promoted to the front page of the site, and/or sticky at the top of content lists. Default settings can be configured for each <a href="!content-type">type of content</a> on your site.', array('!content-type' => \Drupal::url('node.overview_types'))) . '</dd>';
      $output .= '<dt>' . t('Creating custom content types') . '</dt>';
      $output .= '<dd>' . t('The Node module gives users with the <em>Administer content types</em> permission the ability to <a href="!content-new">create new content types</a> in addition to the default ones already configured. Creating custom content types allows you the flexibility to add <a href="!field">fields</a> and configure default settings that suit the differing needs of various site content.', array('!content-new' => \Drupal::url('node.type_add'), '!field' => \Drupal::url('help.page', array('name' => 'field')))) . '</dd>';
      $output .= '<dt>' . t('Administering content') . '</dt>';
      $output .= '<dd>' . t('The <a href="!content">Content administration page</a> allows you to review and bulk manage your site content.', array('!content' => \Drupal::url('node.content_overview'))) . '</dd>';
      $output .= '<dt>' . t('Creating revisions') . '</dt>';
      $output .= '<dd>' . t('The Node module also enables you to create multiple versions of any content, and revert to older versions using the <em>Revision information</em> settings.') . '</dd>';
      $output .= '<dt>' . t('User permissions') . '</dt>';
      $output .= '<dd>' . t('The Node module makes a number of permissions available for each content type, which can be set by role on the <a href="!permissions">permissions page</a>.', array('!permissions' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-node')))) . '</dd>';
      $output .= '</dl>';
      return '<p>' . t('Individual content types can have different fields, behaviors, and permissions assigned to them.') . '</p>';
    case 'admin/structure/types/manage/%/form-display':
      $type = entity_load('node_type', $arg[4]);
      return '<p>' . t('Content items can be edited using different form modes. Here, you can define which fields are shown and hidden when %type content is edited in each form mode, and define how the field form widgets are displayed in each form mode.', array('%type' => $type->label())) . '</p>' ;
      $type =  entity_load('node_type', $arg[4]);
      return '<p>' . t('Content items can be displayed using different view modes: Teaser, Full content, Print, RSS, etc. <em>Teaser</em> is a short format that is typically used in lists of multiple content items. <em>Full content</em> is typically used when the content is displayed on its own page.') . '</p>' .
        '<p>' . t('Here, you can define which fields are shown and hidden when %type content is displayed in each view mode, and define how the fields are displayed in each view mode.', array('%type' => $type->label())) . '</p>';
      return '<p>' . t('Revisions allow you to track differences between multiple versions of your content, and revert back to older versions.') . '</p>';
    case 'node/%/edit':
      $node = node_load($arg[1]);
      return (!empty($type->help) ? filter_xss_admin($type->help) : '');
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

  if ($arg[0] == 'node' && $arg[1] == 'add' && $arg[2]) {
    return (!empty($type->help) ? filter_xss_admin($type->help) : '');
Dries Buytaert's avatar
 
Dries Buytaert committed
}

    'node_add_list' => array(
      'variables' => array('content' => NULL),
      'variables' => array('node' => NULL),
    'node_edit_form' => array(
      'render element' => 'form',
      'template' => 'node-edit-form',
    ),
    'field__node__title' => array(
      'base hook' => 'field',
    ),
/**
 * Implements hook_entity_bundle_info().
 */
function node_entity_bundle_info() {
  $bundles = array();
  // Bundles must provide a human readable name so we can create help and error
  foreach (node_type_get_names() as $id => $label) {
    $bundles['node'][$id]['label'] = $label;
Dries Buytaert's avatar
 
Dries Buytaert committed
}

 * Implements hook_entity_view_display_alter().
function node_entity_view_display_alter(EntityViewDisplayInterface $display, $context) {
  if ($context['entity_type'] == 'node') {
    // Hide field labels in search index.
    if ($context['view_mode'] == 'search_index') {
      foreach ($display->getComponents() as $name => $options) {
        if (isset($options['label'])) {
          $options['label'] = 'hidden';
          $display->setComponent($name, $options);
        }
  }
}

/**
 * Implements hook_entity_form_display_alter().
 */
function node_entity_form_display_alter(EntityFormDisplayInterface $form_display, $context) {
  if ($context['entity_type'] == 'node') {
    $node_type = node_type_load($context['bundle']);
    // @todo Reconsider when per-bundle overrides of field definitions are
    //   possible - https://drupal.org/node/2114707.
    if (!$node_type->has_title) {
      $form_display->removeComponent('title');
    }
 * @param \Drupal\node\NodeInterface $node
 *   An array with 'path' as the key and the path to the node as its value.
function node_uri(NodeInterface $node) {
    'route_name' => 'node.view',
    'route_parameters' => array(
      'node' => $node->id(),
    ),
  if (\Drupal::config('node.settings')->get('use_admin_theme')) {
    $paths = array(
      'node/*/edit' => TRUE,
      'node/*/delete' => TRUE,
      'node/*/revisions' => TRUE,
      'node/*/revisions/*/revert' => TRUE,
      'node/*/revisions/*/delete' => TRUE,
      'node/*/translations' => TRUE,
      'node/*/translations/*' => TRUE,
      'node/add' => TRUE,
      'node/add/*' => TRUE,
    );
    return $paths;
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * @param $result
 *   A database result object from a query to fetch node entities. If your
 *   query joins the {comment_entity_statistics} table so that the comment_count
 *   field is available, a title attribute will be added to show the number of
 *   comments.
Dries Buytaert's avatar
 
Dries Buytaert committed
 * @param $title
 *   (optional) A heading for the resulting list.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * @return
 *   A renderable array containing a list of linked node titles fetched from
 *   $result, or FALSE if there are no rows in $result.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
Dries Buytaert's avatar
 
Dries Buytaert committed
function node_title_list($result, $title = NULL) {
    // Do not use $node->label() here, because $node comes from the database.
    $items[] = l($node->title, 'node/' . $node->nid, !empty($node->comment_count) ? array('attributes' => array('title' => format_plural($node->comment_count, '1 comment', '@count comments'))) : array());
Dries Buytaert's avatar
 
Dries Buytaert committed
  }

  return $num_rows ? array('#theme' => 'item_list__node', '#items' => $items, '#title' => $title) : FALSE;
Dries Buytaert's avatar
 
Dries Buytaert committed
}

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Determines the type of marker to be displayed for a given node.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
Dries Buytaert's avatar
 
Dries Buytaert committed
 * @param $nid
 *   Node ID whose history supplies the "last viewed" timestamp.
 * @param $timestamp
 *   Time which is compared against node's "last viewed" timestamp.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function node_mark($nid, $timestamp) {
Dries Buytaert's avatar
 
Dries Buytaert committed

  if (\Drupal::currentUser()->isAnonymous() || !\Drupal::moduleHandler()->moduleExists('history')) {
Dries Buytaert's avatar
Dries Buytaert committed
  if (!isset($cache[$nid])) {
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
  if ($cache[$nid] == 0 && $timestamp > HISTORY_READ_LIMIT) {
  elseif ($timestamp > $cache[$nid] && $timestamp > HISTORY_READ_LIMIT) {
Dries Buytaert's avatar
 
Dries Buytaert committed
}

/**
 * Returns a list of all the available node types.
 *
 * This list can include types that are queued for addition or deletion.
 *
 * @return array
 *   An array of node type entities, keyed by ID.
  return entity_load_multiple('node_type');
 * Returns a list of available node type names.
 *
 * This list can include types that are queued for addition or deletion.
 *   An array of node type labels, keyed by the node type name.
  $cid = 'node_type:names:' . \Drupal::languageManager()->getCurrentLanguage()->id;
  if ($cache = \Drupal::cache()->get($cid)) {
    return $cache->data;
  }
  // Not using node_type_get_types() or entity_load_multiple() here, to allow
  // this function being used in hook_entity_type_build() implementations.
  // @todo Consider to convert this into a generic config entity helper.
  $config_names = config_get_storage_names_with_prefix('node.type.');
  $names = array();
  foreach ($config_names as $config_name) {
    $config = \Drupal::config($config_name);
    $names[$config->get('type')] = $config->get('name');
  }
  \Drupal::cache()->set($cid, $names, Cache::PERMANENT, array(
    'node_type' => array_keys($names),
    'node_types' => TRUE,
  ));
  return $names;
}

/**
 * Returns the node type label for the passed node.
 *
 * @param \Drupal\node\NodeInterface $node
 *   A node entity to return the node type's label for.
 *
 * @return string|false
 *   The node type label or FALSE if the node type is not found.
 * @todo Add this as generic helper method for config entities representing
 *   entity bundles.
function node_get_type_label(NodeInterface $node) {
  $type = entity_load('node_type', $node->bundle());
  return $type ? $type->label() : FALSE;
}

/**
 * Description callback: Returns the node type description.
 *
 * @param \Drupal\node\NodeTypeInterface $node_type
function node_type_get_description(NodeTypeInterface $node_type) {
 * Menu argument loader: Loads a node type by string.
 *   The machine name of a node type to load.
 * @return \Drupal\node\NodeTypeInterface
 *   A node type object or NULL if $name does not exist.
  return entity_load('node_type', $name);
 * Adds the default body field to a node type.
 * @param \Drupal\node\NodeTypeInterface $type
 *   (optional) The label for the body instance.
function node_add_body_field(NodeTypeInterface $type, $label = 'Body') {
  $field = field_info_field('node', 'body');
  $instance = field_info_instance('node', 'body', $type->id());
    $field = entity_create('field_config', array(
      'name' => 'body',
      'entity_type' => 'node',
    $instance = entity_create('field_instance_config', array(
      'field_name' => 'body',
      'entity_type' => 'node',
      'label' => $label,
      'settings' => array('display_summary' => TRUE),
    // Assign widget settings for the 'default' form mode.
    entity_get_form_display('node', $type->type, 'default')
    // Assign display settings for the 'default' and 'teaser' view modes.
    entity_get_display('node', $type->type, 'default')
        'label' => 'hidden',
        'type' => 'text_default',
      ))
      ->save();
    entity_get_display('node', $type->type, 'teaser')
        'label' => 'hidden',
        'type' => 'text_summary_or_trimmed',
      ))
      ->save();
/**
 * Implements hook_field_extra_fields().
 */
function node_field_extra_fields() {
  $extra = array();
  $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language');
  $description = t('Node module element');

  foreach (node_type_get_types() as $bundle) {
    // Add the 'language' select if Language module is enabled and the bundle
    // has multilingual support.
    // Visibility of the ordering of the language selector is the same as on the
    // node/add form.
    if ($module_language_enabled) {
      $configuration = language_get_default_configuration('node', $bundle->type);
        $extra['node'][$bundle->type]['form']['langcode'] = array(
          'label' => t('Language'),
          'description' => $description,
          'weight' => 0,
        );
      }
    $extra['node'][$bundle->type]['display']['langcode'] = array(
      'label' => t('Language'),
      'description' => $description,
      'weight' => 0,
      'visible' => FALSE,
    );
 * Updates all nodes of one type to be of another type.
 *
 *   The number of nodes whose node type field was modified.
function node_type_update_nodes($old_id, $new_id) {
    ->fields(array('type' => $new_id))
    ->condition('type', $old_id)
Dries Buytaert's avatar
 
Dries Buytaert committed
}
Dries Buytaert's avatar
 
Dries Buytaert committed

 * Loads node entities from the database.
 *
 * This function should be used whenever you need to load more than one node
 * from the database. Nodes are loaded into memory and will not require database
 * access if loaded again during the same page request.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * @param array $nids
 *   (optional) An array of entity IDs. If omitted, all entities are loaded.
 *   (optional) Whether to reset the internal node_load() cache.  Defaults to
 *   FALSE.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * @return \Drupal\node\NodeInterface[]
 *   An array of node entities indexed by nid.
 * @see \Drupal\Core\Entity\Query\EntityQueryInterface
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function node_load_multiple(array $nids = NULL, $reset = FALSE) {
  return entity_load_multiple('node', $nids, $reset);
 * Loads a node entity from the database.
 *   (optional) Whether to reset the node_load_multiple() cache. Defaults to
 *   FALSE.
 * @return \Drupal\node\NodeInterface|null
 *   A fully-populated node entity, or NULL if the node is not found.
function node_load($nid = NULL, $reset = FALSE) {
}

/**
 * Loads a node revision from the database.
 *
 * @return \Drupal\node\NodeInterface|null
 *   A fully-populated node entity, or NULL if the node is not found.
 */
function node_revision_load($vid = NULL) {
  return entity_revision_load('node', $vid);
Dries Buytaert's avatar
 
Dries Buytaert committed
}

 * Deletes a node revision.
 *   TRUE if the revision deletion was successful; otherwise, FALSE.
  entity_revision_delete('node', $revision_id);
 * Checks whether the current page is the full page view of the passed-in node.
 * @param \Drupal\node\NodeInterface $node
 *
 * @return
 *   The ID of the node if this is a full page view, otherwise FALSE.
function node_is_page(NodeInterface $node) {
  $request = \Drupal::request();
  if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.view') {
    $page_node = $request->attributes->get('node');
  }
  return (!empty($page_node) ? $page_node->id() == $node->id() : FALSE);
/**
 * Implements hook_preprocess_HOOK() for HTML document templates.
 */
function node_preprocess_html(&$variables) {
  // If on an individual node page, add the node type to body classes.
  if (($node = \Drupal::request()->attributes->get('node')) && $node instanceof NodeInterface) {
    $variables['attributes']['class'][] = drupal_html_class('node-type-' . $node->getType());
  }
}

 * Implements hook_preprocess_HOOK() for block templates.
 */
function node_preprocess_block(&$variables) {
  if ($variables['configuration']['module'] == 'node') {
    switch ($variables['elements']['#plugin_id']) {
        $variables['attributes']['role'] = 'complementary';
/**
 * Implements hook_theme_suggestions_HOOK().
 */
function node_theme_suggestions_node(array $variables) {
  $suggestions = array();
  $node = $variables['elements']['#node'];

  $suggestions[] = 'node__' . $node->bundle();
  $suggestions[] = 'node__' . $node->id();

  return $suggestions;
}

 * Prepares variables for node templates.
 * Most themes utilize their own copy of node.html.twig. The default is located
 * inside "/core/modules/node/templates/node.html.twig". Look in there for the full
 * list of variables.
 *
 * @param array $variables
 *   An associative array containing:
 *   - elements: An array of elements to display in view mode.
 *   - node: The node object.
 *   - view_mode: View mode; e.g., 'full', 'teaser'...
 */
function template_preprocess_node(&$variables) {
  $variables['view_mode'] = $variables['elements']['#view_mode'];
  // Provide a distinct $teaser boolean.
  $variables['teaser'] = $variables['view_mode'] == 'teaser';
  $variables['node'] = $variables['elements']['#node'];
  /** @var \Drupal\node\NodeInterface $node */
  $variables['date'] = format_date($node->getCreatedTime());
    '#link_options' => array('attributes' => array('rel' => 'author')),
  $variables['label'] = $variables['elements']['title'];
  unset($variables['elements']['title']);
  $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node);
  $variables += array('content' => array());
  foreach (element_children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  }

  // Display post information only on certain node types.
  // Avoid loading the entire node type config entity here that may not exist.
  $node_type_config = \Drupal::config('node.type.' . $node->bundle());
  // Display submitted by default.
  $variables['display_submitted'] = $node_type_config->isNew() || $node_type_config->get('settings.node.submitted');
  if ($variables['display_submitted']) {
    $variables['submitted'] = t('Submitted by !username on !datetime', array('!username' => $variables['name'], '!datetime' => $variables['date']));
    if (theme_get_setting('features.node_user_picture')) {
      // To change user picture settings (e.g. image style), edit the 'compact'
      // view mode on the User entity. Note that the 'compact' view mode might
      // not be configured, so remember to always check the theme setting first.
      $variables['user_picture'] = user_view($node->getOwner(), 'compact');
    }
    else {
      $variables['user_picture'] = array();
    }
  $variables['attributes']['role'] = 'article';
  $variables['attributes']['class'][] = 'node';
  $variables['attributes']['class'][] = drupal_html_class('node-' . $node->bundle());
  if ($node->isPromoted()) {
    $variables['attributes']['class'][] = 'promoted';
    $variables['attributes']['class'][] = 'sticky';
    $variables['attributes']['class'][] = 'unpublished';
    $variables['attributes']['class'][] = drupal_html_class('view-mode-' . $variables['view_mode']);
    $variables['attributes']['class'][] = 'preview';
  $variables['content_attributes']['class'][] = 'content';
/**
 * Returns HTML for the node title field.
 *
 * This is an override of theme_field() for the node title field. See that
 * function for documentation about its details and overrides.
 *
 * @param array $variables
 *   An associative array. See theme_field() for details.
 *
 * @see theme_field()
 *
 * @ingroup themeable
 */
function theme_field__node__title($variables) {
  return '<span' . $variables['attributes'] . '>' . drupal_render($variables['items']) . '</span>';
}

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
    'bypass node access' => array(
      'title' => t('Bypass content access control'),
      'description' => t('View, edit and delete all content regardless of permission restrictions.'),
      'restrict access' => TRUE,
    ),
    'administer content types' => array(
      'title' => t('Administer content types'),
      'title' => t('Administer content'),
      'title' => t('Access the Content overview page'),
      'description' => user_access('access content overview')
        ? t('Get an overview of <a href="@url">all content</a>.', array('@url' => url('admin/content')))
        : t('Get an overview of all content.'),
    'access content' => array(
      'title' => t('View published content'),
    ),
    'view own unpublished content' => array(
      'title' => t('View own unpublished content'),
    'view all revisions' => array(
      'title' => t('View all revisions'),
    'revert all revisions' => array(
      'title' => t('Revert all revisions'),
      'description' => t('Role requires permission <em>view revisions</em> and <em>edit rights</em> for nodes in question, or <em>administer nodes</em>.'),
    'delete all revisions' => array(
      'title' => t('Delete all revisions'),
      'description' => t('Role requires permission to <em>view revisions</em> and <em>delete rights</em> for nodes in question, or <em>administer nodes</em>.'),
  // Generate standard node permissions for all applicable node types.
  foreach (node_permissions_get_configured_types() as $type) {
    $perms += node_list_permissions($type);
Dries Buytaert's avatar
 
Dries Buytaert committed
}

 */
function node_ranking() {
  // Create the ranking array and add the basic ranking options.
  $ranking = array(
    'relevance' => array(
      'title' => t('Keyword relevance'),
      // Average relevance values hover around 0.15
      'score' => 'i.relevance',
    ),
    'sticky' => array(
      'title' => t('Content is sticky at top of lists'),
      // The sticky flag is either 0 or 1, which is automatically normalized.
      'score' => 'n.sticky',
    ),
    'promote' => array(
      'title' => t('Content is promoted to the front page'),
      // The promote flag is either 0 or 1, which is automatically normalized.
      'score' => 'n.promote',
    ),
  );

  // Add relevance based on creation or changed date.
  if ($node_cron_last = \Drupal::state()->get('node.cron_last')) {
    $ranking['recent'] = array(
      'title' => t('Recently posted'),
      // Exponential decay with half-life of 6 months, starting at last indexed node
      'score' => 'POW(2.0, (GREATEST(n.created, n.changed) - :node_cron_last) * 6.43e-8)',
      'arguments' => array(':node_cron_last' => $node_cron_last),
function node_user_cancel($edit, $account, $method) {
  switch ($method) {
    case 'user_cancel_block_unpublish':
      // Unpublish nodes (current revisions).
      module_load_include('inc', 'node', 'node.admin');
      $nodes = db_select('node_field_data', 'n')
        ->distinct()
      node_mass_update($nodes, array('status' => 0), NULL, TRUE);
      break;

    case 'user_cancel_reassign':
      // Anonymize nodes (current revisions).
      module_load_include('inc', 'node', 'node.admin');
      $nodes = db_select('node_field_data', 'n')
        ->distinct()
      node_mass_update($nodes, array('uid' => 0), NULL, TRUE);
  // Delete nodes (current revisions).
  // @todo Introduce node_mass_delete() or make node_mass_update() more flexible.
  $nodes = db_select('node_field_data', 'n')
    ->distinct()
  entity_delete_multiple('node', $nodes);
  $revisions = db_query('SELECT DISTINCT vid FROM {node_field_revision} WHERE uid = :uid', array(':uid' => $account->id()))->fetchCol();
  foreach ($revisions as $revision) {
    node_revision_delete($revision);
 * Returns HTML for the content ranking part of the search settings admin page.
 *
 * @param $variables
 *   An associative array containing:
 *   - form: A render element representing the form.
 * @see node_search_admin()
function theme_node_search_admin($variables) {
  $form = $variables['form'];

  $header = array(t('Factor'), t('Influence'));
  foreach (element_children($form['factors']) as $key) {
    $row = array();
    $row[] = $form['factors'][$key]['#title'];
    $form['factors'][$key]['#title_display'] = 'invisible';
    $row[] = drupal_render($form['factors'][$key]);
  $table = array(
    '#theme' => 'table',
    '#header' => $header,
    '#rows' => $rows,
  );
  $output .= drupal_render($table);
  $output .= drupal_render_children($form);
/**
 * Access callback: Checks node revision access.
 *
 * @param \Drupal\node\NodeInterface $node
 *   The node to check.
 * @param $op
 *   (optional) The specific operation being checked. Defaults to 'view.'
 * @param object $account
 *   (optional) A user object representing the user for whom the operation is
 *   to be performed. Determines access for a user other than the current user.
 * @param $langcode
 *   (optional) Language code for the variant of the node. Different language
 *   variants might have different permissions associated. If NULL, the
 *   original langcode of the node is used. Defaults to NULL.
 *   TRUE if the operation may be performed, FALSE otherwise.
function _node_revision_access(NodeInterface $node, $op = 'view', $account = NULL, $langcode = NULL) {
  if ($account === NULL) {
    $account = \Drupal::currentUser();
  }
  return \Drupal::service('access_check.node.revision')->checkAccess($node, $account, $op, $langcode);
/**
 * Implements hook_menu_link_defaults().
 */
function node_menu_link_defaults() {
  $links['node.admin.content'] = array(
    'link_title' => 'Content',
    'route_name' => 'node.content_overview',
    'parent' => 'system.admin',
    'description' => 'Find and manage content.',
    'weight' => -10,
  );

  $links['node.admin.structure.types'] = array(
    'link_title' => 'Content types',
    'parent' => 'system.admin.structure',
    'description' => 'Manage content types, including default status, front page promotion, comment settings, etc.',
    'route_name' => 'node.overview_types',
  );
  $links['node.add'] = array(
    'link_title' => 'Add content',
    'route_name' => 'node.add_page',
  );
  return $links;
}

 * Title callback: Displays the node's title.
 *
 * @param \Drupal\node\NodeInterface $node
 * @return
 *   An unsanitized string that is the title of the node.
 *
function node_page_title(NodeInterface $node) {
/**
 * Finds the last time a node was changed.
 *
 * @param $nid
 *   The ID of a node.
 * @param string $langcode
 *   (optional) The language the node has been last modified in. Defaults to the
 *   node language.
 *   A unix timestamp indicating the last time the node was changed.
 */
function node_last_changed($nid, $langcode = NULL) {
  if (isset($langcode)) {
    $result = db_query('SELECT changed FROM {node_field_data} WHERE nid = :nid AND langcode = :langcode', array(':nid' => $nid, ':langcode' => $langcode))->fetch();
  }
  else {
    $result = db_query('SELECT changed FROM {node_field_data} WHERE nid = :nid AND default_langcode = :default_langcode', array(':nid' => $nid, ':default_langcode' => 1))->fetch();
  }
  return is_object($result) ? $result->changed : FALSE;