node.module 67.4 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 Drupal\node\NodeInterface;
14
use Symfony\Component\HttpFoundation\Response;
15
use Drupal\Core\Cache\Cache;
16 17
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
18
use Drupal\node\NodeTypeInterface;
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

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

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

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

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

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

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

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

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

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

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

97
  switch ($path) {
Dries's avatar
 
Dries committed
98
    case 'admin/help#node':
99 100
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
101
      $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 online handbook entry for <a href="@node">Node module</a>.', array('@node' => 'http://drupal.org/documentation/modules/node', '@field' => url('admin/help/field'))) . '</p>';
102 103
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
104 105 106
      $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' => url('admin/structure/types'))) . '</dd>';
      $output .= '<dt>' . t('Creating custom content types') . '</dt>';
107
      $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' => url('admin/structure/types/add'), '@field' => url('admin/help/field'))) . '</dd>';
108
      $output .= '<dt>' . t('Administering content') . '</dt>';
109 110 111
      $output .= '<dd>' . t('The <a href="@content">Content administration page</a> allows you to review and bulk manage your site content.', array('@content' => url('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>';
112
      $output .= '<dt>' . t('User permissions') . '</dt>';
113
      $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' => url('admin/people/permissions', array('fragment' => 'module-node')))) . '</dd>';
114
      $output .= '</dl>';
115
      return $output;
116

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/**
328 329 330
 * Returns a list of available node type names.
 *
 * This list can include types that are queued for addition or deletion.
331
 *
332
 * @return array
333
 *   An array of node type labels, keyed by the node type name.
334 335
 */
function node_type_get_names() {
336
  $cid = 'node_type:names:' . language(Language::TYPE_INTERFACE)->id;
337 338 339 340 341 342 343 344 345
  if ($cache = 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_info() 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) {
346
    $config = \Drupal::config($config_name);
347 348
    $names[$config->get('type')] = $config->get('name');
  }
349
  cache()->set($cid, $names, Cache::PERMANENT, array(
350 351 352 353
    'node_type' => array_keys($names),
    'node_types' => TRUE,
  ));
  return $names;
354 355 356 357 358
}

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

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

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

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

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

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

454
  return $instance;
455
}
456

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

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

  return $extra;
489 490
}

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

509
/**
510
 * Loads node entities from the database.
511 512
 *
 * This function should be used whenever you need to load more than one node
513 514
 * 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
515
 *
516 517
 * @param array $nids
 *   (optional) An array of entity IDs. If omitted, all entities are loaded.
518
 * @param bool $reset
519 520
 *   (optional) Whether to reset the internal node_load() cache.  Defaults to
 *   FALSE.
Dries's avatar
 
Dries committed
521
 *
522
 * @return \Drupal\node\NodeInterface[]
523
 *   An array of node entities indexed by nid.
524
 *
525
 * @see entity_load_multiple()
526
 * @see \Drupal\Core\Entity\Query\EntityQueryInterface
Dries's avatar
 
Dries committed
527
 */
528
function node_load_multiple(array $nids = NULL, $reset = FALSE) {
529
  return entity_load_multiple('node', $nids, $reset);
530 531 532
}

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

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

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

574
/**
575
 * Checks whether the current page is the full page view of the passed-in node.
576
 *
577
 * @param \Drupal\Core\Entity\EntityInterface $node
578
 *   A node entity.
579 580 581
 *
 * @return
 *   The ID of the node if this is a full page view, otherwise FALSE.
582
 */
583
function node_is_page(EntityInterface $node) {
584
  $page_node = menu_get_object();
585
  return (!empty($page_node) ? $page_node->id() == $node->id() : FALSE);
586 587
}

588 589 590 591 592 593 594 595 596 597
/**
 * 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 = menu_get_object()) {
    $variables['attributes']['class'][] = drupal_html_class('node-type-' . $node->getType());
  }
}

598
/**
599
 * Implements hook_preprocess_HOOK() for block templates.
600 601
 */
function node_preprocess_block(&$variables) {
602 603
  if ($variables['configuration']['module'] == 'node') {
    switch ($variables['elements']['#plugin_id']) {
604
      case 'node_syndicate_block':
605
        $variables['attributes']['role'] = 'complementary';
606 607 608 609 610
        break;
    }
  }
}

611 612 613 614 615 616 617 618 619 620 621 622 623
/**
 * 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;
}

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

647
  $variables['date'] = format_date($node->getCreatedTime());
648 649
  $username = array(
    '#theme' => 'username',
650
    '#account' => $node->getOwner(),
651
    '#link_options' => array('attributes' => array('rel' => 'author')),
652 653
  );
  $variables['name'] = drupal_render($username);
654

655
  $variables['node_url'] = $node->url();
656 657
  $variables['label'] = $variables['elements']['title'];
  unset($variables['elements']['title']);
658
  $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node);
659

660
  // Helpful $content variable for templates.
661
  $variables += array('content' => array());
662 663 664
  foreach (element_children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  }
665 666

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

688
  // Add article ARIA role.
689
  $variables['attributes']['role'] = 'article';
690

691
  // Gather node classes.
692
  $variables['attributes']['class'][] = 'node';
693 694
  $variables['attributes']['class'][] = drupal_html_class('node-' . $node->bundle());
  if ($node->isPromoted()) {
695
    $variables['attributes']['class'][] = 'promoted';
696
  }
697
  if ($node->isSticky()) {
698
    $variables['attributes']['class'][] = 'sticky';
699
  }
700
  if (!$node->isPublished()) {
701
    $variables['attributes']['class'][] = 'unpublished';
702
  }
703
  if ($variables['view_mode']) {
704
    $variables['attributes']['class'][] = drupal_html_class('view-mode-' . $variables['view_mode']);
webchick's avatar