Skip to content
node.module 47.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\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Database\StatementInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Template\Attribute;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\ConfigurableLanguageInterface;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
 * 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.
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function node_help($route_name, RouteMatchInterface $route_match) {
  // 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 ($route_name != 'node.configure_rebuild_confirm' && $route_name != 'system.batch_page.normal' && $route_name != 'help.page.node' && $route_name != 'help.main'
    && \Drupal::currentUser()->hasPermission('access administration pages') && node_access_needs_rebuild()) {
    if ($route_name == 'system.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');
  }

  switch ($route_name) {
    case 'help.page.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 the <a href=":node">online documentation for the Node module</a>.', array(':node' => 'https://www.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('entity.node_type.collection'))) . '</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 gives 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</a> page lists your content, allowing you add new content, filter, edit or delete existing content, or perform bulk operations on existing content.', array(':content' => \Drupal::url('system.admin_content'))) . '</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 'entity.entity_form_display.node.default':
    case 'entity.entity_form_display.node.form_mode':
      $type = $route_match->getParameter('node_type');
      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>' ;
    case 'entity.entity_view_display.node.default':
    case 'entity.entity_view_display.node.view_mode':
      $type = $route_match->getParameter('node_type');
      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 to older versions.') . '</p>';
      $type = NodeType::load($node->getType());
      $help = $type->getHelp();
      return (!empty($help) ? Xss::filterAdmin($help) : '');
Dries Buytaert's avatar
 
Dries Buytaert committed

      $type = $route_match->getParameter('node_type');
      $help = $type->getHelp();
      return (!empty($help) ? Xss::filterAdmin($help) : '');
Dries Buytaert's avatar
 
Dries Buytaert committed
}

    'node_add_list' => array(
      'variables' => array('content' => NULL),
    'node_edit_form' => array(
      'render element' => 'form',
    ),
    'field__node__title' => array(
      'base hook' => 'field',
    ),
    'field__node__uid' => array(
      'base hook' => 'field',
    ),
    'field__node__created' => array(
      'base hook' => 'field',
    ),
 * 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);
        }
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * @param \Drupal\Core\Database\StatementInterface $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
 */
function node_title_list(StatementInterface $result, $title = NULL) {
  foreach ($result as $row) {
    // Do not use $node->label() or $node->urlInfo() here, because we only have
    // database rows, not actual nodes.
    $options = !empty($row->comment_count) ? array('attributes' => array('title' => \Drupal::translation()->formatPlural($row->comment_count, '1 comment', '@count comments'))) : array();
    $items[] = \Drupal::l($row->title, new Url('entity.node.canonical', ['node' => $row->nid], $options));
Dries Buytaert's avatar
 
Dries Buytaert committed
  }

  return $num_rows ? array('#theme' => 'item_list__node', '#items' => $items, '#title' => $title, '#cache' => ['tags' => Cache::mergeTags(['node_list'], Cache::buildTags('node', $nids))]) : 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
 *   Node ID whose history supplies the "last viewed" timestamp.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *   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 \Drupal\node\NodeTypeInterface[]
 *   An array of node type entities, keyed by ID.
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\node\Entity\NodeType::loadMultiple().
 *
 * @see \Drupal\node\Entity\NodeType::load()
  return NodeType::loadMultiple();
 * 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.
  return array_map(function ($bundle_info) {
    return $bundle_info['label'];
  }, \Drupal::entityManager()->getBundleInfo('node'));
}

/**
 * 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 = NodeType::load($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) {
  return $node_type->getDescription();
 * 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.
 *
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\node\Entity\NodeType::load().
  return NodeType::load($name);
 * Adds the default body field to a node type.
 * @param \Drupal\node\NodeTypeInterface $type
 *   (optional) The label for the body instance.
 * @return \Drupal\field\Entity\FieldConfig Body field.
function node_add_body_field(NodeTypeInterface $type, $label = 'Body') {
  $field_storage = FieldStorageConfig::loadByName('node', 'body');
  $field = FieldConfig::loadByName('node', $type->id(), 'body');
  if (empty($field)) {
    $field = entity_create('field_config', array(
      'field_storage' => $field_storage,
      'label' => $label,
      'settings' => array('display_summary' => TRUE),
    // Assign widget settings for the 'default' form mode.
    entity_get_form_display('node', $type->id(), 'default')
    // Assign display settings for the 'default' and 'teaser' view modes.
    entity_get_display('node', $type->id(), 'default')
        'label' => 'hidden',
        'type' => 'text_default',
      ))
      ->save();

    // The teaser view mode is created by the Standard profile and therefore
    // might not exist.
    $view_modes = \Drupal::entityManager()->getViewModes('node');
    if (isset($view_modes['teaser'])) {
      entity_get_display('node', $type->id(), 'teaser')
        ->setComponent('body', array(
          'label' => 'hidden',
          'type' => 'text_summary_or_trimmed',
        ))
        ->save();
    }
 * Implements hook_entity_extra_field_info().
function node_entity_extra_field_info() {
  $description = t('Node module element');
  foreach (NodeType::loadMultiple() as $bundle) {
    $extra['node'][$bundle->id()]['display']['links'] = array(
      'label' => t('Links'),
      'description' => $description,
      'weight' => 100,
      'visible' => TRUE,
    );
 * 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) {
  return \Drupal::entityManager()->getStorage('node')->updateType($old_id, $new_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.
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\node\Entity\Node::loadMultiple().
 *
 * @see \Drupal\Core\Entity\Query\EntityQueryInterface
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function node_load_multiple(array $nids = NULL, $reset = FALSE) {
  if ($reset) {
    \Drupal::entityManager()->getStorage('node')->resetCache($nids);
  }
  return Node::loadMultiple($nids);
 * 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.
 *
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\node\Entity\Node::load().
function node_load($nid = NULL, $reset = FALSE) {
  if ($reset) {
    \Drupal::entityManager()->getStorage('node')->resetCache(array($nid));
  }
  return Node::load($nid);
}

/**
 * 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) {
  $route_match = \Drupal::routeMatch();
  if ($route_match->getRouteName() == 'entity.node.canonical') {
    $page_node = $route_match->getParameter('node');
  return (!empty($page_node) ? $page_node->id() == $node->id() : FALSE);
/**
 * Prepares variables for list of available node type templates.
 *
 * Default template: node-add-list.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - content: An array of content types.
 *
 * @see node_add_page()
 */
function template_preprocess_node_add_list(&$variables) {
  $variables['types'] = array();
  if (!empty($variables['content'])) {
    foreach ($variables['content'] as $type) {
      $variables['types'][$type->id()] = array(
        'type' => $type->id(),
        'add_link' => \Drupal::l($type->label(), new Url('node.add', array('node_type' => $type->id()))),
        'description' => array(
          '#markup' => $type->getDescription(),
        ),
/**
 * 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::routeMatch()->getParameter('node')) && $node instanceof NodeInterface) {
    $variables['node_type'] = $node->getType();
 * Implements hook_preprocess_HOOK() for block templates.
 */
function node_preprocess_block(&$variables) {
  if ($variables['configuration']['provider'] == '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'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  $suggestions[] = 'node__' . $sanitized_view_mode;
  $suggestions[] = 'node__' . $node->bundle();
  $suggestions[] = 'node__' . $node->bundle() . '__' . $sanitized_view_mode;
  $suggestions[] = 'node__' . $node->id();
  $suggestions[] = 'node__' . $node->id() . '__' . $sanitized_view_mode;
 * Prepares variables for node templates.
 * Most themes use 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.
 *   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', etc.
 */
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'] = drupal_render($variables['elements']['created']);
  unset($variables['elements']['created']);
  $variables['author_name'] = drupal_render($variables['elements']['uid']);
  unset($variables['elements']['uid']);
  $variables['url'] = $node->url('canonical', array(
  $variables['label'] = $variables['elements']['title'];
  unset($variables['elements']['title']);
  // The 'page' variable is set to TRUE in two occasions:
  //   - The view mode is 'full' and we are on the 'node.view' route.
  //   - The node is in preview and view mode is either 'full' or 'default'.
  $variables['page'] = ($variables['view_mode'] == 'full' && (node_is_page($node)) || (isset($node->in_preview) && in_array($node->preview_view_mode, array('full', 'default'))));
  $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.
  // Used by RDF to add attributes around the author and date submitted.
  $variables['author_attributes'] = new Attribute();
  $variables['display_submitted'] = $node_type->displaySubmitted();
  if ($variables['display_submitted']) {
    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['author_picture'] = user_view($node->getOwner(), 'compact');
  $variables['attributes']['role'] = 'article';
/**
 * Implements hook_cron().
 */
function node_cron() {
  // Calculate the oldest and newest node created times, for use in search
  // rankings. (Note that field aliases have to be variables passed by
  // reference.)
  if (\Drupal::moduleHandler()->moduleExists('search')) {
    $min_alias = 'min_created';
    $max_alias = 'max_created';
    $result = \Drupal::entityQueryAggregate('node')
      ->aggregate('created', 'MIN', NULL, $min_alias)
      ->aggregate('created', 'MAX', NULL, $max_alias)
      ->execute();
    if (isset($result[0])) {
      // Make an array with definite keys and store it in the state system.
      $array = array(
        'min_created' => $result[0][$min_alias],
        'max_created' => $result[0][$max_alias],
      );
      \Drupal::state()->set('node.min_max_update_time', $array);
    }
 */
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 updated date, but only if it the scale values have
  // been calculated in node_cron().
  if ($node_min_max = \Drupal::state()->get('node.min_max_update_time')) {
      'title' => t('Recently created'),
      // Exponential decay with half life of 14% of the age range of nodes.
      'score' => 'EXP(-5 * (1 - (n.created - :node_oldest) / :node_range))',
      'arguments' => array(
        ':node_oldest' => $node_min_max['min_created'],
        ':node_range' => max($node_min_max['max_created'] - $node_min_max['min_created'], 1),
      ),
function node_user_cancel($edit, $account, $method) {
  switch ($method) {
    case 'user_cancel_block_unpublish':
      // Unpublish nodes (current revisions).
      $nids = \Drupal::entityQuery('node')
        ->execute();
      module_load_include('inc', 'node', 'node.admin');
      node_mass_update($nids, array('status' => 0), NULL, TRUE);
      // Anonymize all of the nodes for this old account.
      module_load_include('inc', 'node', 'node.admin');
      $vids = \Drupal::entityManager()->getStorage('node')->userRevisionIds($account);
      node_mass_update($vids, array(
        'uid' => 0,
        'revision_uid' => 0,
      ), NULL, TRUE, TRUE);
 * Implements hook_ENTITY_TYPE_predelete() for user entities.
  // Delete nodes (current revisions).
  // @todo Introduce node_mass_delete() or make node_mass_update() more flexible.
  $nids = \Drupal::entityQuery('node')
    ->execute();
  entity_delete_multiple('node', $nids);
  $storage_controller = \Drupal::entityManager()->getStorage('node');
  $revisions = $storage_controller->userRevisionIds($account);
  foreach ($revisions as $revision) {
    node_revision_delete($revision);
 * Finds the most recently changed nodes that are available to the current user.
 *
 * @param $number
 *   (optional) The maximum number of nodes to find. Defaults to 10.
 *
 * @return
 *   An array of node entities or an empty array if there are no recent nodes
 *   visible to the current user.
 */
function node_get_recent($number = 10) {
  $account = \Drupal::currentUser();
  $query = \Drupal::entityQuery('node');
  if (!$account->hasPermission('bypass node access')) {
    // If the user is able to view their own unpublished nodes, allow them
    // to see these in addition to published nodes. Check that they actually
    // have some unpublished nodes to view before adding the condition.
    $access_query =  \Drupal::entityQuery('node')
      ->condition('uid', $account->id())
      ->condition('status', NODE_NOT_PUBLISHED);
    if ($account->hasPermission('view own unpublished content') && ($own_unpublished = $access_query->execute())) {
      $query->orConditionGroup()
        ->condition('status', NODE_PUBLISHED)
        ->condition('nid', $own_unpublished, 'IN');
    }
    else {
      // If not, restrict the query to published nodes.
      $query->condition('status', NODE_PUBLISHED);
    ->range(0, $number)
    ->addTag('node_access')
/**
 * Generates an array for rendering the given node.
 *
 * @param \Drupal\node\NodeInterface $node
 *   (optional) View mode, e.g., 'full', 'teaser', etc. Defaults to 'full.'
 *   (optional) A language code to use for rendering. Defaults to NULL which is
 *   the global content language of the current request.
 *
 * @return
 *   An array as expected by drupal_render().
 */
function node_view(NodeInterface $node, $view_mode = 'full', $langcode = NULL) {
  return entity_view($node, $view_mode, $langcode);
}

 * Constructs a drupal_render() style array from an array of loaded nodes.
 *   An array of nodes as returned by Node::loadMultiple().
 *   (optional) View mode, e.g., 'full', 'teaser', etc. Defaults to 'teaser.'
 * @param $langcode
 *   (optional) A language code to use for rendering. Defaults to the global
 *   content language of the current request.
 *
 * @return
 *   An array in the format expected by drupal_render().
 */
function node_view_multiple($nodes, $view_mode = 'teaser', $langcode = NULL) {
  return entity_view_multiple($nodes, $view_mode, $langcode);
function node_page_top(array &$page) {
  // Add 'Back to content editing' link on preview page.
  $route_match = \Drupal::routeMatch();
  if ($route_match->getRouteName() == 'entity.node.preview') {
    $page['page_top']['node_preview'] = array(
      '#type' => 'container',
      '#attributes' => array(
        'class' => array('node-preview-container', 'container-inline')
      ),
    );

    $form = \Drupal::formBuilder()->getForm('\Drupal\node\Form\NodePreviewForm', $route_match->getParameter('node_preview'));
    $page['page_top']['node_preview']['view_mode'] = $form;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters the theme form to use the admin theme on node editing.
 *
 * @see node_form_system_themes_admin_form_submit()
 */
function node_form_system_themes_admin_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['admin_theme']['use_admin_theme'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use the administration theme when editing or creating content'),
    '#default_value' => \Drupal::configFactory()->getEditable('node.settings')->get('use_admin_theme'),
  );
  $form['#submit'][] = 'node_form_system_themes_admin_form_submit';
}

/**
 * Form submission handler for system_themes_admin_form().
 *
 * @see node_form_system_themes_admin_form_alter()
 */
function node_form_system_themes_admin_form_submit($form, FormStateInterface $form_state) {
  \Drupal::configFactory()->getEditable('node.settings')
    ->set('use_admin_theme', $form_state->getValue('use_admin_theme'))
  \Drupal::service('router.builder')->setRebuildNeeded();
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * @defgroup node_access Node access rights
 * @{
 * The node access system determines who can do what to which nodes.
 *
 * In determining access rights for a node, \Drupal\node\NodeAccessControlHandler
 * first checks whether the user has the "bypass node access" permission. Such
 * users have unrestricted access to all nodes. user 1 will always pass this
 * check.
 *
 * Next, all implementations of hook_node_access() will be called. Each
 * implementation may explicitly allow, explicitly forbid, or ignore the access
 * request. If at least one module says to forbid the request, it will be
 * rejected. If no modules deny the request and at least one says to allow it,
 * the request will be permitted.
 *
 * If all modules ignore the access request, then the node_access table is used
 * to determine access. All node access modules are queried using
 * hook_node_grants() to assemble a list of "grant IDs" for the user. This list
 * is compared against the table. If any row contains the node ID in question
 * (or 0, which stands for "all nodes"), one of the grant IDs returned, and a
 * value of TRUE for the operation in question, then access is granted. Note
 * that this table is a list of grants; any matching row is sufficient to grant
 * access to the node.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * In node listings (lists of nodes generated from a select query, such as the
 * default home page at path 'node', an RSS feed, a recent content block, etc.),
 * the process above is followed except that hook_node_access() is not called on
 * each node for performance reasons and for proper functioning of the pager
 * system. When adding a node listing to your module, be sure to use an entity
 * query, which will add a tag of "node_access". This will allow modules dealing
 * with node access to ensure only nodes to which the user has access are
 * retrieved, through the use of hook_query_TAG_alter(). See the
 * @link entity_api Entity API topic @endlink for more information on entity
 * queries.
 * Note: Even a single module returning an AccessResultInterface object from
 * hook_node_access() whose isForbidden() method equals TRUE will block access
 * to the node. Therefore, implementers should take care to not deny access
 * unless they really intend to. Unless a module wishes to actively forbid
 * access it should return an AccessResultInterface object whose isAllowed() nor
 * isForbidden() methods return TRUE, to allow other modules or the node_access
 * table to control access.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * To see how to write a node access module of your own, see
 * node_access_example.module.
 */

function node_node_access(NodeInterface $node, $op, $account) {
  switch ($op) {
    case 'create':
      return AccessResult::allowedIfHasPermission($account, 'create ' . $type . ' content');
    case 'update':
      if ($account->hasPermission('edit any ' . $type . ' content', $account)) {
        return AccessResult::allowed()->cachePerPermissions();
        return AccessResult::allowedIf($account->hasPermission('edit own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node);
    case 'delete':
      if ($account->hasPermission('delete any ' . $type . ' content', $account)) {
        return AccessResult::allowed()->cachePerPermissions();
        return AccessResult::allowedIf($account->hasPermission('delete own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node);
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Fetches an array of permission IDs granted to the given user ID.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * The implementation here provides only the universal "all" grant. A node
 * access module should implement hook_node_grants() to provide a grant list for
 * the user.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * After the default grants have been loaded, we allow modules to alter the
 * grants array by reference. This hook allows for complex business logic to be
 * applied when integrating multiple node access modules.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *   The operation that the user is trying to perform.
 * @param \Drupal\Core\Session\AccountInterface $account
 *   The account object for the user performing the operation.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *   An associative array in which the keys are realms, and the values are
 *   arrays of grants for those realms.
 */
function node_access_grants($op, AccountInterface $account) {
  // Fetch node access grants from other modules.
  $grants = \Drupal::moduleHandler()->invokeAll('node_grants', array($account, $op));
  // Allow modules to alter the assigned grants.
  \Drupal::moduleHandler()->alter('node_grants', $grants, $account, $op);

  return array_merge(array('all' => array(0)), $grants);
Dries Buytaert's avatar
 
Dries Buytaert committed
}

/**
 * Determines whether the user has a global viewing grant for all nodes.
 *
 * Checks to see whether any module grants global 'view' access to a user
 * account; global 'view' access is encoded in the {node_access} table as a
 * grant with nid=0. If no node access modules are enabled, node.module defines
 * such a global 'view' access grant.
 *
 * This function is called when a node listing query is tagged with
 * 'node_access'; when this function returns TRUE, no node access joins are
 * added to the query.
 *
 * @param $account
 *   (optional) The user object for the user whose access is being checked. If
 *   omitted, the current user is used. Defaults to NULL.
 *
 * @return
 *   TRUE if 'view' access to all nodes is granted, FALSE otherwise.
 *
 * @see hook_node_grants()
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function node_access_view_all_nodes($account = NULL) {
    $account = \Drupal::currentUser();
  // Statically cache results in an array keyed by $account->id().
  $access = &drupal_static(__FUNCTION__);
  if (isset($access[$account->id()])) {
    return $access[$account->id()];
  // If no modules implement the node access system, access is always TRUE.
  if (!\Drupal::moduleHandler()->getImplementations('node_grants')) {
    $access[$account->id()] = \Drupal::entityManager()->getAccessControlHandler('node')->checkAllGrants($account);