node.module 64.9 KB
Newer Older
Dries's avatar
 
Dries committed
1 2
<?php

Dries's avatar
 
Dries committed
3 4
/**
 * @file
5 6 7 8
 * 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's avatar
 
Dries committed
9 10
 */

11
use Drupal\Component\Utility\String;
12
use Drupal\Core\Language\Language;
13
use Symfony\Component\HttpFoundation\Response;
14
use Drupal\Core\Cache\Cache;
15 16
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
17
use Drupal\node\NodeTypeInterface;
18
use Drupal\node\NodeInterface;
19
use Drupal\Core\Entity\EntityInterface;
20 21
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
22
use Drupal\Core\Template\Attribute;
23
use Drupal\file\Entity\File;
24
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
25

26
/**
27
 * Denotes that the node is not published.
28
 */
29
const NODE_NOT_PUBLISHED = 0;
30 31

/**
32
 * Denotes that the node is published.
33
 */
34
const NODE_PUBLISHED = 1;
35 36

/**
37
 * Denotes that the node is not promoted to the front page.
38
 */
39
const NODE_NOT_PROMOTED = 0;
40 41

/**
42
 * Denotes that the node is promoted to the front page.
43
 */
44
const NODE_PROMOTED = 1;
45 46

/**
47
 * Denotes that the node is not sticky at the top of the page.
48
 */
49
const NODE_NOT_STICKY = 0;
50 51

/**
52
 * Denotes that the node is sticky at the top of the page.
53
 */
54
const NODE_STICKY = 1;
55

56
/**
57 58 59 60
 * Denotes that access is allowed for a node.
 *
 * Modules should return this value from hook_node_access() to allow access to a
 * node.
61
 */
62
const NODE_ACCESS_ALLOW = TRUE;
63 64

/**
65 66 67 68
 * Denotes that access is denied for a node.
 *
 * Modules should return this value from hook_node_access() to deny access to a
 * node.
69
 */
70
const NODE_ACCESS_DENY = FALSE;
71 72

/**
73 74 75 76
 * Denotes that access is unaffected for a node.
 *
 * Modules should return this value from hook_node_access() to indicate no
 * effect on node access.
77
 */
78
const NODE_ACCESS_IGNORE = NULL;
79

Dries's avatar
 
Dries committed
80
/**
81
 * Implements hook_help().
Dries's avatar
 
Dries committed
82
 */
83
function node_help($path, $arg) {
84 85 86
  // 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.
87
  if ($path != 'admin/reports/status/rebuild' && $path != 'batch' && strpos($path, '#') === FALSE
88
      && user_access('access administration pages') && node_access_needs_rebuild()) {
89
    if ($path == 'admin/reports/status') {
90 91 92
      $message = t('The content access permissions need to be rebuilt.');
    }
    else {
93
      $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')));
94 95 96 97
    }
    drupal_set_message($message, 'error');
  }

98
  switch ($path) {
Dries's avatar
 
Dries committed
99
    case 'admin/help#node':
100 101
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
102
      $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>';
103 104
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
105
      $output .= '<dt>' . t('Creating content') . '</dt>';
106
      $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>';
107
      $output .= '<dt>' . t('Creating custom content types') . '</dt>';
108
      $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>';
109
      $output .= '<dt>' . t('Administering content') . '</dt>';
110
      $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>';
111 112
      $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>';
113
      $output .= '<dt>' . t('User permissions') . '</dt>';
114
      $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>';
115
      $output .= '</dl>';
116
      return $output;
117

118
    case 'admin/structure/types/add':
119
      return '<p>' . t('Individual content types can have different fields, behaviors, and permissions assigned to them.') . '</p>';
120

121
    case 'admin/structure/types/manage/%/form-display':
122 123
      $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>' ;
124

125
    case 'admin/structure/types/manage/%/display':
126
      $type =  entity_load('node_type', $arg[4]);
127
      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>' .
128
        '<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>';
129

130
    case 'node/%/revisions':
131
      return '<p>' . t('Revisions allow you to track differences between multiple versions of your content, and revert back to older versions.') . '</p>';
132

133 134
    case 'node/%/edit':
      $node = node_load($arg[1]);
135
      $type = node_type_load($node->bundle());
136
      return (!empty($type->help) ? filter_xss_admin($type->help) : '');
Dries's avatar
 
Dries committed
137
  }
Dries's avatar
 
Dries committed
138

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

145
/**
146
 * Implements hook_theme().
147 148 149
 */
function node_theme() {
  return array(
150
    'node' => array(
151
      'render element' => 'elements',
152
      'template' => 'node',
153
    ),
154
    'node_search_admin' => array(
155
      'render element' => 'form',
156
    ),
157
    'node_add_list' => array(
158
      'variables' => array('content' => NULL),
159
      'file' => 'node.pages.inc',
160
      'template' => 'node-add-list',
161 162
    ),
    'node_preview' => array(
163
      'variables' => array('node' => NULL),
164
      'file' => 'node.pages.inc',
165
      'template' => 'node-preview',
166
    ),
167 168 169 170
    'node_edit_form' => array(
      'render element' => 'form',
      'template' => 'node-edit-form',
    ),
171 172 173
    'field__node__title' => array(
      'base hook' => 'field',
    ),
174 175 176
  );
}

177 178 179 180 181
/**
 * Implements hook_entity_bundle_info().
 */
function node_entity_bundle_info() {
  $bundles = array();
182
  // Bundles must provide a human readable name so we can create help and error
183
  // messages.
184 185
  foreach (node_type_get_names() as $id => $label) {
    $bundles['node'][$id]['label'] = $label;
186
  }
187
  return $bundles;
Dries's avatar
 
Dries committed
188 189
}

190
/**
191
 * Implements hook_entity_view_display_alter().
192
 */
193
function node_entity_view_display_alter(EntityViewDisplayInterface $display, $context) {
194 195 196 197 198 199 200 201
  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);
        }
202 203
      }
    }
204 205 206 207 208 209
  }
}

/**
 * Implements hook_entity_form_display_alter().
 */
210
function node_entity_form_display_alter(EntityFormDisplayInterface $form_display, $context) {
211 212
  if ($context['entity_type'] == 'node') {
    $node_type = node_type_load($context['bundle']);
213 214 215
    // @todo Reconsider when per-bundle overrides of field definitions are
    //   possible - https://drupal.org/node/2114707.
    if (!$node_type->has_title) {
216 217
      $form_display->removeComponent('title');
    }
218 219 220
  }
}

221
/**
222
 * Entity URI callback.
223
 *
224
 * @param \Drupal\node\NodeInterface $node
225
 *   A node entity.
226 227
 *
 * @return array
228
 *   An array with 'path' as the key and the path to the node as its value.
229
 */
230
function node_uri(NodeInterface $node) {
231
  return array(
232 233 234 235
    'route_name' => 'node.view',
    'route_parameters' => array(
      'node' => $node->id(),
    ),
236
  );
237 238
}

239
/**
240
 * Implements hook_admin_paths().
241 242
 */
function node_admin_paths() {
243
  if (\Drupal::config('node.settings')->get('use_admin_theme')) {
244 245 246 247 248 249
    $paths = array(
      'node/*/edit' => TRUE,
      'node/*/delete' => TRUE,
      'node/*/revisions' => TRUE,
      'node/*/revisions/*/revert' => TRUE,
      'node/*/revisions/*/delete' => TRUE,
250 251
      'node/*/translations' => TRUE,
      'node/*/translations/*' => TRUE,
252 253 254 255 256
      'node/add' => TRUE,
      'node/add/*' => TRUE,
    );
    return $paths;
  }
257 258
}

Dries's avatar
 
Dries committed
259
/**
260
 * Gathers a listing of links to nodes.
Dries's avatar
 
Dries committed
261 262
 *
 * @param $result
263
 *   A database result object from a query to fetch node entities. If your
264
 *   query joins the {comment_entity_statistics} table so that the comment_count
265 266
 *   field is available, a title attribute will be added to show the number of
 *   comments.
Dries's avatar
 
Dries committed
267
 * @param $title
268
 *   (optional) A heading for the resulting list.
Dries's avatar
 
Dries committed
269 270
 *
 * @return
271 272
 *   A renderable array containing a list of linked node titles fetched from
 *   $result, or FALSE if there are no rows in $result.
Dries's avatar
 
Dries committed
273
 */
Dries's avatar
 
Dries committed
274
function node_title_list($result, $title = NULL) {
275
  $items = array();
276
  $num_rows = FALSE;
277
  foreach ($result as $node) {
278
    // Do not use $node->label() here, because $node comes from the database.
279
    $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());
280
    $num_rows = TRUE;
Dries's avatar
 
Dries committed
281 282
  }

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

Dries's avatar
 
Dries committed
286
/**
287
 * Determines the type of marker to be displayed for a given node.
Dries's avatar
 
Dries committed
288
 *
Dries's avatar
 
Dries committed
289 290 291 292
 * @param $nid
 *   Node ID whose history supplies the "last viewed" timestamp.
 * @param $timestamp
 *   Time which is compared against node's "last viewed" timestamp.
293
 *
294 295
 * @return
 *   One of the MARK constants.
Dries's avatar
 
Dries committed
296
 */
297
function node_mark($nid, $timestamp) {
298

299
  $cache = &drupal_static(__FUNCTION__, array());
Dries's avatar
 
Dries committed
300

301
  if (\Drupal::currentUser()->isAnonymous() || !\Drupal::moduleHandler()->moduleExists('history')) {
302 303
    return MARK_READ;
  }
Dries's avatar
Dries committed
304
  if (!isset($cache[$nid])) {
305
    $cache[$nid] = history_read($nid);
Dries's avatar
 
Dries committed
306
  }
307
  if ($cache[$nid] == 0 && $timestamp > HISTORY_READ_LIMIT) {
308 309
    return MARK_NEW;
  }
310
  elseif ($timestamp > $cache[$nid] && $timestamp > HISTORY_READ_LIMIT) {
311 312 313
    return MARK_UPDATED;
  }
  return MARK_READ;
Dries's avatar
 
Dries committed
314 315
}

316 317 318
/**
 * Returns a list of all the available node types.
 *
319 320
 * This list can include types that are queued for addition or deletion.
 *
321 322
 * @return array
 *   An array of node type entities, keyed by ID.
323
 *
324
 * @see node_type_load()
325 326
 */
function node_type_get_types() {
327
  return entity_load_multiple('node_type');
328 329 330
}

/**
331 332 333
 * Returns a list of available node type names.
 *
 * This list can include types that are queued for addition or deletion.
334
 *
335
 * @return array
336
 *   An array of node type labels, keyed by the node type name.
337 338
 */
function node_type_get_names() {
339
  $cid = 'node_type:names:' . \Drupal::languageManager()->getCurrentLanguage()->id;
340
  if ($cache = \Drupal::cache()->get($cid)) {
341 342 343
    return $cache->data;
  }
  // Not using node_type_get_types() or entity_load_multiple() here, to allow
344
  // this function being used in hook_entity_type_build() implementations.
345 346 347 348
  // @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) {
349
    $config = \Drupal::config($config_name);
350 351
    $names[$config->get('type')] = $config->get('name');
  }
352
  \Drupal::cache()->set($cid, $names, Cache::PERMANENT, array(
353 354 355 356
    'node_type' => array_keys($names),
    'node_types' => TRUE,
  ));
  return $names;
357 358 359 360 361
}

/**
 * Returns the node type label for the passed node.
 *
362
 * @param \Drupal\node\NodeInterface $node
363 364 365 366
 *   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.
367
 *
368 369
 * @todo Add this as generic helper method for config entities representing
 *   entity bundles.
370
 */
371
function node_get_type_label(NodeInterface $node) {
372 373
  $type = entity_load('node_type', $node->bundle());
  return $type ? $type->label() : FALSE;
374 375 376 377 378
}

/**
 * Description callback: Returns the node type description.
 *
379
 * @param \Drupal\node\NodeTypeInterface $node_type
380 381
 *   The node type object.
 *
382
 * @return string
383 384
 *   The node type description.
 */
385
function node_type_get_description(NodeTypeInterface $node_type) {
386 387 388
  return $node_type->description;
}

389
/**
390
 * Menu argument loader: Loads a node type by string.
391 392
 *
 * @param $name
393
 *   The machine name of a node type to load.
394
 *
395
 * @return \Drupal\node\NodeTypeInterface
396
 *   A node type object or NULL if $name does not exist.
397 398
 */
function node_type_load($name) {
399
  return entity_load('node_type', $name);
400
}
401

402
/**
403
 * Adds the default body field to a node type.
404
 *
405
 * @param \Drupal\node\NodeTypeInterface $type
406
 *   A node type object.
407
 * @param $label
408
 *   (optional) The label for the body instance.
409 410 411
 *
 * @return
 *   Body field instance.
412
 */
413
function node_add_body_field(NodeTypeInterface $type, $label = 'Body') {
414
   // Add or remove the body field, as needed.
415
  $field = field_info_field('node', 'body');
416
  $instance = field_info_instance('node', 'body', $type->id());
417
  if (empty($field)) {
418
    $field = entity_create('field_config', array(
419 420
      'name' => 'body',
      'entity_type' => 'node',
421
      'type' => 'text_with_summary',
422 423
    ));
    $field->save();
424
  }
425
  if (empty($instance)) {
426
    $instance = entity_create('field_instance_config', array(
427 428
      'field_name' => 'body',
      'entity_type' => 'node',
429
      'bundle' => $type->id(),
430 431
      'label' => $label,
      'settings' => array('display_summary' => TRUE),
432 433
    ));
    $instance->save();
434

435 436
    // Assign widget settings for the 'default' form mode.
    entity_get_form_display('node', $type->type, 'default')
437
      ->setComponent('body', array(
438 439 440 441
        'type' => 'text_textarea_with_summary',
      ))
      ->save();

442 443
    // Assign display settings for the 'default' and 'teaser' view modes.
    entity_get_display('node', $type->type, 'default')
444
      ->setComponent('body', array(
445 446 447 448 449
        'label' => 'hidden',
        'type' => 'text_default',
      ))
      ->save();
    entity_get_display('node', $type->type, 'teaser')
450
      ->setComponent('body', array(
451 452 453 454
        'label' => 'hidden',
        'type' => 'text_summary_or_trimmed',
      ))
      ->save();
455
  }
456

457
  return $instance;
458
}
459

460 461 462 463 464
/**
 * Implements hook_field_extra_fields().
 */
function node_field_extra_fields() {
  $extra = array();
465
  $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language');
466 467 468
  $description = t('Node module element');

  foreach (node_type_get_types() as $bundle) {
469 470
    // Add the 'language' select if Language module is enabled and the bundle
    // has multilingual support.
471
    // Visibility of the ordering of the language selector is the same as on the
472 473 474
    // node/add form.
    if ($module_language_enabled) {
      $configuration = language_get_default_configuration('node', $bundle->type);
475
      if ($configuration['language_show']) {
476
        $extra['node'][$bundle->type]['form']['langcode'] = array(
477 478 479 480 481
          'label' => t('Language'),
          'description' => $description,
          'weight' => 0,
        );
      }
482
    }
483
    $extra['node'][$bundle->type]['display']['langcode'] = array(
484 485 486 487 488
      'label' => t('Language'),
      'description' => $description,
      'weight' => 0,
      'visible' => FALSE,
    );
489
  }
490 491

  return $extra;
492 493
}

494
/**
495 496
 * Updates all nodes of one type to be of another type.
 *
497
 * @param string $old_id
498
 *   The current node type of the nodes.
499
 * @param string $new_id
500
 *   The new node type of the nodes.
501 502
 *
 * @return
503
 *   The number of nodes whose node type field was modified.
504
 */
505
function node_type_update_nodes($old_id, $new_id) {
506
  return db_update('node')
507 508
    ->fields(array('type' => $new_id))
    ->condition('type', $old_id)
509
    ->execute();
Dries's avatar
 
Dries committed
510
}
Dries's avatar
 
Dries committed
511

512
/**
513
 * Loads node entities from the database.
514 515
 *
 * This function should be used whenever you need to load more than one node
516 517
 * from the database. Nodes are loaded into memory and will not require database
 * access if loaded again during the same page request.
Dries's avatar
 
Dries committed
518
 *
519 520
 * @param array $nids
 *   (optional) An array of entity IDs. If omitted, all entities are loaded.
521
 * @param bool $reset
522 523
 *   (optional) Whether to reset the internal node_load() cache.  Defaults to
 *   FALSE.
Dries's avatar
 
Dries committed
524
 *
525
 * @return \Drupal\node\NodeInterface[]
526
 *   An array of node entities indexed by nid.
527
 *
528
 * @see entity_load_multiple()
529
 * @see \Drupal\Core\Entity\Query\EntityQueryInterface
Dries's avatar
 
Dries committed
530
 */
531
function node_load_multiple(array $nids = NULL, $reset = FALSE) {
532
  return entity_load_multiple('node', $nids, $reset);
533 534 535
}

/**
536
 * Loads a node entity from the database.
537
 *
538
 * @param int $nid
539
 *   The node ID.
540
 * @param bool $reset
541 542
 *   (optional) Whether to reset the node_load_multiple() cache. Defaults to
 *   FALSE.
543
 *
544 545
 * @return \Drupal\node\NodeInterface|null
 *   A fully-populated node entity, or NULL if the node is not found.
546
 */
547
function node_load($nid = NULL, $reset = FALSE) {
548
  return entity_load('node', $nid, $reset);
549 550 551 552 553
}

/**
 * Loads a node revision from the database.
 *
554
 * @param int $vid
555 556
 *   The node revision id.
 *
557 558
 * @return \Drupal\node\NodeInterface|null
 *   A fully-populated node entity, or NULL if the node is not found.
559 560 561
 */
function node_revision_load($vid = NULL) {
  return entity_revision_load('node', $vid);
Dries's avatar
 
Dries committed
562 563
}

564
/**
565
 * Deletes a node revision.
566 567 568
 *
 * @param $revision_id
 *   The revision ID to delete.
569 570
 *
 * @return
571
 *   TRUE if the revision deletion was successful; otherwise, FALSE.
572 573
 */
function node_revision_delete($revision_id) {
574
  entity_revision_delete('node', $revision_id);
575 576
}

577
/**
578
 * Checks whether the current page is the full page view of the passed-in node.
579
 *
580
 * @param \Drupal\node\NodeInterface $node
581
 *   A node entity.
582 583 584
 *
 * @return
 *   The ID of the node if this is a full page view, otherwise FALSE.
585
 */
586
function node_is_page(NodeInterface $node) {
587 588 589 590
  $request = \Drupal::request();
  if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.view') {
    $page_node = $request->attributes->get('node');
  }
591
  return (!empty($page_node) ? $page_node->id() == $node->id() : FALSE);
592 593
}

594 595 596 597 598
/**
 * 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.
599
  if (($node = \Drupal::request()->attributes->get('node')) && $node instanceof NodeInterface) {
600 601 602 603
    $variables['attributes']['class'][] = drupal_html_class('node-type-' . $node->getType());
  }
}

604
/**
605
 * Implements hook_preprocess_HOOK() for block templates.
606 607
 */
function node_preprocess_block(&$variables) {
608 609
  if ($variables['configuration']['module'] == 'node') {
    switch ($variables['elements']['#plugin_id']) {
610
      case 'node_syndicate_block':
611
        $variables['attributes']['role'] = 'complementary';
612 613 614 615 616
        break;
    }
  }
}

617 618 619 620 621 622 623 624 625 626 627 628 629
/**
 * 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;
}

630
/**
631
 * Prepares variables for node templates.
632
 *
633
 * Default template: node.html.twig.
634
 *
635 636 637 638 639
 * 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
640 641 642 643
 *   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'...
644 645
 */
function template_preprocess_node(&$variables) {
646
  $variables['view_mode'] = $variables['elements']['#view_mode'];
647
  // Provide a distinct $teaser boolean.
648
  $variables['teaser'] = $variables['view_mode'] == 'teaser';
649
  $variables['node'] = $variables['elements']['#node'];
650
  /** @var \Drupal\node\NodeInterface $node */
651 652
  $node = $variables['node'];

653
  $variables['date'] = format_date($node->getCreatedTime());
654 655
  $username = array(
    '#theme' => 'username',
656
    '#account' => $node->getOwner(),
657
    '#link_options' => array('attributes' => array('rel' => 'author')),
658 659
  );
  $variables['name'] = drupal_render($username);
660

661
  $variables['node_url'] = $node->url();
662 663
  $variables['label'] = $variables['elements']['title'];
  unset($variables['elements']['title']);
664
  $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node);
665

666
  // Helpful $content variable for templates.
667
  $variables += array('content' => array());
668 669 670
  foreach (element_children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  }
671 672

  // Display post information only on certain node types.
673 674 675 676 677
  // 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']) {
678
    $variables['submitted'] = t('Submitted by !username on !datetime', array('!username' => $variables['name'], '!datetime' => $variables['date']));
679
    if (theme_get_setting('features.node_user_picture')) {
680
      // To change user picture settings (e.g. image style), edit the 'compact'
681 682
      // 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.
683
      $variables['user_picture'] = user_view($node->getOwner(), 'compact');
684 685 686 687
    }
    else {
      $variables['user_picture'] = array();
    }
688 689
  }
  else {
690
    $variables['submitted'] = '';
691
    $variables['user_picture'] = '';
692
  }
693

694
  // Add article ARIA role.
695
  $variables['attributes']['role'] = 'article';
696

697
  // Gather node classes.
698
  $variables['attributes']['class'][] = 'node';
alexpott's avatar